From 6f31ef2c5c3487fc69c2a34573db88b2f46498ba Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 8 Jan 2016 17:14:42 -0800 Subject: clk: sunxi: Drop clk.h include This file is a clock provider, not a clk consumer. Drop the clk.h include. Cc: Jens Kuske Cc: Maxime Ripard Signed-off-by: Stephen Boyd Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sun8i-bus-gates.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sun8i-bus-gates.c b/drivers/clk/sunxi/clk-sun8i-bus-gates.c index e32d18b..1113eb9 100644 --- a/drivers/clk/sunxi/clk-sun8i-bus-gates.c +++ b/drivers/clk/sunxi/clk-sun8i-bus-gates.c @@ -17,7 +17,6 @@ * GNU General Public License for more details. */ -#include #include #include #include -- cgit v1.1 From 63d8c7b9c32004506516f498fa05881c8d258442 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 25 Jan 2016 13:55:00 +0800 Subject: clk: sunxi: usb: Sort clk providers by chip family and name The latest addition of H3 USB clocks placed them at the bottom. Move it before A80 (sun9i), so they are sorted by SoC family then name. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-usb.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-usb.c b/drivers/clk/sunxi/clk-usb.c index 67b8e38..5432b1c 100644 --- a/drivers/clk/sunxi/clk-usb.c +++ b/drivers/clk/sunxi/clk-usb.c @@ -216,6 +216,18 @@ static void __init sun8i_a23_usb_setup(struct device_node *node) } CLK_OF_DECLARE(sun8i_a23_usb, "allwinner,sun8i-a23-usb-clk", sun8i_a23_usb_setup); +static const struct usb_clk_data sun8i_h3_usb_clk_data __initconst = { + .clk_mask = BIT(19) | BIT(18) | BIT(17) | BIT(16) | + BIT(11) | BIT(10) | BIT(9) | BIT(8), + .reset_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0), +}; + +static void __init sun8i_h3_usb_setup(struct device_node *node) +{ + sunxi_usb_clk_setup(node, &sun8i_h3_usb_clk_data, &sun4i_a10_usb_lock); +} +CLK_OF_DECLARE(sun8i_h3_usb, "allwinner,sun8i-h3-usb-clk", sun8i_h3_usb_setup); + static const struct usb_clk_data sun9i_a80_usb_mod_data __initconst = { .clk_mask = BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1), .reset_mask = BIT(19) | BIT(18) | BIT(17), @@ -243,15 +255,3 @@ static void __init sun9i_a80_usb_phy_setup(struct device_node *node) sunxi_usb_clk_setup(node, &sun9i_a80_usb_phy_data, &a80_usb_phy_lock); } CLK_OF_DECLARE(sun9i_a80_usb_phy, "allwinner,sun9i-a80-usb-phy-clk", sun9i_a80_usb_phy_setup); - -static const struct usb_clk_data sun8i_h3_usb_clk_data __initconst = { - .clk_mask = BIT(19) | BIT(18) | BIT(17) | BIT(16) | - BIT(11) | BIT(10) | BIT(9) | BIT(8), - .reset_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0), -}; - -static void __init sun8i_h3_usb_setup(struct device_node *node) -{ - sunxi_usb_clk_setup(node, &sun8i_h3_usb_clk_data, &sun4i_a10_usb_lock); -} -CLK_OF_DECLARE(sun8i_h3_usb, "allwinner,sun8i-h3-usb-clk", sun8i_h3_usb_setup); -- cgit v1.1 From b3e919e03ce84d9768c0e1c12066f502c5bbd485 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 25 Jan 2016 21:15:38 +0800 Subject: clk: sunxi: factors: Make struct clk_factors_config table const struct clk_factors_config contains shifts/widths for the factors of the factors clk. This is used to read out the factors from the register value. In no case is it written to, so make it const. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-factors.c | 4 ++-- drivers/clk/sunxi/clk-factors.h | 4 ++-- drivers/clk/sunxi/clk-mod0.c | 2 +- drivers/clk/sunxi/clk-sun9i-core.c | 8 ++++---- drivers/clk/sunxi/clk-sunxi.c | 16 ++++++++-------- 5 files changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 59428db..928c079 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -48,7 +48,7 @@ static unsigned long clk_factors_recalc_rate(struct clk_hw *hw, u32 reg; unsigned long rate; struct clk_factors *factors = to_clk_factors(hw); - struct clk_factors_config *config = factors->config; + const struct clk_factors_config *config = factors->config; /* Fetch the register value */ reg = readl(factors->reg); @@ -123,7 +123,7 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, 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; + const struct clk_factors_config *config = factors->config; unsigned long flags = 0; factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p); diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h index 171085a..060319b 100644 --- a/drivers/clk/sunxi/clk-factors.h +++ b/drivers/clk/sunxi/clk-factors.h @@ -23,7 +23,7 @@ struct factors_data { int enable; int mux; int muxmask; - struct clk_factors_config *table; + const struct clk_factors_config *table; void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); const char *name; }; @@ -31,7 +31,7 @@ struct factors_data { struct clk_factors { struct clk_hw hw; void __iomem *reg; - struct clk_factors_config *config; + const struct clk_factors_config *config; void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p); spinlock_t *lock; }; diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c index d167e1e..c67fea1 100644 --- a/drivers/clk/sunxi/clk-mod0.c +++ b/drivers/clk/sunxi/clk-mod0.c @@ -62,7 +62,7 @@ static void sun4i_a10_get_mod0_factors(u32 *freq, u32 parent_rate, } /* user manual says "n" but it's really "p" */ -static struct clk_factors_config sun4i_a10_mod0_config = { +static const struct clk_factors_config sun4i_a10_mod0_config = { .mshift = 0, .mwidth = 4, .pshift = 16, diff --git a/drivers/clk/sunxi/clk-sun9i-core.c b/drivers/clk/sunxi/clk-sun9i-core.c index 6c4c983..37c85ca 100644 --- a/drivers/clk/sunxi/clk-sun9i-core.c +++ b/drivers/clk/sunxi/clk-sun9i-core.c @@ -71,7 +71,7 @@ static void sun9i_a80_get_pll4_factors(u32 *freq, u32 parent_rate, *p_ret = p; } -static struct clk_factors_config sun9i_a80_pll4_config = { +static const struct clk_factors_config sun9i_a80_pll4_config = { .mshift = 18, .mwidth = 1, .nshift = 8, @@ -134,7 +134,7 @@ static void sun9i_a80_get_gt_factors(u32 *freq, u32 parent_rate, *m = div; } -static struct clk_factors_config sun9i_a80_gt_config = { +static const struct clk_factors_config sun9i_a80_gt_config = { .mshift = 0, .mwidth = 2, }; @@ -199,7 +199,7 @@ static void sun9i_a80_get_ahb_factors(u32 *freq, u32 parent_rate, *p = _p; } -static struct clk_factors_config sun9i_a80_ahb_config = { +static const struct clk_factors_config sun9i_a80_ahb_config = { .pshift = 0, .pwidth = 2, }; @@ -289,7 +289,7 @@ static void sun9i_a80_get_apb1_factors(u32 *freq, u32 parent_rate, *p = calcp; } -static struct clk_factors_config sun9i_a80_apb1_config = { +static const struct clk_factors_config sun9i_a80_apb1_config = { .mshift = 0, .mwidth = 5, .pshift = 16, diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 5ba2188..3609f08 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -608,7 +608,7 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate, * sunxi_factors_clk_setup() - Setup function for factor clocks */ -static struct clk_factors_config sun4i_pll1_config = { +static const struct clk_factors_config sun4i_pll1_config = { .nshift = 8, .nwidth = 5, .kshift = 4, @@ -619,7 +619,7 @@ static struct clk_factors_config sun4i_pll1_config = { .pwidth = 2, }; -static struct clk_factors_config sun6i_a31_pll1_config = { +static const struct clk_factors_config sun6i_a31_pll1_config = { .nshift = 8, .nwidth = 5, .kshift = 4, @@ -629,7 +629,7 @@ static struct clk_factors_config sun6i_a31_pll1_config = { .n_start = 1, }; -static struct clk_factors_config sun8i_a23_pll1_config = { +static const struct clk_factors_config sun8i_a23_pll1_config = { .nshift = 8, .nwidth = 5, .kshift = 4, @@ -641,14 +641,14 @@ static struct clk_factors_config sun8i_a23_pll1_config = { .n_start = 1, }; -static struct clk_factors_config sun4i_pll5_config = { +static const struct clk_factors_config sun4i_pll5_config = { .nshift = 8, .nwidth = 5, .kshift = 4, .kwidth = 2, }; -static struct clk_factors_config sun6i_a31_pll6_config = { +static const struct clk_factors_config sun6i_a31_pll6_config = { .nshift = 8, .nwidth = 5, .kshift = 4, @@ -656,12 +656,12 @@ static struct clk_factors_config sun6i_a31_pll6_config = { .n_start = 1, }; -static struct clk_factors_config sun5i_a13_ahb_config = { +static const struct clk_factors_config sun5i_a13_ahb_config = { .pshift = 4, .pwidth = 2, }; -static struct clk_factors_config sun4i_apb1_config = { +static const struct clk_factors_config sun4i_apb1_config = { .mshift = 0, .mwidth = 5, .pshift = 16, @@ -669,7 +669,7 @@ static struct clk_factors_config sun4i_apb1_config = { }; /* user manual says "n" but it's really "p" */ -static struct clk_factors_config sun7i_a20_out_config = { +static const struct clk_factors_config sun7i_a20_out_config = { .mshift = 8, .mwidth = 5, .pshift = 20, -- cgit v1.1 From 78ca95c76900e7587d6e076f8162a440f19431ae Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 25 Jan 2016 21:15:39 +0800 Subject: clk: sunxi: factors: Add clk cleanup in sunxi_factors_register() error path sunxi_factors_register() does not check for failures or cleanup after clk_register_composite() or other clk-related calls. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-factors.c | 44 +++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 15 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 928c079..3a18abf 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -172,7 +172,7 @@ struct clk *sunxi_factors_register(struct device_node *node, struct clk_hw *mux_hw = NULL; const char *clk_name = node->name; const char *parents[FACTORS_MAX_PARENTS]; - int i = 0; + int ret, i = 0; /* if we have a mux, we will have >1 parents */ i = of_clk_parent_fill(node, parents, FACTORS_MAX_PARENTS); @@ -188,7 +188,7 @@ struct clk *sunxi_factors_register(struct device_node *node, factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); if (!factors) - return NULL; + goto err_factors; /* set up factors properties */ factors->reg = reg; @@ -199,10 +199,8 @@ struct clk *sunxi_factors_register(struct device_node *node, /* 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; - } + if (!gate) + goto err_gate; /* set up gate properties */ gate->reg = reg; @@ -214,11 +212,8 @@ struct clk *sunxi_factors_register(struct device_node *node, /* 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; - } + if (!mux) + goto err_mux; /* set up gate properties */ mux->reg = reg; @@ -233,11 +228,30 @@ struct clk *sunxi_factors_register(struct device_node *node, mux_hw, &clk_mux_ops, &factors->hw, &clk_factors_ops, gate_hw, &clk_gate_ops, 0); + if (IS_ERR(clk)) + goto err_register; - if (!IS_ERR(clk)) { - of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); - } + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (ret) + goto err_provider; + + ret = clk_register_clkdev(clk, clk_name, NULL); + if (ret) + goto err_clkdev; return clk; + +err_clkdev: + of_clk_del_provider(node); +err_provider: + /* TODO: The composite clock stuff will leak a bit here. */ + clk_unregister(clk); +err_register: + kfree(mux); +err_mux: + kfree(gate); +err_gate: + kfree(factors); +err_factors: + return NULL; } -- cgit v1.1 From 4cbeaebb8af1c86691d1a2d3328d82a01f4380a5 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 25 Jan 2016 21:15:40 +0800 Subject: clk: sunxi: factors: Add unregister function sunxi's factors clk did not have an unregister function. This means multiple structs were leaked whenever a factors clk was unregistered. Add an unregister function for it. Also keep pointers to the mux and gate structs so they can be freed. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-factors.c | 25 +++++++++++++++++++++++++ drivers/clk/sunxi/clk-factors.h | 5 +++++ 2 files changed, 30 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 3a18abf..a657117 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -202,6 +202,8 @@ struct clk *sunxi_factors_register(struct device_node *node, if (!gate) goto err_gate; + factors->gate = gate; + /* set up gate properties */ gate->reg = reg; gate->bit_idx = data->enable; @@ -215,6 +217,8 @@ struct clk *sunxi_factors_register(struct device_node *node, if (!mux) goto err_mux; + factors->mux = mux; + /* set up gate properties */ mux->reg = reg; mux->shift = data->mux; @@ -255,3 +259,24 @@ err_gate: err_factors: return NULL; } + +void sunxi_factors_unregister(struct device_node *node, struct clk *clk) +{ + struct clk_hw *hw = __clk_get_hw(clk); + struct clk_factors *factors; + const char *name; + + if (!hw) + return; + + factors = to_clk_factors(hw); + name = clk_hw_get_name(hw); + + /* No unregister call for clkdev_* */ + of_clk_del_provider(node); + /* TODO: The composite clock stuff will leak a bit here. */ + clk_unregister(clk); + kfree(factors->mux); + kfree(factors->gate); + kfree(factors); +} diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h index 060319b..7ea1379 100644 --- a/drivers/clk/sunxi/clk-factors.h +++ b/drivers/clk/sunxi/clk-factors.h @@ -34,6 +34,9 @@ struct clk_factors { const struct clk_factors_config *config; void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p); spinlock_t *lock; + /* for cleanup */ + struct clk_mux *mux; + struct clk_gate *gate; }; struct clk *sunxi_factors_register(struct device_node *node, @@ -41,4 +44,6 @@ struct clk *sunxi_factors_register(struct device_node *node, spinlock_t *lock, void __iomem *reg); +void sunxi_factors_unregister(struct device_node *node, struct clk *clk); + #endif -- cgit v1.1 From cfa63688603398e8de4315cd626f81516c88a4c4 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 25 Jan 2016 21:15:42 +0800 Subject: clk: sunxi: factors: Consolidate get_factors parameters into a struct The .get_factors callback of factors_clk has 6 parameters. To extend factors_clk in any way that requires adding parameters to .get_factors would make that list even longer, not to mention changing all the function declarations. Do this once now and consolidate all the parameters into a struct. Also drop the space before function pointer arguments, since checkpatch complains. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-factors.c | 24 ++-- drivers/clk/sunxi/clk-factors.h | 13 ++- drivers/clk/sunxi/clk-mod0.c | 20 ++-- drivers/clk/sunxi/clk-sun8i-mbus.c | 18 +-- drivers/clk/sunxi/clk-sun9i-core.c | 77 +++++-------- drivers/clk/sunxi/clk-sunxi.c | 222 +++++++++++++++---------------------- 6 files changed, 155 insertions(+), 219 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index a657117..4a8e36a 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -73,8 +73,13 @@ static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct clk_factors *factors = to_clk_factors(hw); - factors->get_factors((u32 *)&rate, (u32)*parent_rate, - NULL, NULL, NULL, NULL); + struct factors_request req = { + .rate = rate, + .parent_rate = *parent_rate, + }; + + factors->get_factors(&req); + return rate; } @@ -120,13 +125,16 @@ static int clk_factors_determine_rate(struct clk_hw *hw, static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - u8 n = 0, k = 0, m = 0, p = 0; + struct factors_request req = { + .rate = rate, + .parent_rate = parent_rate, + }; u32 reg; struct clk_factors *factors = to_clk_factors(hw); const struct clk_factors_config *config = factors->config; unsigned long flags = 0; - factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p); + factors->get_factors(&req); if (factors->lock) spin_lock_irqsave(factors->lock, flags); @@ -135,10 +143,10 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, reg = readl(factors->reg); /* Set up the new factors - macros do not do anything if width is 0 */ - reg = FACTOR_SET(config->nshift, config->nwidth, reg, n); - reg = FACTOR_SET(config->kshift, config->kwidth, reg, k); - reg = FACTOR_SET(config->mshift, config->mwidth, reg, m); - reg = FACTOR_SET(config->pshift, config->pwidth, reg, p); + reg = FACTOR_SET(config->nshift, config->nwidth, reg, req.n); + reg = FACTOR_SET(config->kshift, config->kwidth, reg, req.k); + reg = FACTOR_SET(config->mshift, config->mwidth, reg, req.m); + reg = FACTOR_SET(config->pshift, config->pwidth, reg, req.p); /* Apply them now */ writel(reg, factors->reg); diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h index 7ea1379..f09d7c2 100644 --- a/drivers/clk/sunxi/clk-factors.h +++ b/drivers/clk/sunxi/clk-factors.h @@ -19,12 +19,21 @@ struct clk_factors_config { u8 n_start; }; +struct factors_request { + unsigned long rate; + unsigned long parent_rate; + u8 n; + u8 k; + u8 m; + u8 p; +}; + struct factors_data { int enable; int mux; int muxmask; const struct clk_factors_config *table; - void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); + void (*getter)(struct factors_request *req); const char *name; }; @@ -32,7 +41,7 @@ struct clk_factors { struct clk_hw hw; void __iomem *reg; const struct clk_factors_config *config; - void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p); + void (*get_factors)(struct factors_request *req); spinlock_t *lock; /* for cleanup */ struct clk_mux *mux; diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c index c67fea1..578bff0 100644 --- a/drivers/clk/sunxi/clk-mod0.c +++ b/drivers/clk/sunxi/clk-mod0.c @@ -28,17 +28,16 @@ * 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) +static void sun4i_a10_get_mod0_factors(struct factors_request *req) { 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; + if (req->rate > req->parent_rate) + req->rate = req->parent_rate; - div = DIV_ROUND_UP(parent_rate, *freq); + div = DIV_ROUND_UP(req->parent_rate, req->rate); if (div < 16) calcp = 0; @@ -51,14 +50,9 @@ static void sun4i_a10_get_mod0_factors(u32 *freq, u32 parent_rate, 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; + req->rate = (req->parent_rate >> calcp) / calcm; + req->m = calcm - 1; + req->p = calcp; } /* user manual says "n" but it's really "p" */ diff --git a/drivers/clk/sunxi/clk-sun8i-mbus.c b/drivers/clk/sunxi/clk-sun8i-mbus.c index bf117a6..78683f0 100644 --- a/drivers/clk/sunxi/clk-sun8i-mbus.c +++ b/drivers/clk/sunxi/clk-sun8i-mbus.c @@ -26,8 +26,7 @@ * 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) +static void sun8i_a23_get_mbus_factors(struct factors_request *req) { u8 div; @@ -35,21 +34,16 @@ static void sun8i_a23_get_mbus_factors(u32 *freq, u32 parent_rate, * 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; + if (req->rate > req->parent_rate) + req->rate = req->parent_rate; - div = DIV_ROUND_UP(parent_rate, *freq); + div = DIV_ROUND_UP(req->parent_rate, req->rate); 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; + req->rate = req->parent_rate / div; + req->m = div - 1; } static struct clk_factors_config sun8i_a23_mbus_config = { diff --git a/drivers/clk/sunxi/clk-sun9i-core.c b/drivers/clk/sunxi/clk-sun9i-core.c index 37c85ca..a812110 100644 --- a/drivers/clk/sunxi/clk-sun9i-core.c +++ b/drivers/clk/sunxi/clk-sun9i-core.c @@ -32,15 +32,14 @@ * p and m are named div1 and div2 in Allwinner's SDK */ -static void sun9i_a80_get_pll4_factors(u32 *freq, u32 parent_rate, - u8 *n_ret, u8 *k, u8 *m_ret, u8 *p_ret) +static void sun9i_a80_get_pll4_factors(struct factors_request *req) { int n; int m = 1; int p = 1; /* Normalize value to a 6 MHz multiple (24 MHz / 4) */ - n = DIV_ROUND_UP(*freq, 6000000); + n = DIV_ROUND_UP(req->rate, 6000000); /* If n is too large switch to steps of 12 MHz */ if (n > 255) { @@ -60,15 +59,10 @@ static void sun9i_a80_get_pll4_factors(u32 *freq, u32 parent_rate, else if (n < 12) n = 12; - *freq = ((24000000 * n) >> p) / (m + 1); - - /* we were called to round the frequency, we can now return */ - if (n_ret == NULL) - return; - - *n_ret = n; - *m_ret = m; - *p_ret = p; + req->rate = ((24000000 * n) >> p) / (m + 1); + req->n = n; + req->m = m; + req->p = p; } static const struct clk_factors_config sun9i_a80_pll4_config = { @@ -111,27 +105,21 @@ CLK_OF_DECLARE(sun9i_a80_pll4, "allwinner,sun9i-a80-pll4-clk", sun9i_a80_pll4_se * rate = parent_rate / (m + 1); */ -static void sun9i_a80_get_gt_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) +static void sun9i_a80_get_gt_factors(struct factors_request *req) { u32 div; - if (parent_rate < *freq) - *freq = parent_rate; + if (req->parent_rate < req->rate) + req->rate = req->parent_rate; - div = DIV_ROUND_UP(parent_rate, *freq); + div = DIV_ROUND_UP(req->parent_rate, req->rate); /* maximum divider is 4 */ if (div > 4) div = 4; - *freq = parent_rate / div; - - /* we were called to round the frequency, we can now return */ - if (!m) - return; - - *m = div; + req->rate = req->parent_rate / div; + req->m = div; } static const struct clk_factors_config sun9i_a80_gt_config = { @@ -176,27 +164,21 @@ CLK_OF_DECLARE(sun9i_a80_gt, "allwinner,sun9i-a80-gt-clk", sun9i_a80_gt_setup); * rate = parent_rate >> p; */ -static void sun9i_a80_get_ahb_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) +static void sun9i_a80_get_ahb_factors(struct factors_request *req) { u32 _p; - if (parent_rate < *freq) - *freq = parent_rate; + if (req->parent_rate < req->rate) + req->rate = req->parent_rate; - _p = order_base_2(DIV_ROUND_UP(parent_rate, *freq)); + _p = order_base_2(DIV_ROUND_UP(req->parent_rate, req->rate)); /* maximum p is 3 */ if (_p > 3) _p = 3; - *freq = parent_rate >> _p; - - /* we were called to round the frequency, we can now return */ - if (!p) - return; - - *p = _p; + req->rate = req->parent_rate >> _p; + req->p = _p; } static const struct clk_factors_config sun9i_a80_ahb_config = { @@ -262,31 +244,22 @@ CLK_OF_DECLARE(sun9i_a80_apb0, "allwinner,sun9i-a80-apb0-clk", sun9i_a80_apb0_se * rate = (parent_rate >> p) / (m + 1); */ -static void sun9i_a80_get_apb1_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) +static void sun9i_a80_get_apb1_factors(struct factors_request *req) { u32 div; - u8 calcm, calcp; - if (parent_rate < *freq) - *freq = parent_rate; + if (req->parent_rate < req->rate) + req->rate = req->parent_rate; - div = DIV_ROUND_UP(parent_rate, *freq); + div = DIV_ROUND_UP(req->parent_rate, req->rate); /* Highest possible divider is 256 (p = 3, m = 31) */ if (div > 256) div = 256; - calcp = order_base_2(div); - calcm = (parent_rate >> calcp) - 1; - *freq = (parent_rate >> calcp) / (calcm + 1); - - /* we were called to round the frequency, we can now return */ - if (n == NULL) - return; - - *m = calcm; - *p = calcp; + req->p = order_base_2(div); + req->m = (req->parent_rate >> req->p) - 1; + req->rate = (req->parent_rate >> req->p) / (req->m + 1); } static const struct clk_factors_config sun9i_a80_apb1_config = { diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 3609f08..a57c36c 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -246,49 +246,45 @@ CLK_OF_DECLARE(sun6i_a31_ahb1, "allwinner,sun6i-a31-ahb1-clk", sun6i_ahb1_clk_se * parent_rate is always 24Mhz */ -static void sun4i_get_pll1_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) +static void sun4i_get_pll1_factors(struct factors_request *req) { u8 div; /* Normalize value to a 6M multiple */ - div = *freq / 6000000; - *freq = 6000000 * div; - - /* we were called to round the frequency, we can now return */ - if (n == NULL) - return; + div = req->rate / 6000000; + req->rate = 6000000 * div; /* m is always zero for pll1 */ - *m = 0; + req->m = 0; /* k is 1 only on these cases */ - if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000) - *k = 1; + if (req->rate >= 768000000 || req->rate == 42000000 || + req->rate == 54000000) + req->k = 1; else - *k = 0; + req->k = 0; /* p will be 3 for divs under 10 */ if (div < 10) - *p = 3; + req->p = 3; /* p will be 2 for divs between 10 - 20 and odd divs under 32 */ else if (div < 20 || (div < 32 && (div & 1))) - *p = 2; + req->p = 2; /* p will be 1 for even divs under 32, divs under 40 and odd pairs * of divs between 40-62 */ else if (div < 40 || (div < 64 && (div & 2))) - *p = 1; + req->p = 1; /* any other entries have p = 0 */ else - *p = 0; + req->p = 0; /* calculate a suitable n based on k and p */ - div <<= *p; - div /= (*k + 1); - *n = div / 4; + div <<= req->p; + div /= (req->k + 1); + req->n = div / 4; } /** @@ -297,15 +293,14 @@ static void sun4i_get_pll1_factors(u32 *freq, u32 parent_rate, * rate = parent_rate * (n + 1) * (k + 1) / (m + 1); * parent_rate should always be 24MHz */ -static void sun6i_a31_get_pll1_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) +static void sun6i_a31_get_pll1_factors(struct factors_request *req) { /* * We can operate only on MHz, this will make our life easier * later. */ - u32 freq_mhz = *freq / 1000000; - u32 parent_freq_mhz = parent_rate / 1000000; + u32 freq_mhz = req->rate / 1000000; + u32 parent_freq_mhz = req->parent_rate / 1000000; /* * Round down the frequency to the closest multiple of either @@ -319,28 +314,20 @@ static void sun6i_a31_get_pll1_factors(u32 *freq, u32 parent_rate, else freq_mhz = round_freq_16; - *freq = freq_mhz * 1000000; - - /* - * If the factors pointer are null, we were just called to - * round down the frequency. - * Exit. - */ - if (n == NULL) - return; + req->rate = freq_mhz * 1000000; /* If the frequency is a multiple of 32 MHz, k is always 3 */ if (!(freq_mhz % 32)) - *k = 3; + req->k = 3; /* If the frequency is a multiple of 9 MHz, k is always 2 */ else if (!(freq_mhz % 9)) - *k = 2; + req->k = 2; /* If the frequency is a multiple of 8 MHz, k is always 1 */ else if (!(freq_mhz % 8)) - *k = 1; + req->k = 1; /* Otherwise, we don't use the k factor */ else - *k = 0; + req->k = 0; /* * If the frequency is a multiple of 2 but not a multiple of @@ -351,27 +338,28 @@ static void sun6i_a31_get_pll1_factors(u32 *freq, u32 parent_rate, * somehow relates to this frequency. */ if ((freq_mhz % 6) == 2 || (freq_mhz % 6) == 4) - *m = 2; + req->m = 2; /* * If the frequency is a multiple of 6MHz, but the factor is * odd, m will be 3 */ else if ((freq_mhz / 6) & 1) - *m = 3; + req->m = 3; /* Otherwise, we end up with m = 1 */ else - *m = 1; + req->m = 1; /* Calculate n thanks to the above factors we already got */ - *n = freq_mhz * (*m + 1) / ((*k + 1) * parent_freq_mhz) - 1; + req->n = freq_mhz * (req->m + 1) / ((req->k + 1) * parent_freq_mhz) + - 1; /* * If n end up being outbound, and that we can still decrease * m, do it. */ - if ((*n + 1) > 31 && (*m + 1) > 1) { - *n = (*n + 1) / 2 - 1; - *m = (*m + 1) / 2 - 1; + if ((req->n + 1) > 31 && (req->m + 1) > 1) { + req->n = (req->n + 1) / 2 - 1; + req->m = (req->m + 1) / 2 - 1; } } @@ -382,45 +370,41 @@ static void sun6i_a31_get_pll1_factors(u32 *freq, u32 parent_rate, * parent_rate is always 24Mhz */ -static void sun8i_a23_get_pll1_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) +static void sun8i_a23_get_pll1_factors(struct factors_request *req) { u8 div; /* Normalize value to a 6M multiple */ - div = *freq / 6000000; - *freq = 6000000 * div; - - /* we were called to round the frequency, we can now return */ - if (n == NULL) - return; + div = req->rate / 6000000; + req->rate = 6000000 * div; /* m is always zero for pll1 */ - *m = 0; + req->m = 0; /* k is 1 only on these cases */ - if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000) - *k = 1; + if (req->rate >= 768000000 || req->rate == 42000000 || + req->rate == 54000000) + req->k = 1; else - *k = 0; + req->k = 0; /* p will be 2 for divs under 20 and odd divs under 32 */ if (div < 20 || (div < 32 && (div & 1))) - *p = 2; + req->p = 2; /* p will be 1 for even divs under 32, divs under 40 and odd pairs * of divs between 40-62 */ else if (div < 40 || (div < 64 && (div & 2))) - *p = 1; + req->p = 1; /* any other entries have p = 0 */ else - *p = 0; + req->p = 0; /* calculate a suitable n based on k and p */ - div <<= *p; - div /= (*k + 1); - *n = div / 4 - 1; + div <<= req->p; + div /= (req->k + 1); + req->n = div / 4 - 1; } /** @@ -430,29 +414,24 @@ static void sun8i_a23_get_pll1_factors(u32 *freq, u32 parent_rate, * parent_rate is always 24Mhz */ -static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) +static void sun4i_get_pll5_factors(struct factors_request *req) { u8 div; /* Normalize value to a parent_rate multiple (24M) */ - div = *freq / parent_rate; - *freq = parent_rate * div; - - /* we were called to round the frequency, we can now return */ - if (n == NULL) - return; + div = req->rate / req->parent_rate; + req->rate = req->parent_rate * div; if (div < 31) - *k = 0; + req->k = 0; else if (div / 2 < 31) - *k = 1; + req->k = 1; else if (div / 3 < 31) - *k = 2; + req->k = 2; else - *k = 3; + req->k = 3; - *n = DIV_ROUND_UP(div, (*k+1)); + req->n = DIV_ROUND_UP(div, (req->k + 1)); } /** @@ -462,24 +441,19 @@ static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate, * parent_rate is always 24Mhz */ -static void sun6i_a31_get_pll6_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) +static void sun6i_a31_get_pll6_factors(struct factors_request *req) { u8 div; /* Normalize value to a parent_rate multiple (24M) */ - div = *freq / parent_rate; - *freq = parent_rate * div; + div = req->rate / req->parent_rate; + req->rate = req->parent_rate * div; - /* we were called to round the frequency, we can now return */ - if (n == NULL) - return; - - *k = div / 32; - if (*k > 3) - *k = 3; + req->k = div / 32; + if (req->k > 3) + req->k = 3; - *n = DIV_ROUND_UP(div, (*k+1)) - 1; + req->n = DIV_ROUND_UP(div, (req->k + 1)) - 1; } /** @@ -488,37 +462,32 @@ static void sun6i_a31_get_pll6_factors(u32 *freq, u32 parent_rate, * rate = parent_rate >> p */ -static void sun5i_a13_get_ahb_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) +static void sun5i_a13_get_ahb_factors(struct factors_request *req) { u32 div; /* divide only */ - if (parent_rate < *freq) - *freq = parent_rate; + if (req->parent_rate < req->rate) + req->rate = req->parent_rate; /* * user manual says valid speed is 8k ~ 276M, but tests show it * can work at speeds up to 300M, just after reparenting to pll6 */ - if (*freq < 8000) - *freq = 8000; - if (*freq > 300000000) - *freq = 300000000; + if (req->rate < 8000) + req->rate = 8000; + if (req->rate > 300000000) + req->rate = 300000000; - div = order_base_2(DIV_ROUND_UP(parent_rate, *freq)); + div = order_base_2(DIV_ROUND_UP(req->parent_rate, req->rate)); /* p = 0 ~ 3 */ if (div > 3) div = 3; - *freq = parent_rate >> div; + req->rate = req->parent_rate >> div; - /* we were called to round the frequency, we can now return */ - if (p == NULL) - return; - - *p = div; + req->p = div; } /** @@ -527,39 +496,34 @@ static void sun5i_a13_get_ahb_factors(u32 *freq, u32 parent_rate, * rate = (parent_rate >> p) / (m + 1); */ -static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) +static void sun4i_get_apb1_factors(struct factors_request *req) { u8 calcm, calcp; + int div; - if (parent_rate < *freq) - *freq = parent_rate; + if (req->parent_rate < req->rate) + req->rate = req->parent_rate; - parent_rate = DIV_ROUND_UP(parent_rate, *freq); + div = DIV_ROUND_UP(req->parent_rate, req->rate); /* Invalid rate! */ - if (parent_rate > 32) + if (div > 32) return; - if (parent_rate <= 4) + if (div <= 4) calcp = 0; - else if (parent_rate <= 8) + else if (div <= 8) calcp = 1; - else if (parent_rate <= 16) + else if (div <= 16) calcp = 2; else calcp = 3; - calcm = (parent_rate >> calcp) - 1; - - *freq = (parent_rate >> calcp) / (calcm + 1); + calcm = (req->parent_rate >> calcp) - 1; - /* we were called to round the frequency, we can now return */ - if (n == NULL) - return; - - *m = calcm; - *p = calcp; + req->rate = (req->parent_rate >> calcp) / (calcm + 1); + req->m = calcm; + req->p = calcp; } @@ -571,17 +535,16 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate, * rate = (parent_rate >> p) / (m + 1); */ -static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) +static void sun7i_a20_get_out_factors(struct factors_request *req) { 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; + if (req->rate > req->parent_rate) + req->rate = req->parent_rate; - div = DIV_ROUND_UP(parent_rate, *freq); + div = DIV_ROUND_UP(req->parent_rate, req->rate); if (div < 32) calcp = 0; @@ -594,14 +557,9 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate, 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; + req->rate = (req->parent_rate >> calcp) / calcm; + req->m = calcm - 1; + req->p = calcp; } /** -- cgit v1.1 From 435b7be1d812cdbbad997f5279293770dff21abb Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 25 Jan 2016 21:15:43 +0800 Subject: clk: sunxi: factors: Support custom formulas Some clocks cannot be modelled using the standard factors clk formula, such as clocks with special pre-dividers on one parent, or clocks with all power-of-two dividers. Add support for a custom .recalc callback for factors clk. Also pass the current parent index to the .get_factor and .recalc callbacks. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-factors.c | 31 +++++++++++++++++++++++++++++-- drivers/clk/sunxi/clk-factors.h | 3 +++ 2 files changed, 32 insertions(+), 2 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 4a8e36a..2927b09 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -63,6 +63,26 @@ static unsigned long clk_factors_recalc_rate(struct clk_hw *hw, if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE) p = FACTOR_GET(config->pshift, config->pwidth, reg); + if (factors->recalc) { + struct factors_request factors_req = { + .parent_rate = parent_rate, + .n = n, + .k = k, + .m = m, + .p = p, + }; + + /* get mux details from mux clk structure */ + if (factors->mux) + factors_req.parent_index = + (reg >> factors->mux->shift) & + factors->mux->mask; + + factors->recalc(&factors_req); + + return factors_req.rate; + } + /* Calculate the rate */ rate = (parent_rate * (n + config->n_start) * (k + 1) >> p) / (m + 1); @@ -87,6 +107,7 @@ static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate, static int clk_factors_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { + struct clk_factors *factors = to_clk_factors(hw); struct clk_hw *parent, *best_parent = NULL; int i, num_parents; unsigned long parent_rate, best = 0, child_rate, best_child_rate = 0; @@ -94,6 +115,10 @@ static int clk_factors_determine_rate(struct clk_hw *hw, /* find the parent that can help provide the fastest rate <= rate */ num_parents = clk_hw_get_num_parents(hw); for (i = 0; i < num_parents; i++) { + struct factors_request factors_req = { + .rate = req->rate, + .parent_index = i, + }; parent = clk_hw_get_parent_by_index(hw, i); if (!parent) continue; @@ -102,8 +127,9 @@ static int clk_factors_determine_rate(struct clk_hw *hw, else parent_rate = clk_hw_get_rate(parent); - child_rate = clk_factors_round_rate(hw, req->rate, - &parent_rate); + factors_req.parent_rate = parent_rate; + factors->get_factors(&factors_req); + child_rate = factors_req.rate; if (child_rate <= req->rate && child_rate > best_child_rate) { best_parent = parent; @@ -202,6 +228,7 @@ struct clk *sunxi_factors_register(struct device_node *node, factors->reg = reg; factors->config = data->table; factors->get_factors = data->getter; + factors->recalc = data->recalc; factors->lock = lock; /* Add a gate if this factor clock can be gated */ diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h index f09d7c2..a44a865 100644 --- a/drivers/clk/sunxi/clk-factors.h +++ b/drivers/clk/sunxi/clk-factors.h @@ -22,6 +22,7 @@ struct clk_factors_config { struct factors_request { unsigned long rate; unsigned long parent_rate; + u8 parent_index; u8 n; u8 k; u8 m; @@ -34,6 +35,7 @@ struct factors_data { int muxmask; const struct clk_factors_config *table; void (*getter)(struct factors_request *req); + void (*recalc)(struct factors_request *req); const char *name; }; @@ -42,6 +44,7 @@ struct clk_factors { void __iomem *reg; const struct clk_factors_config *config; void (*get_factors)(struct factors_request *req); + void (*recalc)(struct factors_request *req); spinlock_t *lock; /* for cleanup */ struct clk_mux *mux; -- cgit v1.1 From 50d6ea47bab46645f28c439d9d8fb4840e10b486 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 25 Jan 2016 21:15:44 +0800 Subject: clk: sunxi: factors: Drop round_rate from clk ops The common clock framework requires either determine_rate or round_rate to be implemented. We use determine_rate so we can pass the parent index to the get_factors callback. This cannot be done easily with round_rate, so just drop it. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-factors.c | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 2927b09..6e97f46 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -89,21 +89,6 @@ static unsigned long clk_factors_recalc_rate(struct clk_hw *hw, return rate; } -static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) -{ - struct clk_factors *factors = to_clk_factors(hw); - struct factors_request req = { - .rate = rate, - .parent_rate = *parent_rate, - }; - - factors->get_factors(&req); - - - return rate; -} - static int clk_factors_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { @@ -189,7 +174,6 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, 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, }; -- cgit v1.1 From a78bb35552a800949b2bf68f372d3d6ccabdd790 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 25 Jan 2016 21:15:45 +0800 Subject: clk: sunxi: rewrite sun6i-a31-ahb1-clk using factors clk with custom recalc The factors clk implementation has been extended to support custom recalc callbacks to support clocks that use one factor for certain parents only, like a pre-divider. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 291 ++++++++++++------------------------------ 1 file changed, 83 insertions(+), 208 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index a57c36c..da15f2b 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -28,214 +28,6 @@ static DEFINE_SPINLOCK(clk_lock); -/** - * sun6i_a31_ahb1_clk_setup() - Setup function for a31 ahb1 composite clk - */ - -#define SUN6I_AHB1_MAX_PARENTS 4 -#define SUN6I_AHB1_MUX_PARENT_PLL6 3 -#define SUN6I_AHB1_MUX_SHIFT 12 -/* un-shifted mask is what mux_clk expects */ -#define SUN6I_AHB1_MUX_MASK 0x3 -#define SUN6I_AHB1_MUX_GET_PARENT(reg) ((reg >> SUN6I_AHB1_MUX_SHIFT) & \ - SUN6I_AHB1_MUX_MASK) - -#define SUN6I_AHB1_DIV_SHIFT 4 -#define SUN6I_AHB1_DIV_MASK (0x3 << SUN6I_AHB1_DIV_SHIFT) -#define SUN6I_AHB1_DIV_GET(reg) ((reg & SUN6I_AHB1_DIV_MASK) >> \ - SUN6I_AHB1_DIV_SHIFT) -#define SUN6I_AHB1_DIV_SET(reg, div) ((reg & ~SUN6I_AHB1_DIV_MASK) | \ - (div << SUN6I_AHB1_DIV_SHIFT)) -#define SUN6I_AHB1_PLL6_DIV_SHIFT 6 -#define SUN6I_AHB1_PLL6_DIV_MASK (0x3 << SUN6I_AHB1_PLL6_DIV_SHIFT) -#define SUN6I_AHB1_PLL6_DIV_GET(reg) ((reg & SUN6I_AHB1_PLL6_DIV_MASK) >> \ - SUN6I_AHB1_PLL6_DIV_SHIFT) -#define SUN6I_AHB1_PLL6_DIV_SET(reg, div) ((reg & ~SUN6I_AHB1_PLL6_DIV_MASK) | \ - (div << SUN6I_AHB1_PLL6_DIV_SHIFT)) - -struct sun6i_ahb1_clk { - struct clk_hw hw; - void __iomem *reg; -}; - -#define to_sun6i_ahb1_clk(_hw) container_of(_hw, struct sun6i_ahb1_clk, hw) - -static unsigned long sun6i_ahb1_clk_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct sun6i_ahb1_clk *ahb1 = to_sun6i_ahb1_clk(hw); - unsigned long rate; - u32 reg; - - /* Fetch the register value */ - reg = readl(ahb1->reg); - - /* apply pre-divider first if parent is pll6 */ - if (SUN6I_AHB1_MUX_GET_PARENT(reg) == SUN6I_AHB1_MUX_PARENT_PLL6) - parent_rate /= SUN6I_AHB1_PLL6_DIV_GET(reg) + 1; - - /* clk divider */ - rate = parent_rate >> SUN6I_AHB1_DIV_GET(reg); - - return rate; -} - -static long sun6i_ahb1_clk_round(unsigned long rate, u8 *divp, u8 *pre_divp, - u8 parent, unsigned long parent_rate) -{ - u8 div, calcp, calcm = 1; - - /* - * clock can only divide, so we will never be able to achieve - * frequencies higher than the parent frequency - */ - if (parent_rate && rate > parent_rate) - rate = parent_rate; - - div = DIV_ROUND_UP(parent_rate, rate); - - /* calculate pre-divider if parent is pll6 */ - if (parent == SUN6I_AHB1_MUX_PARENT_PLL6) { - if (div < 4) - calcp = 0; - else if (div / 2 < 4) - calcp = 1; - else if (div / 4 < 4) - calcp = 2; - else - calcp = 3; - - calcm = DIV_ROUND_UP(div, 1 << calcp); - } else { - calcp = __roundup_pow_of_two(div); - calcp = calcp > 3 ? 3 : calcp; - } - - /* we were asked to pass back divider values */ - if (divp) { - *divp = calcp; - *pre_divp = calcm - 1; - } - - return (parent_rate / calcm) >> calcp; -} - -static int sun6i_ahb1_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - struct clk_hw *parent, *best_parent = NULL; - int i, num_parents; - unsigned long parent_rate, best = 0, child_rate, best_child_rate = 0; - - /* find the parent that can help provide the fastest rate <= rate */ - num_parents = clk_hw_get_num_parents(hw); - for (i = 0; i < num_parents; i++) { - parent = clk_hw_get_parent_by_index(hw, i); - if (!parent) - continue; - if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) - parent_rate = clk_hw_round_rate(parent, req->rate); - else - parent_rate = clk_hw_get_rate(parent); - - child_rate = sun6i_ahb1_clk_round(req->rate, NULL, NULL, i, - parent_rate); - - if (child_rate <= req->rate && child_rate > best_child_rate) { - best_parent = parent; - best = parent_rate; - best_child_rate = child_rate; - } - } - - if (!best_parent) - return -EINVAL; - - req->best_parent_hw = best_parent; - req->best_parent_rate = best; - req->rate = best_child_rate; - - return 0; -} - -static int sun6i_ahb1_clk_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct sun6i_ahb1_clk *ahb1 = to_sun6i_ahb1_clk(hw); - unsigned long flags; - u8 div, pre_div, parent; - u32 reg; - - spin_lock_irqsave(&clk_lock, flags); - - reg = readl(ahb1->reg); - - /* need to know which parent is used to apply pre-divider */ - parent = SUN6I_AHB1_MUX_GET_PARENT(reg); - sun6i_ahb1_clk_round(rate, &div, &pre_div, parent, parent_rate); - - reg = SUN6I_AHB1_DIV_SET(reg, div); - reg = SUN6I_AHB1_PLL6_DIV_SET(reg, pre_div); - writel(reg, ahb1->reg); - - spin_unlock_irqrestore(&clk_lock, flags); - - return 0; -} - -static const struct clk_ops sun6i_ahb1_clk_ops = { - .determine_rate = sun6i_ahb1_clk_determine_rate, - .recalc_rate = sun6i_ahb1_clk_recalc_rate, - .set_rate = sun6i_ahb1_clk_set_rate, -}; - -static void __init sun6i_ahb1_clk_setup(struct device_node *node) -{ - struct clk *clk; - struct sun6i_ahb1_clk *ahb1; - struct clk_mux *mux; - const char *clk_name = node->name; - const char *parents[SUN6I_AHB1_MAX_PARENTS]; - void __iomem *reg; - int i; - - reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (IS_ERR(reg)) - return; - - /* we have a mux, we will have >1 parents */ - i = of_clk_parent_fill(node, parents, SUN6I_AHB1_MAX_PARENTS); - of_property_read_string(node, "clock-output-names", &clk_name); - - ahb1 = kzalloc(sizeof(struct sun6i_ahb1_clk), GFP_KERNEL); - if (!ahb1) - return; - - mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); - if (!mux) { - kfree(ahb1); - return; - } - - /* set up clock properties */ - mux->reg = reg; - mux->shift = SUN6I_AHB1_MUX_SHIFT; - mux->mask = SUN6I_AHB1_MUX_MASK; - mux->lock = &clk_lock; - ahb1->reg = reg; - - clk = clk_register_composite(NULL, clk_name, parents, i, - &mux->hw, &clk_mux_ops, - &ahb1->hw, &sun6i_ahb1_clk_ops, - NULL, NULL, 0); - - if (!IS_ERR(clk)) { - of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); - } -} -CLK_OF_DECLARE(sun6i_a31_ahb1, "allwinner,sun6i-a31-ahb1-clk", sun6i_ahb1_clk_setup); - /* Maximum number of parents our clocks have */ #define SUNXI_MAX_PARENTS 5 @@ -490,6 +282,68 @@ static void sun5i_a13_get_ahb_factors(struct factors_request *req) req->p = div; } +#define SUN6I_AHB1_PARENT_PLL6 3 + +/** + * sun6i_a31_get_ahb_factors() - calculates m, p factors for AHB + * AHB rate is calculated as follows + * rate = parent_rate >> p + * + * if parent is pll6, then + * parent_rate = pll6 rate / (m + 1) + */ + +static void sun6i_get_ahb1_factors(struct factors_request *req) +{ + u8 div, calcp, calcm = 1; + + /* + * clock can only divide, so we will never be able to achieve + * frequencies higher than the parent frequency + */ + if (req->parent_rate && req->rate > req->parent_rate) + req->rate = req->parent_rate; + + div = DIV_ROUND_UP(req->parent_rate, req->rate); + + /* calculate pre-divider if parent is pll6 */ + if (req->parent_index == SUN6I_AHB1_PARENT_PLL6) { + if (div < 4) + calcp = 0; + else if (div / 2 < 4) + calcp = 1; + else if (div / 4 < 4) + calcp = 2; + else + calcp = 3; + + calcm = DIV_ROUND_UP(div, 1 << calcp); + } else { + calcp = __roundup_pow_of_two(div); + calcp = calcp > 3 ? 3 : calcp; + } + + req->rate = (req->parent_rate / calcm) >> calcp; + req->p = calcp; + req->m = calcm - 1; +} + +/** + * sun6i_ahb1_recalc() - calculates AHB clock rate from m, p factors and + * parent index + */ +static void sun6i_ahb1_recalc(struct factors_request *req) +{ + req->rate = req->parent_rate; + + /* apply pre-divider first if parent is pll6 */ + if (req->parent_index == SUN6I_AHB1_PARENT_PLL6) + req->rate /= req->m + 1; + + /* clk divider */ + req->rate >>= req->p; +} + /** * sun4i_get_apb1_factors() - calculates m, p factors for APB1 * APB1 rate is calculated as follows @@ -619,6 +473,13 @@ static const struct clk_factors_config sun5i_a13_ahb_config = { .pwidth = 2, }; +static const struct clk_factors_config sun6i_ahb1_config = { + .mshift = 6, + .mwidth = 2, + .pshift = 4, + .pwidth = 2, +}; + static const struct clk_factors_config sun4i_apb1_config = { .mshift = 0, .mwidth = 5, @@ -686,6 +547,14 @@ static const struct factors_data sun5i_a13_ahb_data __initconst = { .getter = sun5i_a13_get_ahb_factors, }; +static const struct factors_data sun6i_ahb1_data __initconst = { + .mux = 12, + .muxmask = BIT(1) | BIT(0), + .table = &sun6i_ahb1_config, + .getter = sun6i_get_ahb1_factors, + .recalc = sun6i_ahb1_recalc, +}; + static const struct factors_data sun4i_apb1_data __initconst = { .mux = 24, .muxmask = BIT(1) | BIT(0), @@ -716,6 +585,12 @@ static struct clk * __init sunxi_factors_clk_setup(struct device_node *node, return sunxi_factors_register(node, data, &clk_lock, reg); } +static void __init sun6i_ahb1_clk_setup(struct device_node *node) +{ + sunxi_factors_clk_setup(node, &sun6i_ahb1_data); +} +CLK_OF_DECLARE(sun6i_a31_ahb1, "allwinner,sun6i-a31-ahb1-clk", + sun6i_ahb1_clk_setup); /** -- cgit v1.1 From 3ca2377b6fed7c3db0c064ea73327cc6895e175d Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 25 Jan 2016 21:15:46 +0800 Subject: clk: sunxi: rewrite sun6i-ar100 using factors clk sun6i's AR100 clock is a classic factors clk case: AR100 = ((parent mux) >> p) / (m + 1) Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sun6i-ar100.c | 235 ++++++++++-------------------------- 1 file changed, 61 insertions(+), 174 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sun6i-ar100.c b/drivers/clk/sunxi/clk-sun6i-ar100.c index 2088768..a7f5777 100644 --- a/drivers/clk/sunxi/clk-sun6i-ar100.c +++ b/drivers/clk/sunxi/clk-sun6i-ar100.c @@ -8,211 +8,97 @@ * */ +#include #include #include #include #include +#include -#define SUN6I_AR100_MAX_PARENTS 4 -#define SUN6I_AR100_SHIFT_MASK 0x3 -#define SUN6I_AR100_SHIFT_MAX SUN6I_AR100_SHIFT_MASK -#define SUN6I_AR100_SHIFT_SHIFT 4 -#define SUN6I_AR100_DIV_MASK 0x1f -#define SUN6I_AR100_DIV_MAX (SUN6I_AR100_DIV_MASK + 1) -#define SUN6I_AR100_DIV_SHIFT 8 -#define SUN6I_AR100_MUX_MASK 0x3 -#define SUN6I_AR100_MUX_SHIFT 16 - -struct ar100_clk { - struct clk_hw hw; - void __iomem *reg; -}; - -static inline struct ar100_clk *to_ar100_clk(struct clk_hw *hw) -{ - return container_of(hw, struct ar100_clk, hw); -} - -static unsigned long ar100_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct ar100_clk *clk = to_ar100_clk(hw); - u32 val = readl(clk->reg); - int shift = (val >> SUN6I_AR100_SHIFT_SHIFT) & SUN6I_AR100_SHIFT_MASK; - int div = (val >> SUN6I_AR100_DIV_SHIFT) & SUN6I_AR100_DIV_MASK; - - return (parent_rate >> shift) / (div + 1); -} - -static int ar100_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - int nparents = clk_hw_get_num_parents(hw); - long best_rate = -EINVAL; - int i; - - req->best_parent_hw = NULL; - - for (i = 0; i < nparents; i++) { - unsigned long parent_rate; - unsigned long tmp_rate; - struct clk_hw *parent; - unsigned long div; - int shift; - - parent = clk_hw_get_parent_by_index(hw, i); - parent_rate = clk_hw_get_rate(parent); - div = DIV_ROUND_UP(parent_rate, req->rate); - - /* - * The AR100 clk contains 2 divisors: - * - one power of 2 divisor - * - one regular divisor - * - * First check if we can safely shift (or divide by a power - * of 2) without losing precision on the requested rate. - */ - shift = ffs(div) - 1; - if (shift > SUN6I_AR100_SHIFT_MAX) - shift = SUN6I_AR100_SHIFT_MAX; - - div >>= shift; - - /* - * Then if the divisor is still bigger than what the HW - * actually supports, use a bigger shift (or power of 2 - * divider) value and accept to lose some precision. - */ - while (div > SUN6I_AR100_DIV_MAX) { - shift++; - div >>= 1; - if (shift > SUN6I_AR100_SHIFT_MAX) - break; - } - - /* - * If the shift value (or power of 2 divider) is bigger - * than what the HW actually support, skip this parent. - */ - if (shift > SUN6I_AR100_SHIFT_MAX) - continue; - - tmp_rate = (parent_rate >> shift) / div; - if (!req->best_parent_hw || tmp_rate > best_rate) { - req->best_parent_hw = parent; - req->best_parent_rate = parent_rate; - best_rate = tmp_rate; - } - } - - if (best_rate < 0) - return best_rate; - - req->rate = best_rate; - - return 0; -} - -static int ar100_set_parent(struct clk_hw *hw, u8 index) -{ - struct ar100_clk *clk = to_ar100_clk(hw); - u32 val = readl(clk->reg); - - if (index >= SUN6I_AR100_MAX_PARENTS) - return -EINVAL; - - val &= ~(SUN6I_AR100_MUX_MASK << SUN6I_AR100_MUX_SHIFT); - val |= (index << SUN6I_AR100_MUX_SHIFT); - writel(val, clk->reg); - - return 0; -} +#include "clk-factors.h" -static u8 ar100_get_parent(struct clk_hw *hw) -{ - struct ar100_clk *clk = to_ar100_clk(hw); - return (readl(clk->reg) >> SUN6I_AR100_MUX_SHIFT) & - SUN6I_AR100_MUX_MASK; -} - -static int ar100_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) +/** + * sun6i_get_ar100_factors - Calculates factors p, m for AR100 + * + * AR100 rate is calculated as follows + * rate = (parent_rate >> p) / (m + 1); + */ +static void sun6i_get_ar100_factors(struct factors_request *req) { - unsigned long div = parent_rate / rate; - struct ar100_clk *clk = to_ar100_clk(hw); - u32 val = readl(clk->reg); + unsigned long div; int shift; - if (parent_rate % rate) - return -EINVAL; + /* clock only divides */ + if (req->rate > req->parent_rate) + req->rate = req->parent_rate; - shift = ffs(div) - 1; - if (shift > SUN6I_AR100_SHIFT_MAX) - shift = SUN6I_AR100_SHIFT_MAX; + div = DIV_ROUND_UP(req->parent_rate, req->rate); - div >>= shift; + if (div < 32) + shift = 0; + else if (div >> 1 < 32) + shift = 1; + else if (div >> 2 < 32) + shift = 2; + else + shift = 3; - if (div > SUN6I_AR100_DIV_MAX) - return -EINVAL; + div >>= shift; - val &= ~((SUN6I_AR100_SHIFT_MASK << SUN6I_AR100_SHIFT_SHIFT) | - (SUN6I_AR100_DIV_MASK << SUN6I_AR100_DIV_SHIFT)); - val |= (shift << SUN6I_AR100_SHIFT_SHIFT) | - (div << SUN6I_AR100_DIV_SHIFT); - writel(val, clk->reg); + if (div > 32) + div = 32; - return 0; + req->rate = (req->parent_rate >> shift) / div; + req->m = div - 1; + req->p = shift; } -static struct clk_ops ar100_ops = { - .recalc_rate = ar100_recalc_rate, - .determine_rate = ar100_determine_rate, - .set_parent = ar100_set_parent, - .get_parent = ar100_get_parent, - .set_rate = ar100_set_rate, +static const struct clk_factors_config sun6i_ar100_config = { + .mwidth = 5, + .mshift = 8, + .pwidth = 2, + .pshift = 4, }; +static const struct factors_data sun6i_ar100_data __initconst = { + .mux = 16, + .muxmask = GENMASK(1, 0), + .table = &sun6i_ar100_config, + .getter = sun6i_get_ar100_factors, +}; + +static DEFINE_SPINLOCK(sun6i_ar100_lock); + static int sun6i_a31_ar100_clk_probe(struct platform_device *pdev) { - const char *parents[SUN6I_AR100_MAX_PARENTS]; struct device_node *np = pdev->dev.of_node; - const char *clk_name = np->name; - struct clk_init_data init; - struct ar100_clk *ar100; struct resource *r; + void __iomem *reg; struct clk *clk; - int nparents; - - ar100 = devm_kzalloc(&pdev->dev, sizeof(*ar100), GFP_KERNEL); - if (!ar100) - return -ENOMEM; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ar100->reg = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(ar100->reg)) - return PTR_ERR(ar100->reg); + reg = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(reg)) + return PTR_ERR(reg); - nparents = of_clk_get_parent_count(np); - if (nparents > SUN6I_AR100_MAX_PARENTS) - nparents = SUN6I_AR100_MAX_PARENTS; - - of_clk_parent_fill(np, parents, nparents); + clk = sunxi_factors_register(np, &sun6i_ar100_data, &sun6i_ar100_lock, + reg); + if (!clk) + return -ENOMEM; - of_property_read_string(np, "clock-output-names", &clk_name); + platform_set_drvdata(pdev, clk); - init.name = clk_name; - init.ops = &ar100_ops; - init.parent_names = parents; - init.num_parents = nparents; - init.flags = 0; + return 0; +} - ar100->hw.init = &init; +static int sun6i_a31_ar100_clk_remove(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct clk *clk = platform_get_drvdata(pdev); - clk = clk_register(&pdev->dev, &ar100->hw); - if (IS_ERR(clk)) - return PTR_ERR(clk); + sunxi_factors_unregister(np, clk); - return of_clk_add_provider(np, of_clk_src_simple_get, clk); + return 0; } static const struct of_device_id sun6i_a31_ar100_clk_dt_ids[] = { @@ -227,6 +113,7 @@ static struct platform_driver sun6i_a31_ar100_clk_driver = { .of_match_table = sun6i_a31_ar100_clk_dt_ids, }, .probe = sun6i_a31_ar100_clk_probe, + .remove = sun6i_a31_ar100_clk_remove, }; module_platform_driver(sun6i_a31_ar100_clk_driver); -- cgit v1.1 From 8f2bf2ad9673e187b5c2956497003f60e0885e5d Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 25 Jan 2016 21:15:47 +0800 Subject: clk: sunxi: rewrite sun8i-a23-mbus-clk using the simpler composite clk sun8i-a23-mbus-clk used sunxi's factors clk, which is nice for very complicated clocks, but is not really needed here. Convert sun8i-a23-mbus-clk to use clk_composite, as it is a gate + mux + divider. This makes the code easier to understand. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sun8i-mbus.c | 121 +++++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 45 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sun8i-mbus.c b/drivers/clk/sunxi/clk-sun8i-mbus.c index 78683f0..3aaa9cb 100644 --- a/drivers/clk/sunxi/clk-sun8i-mbus.c +++ b/drivers/clk/sunxi/clk-sun8i-mbus.c @@ -15,68 +15,99 @@ */ #include +#include #include +#include +#include #include -#include "clk-factors.h" +#define SUN8I_MBUS_ENABLE 31 +#define SUN8I_MBUS_MUX_SHIFT 24 +#define SUN8I_MBUS_MUX_MASK 0x3 +#define SUN8I_MBUS_DIV_SHIFT 0 +#define SUN8I_MBUS_DIV_WIDTH 3 +#define SUN8I_MBUS_MAX_PARENTS 4 -/** - * sun8i_a23_get_mbus_factors() - calculates m factor for MBUS clocks - * MBUS rate is calculated as follows - * rate = parent_rate / (m + 1); - */ +static DEFINE_SPINLOCK(sun8i_a23_mbus_lock); -static void sun8i_a23_get_mbus_factors(struct factors_request *req) +static void __init sun8i_a23_mbus_setup(struct device_node *node) { - u8 div; + int num_parents = of_clk_get_parent_count(node); + const char *parents[num_parents]; + const char *clk_name = node->name; + struct resource res; + struct clk_divider *div; + struct clk_gate *gate; + struct clk_mux *mux; + struct clk *clk; + void __iomem *reg; + int err; - /* - * These clocks can only divide, so we will never be able to - * achieve frequencies higher than the parent frequency - */ - if (req->rate > req->parent_rate) - req->rate = req->parent_rate; + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (!reg) { + pr_err("Could not get registers for sun8i-mbus-clk\n"); + return; + } - div = DIV_ROUND_UP(req->parent_rate, req->rate); + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + goto err_unmap; - if (div > 8) - div = 8; + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + goto err_free_div; - req->rate = req->parent_rate / div; - req->m = div - 1; -} + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + goto err_free_mux; -static struct clk_factors_config sun8i_a23_mbus_config = { - .mshift = 0, - .mwidth = 3, -}; + of_property_read_string(node, "clock-output-names", &clk_name); + of_clk_parent_fill(node, parents, num_parents); -static const struct factors_data sun8i_a23_mbus_data __initconst = { - .enable = 31, - .mux = 24, - .muxmask = BIT(1) | BIT(0), - .table = &sun8i_a23_mbus_config, - .getter = sun8i_a23_get_mbus_factors, -}; + gate->reg = reg; + gate->bit_idx = SUN8I_MBUS_ENABLE; + gate->lock = &sun8i_a23_mbus_lock; -static DEFINE_SPINLOCK(sun8i_a23_mbus_lock); + div->reg = reg; + div->shift = SUN8I_MBUS_DIV_SHIFT; + div->width = SUN8I_MBUS_DIV_WIDTH; + div->lock = &sun8i_a23_mbus_lock; -static void __init sun8i_a23_mbus_setup(struct device_node *node) -{ - struct clk *mbus; - void __iomem *reg; + mux->reg = reg; + mux->shift = SUN8I_MBUS_MUX_SHIFT; + mux->mask = SUN8I_MBUS_MUX_MASK; + mux->lock = &sun8i_a23_mbus_lock; - reg = of_iomap(node, 0); - if (!reg) { - pr_err("Could not get registers for a23-mbus-clk\n"); - return; - } + clk = clk_register_composite(NULL, clk_name, parents, num_parents, + &mux->hw, &clk_mux_ops, + &div->hw, &clk_divider_ops, + &gate->hw, &clk_gate_ops, + 0); + if (IS_ERR(clk)) + goto err_free_gate; - mbus = sunxi_factors_register(node, &sun8i_a23_mbus_data, - &sun8i_a23_mbus_lock, reg); + err = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (err) + goto err_unregister_clk; /* The MBUS clocks needs to be always enabled */ - __clk_get(mbus); - clk_prepare_enable(mbus); + __clk_get(clk); + clk_prepare_enable(clk); + + return; + +err_unregister_clk: + /* TODO: The composite clock stuff will leak a bit here. */ + clk_unregister(clk); +err_free_gate: + kfree(gate); +err_free_mux: + kfree(mux); +err_free_div: + kfree(div); +err_unmap: + iounmap(reg); + of_address_to_resource(node, 0, &res); + release_mem_region(res.start, resource_size(&res)); } CLK_OF_DECLARE(sun8i_a23_mbus, "allwinner,sun8i-a23-mbus-clk", sun8i_a23_mbus_setup); -- cgit v1.1 From 2d6f5f0cf6bfb17b8f0102cabe0665098ce0a865 Mon Sep 17 00:00:00 2001 From: Vishnu Patekar Date: Sun, 31 Jan 2016 09:20:54 +0800 Subject: clk: sunxi: Add apb0 gates for A83T APB0 is part of PRCM, and is compatible with earlier SOCs. apb0 gates controls R_PIO, R_UART, R_RSB, etc clocks. This patch adds support for APB0 gates for A83T. Signed-off-by: Vishnu Patekar Acked-by: Rob Herring Acked-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-simple-gates.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-simple-gates.c b/drivers/clk/sunxi/clk-simple-gates.c index f4da52b..2cfc5a8 100644 --- a/drivers/clk/sunxi/clk-simple-gates.c +++ b/drivers/clk/sunxi/clk-simple-gates.c @@ -130,6 +130,8 @@ CLK_OF_DECLARE(sun8i_a23_apb2, "allwinner,sun8i-a23-apb2-gates-clk", sunxi_simple_gates_init); CLK_OF_DECLARE(sun8i_a33_ahb1, "allwinner,sun8i-a33-ahb1-gates-clk", sunxi_simple_gates_init); +CLK_OF_DECLARE(sun8i_a83t_apb0, "allwinner,sun8i-a83t-apb0-gates-clk", + sunxi_simple_gates_init); CLK_OF_DECLARE(sun9i_a80_ahb0, "allwinner,sun9i-a80-ahb0-gates-clk", sunxi_simple_gates_init); CLK_OF_DECLARE(sun9i_a80_ahb1, "allwinner,sun9i-a80-ahb1-gates-clk", -- cgit v1.1 From be338e4c589935a95f09022566ec6c511c07bb8c Mon Sep 17 00:00:00 2001 From: Vishnu Patekar Date: Sun, 31 Jan 2016 09:20:55 +0800 Subject: clk: sunxi: add bus gates for A83T A83T has similar bus gates that of H3, including single gating register has different clock parent. As per H3 and A83T datasheet, usbhost is under AHB2. However,below shows allwinner source code assignment: bits: 26 (ehci0), 27 (ehci1), 29 (ohci0) => AHB1 for A83T. bits: 26 (ehci0), 27 (ehci1) => AHB1 for H3 bits 29, 30, 31(ohci0,1,2) => AHB2 for H3. until, this confusion is cleared keep it H3 way. Signed-off-by: Vishnu Patekar Acked-by: Rob Herring Acked-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sun8i-bus-gates.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sun8i-bus-gates.c b/drivers/clk/sunxi/clk-sun8i-bus-gates.c index 1113eb9..63fdb79 100644 --- a/drivers/clk/sunxi/clk-sun8i-bus-gates.c +++ b/drivers/clk/sunxi/clk-sun8i-bus-gates.c @@ -109,3 +109,5 @@ err_unmap: CLK_OF_DECLARE(sun8i_h3_bus_gates, "allwinner,sun8i-h3-bus-gates-clk", sun8i_h3_bus_gates_init); +CLK_OF_DECLARE(sun8i_a83t_bus_gates, "allwinner,sun8i-a83t-bus-gates-clk", + sun8i_h3_bus_gates_init); -- cgit v1.1 From 14b5a4bf7acd3c803c9bde857ae054ba19c4a04c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 2 Feb 2016 16:55:30 +0100 Subject: clk: sunxi: don't mark sun6i_ar100_data __initconst The clk-sun6i-ar100 clk driver is a platform driver that may use deferred probing, so its probe function must not access __init symbols. Kbuild warns about this: WARNING: drivers/clk/sunxi/built-in.o(.text+0x15f0): Section mismatch in reference from the function sun6i_a31_ar100_clk_probe() to the (unknown reference) .init.rodata:(unknown) The function sun6i_a31_ar100_clk_probe() references the (unknown reference) __initconst (unknown). This is often because sun6i_a31_ar100_clk_probe lacks a __initconst annotation or the annotation of (unknown) is wrong. Removing the __initconst annotation avoids the warning and makes deferred probing work. Signed-off-by: Arnd Bergmann Fixes: 3ca2377b6fed ("clk: sunxi: rewrite sun6i-ar100 using factors clk") Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sun6i-ar100.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sun6i-ar100.c b/drivers/clk/sunxi/clk-sun6i-ar100.c index a7f5777..84a187e 100644 --- a/drivers/clk/sunxi/clk-sun6i-ar100.c +++ b/drivers/clk/sunxi/clk-sun6i-ar100.c @@ -60,7 +60,7 @@ static const struct clk_factors_config sun6i_ar100_config = { .pshift = 4, }; -static const struct factors_data sun6i_ar100_data __initconst = { +static const struct factors_data sun6i_ar100_data = { .mux = 16, .muxmask = GENMASK(1, 0), .table = &sun6i_ar100_config, -- cgit v1.1 From d221b7a8781ce594249cb44644ec9fecd893957d Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 1 Feb 2016 17:39:27 +0000 Subject: clk: sunxi: improve error reporting for the mux clock clk_register_mux returns a pointer wrapped error value in case of failure, so a simple NULL check is not sufficient to catch errors. Fix that and elaborate on the failure reason on the way. The whole function does not return any error value, so silently failing may leave users scratching their heads because the kernel does not provide any clues on what's wrong. Signed-off-by: Andre Przywara Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index da15f2b..2524d6f 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -627,17 +627,29 @@ static void __init sunxi_mux_clk_setup(struct device_node *node, reg = of_iomap(node, 0); i = of_clk_parent_fill(node, parents, SUNXI_MAX_PARENTS); - of_property_read_string(node, "clock-output-names", &clk_name); + if (of_property_read_string(node, "clock-output-names", &clk_name)) { + pr_warn("%s: could not read clock-output-names for \"%s\"\n", + __func__, clk_name); + goto out_unmap; + } clk = clk_register_mux(NULL, clk_name, parents, i, CLK_SET_RATE_PARENT, reg, data->shift, SUNXI_MUX_GATE_WIDTH, 0, &clk_lock); - if (clk) { - of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); + if (IS_ERR(clk)) { + pr_warn("%s: failed to register mux clock %s: %ld\n", __func__, + clk_name, PTR_ERR(clk)); + goto out_unmap; } + + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + return; + +out_unmap: + iounmap(reg); } -- cgit v1.1 From 96f185ac9a90264463254366f45d7b46482eec96 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 2 Feb 2016 09:47:10 +0100 Subject: clk: sunxi: Make clocks setup functions return their clock The clocks registration code in clk-sunxi was most of the time not returning the struct clk (or struct clk array) that was registered, preventing the users of such functions to manipulate it, for example to protect it. Make them return it so that we can start using it. Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 2524d6f..f777b4f 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -615,8 +615,8 @@ static const struct mux_data sun8i_h3_ahb2_mux_data __initconst = { .shift = 0, }; -static void __init sunxi_mux_clk_setup(struct device_node *node, - struct mux_data *data) +static struct clk * __init sunxi_mux_clk_setup(struct device_node *node, + struct mux_data *data) { struct clk *clk; const char *clk_name = node->name; @@ -646,10 +646,12 @@ static void __init sunxi_mux_clk_setup(struct device_node *node, of_clk_add_provider(node, of_clk_src_simple_get, clk); clk_register_clkdev(clk, clk_name, NULL); - return; + + return clk; out_unmap: iounmap(reg); + return NULL; } @@ -820,8 +822,8 @@ static const struct divs_data sun6i_a31_pll6_divs_data __initconst = { * |________________________| */ -static void __init sunxi_divs_clk_setup(struct device_node *node, - struct divs_data *data) +static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node, + struct divs_data *data) { struct clk_onecell_data *clk_data; const char *parent; @@ -848,7 +850,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); if (!clk_data) - return; + return NULL; clks = kcalloc(ndivs, sizeof(*clks), GFP_KERNEL); if (!clks) @@ -934,7 +936,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); - return; + return clks; free_gate: kfree(gate); @@ -942,6 +944,7 @@ free_clks: kfree(clks); free_clkdata: kfree(clk_data); + return NULL; } -- cgit v1.1 From 5b5226d17df6175f1f0dd1a1a37e36770ed00116 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 2 Feb 2016 09:47:11 +0100 Subject: clk: sunxi: Make clocks setup functions take const pointer All the data structure that we pass to the clocks setup functions are declared const, while our setup functions expects a regular pointer. This was hidden by the fact that we cast a void * pointer back to these structures, which made it go unnoticed. Fix the functions prototype. Acked-by: Chen-Yu Tsai Reviewed-by: Andre Przywara Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index f777b4f..77d91e3 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -616,7 +616,7 @@ static const struct mux_data sun8i_h3_ahb2_mux_data __initconst = { }; static struct clk * __init sunxi_mux_clk_setup(struct device_node *node, - struct mux_data *data) + const struct mux_data *data) { struct clk *clk; const char *clk_name = node->name; @@ -712,7 +712,7 @@ static const struct div_data sun4i_apb0_data __initconst = { }; static void __init sunxi_divider_clk_setup(struct device_node *node, - struct div_data *data) + const struct div_data *data) { struct clk *clk; const char *clk_name = node->name; @@ -823,7 +823,7 @@ static const struct divs_data sun6i_a31_pll6_divs_data __initconst = { */ static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node, - struct divs_data *data) + const struct divs_data *data) { struct clk_onecell_data *clk_data; const char *parent; -- cgit v1.1 From c0872308752df85d817a730d6d5b2d7175c2305b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 2 Feb 2016 09:07:22 +0100 Subject: clk: sunxi: convert current clocks registration to CLK_OF_DECLARE The current clock registration and protection code has a few drawbacks, the two main ones being that we create a lot of orphans clock in the registration phase, which will be troublesome when we will start being less relaxed about them. The protection code also relies on clkdev, which we don't really use but for this particular case. Fix both at the same time by moving everyone to the CLK_OF_DECLARE that will probe our clock tree in the right and thus avoid orphans, and by protecting directly the clock returned by our registration function. Reviewed-by: Andre Przywara Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 150 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 133 insertions(+), 17 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 77d91e3..bbed4e9 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -585,6 +585,41 @@ static struct clk * __init sunxi_factors_clk_setup(struct device_node *node, return sunxi_factors_register(node, data, &clk_lock, reg); } +static void __init sun4i_pll1_clk_setup(struct device_node *node) +{ + sunxi_factors_clk_setup(node, &sun4i_pll1_data); +} +CLK_OF_DECLARE(sun4i_pll1, "allwinner,sun4i-a10-pll1-clk", + sun4i_pll1_clk_setup); + +static void __init sun6i_pll1_clk_setup(struct device_node *node) +{ + sunxi_factors_clk_setup(node, &sun6i_a31_pll1_data); +} +CLK_OF_DECLARE(sun6i_pll1, "allwinner,sun6i-a31-pll1-clk", + sun6i_pll1_clk_setup); + +static void __init sun8i_pll1_clk_setup(struct device_node *node) +{ + sunxi_factors_clk_setup(node, &sun8i_a23_pll1_data); +} +CLK_OF_DECLARE(sun8i_pll1, "allwinner,sun8i-a23-pll1-clk", + sun8i_pll1_clk_setup); + +static void __init sun7i_pll4_clk_setup(struct device_node *node) +{ + sunxi_factors_clk_setup(node, &sun7i_a20_pll4_data); +} +CLK_OF_DECLARE(sun7i_pll4, "allwinner,sun7i-a20-pll4-clk", + sun7i_pll4_clk_setup); + +static void __init sun5i_ahb_clk_setup(struct device_node *node) +{ + sunxi_factors_clk_setup(node, &sun5i_a13_ahb_data); +} +CLK_OF_DECLARE(sun5i_ahb, "allwinner,sun5i-a13-ahb-clk", + sun5i_ahb_clk_setup); + static void __init sun6i_ahb1_clk_setup(struct device_node *node) { sunxi_factors_clk_setup(node, &sun6i_ahb1_data); @@ -592,6 +627,20 @@ static void __init sun6i_ahb1_clk_setup(struct device_node *node) CLK_OF_DECLARE(sun6i_a31_ahb1, "allwinner,sun6i-a31-ahb1-clk", sun6i_ahb1_clk_setup); +static void __init sun4i_apb1_clk_setup(struct device_node *node) +{ + sunxi_factors_clk_setup(node, &sun4i_apb1_data); +} +CLK_OF_DECLARE(sun4i_apb1, "allwinner,sun4i-a10-apb1-clk", + sun4i_apb1_clk_setup); + +static void __init sun7i_out_clk_setup(struct device_node *node) +{ + sunxi_factors_clk_setup(node, &sun7i_a20_out_data); +} +CLK_OF_DECLARE(sun7i_out, "allwinner,sun7i-a20-out-clk", + sun7i_out_clk_setup); + /** * sunxi_mux_clk_setup() - Setup function for muxes @@ -654,6 +703,34 @@ out_unmap: return NULL; } +static void __init sun4i_cpu_clk_setup(struct device_node *node) +{ + struct clk *clk; + + clk = sunxi_mux_clk_setup(node, &sun4i_cpu_mux_data); + if (!clk) + return; + + /* Protect CPU clock */ + __clk_get(clk); + clk_prepare_enable(clk); +} +CLK_OF_DECLARE(sun4i_cpu, "allwinner,sun4i-a10-cpu-clk", + sun4i_cpu_clk_setup); + +static void __init sun6i_ahb1_mux_clk_setup(struct device_node *node) +{ + sunxi_mux_clk_setup(node, &sun6i_a31_ahb1_mux_data); +} +CLK_OF_DECLARE(sun6i_ahb1_mux, "allwinner,sun6i-a31-ahb1-mux-clk", + sun6i_ahb1_mux_clk_setup); + +static void __init sun8i_ahb2_clk_setup(struct device_node *node) +{ + sunxi_mux_clk_setup(node, &sun8i_h3_ahb2_mux_data); +} +CLK_OF_DECLARE(sun8i_ahb2, "allwinner,sun8i-h3-ahb2-clk", + sun8i_ahb2_clk_setup); /** @@ -735,6 +812,34 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, } } +static void __init sun4i_ahb_clk_setup(struct device_node *node) +{ + sunxi_divider_clk_setup(node, &sun4i_ahb_data); +} +CLK_OF_DECLARE(sun4i_ahb, "allwinner,sun4i-a10-ahb-clk", + sun4i_ahb_clk_setup); + +static void __init sun4i_apb0_clk_setup(struct device_node *node) +{ + sunxi_divider_clk_setup(node, &sun4i_apb0_data); +} +CLK_OF_DECLARE(sun4i_apb0, "allwinner,sun4i-a10-apb0-clk", + sun4i_apb0_clk_setup); + +static void __init sun4i_axi_clk_setup(struct device_node *node) +{ + sunxi_divider_clk_setup(node, &sun4i_axi_data); +} +CLK_OF_DECLARE(sun4i_axi, "allwinner,sun4i-a10-axi-clk", + sun4i_axi_clk_setup); + +static void __init sun8i_axi_clk_setup(struct device_node *node) +{ + sunxi_divider_clk_setup(node, &sun8i_a23_axi_data); +} +CLK_OF_DECLARE(sun8i_axi, "allwinner,sun8i-a23-axi-clk", + sun8i_axi_clk_setup); + /** @@ -947,42 +1052,53 @@ free_clkdata: return NULL; } +static void __init sun4i_pll5_clk_setup(struct device_node *node) +{ + struct clk **clks; + + clks = sunxi_divs_clk_setup(node, &pll5_divs_data); + if (!clks) + return; + + /* Protect PLL5_DDR */ + __clk_get(clks[0]); + clk_prepare_enable(clks[0]); +} +CLK_OF_DECLARE(sun4i_pll5, "allwinner,sun4i-a10-pll5-clk", + sun4i_pll5_clk_setup); + +static void __init sun4i_pll6_clk_setup(struct device_node *node) +{ + sunxi_divs_clk_setup(node, &pll6_divs_data); +} +CLK_OF_DECLARE(sun4i_pll6, "allwinner,sun4i-a10-pll6-clk", + sun4i_pll6_clk_setup); + +static void __init sun6i_pll6_clk_setup(struct device_node *node) +{ + sunxi_divs_clk_setup(node, &sun6i_a31_pll6_divs_data); +} +CLK_OF_DECLARE(sun6i_pll6, "allwinner,sun6i-a31-pll6-clk", + sun6i_pll6_clk_setup); /* Matches for factors clocks */ static const struct of_device_id clk_factors_match[] __initconst = { - {.compatible = "allwinner,sun4i-a10-pll1-clk", .data = &sun4i_pll1_data,}, - {.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,}, - {.compatible = "allwinner,sun8i-a23-pll1-clk", .data = &sun8i_a23_pll1_data,}, - {.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,}, - {.compatible = "allwinner,sun5i-a13-ahb-clk", .data = &sun5i_a13_ahb_data,}, - {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,}, - {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, {} }; /* Matches for divider clocks */ static const struct of_device_id clk_div_match[] __initconst = { - {.compatible = "allwinner,sun4i-a10-axi-clk", .data = &sun4i_axi_data,}, - {.compatible = "allwinner,sun8i-a23-axi-clk", .data = &sun8i_a23_axi_data,}, - {.compatible = "allwinner,sun4i-a10-ahb-clk", .data = &sun4i_ahb_data,}, - {.compatible = "allwinner,sun4i-a10-apb0-clk", .data = &sun4i_apb0_data,}, {} }; /* Matches for divided outputs */ static const struct of_device_id clk_divs_match[] __initconst = { - {.compatible = "allwinner,sun4i-a10-pll5-clk", .data = &pll5_divs_data,}, - {.compatible = "allwinner,sun4i-a10-pll6-clk", .data = &pll6_divs_data,}, - {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_divs_data,}, {} }; /* Matches for mux clocks */ static const struct of_device_id clk_mux_match[] __initconst = { - {.compatible = "allwinner,sun4i-a10-cpu-clk", .data = &sun4i_cpu_mux_data,}, - {.compatible = "allwinner,sun6i-a31-ahb1-mux-clk", .data = &sun6i_a31_ahb1_mux_data,}, - {.compatible = "allwinner,sun8i-h3-ahb2-clk", .data = &sun8i_h3_ahb2_mux_data,}, {} }; -- cgit v1.1 From 3a4d9af48a947b6605fb3a17505da94d70d05de4 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 2 Feb 2016 09:07:37 +0100 Subject: clk: sunxi: Remove old probe and protection code Now that we don't have any user left for the old registration code, we can remove it. Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 108 ------------------------------------------ 1 file changed, 108 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index bbed4e9..6c5b104 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -1080,111 +1080,3 @@ static void __init sun6i_pll6_clk_setup(struct device_node *node) } CLK_OF_DECLARE(sun6i_pll6, "allwinner,sun6i-a31-pll6-clk", sun6i_pll6_clk_setup); - - -/* Matches for factors clocks */ -static const struct of_device_id clk_factors_match[] __initconst = { - {} -}; - -/* Matches for divider clocks */ -static const struct of_device_id clk_div_match[] __initconst = { - {} -}; - -/* Matches for divided outputs */ -static const struct of_device_id clk_divs_match[] __initconst = { - {} -}; - -/* Matches for mux clocks */ -static const struct of_device_id clk_mux_match[] __initconst = { - {} -}; - - -static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match, - void *function) -{ - struct device_node *np; - const struct div_data *data; - const struct of_device_id *match; - void (*setup_function)(struct device_node *, const void *) = function; - - for_each_matching_node_and_match(np, clk_match, &match) { - data = match->data; - setup_function(np, data); - } -} - -static void __init sunxi_init_clocks(const char *clocks[], int nclocks) -{ - unsigned int i; - - /* Register divided output clocks */ - of_sunxi_table_clock_setup(clk_divs_match, sunxi_divs_clk_setup); - - /* Register factor clocks */ - of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup); - - /* Register divider clocks */ - of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup); - - /* Register mux clocks */ - of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup); - - /* Protect the clocks that needs to stay on */ - for (i = 0; i < nclocks; i++) { - struct clk *clk = clk_get(NULL, clocks[i]); - - if (!IS_ERR(clk)) - clk_prepare_enable(clk); - } -} - -static const char *sun4i_a10_critical_clocks[] __initdata = { - "pll5_ddr", -}; - -static void __init sun4i_a10_init_clocks(struct device_node *node) -{ - sunxi_init_clocks(sun4i_a10_critical_clocks, - ARRAY_SIZE(sun4i_a10_critical_clocks)); -} -CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sun4i_a10_init_clocks); - -static const char *sun5i_critical_clocks[] __initdata = { - "cpu", - "pll5_ddr", -}; - -static void __init sun5i_init_clocks(struct device_node *node) -{ - sunxi_init_clocks(sun5i_critical_clocks, - ARRAY_SIZE(sun5i_critical_clocks)); -} -CLK_OF_DECLARE(sun5i_a10s_clk_init, "allwinner,sun5i-a10s", sun5i_init_clocks); -CLK_OF_DECLARE(sun5i_a13_clk_init, "allwinner,sun5i-a13", sun5i_init_clocks); -CLK_OF_DECLARE(sun5i_r8_clk_init, "allwinner,sun5i-r8", sun5i_init_clocks); -CLK_OF_DECLARE(sun7i_a20_clk_init, "allwinner,sun7i-a20", sun5i_init_clocks); - -static const char *sun6i_critical_clocks[] __initdata = { - "cpu", -}; - -static void __init sun6i_init_clocks(struct device_node *node) -{ - sunxi_init_clocks(sun6i_critical_clocks, - ARRAY_SIZE(sun6i_critical_clocks)); -} -CLK_OF_DECLARE(sun6i_a31_clk_init, "allwinner,sun6i-a31", sun6i_init_clocks); -CLK_OF_DECLARE(sun6i_a31s_clk_init, "allwinner,sun6i-a31s", sun6i_init_clocks); -CLK_OF_DECLARE(sun8i_a23_clk_init, "allwinner,sun8i-a23", sun6i_init_clocks); -CLK_OF_DECLARE(sun8i_a33_clk_init, "allwinner,sun8i-a33", sun6i_init_clocks); -CLK_OF_DECLARE(sun8i_h3_clk_init, "allwinner,sun8i-h3", sun6i_init_clocks); - -static void __init sun9i_init_clocks(struct device_node *node) -{ - sunxi_init_clocks(NULL, 0); -} -CLK_OF_DECLARE(sun9i_a80_clk_init, "allwinner,sun9i-a80", sun9i_init_clocks); -- cgit v1.1 From b0b6413f0dc8d1faba98745cf3a008ed9493df59 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 2 Feb 2016 09:37:15 +0100 Subject: clk: sunxi: Remove clk_register_clkdev calls Now that our protection code doesn't use the global name lookup anymore, we can remove the clkdev registrations. Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-a10-hosc.c | 3 +-- drivers/clk/sunxi/clk-a20-gmac.c | 2 -- drivers/clk/sunxi/clk-factors.c | 7 ------- drivers/clk/sunxi/clk-factors.h | 1 - drivers/clk/sunxi/clk-mod0.c | 1 + drivers/clk/sunxi/clk-sun6i-apb0-gates.c | 2 -- drivers/clk/sunxi/clk-sun9i-core.c | 1 + drivers/clk/sunxi/clk-sunxi.c | 6 +----- 8 files changed, 4 insertions(+), 19 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-a10-hosc.c b/drivers/clk/sunxi/clk-a10-hosc.c index 0481d5d..6b598c6 100644 --- a/drivers/clk/sunxi/clk-a10-hosc.c +++ b/drivers/clk/sunxi/clk-a10-hosc.c @@ -15,9 +15,9 @@ */ #include -#include #include #include +#include #define SUNXI_OSC24M_GATE 0 @@ -61,7 +61,6 @@ static void __init sun4i_osc_clk_setup(struct device_node *node) goto err_free_gate; of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); return; diff --git a/drivers/clk/sunxi/clk-a20-gmac.c b/drivers/clk/sunxi/clk-a20-gmac.c index 1611b03..3437f73 100644 --- a/drivers/clk/sunxi/clk-a20-gmac.c +++ b/drivers/clk/sunxi/clk-a20-gmac.c @@ -17,7 +17,6 @@ */ #include -#include #include #include #include @@ -107,7 +106,6 @@ static void __init sun7i_a20_gmac_clk_setup(struct device_node *node) goto iounmap_reg; of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); return; diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 6e97f46..ddefe96 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -258,14 +258,8 @@ struct clk *sunxi_factors_register(struct device_node *node, if (ret) goto err_provider; - ret = clk_register_clkdev(clk, clk_name, NULL); - if (ret) - goto err_clkdev; - return clk; -err_clkdev: - of_clk_del_provider(node); err_provider: /* TODO: The composite clock stuff will leak a bit here. */ clk_unregister(clk); @@ -291,7 +285,6 @@ void sunxi_factors_unregister(struct device_node *node, struct clk *clk) factors = to_clk_factors(hw); name = clk_hw_get_name(hw); - /* No unregister call for clkdev_* */ of_clk_del_provider(node); /* TODO: The composite clock stuff will leak a bit here. */ clk_unregister(clk); diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h index a44a865..1e63c5b 100644 --- a/drivers/clk/sunxi/clk-factors.h +++ b/drivers/clk/sunxi/clk-factors.h @@ -2,7 +2,6 @@ #define __MACH_SUNXI_CLK_FACTORS_H #include -#include #include #define SUNXI_FACTORS_NOT_APPLICABLE (0) diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c index 578bff0..b38d71c 100644 --- a/drivers/clk/sunxi/clk-mod0.c +++ b/drivers/clk/sunxi/clk-mod0.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include diff --git a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c index 23d042a..68021fa 100644 --- a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c +++ b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c @@ -9,7 +9,6 @@ */ #include -#include #include #include #include @@ -87,7 +86,6 @@ static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev) clk_parent, 0, reg, i, 0, NULL); WARN_ON(IS_ERR(clk_data->clks[i])); - clk_register_clkdev(clk_data->clks[i], clk_name, NULL); j++; } diff --git a/drivers/clk/sunxi/clk-sun9i-core.c b/drivers/clk/sunxi/clk-sun9i-core.c index a812110..43f014f 100644 --- a/drivers/clk/sunxi/clk-sun9i-core.c +++ b/drivers/clk/sunxi/clk-sun9i-core.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 6c5b104..99f60ef 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -694,7 +694,6 @@ static struct clk * __init sunxi_mux_clk_setup(struct device_node *node, } of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); return clk; @@ -806,10 +805,8 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, reg, data->shift, data->width, data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, data->table, &clk_lock); - if (clk) { + if (clk) of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); - } } static void __init sun4i_ahb_clk_setup(struct device_node *node) @@ -1033,7 +1030,6 @@ static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node, clkflags); WARN_ON(IS_ERR(clk_data->clks[i])); - clk_register_clkdev(clks[i], clk_name, NULL); } /* Adjust to the real max */ -- cgit v1.1 From 33f60d02605a3a604e56b07a78d80d7d801b2843 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 15 Feb 2016 17:40:19 +0800 Subject: clk: sunxi: Fix sun8i-a23-apb0-clk divider flags The APB0 clock on A23 is a zero-based divider, not a power-of-two based divider. Note that this patch does not apply cleanly to kernels before 4.5-rc1, which added CLK_OF_DECLARE support to this driver. Fixes: 57a1fbf28424 ("clk: sunxi: Add A23 APB0 divider clock support") Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sun8i-apb0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sun8i-apb0.c b/drivers/clk/sunxi/clk-sun8i-apb0.c index 7ba6110..2ea61de 100644 --- a/drivers/clk/sunxi/clk-sun8i-apb0.c +++ b/drivers/clk/sunxi/clk-sun8i-apb0.c @@ -36,7 +36,7 @@ static struct clk *sun8i_a23_apb0_register(struct device_node *node, /* The A23 APB0 clock is a standard 2 bit wide divider clock */ clk = clk_register_divider(NULL, clk_name, clk_parent, 0, reg, - 0, 2, CLK_DIVIDER_POWER_OF_TWO, NULL); + 0, 2, 0, NULL); if (IS_ERR(clk)) return clk; -- cgit v1.1 From 72360b91164aa35753942e5310ae1e1d16e28611 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Tue, 16 Feb 2016 10:46:06 +0000 Subject: clk: sunxi: improve mux_clk error handling and reporting We now catch and report a failing ioremap, also a failure in the final step of the clock registration is now handled and reported. Also warnings are turned into errors. Signed-off-by: Andre Przywara Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 99f60ef..775e137 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -674,11 +674,16 @@ static struct clk * __init sunxi_mux_clk_setup(struct device_node *node, int i; reg = of_iomap(node, 0); + if (!reg) { + pr_err("Could not map registers for mux-clk: %s\n", + of_node_full_name(node)); + return NULL; + } i = of_clk_parent_fill(node, parents, SUNXI_MAX_PARENTS); if (of_property_read_string(node, "clock-output-names", &clk_name)) { - pr_warn("%s: could not read clock-output-names for \"%s\"\n", - __func__, clk_name); + pr_err("%s: could not read clock-output-names from \"%s\"\n", + __func__, of_node_full_name(node)); goto out_unmap; } @@ -688,15 +693,19 @@ static struct clk * __init sunxi_mux_clk_setup(struct device_node *node, 0, &clk_lock); if (IS_ERR(clk)) { - pr_warn("%s: failed to register mux clock %s: %ld\n", __func__, - clk_name, PTR_ERR(clk)); + pr_err("%s: failed to register mux clock %s: %ld\n", __func__, + clk_name, PTR_ERR(clk)); goto out_unmap; } - of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (of_clk_add_provider(node, of_clk_src_simple_get, clk)) { + pr_err("%s: failed to add clock provider for %s\n", + __func__, clk_name); + clk_unregister_divider(clk); + goto out_unmap; + } return clk; - out_unmap: iounmap(reg); return NULL; -- cgit v1.1 From b26803ebfba8d81e2e8fb392c1248df2ebd1ba83 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Tue, 16 Feb 2016 10:46:07 +0000 Subject: clk: sunxi: improve divider_clk error handling and reporting We now report a failing ioremap, failing output names parsing, failures in table registration and in the final step. Also there was a bug where clk_register_divider_table() would return an ERR_PTR value instead of NULL, which we were checking for. We now implement proper rollback in case of an error. Signed-off-by: Andre Przywara Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 775e137..4bd0917 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -805,17 +805,47 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, void __iomem *reg; reg = of_iomap(node, 0); + if (!reg) { + pr_err("Could not map registers for mux-clk: %s\n", + of_node_full_name(node)); + return; + } clk_parent = of_clk_get_parent_name(node, 0); - of_property_read_string(node, "clock-output-names", &clk_name); + if (of_property_read_string(node, "clock-output-names", &clk_name)) { + pr_err("%s: could not read clock-output-names from \"%s\"\n", + __func__, of_node_full_name(node)); + goto out_unmap; + } clk = clk_register_divider_table(NULL, clk_name, clk_parent, 0, reg, data->shift, data->width, data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, data->table, &clk_lock); - if (clk) - of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (IS_ERR(clk)) { + pr_err("%s: failed to register divider clock %s: %ld\n", + __func__, clk_name, PTR_ERR(clk)); + goto out_unmap; + } + + if (of_clk_add_provider(node, of_clk_src_simple_get, clk)) { + pr_err("%s: failed to add clock provider for %s\n", + __func__, clk_name); + goto out_unregister; + } + + if (clk_register_clkdev(clk, clk_name, NULL)) { + of_clk_del_provider(node); + goto out_unregister; + } + + return; +out_unregister: + clk_unregister_divider(clk); + +out_unmap: + iounmap(reg); } static void __init sun4i_ahb_clk_setup(struct device_node *node) -- cgit v1.1 From d331328da6b719e4ffb3b43125bbe540755239ad Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Tue, 16 Feb 2016 10:46:08 +0000 Subject: clk: sunxi: Improve divs_clk error handling and reporting We catch errors in the base clock registration, failure to ioremap and failures in the final of_clk_add_provider() call. Also we unmap the registers when we need to rollback. Signed-off-by: Andre Przywara Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 4bd0917..91de0a0 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -985,13 +985,20 @@ static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node, /* Set up factor clock that we will be dividing */ pclk = sunxi_factors_clk_setup(node, data->factors); + if (!pclk) + return NULL; parent = __clk_get_name(pclk); reg = of_iomap(node, 0); + if (!reg) { + pr_err("Could not map registers for divs-clk: %s\n", + of_node_full_name(node)); + return NULL; + } clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); if (!clk_data) - return NULL; + goto out_unmap; clks = kcalloc(ndivs, sizeof(*clks), GFP_KERNEL); if (!clks) @@ -1074,16 +1081,21 @@ static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node, /* Adjust to the real max */ clk_data->clk_num = i; - of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (of_clk_add_provider(node, of_clk_src_onecell_get, clk_data)) { + pr_err("%s: failed to add clock provider for %s\n", + __func__, clk_name); + goto free_gate; + } return clks; - free_gate: kfree(gate); free_clks: kfree(clks); free_clkdata: kfree(clk_data); +out_unmap: + iounmap(reg); return NULL; } -- cgit v1.1 From 6e17b4181603d183d20c73f4535529ddf2a2a020 Mon Sep 17 00:00:00 2001 From: Krzysztof Adamski Date: Mon, 22 Feb 2016 14:03:25 +0100 Subject: clk: sunxi: Add apb0 gates for H3 This patch adds support for APB0 in H3. It seems to be compatible with earlier SOCs. apb0 gates controls R_ block peripherals (R_PIO, R_IR, etc). Since this gates behave just like any Allwinner clock gate, add a generic compatible that can be reused if we don't have any clock to protect. Signed-off-by: Krzysztof Adamski [Maxime: Removed the H3 compatible from the simple-gates driver, reworked the commit log a bit] Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-simple-gates.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-simple-gates.c b/drivers/clk/sunxi/clk-simple-gates.c index 2cfc5a8..a085c3b 100644 --- a/drivers/clk/sunxi/clk-simple-gates.c +++ b/drivers/clk/sunxi/clk-simple-gates.c @@ -98,6 +98,8 @@ static void __init sunxi_simple_gates_init(struct device_node *node) sunxi_simple_gates_setup(node, NULL, 0); } +CLK_OF_DECLARE(sun4i_a10_gates, "allwinner,sun4i-a10-gates-clk", + sunxi_simple_gates_init); CLK_OF_DECLARE(sun4i_a10_apb0, "allwinner,sun4i-a10-apb0-gates-clk", sunxi_simple_gates_init); CLK_OF_DECLARE(sun4i_a10_apb1, "allwinner,sun4i-a10-apb1-gates-clk", -- cgit v1.1