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 | 652 |
1 files changed, 0 insertions, 652 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 deleted file mode 100644 index fa7b4e9..0000000 --- a/target/linux/ipq806x/patches/0165-clk-qcom-Add-support-for-muxes-dividers-and-mux-divi.patch +++ /dev/null @@ -1,652 +0,0 @@ -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 - ---- 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 ---- /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); ---- /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 |