diff options
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/Kconfig | 7 | ||||
-rw-r--r-- | drivers/clk/Makefile | 8 | ||||
-rw-r--r-- | drivers/clk/clk-divider.c | 189 | ||||
-rw-r--r-- | drivers/clk/clk-fixed-factor.c | 2 | ||||
-rw-r--r-- | drivers/clk/clk-fixed-rate.c | 25 | ||||
-rw-r--r-- | drivers/clk/clk-gate.c | 2 | ||||
-rw-r--r-- | drivers/clk/clk-highbank.c | 346 | ||||
-rw-r--r-- | drivers/clk/clk-mux.c | 2 | ||||
-rw-r--r-- | drivers/clk/clk-u300.c | 746 | ||||
-rw-r--r-- | drivers/clk/clk-wm831x.c | 428 | ||||
-rw-r--r-- | drivers/clk/clk.c | 144 | ||||
-rw-r--r-- | drivers/clk/clkdev.c | 77 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-imx23.c | 3 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-imx28.c | 13 | ||||
-rw-r--r-- | drivers/clk/versatile/Makefile | 3 | ||||
-rw-r--r-- | drivers/clk/versatile/clk-icst.c | 100 | ||||
-rw-r--r-- | drivers/clk/versatile/clk-icst.h | 10 | ||||
-rw-r--r-- | drivers/clk/versatile/clk-integrator.c | 111 |
18 files changed, 2170 insertions, 46 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 4864407e3fc..3f99b909965 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -34,4 +34,11 @@ config COMMON_CLK_DEBUG clk_flags, clk_prepare_count, clk_enable_count & clk_notifier_count. +config COMMON_CLK_WM831X + tristate "Clock driver for WM831x/2x PMICs" + depends on MFD_WM831X + ---help--- + Supports the clocking subsystem of the WM831x/2x series of + PMICs from Wolfson Microlectronics. + endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 3669761d1ba..5869ea38705 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -1,9 +1,15 @@ - +# common clock types 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-fixed-factor.o # SoCs specific obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o +obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ obj-$(CONFIG_PLAT_SPEAR) += spear/ +obj-$(CONFIG_ARCH_U300) += clk-u300.o +obj-$(CONFIG_ARCH_INTEGRATOR) += versatile/ + +# Chip specific +obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 8ea11b44452..a9204c69148 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -30,18 +30,89 @@ #define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw) #define div_mask(d) ((1 << (d->width)) - 1) +#define is_power_of_two(i) !(i & ~i) + +static unsigned int _get_table_maxdiv(const struct clk_div_table *table) +{ + unsigned int maxdiv = 0; + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div > maxdiv) + maxdiv = clkt->div; + return maxdiv; +} + +static unsigned int _get_maxdiv(struct clk_divider *divider) +{ + if (divider->flags & CLK_DIVIDER_ONE_BASED) + return div_mask(divider); + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) + return 1 << div_mask(divider); + if (divider->table) + return _get_table_maxdiv(divider->table); + return div_mask(divider) + 1; +} + +static unsigned int _get_table_div(const struct clk_div_table *table, + unsigned int val) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->val == val) + return clkt->div; + return 0; +} + +static unsigned int _get_div(struct clk_divider *divider, unsigned int val) +{ + if (divider->flags & CLK_DIVIDER_ONE_BASED) + return val; + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) + return 1 << val; + if (divider->table) + return _get_table_div(divider->table, val); + return val + 1; +} + +static unsigned int _get_table_val(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return clkt->val; + return 0; +} + +static unsigned int _get_val(struct clk_divider *divider, u8 div) +{ + if (divider->flags & CLK_DIVIDER_ONE_BASED) + return div; + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) + return __ffs(div); + if (divider->table) + return _get_table_val(divider->table, div); + return div - 1; +} static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_divider *divider = to_clk_divider(hw); - unsigned int div; + unsigned int div, val; - div = readl(divider->reg) >> divider->shift; - div &= div_mask(divider); + val = readl(divider->reg) >> divider->shift; + val &= div_mask(divider); - if (!(divider->flags & CLK_DIVIDER_ONE_BASED)) - div++; + div = _get_div(divider, val); + if (!div) { + WARN(1, "%s: Invalid divisor for clock %s\n", __func__, + __clk_get_name(hw->clk)); + return parent_rate; + } return parent_rate / div; } @@ -52,6 +123,26 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, */ #define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1) +static bool _is_valid_table_div(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return true; + return false; +} + +static bool _is_valid_div(struct clk_divider *divider, unsigned int div) +{ + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) + return is_power_of_two(div); + if (divider->table) + return _is_valid_table_div(divider->table, div); + return true; +} + static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate) { @@ -62,10 +153,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, if (!rate) rate = 1; - maxdiv = (1 << divider->width); - - if (divider->flags & CLK_DIVIDER_ONE_BASED) - maxdiv--; + maxdiv = _get_maxdiv(divider); if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { parent_rate = *best_parent_rate; @@ -82,6 +170,8 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, maxdiv = min(ULONG_MAX / rate, maxdiv); for (i = 1; i <= maxdiv; i++) { + if (!_is_valid_div(divider, i)) + continue; parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); now = parent_rate / i; @@ -93,9 +183,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, } if (!bestdiv) { - bestdiv = (1 << divider->width); - if (divider->flags & CLK_DIVIDER_ONE_BASED) - bestdiv--; + bestdiv = _get_maxdiv(divider); *best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1); } @@ -115,24 +203,22 @@ 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 int div, value; unsigned long flags = 0; u32 val; div = parent_rate / rate; + value = _get_val(divider, div); - if (!(divider->flags & CLK_DIVIDER_ONE_BASED)) - div--; - - if (div > div_mask(divider)) - div = div_mask(divider); + if (value > div_mask(divider)) + value = div_mask(divider); if (divider->lock) spin_lock_irqsave(divider->lock, flags); val = readl(divider->reg); val &= ~(div_mask(divider) << divider->shift); - val |= div << divider->shift; + val |= value << divider->shift; writel(val, divider->reg); if (divider->lock) @@ -148,22 +234,11 @@ const struct clk_ops clk_divider_ops = { }; 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, +static struct clk *_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, - u8 clk_divider_flags, spinlock_t *lock) + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock) { struct clk_divider *div; struct clk *clk; @@ -178,7 +253,7 @@ struct clk *clk_register_divider(struct device *dev, const char *name, init.name = name; init.ops = &clk_divider_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = (parent_name ? &parent_name: NULL); init.num_parents = (parent_name ? 1 : 0); @@ -189,6 +264,7 @@ struct clk *clk_register_divider(struct device *dev, const char *name, div->flags = clk_divider_flags; div->lock = lock; div->hw.init = &init; + div->table = table; /* register the clock */ clk = clk_register(dev, &div->hw); @@ -198,3 +274,48 @@ struct clk *clk_register_divider(struct device *dev, const char *name, return clk; } + +/** + * 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, + u8 clk_divider_flags, spinlock_t *lock) +{ + return _register_divider(dev, name, parent_name, flags, reg, shift, + width, clk_divider_flags, NULL, lock); +} + +/** + * clk_register_divider_table - register a table based 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 + * @table: array of divider/value pairs ending with a div set to 0 + * @lock: shared register lock for this clock + */ +struct clk *clk_register_divider_table(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock) +{ + return _register_divider(dev, name, parent_name, flags, reg, shift, + width, clk_divider_flags, table, lock); +} diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index c8c003e217a..a4899855c0f 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -82,7 +82,7 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, init.name = name; init.ops = &clk_fixed_factor_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = &parent_name; init.num_parents = 1; diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index cbd24622978..f5ec0eebd4d 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -14,6 +14,7 @@ #include <linux/slab.h> #include <linux/io.h> #include <linux/err.h> +#include <linux/of.h> /* * DOC: basic fixed-rate clock that cannot gate @@ -63,7 +64,7 @@ struct clk *clk_register_fixed_rate(struct device *dev, const char *name, init.name = name; init.ops = &clk_fixed_rate_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = (parent_name ? &parent_name: NULL); init.num_parents = (parent_name ? 1 : 0); @@ -79,3 +80,25 @@ struct clk *clk_register_fixed_rate(struct device *dev, const char *name, return clk; } + +#ifdef CONFIG_OF +/** + * of_fixed_clk_setup() - Setup function for simple fixed rate clock + */ +void __init of_fixed_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = node->name; + u32 rate; + + if (of_property_read_u32(node, "clock-frequency", &rate)) + return; + + of_property_read_string(node, "clock-output-names", &clk_name); + + clk = clk_register_fixed_rate(NULL, clk_name, NULL, CLK_IS_ROOT, rate); + if (clk) + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} +EXPORT_SYMBOL_GPL(of_fixed_clk_setup); +#endif diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 578465e04be..15114febfd9 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -130,7 +130,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name, init.name = name; init.ops = &clk_gate_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = (parent_name ? &parent_name: NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c new file mode 100644 index 00000000000..52fecadf004 --- /dev/null +++ b/drivers/clk/clk-highbank.c @@ -0,0 +1,346 @@ +/* + * Copyright 2011-2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> + +extern void __iomem *sregs_base; + +#define HB_PLL_LOCK_500 0x20000000 +#define HB_PLL_LOCK 0x10000000 +#define HB_PLL_DIVF_SHIFT 20 +#define HB_PLL_DIVF_MASK 0x0ff00000 +#define HB_PLL_DIVQ_SHIFT 16 +#define HB_PLL_DIVQ_MASK 0x00070000 +#define HB_PLL_DIVR_SHIFT 8 +#define HB_PLL_DIVR_MASK 0x00001f00 +#define HB_PLL_RANGE_SHIFT 4 +#define HB_PLL_RANGE_MASK 0x00000070 +#define HB_PLL_BYPASS 0x00000008 +#define HB_PLL_RESET 0x00000004 +#define HB_PLL_EXT_BYPASS 0x00000002 +#define HB_PLL_EXT_ENA 0x00000001 + +#define HB_PLL_VCO_MIN_FREQ 2133000000 +#define HB_PLL_MAX_FREQ HB_PLL_VCO_MIN_FREQ +#define HB_PLL_MIN_FREQ (HB_PLL_VCO_MIN_FREQ / 64) + +#define HB_A9_BCLK_DIV_MASK 0x00000006 +#define HB_A9_BCLK_DIV_SHIFT 1 +#define HB_A9_PCLK_DIV 0x00000001 + +struct hb_clk { + struct clk_hw hw; + void __iomem *reg; + char *parent_name; +}; +#define to_hb_clk(p) container_of(p, struct hb_clk, hw) + +static int clk_pll_prepare(struct clk_hw *hwclk) + { + struct hb_clk *hbclk = to_hb_clk(hwclk); + u32 reg; + + reg = readl(hbclk->reg); + reg &= ~HB_PLL_RESET; + writel(reg, hbclk->reg); + + while ((readl(hbclk->reg) & HB_PLL_LOCK) == 0) + ; + while ((readl(hbclk->reg) & HB_PLL_LOCK_500) == 0) + ; + + return 0; +} + +static void clk_pll_unprepare(struct clk_hw *hwclk) +{ + struct hb_clk *hbclk = to_hb_clk(hwclk); + u32 reg; + + reg = readl(hbclk->reg); + reg |= HB_PLL_RESET; + writel(reg, hbclk->reg); +} + +static int clk_pll_enable(struct clk_hw *hwclk) +{ + struct hb_clk *hbclk = to_hb_clk(hwclk); + u32 reg; + + reg = readl(hbclk->reg); + reg |= HB_PLL_EXT_ENA; + writel(reg, hbclk->reg); + + return 0; +} + +static void clk_pll_disable(struct clk_hw *hwclk) +{ + struct hb_clk *hbclk = to_hb_clk(hwclk); + u32 reg; + + reg = readl(hbclk->reg); + reg &= ~HB_PLL_EXT_ENA; + writel(reg, hbclk->reg); +} + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct hb_clk *hbclk = to_hb_clk(hwclk); + unsigned long divf, divq, vco_freq, reg; + + reg = readl(hbclk->reg); + if (reg & HB_PLL_EXT_BYPASS) + return parent_rate; + + divf = (reg & HB_PLL_DIVF_MASK) >> HB_PLL_DIVF_SHIFT; + divq = (reg & HB_PLL_DIVQ_MASK) >> HB_PLL_DIVQ_SHIFT; + vco_freq = parent_rate * (divf + 1); + + return vco_freq / (1 << divq); +} + +static void clk_pll_calc(unsigned long rate, unsigned long ref_freq, + u32 *pdivq, u32 *pdivf) +{ + u32 divq, divf; + unsigned long vco_freq; + + if (rate < HB_PLL_MIN_FREQ) + rate = HB_PLL_MIN_FREQ; + if (rate > HB_PLL_MAX_FREQ) + rate = HB_PLL_MAX_FREQ; + + for (divq = 1; divq <= 6; divq++) { + if ((rate * (1 << divq)) >= HB_PLL_VCO_MIN_FREQ) + break; + } + + vco_freq = rate * (1 << divq); + divf = (vco_freq + (ref_freq / 2)) / ref_freq; + divf--; + + *pdivq = divq; + *pdivf = divf; +} + +static long clk_pll_round_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long *parent_rate) +{ + u32 divq, divf; + unsigned long ref_freq = *parent_rate; + + clk_pll_calc(rate, ref_freq, &divq, &divf); + + return (ref_freq * (divf + 1)) / (1 << divq); +} + +static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long parent_rate) +{ + struct hb_clk *hbclk = to_hb_clk(hwclk); + u32 divq, divf; + u32 reg; + + clk_pll_calc(rate, parent_rate, &divq, &divf); + + reg = readl(hbclk->reg); + if (divf != ((reg & HB_PLL_DIVF_MASK) >> HB_PLL_DIVF_SHIFT)) { + /* Need to re-lock PLL, so put it into bypass mode */ + reg |= HB_PLL_EXT_BYPASS; + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); + + writel(reg | HB_PLL_RESET, hbclk->reg); + reg &= ~(HB_PLL_DIVF_MASK | HB_PLL_DIVQ_MASK); + reg |= (divf << HB_PLL_DIVF_SHIFT) | (divq << HB_PLL_DIVQ_SHIFT); + writel(reg | HB_PLL_RESET, hbclk->reg); + writel(reg, hbclk->reg); + + while ((readl(hbclk->reg) & HB_PLL_LOCK) == 0) + ; + while ((readl(hbclk->reg) & HB_PLL_LOCK_500) == 0) + ; + reg |= HB_PLL_EXT_ENA; + reg &= ~HB_PLL_EXT_BYPASS; + } else { + reg &= ~HB_PLL_DIVQ_MASK; + reg |= divq << HB_PLL_DIVQ_SHIFT; + } + writel(reg, hbclk->reg); + + return 0; +} + +static const struct clk_ops clk_pll_ops = { + .prepare = clk_pll_prepare, + .unprepare = clk_pll_unprepare, + .enable = clk_pll_enable, + .disable = clk_pll_disable, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_round_rate, + .set_rate = clk_pll_set_rate, +}; + +static unsigned long clk_cpu_periphclk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct hb_clk *hbclk = to_hb_clk(hwclk); + u32 div = (readl(hbclk->reg) & HB_A9_PCLK_DIV) ? 8 : 4; + return parent_rate / div; +} + +static const struct clk_ops a9periphclk_ops = { + .recalc_rate = clk_cpu_periphclk_recalc_rate, +}; + +static unsigned long clk_cpu_a9bclk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct hb_clk *hbclk = to_hb_clk(hwclk); + u32 div = (readl(hbclk->reg) & HB_A9_BCLK_DIV_MASK) >> HB_A9_BCLK_DIV_SHIFT; + + return parent_rate / (div + 2); +} + +static const struct clk_ops a9bclk_ops = { + .recalc_rate = clk_cpu_a9bclk_recalc_rate, +}; + +static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct hb_clk *hbclk = to_hb_clk(hwclk); + u32 div; + + div = readl(hbclk->reg) & 0x1f; + div++; + div *= 2; + + return parent_rate / div; +} + +static long clk_periclk_round_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long *parent_rate) +{ + u32 div; + + div = *parent_rate / rate; + div++; + div &= ~0x1; + + return *parent_rate / div; +} + +static int clk_periclk_set_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long parent_rate) +{ + struct hb_clk *hbclk = to_hb_clk(hwclk); + u32 div; + + div = parent_rate / rate; + if (div & 0x1) + return -EINVAL; + + writel(div >> 1, hbclk->reg); + return 0; +} + +static const struct clk_ops periclk_ops = { + .recalc_rate = clk_periclk_recalc_rate, + .round_rate = clk_periclk_round_rate, + .set_rate = clk_periclk_set_rate, +}; + +static __init struct clk *hb_clk_init(struct device_node *node, const struct clk_ops *ops) +{ + u32 reg; + struct clk *clk; + struct hb_clk *hb_clk; + const char *clk_name = node->name; + const char *parent_name; + struct clk_init_data init; + int rc; + + rc = of_property_read_u32(node, "reg", ®); + if (WARN_ON(rc)) + return NULL; + + hb_clk = kzalloc(sizeof(*hb_clk), GFP_KERNEL); + if (WARN_ON(!hb_clk)) + return NULL; + + hb_clk->reg = sregs_base + reg; + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = &parent_name; + init.num_parents = 1; + + hb_clk->hw.init = &init; + + clk = clk_register(NULL, &hb_clk->hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(hb_clk); + return NULL; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + return clk; +} + +static void __init hb_pll_init(struct device_node *node) +{ + hb_clk_init(node, &clk_pll_ops); +} + +static void __init hb_a9periph_init(struct device_node *node) +{ + hb_clk_init(node, &a9periphclk_ops); +} + +static void __init hb_a9bus_init(struct device_node *node) +{ + struct clk *clk = hb_clk_init(node, &a9bclk_ops); + clk_prepare_enable(clk); +} + +static void __init hb_emmc_init(struct device_node *node) +{ + hb_clk_init(node, &periclk_ops); +} + +static const __initconst struct of_device_id clk_match[] = { + { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, + { .compatible = "calxeda,hb-pll-clock", .data = hb_pll_init, }, + { .compatible = "calxeda,hb-a9periph-clock", .data = hb_a9periph_init, }, + { .compatible = "calxeda,hb-a9bus-clock", .data = hb_a9bus_init, }, + { .compatible = "calxeda,hb-emmc-clock", .data = hb_emmc_init, }, + {} +}; + +void __init highbank_clocks_init(void) +{ + of_clk_init(clk_match); +} diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index fd36a8ea73d..508c032edce 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -106,7 +106,7 @@ struct clk *clk_register_mux(struct device *dev, const char *name, init.name = name; init.ops = &clk_mux_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = parent_names; init.num_parents = num_parents; diff --git a/drivers/clk/clk-u300.c b/drivers/clk/clk-u300.c new file mode 100644 index 00000000000..a15f7928fb1 --- /dev/null +++ b/drivers/clk/clk-u300.c @@ -0,0 +1,746 @@ +/* + * U300 clock implementation + * Copyright (C) 2007-2012 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * Author: Linus Walleij <linus.walleij@stericsson.com> + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> + */ +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/clk-provider.h> +#include <linux/spinlock.h> +#include <mach/syscon.h> + +/* + * The clocking hierarchy currently looks like this. + * NOTE: the idea is NOT to show how the clocks are routed on the chip! + * The ideas is to show dependencies, so a clock higher up in the + * hierarchy has to be on in order for another clock to be on. Now, + * both CPU and DMA can actually be on top of the hierarchy, and that + * is not modeled currently. Instead we have the backbone AMBA bus on + * top. This bus cannot be programmed in any way but conceptually it + * needs to be active for the bridges and devices to transport data. + * + * Please be aware that a few clocks are hw controlled, which mean that + * the hw itself can turn on/off or change the rate of the clock when + * needed! + * + * AMBA bus + * | + * +- CPU + * +- FSMC NANDIF NAND Flash interface + * +- SEMI Shared Memory interface + * +- ISP Image Signal Processor (U335 only) + * +- CDS (U335 only) + * +- DMA Direct Memory Access Controller + * +- AAIF APP/ACC Inteface (Mobile Scalable Link, MSL) + * +- APEX + * +- VIDEO_ENC AVE2/3 Video Encoder + * +- XGAM Graphics Accelerator Controller + * +- AHB + * | + * +- ahb:0 AHB Bridge + * | | + * | +- ahb:1 INTCON Interrupt controller + * | +- ahb:3 MSPRO Memory Stick Pro controller + * | +- ahb:4 EMIF External Memory interface + * | + * +- fast:0 FAST bridge + * | | + * | +- fast:1 MMCSD MMC/SD card reader controller + * | +- fast:2 I2S0 PCM I2S channel 0 controller + * | +- fast:3 I2S1 PCM I2S channel 1 controller + * | +- fast:4 I2C0 I2C channel 0 controller + * | +- fast:5 I2C1 I2C channel 1 controller + * | +- fast:6 SPI SPI controller + * | +- fast:7 UART1 Secondary UART (U335 only) + * | + * +- slow:0 SLOW bridge + * | + * +- slow:1 SYSCON (not possible to control) + * +- slow:2 WDOG Watchdog + * +- slow:3 UART0 primary UART + * +- slow:4 TIMER_APP Application timer - used in Linux + * +- slow:5 KEYPAD controller + * +- slow:6 GPIO controller + * +- slow:7 RTC controller + * +- slow:8 BT Bus Tracer (not used currently) + * +- slow:9 EH Event Handler (not used currently) + * +- slow:a TIMER_ACC Access style timer (not used currently) + * +- slow:b PPM (U335 only, what is that?) + */ + +/* Global syscon virtual base */ +static void __iomem *syscon_vbase; + +/** + * struct clk_syscon - U300 syscon clock + * @hw: corresponding clock hardware entry + * @hw_ctrld: whether this clock is hardware controlled (for refcount etc) + * and does not need any magic pokes to be enabled/disabled + * @reset: state holder, whether this block's reset line is asserted or not + * @res_reg: reset line enable/disable flag register + * @res_bit: bit for resetting or taking this consumer out of reset + * @en_reg: clock line enable/disable flag register + * @en_bit: bit for enabling/disabling this consumer clock line + * @clk_val: magic value to poke in the register to enable/disable + * this one clock + */ +struct clk_syscon { + struct clk_hw hw; + bool hw_ctrld; + bool reset; + void __iomem *res_reg; + u8 res_bit; + void __iomem *en_reg; + u8 en_bit; + u16 clk_val; +}; + +#define to_syscon(_hw) container_of(_hw, struct clk_syscon, hw) + +static DEFINE_SPINLOCK(syscon_resetreg_lock); + +/* + * Reset control functions. We remember if a block has been + * taken out of reset and don't remove the reset assertion again + * and vice versa. Currently we only remove resets so the + * enablement function is defined out. + */ +static void syscon_block_reset_enable(struct clk_syscon *sclk) +{ + unsigned long iflags; + u16 val; + + /* Not all blocks support resetting */ + if (!sclk->res_reg) + return; + spin_lock_irqsave(&syscon_resetreg_lock, iflags); + val = readw(sclk->res_reg); + val |= BIT(sclk->res_bit); + writew(val, sclk->res_reg); + spin_unlock_irqrestore(&syscon_resetreg_lock, iflags); + sclk->reset = true; +} + +static void syscon_block_reset_disable(struct clk_syscon *sclk) +{ + unsigned long iflags; + u16 val; + + /* Not all blocks support resetting */ + if (!sclk->res_reg) + return; + spin_lock_irqsave(&syscon_resetreg_lock, iflags); + val = readw(sclk->res_reg); + val &= ~BIT(sclk->res_bit); + writew(val, sclk->res_reg); + spin_unlock_irqrestore(&syscon_resetreg_lock, iflags); + sclk->reset = false; +} + +static int syscon_clk_prepare(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + + /* If the block is in reset, bring it out */ + if (sclk->reset) + syscon_block_reset_disable(sclk); + return 0; +} + +static void syscon_clk_unprepare(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + + /* Please don't force the console into reset */ + if (sclk->clk_val == U300_SYSCON_SBCER_UART_CLK_EN) + return; + /* When unpreparing, force block into reset */ + if (!sclk->reset) + syscon_block_reset_enable(sclk); +} + +static int syscon_clk_enable(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + + /* Don't touch the hardware controlled clocks */ + if (sclk->hw_ctrld) + return 0; + /* These cannot be controlled */ + if (sclk->clk_val == 0xFFFFU) + return 0; + + writew(sclk->clk_val, syscon_vbase + U300_SYSCON_SBCER); + return 0; +} + +static void syscon_clk_disable(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + + /* Don't touch the hardware controlled clocks */ + if (sclk->hw_ctrld) + return; + if (sclk->clk_val == 0xFFFFU) + return; + /* Please don't disable the console port */ + if (sclk->clk_val == U300_SYSCON_SBCER_UART_CLK_EN) + return; + + writew(sclk->clk_val, syscon_vbase + U300_SYSCON_SBCDR); +} + +static int syscon_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + u16 val; + + /* If no enable register defined, it's always-on */ + if (!sclk->en_reg) + return 1; + + val = readw(sclk->en_reg); + val &= BIT(sclk->en_bit); + + return val ? 1 : 0; +} + +static u16 syscon_get_perf(void) +{ + u16 val; + + val = readw(syscon_vbase + U300_SYSCON_CCR); + val &= U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; + return val; +} + +static unsigned long +syscon_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_syscon *sclk = to_syscon(hw); + u16 perf = syscon_get_perf(); + + switch(sclk->clk_val) { + case U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN: + case U300_SYSCON_SBCER_I2C0_CLK_EN: + case U300_SYSCON_SBCER_I2C1_CLK_EN: + case U300_SYSCON_SBCER_MMC_CLK_EN: + case U300_SYSCON_SBCER_SPI_CLK_EN: + /* The FAST clocks have one progression */ + switch(perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 13000000; + default: + return parent_rate; /* 26 MHz */ + } + case U300_SYSCON_SBCER_DMAC_CLK_EN: + case U300_SYSCON_SBCER_NANDIF_CLK_EN: + case U300_SYSCON_SBCER_XGAM_CLK_EN: + /* AMBA interconnect peripherals */ + switch(perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 6500000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + return 26000000; + default: + return parent_rate; /* 52 MHz */ |