diff options
Diffstat (limited to 'target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch')
-rw-r--r-- | target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch b/target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch new file mode 100644 index 0000000000..0964106770 --- /dev/null +++ b/target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch @@ -0,0 +1,663 @@ +From 151d7e91baaa4016ba687b80e8f7ccead62d6c72 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Tue, 25 Mar 2014 13:37:55 -0700 +Subject: [PATCH 165/182] clk: qcom: Add support for muxes, dividers, and mux + dividers + +The Krait CPU clocks are made up of muxes and dividers with a +handful of sources. Add a set of clk_ops that allow us to +configure these clocks so we can support CPU frequency scaling on +Krait CPUs. + +Based on code originally written by Saravana Kannan. + +Cc: Saravana Kannan <skannan@codeaurora.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-generic.c | 405 +++++++++++++++++++++++++++++++++++ + include/linux/clk/msm-clk-generic.h | 208 ++++++++++++++++++ + 3 files changed, 614 insertions(+) + create mode 100644 drivers/clk/qcom/clk-generic.c + create mode 100644 include/linux/clk/msm-clk-generic.h + +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index df2a1b3..2cc6039 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o + clk-qcom-y += clk-rcg.o + clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o ++clk-qcom-y += clk-generic.o + clk-qcom-y += reset.o + + obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o +diff --git a/drivers/clk/qcom/clk-generic.c b/drivers/clk/qcom/clk-generic.c +new file mode 100644 +index 0000000..a0d778b +--- /dev/null ++++ b/drivers/clk/qcom/clk-generic.c +@@ -0,0 +1,405 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/export.h> ++#include <linux/bug.h> ++#include <linux/err.h> ++#include <linux/clk-provider.h> ++#include <linux/clk/msm-clk-generic.h> ++ ++ ++/* ==================== Mux clock ==================== */ ++ ++static int mux_set_parent(struct clk_hw *hw, u8 sel) ++{ ++ struct mux_clk *mux = to_mux_clk(hw); ++ ++ if (mux->parent_map) ++ sel = mux->parent_map[sel]; ++ ++ return mux->ops->set_mux_sel(mux, sel); ++} ++ ++static u8 mux_get_parent(struct clk_hw *hw) ++{ ++ struct mux_clk *mux = to_mux_clk(hw); ++ int num_parents = __clk_get_num_parents(hw->clk); ++ int i; ++ u8 sel; ++ ++ sel = mux->ops->get_mux_sel(mux); ++ if (mux->parent_map) { ++ for (i = 0; i < num_parents; i++) ++ if (sel == mux->parent_map[i]) ++ return i; ++ WARN(1, "Can't find parent\n"); ++ return -EINVAL; ++ } ++ ++ return sel; ++} ++ ++static int mux_enable(struct clk_hw *hw) ++{ ++ struct mux_clk *mux = to_mux_clk(hw); ++ if (mux->ops->enable) ++ return mux->ops->enable(mux); ++ return 0; ++} ++ ++static void mux_disable(struct clk_hw *hw) ++{ ++ struct mux_clk *mux = to_mux_clk(hw); ++ if (mux->ops->disable) ++ return mux->ops->disable(mux); ++} ++ ++static struct clk *mux_get_safe_parent(struct clk_hw *hw) ++{ ++ int i; ++ struct mux_clk *mux = to_mux_clk(hw); ++ int num_parents = __clk_get_num_parents(hw->clk); ++ ++ if (!mux->has_safe_parent) ++ return NULL; ++ ++ i = mux->safe_sel; ++ if (mux->parent_map) ++ for (i = 0; i < num_parents; i++) ++ if (mux->safe_sel == mux->parent_map[i]) ++ break; ++ ++ return clk_get_parent_by_index(hw->clk, i); ++} ++ ++const struct clk_ops clk_ops_gen_mux = { ++ .enable = mux_enable, ++ .disable = mux_disable, ++ .set_parent = mux_set_parent, ++ .get_parent = mux_get_parent, ++ .determine_rate = __clk_mux_determine_rate, ++ .get_safe_parent = mux_get_safe_parent, ++}; ++EXPORT_SYMBOL_GPL(clk_ops_gen_mux); ++ ++/* ==================== Divider clock ==================== */ ++ ++static long __div_round_rate(struct div_data *data, unsigned long rate, ++ struct clk *parent, unsigned int *best_div, unsigned long *best_prate, ++ bool set_parent) ++{ ++ unsigned int div, min_div, max_div, _best_div = 1; ++ unsigned long prate, _best_prate = 0, rrate = 0, req_prate, actual_rate; ++ unsigned int numer; ++ ++ rate = max(rate, 1UL); ++ ++ min_div = max(data->min_div, 1U); ++ max_div = min(data->max_div, (unsigned int) (ULONG_MAX / rate)); ++ ++ /* ++ * div values are doubled for half dividers. ++ * Adjust for that by picking a numer of 2. ++ */ ++ numer = data->is_half_divider ? 2 : 1; ++ ++ if (!set_parent) { ++ prate = *best_prate * numer; ++ div = DIV_ROUND_UP(prate, rate); ++ div = clamp(1U, div, max_div); ++ if (best_div) ++ *best_div = div; ++ return mult_frac(*best_prate, numer, div); ++ } ++ ++ for (div = min_div; div <= max_div; div++) { ++ req_prate = mult_frac(rate, div, numer); ++ prate = __clk_round_rate(parent, req_prate); ++ if (IS_ERR_VALUE(prate)) ++ break; ++ ++ actual_rate = mult_frac(prate, numer, div); ++ if (is_better_rate(rate, rrate, actual_rate)) { ++ rrate = actual_rate; ++ _best_div = div; ++ _best_prate = prate; ++ } ++ ++ /* ++ * Trying higher dividers is only going to ask the parent for ++ * a higher rate. If it can't even output a rate higher than ++ * the one we request for this divider, the parent is not ++ * going to be able to output an even higher rate required ++ * for a higher divider. So, stop trying higher dividers. ++ */ ++ if (actual_rate < rate) ++ break; ++ ++ if (rrate <= rate) ++ break; ++ } ++ ++ if (!rrate) ++ return -EINVAL; ++ if (best_div) ++ *best_div = _best_div; ++ if (best_prate) ++ *best_prate = _best_prate; ++ ++ return rrate; ++} ++ ++static long div_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ bool set_parent = __clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT; ++ ++ return __div_round_rate(&d->data, rate, __clk_get_parent(hw->clk), ++ NULL, parent_rate, set_parent); ++} ++ ++static int div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long ++ parent_rate) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ int div, rc = 0; ++ struct div_data *data = &d->data; ++ ++ div = parent_rate / rate; ++ if (div != data->div) ++ rc = d->ops->set_div(d, div); ++ data->div = div; ++ ++ return rc; ++} ++ ++static int div_enable(struct clk_hw *hw) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ if (d->ops && d->ops->enable) ++ return d->ops->enable(d); ++ return 0; ++} ++ ++static void div_disable(struct clk_hw *hw) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ if (d->ops && d->ops->disable) ++ return d->ops->disable(d); ++} ++ ++static unsigned long div_recalc_rate(struct clk_hw *hw, unsigned long prate) ++{ ++ struct div_clk *d = to_div_clk(hw); ++ unsigned int div = d->data.div; ++ ++ if (d->ops && d->ops->get_div) ++ div = max(d->ops->get_div(d), 1); ++ div = max(div, 1U); ++ ++ if (!d->ops || !d->ops->set_div) ++ d->data.min_div = d->data.max_div = div; ++ d->data.div = div; ++ ++ return prate / div; ++} ++ ++const struct clk_ops clk_ops_div = { ++ .enable = div_enable, ++ .disable = div_disable, ++ .round_rate = div_round_rate, ++ .set_rate = div_set_rate, ++ .recalc_rate = div_recalc_rate, ++}; ++EXPORT_SYMBOL_GPL(clk_ops_div); ++ ++/* ==================== Mux_div clock ==================== */ ++ ++static int mux_div_clk_enable(struct clk_hw *hw) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ ++ if (md->ops->enable) ++ return md->ops->enable(md); ++ return 0; ++} ++ ++static void mux_div_clk_disable(struct clk_hw *hw) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ ++ if (md->ops->disable) ++ return md->ops->disable(md); ++} ++ ++static long __mux_div_round_rate(struct clk_hw *hw, unsigned long rate, ++ struct clk **best_parent, int *best_div, unsigned long *best_prate) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ unsigned int i; ++ unsigned long rrate, best = 0, _best_div = 0, _best_prate = 0; ++ struct clk *_best_parent = 0; ++ int num_parents = __clk_get_num_parents(hw->clk); ++ bool set_parent = __clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT; ++ ++ for (i = 0; i < num_parents; i++) { ++ int div; ++ unsigned long prate; ++ struct clk *p = clk_get_parent_by_index(hw->clk, i); ++ ++ rrate = __div_round_rate(&md->data, rate, p, &div, &prate, ++ set_parent); ++ ++ if (is_better_rate(rate, best, rrate)) { ++ best = rrate; ++ _best_div = div; ++ _best_prate = prate; ++ _best_parent = p; ++ } ++ ++ if (rate <= rrate) ++ break; ++ } ++ ++ if (best_div) ++ *best_div = _best_div; ++ if (best_prate) ++ *best_prate = _best_prate; ++ if (best_parent) ++ *best_parent = _best_parent; ++ ++ if (best) ++ return best; ++ return -EINVAL; ++} ++ ++static long mux_div_clk_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ return __mux_div_round_rate(hw, rate, NULL, NULL, parent_rate); ++} ++ ++/* requires enable lock to be held */ ++static int __set_src_div(struct mux_div_clk *md, u8 src_sel, u32 div) ++{ ++ int rc; ++ ++ rc = md->ops->set_src_div(md, src_sel, div); ++ if (!rc) { ++ md->data.div = div; ++ md->src_sel = src_sel; ++ } ++ ++ return rc; ++} ++ ++/* Must be called after handoff to ensure parent clock rates are initialized */ ++static int safe_parent_init_once(struct clk_hw *hw) ++{ ++ unsigned long rrate; ++ u32 best_div; ++ struct clk *best_parent; ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ ++ if (IS_ERR(md->safe_parent)) ++ return -EINVAL; ++ if (!md->safe_freq || md->safe_parent) ++ return 0; ++ ++ rrate = __mux_div_round_rate(hw, md->safe_freq, &best_parent, ++ &best_div, NULL); ++ ++ if (rrate == md->safe_freq) { ++ md->safe_div = best_div; ++ md->safe_parent = best_parent; ++ } else { ++ md->safe_parent = ERR_PTR(-EINVAL); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int ++__mux_div_clk_set_rate_and_parent(struct clk_hw *hw, u8 index, u32 div) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ int rc; ++ ++ rc = safe_parent_init_once(hw); ++ if (rc) ++ return rc; ++ ++ return __set_src_div(md, index, div); ++} ++ ++static int mux_div_clk_set_rate_and_parent(struct clk_hw *hw, ++ unsigned long rate, unsigned long parent_rate, u8 index) ++{ ++ return __mux_div_clk_set_rate_and_parent(hw, index, parent_rate / rate); ++} ++ ++static int mux_div_clk_set_rate(struct clk_hw *hw, ++ unsigned long rate, unsigned long parent_rate) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ return __mux_div_clk_set_rate_and_parent(hw, md->src_sel, ++ parent_rate / rate); ++} ++ ++static int mux_div_clk_set_parent(struct clk_hw *hw, u8 index) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ return __mux_div_clk_set_rate_and_parent(hw, md->parent_map[index], ++ md->data.div); ++} ++ ++static u8 mux_div_clk_get_parent(struct clk_hw *hw) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ int num_parents = __clk_get_num_parents(hw->clk); ++ u32 i, div, sel; ++ ++ md->ops->get_src_div(md, &sel, &div); ++ md->src_sel = sel; ++ ++ for (i = 0; i < num_parents; i++) ++ if (sel == md->parent_map[i]) ++ return i; ++ WARN(1, "Can't find parent\n"); ++ return -EINVAL; ++} ++ ++static unsigned long ++mux_div_clk_recalc_rate(struct clk_hw *hw, unsigned long prate) ++{ ++ struct mux_div_clk *md = to_mux_div_clk(hw); ++ u32 div, sel; ++ ++ md->ops->get_src_div(md, &sel, &div); ++ ++ return prate / div; ++} ++ ++const struct clk_ops clk_ops_mux_div_clk = { ++ .enable = mux_div_clk_enable, ++ .disable = mux_div_clk_disable, ++ .set_rate_and_parent = mux_div_clk_set_rate_and_parent, ++ .set_rate = mux_div_clk_set_rate, ++ .set_parent = mux_div_clk_set_parent, ++ .round_rate = mux_div_clk_round_rate, ++ .get_parent = mux_div_clk_get_parent, ++ .recalc_rate = mux_div_clk_recalc_rate, ++}; ++EXPORT_SYMBOL_GPL(clk_ops_mux_div_clk); +diff --git a/include/linux/clk/msm-clk-generic.h b/include/linux/clk/msm-clk-generic.h +new file mode 100644 +index 0000000..cee3863 +--- /dev/null ++++ b/include/linux/clk/msm-clk-generic.h +@@ -0,0 +1,208 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __QCOM_CLK_GENERIC_H__ ++#define __QCOM_CLK_GENERIC_H__ ++ ++#include <linux/err.h> ++#include <linux/clk-provider.h> ++ ++static inline bool is_better_rate(unsigned long req, unsigned long best, ++ unsigned long new) ++{ ++ if (IS_ERR_VALUE(new)) ++ return false; ++ ++ return (req <= new && new < best) || (best < req && best < new); ++} ++ ++/* ==================== Mux clock ==================== */ ++ ++struct mux_clk; ++ ++struct clk_mux_ops { ++ int (*set_mux_sel)(struct mux_clk *clk, int sel); ++ int (*get_mux_sel)(struct mux_clk *clk); ++ ++ /* Optional */ ++ bool (*is_enabled)(struct mux_clk *clk); ++ int (*enable)(struct mux_clk *clk); ++ void (*disable)(struct mux_clk *clk); ++}; ++ ++struct mux_clk { ++ /* Parents in decreasing order of preference for obtaining rates. */ ++ u8 *parent_map; ++ bool has_safe_parent; ++ u8 safe_sel; ++ const struct clk_mux_ops *ops; ++ ++ /* Fields not used by helper function. */ ++ void __iomem *base; ++ u32 offset; ++ u32 en_offset; ++ int en_reg; ++ u32 mask; ++ u32 shift; ++ u32 en_mask; ++ void *priv; ++ ++ struct clk_hw hw; ++}; ++ ++static inline struct mux_clk *to_mux_clk(struct clk_hw *hw) ++{ ++ return container_of(hw, struct mux_clk, hw); ++} ++ ++extern const struct clk_ops clk_ops_gen_mux; ++ ++/* ==================== Divider clock ==================== */ ++ ++struct div_clk; ++ ++struct clk_div_ops { ++ int (*set_div)(struct div_clk *clk, int div); ++ int (*get_div)(struct div_clk *clk); ++ bool (*is_enabled)(struct div_clk *clk); ++ int (*enable)(struct div_clk *clk); ++ void (*disable)(struct div_clk *clk); ++}; ++ ++struct div_data { ++ unsigned int div; ++ unsigned int min_div; ++ unsigned int max_div; ++ /* ++ * Indicate whether this divider clock supports half-interger divider. ++ * If it is, all the min_div and max_div have been doubled. It means ++ * they are 2*N. ++ */ ++ bool is_half_divider; ++}; ++ ++struct div_clk { ++ struct div_data data; ++ ++ /* Optional */ ++ const struct clk_div_ops *ops; ++ ++ /* Fields not used by helper function. */ ++ void __iomem *base; ++ u32 offset; ++ u32 mask; ++ u32 shift; ++ u32 en_mask; ++ void *priv; ++ struct clk_hw hw; ++}; ++ ++static inline struct div_clk *to_div_clk(struct clk_hw *hw) ++{ ++ return container_of(hw, struct div_clk, hw); ++} ++ ++extern const struct clk_ops clk_ops_div; ++ ++#define DEFINE_FIXED_DIV_CLK(clk_name, _div, _parent) \ ++static struct div_clk clk_name = { \ ++ .data = { \ ++ .max_div = _div, \ ++ .min_div = _div, \ ++ .div = _div, \ ++ }, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .parent_names = (const char *[]){ _parent }, \ ++ .num_parents = 1, \ ++ .name = #clk_name, \ ++ .ops = &clk_ops_div, \ ++ .flags = CLK_SET_RATE_PARENT, \ ++ } \ ++} ++ ++/* ==================== Mux Div clock ==================== */ ++ ++struct mux_div_clk; ++ ++/* ++ * struct mux_div_ops ++ * the enable and disable ops are optional. ++ */ ++ ++struct mux_div_ops { ++ int (*set_src_div)(struct mux_div_clk *, u32 src_sel, u32 div); ++ void (*get_src_div)(struct mux_div_clk *, u32 *src_sel, u32 *div); ++ int (*enable)(struct mux_div_clk *); ++ void (*disable)(struct mux_div_clk *); ++ bool (*is_enabled)(struct mux_div_clk *); ++}; ++ ++/* ++ * struct mux_div_clk - combined mux/divider clock ++ * @priv ++ parameters needed by ops ++ * @safe_freq ++ when switching rates from A to B, the mux div clock will ++ instead switch from A -> safe_freq -> B. This allows the ++ mux_div clock to change rates while enabled, even if this ++ behavior is not supported by the parent clocks. ++ ++ If changing the rate of parent A also causes the rate of ++ parent B to change, then safe_freq must be defined. ++ ++ safe_freq is expected to have a source clock which is always ++ on and runs at only one rate. ++ * @parents ++ list of parents and mux indicies ++ * @ops ++ function pointers for hw specific operations ++ * @src_sel ++ the mux index which will be used if the clock is enabled. ++ */ ++ ++struct mux_div_clk { ++ /* Required parameters */ ++ const struct mux_div_ops *ops; ++ struct div_data data; ++ u8 *parent_map; ++ ++ struct clk_hw hw; ++ ++ /* Internal */ ++ u32 src_sel; ++ ++ /* Optional parameters */ ++ void *priv; ++ void __iomem *base; ++ u32 div_mask; ++ u32 div_offset; ++ u32 div_shift; ++ u32 src_mask; ++ u32 src_offset; ++ u32 src_shift; ++ u32 en_mask; ++ u32 en_offset; ++ ++ u32 safe_div; ++ struct clk *safe_parent; ++ unsigned long safe_freq; ++}; ++ ++static inline struct mux_div_clk *to_mux_div_clk(struct clk_hw *hw) ++{ ++ return container_of(hw, struct mux_div_clk, hw); ++} ++ ++extern const struct clk_ops clk_ops_mux_div_clk; ++ ++#endif +-- +1.7.10.4 + |