From cfe4c93b58924b3764cd7269d3d953049405e938 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sat, 6 Sep 2014 14:45:10 +0800 Subject: clk: sunxi: add correct divider table for sun4i-apb0 clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sun4i-apb0 clock, as found on all platforms using it, is a power-of-two-based divider clock, with a special divider of 2 for value 0. This was causing the clock framework to incorrectly calculate the clock rate for apb1 and related modules on sun6i and sun8i. On sun[4/5/7]i, u-boot SPL configures the divider with value 1 for /2 divider, so no suprises there. This patch adds a proper divider table for it, so the correct clock rate can be calculated. Signed-off-by: Chen-Yu Tsai Acked-by: Emilio López Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index b654b7b..2cf6581 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -762,10 +762,19 @@ static const struct div_data sun4i_ahb_data __initconst = { .width = 2, }; +static const struct clk_div_table sun4i_apb0_table[] __initconst = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { } /* sentinel */ +}; + static const struct div_data sun4i_apb0_data __initconst = { .shift = 8, .pow = 1, .width = 2, + .table = sun4i_apb0_table, }; static const struct div_data sun6i_a31_apb2_div_data __initconst = { -- cgit v1.1 From e59c5371fb9d8268d1c043172e88cecab9dc934f Mon Sep 17 00:00:00 2001 From: Mike Turquette Date: Tue, 18 Feb 2014 21:21:25 -0800 Subject: clk: introduce clk_set_phase function & callback A common operation for a clock signal generator is to shift the phase of that signal. This patch introduces a new function to the clk.h API to dynamically adjust the phase of a clock signal. Additionally this patch introduces support for the new function in the common clock framework via the .set_phase call back in struct clk_ops. Signed-off-by: Mike Turquette Signed-off-by: Maxime Ripard Reviewed-by: Heiko Stuebner Acked-by: Hans de Goede --- drivers/clk/clk.c | 85 +++++++++++++++++++++++++++++++++++++++++--- include/linux/clk-private.h | 1 + include/linux/clk-provider.h | 5 +++ include/linux/clk.h | 29 +++++++++++++++ 4 files changed, 116 insertions(+), 4 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index b76fa69..d87661a 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -117,11 +117,11 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level) if (!c) return; - seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu\n", + seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n", level * 3 + 1, "", 30 - level * 3, c->name, c->enable_count, c->prepare_count, clk_get_rate(c), - clk_get_accuracy(c)); + clk_get_accuracy(c), clk_get_phase(c)); } static void clk_summary_show_subtree(struct seq_file *s, struct clk *c, @@ -143,8 +143,8 @@ static int clk_summary_show(struct seq_file *s, void *data) struct clk *c; struct hlist_head **lists = (struct hlist_head **)s->private; - seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy\n"); - seq_puts(s, "--------------------------------------------------------------------------------\n"); + seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n"); + seq_puts(s, "----------------------------------------------------------------------------------------\n"); clk_prepare_lock(); @@ -180,6 +180,7 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level) seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); seq_printf(s, "\"rate\": %lu", clk_get_rate(c)); seq_printf(s, "\"accuracy\": %lu", clk_get_accuracy(c)); + seq_printf(s, "\"phase\": %d", clk_get_phase(c)); } static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level) @@ -264,6 +265,11 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry) if (!d) goto err_out; + d = debugfs_create_u32("clk_phase", S_IRUGO, clk->dentry, + (u32 *)&clk->phase); + if (!d) + goto err_out; + d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry, (u32 *)&clk->flags); if (!d) @@ -1739,6 +1745,77 @@ out: EXPORT_SYMBOL_GPL(clk_set_parent); /** + * clk_set_phase - adjust the phase shift of a clock signal + * @clk: clock signal source + * @degrees: number of degrees the signal is shifted + * + * Shifts the phase of a clock signal by the specified + * degrees. Returns 0 on success, -EERROR otherwise. + * + * This function makes no distinction about the input or reference + * signal that we adjust the clock signal phase against. For example + * phase locked-loop clock signal generators we may shift phase with + * respect to feedback clock signal input, but for other cases the + * clock phase may be shifted with respect to some other, unspecified + * signal. + * + * Additionally the concept of phase shift does not propagate through + * the clock tree hierarchy, which sets it apart from clock rates and + * clock accuracy. A parent clock phase attribute does not have an + * impact on the phase attribute of a child clock. + */ +int clk_set_phase(struct clk *clk, int degrees) +{ + int ret = 0; + + if (!clk) + goto out; + + /* sanity check degrees */ + degrees %= 360; + if (degrees < 0) + degrees += 360; + + clk_prepare_lock(); + + if (!clk->ops->set_phase) + goto out_unlock; + + ret = clk->ops->set_phase(clk->hw, degrees); + + if (!ret) + clk->phase = degrees; + +out_unlock: + clk_prepare_unlock(); + +out: + return ret; +} + +/** + * clk_get_phase - return the phase shift of a clock signal + * @clk: clock signal source + * + * Returns the phase shift of a clock node in degrees, otherwise returns + * -EERROR. + */ +int clk_get_phase(struct clk *clk) +{ + int ret = 0; + + if (!clk) + goto out; + + clk_prepare_lock(); + ret = clk->phase; + clk_prepare_unlock(); + +out: + return ret; +} + +/** * __clk_init - initialize the data structures in a struct clk * @dev: device initializing this clk, placeholder for now * @clk: clk being initialized diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h index efbf70b..845be30 100644 --- a/include/linux/clk-private.h +++ b/include/linux/clk-private.h @@ -46,6 +46,7 @@ struct clk { unsigned int enable_count; unsigned int prepare_count; unsigned long accuracy; + int phase; struct hlist_head children; struct hlist_node child_node; unsigned int notifier_count; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 411dd7e..201a619 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -129,6 +129,10 @@ struct dentry; * set then clock accuracy will be initialized to parent accuracy * or 0 (perfect clock) if clock has no parent. * + * @set_phase: Shift the phase this clock signal in degrees specified + * by the second argument. Valid values for degrees are + * 0-359. Return 0 on success, otherwise -EERROR. + * * @init: Perform platform-specific initialization magic. * This is not not used by any of the basic clock types. * Please consider other ways of solving initialization problems @@ -177,6 +181,7 @@ struct clk_ops { unsigned long parent_rate, u8 index); unsigned long (*recalc_accuracy)(struct clk_hw *hw, unsigned long parent_accuracy); + int (*set_phase)(struct clk_hw *hw, int degrees); void (*init)(struct clk_hw *hw); int (*debug_init)(struct clk_hw *hw, struct dentry *dentry); }; diff --git a/include/linux/clk.h b/include/linux/clk.h index fb5e097..38bdedd 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -106,6 +106,25 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb); */ long clk_get_accuracy(struct clk *clk); +/** + * clk_set_phase - adjust the phase shift of a clock signal + * @clk: clock signal source + * @degrees: number of degrees the signal is shifted + * + * Shifts the phase of a clock signal by the specified degrees. Returns 0 on + * success, -EERROR otherwise. + */ +int clk_set_phase(struct clk *clk, int degrees); + +/** + * clk_get_phase - return the phase shift of a clock signal + * @clk: clock signal source + * + * Returns the phase shift of a clock node in degrees, otherwise returns + * -EERROR. + */ +int clk_get_phase(struct clk *clk); + #else static inline long clk_get_accuracy(struct clk *clk) @@ -113,6 +132,16 @@ static inline long clk_get_accuracy(struct clk *clk) return -ENOTSUPP; } +static inline long clk_set_phase(struct clk *clk, int phase) +{ + return -ENOTSUPP; +} + +static inline long clk_get_phase(struct clk *clk) +{ + return -ENOTSUPP; +} + #endif /** -- cgit v1.1 From 355bb165cd8bad2500df37437a9121f0177b6741 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Sat, 30 Aug 2014 21:18:00 +0200 Subject: clk: Include of.h in clock-provider.h CLK_OF_DECLARE relies on OF_DECLARE_1 that is defined in of.h. Fixes build errors when one use CLK_OF_DECLARE but doesn't include of.h Signed-off-by: Maxime Ripard Acked-by: Hans de Goede --- include/linux/clk-provider.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 201a619..69b20d4 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -13,6 +13,7 @@ #include #include +#include #ifdef CONFIG_COMMON_CLK -- cgit v1.1 From 9824cf73c3a5e677bee6fcba43c4807e01ff1b4a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 14 Jul 2014 13:53:27 +0200 Subject: clk: Add a function to retrieve phase The current phase API doesn't look into the actual hardware to get the phase value, but will rather get it from a variable only set by the set_phase function. This will cause issue when the client driver will never call the set_phase function, where we can end up having a reported phase that will not match what the hardware has been programmed to by the bootloader or what phase is programmed out of reset. Add a new get_phase function for the drivers to implement so that we can get this value. Signed-off-by: Maxime Ripard Reviewed-by: Heiko Stuebner Acked-by: Hans de Goede --- drivers/clk/clk.c | 10 ++++++++++ include/linux/clk-provider.h | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index d87661a..113d75db 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1934,6 +1934,16 @@ int __clk_init(struct device *dev, struct clk *clk) clk->accuracy = 0; /* + * Set clk's phase. + * Since a phase is by definition relative to its parent, just + * query the current clock phase, or just assume it's in phase. + */ + if (clk->ops->get_phase) + clk->phase = clk->ops->get_phase(clk->hw); + else + clk->phase = 0; + + /* * Set clk's rate. The preferred method is to use .recalc_rate. For * simple clocks and lazy developers the default fallback is to use the * parent's rate. If a clock doesn't have a parent (or is orphaned) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 69b20d4..abec961 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -130,6 +130,10 @@ struct dentry; * set then clock accuracy will be initialized to parent accuracy * or 0 (perfect clock) if clock has no parent. * + * @get_phase: Queries the hardware to get the current phase of a clock. + * Returned values are 0-359 degrees on success, negative + * error codes on failure. + * * @set_phase: Shift the phase this clock signal in degrees specified * by the second argument. Valid values for degrees are * 0-359. Return 0 on success, otherwise -EERROR. @@ -182,6 +186,7 @@ struct clk_ops { unsigned long parent_rate, u8 index); unsigned long (*recalc_accuracy)(struct clk_hw *hw, unsigned long parent_accuracy); + int (*get_phase)(struct clk_hw *hw); int (*set_phase)(struct clk_hw *hw, int degrees); void (*init)(struct clk_hw *hw); int (*debug_init)(struct clk_hw *hw, struct dentry *dentry); -- cgit v1.1 From 601da9d0a54c0fea7f5a208bc107d7ef1de4f570 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 4 Jul 2014 22:24:52 +0200 Subject: clk: sunxi: factors: Invert the probing logic Until now, the factors clock probing was done directly by sunxi_init_clocks, with the factors registration being called directly with the clocks data passed as an argument. This approch has shown its limits when we added more clocks, since we couldn't really split code with such a logic in smaller files, and led to a huge file having all the clocks. Introduce an intermediate probing function, so that factor clocks will be able to directly be called by CLK_OF_DECLARE, which will in turn ease the split into several files. Signed-off-by: Maxime Ripard Acked-by: Hans de Goede --- drivers/clk/sunxi/clk-factors.c | 101 +++++++++++++++++++++++++++++++++++++--- drivers/clk/sunxi/clk-factors.h | 16 ++++++- drivers/clk/sunxi/clk-sunxi.c | 95 ++----------------------------------- 3 files changed, 113 insertions(+), 99 deletions(-) diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 2057c8a..f83ba09 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -9,18 +9,18 @@ */ #include +#include +#include +#include #include +#include #include -#include -#include #include -#include - #include "clk-factors.h" /* - * DOC: basic adjustable factor-based clock that cannot gate + * DOC: basic adjustable factor-based clock * * Traits of this clock: * prepare - clk_prepare only ensures that parents are prepared @@ -32,6 +32,8 @@ #define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) +#define FACTORS_MAX_PARENTS 5 + #define SETMASK(len, pos) (((1U << (len)) - 1) << (pos)) #define CLRMASK(len, pos) (~(SETMASK(len, pos))) #define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit)) @@ -147,9 +149,96 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -const struct clk_ops clk_factors_ops = { +static const struct clk_ops clk_factors_ops = { .determine_rate = clk_factors_determine_rate, .recalc_rate = clk_factors_recalc_rate, .round_rate = clk_factors_round_rate, .set_rate = clk_factors_set_rate, }; + +struct clk * __init sunxi_factors_register(struct device_node *node, + const struct factors_data *data, + spinlock_t *lock) +{ + struct clk *clk; + struct clk_factors *factors; + struct clk_gate *gate = NULL; + struct clk_mux *mux = NULL; + struct clk_hw *gate_hw = NULL; + struct clk_hw *mux_hw = NULL; + const char *clk_name = node->name; + const char *parents[FACTORS_MAX_PARENTS]; + void __iomem *reg; + int i = 0; + + reg = of_iomap(node, 0); + + /* if we have a mux, we will have >1 parents */ + while (i < FACTORS_MAX_PARENTS && + (parents[i] = of_clk_get_parent_name(node, i)) != NULL) + i++; + + /* + * some factor clocks, such as pll5 and pll6, may have multiple + * outputs, and have their name designated in factors_data + */ + if (data->name) + clk_name = data->name; + else + of_property_read_string(node, "clock-output-names", &clk_name); + + factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); + if (!factors) + return NULL; + + /* set up factors properties */ + factors->reg = reg; + factors->config = data->table; + factors->get_factors = data->getter; + factors->lock = lock; + + /* Add a gate if this factor clock can be gated */ + if (data->enable) { + gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); + if (!gate) { + kfree(factors); + return NULL; + } + + /* set up gate properties */ + gate->reg = reg; + gate->bit_idx = data->enable; + gate->lock = factors->lock; + gate_hw = &gate->hw; + } + + /* Add a mux if this factor clock can be muxed */ + if (data->mux) { + mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); + if (!mux) { + kfree(factors); + kfree(gate); + return NULL; + } + + /* set up gate properties */ + mux->reg = reg; + mux->shift = data->mux; + mux->mask = SUNXI_FACTORS_MUX_MASK; + mux->lock = factors->lock; + mux_hw = &mux->hw; + } + + clk = clk_register_composite(NULL, clk_name, + parents, i, + mux_hw, &clk_mux_ops, + &factors->hw, &clk_factors_ops, + gate_hw, &clk_gate_ops, 0); + + if (!IS_ERR(clk)) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } + + return clk; +} diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h index d2d0efa..9913840 100644 --- a/drivers/clk/sunxi/clk-factors.h +++ b/drivers/clk/sunxi/clk-factors.h @@ -3,9 +3,12 @@ #include #include +#include #define SUNXI_FACTORS_NOT_APPLICABLE (0) +#define SUNXI_FACTORS_MUX_MASK 0x3 + struct clk_factors_config { u8 nshift; u8 nwidth; @@ -18,6 +21,14 @@ struct clk_factors_config { u8 n_start; }; +struct factors_data { + int enable; + int mux; + struct clk_factors_config *table; + void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); + const char *name; +}; + struct clk_factors { struct clk_hw hw; void __iomem *reg; @@ -26,5 +37,8 @@ struct clk_factors { spinlock_t *lock; }; -extern const struct clk_ops clk_factors_ops; +struct clk * __init sunxi_factors_register(struct device_node *node, + const struct factors_data *data, + spinlock_t *lock); + #endif diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 2cf6581..393df32 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "clk-factors.h" @@ -440,16 +441,6 @@ EXPORT_SYMBOL(clk_sunxi_mmc_phase_control); * sunxi_factors_clk_setup() - Setup function for factor clocks */ -#define SUNXI_FACTORS_MUX_MASK 0x3 - -struct factors_data { - int enable; - int mux; - struct clk_factors_config *table; - void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); - const char *name; -}; - static struct clk_factors_config sun4i_pll1_config = { .nshift = 8, .nwidth = 5, @@ -583,89 +574,9 @@ static const struct factors_data sun7i_a20_out_data __initconst = { }; static struct clk * __init sunxi_factors_clk_setup(struct device_node *node, - const struct factors_data *data) + const struct factors_data *data) { - struct clk *clk; - struct clk_factors *factors; - struct clk_gate *gate = NULL; - struct clk_mux *mux = NULL; - struct clk_hw *gate_hw = NULL; - struct clk_hw *mux_hw = NULL; - const char *clk_name = node->name; - const char *parents[SUNXI_MAX_PARENTS]; - void __iomem *reg; - int i = 0; - - reg = of_iomap(node, 0); - - /* if we have a mux, we will have >1 parents */ - while (i < SUNXI_MAX_PARENTS && - (parents[i] = of_clk_get_parent_name(node, i)) != NULL) - i++; - - /* - * some factor clocks, such as pll5 and pll6, may have multiple - * outputs, and have their name designated in factors_data - */ - if (data->name) - clk_name = data->name; - else - of_property_read_string(node, "clock-output-names", &clk_name); - - factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); - if (!factors) - return NULL; - - /* Add a gate if this factor clock can be gated */ - if (data->enable) { - gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); - if (!gate) { - kfree(factors); - return NULL; - } - - /* set up gate properties */ - gate->reg = reg; - gate->bit_idx = data->enable; - gate->lock = &clk_lock; - gate_hw = &gate->hw; - } - - /* Add a mux if this factor clock can be muxed */ - if (data->mux) { - mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); - if (!mux) { - kfree(factors); - kfree(gate); - return NULL; - } - - /* set up gate properties */ - mux->reg = reg; - mux->shift = data->mux; - mux->mask = SUNXI_FACTORS_MUX_MASK; - mux->lock = &clk_lock; - mux_hw = &mux->hw; - } - - /* set up factors properties */ - factors->reg = reg; - factors->config = data->table; - factors->get_factors = data->getter; - factors->lock = &clk_lock; - - clk = clk_register_composite(NULL, clk_name, - parents, i, - mux_hw, &clk_mux_ops, - &factors->hw, &clk_factors_ops, - gate_hw, &clk_gate_ops, 0); - - if (!IS_ERR(clk)) { - of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); - } - - return clk; + return sunxi_factors_register(node, data, &clk_lock); } -- cgit v1.1 From 03e29bbf40ed87657795c774c80449bb86a55415 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 10 Jul 2014 23:53:40 +0200 Subject: clk: sunxi: Introduce mbus compatible Even though the mbus clock is a regular module clock, given its nature, it needs to be enabled all the time. Introduce a new compatible, to differentiate it from the other module clocks. Signed-off-by: Maxime Ripard Acked-by: Hans de Goede --- Documentation/devicetree/bindings/clock/sunxi.txt | 1 + drivers/clk/sunxi/clk-sunxi.c | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index d3a5c3c..b938a99 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -46,6 +46,7 @@ Required properties: "allwinner,sun6i-a31-apb2-div-clk" - for the APB2 gates on A31 "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 "allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23 + "allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13 "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks "allwinner,sun7i-a20-out-clk" - for the external output clocks "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31 diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 393df32..17e1e3b 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -1119,6 +1119,7 @@ static const struct of_device_id clk_factors_match[] __initconst = { {.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,}, {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,}, + {.compatible = "allwinner,sun5i-a13-mbus-clk", .data = &sun4i_mod0_data,}, {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,}, {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, {} -- cgit v1.1 From 7868c5ebdbcb92087a3625cc55225f455eef70a4 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 16 Jul 2014 23:45:48 +0200 Subject: ARM: sunxi: dt: Switch to the new mbus compatible Now that we have a compatible of its own for the mbus clock, switch to it. Signed-off-by: Maxime Ripard Acked-by: Hans de Goede --- arch/arm/boot/dts/sun5i-a10s.dtsi | 2 +- arch/arm/boot/dts/sun5i-a13.dtsi | 2 +- arch/arm/boot/dts/sun7i-a20.dtsi | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi index 24b0ad3..8209428 100644 --- a/arch/arm/boot/dts/sun5i-a10s.dtsi +++ b/arch/arm/boot/dts/sun5i-a10s.dtsi @@ -287,7 +287,7 @@ mbus_clk: clk@01c2015c { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun5i-a13-mbus-clk"; reg = <0x01c2015c 0x4>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clock-output-names = "mbus"; diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi index bf86e65..53e0e72 100644 --- a/arch/arm/boot/dts/sun5i-a13.dtsi +++ b/arch/arm/boot/dts/sun5i-a13.dtsi @@ -285,7 +285,7 @@ mbus_clk: clk@01c2015c { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun5i-a13-mbus-clk"; reg = <0x01c2015c 0x4>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clock-output-names = "mbus"; diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index 4011628..2edef89 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -346,7 +346,7 @@ mbus_clk: clk@01c2015c { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun5i-a13-mbus-clk"; reg = <0x01c2015c 0x4>; clocks = <&osc24M>, <&pll6 2>, <&pll5 1>; clock-output-names = "mbus"; -- cgit v1.1 From 992a56e48996d4dea6cc25a35e180f696935925d Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 10 Jul 2014 23:55:18 +0200 Subject: clk: sunxi: Move mod0 clock to a file of its own Since we know have the ability to declare factors clock outside of clk-sunxi, create a new mod0 driver to deal with the mod0 clocks. Signed-off-by: Maxime Ripard Acked-by: Hans de Goede --- drivers/clk/sunxi/Makefile | 1 + drivers/clk/sunxi/clk-mod0.c | 82 +++++++++++++++++++++++++++++++++++++++++++ drivers/clk/sunxi/clk-sunxi.c | 1 - 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/sunxi/clk-mod0.c diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 6850cba..833f086d 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -5,6 +5,7 @@ obj-y += clk-sunxi.o clk-factors.o obj-y += clk-a10-hosc.o obj-y += clk-a20-gmac.o +obj-y += clk-mod0.o obj-$(CONFIG_MFD_SUN6I_PRCM) += \ clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \ diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c new file mode 100644 index 0000000..bce09a8 --- /dev/null +++ b/drivers/clk/sunxi/clk-mod0.c @@ -0,0 +1,82 @@ +/* + * Copyright 2013 Emilio López + * + * Emilio López + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include + +#include "clk-factors.h" + +/** + * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks + * MOD0 rate is calculated as follows + * rate = (parent_rate >> p) / (m + 1); + */ + +static void sun4i_a10_get_mod0_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 div, calcm, calcp; + + /* These clocks can only divide, so we will never be able to achieve + * frequencies higher than the parent frequency */ + if (*freq > parent_rate) + *freq = parent_rate; + + div = DIV_ROUND_UP(parent_rate, *freq); + + if (div < 16) + calcp = 0; + else if (div / 2 < 16) + calcp = 1; + else if (div / 4 < 16) + calcp = 2; + else + calcp = 3; + + calcm = DIV_ROUND_UP(div, 1 << calcp); + + *freq = (parent_rate >> calcp) / calcm; + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + *m = calcm - 1; + *p = calcp; +} + +/* user manual says "n" but it's really "p" */ +static struct clk_factors_config sun4i_a10_mod0_config = { + .mshift = 0, + .mwidth = 4, + .pshift = 16, + .pwidth = 2, +}; + +static const struct factors_data sun4i_a10_mod0_data __initconst = { + .enable = 31, + .mux = 24, + .table = &sun4i_a10_mod0_config, + .getter = sun4i_a10_get_mod0_factors, +}; + +static DEFINE_SPINLOCK(sun4i_a10_mod0_lock); + +static void __init sun4i_a10_mod0_setup(struct device_node *node) +{ + sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun4i_a10_mod0_lock); +} +CLK_OF_DECLARE(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk", sun4i_a10_mod0_setup); diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 17e1e3b..fc55494 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -1120,7 +1120,6 @@ static const struct of_device_id clk_factors_match[] __initconst = { {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,}, {.compatible = "allwinner,sun5i-a13-mbus-clk", .data = &sun4i_mod0_data,}, - {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,}, {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, {} }; -- cgit v1.1 From eaa18f5d0914b0151cefb52e2977a67ef21dfa64 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 10 Jul 2014 23:56:11 +0200 Subject: clk: sunxi: Move mbus to mod0 file Move the MBUS clock to the module clocks file. It's pretty trivial, but still requires to enable the clocks to make sure it won't get disabled. Signed-off-by: Maxime Ripard Acked-by: Hans de Goede --- drivers/clk/sunxi/clk-mod0.c | 12 +++++++++ drivers/clk/sunxi/clk-sunxi.c | 57 ------------------------------------------- 2 files changed, 12 insertions(+), 57 deletions(-) diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c index bce09a8..8a7f703 100644 --- a/drivers/clk/sunxi/clk-mod0.c +++ b/drivers/clk/sunxi/clk-mod0.c @@ -80,3 +80,15 @@ static void __init sun4i_a10_mod0_setup(struct device_node *node) sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun4i_a10_mod0_lock); } CLK_OF_DECLARE(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk", sun4i_a10_mod0_setup); + +static DEFINE_SPINLOCK(sun5i_a13_mbus_lock); + +static void __init sun5i_a13_mbus_setup(struct device_node *node) +{ + struct clk *mbus = sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun5i_a13_mbus_lock); + + /* The MBUS clocks needs to be always enabled */ + __clk_get(mbus); + clk_prepare_enable(mbus); +} +CLK_OF_DECLARE(sun5i_a13_mbus, "allwinner,sun5i-a13-mbus-clk", sun5i_a13_mbus_setup); diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index fc55494..d5dc951 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -320,46 +320,6 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate, -/** - * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks - * MOD0 rate is calculated as follows - * rate = (parent_rate >> p) / (m + 1); - */ - -static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) -{ - u8 div, calcm, calcp; - - /* These clocks can only divide, so we will never be able to achieve - * frequencies higher than the parent frequency */ - if (*freq > parent_rate) - *freq = parent_rate; - - div = DIV_ROUND_UP(parent_rate, *freq); - - if (div < 16) - calcp = 0; - else if (div / 2 < 16) - calcp = 1; - else if (div / 4 < 16) - calcp = 2; - else - calcp = 3; - - calcm = DIV_ROUND_UP(div, 1 << calcp); - - *freq = (parent_rate >> calcp) / calcm; - - /* we were called to round the frequency, we can now return */ - if (n == NULL) - return; - - *m = calcm - 1; - *p = calcp; -} - - /** * sun7i_a20_get_out_factors() - calculates m, p factors for CLK_OUT_A/B @@ -495,14 +455,6 @@ static struct clk_factors_config sun4i_apb1_config = { }; /* user manual says "n" but it's really "p" */ -static struct clk_factors_config sun4i_mod0_config = { - .mshift = 0, - .mwidth = 4, - .pshift = 16, - .pwidth = 2, -}; - -/* user manual says "n" but it's really "p" */ static struct clk_factors_config sun7i_a20_out_config = { .mshift = 8, .mwidth = 5, @@ -559,13 +511,6 @@ static const struct factors_data sun4i_apb1_data __initconst = { .getter = sun4i_get_apb1_factors, }; -static const struct factors_data sun4i_mod0_data __initconst = { - .enable = 31, - .mux = 24, - .table = &sun4i_mod0_config, - .getter = sun4i_get_mod0_factors, -}; - static const struct factors_data sun7i_a20_out_data __initconst = { .enable = 31, .mux = 24, @@ -1119,7 +1064,6 @@ static const struct of_device_id clk_factors_match[] __initconst = { {.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,}, {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,}, - {.compatible = "allwinner,sun5i-a13-mbus-clk", .data = &sun4i_mod0_data,}, {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, {} }; @@ -1231,7 +1175,6 @@ static void __init sun4i_a10_init_clocks(struct device_node *node) CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sun4i_a10_init_clocks); static const char *sun5i_critical_clocks[] __initdata = { - "mbus", "pll5_ddr", "ahb_sdram", }; -- cgit v1.1 From 37e1041f04717d726931c8688cbf425071aeb9c1 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 11 Jul 2014 18:43:18 +0200 Subject: clk: sunxi: mod0: Introduce MMC proper phase handling The MMC clock we thought we had until now are actually not one but three different clocks. The main one is unchanged, and will have three outputs: - The clock fed into the MMC - a sample and output clocks, to deal with when should we output/sample data to/from the MMC bus The phase control we had are actually controlling the two latter clocks, but the main MMC one is unchanged. We can adjust the phase with a 3 bits value, from 0 to 7, 0 meaning a 180 phase shift, and the other values being the number of periods from the MMC parent clock to outphase the clock of. Signed-off-by: Maxime Ripard Acked-by: Hans de Goede --- Documentation/devicetree/bindings/clock/sunxi.txt | 2 + drivers/clk/sunxi/clk-mod0.c | 189 ++++++++++++++++++++++ 2 files changed, 191 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index b938a99..eb690ed 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -47,6 +47,8 @@ Required properties: "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 "allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23 "allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13 + "allwinner,sun4i-a10-mmc-output-clk" - for the MMC output clock on A10 + "allwinner,sun4i-a10-mmc-sample-clk" - for the MMC sample clock on A10 "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks "allwinner,sun7i-a20-out-clk" - for the external output clocks "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31 diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c index 8a7f703..4a56385 100644 --- a/drivers/clk/sunxi/clk-mod0.c +++ b/drivers/clk/sunxi/clk-mod0.c @@ -16,6 +16,7 @@ #include #include +#include #include "clk-factors.h" @@ -92,3 +93,191 @@ static void __init sun5i_a13_mbus_setup(struct device_node *node) clk_prepare_enable(mbus); } CLK_OF_DECLARE(sun5i_a13_mbus, "allwinner,sun5i-a13-mbus-clk", sun5i_a13_mbus_setup); + +struct mmc_phase_data { + u8 offset; +}; + +struct mmc_phase { + struct clk_hw hw; + void __iomem *reg; + struct mmc_phase_data *data; + spinlock_t *lock; +}; + +#define to_mmc_phase(_hw) container_of(_hw, struct mmc_phase, hw) + +static int mmc_get_phase(struct clk_hw *hw) +{ + struct clk *mmc, *mmc_parent, *clk = hw->clk; + struct mmc_phase *phase = to_mmc_phase(hw); + unsigned int mmc_rate, mmc_parent_rate; + u16 step, mmc_div; + u32 value; + u8 delay; + + value = readl(phase->reg); + delay = (value >> phase->data->offset) & 0x3; + + if (!delay) + return 180; + + /* Get the main MMC clock */ + mmc = clk_get_parent(clk); + if (!mmc) + return -EINVAL; + + /* And its rate */ + mmc_rate = clk_get_rate(mmc); + if (!mmc_rate) + return -EINVAL; + + /* Now, get the MMC parent (most likely some PLL) */ + mmc_parent = clk_get_parent(mmc); + if (!mmc_parent) + return -EINVAL; + + /* And its rate */ + mmc_parent_rate = clk_get_rate(mmc_parent); + if (!mmc_parent_rate) + return -EINVAL; + + /* Get MMC clock divider */ + mmc_div = mmc_parent_rate / mmc_rate; + + step = DIV_ROUND_CLOSEST(360, mmc_div); + return delay * step; +} + +static int mmc_set_phase(struct clk_hw *hw, int degrees) +{ + struct clk *mmc, *mmc_parent, *clk = hw->clk; + struct mmc_phase *phase = to_mmc_phase(hw); + unsigned int mmc_rate, mmc_parent_rate; + unsigned long flags; + u32 value; + u8 delay; + + /* Get the main MMC clock */ + mmc = clk_get_parent(clk); + if (!mmc) + return -EINVAL; + + /* And its rate */ + mmc_rate = clk_get_rate(mmc); + if (!mmc_rate) + return -EINVAL; + + /* Now, get the MMC parent (most likely some PLL) */ + mmc_parent = clk_get_parent(mmc); + if (!mmc_parent) + return -EINVAL; + + /* And its rate */ + mmc_parent_rate = clk_get_rate(mmc_parent); + if (!mmc_parent_rate) + return -EINVAL; + + if (degrees != 180) { + u16 step, mmc_div; + + /* Get MMC clock divider */ + mmc_div = mmc_parent_rate / mmc_rate; + + /* + * We can only outphase the clocks by multiple of the + * PLL's period. + * + * Since the MMC clock in only a divider, and the + * formula to get the outphasing in degrees is deg = + * 360 * delta / period + * + * If we simplify this formula, we can see that the + * only thing that we're concerned about is the number + * of period we want to outphase our clock from, and + * the divider set by the MMC clock. + */ + step = DIV_ROUND_CLOSEST(360, mmc_div); + delay = DIV_ROUND_CLOSEST(degrees, step); + } else { + delay = 0; + } + + spin_lock_irqsave(phase->lock, flags); + value = readl(phase->reg); + value &= ~GENMASK(phase->data->offset + 3, phase->data->offset); + value |= delay << phase->data->offset; + writel(value, phase->reg); + spin_unlock_irqrestore(phase->lock, flags); + + return 0; +} + +static const struct clk_ops mmc_clk_ops = { + .get_phase = mmc_get_phase, + .set_phase = mmc_set_phase, +}; + +static void __init sun4i_a10_mmc_phase_setup(struct device_node *node, + struct mmc_phase_data *data) +{ + const char *parent_names[1] = { of_clk_get_parent_name(node, 0) }; + struct clk_init_data init = { + .num_parents = 1, + .parent_names = parent_names, + .ops = &mmc_clk_ops, + }; + + struct mmc_phase *phase; + struct clk *clk; + + phase = kmalloc(sizeof(*phase), GFP_KERNEL); + if (!phase) + return; + + phase->hw.init = &init; + + phase->reg = of_iomap(node, 0); + if (!phase->reg) + goto err_free; + + phase->data = data; + phase->lock = &sun4i_a10_mod0_lock; + + if (of_property_read_string(node, "clock-output-names", &init.name)) + init.name = node->name; + + clk = clk_register(NULL, &phase->hw); + if (IS_ERR(clk)) + goto err_unmap; + + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return; + +err_unmap: + iounmap(phase->reg); +err_free: + kfree(phase); +} + + +static struct mmc_phase_data mmc_output_clk = { + .offset = 8, +}; + +static struct mmc_phase_data mmc_sample_clk = { + .offset = 20, +}; + +static void __init sun4i_a10_mmc_output_setup(struct device_node *node) +{ + sun4i_a10_mmc_phase_setup(node, &mmc_output_clk); +} +CLK_OF_DECLARE(sun4i_a10_mmc_output, "allwinner,sun4i-a10-mmc-output-clk", sun4i_a10_mmc_output_setup); + +static void __init sun4i_a10_mmc_sample_setup(struct device_node *node) +{ + sun4i_a10_mmc_phase_setup(node, &mmc_sample_clk); +} +CLK_OF_DECLARE(sun4i_a10_mmc_sample, "allwinner,sun4i-a10-mmc-sample-clk", sun4i_a10_mmc_sample_setup); -- cgit v1.1 From 9c8176bfb67f98ed9a521b624dcb6ab7fa254aa7 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 16 Sep 2014 18:04:01 +0800 Subject: clk: sunxi: Add sun8i MBUS clock support The MBUS clock on sun8i is slightly different from the old mod0 clocks. The divider is 3 bits wider, while also needing a divider table for the higher 4 values, which all set the same divider. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- Documentation/devicetree/bindings/clock/sunxi.txt | 1 + drivers/clk/sunxi/Makefile | 1 + drivers/clk/sunxi/clk-sun8i-mbus.c | 78 +++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 drivers/clk/sunxi/clk-sun8i-mbus.c diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index eb690ed..ed116df 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -50,6 +50,7 @@ Required properties: "allwinner,sun4i-a10-mmc-output-clk" - for the MMC output clock on A10 "allwinner,sun4i-a10-mmc-sample-clk" - for the MMC sample clock on A10 "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks + "allwinner,sun8i-a23-mbus-clk" - for the MBUS clock on A23 "allwinner,sun7i-a20-out-clk" - for the external output clocks "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31 "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20 diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 833f086d..7ddc2b5 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -6,6 +6,7 @@ obj-y += clk-sunxi.o clk-factors.o obj-y += clk-a10-hosc.o obj-y += clk-a20-gmac.o obj-y += clk-mod0.o +obj-y += clk-sun8i-mbus.o obj-$(CONFIG_MFD_SUN6I_PRCM) += \ clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \ diff --git a/drivers/clk/sunxi/clk-sun8i-mbus.c b/drivers/clk/sunxi/clk-sun8i-mbus.c new file mode 100644 index 0000000..8e49b44 --- /dev/null +++ b/drivers/clk/sunxi/clk-sun8i-mbus.c @@ -0,0 +1,78 @@ +/* + * Copyright 2014 Chen-Yu Tsai + * + * Chen-Yu Tsai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include + +#include "clk-factors.h" + +/** + * sun8i_a23_get_mbus_factors() - calculates m factor for MBUS clocks + * MBUS rate is calculated as follows + * rate = parent_rate / (m + 1); + */ + +static void sun8i_a23_get_mbus_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 div; + + /* + * These clocks can only divide, so we will never be able to + * achieve frequencies higher than the parent frequency + */ + if (*freq > parent_rate) + *freq = parent_rate; + + div = DIV_ROUND_UP(parent_rate, *freq); + + if (div > 8) + div = 8; + + *freq = parent_rate / div; + + /* we were called to round the frequency, we can now return */ + if (m == NULL) + return; + + *m = div - 1; +} + +static struct clk_factors_config sun8i_a23_mbus_config = { + .mshift = 0, + .mwidth = 3, +}; + +static const struct factors_data sun8i_a23_mbus_data __initconst = { + .enable = 31, + .mux = 24, + .table = &sun8i_a23_mbus_config, + .getter = sun8i_a23_get_mbus_factors, +}; + +static DEFINE_SPINLOCK(sun8i_a23_mbus_lock); + +static void __init sun8i_a23_mbus_setup(struct device_node *node) +{ + struct clk *mbus = sunxi_factors_register(node, &sun8i_a23_mbus_data, + &sun8i_a23_mbus_lock); + + /* The MBUS clocks needs to be always enabled */ + __clk_get(mbus); + clk_prepare_enable(mbus); +} +CLK_OF_DECLARE(sun8i_a23_mbus, "allwinner,sun8i-a23-mbus-clk", sun8i_a23_mbus_setup); -- cgit v1.1