From 107f3198fd2c5902b9cc54c86a0c86f815c173e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Sat, 14 Sep 2013 21:37:59 -0300 Subject: [PATCH 1/5] clk: composite: .determine_rate support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds .determine_rate support to the composite clock. It will use the .determine_rate callback from the rate component if available, and fall back on the mux component otherwise. This allows composite clocks to enjoy the benefits of automatic clock reparenting. Signed-off-by: Emilio López Signed-off-by: Maxime Ripard --- drivers/clk/clk-composite.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index a33f46f20a415..753d0b7847622 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -55,6 +55,30 @@ static unsigned long clk_composite_recalc_rate(struct clk_hw *hw, return rate_ops->recalc_rate(rate_hw, parent_rate); } +static long clk_composite_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_p) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *rate_ops = composite->rate_ops; + const struct clk_ops *mux_ops = composite->mux_ops; + struct clk_hw *rate_hw = composite->rate_hw; + struct clk_hw *mux_hw = composite->mux_hw; + + if (rate_hw && rate_ops && rate_ops->determine_rate) { + rate_hw->clk = hw->clk; + return rate_ops->determine_rate(rate_hw, rate, best_parent_rate, + best_parent_p); + } else if (mux_hw && mux_ops && mux_ops->determine_rate) { + mux_hw->clk = hw->clk; + return mux_ops->determine_rate(rate_hw, rate, best_parent_rate, + best_parent_p); + } else { + pr_err("clk: clk_composite_determine_rate function called, but no mux or rate callback set!\n"); + return 0; + } +} + static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { @@ -147,6 +171,8 @@ struct clk *clk_register_composite(struct device *dev, const char *name, composite->mux_ops = mux_ops; clk_composite_ops->get_parent = clk_composite_get_parent; clk_composite_ops->set_parent = clk_composite_set_parent; + if (mux_ops->determine_rate) + clk_composite_ops->determine_rate = clk_composite_determine_rate; } if (rate_hw && rate_ops) { @@ -170,6 +196,8 @@ struct clk *clk_register_composite(struct device *dev, const char *name, composite->rate_hw = rate_hw; composite->rate_ops = rate_ops; clk_composite_ops->recalc_rate = clk_composite_recalc_rate; + if (rate_ops->determine_rate) + clk_composite_ops->determine_rate = clk_composite_determine_rate; } if (gate_hw && gate_ops) { From c518e84c76e12e12a2a3404a293b2465af9be5f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Fri, 20 Sep 2013 22:03:10 -0300 Subject: [PATCH 2/5] clk: sunxi: factors: fix off-by-one masks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous code would generate one bit too long masks, and was needlessly complicated. This patch replaces it by simpler code that can generate the masks correctly. Signed-off-by: Emilio López Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-factors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 88523f91d9b70..5687ac9bd85a8 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -40,7 +40,7 @@ struct clk_factors { #define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) -#define SETMASK(len, pos) (((-1U) >> (31-len)) << (pos)) +#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)) From 12ef06afb3959e7f6f2ae8917c4750883a3e8c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Fri, 20 Sep 2013 22:03:11 -0300 Subject: [PATCH 3/5] clk: sunxi: factors: clear variables before using them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Random bits may get into our factors if we don't clear n, k, m and p. Signed-off-by: Emilio López Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-factors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 5687ac9bd85a8..f05207a27e5f8 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -88,7 +88,7 @@ static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate, static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - u8 n, k, m, p; + u8 n = 0, k = 0, m = 0, p = 0; u32 reg; struct clk_factors *factors = to_clk_factors(hw); struct clk_factors_config *config = factors->config; From 8e6a4c40bb6f3866548811f9f3882a627293fc2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Fri, 20 Sep 2013 22:03:12 -0300 Subject: [PATCH 4/5] clk: sunxi: protect core clocks from accidental shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some important clocks may get disabled as a side effect of another clock being disabled, because they have no consumers. This patch implements a mechanism so those clocks can be claimed by the driver and therefore remain enabled at all times. Signed-off-by: Emilio López Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 34ee69f4d50c5..83d3eace3ebc7 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -617,6 +617,31 @@ static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_mat } } +/** + * System clock protection + * + * By enabling these critical clocks, we prevent their accidental gating + * by the framework + */ +static void __init sunxi_clock_protect(void) +{ + struct clk *clk; + + /* memory bus clock - sun5i+ */ + clk = clk_get(NULL, "mbus"); + if (!IS_ERR(clk)) { + clk_prepare_enable(clk); + clk_put(clk); + } + + /* DDR clock - sun4i+ */ + clk = clk_get(NULL, "pll5_ddr"); + if (!IS_ERR(clk)) { + clk_prepare_enable(clk); + clk_put(clk); + } +} + void __init sunxi_init_clocks(void) { /* Register all the simple and basic clocks on DT */ @@ -633,4 +658,7 @@ void __init sunxi_init_clocks(void) /* Register gate clocks */ of_sunxi_table_clock_setup(clk_gates_match, sunxi_gates_clk_setup); + + /* Enable core system clocks */ + sunxi_clock_protect(); } From e71c69fc3362b88b09194d486dda6d721a8004f6 Mon Sep 17 00:00:00 2001 From: "Victor N. Ramos Mello" Date: Fri, 18 Oct 2013 20:27:51 -0300 Subject: [PATCH 5/5] drivers: clk: sunxi: Fix memory leakage in clk-sunxi.c Fix a possible memory leak in sun4i_osc_clk_setup(). Moved clock-frequency check to save superfluous allocation. Signed-off-by: Victor N. Ramos Mello Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 83d3eace3ebc7..9665cb8d02387 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -38,18 +38,16 @@ static void __init sun4i_osc_clk_setup(struct device_node *node) const char *clk_name = node->name; u32 rate; + if (of_property_read_u32(node, "clock-frequency", &rate)) + return; + /* allocate fixed-rate and gate clock structs */ fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL); if (!fixed) return; gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); - if (!gate) { - kfree(fixed); - return; - } - - if (of_property_read_u32(node, "clock-frequency", &rate)) - return; + if (!gate) + goto err_free_fixed; /* set up gate and fixed rate properties */ gate->reg = of_iomap(node, 0); @@ -64,10 +62,18 @@ static void __init sun4i_osc_clk_setup(struct device_node *node) &gate->hw, &clk_gate_ops, CLK_IS_ROOT); - if (!IS_ERR(clk)) { - of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); - } + if (IS_ERR(clk)) + goto err_free_gate; + + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + + return; + +err_free_gate: + kfree(gate); +err_free_fixed: + kfree(fixed); } CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-osc-clk", sun4i_osc_clk_setup);