Message ID | 1410000448-9999-4-git-send-email-wens@csie.org (mailing list archive) |
---|---|
State | Not Applicable |
Headers | show |
Hi, On Sat, Sep 06, 2014 at 06:47:24PM +0800, Chen-Yu Tsai wrote: > This patch unifies the sun6i AHB1 clock, originally supported > with separate mux and divider clks. It also adds support for > the pre-divider on the PLL6 input, thus allowing the clock to > be muxed to PLL6 with proper clock rate calculation. > > Signed-off-by: Chen-Yu Tsai <wens@csie.org> It looks fine, but I'd rather see this in a separate file, especially since we don't seem to have any order dependency. Thanks! Maxime
Hi, On Fri, Sep 12, 2014 at 5:02 AM, Maxime Ripard <maxime.ripard@free-electrons.com> wrote: > Hi, > > On Sat, Sep 06, 2014 at 06:47:24PM +0800, Chen-Yu Tsai wrote: >> This patch unifies the sun6i AHB1 clock, originally supported >> with separate mux and divider clks. It also adds support for >> the pre-divider on the PLL6 input, thus allowing the clock to >> be muxed to PLL6 with proper clock rate calculation. >> >> Signed-off-by: Chen-Yu Tsai <wens@csie.org> > > It looks fine, but I'd rather see this in a separate file, especially > since we don't seem to have any order dependency. Sorry, just to be clear, separate file under clk/sunxi? This cannot be in a separate file, as it shares a spinlock with apb1 divider. They share the same register. We could move apb1 out though. But i would prefer to do that when we split out all the clocks into individual OF_CLK_DECLAREs. ChenYu -- To unsubscribe from this list: send the line "unsubscribe dmaengine" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Fri, Sep 12, 2014 at 11:16:26AM +0800, Chen-Yu Tsai wrote: > Hi, > > On Fri, Sep 12, 2014 at 5:02 AM, Maxime Ripard > <maxime.ripard@free-electrons.com> wrote: > > Hi, > > > > On Sat, Sep 06, 2014 at 06:47:24PM +0800, Chen-Yu Tsai wrote: > >> This patch unifies the sun6i AHB1 clock, originally supported > >> with separate mux and divider clks. It also adds support for > >> the pre-divider on the PLL6 input, thus allowing the clock to > >> be muxed to PLL6 with proper clock rate calculation. > >> > >> Signed-off-by: Chen-Yu Tsai <wens@csie.org> > > > > It looks fine, but I'd rather see this in a separate file, especially > > since we don't seem to have any order dependency. > > Sorry, just to be clear, separate file under clk/sunxi? Yes > This cannot be in a separate file, as it shares a spinlock with apb1 > divider. They share the same register. > > We could move apb1 out though. But i would prefer to do that when > we split out all the clocks into individual OF_CLK_DECLAREs. Ah right, my bad :) My plan on the long term is to kill clk-sunxi as a place where all the clocks are defined, and only leave the "policy" there, for example the clock protection code (even if that should probably be removed too, together with clkdev), the various rates / parenting enforcements, etc. Maxime
Quoting Maxime Ripard (2014-09-13 03:26:03) > On Fri, Sep 12, 2014 at 11:16:26AM +0800, Chen-Yu Tsai wrote: > > Hi, > > > > On Fri, Sep 12, 2014 at 5:02 AM, Maxime Ripard > > <maxime.ripard@free-electrons.com> wrote: > > > Hi, > > > > > > On Sat, Sep 06, 2014 at 06:47:24PM +0800, Chen-Yu Tsai wrote: > > >> This patch unifies the sun6i AHB1 clock, originally supported > > >> with separate mux and divider clks. It also adds support for > > >> the pre-divider on the PLL6 input, thus allowing the clock to > > >> be muxed to PLL6 with proper clock rate calculation. > > >> > > >> Signed-off-by: Chen-Yu Tsai <wens@csie.org> > > > > > > It looks fine, but I'd rather see this in a separate file, especially > > > since we don't seem to have any order dependency. > > > > Sorry, just to be clear, separate file under clk/sunxi? > > Yes > > > This cannot be in a separate file, as it shares a spinlock with apb1 > > divider. They share the same register. > > > > We could move apb1 out though. But i would prefer to do that when > > we split out all the clocks into individual OF_CLK_DECLAREs. > > Ah right, my bad :) > > My plan on the long term is to kill clk-sunxi as a place where all the > clocks are defined, and only leave the "policy" there, for example the > clock protection code (even if that should probably be removed too, > together with clkdev), the various rates / parenting enforcements, > etc. Interesting! Where are you planning to store the clock data? Regards, Mike > > Maxime > > -- > Maxime Ripard, Free Electrons > Embedded Linux, Kernel and Android engineering > http://free-electrons.com -- To unsubscribe from this list: send the line "unsubscribe dmaengine" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, Sep 25, 2014 at 04:03:40PM -0700, Mike Turquette wrote: > Quoting Maxime Ripard (2014-09-13 03:26:03) > > On Fri, Sep 12, 2014 at 11:16:26AM +0800, Chen-Yu Tsai wrote: > > > Hi, > > > > > > On Fri, Sep 12, 2014 at 5:02 AM, Maxime Ripard > > > <maxime.ripard@free-electrons.com> wrote: > > > > Hi, > > > > > > > > On Sat, Sep 06, 2014 at 06:47:24PM +0800, Chen-Yu Tsai wrote: > > > >> This patch unifies the sun6i AHB1 clock, originally supported > > > >> with separate mux and divider clks. It also adds support for > > > >> the pre-divider on the PLL6 input, thus allowing the clock to > > > >> be muxed to PLL6 with proper clock rate calculation. > > > >> > > > >> Signed-off-by: Chen-Yu Tsai <wens@csie.org> > > > > > > > > It looks fine, but I'd rather see this in a separate file, especially > > > > since we don't seem to have any order dependency. > > > > > > Sorry, just to be clear, separate file under clk/sunxi? > > > > Yes > > > > > This cannot be in a separate file, as it shares a spinlock with apb1 > > > divider. They share the same register. > > > > > > We could move apb1 out though. But i would prefer to do that when > > > we split out all the clocks into individual OF_CLK_DECLAREs. > > > > Ah right, my bad :) > > > > My plan on the long term is to kill clk-sunxi as a place where all the > > clocks are defined, and only leave the "policy" there, for example the > > clock protection code (even if that should probably be removed too, > > together with clkdev), the various rates / parenting enforcements, > > etc. > > Interesting! Where are you planning to store the clock data? Which data? I guess, for the rate boundaries, the DT would be the right place, wether a clock should be protected can be derived from its compatible. And for the rate to enforce, maybe a clock-frequency property in the DT too, or directly in the driver, I haven't really thought about that part at the moment to be honest :) Maxime
diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index d3a5c3c..3d531d6 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -23,7 +23,7 @@ Required properties: "allwinner,sun5i-a10s-ahb-gates-clk" - for the AHB gates on A10s "allwinner,sun7i-a20-ahb-gates-clk" - for the AHB gates on A20 "allwinner,sun6i-a31-ar100-clk" - for the AR100 on A31 - "allwinner,sun6i-a31-ahb1-mux-clk" - for the AHB1 multiplexer on A31 + "allwinner,sun6i-a31-ahb1-clk" - for the AHB1 clock on A31 "allwinner,sun6i-a31-ahb1-gates-clk" - for the AHB1 gates on A31 "allwinner,sun8i-a23-ahb1-gates-clk" - for the AHB1 gates on A23 "allwinner,sun4i-a10-apb0-clk" - for the APB0 clock diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index be9ac07..87b7740 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -19,6 +19,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/reset-controller.h> +#include <linux/log2.h> #include "clk-factors.h" @@ -1339,3 +1340,211 @@ static void __init sun6i_init_clocks(struct device_node *node) } CLK_OF_DECLARE(sun6i_a31_clk_init, "allwinner,sun6i-a31", sun6i_init_clocks); CLK_OF_DECLARE(sun8i_a23_clk_init, "allwinner,sun8i-a23", sun6i_init_clocks); + + +/** + * 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 +#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 +#define SUN6I_AHB1_DIV_GET(reg) ((reg >> SUN6I_AHB1_DIV_SHIFT) & \ + SUN6I_AHB1_DIV_MASK) +#define SUN6I_AHB1_DIV_SET(reg, div) ((reg & ~(SUN6I_AHB1_DIV_MASK << \ + SUN6I_AHB1_DIV_SHIFT)) | \ + (div << SUN6I_AHB1_DIV_SHIFT)) +#define SUN6I_AHB1_PLL6_DIV_SHIFT 6 +#define SUN6I_AHB1_PLL6_DIV_MASK 0x3 +#define SUN6I_AHB1_PLL6_DIV_GET(reg) ((reg >> SUN6I_AHB1_PLL6_DIV_SHIFT) & \ + SUN6I_AHB1_PLL6_DIV_MASK) +#define SUN6I_AHB1_PLL6_DIV_SET(reg, div) ((reg & \ + ~(SUN6I_AHB1_PLL6_DIV_MASK << \ + SUN6I_AHB1_PLL6_DIV_SHIFT)) | \ + (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; + } + + if (divp) { + *divp = calcp; + *pre_divp = calcm - 1; + } + + return (parent_rate / calcm) >> calcp; +} + +static long sun6i_ahb1_clk_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_clk) +{ + struct clk *clk = hw->clk, *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_get_num_parents(clk); + for (i = 0; i < num_parents; i++) { + parent = clk_get_parent_by_index(clk, i); + if (!parent) + continue; + if (__clk_get_flags(clk) & CLK_SET_RATE_PARENT) + parent_rate = __clk_round_rate(parent, rate); + else + parent_rate = __clk_get_rate(parent); + + child_rate = sun6i_ahb1_clk_round(rate, NULL, NULL, i, + parent_rate); + + if (child_rate <= rate && child_rate > best_child_rate) { + best_parent = parent; + best = parent_rate; + best_child_rate = child_rate; + } + } + + if (best_parent) + *best_parent_clk = best_parent; + *best_parent_rate = best; + + return best_child_rate; +} + +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 = 0; + + reg = of_iomap(node, 0); + + /* we have a mux, we will have >1 parents */ + while (i < SUN6I_AHB1_MAX_PARENTS && + (parents[i] = of_clk_get_parent_name(node, i)) != NULL) + i++; + + 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);
This patch unifies the sun6i AHB1 clock, originally supported with separate mux and divider clks. It also adds support for the pre-divider on the PLL6 input, thus allowing the clock to be muxed to PLL6 with proper clock rate calculation. Signed-off-by: Chen-Yu Tsai <wens@csie.org> --- Documentation/devicetree/bindings/clock/sunxi.txt | 2 +- drivers/clk/sunxi/clk-sunxi.c | 209 ++++++++++++++++++++++ 2 files changed, 210 insertions(+), 1 deletion(-)