diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-21 12:30:30 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-21 12:30:30 -0800 |
commit | 18a8d49973667aa016e68826eeb374788b7c63b0 (patch) | |
tree | d7f90fcf05087ce50ee931b596e60e4c3c082a29 /drivers/clk/sunxi/clk-sunxi.c | |
parent | c189cb8ef62832f33b6cf757350a0270532a1ad8 (diff) | |
parent | ec02ace8ca0a50eef430d3676de5c5fa978b0e29 (diff) | |
download | blackbird-op-linux-18a8d49973667aa016e68826eeb374788b7c63b0.tar.gz blackbird-op-linux-18a8d49973667aa016e68826eeb374788b7c63b0.zip |
Merge tag 'clk-for-linus-3.20' of git://git.linaro.org/people/mike.turquette/linux
Pull clock framework updates from Mike Turquette:
"The clock framework changes contain the usual driver additions,
enhancements and fixes mostly for ARM32, ARM64, MIPS and Power-based
devices.
Additionally the framework core underwent a bit of surgery with two
major changes:
- The boundary between the clock core and clock providers (e.g clock
drivers) is now more well defined with dedicated provider helper
functions. struct clk no longer maps 1:1 with the hardware clock
but is a true per-user cookie which helps us tracker users of
hardware clocks and debug bad behavior.
- The addition of rate constraints for clocks. Rate ranges are now
supported which are analogous to the voltage ranges in the
regulator framework.
Unfortunately these changes to the core created some breakeage. We
think we fixed it all up but for this reason there are lots of last
minute commits trying to undo the damage"
* tag 'clk-for-linus-3.20' of git://git.linaro.org/people/mike.turquette/linux: (113 commits)
clk: Only recalculate the rate if needed
Revert "clk: mxs: Fix invalid 32-bit access to frac registers"
clk: qoriq: Add support for the platform PLL
powerpc/corenet: Enable CLK_QORIQ
clk: Replace explicit clk assignment with __clk_hw_set_clk
clk: Add __clk_hw_set_clk helper function
clk: Don't dereference parent clock if is NULL
MIPS: Alchemy: Remove bogus args from alchemy_clk_fgcs_detr
clkdev: Always allocate a struct clk and call __clk_get() w/ CCF
clk: shmobile: div6: Avoid division by zero in .round_rate()
clk: mxs: Fix invalid 32-bit access to frac registers
clk: omap: compile legacy omap3 clocks conditionally
clkdev: Export clk_register_clkdev
clk: Add rate constraints to clocks
clk: remove clk-private.h
pci: xgene: do not use clk-private.h
arm: omap2+ remove dead clock code
clk: Make clk API return per-user struct clk instances
clk: tegra: Define PLLD_DSI and remove dsia(b)_mux
clk: tegra: Add support for the Tegra132 CAR IP block
...
Diffstat (limited to 'drivers/clk/sunxi/clk-sunxi.c')
-rw-r--r-- | drivers/clk/sunxi/clk-sunxi.c | 262 |
1 files changed, 222 insertions, 40 deletions
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 1818f404538d..379324eb5486 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -20,11 +20,221 @@ #include <linux/of_address.h> #include <linux/reset-controller.h> #include <linux/spinlock.h> +#include <linux/log2.h> #include "clk-factors.h" 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 long sun6i_ahb1_clk_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long min_rate, + unsigned long max_rate, + unsigned long *best_parent_rate, + struct clk_hw **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 = __clk_get_hw(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_io_request_and_map(node, 0, of_node_full_name(node)); + + /* 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); + /* Maximum number of parents our clocks have */ #define SUNXI_MAX_PARENTS 5 @@ -355,43 +565,6 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate, } /** - * clk_sunxi_mmc_phase_control() - configures MMC clock phase control - */ - -void clk_sunxi_mmc_phase_control(struct clk *clk, u8 sample, u8 output) -{ - #define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw) - #define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) - - struct clk_hw *hw = __clk_get_hw(clk); - struct clk_composite *composite = to_clk_composite(hw); - struct clk_hw *rate_hw = composite->rate_hw; - struct clk_factors *factors = to_clk_factors(rate_hw); - unsigned long flags = 0; - u32 reg; - - if (factors->lock) - spin_lock_irqsave(factors->lock, flags); - - reg = readl(factors->reg); - - /* set sample clock phase control */ - reg &= ~(0x7 << 20); - reg |= ((sample & 0x7) << 20); - - /* set output clock phase control */ - reg &= ~(0x7 << 8); - reg |= ((output & 0x7) << 8); - - writel(reg, factors->reg); - - if (factors->lock) - spin_unlock_irqrestore(factors->lock, flags); -} -EXPORT_SYMBOL(clk_sunxi_mmc_phase_control); - - -/** * sunxi_factors_clk_setup() - Setup function for factor clocks */ @@ -413,6 +586,7 @@ static struct clk_factors_config sun6i_a31_pll1_config = { .kwidth = 2, .mshift = 0, .mwidth = 2, + .n_start = 1, }; static struct clk_factors_config sun8i_a23_pll1_config = { @@ -520,7 +694,16 @@ static const struct factors_data sun7i_a20_out_data __initconst = { static struct clk * __init sunxi_factors_clk_setup(struct device_node *node, const struct factors_data *data) { - return sunxi_factors_register(node, data, &clk_lock); + void __iomem *reg; + + reg = of_iomap(node, 0); + if (!reg) { + pr_err("Could not get registers for factors-clk: %s\n", + node->name); + return NULL; + } + + return sunxi_factors_register(node, data, &clk_lock, reg); } @@ -561,7 +744,7 @@ static void __init sunxi_mux_clk_setup(struct device_node *node, of_property_read_string(node, "clock-output-names", &clk_name); clk = clk_register_mux(NULL, clk_name, parents, i, - CLK_SET_RATE_NO_REPARENT, reg, + CLK_SET_RATE_PARENT, reg, data->shift, SUNXI_MUX_GATE_WIDTH, 0, &clk_lock); @@ -1217,7 +1400,6 @@ CLK_OF_DECLARE(sun7i_a20_clk_init, "allwinner,sun7i-a20", sun5i_init_clocks); static const char *sun6i_critical_clocks[] __initdata = { "cpu", - "ahb1_sdram", }; static void __init sun6i_init_clocks(struct device_node *node) |