From bcf6c3d46fa8624397cc52d9f13f9a71bf3a572f Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 25 May 2012 17:25:36 +0800 Subject: clk: mxs: rename the gpmi clock for imx28 rename the clock name from `8000c000.gpmi` to `8000c000.gpmi-nand`. Signed-off-by: Huang Shijie Signed-off-by: Shawn Guo --- drivers/clk/mxs/clk-imx28.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index 2826a26..003b0f3 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -174,7 +174,7 @@ static struct clk_lookup lcdif_lookups[] __initdata = { static struct clk_lookup gpmi_lookups[] __initdata = { { .dev_id = "imx28-gpmi-nand", }, - { .dev_id = "8000c000.gpmi", }, + { .dev_id = "8000c000.gpmi-nand", }, }; static struct clk_lookup fec_lookups[] __initdata = { -- cgit v1.1 From 8c6facab8e99f7f90eaa1edbaf4baf217ac5ea5f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 19 Jun 2012 17:27:32 -0300 Subject: clk: mxs: Fix the GPMI clock name The correct name for the GPMI clock is 'gpmi-nand'. Signed-off-by: Fabio Estevam Acked-by: Marek Vasut Signed-off-by: Shawn Guo --- drivers/clk/mxs/clk-imx23.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c index f7be225..40ad638 100644 --- a/drivers/clk/mxs/clk-imx23.c +++ b/drivers/clk/mxs/clk-imx23.c @@ -106,7 +106,7 @@ static struct clk_lookup lcdif_lookups[] __initdata = { static struct clk_lookup gpmi_lookups[] __initdata = { { .dev_id = "imx23-gpmi-nand", }, - { .dev_id = "8000c000.gpmi", }, + { .dev_id = "8000c000.gpmi-nand", }, }; static const char *sel_pll[] __initconst = { "pll", "ref_xtal", }; -- cgit v1.1 From 275d58025f8d59c493384e1e9660c9c5b7b28d2d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 25 Jun 2012 20:56:03 +0800 Subject: clk: mxs: add clkdev lookup for pwm Add pwm clock lookup for imx23 and imx28 booting from device tree. Signed-off-by: Shawn Guo --- drivers/clk/mxs/clk-imx23.c | 1 + drivers/clk/mxs/clk-imx28.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c index 40ad638..c14b021 100644 --- a/drivers/clk/mxs/clk-imx23.c +++ b/drivers/clk/mxs/clk-imx23.c @@ -189,6 +189,7 @@ int __init mx23_clocks_init(void) } clk_register_clkdev(clks[clk32k], NULL, "timrot"); + clk_register_clkdev(clks[pwm], NULL, "80064000.pwm"); clk_register_clkdevs(clks[hbus], hbus_lookups, ARRAY_SIZE(hbus_lookups)); clk_register_clkdevs(clks[xbus], xbus_lookups, ARRAY_SIZE(xbus_lookups)); clk_register_clkdevs(clks[uart], uart_lookups, ARRAY_SIZE(uart_lookups)); diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index 003b0f3..365053f 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -314,6 +314,7 @@ int __init mx28_clocks_init(void) clk_register_clkdev(clks[clk32k], NULL, "timrot"); clk_register_clkdev(clks[enet_out], NULL, "enet_out"); + clk_register_clkdev(clks[pwm], NULL, "80064000.pwm"); clk_register_clkdevs(clks[hbus], hbus_lookups, ARRAY_SIZE(hbus_lookups)); clk_register_clkdevs(clks[xbus], xbus_lookups, ARRAY_SIZE(xbus_lookups)); clk_register_clkdevs(clks[uart], uart_lookups, ARRAY_SIZE(uart_lookups)); -- cgit v1.1 From 64e2bc41304726a5ca8a4d37754db69b88841ebf Mon Sep 17 00:00:00 2001 From: Lauri Hintsala Date: Wed, 4 Jul 2012 13:49:54 +0300 Subject: clk: mxs: imx28: decrease the frequency of ref_io1 for SSP2 and SSP3 SSP0 and SSP1 use ref_io0 which has decreased frequency. Expand the frequency fix for ref_io1 to get SSP2 and SSP3 to work. Signed-off-by: Lauri Hintsala Signed-off-by: Shawn Guo --- drivers/clk/mxs/clk-imx28.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index 365053f..98624ee 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -112,11 +112,11 @@ static void __init clk_misc_init(void) /* * 480 MHz seems too high to be ssp clock source directly, - * so set frac0 to get a 288 MHz ref_io0. + * so set frac0 to get a 288 MHz ref_io0 and ref_io1. */ val = readl_relaxed(FRAC0); - val &= ~(0x3f << BP_FRAC0_IO0FRAC); - val |= 30 << BP_FRAC0_IO0FRAC; + val &= ~((0x3f << BP_FRAC0_IO0FRAC) | (0x3f << BP_FRAC0_IO1FRAC)); + val |= (30 << BP_FRAC0_IO0FRAC) | (30 << BP_FRAC0_IO1FRAC); writel_relaxed(val, FRAC0); } -- cgit v1.1 From 6d9252bd9a4bb1dadc1f199fd276e3464a251085 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Thu, 17 May 2012 15:52:13 +0530 Subject: clk: Add support for power of two type dividers Quite often dividers and the value programmed in the register have a relation of 'power of two', something like value div 0 1 1 2 2 4 3 8... Add support for such dividers as part of clk-divider. The clk-divider flag 'CLK_DIVIDER_POWER_OF_TWO' should be used to define such clocks. Signed-off-by: Rajendra Nayak Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 66 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 19 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 8ea11b44..e548c43 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -30,18 +30,50 @@ #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_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); + return div_mask(divider) + 1; +} + +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; + return val + 1; +} + +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); + 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; } @@ -62,10 +94,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 +111,9 @@ 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 ((divider->flags & CLK_DIVIDER_POWER_OF_TWO) + && (!is_power_of_two(i))) + continue; parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); now = parent_rate / i; @@ -93,9 +125,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 +145,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) -- cgit v1.1 From 357c3f0a6c7613f7230fcaf1eb16190ed2a4b0af Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Fri, 29 Jun 2012 19:06:32 +0530 Subject: clk: Add support for rate table based dividers Some divider clks do not have any obvious relationship between the divider and the value programmed in the register. For instance, say a value of 1 could signify divide by 6 and a value of 2 could signify divide by 4 etc. Also there are dividers where not all values possible based on the bitfield width are valid. For instance a 3 bit wide bitfield can be used to program a value from 0 to 7. However its possible that only 0 to 4 are valid values. All these cases need the platform code to pass a simple table of divider/value tuple, so the framework knows the exact value to be written based on the divider calculation and can also do better error checking. This patch adds support for such rate table based dividers and as part of the support adds a new registration function 'clk_register_divider_table()' and a new macro for static definition 'DEFINE_CLK_DIVIDER_TABLE'. Signed-off-by: Rajendra Nayak Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 125 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 109 insertions(+), 16 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index e548c43..02a4da9 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -32,30 +32,69 @@ #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; } @@ -84,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) { @@ -111,8 +170,7 @@ 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 ((divider->flags & CLK_DIVIDER_POWER_OF_TWO) - && (!is_power_of_two(i))) + if (!_is_valid_div(divider, i)) continue; parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); @@ -176,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; @@ -217,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); @@ -226,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); +} -- cgit v1.1 From f7d8caadfd2813cbada82ce9041b13c38e8e5282 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Fri, 1 Jun 2012 14:02:47 +0530 Subject: clk: Add CLK_IS_BASIC flag to identify basic clocks Most platforms end up using a mix of basic clock types and some which use clk_hw_foo struct for filling in custom platform information when the clocks don't fit into basic types supported. In platform code, its useful to know if a clock is using a basic type or clk_hw_foo, which helps platforms know if they can safely use to_clk_hw_foo to derive the clk_hw_foo pointer from clk_hw. Mark all basic clocks with a CLK_IS_BASIC flag. Signed-off-by: Rajendra Nayak Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 2 +- drivers/clk/clk-fixed-factor.c | 2 +- drivers/clk/clk-fixed-rate.c | 2 +- drivers/clk/clk-gate.c | 2 +- drivers/clk/clk-mux.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 02a4da9..a9204c6 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -253,7 +253,7 @@ static struct 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); diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index c8c003e..a489985 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 cbd2462..7e14645 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -63,7 +63,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); diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 578465e..15114fe 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-mux.c b/drivers/clk/clk-mux.c index fd36a8e..508c032 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; -- cgit v1.1 From f05259a6ffa40097b5565f25c3fcf833a9d3ecf5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 May 2012 10:04:57 +0100 Subject: clk: wm831x: Add initial WM831x clock driver The WM831x and WM832x series of PMICs contain a flexible clocking subsystem intended to provide always on and system core clocks. It features: - A 32.768kHz crystal oscillator which can optionally be used to pass through an externally generated clock. - A FLL which can be clocked from either the 32.768kHz oscillator or the CLKIN pin. - A CLKOUT pin which can bring out either the oscillator or the FLL output. - The 32.768kHz clock can also optionally be brought out on the GPIO pins of the device. This driver fully supports the 32.768kHz oscillator and CLKOUT. The FLL is supported only in AUTO mode, the full flexibility of the FLL cannot currently be used. Due to a lack of access to systems where the core SoC has been converted to use the generic clock API this driver has been compile tested only. Signed-off-by: Mark Brown Signed-off-by: Mike Turquette --- drivers/clk/Kconfig | 7 + drivers/clk/Makefile | 3 + drivers/clk/clk-wm831x.c | 428 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 438 insertions(+) create mode 100644 drivers/clk/clk-wm831x.c (limited to 'drivers/clk') diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 4864407..3f99b9099 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 b9a5158..b679f11 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -5,3 +5,6 @@ obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \ # SoCs specific obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_PLAT_SPEAR) += spear/ + +# Chip specific +obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c new file mode 100644 index 0000000..e7b7765 --- /dev/null +++ b/drivers/clk/clk-wm831x.c @@ -0,0 +1,428 @@ +/* + * WM831x clock control + * + * Copyright 2011-2 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct wm831x_clk { + struct wm831x *wm831x; + struct clk_hw xtal_hw; + struct clk_hw fll_hw; + struct clk_hw clkout_hw; + struct clk *xtal; + struct clk *fll; + struct clk *clkout; + bool xtal_ena; +}; + +static int wm831x_xtal_is_enabled(struct clk_hw *hw) +{ + struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, + xtal_hw); + + return clkdata->xtal_ena; +} + +static unsigned long wm831x_xtal_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, + xtal_hw); + + if (clkdata->xtal_ena) + return 32768; + else + return 0; +} + +static const struct clk_ops wm831x_xtal_ops = { + .is_enabled = wm831x_xtal_is_enabled, + .recalc_rate = wm831x_xtal_recalc_rate, +}; + +static struct clk_init_data wm831x_xtal_init = { + .name = "xtal", + .ops = &wm831x_xtal_ops, + .flags = CLK_IS_ROOT, +}; + +static const unsigned long wm831x_fll_auto_rates[] = { + 2048000, + 11289600, + 12000000, + 12288000, + 19200000, + 22579600, + 24000000, + 24576000, +}; + +static int wm831x_fll_is_enabled(struct clk_hw *hw) +{ + struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, + fll_hw); + struct wm831x *wm831x = clkdata->wm831x; + int ret; + + ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_1); + if (ret < 0) { + dev_err(wm831x->dev, "Unable to read FLL_CONTROL_1: %d\n", + ret); + return true; + } + + return (ret & WM831X_FLL_ENA) != 0; +} + +static int wm831x_fll_prepare(struct clk_hw *hw) +{ + struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, + fll_hw); + struct wm831x *wm831x = clkdata->wm831x; + int ret; + + ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2, + WM831X_FLL_ENA, WM831X_FLL_ENA); + if (ret != 0) + dev_crit(wm831x->dev, "Failed to enable FLL: %d\n", ret); + + usleep_range(2000, 2000); + + return ret; +} + +static void wm831x_fll_unprepare(struct clk_hw *hw) +{ + struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, + fll_hw); + struct wm831x *wm831x = clkdata->wm831x; + int ret; + + ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2, WM831X_FLL_ENA, 0); + if (ret != 0) + dev_crit(wm831x->dev, "Failed to disaable FLL: %d\n", ret); +} + +static unsigned long wm831x_fll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, + fll_hw); + struct wm831x *wm831x = clkdata->wm831x; + int ret; + + ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2); + if (ret < 0) { + dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n", + ret); + return 0; + } + + if (ret & WM831X_FLL_AUTO) + return wm831x_fll_auto_rates[ret & WM831X_FLL_AUTO_FREQ_MASK]; + + dev_err(wm831x->dev, "FLL only supported in AUTO mode\n"); + + return 0; +} + +static long wm831x_fll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *unused) +{ + int best = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++) + if (abs(wm831x_fll_auto_rates[i] - rate) < + abs(wm831x_fll_auto_rates[best] - rate)) + best = i; + + return wm831x_fll_auto_rates[best]; +} + +static int wm831x_fll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, + fll_hw); + struct wm831x *wm831x = clkdata->wm831x; + int i; + + for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++) + if (wm831x_fll_auto_rates[i] == rate) + break; + if (i == ARRAY_SIZE(wm831x_fll_auto_rates)) + return -EINVAL; + + if (wm831x_fll_is_enabled(hw)) + return -EPERM; + + return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_2, + WM831X_FLL_AUTO_FREQ_MASK, i); +} + +static const char *wm831x_fll_parents[] = { + "xtal", + "clkin", +}; + +static u8 wm831x_fll_get_parent(struct clk_hw *hw) +{ + struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, + fll_hw); + struct wm831x *wm831x = clkdata->wm831x; + int ret; + + /* AUTO mode is always clocked from the crystal */ + ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2); + if (ret < 0) { + dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n", + ret); + return 0; + } + + if (ret & WM831X_FLL_AUTO) + return 0; + + ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_5); + if (ret < 0) { + dev_err(wm831x->dev, "Unable to read FLL_CONTROL_5: %d\n", + ret); + return 0; + } + + switch (ret & WM831X_FLL_CLK_SRC_MASK) { + case 0: + return 0; + case 1: + return 1; + default: + dev_err(wm831x->dev, "Unsupported FLL clock source %d\n", + ret & WM831X_FLL_CLK_SRC_MASK); + return 0; + } +} + +static const struct clk_ops wm831x_fll_ops = { + .is_enabled = wm831x_fll_is_enabled, + .prepare = wm831x_fll_prepare, + .unprepare = wm831x_fll_unprepare, + .round_rate = wm831x_fll_round_rate, + .recalc_rate = wm831x_fll_recalc_rate, + .set_rate = wm831x_fll_set_rate, + .get_parent = wm831x_fll_get_parent, +}; + +static struct clk_init_data wm831x_fll_init = { + .name = "fll", + .ops = &wm831x_fll_ops, + .parent_names = wm831x_fll_parents, + .num_parents = ARRAY_SIZE(wm831x_fll_parents), + .flags = CLK_SET_RATE_GATE, +}; + +static int wm831x_clkout_is_enabled(struct clk_hw *hw) +{ + struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, + clkout_hw); + struct wm831x *wm831x = clkdata->wm831x; + int ret; + + ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1); + if (ret < 0) { + dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n", + ret); + return true; + } + + return (ret & WM831X_CLKOUT_ENA) != 0; +} + +static int wm831x_clkout_prepare(struct clk_hw *hw) +{ + struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, + clkout_hw); + struct wm831x *wm831x = clkdata->wm831x; + int ret; + + ret = wm831x_reg_unlock(wm831x); + if (ret != 0) { + dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret); + return ret; + } + + ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1, + WM831X_CLKOUT_ENA, WM831X_CLKOUT_ENA); + if (ret != 0) + dev_crit(wm831x->dev, "Failed to enable CLKOUT: %d\n", ret); + + wm831x_reg_lock(wm831x); + + return ret; +} + +static void wm831x_clkout_unprepare(struct clk_hw *hw) +{ + struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, + clkout_hw); + struct wm831x *wm831x = clkdata->wm831x; + int ret; + + ret = wm831x_reg_unlock(wm831x); + if (ret != 0) { + dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret); + return; + } + + ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1, + WM831X_CLKOUT_ENA, 0); + if (ret != 0) + dev_crit(wm831x->dev, "Failed to disable CLKOUT: %d\n", ret); + + wm831x_reg_lock(wm831x); +} + +static const char *wm831x_clkout_parents[] = { + "xtal", + "fll", +}; + +static u8 wm831x_clkout_get_parent(struct clk_hw *hw) +{ + struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, + clkout_hw); + struct wm831x *wm831x = clkdata->wm831x; + int ret; + + ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1); + if (ret < 0) { + dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n", + ret); + return 0; + } + + if (ret & WM831X_CLKOUT_SRC) + return 0; + else + return 1; +} + +static int wm831x_clkout_set_parent(struct clk_hw *hw, u8 parent) +{ + struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, + clkout_hw); + struct wm831x *wm831x = clkdata->wm831x; + + return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1, + WM831X_CLKOUT_SRC, + parent << WM831X_CLKOUT_SRC_SHIFT); +} + +static const struct clk_ops wm831x_clkout_ops = { + .is_enabled = wm831x_clkout_is_enabled, + .prepare = wm831x_clkout_prepare, + .unprepare = wm831x_clkout_unprepare, + .get_parent = wm831x_clkout_get_parent, + .set_parent = wm831x_clkout_set_parent, +}; + +static struct clk_init_data wm831x_clkout_init = { + .name = "clkout", + .ops = &wm831x_clkout_ops, + .parent_names = wm831x_clkout_parents, + .num_parents = ARRAY_SIZE(wm831x_clkout_parents), + .flags = CLK_SET_RATE_PARENT, +}; + +static __devinit int wm831x_clk_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_clk *clkdata; + int ret; + + clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL); + if (!clkdata) + return -ENOMEM; + + /* XTAL_ENA can only be set via OTP/InstantConfig so just read once */ + ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2); + if (ret < 0) { + dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n", + ret); + return ret; + } + clkdata->xtal_ena = ret & WM831X_XTAL_ENA; + + clkdata->xtal_hw.init = &wm831x_xtal_init; + clkdata->xtal = clk_register(&pdev->dev, &clkdata->xtal_hw); + if (!clkdata->xtal) + return -EINVAL; + + clkdata->fll_hw.init = &wm831x_fll_init; + clkdata->fll = clk_register(&pdev->dev, &clkdata->fll_hw); + if (!clkdata->fll) { + ret = -EINVAL; + goto err_xtal; + } + + clkdata->clkout_hw.init = &wm831x_clkout_init; + clkdata->clkout = clk_register(&pdev->dev, &clkdata->clkout_hw); + if (!clkdata->clkout) { + ret = -EINVAL; + goto err_fll; + } + + dev_set_drvdata(&pdev->dev, clkdata); + + return 0; + +err_fll: + clk_unregister(clkdata->fll); +err_xtal: + clk_unregister(clkdata->xtal); + return ret; +} + +static int __devexit wm831x_clk_remove(struct platform_device *pdev) +{ + struct wm831x_clk *clkdata = dev_get_drvdata(&pdev->dev); + + clk_unregister(clkdata->clkout); + clk_unregister(clkdata->fll); + clk_unregister(clkdata->xtal); + + return 0; +} + +static struct platform_driver wm831x_clk_driver = { + .probe = wm831x_clk_probe, + .remove = __devexit_p(wm831x_clk_remove), + .driver = { + .name = "wm831x-clk", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(wm831x_clk_driver); + +/* Module information */ +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("WM831x clock driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-clk"); -- cgit v1.1 From 9ca1c5a4bf4105d6f2f2a46892495953dd3e2fec Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Wed, 6 Jun 2012 14:41:30 +0530 Subject: clk: cache parent clocks only for muxes caching parent clocks makes sense only when a clock has more than one parent (mux clocks). Avoid doing this for every other clock. Signed-off-by: Rajendra Nayak [mturquette@linaro.org: removed extra parentheses] Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 9a1eb0c..46317cb 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1235,8 +1235,8 @@ int __clk_init(struct device *dev, struct clk *clk) * If clk->parents is not NULL we skip this entire block. This allows * for clock drivers to statically initialize clk->parents. */ - if (clk->num_parents && !clk->parents) { - clk->parents = kmalloc((sizeof(struct clk*) * clk->num_parents), + if (clk->num_parents > 1 && !clk->parents) { + clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), GFP_KERNEL); /* * __clk_lookup returns NULL for parents that have not been -- cgit v1.1 From 50667d63085af108f625c83102430c1d74931d78 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 Jun 2012 23:44:25 +0200 Subject: ARM: u300: convert to common clock This converts the U300 clock implementation over to use the common struct clk and moves the implementation down into drivers/clk. Since VCO isn't used in tree it was removed, it's not hard to put it back in if need be. Signed-off-by: Linus Walleij [mturquette@linaro.org: trivial Makefile conflict] Signed-off-by: Mike Turquette --- drivers/clk/Makefile | 3 +- drivers/clk/clk-u300.c | 746 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 748 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/clk-u300.c (limited to 'drivers/clk') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index b679f11..35d9fb4 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -1,10 +1,11 @@ - +# 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_MXS) += mxs/ obj-$(CONFIG_PLAT_SPEAR) += spear/ +obj-$(CONFIG_ARCH_U300) += clk-u300.o # Chip specific obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o diff --git a/drivers/clk/clk-u300.c b/drivers/clk/clk-u300.c new file mode 100644 index 0000000..a15f792 --- /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 + * Author: Jonas Aaberg + */ +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 */ + } + case U300_SYSCON_SBCER_SEMI_CLK_EN: + case U300_SYSCON_SBCER_EMIF_CLK_EN: + /* EMIF speeds */ + switch(perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 13000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + return 52000000; + default: + return 104000000; + } + case U300_SYSCON_SBCER_CPU_CLK_EN: + /* And the fast CPU clock */ + switch(perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 13000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + return 52000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: + return 104000000; + default: + return parent_rate; /* 208 MHz */ + } + default: + /* + * The SLOW clocks and default just inherit the rate of + * their parent (typically PLL13 13 MHz). + */ + return parent_rate; + } +} + +static long +syscon_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_syscon *sclk = to_syscon(hw); + + if (sclk->clk_val != U300_SYSCON_SBCER_CPU_CLK_EN) + return *prate; + /* We really only support setting the rate of the CPU clock */ + if (rate <= 13000000) + return 13000000; + if (rate <= 52000000) + return 52000000; + if (rate <= 104000000) + return 104000000; + return 208000000; +} + +static int syscon_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_syscon *sclk = to_syscon(hw); + u16 val; + + /* We only support setting the rate of the CPU clock */ + if (sclk->clk_val != U300_SYSCON_SBCER_CPU_CLK_EN) + return -EINVAL; + switch (rate) { + case 13000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER; + break; + case 52000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE; + break; + case 104000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH; + break; + case 208000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST; + break; + default: + return -EINVAL; + } + val |= readw(syscon_vbase + U300_SYSCON_CCR) & + ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK ; + writew(val, syscon_vbase + U300_SYSCON_CCR); + return 0; +} + +static const struct clk_ops syscon_clk_ops = { + .prepare = syscon_clk_prepare, + .unprepare = syscon_clk_unprepare, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, + .is_enabled = syscon_clk_is_enabled, + .recalc_rate = syscon_clk_recalc_rate, + .round_rate = syscon_clk_round_rate, + .set_rate = syscon_clk_set_rate, +}; + +static struct clk * __init +syscon_clk_register(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + bool hw_ctrld, + void __iomem *res_reg, u8 res_bit, + void __iomem *en_reg, u8 en_bit, + u16 clk_val) +{ + struct clk *clk; + struct clk_syscon *sclk; + struct clk_init_data init; + + sclk = kzalloc(sizeof(struct clk_syscon), GFP_KERNEL); + if (!sclk) { + pr_err("could not allocate syscon clock %s\n", + name); + return ERR_PTR(-ENOMEM); + } + init.name = name; + init.ops = &syscon_clk_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + sclk->hw.init = &init; + sclk->hw_ctrld = hw_ctrld; + /* Assume the block is in reset at registration */ + sclk->reset = true; + sclk->res_reg = res_reg; + sclk->res_bit = res_bit; + sclk->en_reg = en_reg; + sclk->en_bit = en_bit; + sclk->clk_val = clk_val; + + clk = clk_register(dev, &sclk->hw); + if (IS_ERR(clk)) + kfree(sclk); + + return clk; +} + +/** + * struct clk_mclk - U300 MCLK clock (MMC/SD clock) + * @hw: corresponding clock hardware entry + * @is_mspro: if this is the memory stick clock rather than MMC/SD + */ +struct clk_mclk { + struct clk_hw hw; + bool is_mspro; +}; + +#define to_mclk(_hw) container_of(_hw, struct clk_mclk, hw) + +static int mclk_clk_prepare(struct clk_hw *hw) +{ + struct clk_mclk *mclk = to_mclk(hw); + u16 val; + + /* The MMC and MSPRO clocks need some special set-up */ + if (!mclk->is_mspro) { + /* Set default MMC clock divisor to 18.9 MHz */ + writew(0x0054U, syscon_vbase + U300_SYSCON_MMF0R); + val = readw(syscon_vbase + U300_SYSCON_MMCR); + /* Disable the MMC feedback clock */ + val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE; + /* Disable MSPRO frequency */ + val &= ~U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE; + writew(val, syscon_vbase + U300_SYSCON_MMCR); + } else { + val = readw(syscon_vbase + U300_SYSCON_MMCR); + /* Disable the MMC feedback clock */ + val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE; + /* Enable MSPRO frequency */ + val |= U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE; + writew(val, syscon_vbase + U300_SYSCON_MMCR); + } + + return 0; +} + +static unsigned long +mclk_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u16 perf = syscon_get_perf(); + + switch (perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + /* + * Here, the 208 MHz PLL gets shut down and the always + * on 13 MHz PLL used for RTC etc kicks into use + * instead. + */ + return 13000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: + { + /* + * This clock is under program control. The register is + * divided in two nybbles, bit 7-4 gives cycles-1 to count + * high, bit 3-0 gives cycles-1 to count low. Distribute + * these with no more than 1 cycle difference between + * low and high and add low and high to get the actual + * divisor. The base PLL is 208 MHz. Writing 0x00 will + * divide by 1 and 1 so the highest frequency possible + * is 104 MHz. + * + * e.g. 0x54 => + * f = 208 / ((5+1) + (4+1)) = 208 / 11 = 18.9 MHz + */ + u16 val = readw(syscon_vbase + U300_SYSCON_MMF0R) & + U300_SYSCON_MMF0R_MASK; + switch (val) { + case 0x0054: + return 18900000; + case 0x0044: + return 20800000; + case 0x0043: + return 23100000; + case 0x0033: + return 26000000; + case 0x0032: + return 29700000; + case 0x0022: + return 34700000; + case 0x0021: + return 41600000; + case 0x0011: + return 52000000; + case 0x0000: + return 104000000; + default: + break; + } + } + default: + break; + } + return parent_rate; +} + +static long +mclk_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + if (rate <= 18900000) + return 18900000; + if (rate <= 20800000) + return 20800000; + if (rate <= 23100000) + return 23100000; + if (rate <= 26000000) + return 26000000; + if (rate <= 29700000) + return 29700000; + if (rate <= 34700000) + return 34700000; + if (rate <= 41600000) + return 41600000; + /* Highest rate */ + return 52000000; +} + +static int mclk_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u16 val; + u16 reg; + + switch (rate) { + case 18900000: + val = 0x0054; + break; + case 20800000: + val = 0x0044; + break; + case 23100000: + val = 0x0043; + break; + case 26000000: + val = 0x0033; + break; + case 29700000: + val = 0x0032; + break; + case 34700000: + val = 0x0022; + break; + case 41600000: + val = 0x0021; + break; + case 52000000: + val = 0x0011; + break; + case 104000000: + val = 0x0000; + break; + default: + return -EINVAL; + } + + reg = readw(syscon_vbase + U300_SYSCON_MMF0R) & + ~U300_SYSCON_MMF0R_MASK; + writew(reg | val, syscon_vbase + U300_SYSCON_MMF0R); + return 0; +} + +static const struct clk_ops mclk_ops = { + .prepare = mclk_clk_prepare, + .recalc_rate = mclk_clk_recalc_rate, + .round_rate = mclk_clk_round_rate, + .set_rate = mclk_clk_set_rate, +}; + +static struct clk * __init +mclk_clk_register(struct device *dev, const char *name, + const char *parent_name, bool is_mspro) +{ + struct clk *clk; + struct clk_mclk *mclk; + struct clk_init_data init; + + mclk = kzalloc(sizeof(struct clk_mclk), GFP_KERNEL); + if (!mclk) { + pr_err("could not allocate MMC/SD clock %s\n", + name); + return ERR_PTR(-ENOMEM); + } + init.name = "mclk"; + init.ops = &mclk_ops; + init.flags = 0; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + mclk->hw.init = &init; + mclk->is_mspro = is_mspro; + + clk = clk_register(dev, &mclk->hw); + if (IS_ERR(clk)) + kfree(mclk); + + return clk; +} + +void __init u300_clk_init(void __iomem *base) +{ + u16 val; + struct clk *clk; + + syscon_vbase = base; + + /* Set system to run at PLL208, max performance, a known state. */ + val = readw(syscon_vbase + U300_SYSCON_CCR); + val &= ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; + writew(val, syscon_vbase + U300_SYSCON_CCR); + /* Wait for the PLL208 to lock if not locked in yet */ + while (!(readw(syscon_vbase + U300_SYSCON_CSR) & + U300_SYSCON_CSR_PLL208_LOCK_IND)); + + /* Power management enable */ + val = readw(syscon_vbase + U300_SYSCON_PMCR); + val |= U300_SYSCON_PMCR_PWR_MGNT_ENABLE; + writew(val, syscon_vbase + U300_SYSCON_PMCR); + + /* These are always available (RTC and PLL13) */ + clk = clk_register_fixed_rate(NULL, "app_32_clk", NULL, + CLK_IS_ROOT, 32768); + /* The watchdog sits directly on the 32 kHz clock */ + clk_register_clkdev(clk, NULL, "coh901327_wdog"); + clk = clk_register_fixed_rate(NULL, "pll13", NULL, + CLK_IS_ROOT, 13000000); + + /* These derive from PLL208 */ + clk = clk_register_fixed_rate(NULL, "pll208", NULL, + CLK_IS_ROOT, 208000000); + clk = clk_register_fixed_factor(NULL, "app_208_clk", "pll208", + 0, 1, 1); + clk = clk_register_fixed_factor(NULL, "app_104_clk", "pll208", + 0, 1, 2); + clk = clk_register_fixed_factor(NULL, "app_52_clk", "pll208", + 0, 1, 4); + /* The 52 MHz is divided down to 26 MHz */ + clk = clk_register_fixed_factor(NULL, "app_26_clk", "app_52_clk", + 0, 1, 2); + + /* Directly on the AMBA interconnect */ + clk = syscon_clk_register(NULL, "cpu_clk", "app_208_clk", 0, true, + syscon_vbase + U300_SYSCON_RRR, 3, + syscon_vbase + U300_SYSCON_CERR, 3, + U300_SYSCON_SBCER_CPU_CLK_EN); + clk = syscon_clk_register(NULL, "dmac_clk", "app_52_clk", 0, true, + syscon_vbase + U300_SYSCON_RRR, 4, + syscon_vbase + U300_SYSCON_CERR, 4, + U300_SYSCON_SBCER_DMAC_CLK_EN); + clk_register_clkdev(clk, NULL, "dma"); + clk = syscon_clk_register(NULL, "fsmc_clk", "app_52_clk", 0, false, + syscon_vbase + U300_SYSCON_RRR, 6, + syscon_vbase + U300_SYSCON_CERR, 6, + U300_SYSCON_SBCER_NANDIF_CLK_EN); + clk_register_clkdev(clk, NULL, "fsmc-nand"); + clk = syscon_clk_register(NULL, "xgam_clk", "app_52_clk", 0, true, + syscon_vbase + U300_SYSCON_RRR, 8, + syscon_vbase + U300_SYSCON_CERR, 8, + U300_SYSCON_SBCER_XGAM_CLK_EN); + clk_register_clkdev(clk, NULL, "xgam"); + clk = syscon_clk_register(NULL, "semi_clk", "app_104_clk", 0, false, + syscon_vbase + U300_SYSCON_RRR, 9, + syscon_vbase + U300_SYSCON_CERR, 9, + U300_SYSCON_SBCER_SEMI_CLK_EN); + clk_register_clkdev(clk, NULL, "semi"); + + /* AHB bridge clocks */ + clk = syscon_clk_register(NULL, "ahb_subsys_clk", "app_52_clk", 0, true, + syscon_vbase + U300_SYSCON_RRR, 10, + syscon_vbase + U300_SYSCON_CERR, 10, + U300_SYSCON_SBCER_AHB_SUBSYS_BRIDGE_CLK_EN); + clk = syscon_clk_register(NULL, "intcon_clk", "ahb_subsys_clk", 0, false, + syscon_vbase + U300_SYSCON_RRR, 12, + syscon_vbase + U300_SYSCON_CERR, 12, + /* Cannot be enabled, just taken out of reset */ + 0xFFFFU); + clk_register_clkdev(clk, NULL, "intcon"); + clk = syscon_clk_register(NULL, "emif_clk", "ahb_subsys_clk", 0, false, + syscon_vbase + U300_SYSCON_RRR, 5, + syscon_vbase + U300_SYSCON_CERR, 5, + U300_SYSCON_SBCER_EMIF_CLK_EN); + clk_register_clkdev(clk, NULL, "pl172"); + + /* FAST bridge clocks */ + clk = syscon_clk_register(NULL, "fast_clk", "app_26_clk", 0, true, + syscon_vbase + U300_SYSCON_RFR, 0, + syscon_vbase + U300_SYSCON_CEFR, 0, + U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN); + clk = syscon_clk_register(NULL, "i2c0_p_clk", "fast_clk", 0, false, + syscon_vbase + U300_SYSCON_RFR, 1, + syscon_vbase + U300_SYSCON_CEFR, 1, + U300_SYSCON_SBCER_I2C0_CLK_EN); + clk_register_clkdev(clk, NULL, "stu300.0"); + clk = syscon_clk_register(NULL, "i2c1_p_clk", "fast_clk", 0, false, + syscon_vbase + U300_SYSCON_RFR, 2, + syscon_vbase + U300_SYSCON_CEFR, 2, + U300_SYSCON_SBCER_I2C1_CLK_EN); + clk_register_clkdev(clk, NULL, "stu300.1"); + clk = syscon_clk_register(NULL, "mmc_p_clk", "fast_clk", 0, false, + syscon_vbase + U300_SYSCON_RFR, 5, + syscon_vbase + U300_SYSCON_CEFR, 5, + U300_SYSCON_SBCER_MMC_CLK_EN); + clk_register_clkdev(clk, "apb_pclk", "mmci"); + clk = syscon_clk_register(NULL, "spi_p_clk", "fast_clk", 0, false, + syscon_vbase + U300_SYSCON_RFR, 6, + syscon_vbase + U300_SYSCON_CEFR, 6, + U300_SYSCON_SBCER_SPI_CLK_EN); + /* The SPI has no external clock for the outward bus, uses the pclk */ + clk_register_clkdev(clk, NULL, "pl022"); + clk_register_clkdev(clk, "apb_pclk", "pl022"); + + /* SLOW bridge clocks */ + clk = syscon_clk_register(NULL, "slow_clk", "pll13", 0, true, + syscon_vbase + U300_SYSCON_RSR, 0, + syscon_vbase + U300_SYSCON_CESR, 0, + U300_SYSCON_SBCER_SLOW_BRIDGE_CLK_EN); + clk = syscon_clk_register(NULL, "uart0_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 1, + syscon_vbase + U300_SYSCON_CESR, 1, + U300_SYSCON_SBCER_UART_CLK_EN); + /* Same clock is used for APB and outward bus */ + clk_register_clkdev(clk, NULL, "uart0"); + clk_register_clkdev(clk, "apb_pclk", "uart0"); + clk = syscon_clk_register(NULL, "gpio_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 4, + syscon_vbase + U300_SYSCON_CESR, 4, + U300_SYSCON_SBCER_GPIO_CLK_EN); + clk_register_clkdev(clk, NULL, "u300-gpio"); + clk = syscon_clk_register(NULL, "keypad_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 5, + syscon_vbase + U300_SYSCON_CESR, 6, + U300_SYSCON_SBCER_KEYPAD_CLK_EN); + clk_register_clkdev(clk, NULL, "coh901461-keypad"); + clk = syscon_clk_register(NULL, "rtc_clk", "slow_clk", 0, true, + syscon_vbase + U300_SYSCON_RSR, 6, + /* No clock enable register bit */ + NULL, 0, 0xFFFFU); + clk_register_clkdev(clk, NULL, "rtc-coh901331"); + clk = syscon_clk_register(NULL, "app_tmr_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 7, + syscon_vbase + U300_SYSCON_CESR, 7, + U300_SYSCON_SBCER_APP_TMR_CLK_EN); + clk_register_clkdev(clk, NULL, "apptimer"); + clk = syscon_clk_register(NULL, "acc_tmr_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 8, + syscon_vbase + U300_SYSCON_CESR, 8, + U300_SYSCON_SBCER_ACC_TMR_CLK_EN); + clk_register_clkdev(clk, NULL, "timer"); + + /* Then this special MMC/SD clock */ + clk = mclk_clk_register(NULL, "mmc_clk", "mmc_p_clk", false); + clk_register_clkdev(clk, NULL, "mmci"); +} -- cgit v1.1 From 91b87a4795c42b97b8d18c3757eff352458ecef4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 11 Jun 2012 17:29:54 +0200 Subject: clk: add versatile ICST307 driver The ICST307 VCO clock has a shared driver in the ARM architecture. This patch provides a wrapper into the common clock framework so we can use the implementation in the ARM architecture without duplicating the code until all ARM platforms using this VCO are moved over. At that point we can merge the driver from the ARM platform into the generic file altogether. Cc: Russell King Cc: Mike Turquette Signed-off-by: Linus Walleij [mturquette@linaro.org: removed versatile Kconfig] Signed-off-by: Mike Turquette --- drivers/clk/Makefile | 1 + drivers/clk/versatile/Makefile | 2 + drivers/clk/versatile/clk-icst.c | 100 +++++++++++++++++++++++++++++++++++++++ drivers/clk/versatile/clk-icst.h | 10 ++++ 4 files changed, 113 insertions(+) create mode 100644 drivers/clk/versatile/Makefile create mode 100644 drivers/clk/versatile/clk-icst.c create mode 100644 drivers/clk/versatile/clk-icst.h (limited to 'drivers/clk') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 35d9fb4..a4d67d7 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \ obj-$(CONFIG_ARCH_MXS) += mxs/ 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/versatile/Makefile b/drivers/clk/versatile/Makefile new file mode 100644 index 0000000..a83539b --- /dev/null +++ b/drivers/clk/versatile/Makefile @@ -0,0 +1,2 @@ +# Makefile for Versatile-specific clocks +obj-$(CONFIG_ICST) += clk-icst.o diff --git a/drivers/clk/versatile/clk-icst.c b/drivers/clk/versatile/clk-icst.c new file mode 100644 index 0000000..f555b50 --- /dev/null +++ b/drivers/clk/versatile/clk-icst.c @@ -0,0 +1,100 @@ +/* + * Driver for the ICST307 VCO clock found in the ARM Reference designs. + * We wrap the custom interface from into the generic + * clock framework. + * + * TODO: when all ARM reference designs are migrated to generic clocks, the + * ICST clock code from the ARM tree should probably be merged into this + * file. + */ +#include +#include +#include +#include + +#include "clk-icst.h" + +/** + * struct clk_icst - ICST VCO clock wrapper + * @hw: corresponding clock hardware entry + * @params: parameters for this ICST instance + * @rate: current rate + * @setvco: function to commit ICST settings to hardware + */ +struct clk_icst { + struct clk_hw hw; + const struct icst_params *params; + unsigned long rate; + struct icst_vco (*getvco)(void); + void (*setvco)(struct icst_vco); +}; + +#define to_icst(_hw) container_of(_hw, struct clk_icst, hw) + +static unsigned long icst_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_icst *icst = to_icst(hw); + struct icst_vco vco; + + vco = icst->getvco(); + icst->rate = icst_hz(icst->params, vco); + return icst->rate; +} + +static long icst_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_icst *icst = to_icst(hw); + struct icst_vco vco; + + vco = icst_hz_to_vco(icst->params, rate); + return icst_hz(icst->params, vco); +} + +static int icst_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_icst *icst = to_icst(hw); + struct icst_vco vco; + + vco = icst_hz_to_vco(icst->params, rate); + icst->rate = icst_hz(icst->params, vco); + icst->setvco(vco); + return 0; +} + +static const struct clk_ops icst_ops = { + .recalc_rate = icst_recalc_rate, + .round_rate = icst_round_rate, + .set_rate = icst_set_rate, +}; + +struct clk * __init icst_clk_register(struct device *dev, + const struct clk_icst_desc *desc) +{ + struct clk *clk; + struct clk_icst *icst; + struct clk_init_data init; + + icst = kzalloc(sizeof(struct clk_icst), GFP_KERNEL); + if (!icst) { + pr_err("could not allocate ICST clock!\n"); + return ERR_PTR(-ENOMEM); + } + init.name = "icst"; + init.ops = &icst_ops; + init.flags = CLK_IS_ROOT; + init.parent_names = NULL; + init.num_parents = 0; + icst->hw.init = &init; + icst->params = desc->params; + icst->getvco = desc->getvco; + icst->setvco = desc->setvco; + + clk = clk_register(dev, &icst->hw); + if (IS_ERR(clk)) + kfree(icst); + + return clk; +} diff --git a/drivers/clk/versatile/clk-icst.h b/drivers/clk/versatile/clk-icst.h new file mode 100644 index 0000000..71b4c56 --- /dev/null +++ b/drivers/clk/versatile/clk-icst.h @@ -0,0 +1,10 @@ +#include + +struct clk_icst_desc { + const struct icst_params *params; + struct icst_vco (*getvco)(void); + void (*setvco)(struct icst_vco); +}; + +struct clk *icst_clk_register(struct device *dev, + const struct clk_icst_desc *desc); -- cgit v1.1 From a613163dff04cbfcb7d66b06ef4a5f65498ee59b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 11 Jun 2012 17:33:12 +0200 Subject: ARM: integrator: convert to common clock This converts the Integrator platform to use common clock and the ICST driver. Since from this point not all ARM reference platforms use the clock, we define CONFIG_PLAT_VERSATILE_CLOCK and select it for all platforms except the Integrator. Open issue: I could not use the .init_early() field of the machine descriptor to initialize the clocks, but had to move them to .init_irq(), so presumably .init_early() is so early that common clock is not up, and .init_machine() is too late since it's needed for the clockevent/clocksource initialization. Any suggestions on how to solve this is very welcome. Cc: Russell King Signed-off-by: Linus Walleij [mturquette@linaro.org: use 'select' instead of versatile Kconfig] Signed-off-by: Mike Turquette --- drivers/clk/versatile/Makefile | 1 + drivers/clk/versatile/clk-integrator.c | 111 +++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 drivers/clk/versatile/clk-integrator.c (limited to 'drivers/clk') diff --git a/drivers/clk/versatile/Makefile b/drivers/clk/versatile/Makefile index a83539b..50cf6a2 100644 --- a/drivers/clk/versatile/Makefile +++ b/drivers/clk/versatile/Makefile @@ -1,2 +1,3 @@ # Makefile for Versatile-specific clocks obj-$(CONFIG_ICST) += clk-icst.o +obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o diff --git a/drivers/clk/versatile/clk-integrator.c b/drivers/clk/versatile/clk-integrator.c new file mode 100644 index 0000000..a505392 --- /dev/null +++ b/drivers/clk/versatile/clk-integrator.c @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "clk-icst.h" + +/* + * Implementation of the ARM Integrator/AP and Integrator/CP clock tree. + * Inspired by portions of: + * plat-versatile/clock.c and plat-versatile/include/plat/clock.h + */ +#define CM_LOCK (__io_address(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_LOCK_OFFSET) +#define CM_AUXOSC (__io_address(INTEGRATOR_HDR_BASE)+0x1c) + +/** + * cp_auxvco_get() - get ICST VCO settings for the Integrator/CP + * @vco: ICST VCO parameters to update with hardware status + */ +static struct icst_vco cp_auxvco_get(void) +{ + u32 val; + struct icst_vco vco; + + val = readl(CM_AUXOSC); + vco.v = val & 0x1ff; + vco.r = (val >> 9) & 0x7f; + vco.s = (val >> 16) & 03; + return vco; +} + +/** + * cp_auxvco_set() - commit changes to Integrator/CP ICST VCO + * @vco: ICST VCO parameters to commit + */ +static void cp_auxvco_set(struct icst_vco vco) +{ + u32 val; + + val = readl(CM_AUXOSC) & ~0x7ffff; + val |= vco.v | (vco.r << 9) | (vco.s << 16); + + /* This magic unlocks the CM VCO so it can be controlled */ + writel(0xa05f, CM_LOCK); + writel(val, CM_AUXOSC); + /* This locks the CM again */ + writel(0, CM_LOCK); +} + +static const struct icst_params cp_auxvco_params = { + .ref = 24000000, + .vco_max = ICST525_VCO_MAX_5V, + .vco_min = ICST525_VCO_MIN, + .vd_min = 8, + .vd_max = 263, + .rd_min = 3, + .rd_max = 65, + .s2div = icst525_s2div, + .idx2s = icst525_idx2s, +}; + +static const struct clk_icst_desc __initdata cp_icst_desc = { + .params = &cp_auxvco_params, + .getvco = cp_auxvco_get, + .setvco = cp_auxvco_set, +}; + +/* + * integrator_clk_init() - set up the integrator clock tree + * @is_cp: pass true if it's the Integrator/CP else AP is assumed + */ +void __init integrator_clk_init(bool is_cp) +{ + struct clk *clk; + + /* APB clock dummy */ + clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0); + clk_register_clkdev(clk, "apb_pclk", NULL); + + /* UART reference clock */ + clk = clk_register_fixed_rate(NULL, "uartclk", NULL, CLK_IS_ROOT, + 14745600); + clk_register_clkdev(clk, NULL, "uart0"); + clk_register_clkdev(clk, NULL, "uart1"); + if (is_cp) + clk_register_clkdev(clk, NULL, "mmci"); + + /* 24 MHz clock */ + clk = clk_register_fixed_rate(NULL, "clk24mhz", NULL, CLK_IS_ROOT, + 24000000); + clk_register_clkdev(clk, NULL, "kmi0"); + clk_register_clkdev(clk, NULL, "kmi1"); + if (!is_cp) + clk_register_clkdev(clk, NULL, "ap_timer"); + + if (!is_cp) + return; + + /* 1 MHz clock */ + clk = clk_register_fixed_rate(NULL, "clk1mhz", NULL, CLK_IS_ROOT, + 1000000); + clk_register_clkdev(clk, NULL, "sp804"); + + /* ICST VCO clock used on the Integrator/CP CLCD */ + clk = icst_clk_register(NULL, &cp_icst_desc); + clk_register_clkdev(clk, NULL, "clcd"); +} -- cgit v1.1 From 766e6a4ec602d0c107553b91b3434fe9c03474f4 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Mon, 9 Apr 2012 14:50:06 -0500 Subject: clk: add DT clock binding support Based on work 1st by Ben Herrenschmidt and Jeremy Kerr, then by Grant Likely, this patch adds support to clk_get to allow drivers to retrieve clock data from the device tree. Platforms scan for clocks in DT with of_clk_init and a match table, and the register a provider through of_clk_add_provider. The provider's clk_src_get function will be called when a device references the provider's OF node for a clock reference. v6 (Rob Herring): - Return error values instead of NULL to match clock framework expectations v5 (Rob Herring): - Move from drivers/of into common clock subsystem - Squashed "dt/clock: add a simple provider get function" and "dt/clock: add function to get parent clock name" - Rebase to 3.4-rc1 - Drop CONFIG_OF_CLOCK and just use CONFIG_OF - Add missing EXPORT_SYMBOL to various functions - s/clock-output-name/clock-output-names/ - Define that fixed-clock binding is a single output v4 (Rob Herring): - Rework for common clk subsystem - Add of_clk_get_parent_name function v3: - Clarified documentation v2: - fixed errant ';' causing compile error - Editorial fixes from Shawn Guo - merged in adding lookup to clkdev - changed property names to match established convention. After working with the binding a bit it really made more sense to follow the lead of 'reg', 'gpios' and 'interrupts' by making the input simply 'clocks' & 'clock-names' instead of 'clock-input-*', and to only use clock-output* for the producer nodes. (Sorry Shawn, this will mean you need to change some code, but it should be trivial) - Add ability to inherit clocks from parent nodes by using an empty 'clock-ranges' property. Useful for busses. I could use some feedback on the new property name, 'clock-ranges' doesn't feel right to me. Signed-off-by: Grant Likely Signed-off-by: Rob Herring Reviewed-by: Shawn Guo Cc: Sascha Hauer Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/clk/clkdev.c | 77 ++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 46317cb..c87fdd7 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -16,6 +16,7 @@ #include #include #include +#include static DEFINE_SPINLOCK(enable_lock); static DEFINE_MUTEX(prepare_lock); @@ -1550,3 +1551,142 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb) return ret; } EXPORT_SYMBOL_GPL(clk_notifier_unregister); + +#ifdef CONFIG_OF +/** + * struct of_clk_provider - Clock provider registration structure + * @link: Entry in global list of clock providers + * @node: Pointer to device tree node of clock provider + * @get: Get clock callback. Returns NULL or a struct clk for the + * given clock specifier + * @data: context pointer to be passed into @get callback + */ +struct of_clk_provider { + struct list_head link; + + struct device_node *node; + struct clk *(*get)(struct of_phandle_args *clkspec, void *data); + void *data; +}; + +static LIST_HEAD(of_clk_providers); +static DEFINE_MUTEX(of_clk_lock); + +struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, + void *data) +{ + return data; +} +EXPORT_SYMBOL_GPL(of_clk_src_simple_get); + +/** + * of_clk_add_provider() - Register a clock provider for a node + * @np: Device node pointer associated with clock provider + * @clk_src_get: callback for decoding clock + * @data: context pointer for @clk_src_get callback. + */ +int of_clk_add_provider(struct device_node *np, + struct clk *(*clk_src_get)(struct of_phandle_args *clkspec, + void *data), + void *data) +{ + struct of_clk_provider *cp; + + cp = kzalloc(sizeof(struct of_clk_provider), GFP_KERNEL); + if (!cp) + return -ENOMEM; + + cp->node = of_node_get(np); + cp->data = data; + cp->get = clk_src_get; + + mutex_lock(&of_clk_lock); + list_add(&cp->link, &of_clk_providers); + mutex_unlock(&of_clk_lock); + pr_debug("Added clock from %s\n", np->full_name); + + return 0; +} +EXPORT_SYMBOL_GPL(of_clk_add_provider); + +/** + * of_clk_del_provider() - Remove a previously registered clock provider + * @np: Device node pointer associated with clock provider + */ +void of_clk_del_provider(struct device_node *np) +{ + struct of_clk_provider *cp; + + mutex_lock(&of_clk_lock); + list_for_each_entry(cp, &of_clk_providers, link) { + if (cp->node == np) { + list_del(&cp->link); + of_node_put(cp->node); + kfree(cp); + break; + } + } + mutex_unlock(&of_clk_lock); +} +EXPORT_SYMBOL_GPL(of_clk_del_provider); + +struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec) +{ + struct of_clk_provider *provider; + struct clk *clk = ERR_PTR(-ENOENT); + + /* Check if we have such a provider in our array */ + mutex_lock(&of_clk_lock); + list_for_each_entry(provider, &of_clk_providers, link) { + if (provider->node == clkspec->np) + clk = provider->get(clkspec, provider->data); + if (!IS_ERR(clk)) + break; + } + mutex_unlock(&of_clk_lock); + + return clk; +} + +const char *of_clk_get_parent_name(struct device_node *np, int index) +{ + struct of_phandle_args clkspec; + const char *clk_name; + int rc; + + if (index < 0) + return NULL; + + rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index, + &clkspec); + if (rc) + return NULL; + + if (of_property_read_string_index(clkspec.np, "clock-output-names", + clkspec.args_count ? clkspec.args[0] : 0, + &clk_name) < 0) + clk_name = clkspec.np->name; + + of_node_put(clkspec.np); + return clk_name; +} +EXPORT_SYMBOL_GPL(of_clk_get_parent_name); + +/** + * of_clk_init() - Scan and init clock providers from the DT + * @matches: array of compatible values and init functions for providers. + * + * This function scans the device tree for matching clock providers and + * calls their initialization functions + */ +void __init of_clk_init(const struct of_device_id *matches) +{ + struct device_node *np; + + for_each_matching_node(np, matches) { + const struct of_device_id *match = of_match_node(matches, np); + of_clk_init_cb_t clk_init_cb = match->data; + clk_init_cb(np); + } +} +#endif diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index c535cf8..20649b3 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -19,10 +19,80 @@ #include #include #include +#include static LIST_HEAD(clocks); static DEFINE_MUTEX(clocks_mutex); +#ifdef CONFIG_OF +struct clk *of_clk_get(struct device_node *np, int index) +{ + struct of_phandle_args clkspec; + struct clk *clk; + int rc; + + if (index < 0) + return ERR_PTR(-EINVAL); + + rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index, + &clkspec); + if (rc) + return ERR_PTR(rc); + + clk = of_clk_get_from_provider(&clkspec); + of_node_put(clkspec.np); + return clk; +} +EXPORT_SYMBOL(of_clk_get); + +/** + * of_clk_get_by_name() - Parse and lookup a clock referenced by a device node + * @np: pointer to clock consumer node + * @name: name of consumer's clock input, or NULL for the first clock reference + * + * This function parses the clocks and clock-names properties, + * and uses them to look up the struct clk from the registered list of clock + * providers. + */ +struct clk *of_clk_get_by_name(struct device_node *np, const char *name) +{ + struct clk *clk = ERR_PTR(-ENOENT); + + /* Walk up the tree of devices looking for a clock that matches */ + while (np) { + int index = 0; + + /* + * For named clocks, first look up the name in the + * "clock-names" property. If it cannot be found, then + * index will be an error code, and of_clk_get() will fail. + */ + if (name) + index = of_property_match_string(np, "clock-names", name); + clk = of_clk_get(np, index); + if (!IS_ERR(clk)) + break; + else if (name && index >= 0) { + pr_err("ERROR: could not get clock %s:%s(%i)\n", + np->full_name, name ? name : "", index); + return clk; + } + + /* + * No matching clock found on this node. If the parent node + * has a "clock-ranges" property, then we can try one of its + * clocks. + */ + np = np->parent; + if (np && !of_get_property(np, "clock-ranges", NULL)) + break; + } + + return clk; +} +EXPORT_SYMBOL(of_clk_get_by_name); +#endif + /* * Find the correct struct clk for the device and connection ID. * We do slightly fuzzy matching here: @@ -83,6 +153,13 @@ EXPORT_SYMBOL(clk_get_sys); struct clk *clk_get(struct device *dev, const char *con_id) { const char *dev_id = dev ? dev_name(dev) : NULL; + struct clk *clk; + + if (dev) { + clk = of_clk_get_by_name(dev->of_node, con_id); + if (clk && __clk_get(clk)) + return clk; + } return clk_get_sys(dev_id, con_id); } -- cgit v1.1 From 015ba40246497ae02a5f644d4c8adfec76d9b75c Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 7 Apr 2012 21:39:39 -0500 Subject: clk: add DT fixed-clock binding support Add support for DT "fixed-clock" binding to the common fixed rate clock support. Signed-off-by: Grant Likely [Rob Herring] Rework and move into common clock infrastructure Signed-off-by: Rob Herring Signed-off-by: Mike Turquette --- drivers/clk/clk-fixed-rate.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index 7e14645..f5ec0ee 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -14,6 +14,7 @@ #include #include #include +#include /* * DOC: basic fixed-rate clock that cannot gate @@ -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 -- cgit v1.1 From 8d4d9f52081cd45bde42b9452eba95f38feb7dae Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 13 Mar 2012 18:19:19 -0500 Subject: clk: add highbank clock support This adds real clock support to Calxeda Highbank SOC using the common clock infrastructure. Signed-off-by: Rob Herring [mturquette@linaro.org: fixed up invalid writes to const struct member] Signed-off-by: Mike Turquette --- drivers/clk/Makefile | 1 + drivers/clk/clk-highbank.c | 346 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 347 insertions(+) create mode 100644 drivers/clk/clk-highbank.c (limited to 'drivers/clk') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index a4d67d7..02ffdf6 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -3,6 +3,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-fixed-factor.o # SoCs specific +obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-$(CONFIG_ARCH_U300) += clk-u300.o diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c new file mode 100644 index 0000000..52fecad --- /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 . + */ + +#include +#include +#include +#include +#include +#include + +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); +} -- cgit v1.1 From 79ddff903e054e14c4109b4f86ec5333665b1ddd Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Thu, 12 Jul 2012 10:25:26 +0800 Subject: clk: mxs: clk_register_clkdev mx28 usb clocks Signed-off-by: Richard Zhao Tested-by: Subodh Nijsure Signed-off-by: Shawn Guo --- drivers/clk/mxs/clk-imx28.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index 98624ee..eb430bb 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -329,6 +329,10 @@ int __init mx28_clocks_init(void) clk_register_clkdevs(clks[fec], fec_lookups, ARRAY_SIZE(fec_lookups)); clk_register_clkdevs(clks[can0], can0_lookups, ARRAY_SIZE(can0_lookups)); clk_register_clkdevs(clks[can1], can1_lookups, ARRAY_SIZE(can1_lookups)); + clk_register_clkdev(clks[usb0_pwr], NULL, "8007c000.usbphy"); + clk_register_clkdev(clks[usb1_pwr], NULL, "8007e000.usbphy"); + clk_register_clkdev(clks[usb0], NULL, "80080000.usb"); + clk_register_clkdev(clks[usb1], NULL, "80090000.usb"); for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) clk_prepare_enable(clks[clks_init_on[i]]); -- cgit v1.1 From 9f1612d351a8e57d3d694e828641d3e4eeb224f8 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 18 Jul 2012 11:52:22 +0800 Subject: clk: fix clk_get on of_clk_get_by_name return check The commit 766e6a4 (clk: add DT clock binding support) plugs device tree clk lookup of_clk_get_by_name into clk_get, and fall on non-DT lookup clk_get_sys if DT lookup fails. The return check on of_clk_get_by_name takes (clk != NULL) as a successful DT lookup. But it's not the case. For any system that does not define clk lookup in device tree, ERR_PTR(-ENOENT) will be returned, and consequently, all the client drivers calling clk_get in their probe functions will fail to probe with error code -ENOENT returned. Fix the issue by checking of_clk_get_by_name return with !IS_ERR(clk), and update of_clk_get and of_clk_get_by_name for !CONFIG_OF build correspondingly. Signed-off-by: Shawn Guo Acked-by: Rob Herring Tested-by: Marek Vasut Tested-by: Lauri Hintsala Signed-off-by: Mike Turquette --- drivers/clk/clkdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 20649b3..69085e0 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -157,7 +157,7 @@ struct clk *clk_get(struct device *dev, const char *con_id) if (dev) { clk = of_clk_get_by_name(dev->of_node, con_id); - if (clk && __clk_get(clk)) + if (!IS_ERR(clk) && __clk_get(clk)) return clk; } -- cgit v1.1 From 137f8a7213d80c1388ca48280c1ef0856b6fec30 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 18 Jul 2012 11:52:23 +0800 Subject: clk: fix compile for OF && !COMMON_CLK With commit 766e6a4ec602d0c107 (clk: add DT clock binding support), compiling with OF && !COMMON_CLK is broken. Reported-by: Alexandre Pereira da Silva Reported-by: Prashant Gaikwad Signed-off-by: Rob Herring Signed-off-by: Mike Turquette --- drivers/clk/clkdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 69085e0..d423c9b 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -24,7 +24,7 @@ static LIST_HEAD(clocks); static DEFINE_MUTEX(clocks_mutex); -#ifdef CONFIG_OF +#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK) struct clk *of_clk_get(struct device_node *np, int index) { struct of_phandle_args clkspec; -- cgit v1.1