diff options
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/Kconfig | 12 | ||||
-rw-r--r-- | drivers/clk/Makefile | 5 | ||||
-rw-r--r-- | drivers/clk/clk-divider.c | 68 | ||||
-rw-r--r-- | drivers/clk/clk-fixed-factor.c | 95 | ||||
-rw-r--r-- | drivers/clk/clk-fixed-rate.c | 49 | ||||
-rw-r--r-- | drivers/clk/clk-gate.c | 104 | ||||
-rw-r--r-- | drivers/clk/clk-mux.c | 27 | ||||
-rw-r--r-- | drivers/clk/clk.c | 270 | ||||
-rw-r--r-- | drivers/clk/spear/Makefile | 8 | ||||
-rw-r--r-- | drivers/clk/spear/clk-aux-synth.c | 198 | ||||
-rw-r--r-- | drivers/clk/spear/clk-frac-synth.c | 165 | ||||
-rw-r--r-- | drivers/clk/spear/clk-gpt-synth.c | 154 | ||||
-rw-r--r-- | drivers/clk/spear/clk-vco-pll.c | 363 | ||||
-rw-r--r-- | drivers/clk/spear/clk.c | 36 | ||||
-rw-r--r-- | drivers/clk/spear/clk.h | 134 | ||||
-rw-r--r-- | drivers/clk/spear/spear3xx_clock.c | 612 | ||||
-rw-r--r-- | drivers/clk/spear/spear6xx_clock.c | 342 |
17 files changed, 2414 insertions, 228 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 165e1febae53..4864407e3fc4 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -12,6 +12,7 @@ config HAVE_MACH_CLKDEV config COMMON_CLK bool select HAVE_CLK_PREPARE + select CLKDEV_LOOKUP ---help--- The common clock framework is a single definition of struct clk, useful across many platforms, as well as an @@ -22,17 +23,6 @@ config COMMON_CLK menu "Common Clock Framework" depends on COMMON_CLK -config COMMON_CLK_DISABLE_UNUSED - bool "Disabled unused clocks at boot" - depends on COMMON_CLK - ---help--- - Traverses the entire clock tree and disables any clocks that are - enabled in hardware but have not been enabled by any device drivers. - This saves power and keeps the software model of the clock in line - with reality. - - If in doubt, say "N". - config COMMON_CLK_DEBUG bool "DebugFS representation of clock tree" depends on COMMON_CLK diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 1f736bc11c4b..0f5e03d1ef5c 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -1,4 +1,7 @@ obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \ - clk-mux.o clk-divider.o + clk-mux.o clk-divider.o clk-fixed-factor.o + +# SoCs specific +obj-$(CONFIG_PLAT_SPEAR) += spear/ diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index d5ac6a75ea57..8ea11b444528 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -45,7 +45,6 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, return parent_rate / div; } -EXPORT_SYMBOL_GPL(clk_divider_recalc_rate); /* * The reverse of DIV_ROUND_UP: The maximum number which @@ -68,8 +67,8 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, if (divider->flags & CLK_DIVIDER_ONE_BASED) maxdiv--; - if (!best_parent_rate) { - parent_rate = __clk_get_rate(__clk_get_parent(hw->clk)); + if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { + parent_rate = *best_parent_rate; bestdiv = DIV_ROUND_UP(parent_rate, rate); bestdiv = bestdiv == 0 ? 1 : bestdiv; bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; @@ -109,24 +108,18 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, int div; div = clk_divider_bestdiv(hw, rate, prate); - if (prate) - return *prate / div; - else { - unsigned long r; - r = __clk_get_rate(__clk_get_parent(hw->clk)); - return r / div; - } + return *prate / div; } -EXPORT_SYMBOL_GPL(clk_divider_round_rate); -static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate) +static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) { struct clk_divider *divider = to_clk_divider(hw); unsigned int div; unsigned long flags = 0; u32 val; - div = __clk_get_rate(__clk_get_parent(hw->clk)) / rate; + div = parent_rate / rate; if (!(divider->flags & CLK_DIVIDER_ONE_BASED)) div--; @@ -147,15 +140,26 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate) return 0; } -EXPORT_SYMBOL_GPL(clk_divider_set_rate); -struct clk_ops clk_divider_ops = { +const struct clk_ops clk_divider_ops = { .recalc_rate = clk_divider_recalc_rate, .round_rate = clk_divider_round_rate, .set_rate = clk_divider_set_rate, }; EXPORT_SYMBOL_GPL(clk_divider_ops); +/** + * clk_register_divider - register a divider clock with the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust divider + * @shift: number of bits to shift the bitfield + * @width: width of the bitfield + * @clk_divider_flags: divider-specific flags for this clock + * @lock: shared register lock for this clock + */ struct clk *clk_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, @@ -163,38 +167,34 @@ struct clk *clk_register_divider(struct device *dev, const char *name, { struct clk_divider *div; struct clk *clk; + struct clk_init_data init; + /* allocate the divider */ div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL); - if (!div) { pr_err("%s: could not allocate divider clk\n", __func__); - return NULL; + return ERR_PTR(-ENOMEM); } + init.name = name; + init.ops = &clk_divider_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name: NULL); + init.num_parents = (parent_name ? 1 : 0); + /* struct clk_divider assignments */ div->reg = reg; div->shift = shift; div->width = width; div->flags = clk_divider_flags; div->lock = lock; + div->hw.init = &init; - if (parent_name) { - div->parent[0] = kstrdup(parent_name, GFP_KERNEL); - if (!div->parent[0]) - goto out; - } - - clk = clk_register(dev, name, - &clk_divider_ops, &div->hw, - div->parent, - (parent_name ? 1 : 0), - flags); - if (clk) - return clk; + /* register the clock */ + clk = clk_register(dev, &div->hw); -out: - kfree(div->parent[0]); - kfree(div); + if (IS_ERR(clk)) + kfree(div); - return NULL; + return clk; } diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c new file mode 100644 index 000000000000..c8c003e217ad --- /dev/null +++ b/drivers/clk/clk-fixed-factor.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Standard functionality for the common clock API. + */ +#include <linux/module.h> +#include <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/err.h> + +/* + * DOC: basic fixed multiplier and divider clock that cannot gate + * + * Traits of this clock: + * prepare - clk_prepare only ensures that parents are prepared + * enable - clk_enable only ensures that parents are enabled + * rate - rate is fixed. clk->rate = parent->rate / div * mult + * parent - fixed parent. No clk_set_parent support + */ + +#define to_clk_fixed_factor(_hw) container_of(_hw, struct clk_fixed_factor, hw) + +static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_fixed_factor *fix = to_clk_fixed_factor(hw); + + return parent_rate * fix->mult / fix->div; +} + +static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_fixed_factor *fix = to_clk_fixed_factor(hw); + + if (__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT) { + unsigned long best_parent; + + best_parent = (rate / fix->mult) * fix->div; + *prate = __clk_round_rate(__clk_get_parent(hw->clk), + best_parent); + } + + return (*prate / fix->div) * fix->mult; +} + +static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + return 0; +} + +struct clk_ops clk_fixed_factor_ops = { + .round_rate = clk_factor_round_rate, + .set_rate = clk_factor_set_rate, + .recalc_rate = clk_factor_recalc_rate, +}; +EXPORT_SYMBOL_GPL(clk_fixed_factor_ops); + +struct clk *clk_register_fixed_factor(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + unsigned int mult, unsigned int div) +{ + struct clk_fixed_factor *fix; + struct clk_init_data init; + struct clk *clk; + + fix = kmalloc(sizeof(*fix), GFP_KERNEL); + if (!fix) { + pr_err("%s: could not allocate fixed factor clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + /* struct clk_fixed_factor assignments */ + fix->mult = mult; + fix->div = div; + fix->hw.init = &init; + + init.name = name; + init.ops = &clk_fixed_factor_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(dev, &fix->hw); + + if (IS_ERR(clk)) + kfree(fix); + + return clk; +} diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index 90c79fb5d1bd..cbd246229786 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -32,51 +32,50 @@ static unsigned long clk_fixed_rate_recalc_rate(struct clk_hw *hw, { return to_clk_fixed_rate(hw)->fixed_rate; } -EXPORT_SYMBOL_GPL(clk_fixed_rate_recalc_rate); -struct clk_ops clk_fixed_rate_ops = { +const struct clk_ops clk_fixed_rate_ops = { .recalc_rate = clk_fixed_rate_recalc_rate, }; EXPORT_SYMBOL_GPL(clk_fixed_rate_ops); +/** + * clk_register_fixed_rate - register fixed-rate clock with the clock framework + * @dev: device that is registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @fixed_rate: non-adjustable clock rate + */ struct clk *clk_register_fixed_rate(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned long fixed_rate) { struct clk_fixed_rate *fixed; - char **parent_names = NULL; - u8 len; + struct clk *clk; + struct clk_init_data init; + /* allocate fixed-rate clock */ fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL); - if (!fixed) { pr_err("%s: could not allocate fixed clk\n", __func__); return ERR_PTR(-ENOMEM); } + init.name = name; + init.ops = &clk_fixed_rate_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name: NULL); + init.num_parents = (parent_name ? 1 : 0); + /* struct clk_fixed_rate assignments */ fixed->fixed_rate = fixed_rate; + fixed->hw.init = &init; - if (parent_name) { - parent_names = kmalloc(sizeof(char *), GFP_KERNEL); - - if (! parent_names) - goto out; + /* register the clock */ + clk = clk_register(dev, &fixed->hw); - len = sizeof(char) * strlen(parent_name); - - parent_names[0] = kmalloc(len, GFP_KERNEL); - - if (!parent_names[0]) - goto out; - - strncpy(parent_names[0], parent_name, len); - } + if (IS_ERR(clk)) + kfree(fixed); -out: - return clk_register(dev, name, - &clk_fixed_rate_ops, &fixed->hw, - parent_names, - (parent_name ? 1 : 0), - flags); + return clk; } diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index b5902e2ef2fd..578465e04be6 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -28,32 +28,38 @@ #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) -static void clk_gate_set_bit(struct clk_gate *gate) +/* + * It works on following logic: + * + * For enabling clock, enable = 1 + * set2dis = 1 -> clear bit -> set = 0 + * set2dis = 0 -> set bit -> set = 1 + * + * For disabling clock, enable = 0 + * set2dis = 1 -> set bit -> set = 1 + * set2dis = 0 -> clear bit -> set = 0 + * + * So, result is always: enable xor set2dis. + */ +static void clk_gate_endisable(struct clk_hw *hw, int enable) { - u32 reg; + struct clk_gate *gate = to_clk_gate(hw); + int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; unsigned long flags = 0; + u32 reg; + + set ^= enable; if (gate->lock) spin_lock_irqsave(gate->lock, flags); reg = readl(gate->reg); - reg |= BIT(gate->bit_idx); - writel(reg, gate->reg); - - if (gate->lock) - spin_unlock_irqrestore(gate->lock, flags); -} - -static void clk_gate_clear_bit(struct clk_gate *gate) -{ - u32 reg; - unsigned long flags = 0; - if (gate->lock) - spin_lock_irqsave(gate->lock, flags); + if (set) + reg |= BIT(gate->bit_idx); + else + reg &= ~BIT(gate->bit_idx); - reg = readl(gate->reg); - reg &= ~BIT(gate->bit_idx); writel(reg, gate->reg); if (gate->lock) @@ -62,27 +68,15 @@ static void clk_gate_clear_bit(struct clk_gate *gate) static int clk_gate_enable(struct clk_hw *hw) { - struct clk_gate *gate = to_clk_gate(hw); - - if (gate->flags & CLK_GATE_SET_TO_DISABLE) - clk_gate_clear_bit(gate); - else - clk_gate_set_bit(gate); + clk_gate_endisable(hw, 1); return 0; } -EXPORT_SYMBOL_GPL(clk_gate_enable); static void clk_gate_disable(struct clk_hw *hw) { - struct clk_gate *gate = to_clk_gate(hw); - - if (gate->flags & CLK_GATE_SET_TO_DISABLE) - clk_gate_set_bit(gate); - else - clk_gate_clear_bit(gate); + clk_gate_endisable(hw, 0); } -EXPORT_SYMBOL_GPL(clk_gate_disable); static int clk_gate_is_enabled(struct clk_hw *hw) { @@ -99,15 +93,25 @@ static int clk_gate_is_enabled(struct clk_hw *hw) return reg ? 1 : 0; } -EXPORT_SYMBOL_GPL(clk_gate_is_enabled); -struct clk_ops clk_gate_ops = { +const struct clk_ops clk_gate_ops = { .enable = clk_gate_enable, .disable = clk_gate_disable, .is_enabled = clk_gate_is_enabled, }; EXPORT_SYMBOL_GPL(clk_gate_ops); +/** + * clk_register_gate - register a gate clock with the clock framework + * @dev: device that is registering this clock + * @name: name of this clock + * @parent_name: name of this clock's parent + * @flags: framework-specific flags for this clock + * @reg: register address to control gating of this clock + * @bit_idx: which bit in the register controls gating of this clock + * @clk_gate_flags: gate-specific flags for this clock + * @lock: shared register lock for this clock + */ struct clk *clk_register_gate(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, @@ -115,36 +119,32 @@ struct clk *clk_register_gate(struct device *dev, const char *name, { struct clk_gate *gate; struct clk *clk; + struct clk_init_data init; + /* allocate the gate */ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); - if (!gate) { pr_err("%s: could not allocate gated clk\n", __func__); - return NULL; + return ERR_PTR(-ENOMEM); } + init.name = name; + init.ops = &clk_gate_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name: NULL); + init.num_parents = (parent_name ? 1 : 0); + /* struct clk_gate assignments */ gate->reg = reg; gate->bit_idx = bit_idx; gate->flags = clk_gate_flags; gate->lock = lock; + gate->hw.init = &init; - if (parent_name) { - gate->parent[0] = kstrdup(parent_name, GFP_KERNEL); - if (!gate->parent[0]) - goto out; - } + clk = clk_register(dev, &gate->hw); + + if (IS_ERR(clk)) + kfree(gate); - clk = clk_register(dev, name, - &clk_gate_ops, &gate->hw, - gate->parent, - (parent_name ? 1 : 0), - flags); - if (clk) - return clk; -out: - kfree(gate->parent[0]); - kfree(gate); - - return NULL; + return clk; } diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index c71ad1f41a97..fd36a8ea73d9 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -55,7 +55,6 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) return val; } -EXPORT_SYMBOL_GPL(clk_mux_get_parent); static int clk_mux_set_parent(struct clk_hw *hw, u8 index) { @@ -82,35 +81,47 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) return 0; } -EXPORT_SYMBOL_GPL(clk_mux_set_parent); -struct clk_ops clk_mux_ops = { +const struct clk_ops clk_mux_ops = { .get_parent = clk_mux_get_parent, .set_parent = clk_mux_set_parent, }; EXPORT_SYMBOL_GPL(clk_mux_ops); struct clk *clk_register_mux(struct device *dev, const char *name, - char **parent_names, u8 num_parents, unsigned long flags, + const char **parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_mux_flags, spinlock_t *lock) { struct clk_mux *mux; + struct clk *clk; + struct clk_init_data init; - mux = kmalloc(sizeof(struct clk_mux), GFP_KERNEL); - + /* allocate the mux */ + mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); if (!mux) { pr_err("%s: could not allocate mux clk\n", __func__); return ERR_PTR(-ENOMEM); } + init.name = name; + init.ops = &clk_mux_ops; + init.flags = flags; + init.parent_names = parent_names; + init.num_parents = num_parents; + /* struct clk_mux assignments */ mux->reg = reg; mux->shift = shift; mux->width = width; mux->flags = clk_mux_flags; mux->lock = lock; + mux->hw.init = &init; + + clk = clk_register(dev, &mux->hw); + + if (IS_ERR(clk)) + kfree(mux); - return clk_register(dev, name, &clk_mux_ops, &mux->hw, - parent_names, num_parents, flags); + return clk; } diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 9cf6f59e3e19..e5d5dc13bcfd 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -194,9 +194,8 @@ static int __init clk_debug_init(void) late_initcall(clk_debug_init); #else static inline int clk_debug_register(struct clk *clk) { return 0; } -#endif /* CONFIG_COMMON_CLK_DEBUG */ +#endif -#ifdef CONFIG_COMMON_CLK_DISABLE_UNUSED /* caller must hold prepare_lock */ static void clk_disable_unused_subtree(struct clk *clk) { @@ -246,9 +245,6 @@ static int clk_disable_unused(void) return 0; } late_initcall(clk_disable_unused); -#else -static inline int clk_disable_unused(struct clk *clk) { return 0; } -#endif /* CONFIG_COMMON_CLK_DISABLE_UNUSED */ /*** helper functions ***/ @@ -287,7 +283,7 @@ unsigned long __clk_get_rate(struct clk *clk) unsigned long ret; if (!clk) { - ret = -EINVAL; + ret = 0; goto out; } @@ -297,7 +293,7 @@ unsigned long __clk_get_rate(struct clk *clk) goto out; if (!clk->parent) - ret = -ENODEV; + ret = 0; out: return ret; @@ -562,7 +558,7 @@ EXPORT_SYMBOL_GPL(clk_enable); * @clk: the clk whose rate is being returned * * Simply returns the cached rate of the clk. Does not query the hardware. If - * clk is NULL then returns -EINVAL. + * clk is NULL then returns 0. */ unsigned long clk_get_rate(struct clk *clk) { @@ -584,18 +580,22 @@ EXPORT_SYMBOL_GPL(clk_get_rate); */ unsigned long __clk_round_rate(struct clk *clk, unsigned long rate) { - unsigned long unused; + unsigned long parent_rate = 0; if (!clk) return -EINVAL; - if (!clk->ops->round_rate) - return clk->rate; + if (!clk->ops->round_rate) { + if (clk->flags & CLK_SET_RATE_PARENT) + return __clk_round_rate(clk->parent, rate); + else + return clk->rate; + } - if (clk->flags & CLK_SET_RATE_PARENT) - return clk->ops->round_rate(clk->hw, rate, &unused); - else - return clk->ops->round_rate(clk->hw, rate, NULL); + if (clk->parent) + parent_rate = clk->parent->rate; + + return clk->ops->round_rate(clk->hw, rate, &parent_rate); } /** @@ -765,25 +765,41 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate) static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) { struct clk *top = clk; - unsigned long best_parent_rate = clk->parent->rate; + unsigned long best_parent_rate = 0; unsigned long new_rate; - if (!clk->ops->round_rate && !(clk->flags & CLK_SET_RATE_PARENT)) { - clk->new_rate = clk->rate; + /* sanity */ + if (IS_ERR_OR_NULL(clk)) return NULL; + + /* save parent rate, if it exists */ + if (clk->parent) + best_parent_rate = clk->parent->rate; + + /* never propagate up to the parent */ + if (!(clk->flags & CLK_SET_RATE_PARENT)) { + if (!clk->ops->round_rate) { + clk->new_rate = clk->rate; + return NULL; + } + new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); + goto out; } - if (!clk->ops->round_rate && (clk->flags & CLK_SET_RATE_PARENT)) { + /* need clk->parent from here on out */ + if (!clk->parent) { + pr_debug("%s: %s has NULL parent\n", __func__, clk->name); + return NULL; + } + + if (!clk->ops->round_rate) { top = clk_calc_new_rates(clk->parent, rate); - new_rate = clk->new_rate = clk->parent->new_rate; + new_rate = clk->parent->new_rate; goto out; } - if (clk->flags & CLK_SET_RATE_PARENT) - new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); - else - new_rate = clk->ops->round_rate(clk->hw, rate, NULL); + new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); if (best_parent_rate != clk->parent->rate) { top = clk_calc_new_rates(clk->parent, best_parent_rate); @@ -839,7 +855,7 @@ static void clk_change_rate(struct clk *clk) old_rate = clk->rate; if (clk->ops->set_rate) - clk->ops->set_rate(clk->hw, clk->new_rate); + clk->ops->set_rate(clk->hw, clk->new_rate, clk->parent->rate); if (clk->ops->recalc_rate) clk->rate = clk->ops->recalc_rate(clk->hw, @@ -859,38 +875,19 @@ static void clk_change_rate(struct clk *clk) * @clk: the clk whose rate is being changed * @rate: the new rate for clk * - * In the simplest case clk_set_rate will only change the rate of clk. + * In the simplest case clk_set_rate will only adjust the rate of clk. * - * If clk has the CLK_SET_RATE_GATE flag set and it is enabled this call - * will fail; only when the clk is disabled will it be able to change - * its rate. + * Setting the CLK_SET_RATE_PARENT flag allows the rate change operation to + * propagate up to clk's parent; whether or not this happens depends on the + * outcome of clk's .round_rate implementation. If *parent_rate is unchanged + * after calling .round_rate then upstream parent propagation is ignored. If + * *parent_rate comes back with a new rate for clk's parent then we propagate + * up to clk's parent and set it's rate. Upward propagation will continue + * until either a clk does not support the CLK_SET_RATE_PARENT flag or + * .round_rate stops requesting changes to clk's parent_rate. * - * Setting the CLK_SET_RATE_PARENT flag allows clk_set_rate to - * recursively propagate up to clk's parent; whether or not this happens - * depends on the outcome of clk's .round_rate implementation. If - * *parent_rate is 0 after calling .round_rate then upstream parent - * propagation is ignored. If *parent_rate comes back with a new rate - * for clk's parent then we propagate up to clk's parent and set it's - * rate. Upward propagation will continue until either a clk does not - * support the CLK_SET_RATE_PARENT flag or .round_rate stops requesting - * changes to clk's parent_rate. If there is a failure during upstream - * propagation then clk_set_rate will unwind and restore each clk's rate - * that had been successfully changed. Afterwards a rate change abort - * notification will be propagated downstream, starting from the clk - * that failed. - * - * At the end of all of the rate setting, clk_set_rate internally calls - * __clk_recalc_rates and propagates the rate changes downstream, - * starting from the highest clk whose rate was changed. This has the - * added benefit of propagating post-rate change notifiers. - * - * Note that while post-rate change and rate change abort notifications - * are guaranteed to be sent to a clk only once per call to - * clk_set_rate, pre-change notifications will be sent for every clk - * whose rate is changed. Stacking pre-change notifications is noisy - * for the drivers subscribed to them, but this allows drivers to react - * to intermediate clk rate changes up until the point where the final - * rate is achieved at the end of upstream propagation. + * Rate changes are accomplished via tree traversal that also recalculates the + * rates for the clocks and fires off POST_RATE_CHANGE notifiers. * * Returns 0 on success, -EERROR otherwise. */ @@ -906,6 +903,11 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (rate == clk->rate) goto out; + if ((clk->flags & CLK_SET_RATE_GATE) && __clk_is_enabled(clk)) { + ret = -EBUSY; + goto out; + } + /* calculate new rates and get the topmost changed clock */ top = clk_calc_new_rates(clk, rate); if (!top) { @@ -1175,40 +1177,41 @@ EXPORT_SYMBOL_GPL(clk_set_parent); * * Initializes the lists in struct clk, queries the hardware for the * parent and rate and sets them both. - * - * Any struct clk passed into __clk_init must have the following members - * populated: - * .name - * .ops - * .hw - * .parent_names - * .num_parents - * .flags - * - * Essentially, everything that would normally be passed into clk_register is - * assumed to be initialized already in __clk_init. The other members may be - * populated, but are optional. - * - * __clk_init is only exposed via clk-private.h and is intended for use with - * very large numbers of clocks that need to be statically initialized. It is - * a layering violation to include clk-private.h from any code which implements - * a clock's .ops; as such any statically initialized clock data MUST be in a - * separate C file from the logic that implements it's operations. */ -void __clk_init(struct device *dev, struct clk *clk) +int __clk_init(struct device *dev, struct clk *clk) { - int i; + int i, ret = 0; struct clk *orphan; struct hlist_node *tmp, *tmp2; if (!clk) - return; + return -EINVAL; mutex_lock(&prepare_lock); /* check to see if a clock with this name is already registered */ - if (__clk_lookup(clk->name)) + if (__clk_lookup(clk->name)) { + pr_debug("%s: clk %s already initialized\n", + __func__, clk->name); + ret = -EEXIST; + goto out; + } + + /* check that clk_ops are sane. See Documentation/clk.txt */ + if (clk->ops->set_rate && + !(clk->ops->round_rate && clk->ops->recalc_rate)) { + pr_warning("%s: %s must implement .round_rate & .recalc_rate\n", + __func__, clk->name); + ret = -EINVAL; goto out; + } + + if (clk->ops->set_parent && !clk->ops->get_parent) { + pr_warning("%s: %s must implement .get_parent & .set_parent\n", + __func__, clk->name); + ret = -EINVAL; + goto out; + } /* throw a WARN if any entries in parent_names are NULL */ for (i = 0; i < clk->num_parents; i++) @@ -1302,45 +1305,118 @@ void __clk_init(struct device *dev, struct clk *clk) out: mutex_unlock(&prepare_lock); - return; + return ret; } /** + * __clk_register - register a clock and return a cookie. + * + * Same as clk_register, except that the .clk field inside hw shall point to a + * preallocated (generally statically allocated) struct clk. None of the fields + * of the struct clk need to be initialized. + * + * The data pointed to by .init and .clk field shall NOT be marked as init + * data. + * + * __clk_register is only exposed via clk-private.h and is intended for use with + * very large numbers of clocks that need to be statically initialized. It is + * a layering violation to include clk-private.h from any code which implements + * a clock's .ops; as such any statically initialized clock data MUST be in a + * separate C file from the logic that implements it's operations. Returns 0 + * on success, otherwise an error code. + */ +struct clk *__clk_register(struct device *dev, struct clk_hw *hw) +{ + int ret; + struct clk *clk; + + clk = hw->clk; + clk->name = hw->init->name; + clk->ops = hw->init->ops; + clk->hw = hw; + clk->flags = hw->init->flags; + clk->parent_names = hw->init->parent_names; + clk->num_parents = hw->init->num_parents; + + ret = __clk_init(dev, clk); + if (ret) + return ERR_PTR(ret); + + return clk; +} +EXPORT_SYMBOL_GPL(__clk_register); + +/** * clk_register - allocate a new clock, register it and return an opaque cookie * @dev: device that is registering this clock - * @name: clock name - * @ops: operations this clock supports * @hw: link to hardware-specific clock data - * @parent_names: array of string names for all possible parents - * @num_parents: number of possible parents - * @flags: framework-level hints and quirks * * clk_register is the primary interface for populating the clock tree with new * clock nodes. It returns a pointer to the newly allocated struct clk which * cannot be dereferenced by driver code but may be used in conjuction with the - * rest of the clock API. + * rest of the clock API. In the event of an error clk_register will return an + * error code; drivers must test for an error code after calling clk_register. */ -struct clk *clk_register(struct device *dev, const char *name, - const struct clk_ops *ops, struct clk_hw *hw, - char **parent_names, u8 num_parents, unsigned long flags) +struct clk *clk_register(struct device *dev, struct clk_hw *hw) { + int i, ret; struct clk *clk; clk = kzalloc(sizeof(*clk), GFP_KERNEL); - if (!clk) - return NULL; + if (!clk) { + pr_err("%s: could not allocate clk\n", __func__); + ret = -ENOMEM; + goto fail_out; + } - clk->name = name; - clk->ops = ops; + clk->name = kstrdup(hw->init->name, GFP_KERNEL); + if (!clk->name) { + pr_err("%s: could not allocate clk->name\n", __func__); + ret = -ENOMEM; + goto fail_name; + } + clk->ops = hw->init->ops; clk->hw = hw; - clk->flags = flags; - clk->parent_names = parent_names; - clk->num_parents = num_parents; + clk->flags = hw->init->flags; + clk->num_parents = hw->init->num_parents; hw->clk = clk; - __clk_init(dev, clk); + /* allocate local copy in case parent_names is __initdata */ + clk->parent_names = kzalloc((sizeof(char*) * clk->num_parents), + GFP_KERNEL); - return clk; + if (!clk->parent_names) { + pr_err("%s: could not allocate clk->parent_names\n", __func__); + ret = -ENOMEM; + goto fail_parent_names; + } + + + /* copy each string name in case parent_names is __initdata */ + for (i = 0; i < clk->num_parents; i++) { + clk->parent_names[i] = kstrdup(hw->init->parent_names[i], + GFP_KERNEL); + if (!clk->parent_names[i]) { + pr_err("%s: could not copy parent_names\n", __func__); + ret = -ENOMEM; + goto fail_parent_names_copy; + } + } + + ret = __clk_init(dev, clk); + if (!ret) + return clk; + +fail_parent_names_copy: + while (--i >= 0) + kfree(clk->parent_names[i]); + kfree(clk->parent_names); +fail_parent_names: + kfree(clk->name); +fail_name: + kfree(clk); +fail_out: + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(clk_register); diff --git a/drivers/clk/spear/Makefile b/drivers/clk/spear/Makefile new file mode 100644 index 000000000000..335886049c83 --- /dev/null +++ b/drivers/clk/spear/Makefile @@ -0,0 +1,8 @@ +# +# SPEAr Clock specific Makefile +# + +obj-y += clk.o clk-aux-synth.o clk-frac-synth.o clk-gpt-synth.o clk-vco-pll.o + +obj-$(CONFIG_ARCH_SPEAR3XX) += spear3xx_clock.o +obj-$(CONFIG_ARCH_SPEAR6XX) += spear6xx_clock.o diff --git a/drivers/clk/spear/clk-aux-synth.c b/drivers/clk/spear/clk-aux-synth.c new file mode 100644 index 000000000000..af34074e702b --- /dev/null +++ b/drivers/clk/spear/clk-aux-synth.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * Auxiliary Synthesizer clock implementation + */ + +#define pr_fmt(fmt) "clk-aux-synth: " fmt + +#include <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include "clk.h" + +/* + * DOC: Auxiliary Synthesizer clock + * + * Aux synth gives rate for different values of eq, x and y + * + * Fout from synthesizer can be given from two equations: + * Fout1 = (Fin * X/Y)/2 EQ1 + * Fout2 = Fin * X/Y EQ2 + */ + +#define to_clk_aux(_hw) container_of(_hw, struct clk_aux, hw) + +static struct aux_clk_masks default_aux_masks = { + .eq_sel_mask = AUX_EQ_SEL_MASK, + .eq_sel_shift = AUX_EQ_SEL_SHIFT, + .eq1_mask = AUX_EQ1_SEL, + .eq2_mask = AUX_EQ2_SEL, + .xscale_sel_mask = AUX_XSCALE_MASK, + .xscale_sel_shift = AUX_XSCALE_SHIFT, + .yscale_sel_mask = AUX_YSCALE_MASK, + .yscale_sel_shift = AUX_YSCALE_SHIFT, + .enable_bit = AUX_SYNT_ENB, +}; + +static unsigned long aux_calc_rate(struct clk_hw *hw, unsigned long prate, + int index) +{ + struct clk_aux *aux = to_clk_aux(hw); + struct aux_rate_tbl *rtbl = aux->rtbl; + u8 eq = rtbl[index].eq ? 1 : 2; + + return (((prate / 10000) * rtbl[index].xscale) / + (rtbl[index].yscale * eq)) * 10000; +} + +static long clk_aux_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + struct clk_aux *aux = to_clk_aux(hw); + int unused; + + return clk_round_rate_index(hw, drate, *prate, aux_calc_rate, + aux->rtbl_cnt, &unused); +} + +static unsigned long clk_aux_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_aux *aux = to_clk_aux(hw); + unsigned int num = 1, den = 1, val, eqn; + unsigned long flags = 0; + + if (aux->lock) + spin_lock_irqsave(aux->lock, flags); + + val = readl_relaxed(aux->reg); + + if (aux->lock) + spin_unlock_irqrestore(aux->lock, flags); + + eqn = (val >> aux->masks->eq_sel_shift) & aux->masks->eq_sel_mask; + if (eqn == aux->masks->eq1_mask) + den = 2; + + /* calculate numerator */ + num = (val >> aux->masks->xscale_sel_shift) & + aux->masks->xscale_sel_mask; + + /* calculate denominator */ + den *= (val >> aux->masks->yscale_sel_shift) & + aux->masks->yscale_sel_mask; + + if (!den) + return 0; + + return (((parent_rate / 10000) * num) / den) * 10000; +} + +/* Configures new clock rate of aux */ +static int clk_aux_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_aux *aux = to_clk_aux(hw); + struct aux_rate_tbl *rtbl = aux->rtbl; + unsigned long val, flags = 0; + int i; + + clk_round_rate_index(hw, drate, prate, aux_calc_rate, aux->rtbl_cnt, + &i); + + if (aux->lock) + spin_lock_irqsave(aux->lock, flags); + + val = readl_relaxed(aux->reg) & + ~(aux->masks->eq_sel_mask << aux->masks->eq_sel_shift); + val |= (rtbl[i].eq & aux->masks->eq_sel_mask) << + aux->masks->eq_sel_shift; + val &= ~(aux->masks->xscale_sel_mask << aux->masks->xscale_sel_shift); + val |= (rtbl[i].xscale & aux->masks->xscale_sel_mask) << + aux->masks->xscale_sel_shift; + val &= ~(aux->masks->yscale_sel_mask << aux->masks->yscale_sel_shift); + val |= (rtbl[i].yscale & aux->masks->yscale_sel_mask) << + aux->masks->yscale_sel_shift; + writel_relaxed(val, aux->reg); + + if (aux->lock) + spin_unlock_irqrestore(aux->lock, flags); + + return 0; +} + +static struct clk_ops clk_aux_ops = { + .recalc_rate = clk_aux_recalc_rate, + .round_rate = clk_aux_round_rate, + .set_rate = clk_aux_set_rate, +}; + +struct clk *clk_register_aux(const char *aux_name, const char *gate_name, + const char *parent_name, unsigned long flags, void __iomem *reg, + struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, + u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk) +{ + struct clk_aux *aux; + struct clk_init_data init; + struct clk *clk; + + if (!aux_name || !parent_name || !reg || !rtbl || !rtbl_cnt) { + pr_err("Invalid arguments passed"); + return ERR_PTR(-EINVAL); + } + + aux = kzalloc(sizeof(*aux), GFP_KERNEL); + if (!aux) { + pr_err("could not allocate aux clk\n"); + return ERR_PTR(-ENOMEM); + } + + /* struct clk_aux assignments */ + if (!masks) + aux->masks = &default_aux_masks; + else + aux->masks = masks; + + aux->reg = reg; + aux->rtbl = rtbl; + aux->rtbl_cnt = rtbl_cnt; + aux->lock = lock; + aux->hw.init = &init; + + init.name = aux_name; + init.ops = &clk_aux_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &aux->hw); + if (IS_ERR_OR_NULL(clk)) + goto free_aux; + + if (gate_name) { + struct clk *tgate_clk; + + tgate_clk = clk_register_gate(NULL, gate_name, aux_name, 0, reg, + aux->masks->enable_bit, 0, lock); + if (IS_ERR_OR_NULL(tgate_clk)) + goto free_aux; + + if (gate_clk) + *gate_clk = tgate_clk; + } + + return clk; + +free_aux: + kfree(aux); + pr_err("clk register failed\n"); + + return NULL; +} diff --git a/drivers/clk/spear/clk-frac-synth.c b/drivers/clk/spear/clk-frac-synth.c new file mode 100644 index 000000000000..4dbdb3fe18e0 --- /dev/null +++ b/drivers/clk/spear/clk-frac-synth.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * Fractional Synthesizer clock implementation + */ + +#define pr_fmt(fmt) "clk-frac-synth: " fmt + +#include <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include "clk.h" + +#define DIV_FACTOR_MASK 0x1FFFF + +/* + * DOC: Fractional Synthesizer clock + * + * Fout from synthesizer can be given from below equation: + * + * Fout= Fin/2*div (division factor) + * div is 17 bits:- + * 0-13 (fractional part) + * 14-16 (integer part) + * div is (16-14 bits).(13-0 bits) (in binary) + * + * Fout = Fin/(2 * div) + * Fout = ((Fin / 10000)/(2 * div)) * 10000 + * Fout = (2^14 * (Fin / 10000)/(2^14 * (2 * div))) * 10000 + * Fout = (((Fin / 10000) << 14)/(2 * (div << 14))) * 10000 + * + * div << 14 simply 17 bit value written at register. + * Max error due to scaling down by 10000 is 10 KHz + */ + +#define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw) + +static unsigned long frac_calc_rate(struct clk_hw *hw, unsigned long prate, + int index) +{ + struct clk_frac *frac = to_clk_frac(hw); + struct frac_rate_tbl *rtbl = frac->rtbl; + + prate /= 10000; + prate <<= 14; + prate /= (2 * rtbl[index].div); + prate *= 10000; + + return prate; +} + +static long clk_frac_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + struct clk_frac *frac = to_clk_frac(hw); + int unused; + + return clk_round_rate_index(hw, drate, *prate, frac_calc_rate, + frac->rtbl_cnt, &unused); +} + +static unsigned long clk_frac_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_frac *frac = to_clk_frac(hw); + unsigned long flags = 0; + unsigned int div = 1, val; + + if (frac->lock) + spin_lock_irqsave(frac->lock, flags); + + val = readl_relaxed(frac->reg); + + if (frac->lock) + spin_unlock_irqrestore(frac->lock, flags); + + div = val & DIV_FACTOR_MASK; + + if (!div) + return 0; + + parent_rate = parent_rate / 10000; + + parent_rate = (parent_rate << 14) / (2 * div); + return parent_rate * 10000; +} + +/* Configures new clock rate of frac */ +static int clk_frac_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_frac *frac = to_clk_frac(hw); + struct frac_rate_tbl *rtbl = frac->rtbl; + unsigned long flags = 0, val; + int i; + + clk_round_rate_index(hw, drate, prate, frac_calc_rate, frac->rtbl_cnt, + &i); + + if (frac->lock) + spin_lock_irqsave(frac->lock, flags); + + val = readl_relaxed(frac->reg) & ~DIV_FACTOR_MASK; + val |= rtbl[i].div & DIV_FACTOR_MASK; + writel_relaxed(val, frac->reg); + + if (frac->lock) + spin_unlock_irqrestore(frac->lock, flags); + + return 0; +} + +struct clk_ops clk_frac_ops = { + .recalc_rate = clk_frac_recalc_rate, + .round_rate = clk_frac_round_rate, + .set_rate = clk_frac_set_rate, +}; + +struct clk *clk_register_frac(const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, + struct frac_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock) +{ + struct clk_init_data init; + struct clk_frac *frac; + struct clk *clk; + + if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) { + pr_err("Invalid arguments passed"); + return ERR_PTR(-EINVAL); + } + + frac = kzalloc(sizeof(*frac), GFP_KERNEL); + if (!frac) { + pr_err("could not allocate frac clk\n"); + return ERR_PTR(-ENOMEM); + } + + /* struct clk_frac assignments */ + frac->reg = reg; + frac->rtbl = rtbl; + frac->rtbl_cnt = rtbl_cnt; + frac->lock = lock; + frac->hw.init = &init; + + init.name = name; + init.ops = &clk_frac_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &frac->hw); + if (!IS_ERR_OR_NULL(clk)) + return clk; + + pr_err("clk register failed\n"); + kfree(frac); + + return NULL; +} diff --git a/drivers/clk/spear/clk-gpt-synth.c b/drivers/clk/spear/clk-gpt-synth.c new file mode 100644 index 000000000000..b471c9762a97 --- /dev/null +++ b/drivers/clk/spear/clk-gpt-synth.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * General Purpose Timer Synthesizer clock implementation + */ + +#define pr_fmt(fmt) "clk-gpt-synth: " fmt + +#include <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include "clk.h" + +#define GPT_MSCALE_MASK 0xFFF +#define GPT_NSCALE_SHIFT 12 +#define GPT_NSCALE_MASK 0xF + +/* + * DOC: General Purpose Timer Synthesizer clock + * + * Calculates gpt synth clk rate for different values of mscale and nscale + * + * Fout= Fin/((2 ^ (N+1)) * (M+1)) + */ + +#define to_clk_gpt(_hw) container_of(_hw, struct clk_gpt, hw) + +static unsigned long gpt_calc_rate(struct clk_hw *hw, unsigned long prate, + int index) +{ + struct clk_gpt *gpt = to_clk_gpt(hw); + struct gpt_rate_tbl *rtbl = gpt->rtbl; + + prate /= ((1 << (rtbl[index].nscale + 1)) * (rtbl[index].mscale + 1)); + + return prate; +} + +static long clk_gpt_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + struct clk_gpt *gpt = to_clk_gpt(hw); + int unused; + + return clk_round_rate_index(hw, drate, *prate, gpt_calc_rate, + gpt->rtbl_cnt, &unused); +} + +static unsigned long clk_gpt_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_gpt *gpt = to_clk_gpt(hw); + unsigned long flags = 0; + unsigned int div = 1, val; + + if (gpt->lock) + spin_lock_irqsave(gpt->lock, flags); + + val = readl_relaxed(gpt->reg); + + if (gpt->lock) + spin_unlock_irqrestore(gpt->lock, flags); + + div += val & GPT_MSCALE_MASK; + div *= 1 << (((val >> GPT_NSCALE_SHIFT) & GPT_NSCALE_MASK) + 1); + + if (!div) + return 0; + + return parent_rate / div; +} + +/* Configures new clock rate of gpt */ +static int clk_gpt_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_gpt *gpt = to_clk_gpt(hw); + struct gpt_rate_tbl *rtbl = gpt->rtbl; + unsigned long flags = 0, val; + int i; + + clk_round_rate_index(hw, drate, prate, gpt_calc_rate, gpt->rtbl_cnt, + &i); + + if (gpt->lock) + spin_lock_irqsave(gpt->lock, flags); + + val = readl(gpt->reg) & ~GPT_MSCALE_MASK; + val &= ~(GPT_NSCALE_MASK << GPT_NSCALE_SHIFT); + + val |= rtbl[i].mscale & GPT_MSCALE_MASK; + val |= (rtbl[i].nscale & GPT_NSCALE_MASK) << GPT_NSCALE_SHIFT; + + writel_relaxed(val, gpt->reg); + + if (gpt->lock) + spin_unlock_irqrestore(gpt->lock, flags); + + return 0; +} + +static struct clk_ops clk_gpt_ops = { + .recalc_rate = clk_gpt_recalc_rate, + .round_rate = clk_gpt_round_rate, + .set_rate = clk_gpt_set_rate, +}; + +struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned + long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8 + rtbl_cnt, spinlock_t *lock) +{ + struct clk_init_data init; + struct clk_gpt *gpt; + struct clk *clk; + + if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) { + pr_err("Invalid arguments passed"); + return ERR_PTR(-EINVAL); + } + + gpt = kzalloc(sizeof(*gpt), GFP_KERNEL); + if (!gpt) { + pr_err("could not allocate gpt clk\n"); + return ERR_PTR(-ENOMEM); + } + + /* struct clk_gpt assignments */ + gpt->reg = reg; + gpt->rtbl = rtbl; + gpt->rtbl_cnt = rtbl_cnt; + gpt->lock = lock; + gpt->hw.init = &init; + + init.name = name; + init.ops = &clk_gpt_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &gpt->hw); + if (!IS_ERR_OR_NULL(clk)) + return clk; + + pr_err("clk register failed\n"); + kfree(gpt); + + return NULL; +} diff --git a/drivers/clk/spear/clk-vco-pll.c b/drivers/clk/spear/clk-vco-pll.c new file mode 100644 index 000000000000..dcd4bdf4b0d9 --- /dev/null +++ b/drivers/clk/spear/clk-vco-pll.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * VCO-PLL clock implementation + */ + +#define pr_fmt(fmt) "clk-vco-pll: " fmt + +#include <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include "clk.h" + +/* + * DOC: VCO-PLL clock + * + * VCO and PLL rate are derived from following equations: + * + * In normal mode + * vco = (2 * M[15:8] * Fin)/N + * + * In Dithered mode + * vco = (2 * M[15:0] * Fin)/(256 * N) + * + * pll_rate = pll/2^p + * + * vco and pll are very closely bound to each other, "vco needs to program: + * mode, m & n" and "pll needs to program p", both share common enable/disable + * logic. + * + * clk_register_vco_pll() registers instances of both vco & pll. + * CLK_SET_RATE_PARENT flag is forced for pll, as it will always pass its + * set_rate to vco. A single rate table exists for both the clocks, which + * configures m, n and p. + */ + +/* PLL_CTR register masks */ +#define PLL_MODE_NORMAL 0 +#define PLL_MODE_FRACTION 1 +#define PLL_MODE_DITH_DSM 2 +#define PLL_MODE_DITH_SSM 3 +#define PLL_MODE_MASK 3 +#define PLL_MODE_SHIFT 3 +#define PLL_ENABLE 2 + +#define PLL_LOCK_SHIFT 0 +#define PLL_LOCK_MASK 1 + +/* PLL FRQ register masks */ +#define PLL_NORM_FDBK_M_MASK 0xFF +#define PLL_NORM_FDBK_M_SHIFT 24 +#define PLL_DITH_FDBK_M_MASK 0xFFFF +#define PLL_DITH_FDBK_M_SHIFT 16 +#define PLL_DIV_P_MASK 0x7 +#define PLL_DIV_P_SHIFT 8 +#define PLL_DIV_N_MASK 0xFF +#define PLL_DIV_N_SHIFT 0 + +#define to_clk_vco(_hw) container_of(_hw, struct clk_vco, hw) +#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw) + +/* Calculates pll clk rate for specific value of mode, m, n and p */ +static unsigned long pll_calc_rate(struct pll_rate_tbl *rtbl, + unsigned long prate, int index, unsigned long *pll_rate) +{ + unsigned long rate = prate; + unsigned int mode; + + mode = rtbl[index].mode ? 256 : 1; + rate = (((2 * rate / 10000) * rtbl[index].m) / (mode * rtbl[index].n)); + + if (pll_rate) + *pll_rate = (rate / (1 << rtbl[index].p)) * 10000; + + return rate * 10000; +} + +static long clk_pll_round_rate_index(struct clk_hw *hw, unsigned long drate, + unsigned long *prate, int *index) +{ + struct clk_pll *pll = to_clk_pll(hw); + unsigned long prev_rate, vco_prev_rate, rate = 0; + unsigned long vco_parent_rate = + __clk_get_rate(__clk_get_parent(__clk_get_parent(hw->clk))); + + if (!prate) { + pr_err("%s: prate is must for pll clk\n", __func__); + return -EINVAL; + } + + for (*index = 0; *index < pll->vco->rtbl_cnt; (*index)++) { + prev_rate = rate; + vco_prev_rate = *prate; + *prate = pll_calc_rate(pll->vco->rtbl, vco_parent_rate, *index, + &rate); + if (drate < rate) { + /* previous clock was best */ + if (*index) { + rate = prev_rate; + *prate = vco_prev_rate; + (*index)--; + } + break; + } + } + + return rate; +} + +static long clk_pll_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + int unused; + + return clk_pll_round_rate_index(hw, drate, prate, &unused); +} + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long + parent_rate) +{ + struct clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + unsigned int p; + + if (pll->vco->lock) + spin_lock_irqsave(pll->vco->lock, flags); + + p = readl_relaxed(pll->vco->cfg_reg); + + if (pll->vco->lock) + spin_unlock_irqrestore(pll->vco->lock, flags); + + p = (p >> PLL_DIV_P_SHIFT) & PLL_DIV_P_MASK; + + return parent_rate / (1 << p); +} + +static int clk_pll_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_pll *pll = to_clk_pll(hw); + struct pll_rate_tbl *rtbl = pll->vco->rtbl; + unsigned long flags = 0, val; + int i; + + clk_pll_round_rate_index(hw, drate, NULL, &i); + + if (pll->vco->lock) + spin_lock_irqsave(pll->vco->lock, flags); + + val = readl_relaxed(pll->vco->cfg_reg); + val &= ~(PLL_DIV_P_MASK << PLL_DIV_P_SHIFT); + val |= (rtbl[i].p & PLL_DIV_P_MASK) << PLL_DIV_P_SHIFT; + writel_relaxed(val, pll->vco->cfg_reg); + + if (pll->vco->lock) + spin_unlock_irqrestore(pll->vco->lock, flags); + + return 0; +} + +static struct clk_ops clk_pll_ops = { + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_round_rate, + .set_rate = clk_pll_set_rate, +}; + +static inline unsigned long vco_calc_rate(struct clk_hw *hw, + unsigned long prate, int index) +{ + struct clk_vco *vco = to_clk_vco(hw); + + return pll_calc_rate(vco->rtbl, prate, index, NULL); +} + +static long clk_vco_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + struct clk_vco *vco = to_clk_vco(hw); + int unused; + + return clk_round_rate_index(hw, drate, *prate, vco_calc_rate, + vco->rtbl_cnt, &unused); +} + +static unsigned long clk_vco_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_vco *vco = to_clk_vco(hw); + unsigned long flags = 0; + unsigned int num = 2, den = 0, val, mode = 0; + + if (vco->lock) + spin_lock_irqsave(vco->lock, flags); + + mode = (readl_relaxed(vco->mode_reg) >> PLL_MODE_SHIFT) & PLL_MODE_MASK; + + val = readl_relaxed(vco->cfg_reg); + + if (vco->lock) + spin_unlock_irqrestore(vco->lock, flags); + + den = (val >> PLL_DIV_N_SHIFT) & PLL_DIV_N_MASK; + + /* calculate numerator & denominator */ + if (!mode) { + /* Normal mode */ + num *= (val >> PLL_NORM_FDBK_M_SHIFT) & PLL_NORM_FDBK_M_MASK; + } else { + /* Dithered mode */ + num *= (val >> PLL_DITH_FDBK_M_SHIFT) & PLL_DITH_FDBK_M_MASK; + den *= 256; + } + + if (!den) { + WARN(1, "%s: denominator can't be zero\n", __func__); + return 0; + } + + return (((parent_rate / 10000) * num) / den) * 10000; +} + +/* Configures new clock rate of vco */ +static int clk_vco_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_vco *vco = to_clk_vco(hw); + struct pll_rate_tbl *rtbl = vco->rtbl; + unsigned long flags = 0, val; + int i; + + clk_round_rate_index(hw, drate, prate, vco_calc_rate, vco->rtbl_cnt, + &i); + + if (vco->lock) + spin_lock_irqsave(vco->lock, flags); + + val = readl_relaxed(vco->mode_reg); + val &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT); + val |= (rtbl[i].mode & PLL_MODE_MASK) << PLL_MODE_SHIFT; + writel_relaxed(val, vco->mode_reg); + + val = readl_relaxed(vco->cfg_reg); + val &= ~(PLL_DIV_N_MASK << PLL_DIV_N_SHIFT); + val |= (rtbl[i].n & PLL_DIV_N_MASK) << PLL_DIV_N_SHIFT; + + val &= ~(PLL_DITH_FDBK_M_MASK << PLL_DITH_FDBK_M_SHIFT); + if (rtbl[i].mode) + val |= (rtbl[i].m & PLL_DITH_FDBK_M_MASK) << + PLL_DITH_FDBK_M_SHIFT; + else + val |= (rtbl[i].m & PLL_NORM_FDBK_M_MASK) << + PLL_NORM_FDBK_M_SHIFT; + + writel_relaxed(val, vco->cfg_reg); + + if (vco->lock) + spin_unlock_irqrestore(vco->lock, flags); + + return 0; +} + +static struct clk_ops clk_vco_ops = { + .recalc_rate = clk_vco_recalc_rate, + .round_rate = clk_vco_round_rate, + .set_rate = clk_vco_set_rate, +}; + +struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name, + const char *vco_gate_name, const char *parent_name, + unsigned long flags, void __iomem *mode_reg, void __iomem + *cfg_reg, struct pll_rate_tbl *rtbl, u8 rtbl_cnt, + spinlock_t *lock, struct clk **pll_clk, + struct clk **vco_gate_clk) +{ + struct clk_vco *vco; + struct clk_pll *pll; + struct clk *vco_clk, *tpll_clk, *tvco_gate_clk; + struct clk_init_data vco_init, pll_init; + const char **vco_parent_name; + + if (!vco_name || !pll_name || !parent_name || !mode_reg || !cfg_reg || + !rtbl || !rtbl_cnt) { + pr_err("Invalid arguments passed"); + return ERR_PTR(-EINVAL); + } + + vco = kzalloc(sizeof(*vco), GFP_KERNEL); + if (!vco) { + pr_err("could not allocate vco clk\n"); + return ERR_PTR(-ENOMEM); + } + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("could not allocate pll clk\n"); + goto free_vco; + } + + /* struct clk_vco assignments */ + vco->mode_reg = mode_reg; + vco->cfg_reg = cfg_reg; + vco->rtbl = rtbl; + vco->rtbl_cnt = rtbl_cnt; + vco->lock = lock; + vco->hw.init = &vco_init; + + pll->vco = vco; + pll->hw.init = &pll_init; + + if (vco_gate_name) { + tvco_gate_clk = clk_register_gate(NULL, vco_gate_name, + parent_name, 0, mode_reg, PLL_ENABLE, 0, lock); + if (IS_ERR_OR_NULL(tvco_gate_clk)) + goto free_pll; + + if (vco_gate_clk) + *vco_gate_clk = tvco_gate_clk; + vco_parent_name = &vco_gate_name; + } else { + vco_parent_name = &parent_name; + } + + vco_init.name = vco_name; + vco_init.ops = &clk_vco_ops; + vco_init.flags = flags; + vco_init.parent_names = vco_parent_name; + vco_init.num_parents = 1; + + pll_init.name = pll_name; + pll_init.ops = &clk_pll_ops; + pll_init.flags = CLK_SET_RATE_PARENT; + pll_init.parent_names = &vco_name; + pll_init.num_parents = 1; + + vco_clk = clk_register(NULL, &vco->hw); + if (IS_ERR_OR_NULL(vco_clk)) + goto free_pll; + + tpll_clk = clk_register(NULL, &pll->hw); + if (IS_ERR_OR_NULL(tpll_clk)) + goto free_pll; + + if (pll_clk) + *pll_clk = tpll_clk; + + return vco_clk; + +free_pll: + kfree(pll); +free_vco: + kfree(vco); + + pr_err("Failed to register vco pll clock\n"); + + return ERR_PTR(-ENOMEM); +} diff --git a/drivers/clk/spear/clk.c b/drivers/clk/spear/clk.c new file mode 100644 index 000000000000..376d4e5ff326 --- /dev/null +++ b/drivers/clk/spear/clk.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * SPEAr clk - Common routines + */ + +#include <linux/clk-provider.h> +#include <linux/types.h> +#include "clk.h" + +long clk_round_rate_index(struct clk_hw *hw, unsigned long drate, + unsigned long parent_rate, clk_calc_rate calc_rate, u8 rtbl_cnt, + int *index) +{ + unsigned long prev_rate, rate = 0; + + for (*index = 0; *index < rtbl_cnt; (*index)++) { + prev_rate = rate; + rate = calc_rate(hw, parent_rate, *index); + if (drate < rate) { + /* previous clock was best */ + if (*index) { + rate = prev_rate; + (*index)--; + } + break; + } + } + + return rate; +} diff --git a/drivers/clk/spear/clk.h b/drivers/clk/spear/clk.h new file mode 100644 index 000000000000..3321c46a071c --- /dev/null +++ b/drivers/clk/spear/clk.h @@ -0,0 +1,134 @@ +/* + * Clock framework definitions for SPEAr platform + * + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __SPEAR_CLK_H +#define __SPEAR_CLK_H + +#include <linux/clk-provider.h> +#include <linux/spinlock_types.h> +#include <linux/types.h> + +/* Auxiliary Synth clk */ +/* Default masks */ +#define AUX_EQ_SEL_SHIFT 30 +#define AUX_EQ_SEL_MASK 1 +#define AUX_EQ1_SEL 0 +#define AUX_EQ2_SEL 1 +#define AUX_XSCALE_SHIFT 16 +#define AUX_XSCALE_MASK 0xFFF +#define AUX_YSCALE_SHIFT 0 +#define AUX_YSCALE_MASK 0xFFF +#define AUX_SYNT_ENB 31 + +struct aux_clk_masks { + u32 eq_sel_mask; + u32 eq_sel_shift; + u32 eq1_mask; + u32 eq2_mask; + u32 xscale_sel_mask; + u32 xscale_sel_shift; + u32 yscale_sel_mask; + u32 yscale_sel_shift; + u32 enable_bit; +}; + +struct aux_rate_tbl { + u16 xscale; + u16 yscale; + u8 eq; +}; + +struct clk_aux { + struct clk_hw hw; + void __iomem *reg; + struct aux_clk_masks *masks; + struct aux_rate_tbl *rtbl; + u8 rtbl_cnt; + spinlock_t *lock; +}; + +/* Fractional Synth clk */ +struct frac_rate_tbl { + u32 div; +}; + +struct clk_frac { + struct clk_hw hw; + void __iomem *reg; + struct frac_rate_tbl *rtbl; + u8 rtbl_cnt; + spinlock_t *lock; +}; + +/* GPT clk */ +struct gpt_rate_tbl { + u16 mscale; + u16 nscale; +}; + +struct clk_gpt { + struct clk_hw hw; + void __iomem *reg; + struct gpt_rate_tbl *rtbl; + u8 rtbl_cnt; + spinlock_t *lock; +}; + +/* VCO-PLL clk */ +struct pll_rate_tbl { + u8 mode; + u16 m; + u8 n; + u8 p; +}; + +struct clk_vco { + struct clk_hw hw; + void __iomem *mode_reg; + void __iomem *cfg_reg; + struct pll_rate_tbl *rtbl; + u8 rtbl_cnt; + spinlock_t *lock; +}; + +struct clk_pll { + struct clk_hw hw; + struct clk_vco *vco; + const char *parent[1]; + spinlock_t *lock; +}; + +typedef unsigned long (*clk_calc_rate)(struct clk_hw *hw, unsigned long prate, + int index); + +/* clk register routines */ +struct clk *clk_register_aux(const char *aux_name, const char *gate_name, + const char *parent_name, unsigned long flags, void __iomem *reg, + struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, + u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk); +struct clk *clk_register_frac(const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, + struct frac_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock); +struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned + long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8 + rtbl_cnt, spinlock_t *lock); +struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name, + const char *vco_gate_name, const char *parent_name, + unsigned long flags, void __iomem *mode_reg, void __iomem + *cfg_reg, struct pll_rate_tbl *rtbl, u8 rtbl_cnt, + spinlock_t *lock, struct clk **pll_clk, + struct clk **vco_gate_clk); + +long clk_round_rate_index(struct clk_hw *hw, unsigned long drate, + unsigned long parent_rate, clk_calc_rate calc_rate, u8 rtbl_cnt, + int *index); + +#endif /* __SPEAR_CLK_H */ diff --git a/drivers/clk/spear/spear3xx_clock.c b/drivers/clk/spear/spear3xx_clock.c new file mode 100644 index 000000000000..440bb3e4c971 --- /dev/null +++ b/drivers/clk/spear/spear3xx_clock.c @@ -0,0 +1,612 @@ +/* + * SPEAr3xx machines clock framework source file + * + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of_platform.h> +#include <linux/spinlock_types.h> +#include <mach/misc_regs.h> +#include "clk.h" + +static DEFINE_SPINLOCK(_lock); + +#define PLL1_CTR (MISC_BASE + 0x008) +#define PLL1_FRQ (MISC_BASE + 0x00C) +#define PLL2_CTR (MISC_BASE + 0x014) +#define PLL2_FRQ (MISC_BASE + 0x018) +#define PLL_CLK_CFG (MISC_BASE + 0x020) + /* PLL_CLK_CFG register masks */ + #define MCTR_CLK_SHIFT 28 + #define MCTR_CLK_MASK 3 + +#define CORE_CLK_CFG (MISC_BASE + 0x024) + /* CORE CLK CFG register masks */ + #define GEN_SYNTH2_3_CLK_SHIFT 18 + #define GEN_SYNTH2_3_CLK_MASK 1 + + #define HCLK_RATIO_SHIFT 10 + #define HCLK_RATIO_MASK 2 + #define PCLK_RATIO_SHIFT 8 + #define PCLK_RATIO_MASK 2 + +#define PERIP_CLK_CFG (MISC_BASE + 0x028) + /* PERIP_CLK_CFG register masks */ + #define UART_CLK_SHIFT 4 + #define UART_CLK_MASK 1 + #define FIRDA_CLK_SHIFT 5 + #define FIRDA_CLK_MASK 2 + #define GPT0_CLK_SHIFT 8 + #define GPT1_CLK_SHIFT 11 + #define GPT2_CLK_SHIFT 12 + #define GPT_CLK_MASK 1 + +#define PERIP1_CLK_ENB (MISC_BASE + 0x02C) + /* PERIP1_CLK_ENB register masks */ + #define UART_CLK_ENB 3 + #define SSP_CLK_ENB 5 + #define I2C_CLK_ENB 7 + #define JPEG_CLK_ENB 8 + #define FIRDA_CLK_ENB 10 + #define GPT1_CLK_ENB 11 + #define GPT2_CLK_ENB 12 + #define ADC_CLK_ENB 15 + #define RTC_CLK_ENB 17 + #define GPIO_CLK_ENB 18 + #define DMA_CLK_ENB 19 + #define SMI_CLK_ENB 21 + #define GMAC_CLK_ENB 23 + #define USBD_CLK_ENB 24 + #define USBH_CLK_ENB 25 + #define C3_CLK_ENB 31 + +#define RAS_CLK_ENB (MISC_BASE + 0x034) + #define RAS_AHB_CLK_ENB 0 + #define RAS_PLL1_CLK_ENB 1 + #define RAS_APB_CLK_ENB 2 + #define RAS_32K_CLK_ENB 3 + #define RAS_24M_CLK_ENB 4 + #define RAS_48M_CLK_ENB 5 + #define RAS_PLL2_CLK_ENB 7 + #define RAS_SYNT0_CLK_ENB 8 + #define RAS_SYNT1_CLK_ENB 9 + #define RAS_SYNT2_CLK_ENB 10 + #define RAS_SYNT3_CLK_ENB 11 + +#define PRSC0_CLK_CFG (MISC_BASE + 0x044) +#define PRSC1_CLK_CFG (MISC_BASE + 0x048) +#define PRSC2_CLK_CFG (MISC_BASE + 0x04C) +#define AMEM_CLK_CFG (MISC_BASE + 0x050) + #define AMEM_CLK_ENB 0 + +#define CLCD_CLK_SYNT (MISC_BASE + 0x05C) +#define FIRDA_CLK_SYNT (MISC_BASE + 0x060) +#define UART_CLK_SYNT (MISC_BASE + 0x064) +#define GMAC_CLK_SYNT (MISC_BASE + 0x068) +#define GEN0_CLK_SYNT (MISC_BASE + 0x06C) +#define GEN1_CLK_SYNT (MISC_BASE + 0x070) +#define GEN2_CLK_SYNT (MISC_BASE + 0x074) +#define GEN3_CLK_SYNT (MISC_BASE + 0x078) + +/* pll rate configuration table, in ascending order of rates */ +static struct pll_rate_tbl pll_rtbl[] = { + {.mode = 0, .m = 0x53, .n = 0x0C, .p = 0x1}, /* vco 332 & pll 166 MHz */ + {.mode = 0, .m = 0x85, .n = 0x0C, .p = 0x1}, /* vco 532 & pll 266 MHz */ + {.mode = 0, .m = 0xA6, .n = 0x0C, .p = 0x1}, /* vco 664 & pll 332 MHz */ +}; + +/* aux rate configuration table, in ascending order of rates */ +static struct aux_rate_tbl aux_rtbl[] = { + /* For PLL1 = 332 MHz */ + {.xscale = 2, .yscale = 27, .eq = 0}, /* 12.296 MHz */ + {.xscale = 2, .yscale = 8, .eq = 0}, /* 41.5 MHz */ + {.xscale = 2, .yscale = 4, .eq = 0}, /* 83 MHz */ + {.xscale = 1, .yscale = 2, .eq = 1}, /* 166 MHz */ +}; + +/* gpt rate configuration table, in ascending order of rates */ +static struct gpt_rate_tbl gpt_rtbl[] = { + /* For pll1 = 332 MHz */ + {.mscale = 4, .nscale = 0}, /* 41.5 MHz */ + {.mscale = 2, .nscale = 0}, /* 55.3 MHz */ + {.mscale = 1, .nscale = 0}, /* 83 MHz */ +}; + +/* clock parents */ +static const char *uart0_parents[] = { "pll3_48m_clk", "uart_synth_gate_clk", }; +static const char *firda_parents[] = { "pll3_48m_clk", "firda_synth_gate_clk", +}; +static const char *gpt0_parents[] = { "pll3_48m_clk", "gpt0_synth_clk", }; +static const char *gpt1_parents[] = { "pll3_48m_clk", "gpt1_synth_clk", }; +static const char *gpt2_parents[] = { "pll3_48m_clk", "gpt2_synth_clk", }; +static const char *gen2_3_parents[] = { "pll1_clk", "pll2_clk", }; +static const char *ddr_parents[] = { "ahb_clk", "ahbmult2_clk", "none", + "pll2_clk", }; + +#ifdef CONFIG_MACH_SPEAR300 +static void __init spear300_clk_init(void) +{ + struct clk *clk; + + clk = clk_register_fixed_factor(NULL, "clcd_clk", "ras_pll3_48m_clk", 0, + 1, 1); + clk_register_clkdev(clk, NULL, "60000000.clcd"); + + clk = clk_register_fixed_factor(NULL, "fsmc_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "94000000.flash"); + + clk = clk_register_fixed_factor(NULL, "sdhci_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "70000000.sdhci"); + + clk = clk_register_fixed_factor(NULL, "gpio1_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a9000000.gpio"); + + clk = clk_register_fixed_factor(NULL, "kbd_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a0000000.kbd"); +} +#endif + +/* array of all spear 310 clock lookups */ +#ifdef CONFIG_MACH_SPEAR310 +static void __init spear310_clk_init(void) +{ + struct clk *clk; + + clk = clk_register_fixed_factor(NULL, "emi_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, "emi", NULL); + + clk = clk_register_fixed_factor(NULL, "fsmc_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "44000000.flash"); + + clk = clk_register_fixed_factor(NULL, "tdm_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "tdm"); + + clk = clk_register_fixed_factor(NULL, "uart1_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2000000.serial"); + + clk = clk_register_fixed_factor(NULL, "uart2_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2080000.serial"); + + clk = clk_register_fixed_factor(NULL, "uart3_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2100000.serial"); + + clk = clk_register_fixed_factor(NULL, "uart4_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2180000.serial"); + + clk = clk_register_fixed_factor(NULL, "uart5_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2200000.serial"); +} +#endif + +/* array of all spear 320 clock lookups */ +#ifdef CONFIG_MACH_SPEAR320 + #define SMII_PCLK_SHIFT 18 + #define SMII_PCLK_MASK 2 + #define SMII_PCLK_VAL_PAD 0x0 + #define SMII_PCLK_VAL_PLL2 0x1 + #define SMII_PCLK_VAL_SYNTH0 0x2 + #define SDHCI_PCLK_SHIFT 15 + #define SDHCI_PCLK_MASK 1 + #define SDHCI_PCLK_VAL_48M 0x0 + #define SDHCI_PCLK_VAL_SYNTH3 0x1 + #define I2S_REF_PCLK_SHIFT 8 + #define I2S_REF_PCLK_MASK 1 + #define I2S_REF_PCLK_SYNTH_VAL 0x1 + #define I2S_REF_PCLK_PLL2_VAL 0x0 + #define UART1_PCLK_SHIFT 6 + #define UART1_PCLK_MASK 1 + #define SPEAR320_UARTX_PCLK_VAL_SYNTH1 0x0 + #define SPEAR320_UARTX_PCLK_VAL_APB 0x1 + +static const char *i2s_ref_parents[] = { "ras_pll2_clk", + "ras_gen2_synth_gate_clk", }; +static const char *sdhci_parents[] = { "ras_pll3_48m_clk", + "ras_gen3_synth_gate_clk", +}; +static const char *smii0_parents[] = { "smii_125m_pad", "ras_pll2_clk", + "ras_gen0_synth_gate_clk", }; +static const char *uartx_parents[] = { "ras_gen1_synth_gate_clk", "ras_apb_clk", +}; + +static void __init spear320_clk_init(void) +{ + struct clk *clk; + + clk = clk_register_fixed_rate(NULL, "smii_125m_pad_clk", NULL, + CLK_IS_ROOT, 125000000); + clk_register_clkdev(clk, "smii_125m_pad", NULL); + + clk = clk_register_fixed_factor(NULL, "clcd_clk", "ras_pll3_48m_clk", 0, + 1, 1); + clk_register_clkdev(clk, NULL, "90000000.clcd"); + + clk = clk_register_fixed_factor(NULL, "emi_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, "emi", NULL); + + clk = clk_register_fixed_factor(NULL, "fsmc_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "4c000000.flash"); + + clk = clk_register_fixed_factor(NULL, "i2c1_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a7000000.i2c"); + + clk = clk_register_fixed_factor(NULL, "pwm_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, "pwm", NULL); + + clk = clk_register_fixed_factor(NULL, "ssp1_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a5000000.spi"); + + clk = clk_register_fixed_factor(NULL, "ssp2_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a6000000.spi"); + + clk = clk_register_fixed_factor(NULL, "can0_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "c_can_platform.0"); + + clk = clk_register_fixed_factor(NULL, "can1_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "c_can_platform.1"); + + clk = clk_register_fixed_factor(NULL, "i2s_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "i2s"); + + clk = clk_register_mux(NULL, "i2s_ref_clk", i2s_ref_parents, + ARRAY_SIZE(i2s_ref_parents), 0, SPEAR320_CONTROL_REG, + I2S_REF_PCLK_SHIFT, I2S_REF_PCLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "i2s_ref_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "i2s_sclk", "i2s_ref_clk", 0, 1, + 4); + clk_register_clkdev(clk, "i2s_sclk", NULL); + + clk = clk_register_mux(NULL, "rs485_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_RS485_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "a9300000.serial"); + + clk = clk_register_mux(NULL, "sdhci_clk", sdhci_parents, + ARRAY_SIZE(sdhci_parents), 0, SPEAR320_CONTROL_REG, + SDHCI_PCLK_SHIFT, SDHCI_PCLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "70000000.sdhci"); + + clk = clk_register_mux(NULL, "smii_pclk", smii0_parents, + ARRAY_SIZE(smii0_parents), 0, SPEAR320_CONTROL_REG, + SMII_PCLK_SHIFT, SMII_PCLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "smii_pclk"); + + clk = clk_register_fixed_factor(NULL, "smii_clk", "smii_pclk", 0, 1, 1); + clk_register_clkdev(clk, NULL, "smii"); + + clk = clk_register_mux(NULL, "uart1_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_CONTROL_REG, + UART1_PCLK_SHIFT, UART1_PCLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "a3000000.serial"); + + clk = clk_register_mux(NULL, "uart2_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART2_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "a4000000.serial"); + + clk = clk_register_mux(NULL, "uart3_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART3_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "a9100000.serial"); + + clk = clk_register_mux(NULL, "uart4_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART4_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "a9200000.serial"); + + clk = clk_register_mux(NULL, "uart5_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART5_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "60000000.serial"); + + clk = clk_register_mux(NULL, "uart6_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART6_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "60100000.serial"); +} +#endif + +void __init spear3xx_clk_init(void) +{ + struct clk *clk, *clk1; + + clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0); + clk_register_clkdev(clk, "apb_pclk", NULL); + + clk = clk_register_fixed_rate(NULL, "osc_32k_clk", NULL, CLK_IS_ROOT, + 32000); + clk_register_clkdev(clk, "osc_32k_clk", NULL); + + clk = clk_register_fixed_rate(NULL, "osc_24m_clk", NULL, CLK_IS_ROOT, + 24000000); + clk_register_clkdev(clk, "osc_24m_clk", NULL); + + /* clock derived from 32 KHz osc clk */ + clk = clk_register_gate(NULL, "rtc-spear", "osc_32k_clk", 0, + PERIP1_CLK_ENB, RTC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc900000.rtc"); + + /* clock derived from 24 MHz osc clk */ + clk = clk_register_fixed_rate(NULL, "pll3_48m_clk", "osc_24m_clk", 0, + 48000000); + clk_register_clkdev(clk, "pll3_48m_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "wdt_clk", "osc_24m_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "fc880000.wdt"); + + clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, + "osc_24m_clk", 0, PLL1_CTR, PLL1_FRQ, pll_rtbl, + ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL); + clk_register_clkdev(clk, "vco1_clk", NULL); + clk_register_clkdev(clk1, "pll1_clk", NULL); + + clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, + "osc_24m_clk", 0, PLL2_CTR, PLL2_FRQ, pll_rtbl, + ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL); + clk_register_clkdev(clk, "vco2_clk", NULL); + clk_register_clkdev(clk1, "pll2_clk", NULL); + + /* clock derived from pll1 clk */ + clk = clk_register_fixed_factor(NULL, "cpu_clk", "pll1_clk", 0, 1, 1); + clk_register_clkdev(clk, "cpu_clk", NULL); + + clk = clk_register_divider(NULL, "ahb_clk", "pll1_clk", + CLK_SET_RATE_PARENT, CORE_CLK_CFG, HCLK_RATIO_SHIFT, + HCLK_RATIO_MASK, 0, &_lock); + clk_register_clkdev(clk, "ahb_clk", NULL); + + clk = clk_register_aux("uart_synth_clk", "uart_synth_gate_clk", + "pll1_clk", 0, UART_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "uart_synth_clk", NULL); + clk_register_clkdev(clk1, "uart_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "uart0_mux_clk", uart0_parents, + ARRAY_SIZE(uart0_parents), 0, PERIP_CLK_CFG, + UART_CLK_SHIFT, UART_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "uart0_mux_clk", NULL); + + clk = clk_register_gate(NULL, "uart0", "uart0_mux_clk", 0, + PERIP1_CLK_ENB, UART_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0000000.serial"); + + clk = clk_register_aux("firda_synth_clk", "firda_synth_gate_clk", + "pll1_clk", 0, FIRDA_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "firda_synth_clk", NULL); + clk_register_clkdev(clk1, "firda_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "firda_mux_clk", firda_parents, + ARRAY_SIZE(firda_parents), 0, PERIP_CLK_CFG, + FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "firda_mux_clk", NULL); + + clk = clk_register_gate(NULL, "firda_clk", "firda_mux_clk", 0, + PERIP1_CLK_ENB, FIRDA_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "firda"); + + /* gpt clocks */ + clk_register_gpt("gpt0_synth_clk", "pll1_clk", 0, PRSC0_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk = clk_register_mux(NULL, "gpt0_clk", gpt0_parents, + ARRAY_SIZE(gpt0_parents), 0, PERIP_CLK_CFG, + GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt0"); + + clk_register_gpt("gpt1_synth_clk", "pll1_clk", 0, PRSC1_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk = clk_register_mux(NULL, "gpt1_mux_clk", gpt1_parents, + ARRAY_SIZE(gpt1_parents), 0, PERIP_CLK_CFG, + GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt1_mux_clk", NULL); + clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mux_clk", 0, + PERIP1_CLK_ENB, GPT1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt1"); + + clk_register_gpt("gpt2_synth_clk", "pll1_clk", 0, PRSC2_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk = clk_register_mux(NULL, "gpt2_mux_clk", gpt2_parents, + ARRAY_SIZE(gpt2_parents), 0, PERIP_CLK_CFG, + GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt2_mux_clk", NULL); + clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mux_clk", 0, + PERIP1_CLK_ENB, GPT2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt2"); + + /* general synths clocks */ + clk = clk_register_aux("gen0_synth_clk", "gen0_synth_gate_clk", + "pll1_clk", 0, GEN0_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "gen0_synth_clk", NULL); + clk_register_clkdev(clk1, "gen0_synth_gate_clk", NULL); + + clk = clk_register_aux("gen1_synth_clk", "gen1_synth_gate_clk", + "pll1_clk", 0, GEN1_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "gen1_synth_clk", NULL); + clk_register_clkdev(clk1, "gen1_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "gen2_3_parent_clk", gen2_3_parents, + ARRAY_SIZE(gen2_3_parents), 0, CORE_CLK_CFG, + GEN_SYNTH2_3_CLK_SHIFT, GEN_SYNTH2_3_CLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, "gen2_3_parent_clk", NULL); + + clk = clk_register_aux("gen2_synth_clk", "gen2_synth_gate_clk", + "gen2_3_parent_clk", 0, GEN2_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "gen2_synth_clk", NULL); + clk_register_clkdev(clk1, "gen2_synth_gate_clk", NULL); + + clk = clk_register_aux("gen3_synth_clk", "gen3_synth_gate_clk", + "gen2_3_parent_clk", 0, GEN3_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "gen3_synth_clk", NULL); + clk_register_clkdev(clk1, "gen3_synth_gate_clk", NULL); + + /* clock derived from pll3 clk */ + clk = clk_register_gate(NULL, "usbh_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBH_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "usbh_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "usbh.0_clk", "usbh_clk", 0, 1, + 1); + clk_register_clkdev(clk, "usbh.0_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "usbh.1_clk", "usbh_clk", 0, 1, + 1); + clk_register_clkdev(clk, "usbh.1_clk", NULL); + + clk = clk_register_gate(NULL, "usbd_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBD_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "designware_udc"); + + /* clock derived from ahb clk */ + clk = clk_register_fixed_factor(NULL, "ahbmult2_clk", "ahb_clk", 0, 2, + 1); + clk_register_clkdev(clk, "ahbmult2_clk", NULL); + + clk = clk_register_mux(NULL, "ddr_clk", ddr_parents, + ARRAY_SIZE(ddr_parents), 0, PLL_CLK_CFG, MCTR_CLK_SHIFT, + MCTR_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "ddr_clk", NULL); + + clk = clk_register_divider(NULL, "apb_clk", "ahb_clk", + CLK_SET_RATE_PARENT, CORE_CLK_CFG, PCLK_RATIO_SHIFT, + PCLK_RATIO_MASK, 0, &_lock); + clk_register_clkdev(clk, "apb_clk", NULL); + + clk = clk_register_gate(NULL, "amem_clk", "ahb_clk", 0, AMEM_CLK_CFG, + AMEM_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "amem_clk", NULL); + + clk = clk_register_gate(NULL, "c3_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + C3_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "c3_clk"); + + clk = clk_register_gate(NULL, "dma_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + DMA_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc400000.dma"); + + clk = clk_register_gate(NULL, "gmac_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + GMAC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "e0800000.eth"); + + clk = clk_register_gate(NULL, "i2c0_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + I2C_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0180000.i2c"); + + clk = clk_register_gate(NULL, "jpeg_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + JPEG_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "jpeg"); + + clk = clk_register_gate(NULL, "smi_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + SMI_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc000000.flash"); + + /* clock derived from apb clk */ + clk = clk_register_gate(NULL, "adc_clk", "apb_clk", 0, PERIP1_CLK_ENB, + ADC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "adc"); + + clk = clk_register_gate(NULL, "gpio0_clk", "apb_clk", 0, PERIP1_CLK_ENB, + GPIO_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc980000.gpio"); + + clk = clk_register_gate(NULL, "ssp0_clk", "apb_clk", 0, PERIP1_CLK_ENB, + SSP_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0100000.spi"); + + /* RAS clk enable */ + clk = clk_register_gate(NULL, "ras_ahb_clk", "ahb_clk", 0, RAS_CLK_ENB, + RAS_AHB_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_ahb_clk", NULL); + + clk = clk_register_gate(NULL, "ras_apb_clk", "apb_clk", 0, RAS_CLK_ENB, + RAS_APB_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_apb_clk", NULL); + + clk = clk_register_gate(NULL, "ras_32k_clk", "osc_32k_clk", 0, + RAS_CLK_ENB, RAS_32K_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_32k_clk", NULL); + + clk = clk_register_gate(NULL, "ras_24m_clk", "osc_24m_clk", 0, + RAS_CLK_ENB, RAS_24M_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_24m_clk", NULL); + + clk = clk_register_gate(NULL, "ras_pll1_clk", "pll1_clk", 0, + RAS_CLK_ENB, RAS_PLL1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_pll1_clk", NULL); + + clk = clk_register_gate(NULL, "ras_pll2_clk", "pll2_clk", 0, + RAS_CLK_ENB, RAS_PLL2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_pll2_clk", NULL); + + clk = clk_register_gate(NULL, "ras_pll3_48m_clk", "pll3_48m_clk", 0, + RAS_CLK_ENB, RAS_48M_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_pll3_48m_clk", NULL); + + clk = clk_register_gate(NULL, "ras_gen0_synth_gate_clk", + "gen0_synth_gate_clk", 0, RAS_CLK_ENB, + RAS_SYNT0_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_gen0_synth_gate_clk", NULL); + + clk = clk_register_gate(NULL, "ras_gen1_synth_gate_clk", + "gen1_synth_gate_clk", 0, RAS_CLK_ENB, + RAS_SYNT1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_gen1_synth_gate_clk", NULL); + + clk = clk_register_gate(NULL, "ras_gen2_synth_gate_clk", + "gen2_synth_gate_clk", 0, RAS_CLK_ENB, + RAS_SYNT2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_gen2_synth_gate_clk", NULL); + + clk = clk_register_gate(NULL, "ras_gen3_synth_gate_clk", + "gen3_synth_gate_clk", 0, RAS_CLK_ENB, + RAS_SYNT3_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_gen3_synth_gate_clk", NULL); + + if (of_machine_is_compatible("st,spear300")) + spear300_clk_init(); + else if (of_machine_is_compatible("st,spear310")) + spear310_clk_init(); + else if (of_machine_is_compatible("st,spear320")) + spear320_clk_init(); +} diff --git a/drivers/clk/spear/spear6xx_clock.c b/drivers/clk/spear/spear6xx_clock.c new file mode 100644 index 000000000000..f9a20b382304 --- /dev/null +++ b/drivers/clk/spear/spear6xx_clock.c @@ -0,0 +1,342 @@ +/* + * SPEAr6xx machines clock framework source file + * + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/io.h> +#include <linux/spinlock_types.h> +#include <mach/misc_regs.h> +#include "clk.h" + +static DEFINE_SPINLOCK(_lock); + +#define PLL1_CTR (MISC_BASE + 0x008) +#define PLL1_FRQ (MISC_BASE + 0x00C) +#define PLL2_CTR (MISC_BASE + 0x014) +#define PLL2_FRQ (MISC_BASE + 0x018) +#define PLL_CLK_CFG (MISC_BASE + 0x020) + /* PLL_CLK_CFG register masks */ + #define MCTR_CLK_SHIFT 28 + #define MCTR_CLK_MASK 3 + +#define CORE_CLK_CFG (MISC_BASE + 0x024) + /* CORE CLK CFG register masks */ + #define HCLK_RATIO_SHIFT 10 + #define HCLK_RATIO_MASK 2 + #define PCLK_RATIO_SHIFT 8 + #define PCLK_RATIO_MASK 2 + +#define PERIP_CLK_CFG (MISC_BASE + 0x028) + /* PERIP_CLK_CFG register masks */ + #define CLCD_CLK_SHIFT 2 + #define CLCD_CLK_MASK 2 + #define UART_CLK_SHIFT 4 + #define UART_CLK_MASK 1 + #define FIRDA_CLK_SHIFT 5 + #define FIRDA_CLK_MASK 2 + #define GPT0_CLK_SHIFT 8 + #define GPT1_CLK_SHIFT 10 + #define GPT2_CLK_SHIFT 11 + #define GPT3_CLK_SHIFT 12 + #define GPT_CLK_MASK 1 + +#define PERIP1_CLK_ENB (MISC_BASE + 0x02C) + /* PERIP1_CLK_ENB register masks */ + #define UART0_CLK_ENB 3 + #define UART1_CLK_ENB 4 + #define SSP0_CLK_ENB 5 + #define SSP1_CLK_ENB 6 + #define I2C_CLK_ENB 7 + #define JPEG_CLK_ENB 8 + #define FSMC_CLK_ENB 9 + #define FIRDA_CLK_ENB 10 + #define GPT2_CLK_ENB 11 + #define GPT3_CLK_ENB 12 + #define GPIO2_CLK_ENB 13 + #define SSP2_CLK_ENB 14 + #define ADC_CLK_ENB 15 + #define GPT1_CLK_ENB 11 + #define RTC_CLK_ENB 17 + #define GPIO1_CLK_ENB 18 + #define DMA_CLK_ENB 19 + #define SMI_CLK_ENB 21 + #define CLCD_CLK_ENB 22 + #define GMAC_CLK_ENB 23 + #define USBD_CLK_ENB 24 + #define USBH0_CLK_ENB 25 + #define USBH1_CLK_ENB 26 + +#define PRSC0_CLK_CFG (MISC_BASE + 0x044) +#define PRSC1_CLK_CFG (MISC_BASE + 0x048) +#define PRSC2_CLK_CFG (MISC_BASE + 0x04C) + +#define CLCD_CLK_SYNT (MISC_BASE + 0x05C) +#define FIRDA_CLK_SYNT (MISC_BASE + 0x060) +#define UART_CLK_SYNT (MISC_BASE + 0x064) + +/* vco rate configuration table, in ascending order of rates */ +static struct pll_rate_tbl pll_rtbl[] = { + {.mode = 0, .m = 0x53, .n = 0x0F, .p = 0x1}, /* vco 332 & pll 166 MHz */ + {.mode = 0, .m = 0x85, .n = 0x0F, .p = 0x1}, /* vco 532 & pll 266 MHz */ + {.mode = 0, .m = 0xA6, .n = 0x0F, .p = 0x1}, /* vco 664 & pll 332 MHz */ +}; + +/* aux rate configuration table, in ascending order of rates */ +static struct aux_rate_tbl aux_rtbl[] = { + /* For PLL1 = 332 MHz */ + {.xscale = 2, .yscale = 8, .eq = 0}, /* 41.5 MHz */ + {.xscale = 2, .yscale = 4, .eq = 0}, /* 83 MHz */ + {.xscale = 1, .yscale = 2, .eq = 1}, /* 166 MHz */ +}; + +static const char *clcd_parents[] = { "pll3_48m_clk", "clcd_synth_gate_clk", }; +static const char *firda_parents[] = { "pll3_48m_clk", "firda_synth_gate_clk", +}; +static const char *uart_parents[] = { "pll3_48m_clk", "uart_synth_gate_clk", }; +static const char *gpt0_1_parents[] = { "pll3_48m_clk", "gpt0_1_synth_clk", }; +static const char *gpt2_parents[] = { "pll3_48m_clk", "gpt2_synth_clk", }; +static const char *gpt3_parents[] = { "pll3_48m_clk", "gpt3_synth_clk", }; +static const char *ddr_parents[] = { "ahb_clk", "ahbmult2_clk", "none", + "pll2_clk", }; + +/* gpt rate configuration table, in ascending order of rates */ +static struct gpt_rate_tbl gpt_rtbl[] = { + /* For pll1 = 332 MHz */ + {.mscale = 4, .nscale = 0}, /* 41.5 MHz */ + {.mscale = 2, .nscale = 0}, /* 55.3 MHz */ + {.mscale = 1, .nscale = 0}, /* 83 MHz */ +}; + +void __init spear6xx_clk_init(void) +{ + struct clk *clk, *clk1; + + clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0); + clk_register_clkdev(clk, "apb_pclk", NULL); + + clk = clk_register_fixed_rate(NULL, "osc_32k_clk", NULL, CLK_IS_ROOT, + 32000); + clk_register_clkdev(clk, "osc_32k_clk", NULL); + + clk = clk_register_fixed_rate(NULL, "osc_30m_clk", NULL, CLK_IS_ROOT, + 30000000); + clk_register_clkdev(clk, "osc_30m_clk", NULL); + + /* clock derived from 32 KHz osc clk */ + clk = clk_register_gate(NULL, "rtc_spear", "osc_32k_clk", 0, + PERIP1_CLK_ENB, RTC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "rtc-spear"); + + /* clock derived from 30 MHz osc clk */ + clk = clk_register_fixed_rate(NULL, "pll3_48m_clk", "osc_24m_clk", 0, + 48000000); + clk_register_clkdev(clk, "pll3_48m_clk", NULL); + + clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, "osc_30m_clk", + 0, PLL1_CTR, PLL1_FRQ, pll_rtbl, ARRAY_SIZE(pll_rtbl), + &_lock, &clk1, NULL); + clk_register_clkdev(clk, "vco1_clk", NULL); + clk_register_clkdev(clk1, "pll1_clk", NULL); + + clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, + "osc_30m_clk", 0, PLL2_CTR, PLL2_FRQ, pll_rtbl, + ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL); + clk_register_clkdev(clk, "vco2_clk", NULL); + clk_register_clkdev(clk1, "pll2_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "wdt_clk", "osc_30m_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "wdt"); + + /* clock derived from pll1 clk */ + clk = clk_register_fixed_factor(NULL, "cpu_clk", "pll1_clk", 0, 1, 1); + clk_register_clkdev(clk, "cpu_clk", NULL); + + clk = clk_register_divider(NULL, "ahb_clk", "pll1_clk", + CLK_SET_RATE_PARENT, CORE_CLK_CFG, HCLK_RATIO_SHIFT, + HCLK_RATIO_MASK, 0, &_lock); + clk_register_clkdev(clk, "ahb_clk", NULL); + + clk = clk_register_aux("uart_synth_clk", "uart_synth_gate_clk", + "pll1_clk", 0, UART_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "uart_synth_clk", NULL); + clk_register_clkdev(clk1, "uart_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "uart_mux_clk", uart_parents, + ARRAY_SIZE(uart_parents), 0, PERIP_CLK_CFG, + UART_CLK_SHIFT, UART_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "uart_mux_clk", NULL); + + clk = clk_register_gate(NULL, "uart0", "uart_mux_clk", 0, + PERIP1_CLK_ENB, UART0_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0000000.serial"); + + clk = clk_register_gate(NULL, "uart1", "uart_mux_clk", 0, + PERIP1_CLK_ENB, UART1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0080000.serial"); + + clk = clk_register_aux("firda_synth_clk", "firda_synth_gate_clk", + "pll1_clk", 0, FIRDA_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "firda_synth_clk", NULL); + clk_register_clkdev(clk1, "firda_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "firda_mux_clk", firda_parents, + ARRAY_SIZE(firda_parents), 0, PERIP_CLK_CFG, + FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "firda_mux_clk", NULL); + + clk = clk_register_gate(NULL, "firda_clk", "firda_mux_clk", 0, + PERIP1_CLK_ENB, FIRDA_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "firda"); + + clk = clk_register_aux("clcd_synth_clk", "clcd_synth_gate_clk", + "pll1_clk", 0, CLCD_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "clcd_synth_clk", NULL); + clk_register_clkdev(clk1, "clcd_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "clcd_mux_clk", clcd_parents, + ARRAY_SIZE(clcd_parents), 0, PERIP_CLK_CFG, + CLCD_CLK_SHIFT, CLCD_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "clcd_mux_clk", NULL); + + clk = clk_register_gate(NULL, "clcd_clk", "clcd_mux_clk", 0, + PERIP1_CLK_ENB, CLCD_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "clcd"); + + /* gpt clocks */ + clk = clk_register_gpt("gpt0_1_synth_clk", "pll1_clk", 0, PRSC0_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk_register_clkdev(clk, "gpt0_1_synth_clk", NULL); + + clk = clk_register_mux(NULL, "gpt0_mux_clk", gpt0_1_parents, + ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG, + GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt0"); + + clk = clk_register_mux(NULL, "gpt1_mux_clk", gpt0_1_parents, + ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG, + GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt1_mux_clk", NULL); + + clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mux_clk", 0, + PERIP1_CLK_ENB, GPT1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt1"); + + clk = clk_register_gpt("gpt2_synth_clk", "pll1_clk", 0, PRSC1_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk_register_clkdev(clk, "gpt2_synth_clk", NULL); + + clk = clk_register_mux(NULL, "gpt2_mux_clk", gpt2_parents, + ARRAY_SIZE(gpt2_parents), 0, PERIP_CLK_CFG, + GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt2_mux_clk", NULL); + + clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mux_clk", 0, + PERIP1_CLK_ENB, GPT2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt2"); + + clk = clk_register_gpt("gpt3_synth_clk", "pll1_clk", 0, PRSC2_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk_register_clkdev(clk, "gpt3_synth_clk", NULL); + + clk = clk_register_mux(NULL, "gpt3_mux_clk", gpt3_parents, + ARRAY_SIZE(gpt3_parents), 0, PERIP_CLK_CFG, + GPT3_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt3_mux_clk", NULL); + + clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mux_clk", 0, + PERIP1_CLK_ENB, GPT3_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt3"); + + /* clock derived from pll3 clk */ + clk = clk_register_gate(NULL, "usbh0_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBH0_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "usbh.0_clk"); + + clk = clk_register_gate(NULL, "usbh1_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBH1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "usbh.1_clk"); + + clk = clk_register_gate(NULL, "usbd_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBD_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "designware_udc"); + + /* clock derived from ahb clk */ + clk = clk_register_fixed_factor(NULL, "ahbmult2_clk", "ahb_clk", 0, 2, + 1); + clk_register_clkdev(clk, "ahbmult2_clk", NULL); + + clk = clk_register_mux(NULL, "ddr_clk", ddr_parents, + ARRAY_SIZE(ddr_parents), + 0, PLL_CLK_CFG, MCTR_CLK_SHIFT, MCTR_CLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, "ddr_clk", NULL); + + clk = clk_register_divider(NULL, "apb_clk", "ahb_clk", + CLK_SET_RATE_PARENT, CORE_CLK_CFG, PCLK_RATIO_SHIFT, + PCLK_RATIO_MASK, 0, &_lock); + clk_register_clkdev(clk, "apb_clk", NULL); + + clk = clk_register_gate(NULL, "dma_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + DMA_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc400000.dma"); + + clk = clk_register_gate(NULL, "fsmc_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + FSMC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d1800000.flash"); + + clk = clk_register_gate(NULL, "gmac_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + GMAC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gmac"); + + clk = clk_register_gate(NULL, "i2c_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + I2C_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0200000.i2c"); + + clk = clk_register_gate(NULL, "jpeg_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + JPEG_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "jpeg"); + + clk = clk_register_gate(NULL, "smi_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + SMI_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc000000.flash"); + + /* clock derived from apb clk */ + clk = clk_register_gate(NULL, "adc_clk", "apb_clk", 0, PERIP1_CLK_ENB, + ADC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "adc"); + + clk = clk_register_fixed_factor(NULL, "gpio0_clk", "apb_clk", 0, 1, 1); + clk_register_clkdev(clk, NULL, "f0100000.gpio"); + + clk = clk_register_gate(NULL, "gpio1_clk", "apb_clk", 0, PERIP1_CLK_ENB, + GPIO1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc980000.gpio"); + + clk = clk_register_gate(NULL, "gpio2_clk", "apb_clk", 0, PERIP1_CLK_ENB, + GPIO2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d8100000.gpio"); + + clk = clk_register_gate(NULL, "ssp0_clk", "apb_clk", 0, PERIP1_CLK_ENB, + SSP0_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "ssp-pl022.0"); + + clk = clk_register_gate(NULL, "ssp1_clk", "apb_clk", 0, PERIP1_CLK_ENB, + SSP1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "ssp-pl022.1"); + + clk = clk_register_gate(NULL, "ssp2_clk", "apb_clk", 0, PERIP1_CLK_ENB, + SSP2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "ssp-pl022.2"); +} |