diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-26 12:42:29 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-26 12:42:29 -0700 |
commit | 27953437059c64d14086196eb96f43c78caa9db3 (patch) | |
tree | 0cfd5fb21262a6db3de0c64462847b4c0c43e9df /drivers | |
parent | 2c757fd5d1a92086f225a75a8fac7cab242d11b0 (diff) | |
parent | 3c0dec5f58b3c7b3627715126d1bf9b030a076f0 (diff) | |
download | linux-27953437059c64d14086196eb96f43c78caa9db3.tar.gz linux-27953437059c64d14086196eb96f43c78caa9db3.tar.bz2 linux-27953437059c64d14086196eb96f43c78caa9db3.zip |
Merge tag 'clock' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull arm-soc clock driver changes from Olof Johansson:
"The new clock subsystem was merged in linux-3.4 without any users,
this now moves the first three platforms over to it: imx, mxs and
spear.
The series also contains the changes for the clock subsystem itself,
since Mike preferred to have it together with the platforms that
require these changes, in order to avoid interdependencies and
conflicts."
Fix up trivial conflicts in arch/arm/mach-kirkwood/common.c (code
removed in one branch, added OF support in another) and
drivers/dma/imx-sdma.c (independent changes next to each other).
* tag 'clock' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (97 commits)
clk: Fix CLK_SET_RATE_GATE flag validation in clk_set_rate().
clk: Provide dummy clk_unregister()
SPEAr: Update defconfigs
SPEAr: Add SMI NOR partition info in dts files
SPEAr: Switch to common clock framework
SPEAr: Call clk_prepare() before calling clk_enable
SPEAr: clk: Add General Purpose Timer Synthesizer clock
SPEAr: clk: Add Fractional Synthesizer clock
SPEAr: clk: Add Auxiliary Synthesizer clock
SPEAr: clk: Add VCO-PLL Synthesizer clock
SPEAr: Add DT bindings for SPEAr's timer
ARM i.MX: remove now unused clock files
ARM: i.MX6: implement clocks using common clock framework
ARM i.MX35: implement clocks using common clock framework
ARM i.MX5: implement clocks using common clock framework
ARM: Kirkwood: Replace clock gating
ARM: Orion: Audio: Add clk/clkdev support
ARM: Orion: PCIE: Add support for clk
ARM: Orion: XOR: Add support for clk
ARM: Orion: CESA: Add support for clk
...
Diffstat (limited to 'drivers')
50 files changed, 3988 insertions, 395 deletions
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 7336d4a7ab31..24712adf69df 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -553,6 +553,7 @@ struct mv_host_priv { #if defined(CONFIG_HAVE_CLK) struct clk *clk; + struct clk **port_clks; #endif /* * These consistent DMA memory pools give us guaranteed @@ -4027,6 +4028,9 @@ static int mv_platform_probe(struct platform_device *pdev) struct resource *res; int n_ports = 0; int rc; +#if defined(CONFIG_HAVE_CLK) + int port; +#endif ata_print_version_once(&pdev->dev, DRV_VERSION); @@ -4054,6 +4058,13 @@ static int mv_platform_probe(struct platform_device *pdev) if (!host || !hpriv) return -ENOMEM; +#if defined(CONFIG_HAVE_CLK) + hpriv->port_clks = devm_kzalloc(&pdev->dev, + sizeof(struct clk *) * n_ports, + GFP_KERNEL); + if (!hpriv->port_clks) + return -ENOMEM; +#endif host->private_data = hpriv; hpriv->n_ports = n_ports; hpriv->board_idx = chip_soc; @@ -4066,9 +4077,17 @@ static int mv_platform_probe(struct platform_device *pdev) #if defined(CONFIG_HAVE_CLK) hpriv->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(hpriv->clk)) - dev_notice(&pdev->dev, "cannot get clkdev\n"); + dev_notice(&pdev->dev, "cannot get optional clkdev\n"); else - clk_enable(hpriv->clk); + clk_prepare_enable(hpriv->clk); + + for (port = 0; port < n_ports; port++) { + char port_number[16]; + sprintf(port_number, "%d", port); + hpriv->port_clks[port] = clk_get(&pdev->dev, port_number); + if (!IS_ERR(hpriv->port_clks[port])) + clk_prepare_enable(hpriv->port_clks[port]); + } #endif /* @@ -4098,9 +4117,15 @@ static int mv_platform_probe(struct platform_device *pdev) err: #if defined(CONFIG_HAVE_CLK) if (!IS_ERR(hpriv->clk)) { - clk_disable(hpriv->clk); + clk_disable_unprepare(hpriv->clk); clk_put(hpriv->clk); } + for (port = 0; port < n_ports; port++) { + if (!IS_ERR(hpriv->port_clks[port])) { + clk_disable_unprepare(hpriv->port_clks[port]); + clk_put(hpriv->port_clks[port]); + } + } #endif return rc; @@ -4119,14 +4144,21 @@ static int __devexit mv_platform_remove(struct platform_device *pdev) struct ata_host *host = platform_get_drvdata(pdev); #if defined(CONFIG_HAVE_CLK) struct mv_host_priv *hpriv = host->private_data; + int port; #endif ata_host_detach(host); #if defined(CONFIG_HAVE_CLK) if (!IS_ERR(hpriv->clk)) { - clk_disable(hpriv->clk); + clk_disable_unprepare(hpriv->clk); clk_put(hpriv->clk); } + for (port = 0; port < host->n_ports; port++) { + if (!IS_ERR(hpriv->port_clks[port])) { + clk_disable_unprepare(hpriv->port_clks[port]); + clk_put(hpriv->port_clks[port]); + } + } #endif return 0; } diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 165e1febae53..4864407e3fc4 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -12,6 +12,7 @@ config HAVE_MACH_CLKDEV config COMMON_CLK bool select HAVE_CLK_PREPARE + select CLKDEV_LOOKUP ---help--- The common clock framework is a single definition of struct clk, useful across many platforms, as well as an @@ -22,17 +23,6 @@ config COMMON_CLK menu "Common Clock Framework" depends on COMMON_CLK -config COMMON_CLK_DISABLE_UNUSED - bool "Disabled unused clocks at boot" - depends on COMMON_CLK - ---help--- - Traverses the entire clock tree and disables any clocks that are - enabled in hardware but have not been enabled by any device drivers. - This saves power and keeps the software model of the clock in line - with reality. - - If in doubt, say "N". - config COMMON_CLK_DEBUG bool "DebugFS representation of clock tree" depends on COMMON_CLK diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 1f736bc11c4b..b9a5158a30b1 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -1,4 +1,7 @@ obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \ - clk-mux.o clk-divider.o + clk-mux.o clk-divider.o clk-fixed-factor.o +# SoCs specific +obj-$(CONFIG_ARCH_MXS) += mxs/ +obj-$(CONFIG_PLAT_SPEAR) += spear/ diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index d5ac6a75ea57..8ea11b444528 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -45,7 +45,6 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, return parent_rate / div; } -EXPORT_SYMBOL_GPL(clk_divider_recalc_rate); /* * The reverse of DIV_ROUND_UP: The maximum number which @@ -68,8 +67,8 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, if (divider->flags & CLK_DIVIDER_ONE_BASED) maxdiv--; - if (!best_parent_rate) { - parent_rate = __clk_get_rate(__clk_get_parent(hw->clk)); + if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { + parent_rate = *best_parent_rate; bestdiv = DIV_ROUND_UP(parent_rate, rate); bestdiv = bestdiv == 0 ? 1 : bestdiv; bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; @@ -109,24 +108,18 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, int div; div = clk_divider_bestdiv(hw, rate, prate); - if (prate) - return *prate / div; - else { - unsigned long r; - r = __clk_get_rate(__clk_get_parent(hw->clk)); - return r / div; - } + return *prate / div; } -EXPORT_SYMBOL_GPL(clk_divider_round_rate); -static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate) +static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) { struct clk_divider *divider = to_clk_divider(hw); unsigned int div; unsigned long flags = 0; u32 val; - div = __clk_get_rate(__clk_get_parent(hw->clk)) / rate; + div = parent_rate / rate; if (!(divider->flags & CLK_DIVIDER_ONE_BASED)) div--; @@ -147,15 +140,26 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate) return 0; } -EXPORT_SYMBOL_GPL(clk_divider_set_rate); -struct clk_ops clk_divider_ops = { +const struct clk_ops clk_divider_ops = { .recalc_rate = clk_divider_recalc_rate, .round_rate = clk_divider_round_rate, .set_rate = clk_divider_set_rate, }; EXPORT_SYMBOL_GPL(clk_divider_ops); +/** + * clk_register_divider - register a divider clock with the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust divider + * @shift: number of bits to shift the bitfield + * @width: width of the bitfield + * @clk_divider_flags: divider-specific flags for this clock + * @lock: shared register lock for this clock + */ struct clk *clk_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, @@ -163,38 +167,34 @@ struct clk *clk_register_divider(struct device *dev, const char *name, { struct clk_divider *div; struct clk *clk; + struct clk_init_data init; + /* allocate the divider */ div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL); - if (!div) { pr_err("%s: could not allocate divider clk\n", __func__); - return NULL; + return ERR_PTR(-ENOMEM); } + init.name = name; + init.ops = &clk_divider_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name: NULL); + init.num_parents = (parent_name ? 1 : 0); + /* struct clk_divider assignments */ div->reg = reg; div->shift = shift; div->width = width; div->flags = clk_divider_flags; div->lock = lock; + div->hw.init = &init; - if (parent_name) { - div->parent[0] = kstrdup(parent_name, GFP_KERNEL); - if (!div->parent[0]) - goto out; - } - - clk = clk_register(dev, name, - &clk_divider_ops, &div->hw, - div->parent, - (parent_name ? 1 : 0), - flags); - if (clk) - return clk; + /* register the clock */ + clk = clk_register(dev, &div->hw); -out: - kfree(div->parent[0]); - kfree(div); + if (IS_ERR(clk)) + kfree(div); - return NULL; + return clk; } diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c new file mode 100644 index 000000000000..c8c003e217ad --- /dev/null +++ b/drivers/clk/clk-fixed-factor.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Standard functionality for the common clock API. + */ +#include <linux/module.h> +#include <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/err.h> + +/* + * DOC: basic fixed multiplier and divider clock that cannot gate + * + * Traits of this clock: + * prepare - clk_prepare only ensures that parents are prepared + * enable - clk_enable only ensures that parents are enabled + * rate - rate is fixed. clk->rate = parent->rate / div * mult + * parent - fixed parent. No clk_set_parent support + */ + +#define to_clk_fixed_factor(_hw) container_of(_hw, struct clk_fixed_factor, hw) + +static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_fixed_factor *fix = to_clk_fixed_factor(hw); + + return parent_rate * fix->mult / fix->div; +} + +static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_fixed_factor *fix = to_clk_fixed_factor(hw); + + if (__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT) { + unsigned long best_parent; + + best_parent = (rate / fix->mult) * fix->div; + *prate = __clk_round_rate(__clk_get_parent(hw->clk), + best_parent); + } + + return (*prate / fix->div) * fix->mult; +} + +static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + return 0; +} + +struct clk_ops clk_fixed_factor_ops = { + .round_rate = clk_factor_round_rate, + .set_rate = clk_factor_set_rate, + .recalc_rate = clk_factor_recalc_rate, +}; +EXPORT_SYMBOL_GPL(clk_fixed_factor_ops); + +struct clk *clk_register_fixed_factor(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + unsigned int mult, unsigned int div) +{ + struct clk_fixed_factor *fix; + struct clk_init_data init; + struct clk *clk; + + fix = kmalloc(sizeof(*fix), GFP_KERNEL); + if (!fix) { + pr_err("%s: could not allocate fixed factor clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + /* struct clk_fixed_factor assignments */ + fix->mult = mult; + fix->div = div; + fix->hw.init = &init; + + init.name = name; + init.ops = &clk_fixed_factor_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(dev, &fix->hw); + + if (IS_ERR(clk)) + kfree(fix); + + return clk; +} diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index 90c79fb5d1bd..cbd246229786 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -32,51 +32,50 @@ static unsigned long clk_fixed_rate_recalc_rate(struct clk_hw *hw, { return to_clk_fixed_rate(hw)->fixed_rate; } -EXPORT_SYMBOL_GPL(clk_fixed_rate_recalc_rate); -struct clk_ops clk_fixed_rate_ops = { +const struct clk_ops clk_fixed_rate_ops = { .recalc_rate = clk_fixed_rate_recalc_rate, }; EXPORT_SYMBOL_GPL(clk_fixed_rate_ops); +/** + * clk_register_fixed_rate - register fixed-rate clock with the clock framework + * @dev: device that is registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @fixed_rate: non-adjustable clock rate + */ struct clk *clk_register_fixed_rate(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned long fixed_rate) { struct clk_fixed_rate *fixed; - char **parent_names = NULL; - u8 len; + struct clk *clk; + struct clk_init_data init; + /* allocate fixed-rate clock */ fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL); - if (!fixed) { pr_err("%s: could not allocate fixed clk\n", __func__); return ERR_PTR(-ENOMEM); } + init.name = name; + init.ops = &clk_fixed_rate_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name: NULL); + init.num_parents = (parent_name ? 1 : 0); + /* struct clk_fixed_rate assignments */ fixed->fixed_rate = fixed_rate; + fixed->hw.init = &init; - if (parent_name) { - parent_names = kmalloc(sizeof(char *), GFP_KERNEL); - - if (! parent_names) - goto out; + /* register the clock */ + clk = clk_register(dev, &fixed->hw); - len = sizeof(char) * strlen(parent_name); - - parent_names[0] = kmalloc(len, GFP_KERNEL); - - if (!parent_names[0]) - goto out; - - strncpy(parent_names[0], parent_name, len); - } + if (IS_ERR(clk)) + kfree(fixed); -out: - return clk_register(dev, name, - &clk_fixed_rate_ops, &fixed->hw, - parent_names, - (parent_name ? 1 : 0), - flags); + return clk; } diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index b5902e2ef2fd..578465e04be6 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -28,32 +28,38 @@ #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) -static void clk_gate_set_bit(struct clk_gate *gate) +/* + * It works on following logic: + * + * For enabling clock, enable = 1 + * set2dis = 1 -> clear bit -> set = 0 + * set2dis = 0 -> set bit -> set = 1 + * + * For disabling clock, enable = 0 + * set2dis = 1 -> set bit -> set = 1 + * set2dis = 0 -> clear bit -> set = 0 + * + * So, result is always: enable xor set2dis. + */ +static void clk_gate_endisable(struct clk_hw *hw, int enable) { - u32 reg; + struct clk_gate *gate = to_clk_gate(hw); + int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; unsigned long flags = 0; + u32 reg; + + set ^= enable; if (gate->lock) spin_lock_irqsave(gate->lock, flags); reg = readl(gate->reg); - reg |= BIT(gate->bit_idx); - writel(reg, gate->reg); - - if (gate->lock) - spin_unlock_irqrestore(gate->lock, flags); -} - -static void clk_gate_clear_bit(struct clk_gate *gate) -{ - u32 reg; - unsigned long flags = 0; - if (gate->lock) - spin_lock_irqsave(gate->lock, flags); + if (set) + reg |= BIT(gate->bit_idx); + else + reg &= ~BIT(gate->bit_idx); - reg = readl(gate->reg); - reg &= ~BIT(gate->bit_idx); writel(reg, gate->reg); if (gate->lock) @@ -62,27 +68,15 @@ static void clk_gate_clear_bit(struct clk_gate *gate) static int clk_gate_enable(struct clk_hw *hw) { - struct clk_gate *gate = to_clk_gate(hw); - - if (gate->flags & CLK_GATE_SET_TO_DISABLE) - clk_gate_clear_bit(gate); - else - clk_gate_set_bit(gate); + clk_gate_endisable(hw, 1); return 0; } -EXPORT_SYMBOL_GPL(clk_gate_enable); static void clk_gate_disable(struct clk_hw *hw) { - struct clk_gate *gate = to_clk_gate(hw); - - if (gate->flags & CLK_GATE_SET_TO_DISABLE) - clk_gate_set_bit(gate); - else - clk_gate_clear_bit(gate); + clk_gate_endisable(hw, 0); } -EXPORT_SYMBOL_GPL(clk_gate_disable); static int clk_gate_is_enabled(struct clk_hw *hw) { @@ -99,15 +93,25 @@ static int clk_gate_is_enabled(struct clk_hw *hw) return reg ? 1 : 0; } -EXPORT_SYMBOL_GPL(clk_gate_is_enabled); -struct clk_ops clk_gate_ops = { +const struct clk_ops clk_gate_ops = { .enable = clk_gate_enable, .disable = clk_gate_disable, .is_enabled = clk_gate_is_enabled, }; EXPORT_SYMBOL_GPL(clk_gate_ops); +/** + * clk_register_gate - register a gate clock with the clock framework + * @dev: device that is registering this clock + * @name: name of this clock + * @parent_name: name of this clock's parent + * @flags: framework-specific flags for this clock + * @reg: register address to control gating of this clock + * @bit_idx: which bit in the register controls gating of this clock + * @clk_gate_flags: gate-specific flags for this clock + * @lock: shared register lock for this clock + */ struct clk *clk_register_gate(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, @@ -115,36 +119,32 @@ struct clk *clk_register_gate(struct device *dev, const char *name, { struct clk_gate *gate; struct clk *clk; + struct clk_init_data init; + /* allocate the gate */ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); - if (!gate) { pr_err("%s: could not allocate gated clk\n", __func__); - return NULL; + return ERR_PTR(-ENOMEM); } + init.name = name; + init.ops = &clk_gate_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name: NULL); + init.num_parents = (parent_name ? 1 : 0); + /* struct clk_gate assignments */ gate->reg = reg; gate->bit_idx = bit_idx; gate->flags = clk_gate_flags; gate->lock = lock; + gate->hw.init = &init; - if (parent_name) { - gate->parent[0] = kstrdup(parent_name, GFP_KERNEL); - if (!gate->parent[0]) - goto out; - } + clk = clk_register(dev, &gate->hw); + + if (IS_ERR(clk)) + kfree(gate); - clk = clk_register(dev, name, - &clk_gate_ops, &gate->hw, - gate->parent, - (parent_name ? 1 : 0), - flags); - if (clk) - return clk; -out: - kfree(gate->parent[0]); - kfree(gate); - - return NULL; + return clk; } diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index c71ad1f41a97..fd36a8ea73d9 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -55,7 +55,6 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) return val; } -EXPORT_SYMBOL_GPL(clk_mux_get_parent); static int clk_mux_set_parent(struct clk_hw *hw, u8 index) { @@ -82,35 +81,47 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) return 0; } -EXPORT_SYMBOL_GPL(clk_mux_set_parent); -struct clk_ops clk_mux_ops = { +const struct clk_ops clk_mux_ops = { .get_parent = clk_mux_get_parent, .set_parent = clk_mux_set_parent, }; EXPORT_SYMBOL_GPL(clk_mux_ops); struct clk *clk_register_mux(struct device *dev, const char *name, - char **parent_names, u8 num_parents, unsigned long flags, + const char **parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_mux_flags, spinlock_t *lock) { struct clk_mux *mux; + struct clk *clk; + struct clk_init_data init; - mux = kmalloc(sizeof(struct clk_mux), GFP_KERNEL); - + /* allocate the mux */ + mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); if (!mux) { pr_err("%s: could not allocate mux clk\n", __func__); return ERR_PTR(-ENOMEM); } + init.name = name; + init.ops = &clk_mux_ops; + init.flags = flags; + init.parent_names = parent_names; + init.num_parents = num_parents; + /* struct clk_mux assignments */ mux->reg = reg; mux->shift = shift; mux->width = width; mux->flags = clk_mux_flags; mux->lock = lock; + mux->hw.init = &init; + + clk = clk_register(dev, &mux->hw); + + if (IS_ERR(clk)) + kfree(mux); - return clk_register(dev, name, &clk_mux_ops, &mux->hw, - parent_names, num_parents, flags); + return clk; } diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 9cf6f59e3e19..687b00d67c8a 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -194,9 +194,8 @@ static int __init clk_debug_init(void) late_initcall(clk_debug_init); #else static inline int clk_debug_register(struct clk *clk) { return 0; } -#endif /* CONFIG_COMMON_CLK_DEBUG */ +#endif -#ifdef CONFIG_COMMON_CLK_DISABLE_UNUSED /* caller must hold prepare_lock */ static void clk_disable_unused_subtree(struct clk *clk) { @@ -246,9 +245,6 @@ static int clk_disable_unused(void) return 0; } late_initcall(clk_disable_unused); -#else -static inline int clk_disable_unused(struct clk *clk) { return 0; } -#endif /* CONFIG_COMMON_CLK_DISABLE_UNUSED */ /*** helper functions ***/ @@ -287,7 +283,7 @@ unsigned long __clk_get_rate(struct clk *clk) unsigned long ret; if (!clk) { - ret = -EINVAL; + ret = 0; goto out; } @@ -297,7 +293,7 @@ unsigned long __clk_get_rate(struct clk *clk) goto out; if (!clk->parent) - ret = -ENODEV; + ret = 0; out: return ret; @@ -562,7 +558,7 @@ EXPORT_SYMBOL_GPL(clk_enable); * @clk: the clk whose rate is being returned * * Simply returns the cached rate of the clk. Does not query the hardware. If - * clk is NULL then returns -EINVAL. + * clk is NULL then returns 0. */ unsigned long clk_get_rate(struct clk *clk) { @@ -584,18 +580,22 @@ EXPORT_SYMBOL_GPL(clk_get_rate); */ unsigned long __clk_round_rate(struct clk *clk, unsigned long rate) { - unsigned long unused; + unsigned long parent_rate = 0; if (!clk) return -EINVAL; - if (!clk->ops->round_rate) - return clk->rate; + if (!clk->ops->round_rate) { + if (clk->flags & CLK_SET_RATE_PARENT) + return __clk_round_rate(clk->parent, rate); + else + return clk->rate; + } - if (clk->flags & CLK_SET_RATE_PARENT) - return clk->ops->round_rate(clk->hw, rate, &unused); - else - return clk->ops->round_rate(clk->hw, rate, NULL); + if (clk->parent) + parent_rate = clk->parent->rate; + + return clk->ops->round_rate(clk->hw, rate, &parent_rate); } /** @@ -765,25 +765,41 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate) static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) { struct clk *top = clk; - unsigned long best_parent_rate = clk->parent->rate; + unsigned long best_parent_rate = 0; unsigned long new_rate; - if (!clk->ops->round_rate && !(clk->flags & CLK_SET_RATE_PARENT)) { - clk->new_rate = clk->rate; + /* sanity */ + if (IS_ERR_OR_NULL(clk)) + return NULL; + + /* save parent rate, if it exists */ + if (clk->parent) + best_parent_rate = clk->parent->rate; + + /* never propagate up to the parent */ + if (!(clk->flags & CLK_SET_RATE_PARENT)) { + if (!clk->ops->round_rate) { + clk->new_rate = clk->rate; + return NULL; + } + new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); + goto out; + } + + /* need clk->parent from here on out */ + if (!clk->parent) { + pr_debug("%s: %s has NULL parent\n", __func__, clk->name); return NULL; } - if (!clk->ops->round_rate && (clk->flags & CLK_SET_RATE_PARENT)) { + if (!clk->ops->round_rate) { top = clk_calc_new_rates(clk->parent, rate); - new_rate = clk->new_rate = clk->parent->new_rate; + new_rate = clk->parent->new_rate; goto out; } - if (clk->flags & CLK_SET_RATE_PARENT) - new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); - else - new_rate = clk->ops->round_rate(clk->hw, rate, NULL); + new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); if (best_parent_rate != clk->parent->rate) { top = clk_calc_new_rates(clk->parent, best_parent_rate); @@ -839,7 +855,7 @@ static void clk_change_rate(struct clk *clk) old_rate = clk->rate; if (clk->ops->set_rate) - clk->ops->set_rate(clk->hw, clk->new_rate); + clk->ops->set_rate(clk->hw, clk->new_rate, clk->parent->rate); if (clk->ops->recalc_rate) clk->rate = clk->ops->recalc_rate(clk->hw, @@ -859,38 +875,19 @@ static void clk_change_rate(struct clk *clk) * @clk: the clk whose rate is being changed * @rate: the new rate for clk * - * In the simplest case clk_set_rate will only change the rate of clk. - * - * If clk has the CLK_SET_RATE_GATE flag set and it is enabled this call - * will fail; only when the clk is disabled will it be able to change - * its rate. + * In the simplest case clk_set_rate will only adjust the rate of clk. * - * Setting the CLK_SET_RATE_PARENT flag allows clk_set_rate to - * recursively propagate up to clk's parent; whether or not this happens - * depends on the outcome of clk's .round_rate implementation. If - * *parent_rate is 0 after calling .round_rate then upstream parent - * propagation is ignored. If *parent_rate comes back with a new rate - * for clk's parent then we propagate up to clk's parent and set it's - * rate. Upward propagation will continue until either a clk does not - * support the CLK_SET_RATE_PARENT flag or .round_rate stops requesting - * changes to clk's parent_rate. If there is a failure during upstream - * propagation then clk_set_rate will unwind and restore each clk's rate - * that had been successfully changed. Afterwards a rate change abort - * notification will be propagated downstream, starting from the clk - * that failed. + * Setting the CLK_SET_RATE_PARENT flag allows the rate change operation to + * propagate up to clk's parent; whether or not this happens depends on the + * outcome of clk's .round_rate implementation. If *parent_rate is unchanged + * after calling .round_rate then upstream parent propagation is ignored. If + * *parent_rate comes back with a new rate for clk's parent then we propagate + * up to clk's parent and set it's rate. Upward propagation will continue + * until either a clk does not support the CLK_SET_RATE_PARENT flag or + * .round_rate stops requesting changes to clk's parent_rate. * - * At the end of all of the rate setting, clk_set_rate internally calls - * __clk_recalc_rates and propagates the rate changes downstream, - * starting from the highest clk whose rate was changed. This has the - * added benefit of propagating post-rate change notifiers. - * - * Note that while post-rate change and rate change abort notifications - * are guaranteed to be sent to a clk only once per call to - * clk_set_rate, pre-change notifications will be sent for every clk - * whose rate is changed. Stacking pre-change notifications is noisy - * for the drivers subscribed to them, but this allows drivers to react - * to intermediate clk rate changes up until the point where the final - * rate is achieved at the end of upstream propagation. + * Rate changes are accomplished via tree traversal that also recalculates the + * rates for the clocks and fires off POST_RATE_CHANGE notifiers. * * Returns 0 on success, -EERROR otherwise. */ @@ -906,6 +903,11 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (rate == clk->rate) goto out; + if ((clk->flags & CLK_SET_RATE_GATE) && clk->prepare_count) { + ret = -EBUSY; + goto out; + } + /* calculate new rates and get the topmost changed clock */ top = clk_calc_new_rates(clk, rate); if (!top) { @@ -1175,40 +1177,41 @@ EXPORT_SYMBOL_GPL(clk_set_parent); * * Initializes the lists in struct clk, queries the hardware for the * parent and rate and sets them both. - * - * Any struct clk passed into __clk_init must have the following members - * populated: - * .name - * .ops - * .hw - * .parent_names - * .num_parents - * .flags - * - * Essentially, everything that would normally be passed into clk_register is - * assumed to be initialized already in __clk_init. The other members may be - * populated, but are optional. - * - * __clk_init is only exposed via clk-private.h and is intended for use with - * very large numbers of clocks that need to be statically initialized. It is - * a layering violation to include clk-private.h from any code which implements - * a clock's .ops; as such any statically initialized clock data MUST be in a - * separate C file from the logic that implements it's operations. */ -void __clk_init(struct device *dev, struct clk *clk) +int __clk_init(struct device *dev, struct clk *clk) { - int i; + int i, ret = 0; struct clk *orphan; struct hlist_node *tmp, *tmp2; if (!clk) - return; + return -EINVAL; mutex_lock(&prepare_lock); /* check to see if a clock with this name is already registered */ - if (__clk_lookup(clk->name)) + if (__clk_lookup(clk->name)) { + pr_debug("%s: clk %s already initialized\n", + __func__, clk->name); + ret = -EEXIST; + goto out; + } + + /* check that clk_ops are sane. See Documentation/clk.txt */ + if (clk->ops->set_rate && + !(clk->ops->round_rate && clk->ops->recalc_rate)) { + pr_warning("%s: %s must implement .round_rate & .recalc_rate\n", + __func__, clk->name); + ret = -EINVAL; + goto out; + } + + if (clk->ops->set_parent && !clk->ops->get_parent) { + pr_warning("%s: %s must implement .get_parent & .set_parent\n", + __func__, clk->name); + ret = -EINVAL; goto out; + } /* throw a WARN if any entries in parent_names are NULL */ for (i = 0; i < clk->num_parents; i++) @@ -1302,48 +1305,130 @@ void __clk_init(struct device *dev, struct clk *clk) out: mutex_unlock(&prepare_lock); - return; + return ret; } /** + * __clk_register - register a clock and return a cookie. + * + * Same as clk_register, except that the .clk field inside hw shall point to a + * preallocated (generally statically allocated) struct clk. None of the fields + * of the struct clk need to be initialized. + * + * The data pointed to by .init and .clk field shall NOT be marked as init + * data. + * + * __clk_register is only exposed via clk-private.h and is intended for use with + * very large numbers of clocks that need to be statically initialized. It is + * a layering violation to include clk-private.h from any code which implements + * a clock's .ops; as such any statically initialized clock data MUST be in a + * separate C file from the logic that implements it's operations. Returns 0 + * on success, otherwise an error code. + */ +struct clk *__clk_register(struct device *dev, struct clk_hw *hw) +{ + int ret; + struct clk *clk; + + clk = hw->clk; + clk->name = hw->init->name; + clk->ops = hw->init->ops; + clk->hw = hw; + clk->flags = hw->init->flags; + clk->parent_names = hw->init->parent_names; + clk->num_parents = hw->init->num_parents; + + ret = __clk_init(dev, clk); + if (ret) + return ERR_PTR(ret); + + return clk; +} +EXPORT_SYMBOL_GPL(__clk_register); + +/** * clk_register - allocate a new clock, register it and return an opaque cookie * @dev: device that is registering this clock - * @name: clock name - * @ops: operations this clock supports * @hw: link to hardware-specific clock data - * @parent_names: array of string names for all possible parents - * @num_parents: number of possible parents - * @flags: framework-level hints and quirks * * clk_register is the primary interface for populating the clock tree with new * clock nodes. It returns a pointer to the newly allocated struct clk which * cannot be dereferenced by driver code but may be used in conjuction with the - * rest of the clock API. + * rest of the clock API. In the event of an error clk_register will return an + * error code; drivers must test for an error code after calling clk_register. */ -struct clk *clk_register(struct device *dev, const char *name, - const struct clk_ops *ops, struct clk_hw *hw, - char **parent_names, u8 num_parents, unsigned long flags) +struct clk *clk_register(struct device *dev, struct clk_hw *hw) { + int i, ret; struct clk *clk; clk = kzalloc(sizeof(*clk), GFP_KERNEL); - if (!clk) - return NULL; + if (!clk) { + pr_err("%s: could not allocate clk\n", __func__); + ret = -ENOMEM; + goto fail_out; + } - clk->name = name; - clk->ops = ops; + clk->name = kstrdup(hw->init->name, GFP_KERNEL); + if (!clk->name) { + pr_err("%s: could not allocate clk->name\n", __func__); + ret = -ENOMEM; + goto fail_name; + } + clk->ops = hw->init->ops; clk->hw = hw; - clk->flags = flags; - clk->parent_names = parent_names; - clk->num_parents = num_parents; + clk->flags = hw->init->flags; + clk->num_parents = hw->init->num_parents; hw->clk = clk; - __clk_init(dev, clk); + /* allocate local copy in case parent_names is __initdata */ + clk->parent_names = kzalloc((sizeof(char*) * clk->num_parents), + GFP_KERNEL); - return clk; + if (!clk->parent_names) { + pr_err("%s: could not allocate clk->parent_names\n", __func__); + ret = -ENOMEM; + goto fail_parent_names; + } + + + /* copy each string name in case parent_names is __initdata */ + for (i = 0; i < clk->num_parents; i++) { + clk->parent_names[i] = kstrdup(hw->init->parent_names[i], + GFP_KERNEL); + if (!clk->parent_names[i]) { + pr_err("%s: could not copy parent_names\n", __func__); + ret = -ENOMEM; + goto fail_parent_names_copy; + } + } + + ret = __clk_init(dev, clk); + if (!ret) + return clk; + +fail_parent_names_copy: + while (--i >= 0) + kfree(clk->parent_names[i]); + kfree(clk->parent_names); +fail_parent_names: + kfree(clk->name); +fail_name: + kfree(clk); +fail_out: + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(clk_register); +/** + * clk_unregister - unregister a currently registered clock + * @clk: clock to unregister + * + * Currently unimplemented. + */ +void clk_unregister(struct clk *clk) {} +EXPORT_SYMBOL_GPL(clk_unregister); + /*** clk rate change notifiers ***/ /** diff --git a/drivers/clk/mxs/Makefile b/drivers/clk/mxs/Makefile new file mode 100644 index 000000000000..7bedeec08524 --- /dev/null +++ b/drivers/clk/mxs/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for mxs specific clk +# + +obj-y += clk.o clk-pll.o clk-ref.o clk-div.o clk-frac.o + +obj-$(CONFIG_SOC_IMX23) += clk-imx23.o +obj-$(CONFIG_SOC_IMX28) += clk-imx28.o diff --git a/drivers/clk/mxs/clk-div.c b/drivers/clk/mxs/clk-div.c new file mode 100644 index 000000000000..90e1da93877e --- /dev/null +++ b/drivers/clk/mxs/clk-div.c @@ -0,0 +1,110 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/slab.h> +#include "clk.h" + +/** + * struct clk_div - mxs integer divider clock + * @divider: the parent class + * @ops: pointer to clk_ops of parent class + * @reg: register address + * @busy: busy bit shift + * + * The mxs divider clock is a subclass of basic clk_divider with an + * addtional busy bit. + */ +struct clk_div { + struct clk_divider divider; + const struct clk_ops *ops; + void __iomem *reg; + u8 busy; +}; + +static inline struct clk_div *to_clk_div(struct clk_hw *hw) +{ + struct clk_divider *divider = container_of(hw, struct clk_divider, hw); + + return container_of(divider, struct clk_div, divider); +} + +static unsigned long clk_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_div *div = to_clk_div(hw); + + return div->ops->recalc_rate(&div->divider.hw, parent_rate); +} + +static long clk_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_div *div = to_clk_div(hw); + + return div->ops->round_rate(&div->divider.hw, rate, prate); +} + +static int clk_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_div *div = to_clk_div(hw); + int ret; + + ret = div->ops->set_rate(&div->divider.hw, rate, parent_rate); + if (!ret) + ret = mxs_clk_wait(div->reg, div->busy); + + return ret; +} + +static struct clk_ops clk_div_ops = { + .recalc_rate = clk_div_recalc_rate, + .round_rate = clk_div_round_rate, + .set_rate = clk_div_set_rate, +}; + +struct clk *mxs_clk_div(const char *name, const char *parent_name, + void __iomem *reg, u8 shift, u8 width, u8 busy) +{ + struct clk_div *div; + struct clk *clk; + struct clk_init_data init; + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_div_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = (parent_name ? &parent_name: NULL); + init.num_parents = (parent_name ? 1 : 0); + + div->reg = reg; + div->busy = busy; + + div->divider.reg = reg; + div->divider.shift = shift; + div->divider.width = width; + div->divider.flags = CLK_DIVIDER_ONE_BASED; + div->divider.lock = &mxs_lock; + div->divider.hw.init = &init; + div->ops = &clk_divider_ops; + + clk = clk_register(NULL, &div->divider.hw); + if (IS_ERR(clk)) + kfree(div); + + return clk; +} diff --git a/drivers/clk/mxs/clk-frac.c b/drivers/clk/mxs/clk-frac.c new file mode 100644 index 000000000000..e6aa6b567d68 --- /dev/null +++ b/drivers/clk/mxs/clk-frac.c @@ -0,0 +1,139 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include "clk.h" + +/** + * struct clk_frac - mxs fractional divider clock + * @hw: clk_hw for the fractional divider clock + * @reg: register address + * @shift: the divider bit shift + * @width: the divider bit width + * @busy: busy bit shift + * + * The clock is an adjustable fractional divider with a busy bit to wait + * when the divider is adjusted. + */ +struct clk_frac { + struct clk_hw hw; + void __iomem *reg; + u8 shift; + u8 width; + u8 busy; +}; + +#define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw) + +static unsigned long clk_frac_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_frac *frac = to_clk_frac(hw); + u32 div; + + div = readl_relaxed(frac->reg) >> frac->shift; + div &= (1 << frac->width) - 1; + + return (parent_rate >> frac->width) * div; +} + +static long clk_frac_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_frac *frac = to_clk_frac(hw); + unsigned long parent_rate = *prate; + u32 div; + u64 tmp; + + if (rate > parent_rate) + return -EINVAL; + + tmp = rate; + tmp <<= frac->width; + do_div(tmp, parent_rate); + div = tmp; + + if (!div) + return -EINVAL; + + return (parent_rate >> frac->width) * div; +} + +static int clk_frac_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_frac *frac = to_clk_frac(hw); + unsigned long flags; + u32 div, val; + u64 tmp; + + if (rate > parent_rate) + return -EINVAL; + + tmp = rate; + tmp <<= frac->width; + do_div(tmp, parent_rate); + div = tmp; + + if (!div) + return -EINVAL; + + spin_lock_irqsave(&mxs_lock, flags); + + val = readl_relaxed(frac->reg); + val &= ~(((1 << frac->width) - 1) << frac->shift); + val |= div << frac->shift; + writel_relaxed(val, frac->reg); + + spin_unlock_irqrestore(&mxs_lock, flags); + + return mxs_clk_wait(frac->reg, frac->busy); +} + +static struct clk_ops clk_frac_ops = { + .recalc_rate = clk_frac_recalc_rate, + .round_rate = clk_frac_round_rate, + .set_rate = clk_frac_set_rate, +}; + +struct clk *mxs_clk_frac(const char *name, const char *parent_name, + void __iomem *reg, u8 shift, u8 width, u8 busy) +{ + struct clk_frac *frac; + struct clk *clk; + struct clk_init_data init; + + frac = kzalloc(sizeof(*frac), GFP_KERNEL); + if (!frac) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_frac_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = (parent_name ? &parent_name: NULL); + init.num_parents = (parent_name ? 1 : 0); + + frac->reg = reg; + frac->shift = shift; + frac->width = width; + frac->busy = busy; + frac->hw.init = &init; + + clk = clk_register(NULL, &frac->hw); + if (IS_ERR(clk)) + kfree(frac); + + return clk; +} diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c new file mode 100644 index 000000000000..dcae11285716 --- /dev/null +++ b/drivers/clk/mxs/clk-imx23.c @@ -0,0 +1,204 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <mach/common.h> +#include <mach/mx23.h> +#include "clk.h" + +#define DIGCTRL MX23_IO_ADDRESS(MX23_DIGCTL_BASE_ADDR) +#define CLKCTRL MX23_IO_ADDRESS(MX23_CLKCTRL_BASE_ADDR) +#define PLLCTRL0 (CLKCTRL + 0x0000) +#define CPU (CLKCTRL + 0x0020) +#define HBUS (CLKCTRL + 0x0030) +#define XBUS (CLKCTRL + 0x0040) +#define XTAL (CLKCTRL + 0x0050) +#define PIX (CLKCTRL + 0x0060) +#define SSP (CLKCTRL + 0x0070) +#define GPMI (CLKCTRL + 0x0080) +#define SPDIF (CLKCTRL + 0x0090) +#define EMI (CLKCTRL + 0x00a0) +#define SAIF (CLKCTRL + 0x00c0) +#define TV (CLKCTRL + 0x00d0) +#define ETM (CLKCTRL + 0x00e0) +#define FRAC (CLKCTRL + 0x00f0) +#define CLKSEQ (CLKCTRL + 0x0110) + +#define BP_CPU_INTERRUPT_WAIT 12 +#define BP_CLKSEQ_BYPASS_SAIF 0 +#define BP_CLKSEQ_BYPASS_SSP 5 +#define BP_SAIF_DIV_FRAC_EN 16 +#define BP_FRAC_IOFRAC 24 + +static void __init clk_misc_init(void) +{ + u32 val; + + /* Gate off cpu clock in WFI for power saving */ + __mxs_setl(1 << BP_CPU_INTERRUPT_WAIT, CPU); + + /* Clear BYPASS for SAIF */ + __mxs_clrl(1 << BP_CLKSEQ_BYPASS_SAIF, CLKSEQ); + + /* SAIF has to use frac div for functional operation */ + val = readl_relaxed(SAIF); + val |= 1 << BP_SAIF_DIV_FRAC_EN; + writel_relaxed(val, SAIF); + + /* + * Source ssp clock from ref_io than ref_xtal, + * as ref_xtal only provides 24 MHz as maximum. + */ + __mxs_clrl(1 << BP_CLKSEQ_BYPASS_SSP, CLKSEQ); + + /* + * 480 MHz seems too high to be ssp clock source directly, + * so set frac to get a 288 MHz ref_io. + */ + __mxs_clrl(0x3f << BP_FRAC_IOFRAC, FRAC); + __mxs_setl(30 << BP_FRAC_IOFRAC, FRAC); +} + +static struct clk_lookup uart_lookups[] __initdata = { + { .dev_id = "duart", }, + { .dev_id = "mxs-auart.0", }, + { .dev_id = "mxs-auart.1", }, + { .dev_id = "8006c000.serial", }, + { .dev_id = "8006e000.serial", }, + { .dev_id = "80070000.serial", }, +}; + +static struct clk_lookup hbus_lookups[] __initdata = { + { .dev_id = "mxs-dma-apbh", }, + { .dev_id = "80004000.dma-apbh", }, +}; + +static struct clk_lookup xbus_lookups[] __initdata = { + { .dev_id = "duart", .con_id = "apb_pclk"}, + { .dev_id = "mxs-dma-apbx", }, + { .dev_id = "80024000.dma-apbx", }, +}; + +static struct clk_lookup ssp_lookups[] __initdata = { + { .dev_id = "mxs-mmc.0", }, + { .dev_id = "mxs-mmc.1", }, + { .dev_id = "80010000.ssp", }, + { .dev_id = "80034000.ssp", }, +}; + +static struct clk_lookup lcdif_lookups[] __initdata = { + { .dev_id = "imx23-fb", }, + { .dev_id = "80030000.lcdif", }, +}; + +static struct clk_lookup gpmi_lookups[] __initdata = { + { .dev_id = "imx23-gpmi-nand", }, + { .dev_id = "8000c000.gpmi", }, +}; + +static const char *sel_pll[] __initconst = { "pll", "ref_xtal", }; +static const char *sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", }; +static const char *sel_pix[] __initconst = { "ref_pix", "ref_xtal", }; +static const char *sel_io[] __initconst = { "ref_io", "ref_xtal", }; +static const char *cpu_sels[] __initconst = { "cpu_pll", "cpu_xtal", }; +static const char *emi_sels[] __initconst = { "emi_pll", "emi_xtal", }; + +enum imx23_clk { + ref_xtal, pll, ref_cpu, ref_emi, ref_pix, ref_io, saif_sel, + lcdif_sel, gpmi_sel, ssp_sel, emi_sel, cpu, etm_sel, cpu_pll, + cpu_xtal, hbus, xbus, lcdif_div, ssp_div, gpmi_div, emi_pll, + emi_xtal, etm_div, saif_div, clk32k_div, rtc, adc, spdif_div, + clk32k, dri, pwm, filt, uart, ssp, gpmi, spdif, emi, saif, + lcdif, etm, usb, usb_pwr, + clk_max +}; + +static struct clk *clks[clk_max]; + +static enum imx23_clk clks_init_on[] __initdata = { + cpu, hbus, xbus, emi, uart, +}; + +int __init mx23_clocks_init(void) +{ + int i; + + clk_misc_init(); + + clks[ref_xtal] = mxs_clk_fixed("ref_xtal", 24000000); + clks[pll] = mxs_clk_pll("pll", "ref_xtal", PLLCTRL0, 16, 480000000); + clks[ref_cpu] = mxs_clk_ref("ref_cpu", "pll", FRAC, 0); + clks[ref_emi] = mxs_clk_ref("ref_emi", "pll", FRAC, 1); + clks[ref_pix] = mxs_clk_ref("ref_pix", "pll", FRAC, 2); + clks[ref_io] = mxs_clk_ref("ref_io", "pll", FRAC, 3); + clks[saif_sel] = mxs_clk_mux("saif_sel", CLKSEQ, 0, 1, sel_pll, ARRAY_SIZE(sel_pll)); + clks[lcdif_sel] = mxs_clk_mux("lcdif_sel", CLKSEQ, 1, 1, sel_pix, ARRAY_SIZE(sel_pix)); + clks[gpmi_sel] = mxs_clk_mux("gpmi_sel", CLKSEQ, 4, 1, sel_io, ARRAY_SIZE(sel_io)); + clks[ssp_sel] = mxs_clk_mux("ssp_sel", CLKSEQ, 5, 1, sel_io, ARRAY_SIZE(sel_io)); + clks[emi_sel] = mxs_clk_mux("emi_sel", CLKSEQ, 6, 1, emi_sels, ARRAY_SIZE(emi_sels)); + clks[cpu] = mxs_clk_mux("cpu", CLKSEQ, 7, 1, cpu_sels, ARRAY_SIZE(cpu_sels)); + clks[etm_sel] = mxs_clk_mux("etm_sel", CLKSEQ, 8, 1, sel_cpu, ARRAY_SIZE(sel_cpu)); + clks[cpu_pll] = mxs_clk_div("cpu_pll", "ref_cpu", CPU, 0, 6, 28); + clks[cpu_xtal] = mxs_clk_div("cpu_xtal", "ref_xtal", CPU, 16, 10, 29); + clks[hbus] = mxs_clk_div("hbus", "cpu", HBUS, 0, 5, 29); + clks[xbus] = mxs_clk_div("xbus", "ref_xtal", XBUS, 0, 10, 31); + clks[lcdif_div] = mxs_clk_div("lcdif_div", "lcdif_sel", PIX, 0, 12, 29); + clks[ssp_div] = mxs_clk_div("ssp_div", "ssp_sel", SSP, 0, 9, 29); + clks[gpmi_div] = mxs_clk_div("gpmi_div", "gpmi_sel", GPMI, 0, 10, 29); + clks[emi_pll] = mxs_clk_div("emi_pll", "ref_emi", EMI, 0, 6, 28); + clks[emi_xtal] = mxs_clk_div("emi_xtal", "ref_xtal", EMI, 8, 4, 29); + clks[etm_div] = mxs_clk_div("etm_div", "etm_sel", ETM, 0, 6, 29); + clks[saif_div] = mxs_clk_frac("saif_div", "saif_sel", SAIF, 0, 16, 29); + clks[clk32k_div] = mxs_clk_fixed_factor("clk32k_div", "ref_xtal", 1, 750); + clks[rtc] = mxs_clk_fixed_factor("rtc", "ref_xtal", 1, 768); + clks[adc] = mxs_clk_fixed_factor("adc", "clk32k", 1, 16); + clks[spdif_div] = mxs_clk_fixed_factor("spdif_div", "pll", 1, 4); + clks[clk32k] = mxs_clk_gate("clk32k", "clk32k_div", XTAL, 26); + clks[dri] = mxs_clk_gate("dri", "ref_xtal", XTAL, 28); + clks[pwm] = mxs_clk_gate("pwm", "ref_xtal", XTAL, 29); + clks[filt] = mxs_clk_gate("filt", "ref_xtal", XTAL, 30); + clks[uart] = mxs_clk_gate("uart", "ref_xtal", XTAL, 31); + clks[ssp] = mxs_clk_gate("ssp", "ssp_div", SSP, 31); + clks[gpmi] = mxs_clk_gate("gpmi", "gpmi_div", GPMI, 31); + clks[spdif] = mxs_clk_gate("spdif", "spdif_div", SPDIF, 31); + clks[emi] = mxs_clk_gate("emi", "emi_sel", EMI, 31); + clks[saif] = mxs_clk_gate("saif", "saif_div", SAIF, 31); + clks[lcdif] = mxs_clk_gate("lcdif", "lcdif_div", PIX, 31); + clks[etm] = mxs_clk_gate("etm", "etm_div", ETM, 31); + clks[usb] = mxs_clk_gate("usb", "usb_pwr", DIGCTRL, 2); + clks[usb_pwr] = clk_register_gate(NULL, "usb_pwr", "pll", 0, PLLCTRL0, 18, 0, &mxs_lock); + + for (i = 0; i < ARRAY_SIZE(clks); i++) + if (IS_ERR(clks[i])) { + pr_err("i.MX23 clk %d: register failed with %ld\n", + i, PTR_ERR(clks[i])); + return PTR_ERR(clks[i]); + } + + clk_register_clkdev(clks[clk32k], NULL, "timrot"); + 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)); + clk_register_clkdevs(clks[ssp], ssp_lookups, ARRAY_SIZE(ssp_lookups)); + clk_register_clkdevs(clks[gpmi], gpmi_lookups, ARRAY_SIZE(gpmi_lookups)); + clk_register_clkdevs(clks[lcdif], lcdif_lookups, ARRAY_SIZE(lcdif_lookups)); + + for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) + clk_prepare_enable(clks[clks_init_on[i]]); + + mxs_timer_init(MX23_INT_TIMER0); + + return 0; +} diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c new file mode 100644 index 000000000000..b2a3257d4f66 --- /dev/null +++ b/drivers/clk/mxs/clk-imx28.c @@ -0,0 +1,337 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <mach/common.h> +#include <mach/mx28.h> +#include "clk.h" + +#define CLKCTRL MX28_IO_ADDRESS(MX28_CLKCTRL_BASE_ADDR) +#define PLL0CTRL0 (CLKCTRL + 0x0000) +#define PLL1CTRL0 (CLKCTRL + 0x0020) +#define PLL2CTRL0 (CLKCTRL + 0x0040) +#define CPU (CLKCTRL + 0x0050) +#define HBUS (CLKCTRL + 0x0060) +#define XBUS (CLKCTRL + 0x0070) +#define XTAL (CLKCTRL + 0x0080) +#define SSP0 (CLKCTRL + 0x0090) +#define SSP1 (CLKCTRL + 0x00a0) +#define SSP2 (CLKCTRL + 0x00b0) +#define SSP3 (CLKCTRL + 0x00c0) +#define GPMI (CLKCTRL + 0x00d0) +#define SPDIF (CLKCTRL + 0x00e0) +#define EMI (CLKCTRL + 0x00f0) +#define SAIF0 (CLKCTRL + 0x0100) +#define SAIF1 (CLKCTRL + 0x0110) +#define LCDIF (CLKCTRL + 0x0120) +#define ETM (CLKCTRL + 0x0130) +#define ENET (CLKCTRL + 0x0140) +#define FLEXCAN (CLKCTRL + 0x0160) +#define FRAC0 (CLKCTRL + 0x01b0) +#define FRAC1 (CLKCTRL + 0x01c0) +#define CLKSEQ (CLKCTRL + 0x01d0) + +#define BP_CPU_INTERRUPT_WAIT 12 +#define BP_SAIF_DIV_FRAC_EN 16 +#define BP_ENET_DIV_TIME 21 +#define BP_ENET_SLEEP 31 +#define BP_CLKSEQ_BYPASS_SAIF0 0 +#define BP_CLKSEQ_BYPASS_SSP0 3 +#define BP_FRAC0_IO1FRAC 16 +#define BP_FRAC0_IO0FRAC 24 + +#define DIGCTRL MX28_IO_ADDRESS(MX28_DIGCTL_BASE_ADDR) +#define BP_SAIF_CLKMUX 10 + +/* + * HW_SAIF_CLKMUX_SEL: + * DIRECT(0x0): SAIF0 clock pins selected for SAIF0 input clocks, and SAIF1 + * clock pins selected for SAIF1 input clocks. + * CROSSINPUT(0x1): SAIF1 clock inputs selected for SAIF0 input clocks, and + * SAIF0 clock inputs selected for SAIF1 input clocks. + * EXTMSTR0(0x2): SAIF0 clock pin selected for both SAIF0 and SAIF1 input + * clocks. + * EXTMSTR1(0x3): SAIF1 clock pin selected for both SAIF0 and SAIF1 input + * clocks. + */ +int mxs_saif_clkmux_select(unsigned int clkmux) +{ + if (clkmux > 0x3) + return -EINVAL; + + __mxs_clrl(0x3 << BP_SAIF_CLKMUX, DIGCTRL); + __mxs_setl(clkmux << BP_SAIF_CLKMUX, DIGCTRL); + + return 0; +} + +static void __init clk_misc_init(void) +{ + u32 val; + + /* Gate off cpu clock in WFI for power saving */ + __mxs_setl(1 << BP_CPU_INTERRUPT_WAIT, CPU); + + /* 0 is a bad default value for a divider */ + __mxs_setl(1 << BP_ENET_DIV_TIME, ENET); + + /* Clear BYPASS for SAIF */ + __mxs_clrl(0x3 << BP_CLKSEQ_BYPASS_SAIF0, CLKSEQ); + + /* SAIF has to use frac div for functional operation */ + val = readl_relaxed(SAIF0); + val |= 1 << BP_SAIF_DIV_FRAC_EN; + writel_relaxed(val, SAIF0); + + val = readl_relaxed(SAIF1); + val |= 1 << BP_SAIF_DIV_FRAC_EN; + writel_relaxed(val, SAIF1); + + /* Extra fec clock setting */ + val = readl_relaxed(ENET); + val &= ~(1 << BP_ENET_SLEEP); + writel_relaxed(val, ENET); + + /* + * Source ssp clock from ref_io than ref_xtal, + * as ref_xtal only provides 24 MHz as maximum. + */ + __mxs_clrl(0xf << BP_CLKSEQ_BYPASS_SSP0, CLKSEQ); + + /* + * 480 MHz seems too high to be ssp clock source directly, + * so set frac0 to get a 288 MHz ref_io0. + */ + val = readl_relaxed(FRAC0); + val &= ~(0x3f << BP_FRAC0_IO0FRAC); + val |= 30 << BP_FRAC0_IO0FRAC; + writel_relaxed(val, FRAC0); +} + +static struct clk_lookup uart_lookups[] __initdata = { + { .dev_id = "duart", }, + { .dev_id = "mxs-auart.0", }, + { .dev_id = "mxs-auart.1", }, + { .dev_id = "mxs-auart.2", }, + { .dev_id = "mxs-auart.3", }, + { .dev_id = "mxs-auart.4", }, + { .dev_id = "8006a000.serial", }, + { .dev_id = "8006c000.serial", }, + { .dev_id = "8006e000.serial", }, + { .dev_id = "80070000.serial", }, + { .dev_id = "80072000.serial", }, + { .dev_id = "80074000.serial", }, +}; + +static struct clk_lookup hbus_lookups[] __initdata = { + { .dev_id = "mxs-dma-apbh", }, + { .dev_id = "80004000.dma-apbh", }, +}; + +static struct clk_lookup xbus_lookups[] __initdata = { + { .dev_id = "duart", .con_id = "apb_pclk"}, + { .dev_id = "mxs-dma-apbx", }, + { .dev_id = "80024000.dma-apbx", }, +}; + +static struct clk_lookup ssp0_lookups[] __initdata = { + { .dev_id = "mxs-mmc.0", }, + { .dev_id = "80010000.ssp", }, +}; + +static struct clk_lookup ssp1_lookups[] __initdata = { + { .dev_id = "mxs-mmc.1", }, + { .dev_id = "80012000.ssp", }, +}; + +static struct clk_lookup ssp2_lookups[] __initdata = { + { .dev_id = "mxs-mmc.2", }, + { .dev_id = "80014000.ssp", }, +}; + +static struct clk_lookup ssp3_lookups[] __initdata = { + { .dev_id = "mxs-mmc.3", }, + { .dev_id = "80016000.ssp", }, +}; + +static struct clk_lookup lcdif_lookups[] __initdata = { + { .dev_id = "imx28-fb", }, + { .dev_id = "80030000.lcdif", }, +}; + +static struct clk_lookup gpmi_lookups[] __initdata = { + { .dev_id = "imx28-gpmi-nand", }, + { .dev_id = "8000c000.gpmi", }, +}; + +static struct clk_lookup fec_lookups[] __initdata = { + { .dev_id = "imx28-fec.0", }, + { .dev_id = "imx28-fec.1", }, + { .dev_id = "800f0000.ethernet", }, + { .dev_id = "800f4000.ethernet", }, +}; + +static struct clk_lookup can0_lookups[] __initdata = { + { .dev_id = "flexcan.0", }, + { .dev_id = "80032000.can", }, +}; + +static struct clk_lookup can1_lookups[] __initdata = { + { .dev_id = "flexcan.1", }, + { .dev_id = "80034000.can", }, +}; + +static struct clk_lookup saif0_lookups[] __initdata = { + { .dev_id = "mxs-saif.0", }, + { .dev_id = "80042000.saif", }, +}; + +static struct clk_lookup saif1_lookups[] __initdata = { + { .dev_id = "mxs-saif.1", }, + { .dev_id = "80046000.saif", }, +}; + +static const char *sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", }; +static const char *sel_io0[] __initconst = { "ref_io0", "ref_xtal", }; +static const char *sel_io1[] __initconst = { "ref_io1", "ref_xtal", }; +static const char *sel_pix[] __initconst = { "ref_pix", "ref_xtal", }; +static const char *sel_gpmi[] __initconst = { "ref_gpmi", "ref_xtal", }; +static const char *sel_pll0[] __initconst = { "pll0", "ref_xtal", }; +static const char *cpu_sels[] __initconst = { "cpu_pll", "cpu_xtal", }; +static const char *emi_sels[] __initconst = { "emi_pll", "emi_xtal", }; +static const char *ptp_sels[] __initconst = { "ref_xtal", "pll0", }; + +enum imx28_clk { + ref_xtal, pll0, pll1, pll2, ref_cpu, ref_emi, ref_io0, ref_io1, + ref_pix, ref_hsadc, ref_gpmi, saif0_sel, saif1_sel, gpmi_sel, + ssp0_sel, ssp1_sel, ssp2_sel, ssp3_sel, emi_sel, etm_sel, + lcdif_sel, cpu, ptp_sel, cpu_pll, cpu_xtal, hbus, xbus, + ssp0_div, ssp1_div, ssp2_div, ssp3_div, gpmi_div, emi_pll, + emi_xtal, lcdif_div, etm_div, ptp, saif0_div, saif1_div, + clk32k_div, rtc, lradc, spdif_div, clk32k, pwm, uart, ssp0, + ssp1, ssp2, ssp3, gpmi, spdif, emi, saif0, saif1, lcdif, etm, + fec, can0, can1, usb0, usb1, usb0_pwr, usb1_pwr, enet_out, + clk_max +}; + +static struct clk *clks[clk_max]; + +static enum imx28_clk clks_init_on[] __initdata = { + cpu, hbus, xbus, emi, uart, +}; + +int __init mx28_clocks_init(void) +{ + int i; + + clk_misc_init(); + + clks[ref_xtal] = mxs_clk_fixed("ref_xtal", 24000000); + clks[pll0] = mxs_clk_pll("pll0", "ref_xtal", PLL0CTRL0, 17, 480000000); + clks[pll1] = mxs_clk_pll("pll1", "ref_xtal", PLL1CTRL0, 17, 480000000); + clks[pll2] = mxs_clk_pll("pll2", "ref_xtal", PLL2CTRL0, 23, 50000000); + clks[ref_cpu] = mxs_clk_ref("ref_cpu", "pll0", FRAC0, 0); + clks[ref_emi] = mxs_clk_ref("ref_emi", "pll0", FRAC0, 1); + clks[ref_io0] = mxs_clk_ref("ref_io0", "pll0", FRAC0, 2); + clks[ref_io1] = mxs_clk_ref("ref_io1", "pll0", FRAC0, 3); + clks[ref_pix] = mxs_clk_ref("ref_pix", "pll0", FRAC1, 0); + clks[ref_hsadc] = mxs_clk_ref("ref_hsadc", "pll0", FRAC1, 1); + clks[ref_gpmi] = mxs_clk_ref("ref_gpmi", "pll0", FRAC1, 2); + clks[saif0_sel] = mxs_clk_mux("saif0_sel", CLKSEQ, 0, 1, sel_pll0, ARRAY_SIZE(sel_pll0)); + clks[saif1_sel] = mxs_clk_mux("saif1_sel", CLKSEQ, 1, 1, sel_pll0, ARRAY_SIZE(sel_pll0)); + clks[gpmi_sel] = mxs_clk_mux("gpmi_sel", CLKSEQ, 2, 1, sel_gpmi, ARRAY_SIZE(sel_gpmi)); + clks[ssp0_sel] = mxs_clk_mux("ssp0_sel", CLKSEQ, 3, 1, sel_io0, ARRAY_SIZE(sel_io0)); + clks[ssp1_sel] = mxs_clk_mux("ssp1_sel", CLKSEQ, 4, 1, sel_io0, ARRAY_SIZE(sel_io0)); + clks[ssp2_sel] = mxs_clk_mux("ssp2_sel", CLKSEQ, 5, 1, sel_io1, ARRAY_SIZE(sel_io1)); + clks[ssp3_sel] = mxs_clk_mux("ssp3_sel", CLKSEQ, 6, 1, sel_io1, ARRAY_SIZE(sel_io1)); + clks[emi_sel] = mxs_clk_mux("emi_sel", CLKSEQ, 7, 1, emi_sels, ARRAY_SIZE(emi_sels)); + clks[etm_sel] = mxs_clk_mux("etm_sel", CLKSEQ, 8, 1, sel_cpu, ARRAY_SIZE(sel_cpu)); + clks[lcdif_sel] = mxs_clk_mux("lcdif_sel", CLKSEQ, 14, 1, sel_pix, ARRAY_SIZE(sel_pix)); + clks[cpu] = mxs_clk_mux("cpu", CLKSEQ, 18, 1, cpu_sels, ARRAY_SIZE(cpu_sels)); + clks[ptp_sel] = mxs_clk_mux("ptp_sel", ENET, 19, 1, ptp_sels, ARRAY_SIZE(ptp_sels)); + clks[cpu_pll] = mxs_clk_div("cpu_pll", "ref_cpu", CPU, 0, 6, 28); + clks[cpu_xtal] = mxs_clk_div("cpu_xtal", "ref_xtal", CPU, 16, 10, 29); + clks[hbus] = mxs_clk_div("hbus", "cpu", HBUS, 0, 5, 31); + clks[xbus] = mxs_clk_div("xbus", "ref_xtal", XBUS, 0, 10, 31); + clks[ssp0_div] = mxs_clk_div("ssp0_div", "ssp0_sel", SSP0, 0, 9, 29); + clks[ssp1_div] = mxs_clk_div("ssp1_div", "ssp1_sel", SSP1, 0, 9, 29); + clks[ssp2_div] = mxs_clk_div("ssp2_div", "ssp2_sel", SSP2, 0, 9, 29); + clks[ssp3_div] = mxs_clk_div("ssp3_div", "ssp3_sel", SSP3, 0, 9, 29); + clks[gpmi_div] = mxs_clk_div("gpmi_div", "gpmi_sel", GPMI, 0, 10, 29); + clks[emi_pll] = mxs_clk_div("emi_pll", "ref_emi", EMI, 0, 6, 28); + clks[emi_xtal] = mxs_clk_div("emi_xtal", "ref_xtal", EMI, 8, 4, 29); + clks[lcdif_div] = mxs_clk_div("lcdif_div", "lcdif_sel", LCDIF, 0, 13, 29); + clks[etm_div] = mxs_clk_div("etm_div", "etm_sel", ETM, 0, 7, 29); + clks[ptp] = mxs_clk_div("ptp", "ptp_sel", ENET, 21, 6, 27); + clks[saif0_div] = mxs_clk_frac("saif0_div", "saif0_sel", SAIF0, 0, 16, 29); + clks[saif1_div] = mxs_clk_frac("saif1_div", "saif1_sel", SAIF1, 0, 16, 29); + clks[clk32k_div] = mxs_clk_fixed_factor("clk32k_div", "ref_xtal", 1, 750); + clks[rtc] = mxs_clk_fixed_factor("rtc", "ref_xtal", 1, 768); + clks[lradc] = mxs_clk_fixed_factor("lradc", "clk32k", 1, 16); + clks[spdif_div] = mxs_clk_fixed_factor("spdif_div", "pll0", 1, 4); + clks[clk32k] = mxs_clk_gate("clk32k", "clk32k_div", XTAL, 26); + clks[pwm] = mxs_clk_gate("pwm", "ref_xtal", XTAL, 29); + clks[uart] = mxs_clk_gate("uart", "ref_xtal", XTAL, 31); + clks[ssp0] = mxs_clk_gate("ssp0", "ssp0_div", SSP0, 31); + clks[ssp1] = mxs_clk_gate("ssp1", "ssp1_div", SSP1, 31); + clks[ssp2] = mxs_clk_gate("ssp2", "ssp2_div", SSP2, 31); + clks[ssp3] = mxs_clk_gate("ssp3", "ssp3_div", SSP3, 31); + clks[gpmi] = mxs_clk_gate("gpmi", "gpmi_div", GPMI, 31); + clks[spdif] = mxs_clk_gate("spdif", "spdif_div", SPDIF, 31); + clks[emi] = mxs_clk_gate("emi", "emi_sel", EMI, 31); + clks[saif0] = mxs_clk_gate("saif0", "saif0_div", SAIF0, 31); + clks[saif1] = mxs_clk_gate("saif1", "saif1_div", SAIF1, 31); + clks[lcdif] = mxs_clk_gate("lcdif", "lcdif_div", LCDIF, 31); + clks[etm] = mxs_clk_gate("etm", "etm_div", ETM, 31); + clks[fec] = mxs_clk_gate("fec", "hbus", ENET, 30); + clks[can0] = mxs_clk_gate("can0", "ref_xtal", FLEXCAN, 30); + clks[can1] = mxs_clk_gate("can1", "ref_xtal", FLEXCAN, 28); + clks[usb0] = mxs_clk_gate("usb0", "usb0_pwr", DIGCTRL, 2); + clks[usb1] = mxs_clk_gate("usb1", "usb1_pwr", DIGCTRL, 16); + clks[usb0_pwr] = clk_register_gate(NULL, "usb0_pwr", "pll0", 0, PLL0CTRL0, 18, 0, &mxs_lock); + clks[usb1_pwr] = clk_register_gate(NULL, "usb1_pwr", "pll1", 0, PLL1CTRL0, 18, 0, &mxs_lock); + clks[enet_out] = clk_register_gate(NULL, "enet_out", "pll2", 0, ENET, 18, 0, &mxs_lock); + + for (i = 0; i < ARRAY_SIZE(clks); i++) + if (IS_ERR(clks[i])) { + pr_err("i.MX28 clk %d: register failed with %ld\n", + i, PTR_ERR(clks[i])); + return PTR_ERR(clks[i]); + } + + clk_register_clkdev(clks[clk32k], NULL, "timrot"); + clk_register_clkdev(clks[enet_out], NULL, "enet_out"); + 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)); + clk_register_clkdevs(clks[ssp0], ssp0_lookups, ARRAY_SIZE(ssp0_lookups)); + clk_register_clkdevs(clks[ssp1], ssp1_lookups, ARRAY_SIZE(ssp1_lookups)); + clk_register_clkdevs(clks[ssp2], ssp2_lookups, ARRAY_SIZE(ssp2_lookups)); + clk_register_clkdevs(clks[ssp3], ssp3_lookups, ARRAY_SIZE(ssp3_lookups)); + clk_register_clkdevs(clks[gpmi], gpmi_lookups, ARRAY_SIZE(gpmi_lookups)); + clk_register_clkdevs(clks[saif0], saif0_lookups, ARRAY_SIZE(saif0_lookups)); + clk_register_clkdevs(clks[saif1], saif1_lookups, ARRAY_SIZE(saif1_lookups)); + clk_register_clkdevs(clks[lcdif], lcdif_lookups, ARRAY_SIZE(lcdif_lookups)); + 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)); + + for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) + clk_prepare_enable(clks[clks_init_on[i]]); + + mxs_timer_init(MX28_INT_TIMER0); + + return 0; +} diff --git a/drivers/clk/mxs/clk-pll.c b/drivers/clk/mxs/clk-pll.c new file mode 100644 index 000000000000..fadae41833ec --- /dev/null +++ b/drivers/clk/mxs/clk-pll.c @@ -0,0 +1,116 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include "clk.h" + +/** + * struct clk_pll - mxs pll clock + * @hw: clk_hw for the pll + * @base: base address of the pll + * @power: the shift of power bit + * @rate: the clock rate of the pll + * + * The mxs pll is a fixed rate clock with power and gate control, + * and the shift of gate bit is always 31. + */ +struct clk_pll { + struct clk_hw hw; + void __iomem *base; + u8 power; + unsigned long rate; +}; + +#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw) + +static int clk_pll_prepare(struct clk_hw *hw) +{ + struct clk_pll *pll = to_clk_pll(hw); + + writel_relaxed(1 << pll->power, pll->base + SET); + + udelay(10); + + return 0; +} + +static void clk_pll_unprepare(struct clk_hw *hw) +{ + struct clk_pll *pll = to_clk_pll(hw); + + writel_relaxed(1 << pll->power, pll->base + CLR); +} + +static int clk_pll_enable(struct clk_hw *hw) +{ + struct clk_pll *pll = to_clk_pll(hw); + + writel_relaxed(1 << 31, pll->base + CLR); + + return 0; +} + +static void clk_pll_disable(struct clk_hw *hw) +{ + struct clk_pll *pll = to_clk_pll(hw); + + writel_relaxed(1 << 31, pll->base + SET); +} + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pll *pll = to_clk_pll(hw); + + return pll->rate; +} + +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, +}; + +struct clk *mxs_clk_pll(const char *name, const char *parent_name, + void __iomem *base, u8 power, unsigned long rate) +{ + struct clk_pll *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_pll_ops; + init.flags = 0; + init.parent_names = (parent_name ? &parent_name: NULL); + init.num_parents = (parent_name ? 1 : 0); + + pll->base = base; + pll->rate = rate; + pll->power = power; + pll->hw.init = &init; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} diff --git a/drivers/clk/mxs/clk-ref.c b/drivers/clk/mxs/clk-ref.c new file mode 100644 index 000000000000..4adeed6c2f94 --- /dev/null +++ b/drivers/clk/mxs/clk-ref.c @@ -0,0 +1,154 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include "clk.h" + +/** + * struct clk_ref - mxs reference clock + * @hw: clk_hw for the reference clock + * @reg: register address + * @idx: the index of the reference clock within the same register + * + * The mxs reference clock sources from pll. Every 4 reference clocks share + * one register space, and @idx is used to identify them. Each reference + * clock has a gate control and a fractional * divider. The rate is calculated + * as pll rate * (18 / FRAC), where FRAC = 18 ~ 35. + */ +struct clk_ref { + struct clk_hw hw; + void __iomem *reg; + u8 idx; +}; + +#define to_clk_ref(_hw) container_of(_hw, struct clk_ref, hw) + +static int clk_ref_enable(struct clk_hw *hw) +{ + struct clk_ref *ref = to_clk_ref(hw); + + writel_relaxed(1 << ((ref->idx + 1) * 8 - 1), ref->reg + CLR); + + return 0; +} + +static void clk_ref_disable(struct clk_hw *hw) +{ + struct clk_ref *ref = to_clk_ref(hw); + + writel_relaxed(1 << ((ref->idx + 1) * 8 - 1), ref->reg + SET); +} + +static unsigned long clk_ref_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_ref *ref = to_clk_ref(hw); + u64 tmp = parent_rate; + u8 frac = (readl_relaxed(ref->reg) >> (ref->idx * 8)) & 0x3f; + + tmp *= 18; + do_div(tmp, frac); + + return tmp; +} + +static long clk_ref_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + unsigned long parent_rate = *prate; + u64 tmp = parent_rate; + u8 frac; + + tmp = tmp * 18 + rate / 2; + do_div(tmp, rate); + frac = tmp; + + if (frac < 18) + frac = 18; + else if (frac > 35) + frac = 35; + + tmp = parent_rate; + tmp *= 18; + do_div(tmp, frac); + + return tmp; +} + +static int clk_ref_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_ref *ref = to_clk_ref(hw); + unsigned long flags; + u64 tmp = parent_rate; + u32 val; + u8 frac, shift = ref->idx * 8; + + tmp = tmp * 18 + rate / 2; + do_div(tmp, rate); + frac = tmp; + + if (frac < 18) + frac = 18; + else if (frac > 35) + frac = 35; + + spin_lock_irqsave(&mxs_lock, flags); + + val = readl_relaxed(ref->reg); + val &= ~(0x3f << shift); + val |= frac << shift; + writel_relaxed(val, ref->reg); + + spin_unlock_irqrestore(&mxs_lock, flags); + + return 0; +} + +static const struct clk_ops clk_ref_ops = { + .enable = clk_ref_enable, + .disable = clk_ref_disable, + .recalc_rate = clk_ref_recalc_rate, + .round_rate = clk_ref_round_rate, + .set_rate = clk_ref_set_rate, +}; + +struct clk *mxs_clk_ref(const char *name, const char *parent_name, + void __iomem *reg, u8 idx) +{ + struct clk_ref *ref; + struct clk *clk; + struct clk_init_data init; + + ref = kzalloc(sizeof(*ref), GFP_KERNEL); + if (!ref) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_ref_ops; + init.flags = 0; + init.parent_names = (parent_name ? &parent_name: NULL); + init.num_parents = (parent_name ? 1 : 0); + + ref->reg = reg; + ref->idx = idx; + ref->hw.init = &init; + + clk = clk_register(NULL, &ref->hw); + if (IS_ERR(clk)) + kfree(ref); + + return clk; +} diff --git a/drivers/clk/mxs/clk.c b/drivers/clk/mxs/clk.c new file mode 100644 index 000000000000..b24d56067c80 --- /dev/null +++ b/drivers/clk/mxs/clk.c @@ -0,0 +1,28 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/jiffies.h> +#include <linux/spinlock.h> + +DEFINE_SPINLOCK(mxs_lock); + +int mxs_clk_wait(void __iomem *reg, u8 shift) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(10); + + while (readl_relaxed(reg) & (1 << shift)) + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + + return 0; +} diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h new file mode 100644 index 000000000000..81421e28e69c --- /dev/null +++ b/drivers/clk/mxs/clk.h @@ -0,0 +1,66 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef __MXS_CLK_H +#define __MXS_CLK_H + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/spinlock.h> + +#define SET 0x4 +#define CLR 0x8 + +extern spinlock_t mxs_lock; + +int mxs_clk_wait(void __iomem *reg, u8 shift); + +struct clk *mxs_clk_pll(const char *name, const char *parent_name, + void __iomem *base, u8 power, unsigned long rate); + +struct clk *mxs_clk_ref(const char *name, const char *parent_name, + void __iomem *reg, u8 idx); + +struct clk *mxs_clk_div(const char *name, const char *parent_name, + void __iomem *reg, u8 shift, u8 width, u8 busy); + +struct clk *mxs_clk_frac(const char *name, const char *parent_name, + void __iomem *reg, u8 shift, u8 width, u8 busy); + +static inline struct clk *mxs_clk_fixed(const char *name, int rate) +{ + return clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, rate); +} + +static inline struct clk *mxs_clk_gate(const char *name, + const char *parent_name, void __iomem *reg, u8 shift) +{ + return clk_register_gate(NULL, name, parent_name, CLK_SET_RATE_PARENT, + reg, shift, CLK_GATE_SET_TO_DISABLE, + &mxs_lock); +} + +static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg, + u8 shift, u8 width, const char **parent_names, int num_parents) +{ + return clk_register_mux(NULL, name, parent_names, num_parents, + CLK_SET_RATE_PARENT, reg, shift, width, + 0, &mxs_lock); +} + +static inline struct clk *mxs_clk_fixed_factor(const char *name, + const char *parent_name, unsigned int mult, unsigned int div) +{ + return clk_register_fixed_factor(NULL, name, parent_name, + CLK_SET_RATE_PARENT, mult, div); +} + +#endif /* __MXS_CLK_H */ diff --git a/drivers/clk/spear/Makefile b/drivers/clk/spear/Makefile new file mode 100644 index 000000000000..335886049c83 --- /dev/null +++ b/drivers/clk/spear/Makefile @@ -0,0 +1,8 @@ +# +# SPEAr Clock specific Makefile +# + +obj-y += clk.o clk-aux-synth.o clk-frac-synth.o clk-gpt-synth.o clk-vco-pll.o + +obj-$(CONFIG_ARCH_SPEAR3XX) += spear3xx_clock.o +obj-$(CONFIG_ARCH_SPEAR6XX) += spear6xx_clock.o diff --git a/drivers/clk/spear/clk-aux-synth.c b/drivers/clk/spear/clk-aux-synth.c new file mode 100644 index 000000000000..af34074e702b --- /dev/null +++ b/drivers/clk/spear/clk-aux-synth.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * Auxiliary Synthesizer clock implementation + */ + +#define pr_fmt(fmt) "clk-aux-synth: " fmt + +#include <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include "clk.h" + +/* + * DOC: Auxiliary Synthesizer clock + * + * Aux synth gives rate for different values of eq, x and y + * + * Fout from synthesizer can be given from two equations: + * Fout1 = (Fin * X/Y)/2 EQ1 + * Fout2 = Fin * X/Y EQ2 + */ + +#define to_clk_aux(_hw) container_of(_hw, struct clk_aux, hw) + +static struct aux_clk_masks default_aux_masks = { + .eq_sel_mask = AUX_EQ_SEL_MASK, + .eq_sel_shift = AUX_EQ_SEL_SHIFT, + .eq1_mask = AUX_EQ1_SEL, + .eq2_mask = AUX_EQ2_SEL, + .xscale_sel_mask = AUX_XSCALE_MASK, + .xscale_sel_shift = AUX_XSCALE_SHIFT, + .yscale_sel_mask = AUX_YSCALE_MASK, + .yscale_sel_shift = AUX_YSCALE_SHIFT, + .enable_bit = AUX_SYNT_ENB, +}; + +static unsigned long aux_calc_rate(struct clk_hw *hw, unsigned long prate, + int index) +{ + struct clk_aux *aux = to_clk_aux(hw); + struct aux_rate_tbl *rtbl = aux->rtbl; + u8 eq = rtbl[index].eq ? 1 : 2; + + return (((prate / 10000) * rtbl[index].xscale) / + (rtbl[index].yscale * eq)) * 10000; +} + +static long clk_aux_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + struct clk_aux *aux = to_clk_aux(hw); + int unused; + + return clk_round_rate_index(hw, drate, *prate, aux_calc_rate, + aux->rtbl_cnt, &unused); +} + +static unsigned long clk_aux_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_aux *aux = to_clk_aux(hw); + unsigned int num = 1, den = 1, val, eqn; + unsigned long flags = 0; + + if (aux->lock) + spin_lock_irqsave(aux->lock, flags); + + val = readl_relaxed(aux->reg); + + if (aux->lock) + spin_unlock_irqrestore(aux->lock, flags); + + eqn = (val >> aux->masks->eq_sel_shift) & aux->masks->eq_sel_mask; + if (eqn == aux->masks->eq1_mask) + den = 2; + + /* calculate numerator */ + num = (val >> aux->masks->xscale_sel_shift) & + aux->masks->xscale_sel_mask; + + /* calculate denominator */ + den *= (val >> aux->masks->yscale_sel_shift) & + aux->masks->yscale_sel_mask; + + if (!den) + return 0; + + return (((parent_rate / 10000) * num) / den) * 10000; +} + +/* Configures new clock rate of aux */ +static int clk_aux_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_aux *aux = to_clk_aux(hw); + struct aux_rate_tbl *rtbl = aux->rtbl; + unsigned long val, flags = 0; + int i; + + clk_round_rate_index(hw, drate, prate, aux_calc_rate, aux->rtbl_cnt, + &i); + + if (aux->lock) + spin_lock_irqsave(aux->lock, flags); + + val = readl_relaxed(aux->reg) & + ~(aux->masks->eq_sel_mask << aux->masks->eq_sel_shift); + val |= (rtbl[i].eq & aux->masks->eq_sel_mask) << + aux->masks->eq_sel_shift; + val &= ~(aux->masks->xscale_sel_mask << aux->masks->xscale_sel_shift); + val |= (rtbl[i].xscale & aux->masks->xscale_sel_mask) << + aux->masks->xscale_sel_shift; + val &= ~(aux->masks->yscale_sel_mask << aux->masks->yscale_sel_shift); + val |= (rtbl[i].yscale & aux->masks->yscale_sel_mask) << + aux->masks->yscale_sel_shift; + writel_relaxed(val, aux->reg); + + if (aux->lock) + spin_unlock_irqrestore(aux->lock, flags); + + return 0; +} + +static struct clk_ops clk_aux_ops = { + .recalc_rate = clk_aux_recalc_rate, + .round_rate = clk_aux_round_rate, + .set_rate = clk_aux_set_rate, +}; + +struct clk *clk_register_aux(const char *aux_name, const char *gate_name, + const char *parent_name, unsigned long flags, void __iomem *reg, + struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, + u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk) +{ + struct clk_aux *aux; + struct clk_init_data init; + struct clk *clk; + + if (!aux_name || !parent_name || !reg || !rtbl || !rtbl_cnt) { + pr_err("Invalid arguments passed"); + return ERR_PTR(-EINVAL); + } + + aux = kzalloc(sizeof(*aux), GFP_KERNEL); + if (!aux) { + pr_err("could not allocate aux clk\n"); + return ERR_PTR(-ENOMEM); + } + + /* struct clk_aux assignments */ + if (!masks) + aux->masks = &default_aux_masks; + else + aux->masks = masks; + + aux->reg = reg; + aux->rtbl = rtbl; + aux->rtbl_cnt = rtbl_cnt; + aux->lock = lock; + aux->hw.init = &init; + + init.name = aux_name; + init.ops = &clk_aux_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &aux->hw); + if (IS_ERR_OR_NULL(clk)) + goto free_aux; + + if (gate_name) { + struct clk *tgate_clk; + + tgate_clk = clk_register_gate(NULL, gate_name, aux_name, 0, reg, + aux->masks->enable_bit, 0, lock); + if (IS_ERR_OR_NULL(tgate_clk)) + goto free_aux; + + if (gate_clk) + *gate_clk = tgate_clk; + } + + return clk; + +free_aux: + kfree(aux); + pr_err("clk register failed\n"); + + return NULL; +} diff --git a/drivers/clk/spear/clk-frac-synth.c b/drivers/clk/spear/clk-frac-synth.c new file mode 100644 index 000000000000..4dbdb3fe18e0 --- /dev/null +++ b/drivers/clk/spear/clk-frac-synth.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * Fractional Synthesizer clock implementation + */ + +#define pr_fmt(fmt) "clk-frac-synth: " fmt + +#include <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include "clk.h" + +#define DIV_FACTOR_MASK 0x1FFFF + +/* + * DOC: Fractional Synthesizer clock + * + * Fout from synthesizer can be given from below equation: + * + * Fout= Fin/2*div (division factor) + * div is 17 bits:- + * 0-13 (fractional part) + * 14-16 (integer part) + * div is (16-14 bits).(13-0 bits) (in binary) + * + * Fout = Fin/(2 * div) + * Fout = ((Fin / 10000)/(2 * div)) * 10000 + * Fout = (2^14 * (Fin / 10000)/(2^14 * (2 * div))) * 10000 + * Fout = (((Fin / 10000) << 14)/(2 * (div << 14))) * 10000 + * + * div << 14 simply 17 bit value written at register. + * Max error due to scaling down by 10000 is 10 KHz + */ + +#define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw) + +static unsigned long frac_calc_rate(struct clk_hw *hw, unsigned long prate, + int index) +{ + struct clk_frac *frac = to_clk_frac(hw); + struct frac_rate_tbl *rtbl = frac->rtbl; + + prate /= 10000; + prate <<= 14; + prate /= (2 * rtbl[index].div); + prate *= 10000; + + return prate; +} + +static long clk_frac_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + struct clk_frac *frac = to_clk_frac(hw); + int unused; + + return clk_round_rate_index(hw, drate, *prate, frac_calc_rate, + frac->rtbl_cnt, &unused); +} + +static unsigned long clk_frac_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_frac *frac = to_clk_frac(hw); + unsigned long flags = 0; + unsigned int div = 1, val; + + if (frac->lock) + spin_lock_irqsave(frac->lock, flags); + + val = readl_relaxed(frac->reg); + + if (frac->lock) + spin_unlock_irqrestore(frac->lock, flags); + + div = val & DIV_FACTOR_MASK; + + if (!div) + return 0; + + parent_rate = parent_rate / 10000; + + parent_rate = (parent_rate << 14) / (2 * div); + return parent_rate * 10000; +} + +/* Configures new clock rate of frac */ +static int clk_frac_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_frac *frac = to_clk_frac(hw); + struct frac_rate_tbl *rtbl = frac->rtbl; + unsigned long flags = 0, val; + int i; + + clk_round_rate_index(hw, drate, prate, frac_calc_rate, frac->rtbl_cnt, + &i); + + if (frac->lock) + spin_lock_irqsave(frac->lock, flags); + + val = readl_relaxed(frac->reg) & ~DIV_FACTOR_MASK; + val |= rtbl[i].div & DIV_FACTOR_MASK; + writel_relaxed(val, frac->reg); + + if (frac->lock) + spin_unlock_irqrestore(frac->lock, flags); + + return 0; +} + +struct clk_ops clk_frac_ops = { + .recalc_rate = clk_frac_recalc_rate, + .round_rate = clk_frac_round_rate, + .set_rate = clk_frac_set_rate, +}; + +struct clk *clk_register_frac(const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, + struct frac_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock) +{ + struct clk_init_data init; + struct clk_frac *frac; + struct clk *clk; + + if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) { + pr_err("Invalid arguments passed"); + return ERR_PTR(-EINVAL); + } + + frac = kzalloc(sizeof(*frac), GFP_KERNEL); + if (!frac) { + pr_err("could not allocate frac clk\n"); + return ERR_PTR(-ENOMEM); + } + + /* struct clk_frac assignments */ + frac->reg = reg; + frac->rtbl = rtbl; + frac->rtbl_cnt = rtbl_cnt; + frac->lock = lock; + frac->hw.init = &init; + + init.name = name; + init.ops = &clk_frac_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &frac->hw); + if (!IS_ERR_OR_NULL(clk)) + return clk; + + pr_err("clk register failed\n"); + kfree(frac); + + return NULL; +} diff --git a/drivers/clk/spear/clk-gpt-synth.c b/drivers/clk/spear/clk-gpt-synth.c new file mode 100644 index 000000000000..b471c9762a97 --- /dev/null +++ b/drivers/clk/spear/clk-gpt-synth.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * General Purpose Timer Synthesizer clock implementation + */ + +#define pr_fmt(fmt) "clk-gpt-synth: " fmt + +#include <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include "clk.h" + +#define GPT_MSCALE_MASK 0xFFF +#define GPT_NSCALE_SHIFT 12 +#define GPT_NSCALE_MASK 0xF + +/* + * DOC: General Purpose Timer Synthesizer clock + * + * Calculates gpt synth clk rate for different values of mscale and nscale + * + * Fout= Fin/((2 ^ (N+1)) * (M+1)) + */ + +#define to_clk_gpt(_hw) container_of(_hw, struct clk_gpt, hw) + +static unsigned long gpt_calc_rate(struct clk_hw *hw, unsigned long prate, + int index) +{ + struct clk_gpt *gpt = to_clk_gpt(hw); + struct gpt_rate_tbl *rtbl = gpt->rtbl; + + prate /= ((1 << (rtbl[index].nscale + 1)) * (rtbl[index].mscale + 1)); + + return prate; +} + +static long clk_gpt_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + struct clk_gpt *gpt = to_clk_gpt(hw); + int unused; + + return clk_round_rate_index(hw, drate, *prate, gpt_calc_rate, + gpt->rtbl_cnt, &unused); +} + +static unsigned long clk_gpt_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_gpt *gpt = to_clk_gpt(hw); + unsigned long flags = 0; + unsigned int div = 1, val; + + if (gpt->lock) + spin_lock_irqsave(gpt->lock, flags); + + val = readl_relaxed(gpt->reg); + + if (gpt->lock) + spin_unlock_irqrestore(gpt->lock, flags); + + div += val & GPT_MSCALE_MASK; + div *= 1 << (((val >> GPT_NSCALE_SHIFT) & GPT_NSCALE_MASK) + 1); + + if (!div) + return 0; + + return parent_rate / div; +} + +/* Configures new clock rate of gpt */ +static int clk_gpt_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_gpt *gpt = to_clk_gpt(hw); + struct gpt_rate_tbl *rtbl = gpt->rtbl; + unsigned long flags = 0, val; + int i; + + clk_round_rate_index(hw, drate, prate, gpt_calc_rate, gpt->rtbl_cnt, + &i); + + if (gpt->lock) + spin_lock_irqsave(gpt->lock, flags); + + val = readl(gpt->reg) & ~GPT_MSCALE_MASK; + val &= ~(GPT_NSCALE_MASK << GPT_NSCALE_SHIFT); + + val |= rtbl[i].mscale & GPT_MSCALE_MASK; + val |= (rtbl[i].nscale & GPT_NSCALE_MASK) << GPT_NSCALE_SHIFT; + + writel_relaxed(val, gpt->reg); + + if (gpt->lock) + spin_unlock_irqrestore(gpt->lock, flags); + + return 0; +} + +static struct clk_ops clk_gpt_ops = { + .recalc_rate = clk_gpt_recalc_rate, + .round_rate = clk_gpt_round_rate, + .set_rate = clk_gpt_set_rate, +}; + +struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned + long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8 + rtbl_cnt, spinlock_t *lock) +{ + struct clk_init_data init; + struct clk_gpt *gpt; + struct clk *clk; + + if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) { + pr_err("Invalid arguments passed"); + return ERR_PTR(-EINVAL); + } + + gpt = kzalloc(sizeof(*gpt), GFP_KERNEL); + if (!gpt) { + pr_err("could not allocate gpt clk\n"); + return ERR_PTR(-ENOMEM); + } + + /* struct clk_gpt assignments */ + gpt->reg = reg; + gpt->rtbl = rtbl; + gpt->rtbl_cnt = rtbl_cnt; + gpt->lock = lock; + gpt->hw.init = &init; + + init.name = name; + init.ops = &clk_gpt_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &gpt->hw); + if (!IS_ERR_OR_NULL(clk)) + return clk; + + pr_err("clk register failed\n"); + kfree(gpt); + + return NULL; +} diff --git a/drivers/clk/spear/clk-vco-pll.c b/drivers/clk/spear/clk-vco-pll.c new file mode 100644 index 000000000000..dcd4bdf4b0d9 --- /dev/null +++ b/drivers/clk/spear/clk-vco-pll.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * VCO-PLL clock implementation + */ + +#define pr_fmt(fmt) "clk-vco-pll: " fmt + +#include <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include "clk.h" + +/* + * DOC: VCO-PLL clock + * + * VCO and PLL rate are derived from following equations: + * + * In normal mode + * vco = (2 * M[15:8] * Fin)/N + * + * In Dithered mode + * vco = (2 * M[15:0] * Fin)/(256 * N) + * + * pll_rate = pll/2^p + * + * vco and pll are very closely bound to each other, "vco needs to program: + * mode, m & n" and "pll needs to program p", both share common enable/disable + * logic. + * + * clk_register_vco_pll() registers instances of both vco & pll. + * CLK_SET_RATE_PARENT flag is forced for pll, as it will always pass its + * set_rate to vco. A single rate table exists for both the clocks, which + * configures m, n and p. + */ + +/* PLL_CTR register masks */ +#define PLL_MODE_NORMAL 0 +#define PLL_MODE_FRACTION 1 +#define PLL_MODE_DITH_DSM 2 +#define PLL_MODE_DITH_SSM 3 +#define PLL_MODE_MASK 3 +#define PLL_MODE_SHIFT 3 +#define PLL_ENABLE 2 + +#define PLL_LOCK_SHIFT 0 +#define PLL_LOCK_MASK 1 + +/* PLL FRQ register masks */ +#define PLL_NORM_FDBK_M_MASK 0xFF +#define PLL_NORM_FDBK_M_SHIFT 24 +#define PLL_DITH_FDBK_M_MASK 0xFFFF +#define PLL_DITH_FDBK_M_SHIFT 16 +#define PLL_DIV_P_MASK 0x7 +#define PLL_DIV_P_SHIFT 8 +#define PLL_DIV_N_MASK 0xFF +#define PLL_DIV_N_SHIFT 0 + +#define to_clk_vco(_hw) container_of(_hw, struct clk_vco, hw) +#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw) + +/* Calculates pll clk rate for specific value of mode, m, n and p */ +static unsigned long pll_calc_rate(struct pll_rate_tbl *rtbl, + unsigned long prate, int index, unsigned long *pll_rate) +{ + unsigned long rate = prate; + unsigned int mode; + + mode = rtbl[index].mode ? 256 : 1; + rate = (((2 * rate / 10000) * rtbl[index].m) / (mode * rtbl[index].n)); + + if (pll_rate) + *pll_rate = (rate / (1 << rtbl[index].p)) * 10000; + + return rate * 10000; +} + +static long clk_pll_round_rate_index(struct clk_hw *hw, unsigned long drate, + unsigned long *prate, int *index) +{ + struct clk_pll *pll = to_clk_pll(hw); + unsigned long prev_rate, vco_prev_rate, rate = 0; + unsigned long vco_parent_rate = + __clk_get_rate(__clk_get_parent(__clk_get_parent(hw->clk))); + + if (!prate) { + pr_err("%s: prate is must for pll clk\n", __func__); + return -EINVAL; + } + + for (*index = 0; *index < pll->vco->rtbl_cnt; (*index)++) { + prev_rate = rate; + vco_prev_rate = *prate; + *prate = pll_calc_rate(pll->vco->rtbl, vco_parent_rate, *index, + &rate); + if (drate < rate) { + /* previous clock was best */ + if (*index) { + rate = prev_rate; + *prate = vco_prev_rate; + (*index)--; + } + break; + } + } + + return rate; +} + +static long clk_pll_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + int unused; + + return clk_pll_round_rate_index(hw, drate, prate, &unused); +} + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long + parent_rate) +{ + struct clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + unsigned int p; + + if (pll->vco->lock) + spin_lock_irqsave(pll->vco->lock, flags); + + p = readl_relaxed(pll->vco->cfg_reg); + + if (pll->vco->lock) + spin_unlock_irqrestore(pll->vco->lock, flags); + + p = (p >> PLL_DIV_P_SHIFT) & PLL_DIV_P_MASK; + + return parent_rate / (1 << p); +} + +static int clk_pll_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_pll *pll = to_clk_pll(hw); + struct pll_rate_tbl *rtbl = pll->vco->rtbl; + unsigned long flags = 0, val; + int i; + + clk_pll_round_rate_index(hw, drate, NULL, &i); + + if (pll->vco->lock) + spin_lock_irqsave(pll->vco->lock, flags); + + val = readl_relaxed(pll->vco->cfg_reg); + val &= ~(PLL_DIV_P_MASK << PLL_DIV_P_SHIFT); + val |= (rtbl[i].p & PLL_DIV_P_MASK) << PLL_DIV_P_SHIFT; + writel_relaxed(val, pll->vco->cfg_reg); + + if (pll->vco->lock) + spin_unlock_irqrestore(pll->vco->lock, flags); + + return 0; +} + +static struct clk_ops clk_pll_ops = { + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_round_rate, + .set_rate = clk_pll_set_rate, +}; + +static inline unsigned long vco_calc_rate(struct clk_hw *hw, + unsigned long prate, int index) +{ + struct clk_vco *vco = to_clk_vco(hw); + + return pll_calc_rate(vco->rtbl, prate, index, NULL); +} + +static long clk_vco_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + struct clk_vco *vco = to_clk_vco(hw); + int unused; + + return clk_round_rate_index(hw, drate, *prate, vco_calc_rate, + vco->rtbl_cnt, &unused); +} + +static unsigned long clk_vco_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_vco *vco = to_clk_vco(hw); + unsigned long flags = 0; + unsigned int num = 2, den = 0, val, mode = 0; + + if (vco->lock) + spin_lock_irqsave(vco->lock, flags); + + mode = (readl_relaxed(vco->mode_reg) >> PLL_MODE_SHIFT) & PLL_MODE_MASK; + + val = readl_relaxed(vco->cfg_reg); + + if (vco->lock) + spin_unlock_irqrestore(vco->lock, flags); + + den = (val >> PLL_DIV_N_SHIFT) & PLL_DIV_N_MASK; + + /* calculate numerator & denominator */ + if (!mode) { + /* Normal mode */ + num *= (val >> PLL_NORM_FDBK_M_SHIFT) & PLL_NORM_FDBK_M_MASK; + } else { + /* Dithered mode */ + num *= (val >> PLL_DITH_FDBK_M_SHIFT) & PLL_DITH_FDBK_M_MASK; + den *= 256; + } + + if (!den) { + WARN(1, "%s: denominator can't be zero\n", __func__); + return 0; + } + + return (((parent_rate / 10000) * num) / den) * 10000; +} + +/* Configures new clock rate of vco */ +static int clk_vco_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_vco *vco = to_clk_vco(hw); + struct pll_rate_tbl *rtbl = vco->rtbl; + unsigned long flags = 0, val; + int i; + + clk_round_rate_index(hw, drate, prate, vco_calc_rate, vco->rtbl_cnt, + &i); + + if (vco->lock) + spin_lock_irqsave(vco->lock, flags); + + val = readl_relaxed(vco->mode_reg); + val &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT); + val |= (rtbl[i].mode & PLL_MODE_MASK) << PLL_MODE_SHIFT; + writel_relaxed(val, vco->mode_reg); + + val = readl_relaxed(vco->cfg_reg); + val &= ~(PLL_DIV_N_MASK << PLL_DIV_N_SHIFT); + val |= (rtbl[i].n & PLL_DIV_N_MASK) << PLL_DIV_N_SHIFT; + + val &= ~(PLL_DITH_FDBK_M_MASK << PLL_DITH_FDBK_M_SHIFT); + if (rtbl[i].mode) + val |= (rtbl[i].m & PLL_DITH_FDBK_M_MASK) << + PLL_DITH_FDBK_M_SHIFT; + else + val |= (rtbl[i].m & PLL_NORM_FDBK_M_MASK) << + PLL_NORM_FDBK_M_SHIFT; + + writel_relaxed(val, vco->cfg_reg); + + if (vco->lock) + spin_unlock_irqrestore(vco->lock, flags); + + return 0; +} + +static struct clk_ops clk_vco_ops = { + .recalc_rate = clk_vco_recalc_rate, + .round_rate = clk_vco_round_rate, + .set_rate = clk_vco_set_rate, +}; + +struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name, + const char *vco_gate_name, const char *parent_name, + unsigned long flags, void __iomem *mode_reg, void __iomem + *cfg_reg, struct pll_rate_tbl *rtbl, u8 rtbl_cnt, + spinlock_t *lock, struct clk **pll_clk, + struct clk **vco_gate_clk) +{ + struct clk_vco *vco; + struct clk_pll *pll; + struct clk *vco_clk, *tpll_clk, *tvco_gate_clk; + struct clk_init_data vco_init, pll_init; + const char **vco_parent_name; + + if (!vco_name || !pll_name || !parent_name || !mode_reg || !cfg_reg || + !rtbl || !rtbl_cnt) { + pr_err("Invalid arguments passed"); + return ERR_PTR(-EINVAL); + } + + vco = kzalloc(sizeof(*vco), GFP_KERNEL); + if (!vco) { + pr_err("could not allocate vco clk\n"); + return ERR_PTR(-ENOMEM); + } + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("could not allocate pll clk\n"); + goto free_vco; + } + + /* struct clk_vco assignments */ + vco->mode_reg = mode_reg; + vco->cfg_reg = cfg_reg; + vco->rtbl = rtbl; + vco->rtbl_cnt = rtbl_cnt; + vco->lock = lock; + vco->hw.init = &vco_init; + + pll->vco = vco; + pll->hw.init = &pll_init; + + if (vco_gate_name) { + tvco_gate_clk = clk_register_gate(NULL, vco_gate_name, + parent_name, 0, mode_reg, PLL_ENABLE, 0, lock); + if (IS_ERR_OR_NULL(tvco_gate_clk)) + goto free_pll; + + if (vco_gate_clk) + *vco_gate_clk = tvco_gate_clk; + vco_parent_name = &vco_gate_name; + } else { + vco_parent_name = &parent_name; + } + + vco_init.name = vco_name; + vco_init.ops = &clk_vco_ops; + vco_init.flags = flags; + vco_init.parent_names = vco_parent_name; + vco_init.num_parents = 1; + + pll_init.name = pll_name; + pll_init.ops = &clk_pll_ops; + pll_init.flags = CLK_SET_RATE_PARENT; + pll_init.parent_names = &vco_name; + pll_init.num_parents = 1; + + vco_clk = clk_register(NULL, &vco->hw); + if (IS_ERR_OR_NULL(vco_clk)) + goto free_pll; + + tpll_clk = clk_register(NULL, &pll->hw); + if (IS_ERR_OR_NULL(tpll_clk)) + goto free_pll; + + if (pll_clk) + *pll_clk = tpll_clk; + + return vco_clk; + +free_pll: + kfree(pll); +free_vco: + kfree(vco); + + pr_err("Failed to register vco pll clock\n"); + + return ERR_PTR(-ENOMEM); +} diff --git a/drivers/clk/spear/clk.c b/drivers/clk/spear/clk.c new file mode 100644 index 000000000000..376d4e5ff326 --- /dev/null +++ b/drivers/clk/spear/clk.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * SPEAr clk - Common routines + */ + +#include <linux/clk-provider.h> +#include <linux/types.h> +#include "clk.h" + +long clk_round_rate_index(struct clk_hw *hw, unsigned long drate, + unsigned long parent_rate, clk_calc_rate calc_rate, u8 rtbl_cnt, + int *index) +{ + unsigned long prev_rate, rate = 0; + + for (*index = 0; *index < rtbl_cnt; (*index)++) { + prev_rate = rate; + rate = calc_rate(hw, parent_rate, *index); + if (drate < rate) { + /* previous clock was best */ + if (*index) { + rate = prev_rate; + (*index)--; + } + break; + } + } + + return rate; +} diff --git a/drivers/clk/spear/clk.h b/drivers/clk/spear/clk.h new file mode 100644 index 000000000000..3321c46a071c --- /dev/null +++ b/drivers/clk/spear/clk.h @@ -0,0 +1,134 @@ +/* + * Clock framework definitions for SPEAr platform + * + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __SPEAR_CLK_H +#define __SPEAR_CLK_H + +#include <linux/clk-provider.h> +#include <linux/spinlock_types.h> +#include <linux/types.h> + +/* Auxiliary Synth clk */ +/* Default masks */ +#define AUX_EQ_SEL_SHIFT 30 +#define AUX_EQ_SEL_MASK 1 +#define AUX_EQ1_SEL 0 +#define AUX_EQ2_SEL 1 +#define AUX_XSCALE_SHIFT 16 +#define AUX_XSCALE_MASK 0xFFF +#define AUX_YSCALE_SHIFT 0 +#define AUX_YSCALE_MASK 0xFFF +#define AUX_SYNT_ENB 31 + +struct aux_clk_masks { + u32 eq_sel_mask; + u32 eq_sel_shift; + u32 eq1_mask; + u32 eq2_mask; + u32 xscale_sel_mask; + u32 xscale_sel_shift; + u32 yscale_sel_mask; + u32 yscale_sel_shift; + u32 enable_bit; +}; + +struct aux_rate_tbl { + u16 xscale; + u16 yscale; + u8 eq; +}; + +struct clk_aux { + struct clk_hw hw; + void __iomem *reg; + struct aux_clk_masks *masks; + struct aux_rate_tbl *rtbl; + u8 rtbl_cnt; + spinlock_t *lock; +}; + +/* Fractional Synth clk */ +struct frac_rate_tbl { + u32 div; +}; + +struct clk_frac { + struct clk_hw hw; + void __iomem *reg; + struct frac_rate_tbl *rtbl; + u8 rtbl_cnt; + spinlock_t *lock; +}; + +/* GPT clk */ +struct gpt_rate_tbl { + u16 mscale; + u16 nscale; +}; + +struct clk_gpt { + struct clk_hw hw; + void __iomem *reg; + struct gpt_rate_tbl *rtbl; + u8 rtbl_cnt; + spinlock_t *lock; +}; + +/* VCO-PLL clk */ +struct pll_rate_tbl { + u8 mode; + u16 m; + u8 n; + u8 p; +}; + +struct clk_vco { + struct clk_hw hw; + void __iomem *mode_reg; + void __iomem *cfg_reg; + struct pll_rate_tbl *rtbl; + u8 rtbl_cnt; + spinlock_t *lock; +}; + +struct clk_pll { + struct clk_hw hw; + struct clk_vco *vco; + const char *parent[1]; + spinlock_t *lock; +}; + +typedef unsigned long (*clk_calc_rate)(struct clk_hw *hw, unsigned long prate, + int index); + +/* clk register routines */ +struct clk *clk_register_aux(const char *aux_name, const char *gate_name, + const char *parent_name, unsigned long flags, void __iomem *reg, + struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, + u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk); +struct clk *clk_register_frac(const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, + struct frac_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock); +struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned + long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8 + rtbl_cnt, spinlock_t *lock); +struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name, + const char *vco_gate_name, const char *parent_name, + unsigned long flags, void __iomem *mode_reg, void __iomem + *cfg_reg, struct pll_rate_tbl *rtbl, u8 rtbl_cnt, + spinlock_t *lock, struct clk **pll_clk, + struct clk **vco_gate_clk); + +long clk_round_rate_index(struct clk_hw *hw, unsigned long drate, + unsigned long parent_rate, clk_calc_rate calc_rate, u8 rtbl_cnt, + int *index); + +#endif /* __SPEAR_CLK_H */ diff --git a/drivers/clk/spear/spear3xx_clock.c b/drivers/clk/spear/spear3xx_clock.c new file mode 100644 index 000000000000..440bb3e4c971 --- /dev/null +++ b/drivers/clk/spear/spear3xx_clock.c @@ -0,0 +1,612 @@ +/* + * SPEAr3xx machines clock framework source file + * + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of_platform.h> +#include <linux/spinlock_types.h> +#include <mach/misc_regs.h> +#include "clk.h" + +static DEFINE_SPINLOCK(_lock); + +#define PLL1_CTR (MISC_BASE + 0x008) +#define PLL1_FRQ (MISC_BASE + 0x00C) +#define PLL2_CTR (MISC_BASE + 0x014) +#define PLL2_FRQ (MISC_BASE + 0x018) +#define PLL_CLK_CFG (MISC_BASE + 0x020) + /* PLL_CLK_CFG register masks */ + #define MCTR_CLK_SHIFT 28 + #define MCTR_CLK_MASK 3 + +#define CORE_CLK_CFG (MISC_BASE + 0x024) + /* CORE CLK CFG register masks */ + #define GEN_SYNTH2_3_CLK_SHIFT 18 + #define GEN_SYNTH2_3_CLK_MASK 1 + + #define HCLK_RATIO_SHIFT 10 + #define HCLK_RATIO_MASK 2 + #define PCLK_RATIO_SHIFT 8 + #define PCLK_RATIO_MASK 2 + +#define PERIP_CLK_CFG (MISC_BASE + 0x028) + /* PERIP_CLK_CFG register masks */ + #define UART_CLK_SHIFT 4 + #define UART_CLK_MASK 1 + #define FIRDA_CLK_SHIFT 5 + #define FIRDA_CLK_MASK 2 + #define GPT0_CLK_SHIFT 8 + #define GPT1_CLK_SHIFT 11 + #define GPT2_CLK_SHIFT 12 + #define GPT_CLK_MASK 1 + +#define PERIP1_CLK_ENB (MISC_BASE + 0x02C) + /* PERIP1_CLK_ENB register masks */ + #define UART_CLK_ENB 3 + #define SSP_CLK_ENB 5 + #define I2C_CLK_ENB 7 + #define JPEG_CLK_ENB 8 + #define FIRDA_CLK_ENB 10 + #define GPT1_CLK_ENB 11 + #define GPT2_CLK_ENB 12 + #define ADC_CLK_ENB 15 + #define RTC_CLK_ENB 17 + #define GPIO_CLK_ENB 18 + #define DMA_CLK_ENB 19 + #define SMI_CLK_ENB 21 + #define GMAC_CLK_ENB 23 + #define USBD_CLK_ENB 24 + #define USBH_CLK_ENB 25 + #define C3_CLK_ENB 31 + +#define RAS_CLK_ENB (MISC_BASE + 0x034) + #define RAS_AHB_CLK_ENB 0 + #define RAS_PLL1_CLK_ENB 1 + #define RAS_APB_CLK_ENB 2 + #define RAS_32K_CLK_ENB 3 + #define RAS_24M_CLK_ENB 4 + #define RAS_48M_CLK_ENB 5 + #define RAS_PLL2_CLK_ENB 7 + #define RAS_SYNT0_CLK_ENB 8 + #define RAS_SYNT1_CLK_ENB 9 + #define RAS_SYNT2_CLK_ENB 10 + #define RAS_SYNT3_CLK_ENB 11 + +#define PRSC0_CLK_CFG (MISC_BASE + 0x044) +#define PRSC1_CLK_CFG (MISC_BASE + 0x048) +#define PRSC2_CLK_CFG (MISC_BASE + 0x04C) +#define AMEM_CLK_CFG (MISC_BASE + 0x050) + #define AMEM_CLK_ENB 0 + +#define CLCD_CLK_SYNT (MISC_BASE + 0x05C) +#define FIRDA_CLK_SYNT (MISC_BASE + 0x060) +#define UART_CLK_SYNT (MISC_BASE + 0x064) +#define GMAC_CLK_SYNT (MISC_BASE + 0x068) +#define GEN0_CLK_SYNT (MISC_BASE + 0x06C) +#define GEN1_CLK_SYNT (MISC_BASE + 0x070) +#define GEN2_CLK_SYNT (MISC_BASE + 0x074) +#define GEN3_CLK_SYNT (MISC_BASE + 0x078) + +/* pll rate configuration table, in ascending order of rates */ +static struct pll_rate_tbl pll_rtbl[] = { + {.mode = 0, .m = 0x53, .n = 0x0C, .p = 0x1}, /* vco 332 & pll 166 MHz */ + {.mode = 0, .m = 0x85, .n = 0x0C, .p = 0x1}, /* vco 532 & pll 266 MHz */ + {.mode = 0, .m = 0xA6, .n = 0x0C, .p = 0x1}, /* vco 664 & pll 332 MHz */ +}; + +/* aux rate configuration table, in ascending order of rates */ +static struct aux_rate_tbl aux_rtbl[] = { + /* For PLL1 = 332 MHz */ + {.xscale = 2, .yscale = 27, .eq = 0}, /* 12.296 MHz */ + {.xscale = 2, .yscale = 8, .eq = 0}, /* 41.5 MHz */ + {.xscale = 2, .yscale = 4, .eq = 0}, /* 83 MHz */ + {.xscale = 1, .yscale = 2, .eq = 1}, /* 166 MHz */ +}; + +/* gpt rate configuration table, in ascending order of rates */ +static struct gpt_rate_tbl gpt_rtbl[] = { + /* For pll1 = 332 MHz */ + {.mscale = 4, .nscale = 0}, /* 41.5 MHz */ + {.mscale = 2, .nscale = 0}, /* 55.3 MHz */ + {.mscale = 1, .nscale = 0}, /* 83 MHz */ +}; + +/* clock parents */ +static const char *uart0_parents[] = { "pll3_48m_clk", "uart_synth_gate_clk", }; +static const char *firda_parents[] = { "pll3_48m_clk", "firda_synth_gate_clk", +}; +static const char *gpt0_parents[] = { "pll3_48m_clk", "gpt0_synth_clk", }; +static const char *gpt1_parents[] = { "pll3_48m_clk", "gpt1_synth_clk", }; +static const char *gpt2_parents[] = { "pll3_48m_clk", "gpt2_synth_clk", }; +static const char *gen2_3_parents[] = { "pll1_clk", "pll2_clk", }; +static const char *ddr_parents[] = { "ahb_clk", "ahbmult2_clk", "none", + "pll2_clk", }; + +#ifdef CONFIG_MACH_SPEAR300 +static void __init spear300_clk_init(void) +{ + struct clk *clk; + + clk = clk_register_fixed_factor(NULL, "clcd_clk", "ras_pll3_48m_clk", 0, + 1, 1); + clk_register_clkdev(clk, NULL, "60000000.clcd"); + + clk = clk_register_fixed_factor(NULL, "fsmc_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "94000000.flash"); + + clk = clk_register_fixed_factor(NULL, "sdhci_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "70000000.sdhci"); + + clk = clk_register_fixed_factor(NULL, "gpio1_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a9000000.gpio"); + + clk = clk_register_fixed_factor(NULL, "kbd_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a0000000.kbd"); +} +#endif + +/* array of all spear 310 clock lookups */ +#ifdef CONFIG_MACH_SPEAR310 +static void __init spear310_clk_init(void) +{ + struct clk *clk; + + clk = clk_register_fixed_factor(NULL, "emi_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, "emi", NULL); + + clk = clk_register_fixed_factor(NULL, "fsmc_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "44000000.flash"); + + clk = clk_register_fixed_factor(NULL, "tdm_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "tdm"); + + clk = clk_register_fixed_factor(NULL, "uart1_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2000000.serial"); + + clk = clk_register_fixed_factor(NULL, "uart2_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2080000.serial"); + + clk = clk_register_fixed_factor(NULL, "uart3_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2100000.serial"); + + clk = clk_register_fixed_factor(NULL, "uart4_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2180000.serial"); + + clk = clk_register_fixed_factor(NULL, "uart5_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2200000.serial"); +} +#endif + +/* array of all spear 320 clock lookups */ +#ifdef CONFIG_MACH_SPEAR320 + #define SMII_PCLK_SHIFT 18 + #define SMII_PCLK_MASK 2 + #define SMII_PCLK_VAL_PAD 0x0 + #define SMII_PCLK_VAL_PLL2 0x1 + #define SMII_PCLK_VAL_SYNTH0 0x2 + #define SDHCI_PCLK_SHIFT 15 + #define SDHCI_PCLK_MASK 1 + #define SDHCI_PCLK_VAL_48M 0x0 + #define SDHCI_PCLK_VAL_SYNTH3 0x1 + #define I2S_REF_PCLK_SHIFT 8 + #define I2S_REF_PCLK_MASK 1 + #define I2S_REF_PCLK_SYNTH_VAL 0x1 + #define I2S_REF_PCLK_PLL2_VAL 0x0 + #define UART1_PCLK_SHIFT 6 + #define UART1_PCLK_MASK 1 + #define SPEAR320_UARTX_PCLK_VAL_SYNTH1 0x0 + #define SPEAR320_UARTX_PCLK_VAL_APB 0x1 + +static const char *i2s_ref_parents[] = { "ras_pll2_clk", + "ras_gen2_synth_gate_clk", }; +static const char *sdhci_parents[] = { "ras_pll3_48m_clk", + "ras_gen3_synth_gate_clk", +}; +static const char *smii0_parents[] = { "smii_125m_pad", "ras_pll2_clk", + "ras_gen0_synth_gate_clk", }; +static const char *uartx_parents[] = { "ras_gen1_synth_gate_clk", "ras_apb_clk", +}; + +static void __init spear320_clk_init(void) +{ + struct clk *clk; + + clk = clk_register_fixed_rate(NULL, "smii_125m_pad_clk", NULL, + CLK_IS_ROOT, 125000000); + clk_register_clkdev(clk, "smii_125m_pad", NULL); + + clk = clk_register_fixed_factor(NULL, "clcd_clk", "ras_pll3_48m_clk", 0, + 1, 1); + clk_register_clkdev(clk, NULL, "90000000.clcd"); + + clk = clk_register_fixed_factor(NULL, "emi_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, "emi", NULL); + + clk = clk_register_fixed_factor(NULL, "fsmc_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "4c000000.flash"); + + clk = clk_register_fixed_factor(NULL, "i2c1_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a7000000.i2c"); + + clk = clk_register_fixed_factor(NULL, "pwm_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, "pwm", NULL); + + clk = clk_register_fixed_factor(NULL, "ssp1_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a5000000.spi"); + + clk = clk_register_fixed_factor(NULL, "ssp2_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a6000000.spi"); + + clk = clk_register_fixed_factor(NULL, "can0_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "c_can_platform.0"); + + clk = clk_register_fixed_factor(NULL, "can1_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "c_can_platform.1"); + + clk = clk_register_fixed_factor(NULL, "i2s_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "i2s"); + + clk = clk_register_mux(NULL, "i2s_ref_clk", i2s_ref_parents, + ARRAY_SIZE(i2s_ref_parents), 0, SPEAR320_CONTROL_REG, + I2S_REF_PCLK_SHIFT, I2S_REF_PCLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "i2s_ref_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "i2s_sclk", "i2s_ref_clk", 0, 1, + 4); + clk_register_clkdev(clk, "i2s_sclk", NULL); + + clk = clk_register_mux(NULL, "rs485_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_RS485_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "a9300000.serial"); + + clk = clk_register_mux(NULL, "sdhci_clk", sdhci_parents, + ARRAY_SIZE(sdhci_parents), 0, SPEAR320_CONTROL_REG, + SDHCI_PCLK_SHIFT, SDHCI_PCLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "70000000.sdhci"); + + clk = clk_register_mux(NULL, "smii_pclk", smii0_parents, + ARRAY_SIZE(smii0_parents), 0, SPEAR320_CONTROL_REG, + SMII_PCLK_SHIFT, SMII_PCLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "smii_pclk"); + + clk = clk_register_fixed_factor(NULL, "smii_clk", "smii_pclk", 0, 1, 1); + clk_register_clkdev(clk, NULL, "smii"); + + clk = clk_register_mux(NULL, "uart1_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_CONTROL_REG, + UART1_PCLK_SHIFT, UART1_PCLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "a3000000.serial"); + + clk = clk_register_mux(NULL, "uart2_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART2_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "a4000000.serial"); + + clk = clk_register_mux(NULL, "uart3_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART3_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "a9100000.serial"); + + clk = clk_register_mux(NULL, "uart4_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART4_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "a9200000.serial"); + + clk = clk_register_mux(NULL, "uart5_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART5_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "60000000.serial"); + + clk = clk_register_mux(NULL, "uart6_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART6_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "60100000.serial"); +} +#endif + +void __init spear3xx_clk_init(void) +{ + struct clk *clk, *clk1; + + clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0); + clk_register_clkdev(clk, "apb_pclk", NULL); + + clk = clk_register_fixed_rate(NULL, "osc_32k_clk", NULL, CLK_IS_ROOT, + 32000); + clk_register_clkdev(clk, "osc_32k_clk", NULL); + + clk = clk_register_fixed_rate(NULL, "osc_24m_clk", NULL, CLK_IS_ROOT, + 24000000); + clk_register_clkdev(clk, "osc_24m_clk", NULL); + + /* clock derived from 32 KHz osc clk */ + clk = clk_register_gate(NULL, "rtc-spear", "osc_32k_clk", 0, + PERIP1_CLK_ENB, RTC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc900000.rtc"); + + /* clock derived from 24 MHz osc clk */ + clk = clk_register_fixed_rate(NULL, "pll3_48m_clk", "osc_24m_clk", 0, + 48000000); + clk_register_clkdev(clk, "pll3_48m_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "wdt_clk", "osc_24m_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "fc880000.wdt"); + + clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, + "osc_24m_clk", 0, PLL1_CTR, PLL1_FRQ, pll_rtbl, + ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL); + clk_register_clkdev(clk, "vco1_clk", NULL); + clk_register_clkdev(clk1, "pll1_clk", NULL); + + clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, + "osc_24m_clk", 0, PLL2_CTR, PLL2_FRQ, pll_rtbl, + ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL); + clk_register_clkdev(clk, "vco2_clk", NULL); + clk_register_clkdev(clk1, "pll2_clk", NULL); + + /* clock derived from pll1 clk */ + clk = clk_register_fixed_factor(NULL, "cpu_clk", "pll1_clk", 0, 1, 1); + clk_register_clkdev(clk, "cpu_clk", NULL); + + clk = clk_register_divider(NULL, "ahb_clk", "pll1_clk", + CLK_SET_RATE_PARENT, CORE_CLK_CFG, HCLK_RATIO_SHIFT, + HCLK_RATIO_MASK, 0, &_lock); + clk_register_clkdev(clk, "ahb_clk", NULL); + + clk = clk_register_aux("uart_synth_clk", "uart_synth_gate_clk", + "pll1_clk", 0, UART_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "uart_synth_clk", NULL); + clk_register_clkdev(clk1, "uart_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "uart0_mux_clk", uart0_parents, + ARRAY_SIZE(uart0_parents), 0, PERIP_CLK_CFG, + UART_CLK_SHIFT, UART_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "uart0_mux_clk", NULL); + + clk = clk_register_gate(NULL, "uart0", "uart0_mux_clk", 0, + PERIP1_CLK_ENB, UART_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0000000.serial"); + + clk = clk_register_aux("firda_synth_clk", "firda_synth_gate_clk", + "pll1_clk", 0, FIRDA_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "firda_synth_clk", NULL); + clk_register_clkdev(clk1, "firda_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "firda_mux_clk", firda_parents, + ARRAY_SIZE(firda_parents), 0, PERIP_CLK_CFG, + FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "firda_mux_clk", NULL); + + clk = clk_register_gate(NULL, "firda_clk", "firda_mux_clk", 0, + PERIP1_CLK_ENB, FIRDA_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "firda"); + + /* gpt clocks */ + clk_register_gpt("gpt0_synth_clk", "pll1_clk", 0, PRSC0_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk = clk_register_mux(NULL, "gpt0_clk", gpt0_parents, + ARRAY_SIZE(gpt0_parents), 0, PERIP_CLK_CFG, + GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt0"); + + clk_register_gpt("gpt1_synth_clk", "pll1_clk", 0, PRSC1_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk = clk_register_mux(NULL, "gpt1_mux_clk", gpt1_parents, + ARRAY_SIZE(gpt1_parents), 0, PERIP_CLK_CFG, + GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt1_mux_clk", NULL); + clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mux_clk", 0, + PERIP1_CLK_ENB, GPT1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt1"); + + clk_register_gpt("gpt2_synth_clk", "pll1_clk", 0, PRSC2_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk = clk_register_mux(NULL, "gpt2_mux_clk", gpt2_parents, + ARRAY_SIZE(gpt2_parents), 0, PERIP_CLK_CFG, + GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt2_mux_clk", NULL); + clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mux_clk", 0, + PERIP1_CLK_ENB, GPT2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt2"); + + /* general synths clocks */ + clk = clk_register_aux("gen0_synth_clk", "gen0_synth_gate_clk", + "pll1_clk", 0, GEN0_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "gen0_synth_clk", NULL); + clk_register_clkdev(clk1, "gen0_synth_gate_clk", NULL); + + clk = clk_register_aux("gen1_synth_clk", "gen1_synth_gate_clk", + "pll1_clk", 0, GEN1_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "gen1_synth_clk", NULL); + clk_register_clkdev(clk1, "gen1_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "gen2_3_parent_clk", gen2_3_parents, + ARRAY_SIZE(gen2_3_parents), 0, CORE_CLK_CFG, + GEN_SYNTH2_3_CLK_SHIFT, GEN_SYNTH2_3_CLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, "gen2_3_parent_clk", NULL); + + clk = clk_register_aux("gen2_synth_clk", "gen2_synth_gate_clk", + "gen2_3_parent_clk", 0, GEN2_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "gen2_synth_clk", NULL); + clk_register_clkdev(clk1, "gen2_synth_gate_clk", NULL); + + clk = clk_register_aux("gen3_synth_clk", "gen3_synth_gate_clk", + "gen2_3_parent_clk", 0, GEN3_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "gen3_synth_clk", NULL); + clk_register_clkdev(clk1, "gen3_synth_gate_clk", NULL); + + /* clock derived from pll3 clk */ + clk = clk_register_gate(NULL, "usbh_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBH_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "usbh_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "usbh.0_clk", "usbh_clk", 0, 1, + 1); + clk_register_clkdev(clk, "usbh.0_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "usbh.1_clk", "usbh_clk", 0, 1, + 1); + clk_register_clkdev(clk, "usbh.1_clk", NULL); + + clk = clk_register_gate(NULL, "usbd_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBD_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "designware_udc"); + + /* clock derived from ahb clk */ + clk = clk_register_fixed_factor(NULL, "ahbmult2_clk", "ahb_clk", 0, 2, + 1); + clk_register_clkdev(clk, "ahbmult2_clk", NULL); + + clk = clk_register_mux(NULL, "ddr_clk", ddr_parents, + ARRAY_SIZE(ddr_parents), 0, PLL_CLK_CFG, MCTR_CLK_SHIFT, + MCTR_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "ddr_clk", NULL); + + clk = clk_register_divider(NULL, "apb_clk", "ahb_clk", + CLK_SET_RATE_PARENT, CORE_CLK_CFG, PCLK_RATIO_SHIFT, + PCLK_RATIO_MASK, 0, &_lock); + clk_register_clkdev(clk, "apb_clk", NULL); + + clk = clk_register_gate(NULL, "amem_clk", "ahb_clk", 0, AMEM_CLK_CFG, + AMEM_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "amem_clk", NULL); + + clk = clk_register_gate(NULL, "c3_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + C3_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "c3_clk"); + + clk = clk_register_gate(NULL, "dma_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + DMA_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc400000.dma"); + + clk = clk_register_gate(NULL, "gmac_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + GMAC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "e0800000.eth"); + + clk = clk_register_gate(NULL, "i2c0_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + I2C_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0180000.i2c"); + + clk = clk_register_gate(NULL, "jpeg_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + JPEG_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "jpeg"); + + clk = clk_register_gate(NULL, "smi_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + SMI_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc000000.flash"); + + /* clock derived from apb clk */ + clk = clk_register_gate(NULL, "adc_clk", "apb_clk", 0, PERIP1_CLK_ENB, + ADC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "adc"); + + clk = clk_register_gate(NULL, "gpio0_clk", "apb_clk", 0, PERIP1_CLK_ENB, + GPIO_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc980000.gpio"); + + clk = clk_register_gate(NULL, "ssp0_clk", "apb_clk", 0, PERIP1_CLK_ENB, + SSP_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0100000.spi"); + + /* RAS clk enable */ + clk = clk_register_gate(NULL, "ras_ahb_clk", "ahb_clk", 0, RAS_CLK_ENB, + RAS_AHB_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_ahb_clk", NULL); + + clk = clk_register_gate(NULL, "ras_apb_clk", "apb_clk", 0, RAS_CLK_ENB, + RAS_APB_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_apb_clk", NULL); + + clk = clk_register_gate(NULL, "ras_32k_clk", "osc_32k_clk", 0, + RAS_CLK_ENB, RAS_32K_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_32k_clk", NULL); + + clk = clk_register_gate(NULL, "ras_24m_clk", "osc_24m_clk", 0, + RAS_CLK_ENB, RAS_24M_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_24m_clk", NULL); + + clk = clk_register_gate(NULL, "ras_pll1_clk", "pll1_clk", 0, + RAS_CLK_ENB, RAS_PLL1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_pll1_clk", NULL); + + clk = clk_register_gate(NULL, "ras_pll2_clk", "pll2_clk", 0, + RAS_CLK_ENB, RAS_PLL2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_pll2_clk", NULL); + + clk = clk_register_gate(NULL, "ras_pll3_48m_clk", "pll3_48m_clk", 0, + RAS_CLK_ENB, RAS_48M_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_pll3_48m_clk", NULL); + + clk = clk_register_gate(NULL, "ras_gen0_synth_gate_clk", + "gen0_synth_gate_clk", 0, RAS_CLK_ENB, + RAS_SYNT0_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_gen0_synth_gate_clk", NULL); + + clk = clk_register_gate(NULL, "ras_gen1_synth_gate_clk", + "gen1_synth_gate_clk", 0, RAS_CLK_ENB, + RAS_SYNT1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_gen1_synth_gate_clk", NULL); + + clk = clk_register_gate(NULL, "ras_gen2_synth_gate_clk", + "gen2_synth_gate_clk", 0, RAS_CLK_ENB, + RAS_SYNT2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_gen2_synth_gate_clk", NULL); + + clk = clk_register_gate(NULL, "ras_gen3_synth_gate_clk", + "gen3_synth_gate_clk", 0, RAS_CLK_ENB, + RAS_SYNT3_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_gen3_synth_gate_clk", NULL); + + if (of_machine_is_compatible("st,spear300")) + spear300_clk_init(); + else if (of_machine_is_compatible("st,spear310")) + spear310_clk_init(); + else if (of_machine_is_compatible("st,spear320")) + spear320_clk_init(); +} diff --git a/drivers/clk/spear/spear6xx_clock.c b/drivers/clk/spear/spear6xx_clock.c new file mode 100644 index 000000000000..f9a20b382304 --- /dev/null +++ b/drivers/clk/spear/spear6xx_clock.c @@ -0,0 +1,342 @@ +/* + * SPEAr6xx machines clock framework source file + * + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar <viresh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/io.h> +#include <linux/spinlock_types.h> +#include <mach/misc_regs.h> +#include "clk.h" + +static DEFINE_SPINLOCK(_lock); + +#define PLL1_CTR (MISC_BASE + 0x008) +#define PLL1_FRQ (MISC_BASE + 0x00C) +#define PLL2_CTR (MISC_BASE + 0x014) +#define PLL2_FRQ (MISC_BASE + 0x018) +#define PLL_CLK_CFG (MISC_BASE + 0x020) + /* PLL_CLK_CFG register masks */ + #define MCTR_CLK_SHIFT 28 + #define MCTR_CLK_MASK 3 + +#define CORE_CLK_CFG (MISC_BASE + 0x024) + /* CORE CLK CFG register masks */ + #define HCLK_RATIO_SHIFT 10 + #define HCLK_RATIO_MASK 2 + #define PCLK_RATIO_SHIFT 8 + #define PCLK_RATIO_MASK 2 + +#define PERIP_CLK_CFG (MISC_BASE + 0x028) + /* PERIP_CLK_CFG register masks */ + #define CLCD_CLK_SHIFT 2 + #define CLCD_CLK_MASK 2 + #define UART_CLK_SHIFT 4 + #define UART_CLK_MASK 1 + #define FIRDA_CLK_SHIFT 5 + #define FIRDA_CLK_MASK 2 + #define GPT0_CLK_SHIFT 8 + #define GPT1_CLK_SHIFT 10 + #define GPT2_CLK_SHIFT 11 + #define GPT3_CLK_SHIFT 12 + #define GPT_CLK_MASK 1 + +#define PERIP1_CLK_ENB (MISC_BASE + 0x02C) + /* PERIP1_CLK_ENB register masks */ + #define UART0_CLK_ENB 3 + #define UART1_CLK_ENB 4 + #define SSP0_CLK_ENB 5 + #define SSP1_CLK_ENB 6 + #define I2C_CLK_ENB 7 + #define JPEG_CLK_ENB 8 + #define FSMC_CLK_ENB 9 + #define FIRDA_CLK_ENB 10 + #define GPT2_CLK_ENB 11 + #define GPT3_CLK_ENB 12 + #define GPIO2_CLK_ENB 13 + #define SSP2_CLK_ENB 14 + #define ADC_CLK_ENB 15 + #define GPT1_CLK_ENB 11 + #define RTC_CLK_ENB 17 + #define GPIO1_CLK_ENB 18 + #define DMA_CLK_ENB 19 + #define SMI_CLK_ENB 21 + #define CLCD_CLK_ENB 22 + #define GMAC_CLK_ENB 23 + #define USBD_CLK_ENB 24 + #define USBH0_CLK_ENB 25 + #define USBH1_CLK_ENB 26 + +#define PRSC0_CLK_CFG (MISC_BASE + 0x044) +#define PRSC1_CLK_CFG (MISC_BASE + 0x048) +#define PRSC2_CLK_CFG (MISC_BASE + 0x04C) + +#define CLCD_CLK_SYNT (MISC_BASE + 0x05C) +#define FIRDA_CLK_SYNT (MISC_BASE + 0x060) +#define UART_CLK_SYNT (MISC_BASE + 0x064) + +/* vco rate configuration table, in ascending order of rates */ +static struct pll_rate_tbl pll_rtbl[] = { + {.mode = 0, .m = 0x53, .n = 0x0F, .p = 0x1}, /* vco 332 & pll 166 MHz */ + {.mode = 0, .m = 0x85, .n = 0x0F, .p = 0x1}, /* vco 532 & pll 266 MHz */ + {.mode = 0, .m = 0xA6, .n = 0x0F, .p = 0x1}, /* vco 664 & pll 332 MHz */ +}; + +/* aux rate configuration table, in ascending order of rates */ +static struct aux_rate_tbl aux_rtbl[] = { + /* For PLL1 = 332 MHz */ + {.xscale = 2, .yscale = 8, .eq = 0}, /* 41.5 MHz */ + {.xscale = 2, .yscale = 4, .eq = 0}, /* 83 MHz */ + {.xscale = 1, .yscale = 2, .eq = 1}, /* 166 MHz */ +}; + +static const char *clcd_parents[] = { "pll3_48m_clk", "clcd_synth_gate_clk", }; +static const char *firda_parents[] = { "pll3_48m_clk", "firda_synth_gate_clk", +}; +static const char *uart_parents[] = { "pll3_48m_clk", "uart_synth_gate_clk", }; +static const char *gpt0_1_parents[] = { "pll3_48m_clk", "gpt0_1_synth_clk", }; +static const char *gpt2_parents[] = { "pll3_48m_clk", "gpt2_synth_clk", }; +static const char *gpt3_parents[] = { "pll3_48m_clk", "gpt3_synth_clk", }; +static const char *ddr_parents[] = { "ahb_clk", "ahbmult2_clk", "none", + "pll2_clk", }; + +/* gpt rate configuration table, in ascending order of rates */ +static struct gpt_rate_tbl gpt_rtbl[] = { + /* For pll1 = 332 MHz */ + {.mscale = 4, .nscale = 0}, /* 41.5 MHz */ + {.mscale = 2, .nscale = 0}, /* 55.3 MHz */ + {.mscale = 1, .nscale = 0}, /* 83 MHz */ +}; + +void __init spear6xx_clk_init(void) +{ + struct clk *clk, *clk1; + + clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0); + clk_register_clkdev(clk, "apb_pclk", NULL); + + clk = clk_register_fixed_rate(NULL, "osc_32k_clk", NULL, CLK_IS_ROOT, + 32000); + clk_register_clkdev(clk, "osc_32k_clk", NULL); + + clk = clk_register_fixed_rate(NULL, "osc_30m_clk", NULL, CLK_IS_ROOT, + 30000000); + clk_register_clkdev(clk, "osc_30m_clk", NULL); + + /* clock derived from 32 KHz osc clk */ + clk = clk_register_gate(NULL, "rtc_spear", "osc_32k_clk", 0, + PERIP1_CLK_ENB, RTC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "rtc-spear"); + + /* clock derived from 30 MHz osc clk */ + clk = clk_register_fixed_rate(NULL, "pll3_48m_clk", "osc_24m_clk", 0, + 48000000); + clk_register_clkdev(clk, "pll3_48m_clk", NULL); + + clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, "osc_30m_clk", + 0, PLL1_CTR, PLL1_FRQ, pll_rtbl, ARRAY_SIZE(pll_rtbl), + &_lock, &clk1, NULL); + clk_register_clkdev(clk, "vco1_clk", NULL); + clk_register_clkdev(clk1, "pll1_clk", NULL); + + clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, + "osc_30m_clk", 0, PLL2_CTR, PLL2_FRQ, pll_rtbl, + ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL); + clk_register_clkdev(clk, "vco2_clk", NULL); + clk_register_clkdev(clk1, "pll2_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "wdt_clk", "osc_30m_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "wdt"); + + /* clock derived from pll1 clk */ + clk = clk_register_fixed_factor(NULL, "cpu_clk", "pll1_clk", 0, 1, 1); + clk_register_clkdev(clk, "cpu_clk", NULL); + + clk = clk_register_divider(NULL, "ahb_clk", "pll1_clk", + CLK_SET_RATE_PARENT, CORE_CLK_CFG, HCLK_RATIO_SHIFT, + HCLK_RATIO_MASK, 0, &_lock); + clk_register_clkdev(clk, "ahb_clk", NULL); + + clk = clk_register_aux("uart_synth_clk", "uart_synth_gate_clk", + "pll1_clk", 0, UART_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "uart_synth_clk", NULL); + clk_register_clkdev(clk1, "uart_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "uart_mux_clk", uart_parents, + ARRAY_SIZE(uart_parents), 0, PERIP_CLK_CFG, + UART_CLK_SHIFT, UART_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "uart_mux_clk", NULL); + + clk = clk_register_gate(NULL, "uart0", "uart_mux_clk", 0, + PERIP1_CLK_ENB, UART0_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0000000.serial"); + + clk = clk_register_gate(NULL, "uart1", "uart_mux_clk", 0, + PERIP1_CLK_ENB, UART1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0080000.serial"); + + clk = clk_register_aux("firda_synth_clk", "firda_synth_gate_clk", + "pll1_clk", 0, FIRDA_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "firda_synth_clk", NULL); + clk_register_clkdev(clk1, "firda_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "firda_mux_clk", firda_parents, + ARRAY_SIZE(firda_parents), 0, PERIP_CLK_CFG, + FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "firda_mux_clk", NULL); + + clk = clk_register_gate(NULL, "firda_clk", "firda_mux_clk", 0, + PERIP1_CLK_ENB, FIRDA_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "firda"); + + clk = clk_register_aux("clcd_synth_clk", "clcd_synth_gate_clk", + "pll1_clk", 0, CLCD_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "clcd_synth_clk", NULL); + clk_register_clkdev(clk1, "clcd_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "clcd_mux_clk", clcd_parents, + ARRAY_SIZE(clcd_parents), 0, PERIP_CLK_CFG, + CLCD_CLK_SHIFT, CLCD_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "clcd_mux_clk", NULL); + + clk = clk_register_gate(NULL, "clcd_clk", "clcd_mux_clk", 0, + PERIP1_CLK_ENB, CLCD_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "clcd"); + + /* gpt clocks */ + clk = clk_register_gpt("gpt0_1_synth_clk", "pll1_clk", 0, PRSC0_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk_register_clkdev(clk, "gpt0_1_synth_clk", NULL); + + clk = clk_register_mux(NULL, "gpt0_mux_clk", gpt0_1_parents, + ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG, + GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt0"); + + clk = clk_register_mux(NULL, "gpt1_mux_clk", gpt0_1_parents, + ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG, + GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt1_mux_clk", NULL); + + clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mux_clk", 0, + PERIP1_CLK_ENB, GPT1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt1"); + + clk = clk_register_gpt("gpt2_synth_clk", "pll1_clk", 0, PRSC1_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk_register_clkdev(clk, "gpt2_synth_clk", NULL); + + clk = clk_register_mux(NULL, "gpt2_mux_clk", gpt2_parents, + ARRAY_SIZE(gpt2_parents), 0, PERIP_CLK_CFG, + GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt2_mux_clk", NULL); + + clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mux_clk", 0, + PERIP1_CLK_ENB, GPT2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt2"); + + clk = clk_register_gpt("gpt3_synth_clk", "pll1_clk", 0, PRSC2_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk_register_clkdev(clk, "gpt3_synth_clk", NULL); + + clk = clk_register_mux(NULL, "gpt3_mux_clk", gpt3_parents, + ARRAY_SIZE(gpt3_parents), 0, PERIP_CLK_CFG, + GPT3_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt3_mux_clk", NULL); + + clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mux_clk", 0, + PERIP1_CLK_ENB, GPT3_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt3"); + + /* clock derived from pll3 clk */ + clk = clk_register_gate(NULL, "usbh0_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBH0_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "usbh.0_clk"); + + clk = clk_register_gate(NULL, "usbh1_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBH1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "usbh.1_clk"); + + clk = clk_register_gate(NULL, "usbd_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBD_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "designware_udc"); + + /* clock derived from ahb clk */ + clk = clk_register_fixed_factor(NULL, "ahbmult2_clk", "ahb_clk", 0, 2, + 1); + clk_register_clkdev(clk, "ahbmult2_clk", NULL); + + clk = clk_register_mux(NULL, "ddr_clk", ddr_parents, + ARRAY_SIZE(ddr_parents), + 0, PLL_CLK_CFG, MCTR_CLK_SHIFT, MCTR_CLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, "ddr_clk", NULL); + + clk = clk_register_divider(NULL, "apb_clk", "ahb_clk", + CLK_SET_RATE_PARENT, CORE_CLK_CFG, PCLK_RATIO_SHIFT, + PCLK_RATIO_MASK, 0, &_lock); + clk_register_clkdev(clk, "apb_clk", NULL); + + clk = clk_register_gate(NULL, "dma_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + DMA_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc400000.dma"); + + clk = clk_register_gate(NULL, "fsmc_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + FSMC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d1800000.flash"); + + clk = clk_register_gate(NULL, "gmac_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + GMAC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gmac"); + + clk = clk_register_gate(NULL, "i2c_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + I2C_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0200000.i2c"); + + clk = clk_register_gate(NULL, "jpeg_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + JPEG_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "jpeg"); + + clk = clk_register_gate(NULL, "smi_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + SMI_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc000000.flash"); + + /* clock derived from apb clk */ + clk = clk_register_gate(NULL, "adc_clk", "apb_clk", 0, PERIP1_CLK_ENB, + ADC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "adc"); + + clk = clk_register_fixed_factor(NULL, "gpio0_clk", "apb_clk", 0, 1, 1); + clk_register_clkdev(clk, NULL, "f0100000.gpio"); + + clk = clk_register_gate(NULL, "gpio1_clk", "apb_clk", 0, PERIP1_CLK_ENB, + GPIO1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc980000.gpio"); + + clk = clk_register_gate(NULL, "gpio2_clk", "apb_clk", 0, PERIP1_CLK_ENB, + GPIO2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d8100000.gpio"); + + clk = clk_register_gate(NULL, "ssp0_clk", "apb_clk", 0, PERIP1_CLK_ENB, + SSP0_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "ssp-pl022.0"); + + clk = clk_register_gate(NULL, "ssp1_clk", "apb_clk", 0, PERIP1_CLK_ENB, + SSP1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "ssp-pl022.1"); + + clk = clk_register_gate(NULL, "ssp2_clk", "apb_clk", 0, PERIP1_CLK_ENB, + SSP2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "ssp-pl022.2"); +} diff --git a/drivers/crypto/mv_cesa.c b/drivers/crypto/mv_cesa.c index e6ecc5f23943..1cc6b3f3e262 100644 --- a/drivers/crypto/mv_cesa.c +++ b/drivers/crypto/mv_cesa.c @@ -16,6 +16,7 @@ #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/clk.h> #include <crypto/internal/hash.h> #include <crypto/sha.h> @@ -79,6 +80,7 @@ struct crypto_priv { void __iomem *reg; void __iomem *sram; int irq; + struct clk *clk; struct task_struct *queue_th; /* the lock protects queue and eng_st */ @@ -1053,6 +1055,12 @@ static int mv_probe(struct platform_device *pdev) if (ret) goto err_thread; + /* Not all platforms can gate the clock, so it is not + an error if the clock does not exists. */ + cp->clk = clk_get(&pdev->dev, NULL); + if (!IS_ERR(cp->clk)) + clk_prepare_enable(cp->clk); + writel(SEC_INT_ACCEL0_DONE, cpg->reg + SEC_ACCEL_INT_MASK); writel(SEC_CFG_STOP_DIG_ERR, cpg->reg + SEC_ACCEL_CFG); writel(SRAM_CONFIG, cpg->reg + SEC_ACCEL_DESC_P0); @@ -1118,6 +1126,12 @@ static int mv_remove(struct platform_device *pdev) memset(cp->sram, 0, cp->sram_size); iounmap(cp->sram); iounmap(cp->reg); + + if (!IS_ERR(cp->clk)) { + clk_disable_unprepare(cp->clk); + clk_put(cp->clk); + } + kfree(cp); cpg = NULL; return 0; diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index a472a29d8497..fb4f4990f5eb 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -323,7 +323,8 @@ struct sdma_engine { struct sdma_context_data *context; dma_addr_t context_phys; struct dma_device dma_device; - struct clk *clk; + struct clk *clk_ipg; + struct clk *clk_ahb; spinlock_t channel_0_lock; struct sdma_script_start_addrs *script_addrs; }; @@ -867,7 +868,8 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan) sdmac->peripheral_type = data->peripheral_type; sdmac->event_id0 = data->dma_request; - clk_enable(sdmac->sdma->clk); + clk_enable(sdmac->sdma->clk_ipg); + clk_enable(sdmac->sdma->clk_ahb); ret = sdma_request_channel(sdmac); if (ret) @@ -904,7 +906,8 @@ static void sdma_free_chan_resources(struct dma_chan *chan) dma_free_coherent(NULL, PAGE_SIZE, sdmac->bd, sdmac->bd_phys); - clk_disable(sdma->clk); + clk_disable(sdma->clk_ipg); + clk_disable(sdma->clk_ahb); } static struct dma_async_tx_descriptor *sdma_prep_slave_sg( @@ -1177,12 +1180,14 @@ static void sdma_load_firmware(const struct firmware *fw, void *context) addr = (void *)header + header->script_addrs_start; ram_code = (void *)header + header->ram_code_start; - clk_enable(sdma->clk); + clk_enable(sdma->clk_ipg); + clk_enable(sdma->clk_ahb); /* download the RAM image for SDMA */ sdma_load_script(sdma, ram_code, header->ram_code_size, addr->ram_code_start_addr); - clk_disable(sdma->clk); + clk_disable(sdma->clk_ipg); + clk_disable(sdma->clk_ahb); sdma_add_scripts(sdma, addr); @@ -1224,7 +1229,8 @@ static int __init sdma_init(struct sdma_engine *sdma) return -ENODEV; } - clk_enable(sdma->clk); + clk_enable(sdma->clk_ipg); + clk_enable(sdma->clk_ahb); /* Be sure SDMA has not started yet */ writel_relaxed(0, sdma->regs + SDMA_H_C0PTR); @@ -1277,12 +1283,14 @@ static int __init sdma_init(struct sdma_engine *sdma) /* Initializes channel's priorities */ sdma_set_channel_priority(&sdma->channel[0], 7); - clk_disable(sdma->clk); + clk_disable(sdma->clk_ipg); + clk_disable(sdma->clk_ahb); return 0; err_dma_alloc: - clk_disable(sdma->clk); + clk_disable(sdma->clk_ipg); + clk_disable(sdma->clk_ahb); dev_err(sdma->dev, "initialisation failed with %d\n", ret); return ret; } @@ -1321,12 +1329,21 @@ static int __init sdma_probe(struct platform_device *pdev) goto err_request_region; } - sdma->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(sdma->clk)) { - ret = PTR_ERR(sdma->clk); + sdma->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(sdma->clk_ipg)) { + ret = PTR_ERR(sdma->clk_ipg); goto err_clk; } + sdma->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(sdma->clk_ahb)) { + ret = PTR_ERR(sdma->clk_ahb); + goto err_clk; + } + + clk_prepare(sdma->clk_ipg); + clk_prepare(sdma->clk_ahb); + sdma->regs = ioremap(iores->start, resource_size(iores)); if (!sdma->regs) { ret = -ENOMEM; @@ -1436,7 +1453,6 @@ err_alloc: err_request_irq: iounmap(sdma->regs); err_ioremap: - clk_put(sdma->clk); err_clk: release_mem_region(iores->start, resource_size(iores)); err_request_region: diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index 62e3f8ec2461..5ec72044ea4c 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -1715,7 +1715,7 @@ static int __init ipu_probe(struct platform_device *pdev) } /* Make sure IPU HSP clock is running */ - clk_enable(ipu_data.ipu_clk); + clk_prepare_enable(ipu_data.ipu_clk); /* Disable all interrupts */ idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_1); @@ -1747,7 +1747,7 @@ static int __init ipu_probe(struct platform_device *pdev) err_idmac_init: err_attach_irq: ipu_irq_detach_irq(&ipu_data, pdev); - clk_disable(ipu_data.ipu_clk); + clk_disable_unprepare(ipu_data.ipu_clk); clk_put(ipu_data.ipu_clk); err_clk_get: iounmap(ipu_data.reg_ic); @@ -1765,7 +1765,7 @@ static int __exit ipu_remove(struct platform_device *pdev) ipu_idmac_exit(ipu); ipu_irq_detach_irq(ipu, pdev); - clk_disable(ipu->ipu_clk); + clk_disable_unprepare(ipu->ipu_clk); clk_put(ipu->ipu_clk); iounmap(ipu->reg_ic); iounmap(ipu->reg_ipu); diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index fa5d55fea46c..0b12e68bf79c 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -25,6 +25,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/memory.h> +#include <linux/clk.h> #include <plat/mv_xor.h> #include "dmaengine.h" @@ -1307,11 +1308,25 @@ static int mv_xor_shared_probe(struct platform_device *pdev) if (dram) mv_xor_conf_mbus_windows(msp, dram); + /* Not all platforms can gate the clock, so it is not + * an error if the clock does not exists. + */ + msp->clk = clk_get(&pdev->dev, NULL); + if (!IS_ERR(msp->clk)) + clk_prepare_enable(msp->clk); + return 0; } static int mv_xor_shared_remove(struct platform_device *pdev) { + struct mv_xor_shared_private *msp = platform_get_drvdata(pdev); + + if (!IS_ERR(msp->clk)) { + clk_disable_unprepare(msp->clk); + clk_put(msp->clk); + } + return 0; } diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h index 654876b7ba1d..a5b422f5a8ab 100644 --- a/drivers/dma/mv_xor.h +++ b/drivers/dma/mv_xor.h @@ -55,6 +55,7 @@ struct mv_xor_shared_private { void __iomem *xor_base; void __iomem *xor_high_base; + struct clk *clk; }; diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index 02d54a057b60..f13643d31353 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -511,7 +511,7 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam, /* ipu_csi_init_interface() */ csi_reg_write(mx3_cam, conf, CSI_SENS_CONF); - clk_enable(mx3_cam->clk); + clk_prepare_enable(mx3_cam->clk); rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk); dev_dbg(icd->parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate); if (rate) @@ -552,7 +552,7 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd) *ichan = NULL; } - clk_disable(mx3_cam->clk); + clk_disable_unprepare(mx3_cam->clk); mx3_cam->icd = NULL; diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index eeb8cd125b0c..3b9136c1a475 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -19,6 +19,7 @@ #include <linux/dma-mapping.h> #include <linux/scatterlist.h> #include <linux/irq.h> +#include <linux/clk.h> #include <linux/gpio.h> #include <linux/mmc/host.h> @@ -51,6 +52,7 @@ struct mvsd_host { struct device *dev; struct resource *res; int irq; + struct clk *clk; int gpio_card_detect; int gpio_write_protect; }; @@ -770,6 +772,13 @@ static int __init mvsd_probe(struct platform_device *pdev) } else host->irq = irq; + /* Not all platforms can gate the clock, so it is not + an error if the clock does not exists. */ + host->clk = clk_get(&pdev->dev, NULL); + if (!IS_ERR(host->clk)) { + clk_prepare_enable(host->clk); + } + if (mvsd_data->gpio_card_detect) { ret = gpio_request(mvsd_data->gpio_card_detect, DRIVER_NAME " cd"); @@ -854,6 +863,11 @@ static int __exit mvsd_remove(struct platform_device *pdev) mvsd_power_down(host); iounmap(host->base); release_resource(host->res); + + if (!IS_ERR(host->clk)) { + clk_disable_unprepare(host->clk); + clk_put(host->clk); + } mmc_free_host(mmc); } platform_set_drvdata(pdev, NULL); diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index b2058b432320..28ed52d58f7f 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -136,7 +136,8 @@ struct mxcmci_host { u16 rev_no; unsigned int cmdat; - struct clk *clk; + struct clk *clk_ipg; + struct clk *clk_per; int clock; @@ -672,7 +673,7 @@ static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios) { unsigned int divider; int prescaler = 0; - unsigned int clk_in = clk_get_rate(host->clk); + unsigned int clk_in = clk_get_rate(host->clk_per); while (prescaler <= 0x800) { for (divider = 1; divider <= 0xF; divider++) { @@ -900,12 +901,20 @@ static int mxcmci_probe(struct platform_device *pdev) host->res = r; host->irq = irq; - host->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); + host->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(host->clk_ipg)) { + ret = PTR_ERR(host->clk_ipg); goto out_iounmap; } - clk_enable(host->clk); + + host->clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(host->clk_per)) { + ret = PTR_ERR(host->clk_per); + goto out_iounmap; + } + + clk_prepare_enable(host->clk_per); + clk_prepare_enable(host->clk_ipg); mxcmci_softreset(host); @@ -917,8 +926,8 @@ static int mxcmci_probe(struct platform_device *pdev) goto out_clk_put; } - mmc->f_min = clk_get_rate(host->clk) >> 16; - mmc->f_max = clk_get_rate(host->clk) >> 1; + mmc->f_min = clk_get_rate(host->clk_per) >> 16; + mmc->f_max = clk_get_rate(host->clk_per) >> 1; /* recommended in data sheet */ writew(0x2db4, host->base + MMC_REG_READ_TO); @@ -967,8 +976,8 @@ out_free_dma: if (host->dma) dma_release_channel(host->dma); out_clk_put: - clk_disable(host->clk); - clk_put(host->clk); + clk_disable_unprepare(host->clk_per); + clk_disable_unprepare(host->clk_ipg); out_iounmap: iounmap(host->base); out_free: @@ -999,8 +1008,8 @@ static int mxcmci_remove(struct platform_device *pdev) if (host->dma) dma_release_channel(host->dma); - clk_disable(host->clk); - clk_put(host->clk); + clk_disable_unprepare(host->clk_per); + clk_disable_unprepare(host->clk_ipg); release_mem_region(host->res->start, resource_size(host->res)); @@ -1018,7 +1027,8 @@ static int mxcmci_suspend(struct device *dev) if (mmc) ret = mmc_suspend_host(mmc); - clk_disable(host->clk); + clk_disable_unprepare(host->clk_per); + clk_disable_unprepare(host->clk_ipg); return ret; } @@ -1029,7 +1039,8 @@ static int mxcmci_resume(struct device *dev) struct mxcmci_host *host = mmc_priv(mmc); int ret = 0; - clk_enable(host->clk); + clk_prepare_enable(host->clk_per); + clk_prepare_enable(host->clk_ipg); if (mmc) ret = mmc_resume_host(mmc); diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 365b16c230f8..ebbe984e5d00 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -71,6 +71,9 @@ struct pltfm_imx_data { enum imx_esdhc_type devtype; struct pinctrl *pinctrl; struct esdhc_platform_data boarddata; + struct clk *clk_ipg; + struct clk *clk_ahb; + struct clk *clk_per; }; static struct platform_device_id imx_esdhc_devtype[] = { @@ -439,7 +442,6 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev) struct sdhci_pltfm_host *pltfm_host; struct sdhci_host *host; struct esdhc_platform_data *boarddata; - struct clk *clk; int err; struct pltfm_imx_data *imx_data; @@ -460,14 +462,29 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev) imx_data->devtype = pdev->id_entry->driver_data; pltfm_host->priv = imx_data; - clk = clk_get(mmc_dev(host->mmc), NULL); - if (IS_ERR(clk)) { - dev_err(mmc_dev(host->mmc), "clk err\n"); - err = PTR_ERR(clk); + imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(imx_data->clk_ipg)) { + err = PTR_ERR(imx_data->clk_ipg); goto err_clk_get; } - clk_prepare_enable(clk); - pltfm_host->clk = clk; + + imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(imx_data->clk_ahb)) { + err = PTR_ERR(imx_data->clk_ahb); + goto err_clk_get; + } + + imx_data->clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(imx_data->clk_per)) { + err = PTR_ERR(imx_data->clk_per); + goto err_clk_get; + } + + pltfm_host->clk = imx_data->clk_per; + + clk_prepare_enable(imx_data->clk_per); + clk_prepare_enable(imx_data->clk_ipg); + clk_prepare_enable(imx_data->clk_ahb); imx_data->pinctrl = devm_pinctrl_get_select_default(&pdev->dev); if (IS_ERR(imx_data->pinctrl)) { @@ -567,8 +584,9 @@ no_card_detect_irq: no_card_detect_pin: no_board_data: pin_err: - clk_disable_unprepare(pltfm_host->clk); - clk_put(pltfm_host->clk); + clk_disable_unprepare(imx_data->clk_per); + clk_disable_unprepare(imx_data->clk_ipg); + clk_disable_unprepare(imx_data->clk_ahb); err_clk_get: kfree(imx_data); err_imx_data: @@ -594,8 +612,10 @@ static int __devexit sdhci_esdhc_imx_remove(struct platform_device *pdev) gpio_free(boarddata->cd_gpio); } - clk_disable_unprepare(pltfm_host->clk); - clk_put(pltfm_host->clk); + clk_disable_unprepare(imx_data->clk_per); + clk_disable_unprepare(imx_data->clk_ipg); + clk_disable_unprepare(imx_data->clk_ahb); + kfree(imx_data); sdhci_pltfm_free(pdev); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index cc0678a967c1..9e374e9bd296 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -690,7 +690,7 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) if (chip == -1) { /* Disable the NFC clock */ if (host->clk_act) { - clk_disable(host->clk); + clk_disable_unprepare(host->clk); host->clk_act = 0; } return; @@ -698,7 +698,7 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) if (!host->clk_act) { /* Enable the NFC clock */ - clk_enable(host->clk); + clk_prepare_enable(host->clk); host->clk_act = 1; } @@ -1078,7 +1078,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto eclk; } - clk_enable(host->clk); + clk_prepare_enable(host->clk); host->clk_act = 1; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 0f50ef38b87b..513dc88a05ca 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -17,6 +17,8 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/clk.h> +#include <linux/err.h> #include <asm/io.h> #include <asm/sizes.h> #include <mach/hardware.h> @@ -79,6 +81,7 @@ static int __init orion_nand_probe(struct platform_device *pdev) struct nand_chip *nc; struct orion_nand_data *board; struct resource *res; + struct clk *clk; void __iomem *io_base; int ret = 0; u32 val = 0; @@ -155,6 +158,14 @@ static int __init orion_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mtd); + /* Not all platforms can gate the clock, so it is not + an error if the clock does not exists. */ + clk = clk_get(&pdev->dev, NULL); + if (!IS_ERR(clk)) { + clk_prepare_enable(clk); + clk_put(clk); + } + if (nand_scan(mtd, 1)) { ret = -ENXIO; goto no_dev; @@ -184,6 +195,7 @@ static int __devexit orion_nand_remove(struct platform_device *pdev) { struct mtd_info *mtd = platform_get_drvdata(pdev); struct nand_chip *nc = mtd->priv; + struct clk *clk; nand_release(mtd); @@ -191,6 +203,12 @@ static int __devexit orion_nand_remove(struct platform_device *pdev) kfree(nc); + clk = clk_get(&pdev->dev, NULL); + if (!IS_ERR(clk)) { + clk_disable_unprepare(clk); + clk_put(clk); + } + return 0; } diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index 8f2cf8c09e2d..ff7f4c5115a1 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -207,7 +207,8 @@ struct fec_enet_private { struct net_device *netdev; - struct clk *clk; + struct clk *clk_ipg; + struct clk *clk_ahb; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ unsigned char *tx_bounce[TX_RING_SIZE]; @@ -1065,7 +1066,7 @@ static int fec_enet_mii_init(struct platform_device *pdev) * Reference Manual has an error on this, and gets fixed on i.MX6Q * document. */ - fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), 5000000); + fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ahb), 5000000); if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) fep->phy_speed--; fep->phy_speed <<= 1; @@ -1618,12 +1619,20 @@ fec_probe(struct platform_device *pdev) goto failed_pin; } - fep->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(fep->clk)) { - ret = PTR_ERR(fep->clk); + fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(fep->clk_ipg)) { + ret = PTR_ERR(fep->clk_ipg); goto failed_clk; } - clk_prepare_enable(fep->clk); + + fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(fep->clk_ahb)) { + ret = PTR_ERR(fep->clk_ahb); + goto failed_clk; + } + + clk_prepare_enable(fep->clk_ahb); + clk_prepare_enable(fep->clk_ipg); ret = fec_enet_init(ndev); if (ret) @@ -1646,8 +1655,8 @@ failed_register: fec_enet_mii_remove(fep); failed_mii_init: failed_init: - clk_disable_unprepare(fep->clk); - clk_put(fep->clk); + clk_disable_unprepare(fep->clk_ahb); + clk_disable_unprepare(fep->clk_ipg); failed_pin: failed_clk: for (i = 0; i < FEC_IRQ_NUM; i++) { @@ -1680,8 +1689,8 @@ fec_drv_remove(struct platform_device *pdev) if (irq > 0) free_irq(irq, ndev); } - clk_disable_unprepare(fep->clk); - clk_put(fep->clk); + clk_disable_unprepare(fep->clk_ahb); + clk_disable_unprepare(fep->clk_ipg); iounmap(fep->hwp); free_netdev(ndev); @@ -1705,7 +1714,8 @@ fec_suspend(struct device *dev) fec_stop(ndev); netif_device_detach(ndev); } - clk_disable_unprepare(fep->clk); + clk_disable_unprepare(fep->clk_ahb); + clk_disable_unprepare(fep->clk_ipg); return 0; } @@ -1716,7 +1726,8 @@ fec_resume(struct device *dev) struct net_device *ndev = dev_get_drvdata(dev); struct fec_enet_private *fep = netdev_priv(ndev); - clk_prepare_enable(fep->clk); + clk_prepare_enable(fep->clk_ahb); + clk_prepare_enable(fep->clk_ipg); if (netif_running(ndev)) { fec_restart(ndev, fep->full_duplex); netif_device_attach(ndev); diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index c8950da60e6b..04d901d0ff63 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -57,6 +57,7 @@ #include <linux/types.h> #include <linux/inet_lro.h> #include <linux/slab.h> +#include <linux/clk.h> static char mv643xx_eth_driver_name[] = "mv643xx_eth"; static char mv643xx_eth_driver_version[] = "1.4"; @@ -289,10 +290,10 @@ struct mv643xx_eth_shared_private { /* * Hardware-specific parameters. */ - unsigned int t_clk; int extended_rx_coal_limit; int tx_bw_control; int tx_csum_limit; + }; #define TX_BW_CONTROL_ABSENT 0 @@ -431,6 +432,12 @@ struct mv643xx_eth_private { int tx_desc_sram_size; int txq_count; struct tx_queue txq[8]; + + /* + * Hardware-specific parameters. + */ + struct clk *clk; + unsigned int t_clk; }; @@ -1010,7 +1017,7 @@ static void tx_set_rate(struct mv643xx_eth_private *mp, int rate, int burst) int mtu; int bucket_size; - token_rate = ((rate / 1000) * 64) / (mp->shared->t_clk / 1000); + token_rate = ((rate / 1000) * 64) / (mp->t_clk / 1000); if (token_rate > 1023) token_rate = 1023; @@ -1042,7 +1049,7 @@ static void txq_set_rate(struct tx_queue *txq, int rate, int burst) int token_rate; int bucket_size; - token_rate = ((rate / 1000) * 64) / (mp->shared->t_clk / 1000); + token_rate = ((rate / 1000) * 64) / (mp->t_clk / 1000); if (token_rate > 1023) token_rate = 1023; @@ -1309,7 +1316,7 @@ static unsigned int get_rx_coal(struct mv643xx_eth_private *mp) temp = (val & 0x003fff00) >> 8; temp *= 64000000; - do_div(temp, mp->shared->t_clk); + do_div(temp, mp->t_clk); return (unsigned int)temp; } @@ -1319,7 +1326,7 @@ static void set_rx_coal(struct mv643xx_eth_private *mp, unsigned int usec) u64 temp; u32 val; - temp = (u64)usec * mp->shared->t_clk; + temp = (u64)usec * mp->t_clk; temp += 31999999; do_div(temp, 64000000); @@ -1345,7 +1352,7 @@ static unsigned int get_tx_coal(struct mv643xx_eth_private *mp) temp = (rdlp(mp, TX_FIFO_URGENT_THRESHOLD) & 0x3fff0) >> 4; temp *= 64000000; - do_div(temp, mp->shared->t_clk); + do_div(temp, mp->t_clk); return (unsigned int)temp; } @@ -1354,7 +1361,7 @@ static void set_tx_coal(struct mv643xx_eth_private *mp, unsigned int usec) { u64 temp; - temp = (u64)usec * mp->shared->t_clk; + temp = (u64)usec * mp->t_clk; temp += 31999999; do_div(temp, 64000000); @@ -2663,10 +2670,6 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) if (dram) mv643xx_eth_conf_mbus_windows(msp, dram); - /* - * Detect hardware parameters. - */ - msp->t_clk = (pd != NULL && pd->t_clk != 0) ? pd->t_clk : 133000000; msp->tx_csum_limit = (pd != NULL && pd->tx_csum_limit) ? pd->tx_csum_limit : 9 * 1024; infer_hw_params(msp); @@ -2891,6 +2894,18 @@ static int mv643xx_eth_probe(struct platform_device *pdev) mp->dev = dev; + /* + * Get the clk rate, if there is one, otherwise use the default. + */ + mp->clk = clk_get(&pdev->dev, (pdev->id ? "1" : "0")); + if (!IS_ERR(mp->clk)) { + clk_prepare_enable(mp->clk); + mp->t_clk = clk_get_rate(mp->clk); + } else { + mp->t_clk = 133000000; + printk(KERN_WARNING "Unable to get clock"); + } + set_params(mp, pd); netif_set_real_num_tx_queues(dev, mp->txq_count); netif_set_real_num_rx_queues(dev, mp->rxq_count); @@ -2979,6 +2994,11 @@ static int mv643xx_eth_remove(struct platform_device *pdev) if (mp->phy != NULL) phy_detach(mp->phy); cancel_work_sync(&mp->tx_timeout_task); + + if (!IS_ERR(mp->clk)) { + clk_disable_unprepare(mp->clk); + clk_put(mp->clk); + } free_netdev(mp->dev); platform_set_drvdata(pdev, NULL); diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c index d93a9608b1f0..891cd6c61d0a 100644 --- a/drivers/rtc/rtc-imxdi.c +++ b/drivers/rtc/rtc-imxdi.c @@ -405,7 +405,7 @@ static int dryice_rtc_probe(struct platform_device *pdev) imxdi->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(imxdi->clk)) return PTR_ERR(imxdi->clk); - clk_enable(imxdi->clk); + clk_prepare_enable(imxdi->clk); /* * Initialize dryice hardware @@ -470,7 +470,7 @@ static int dryice_rtc_probe(struct platform_device *pdev) return 0; err: - clk_disable(imxdi->clk); + clk_disable_unprepare(imxdi->clk); clk_put(imxdi->clk); return rc; @@ -487,7 +487,7 @@ static int __devexit dryice_rtc_remove(struct platform_device *pdev) rtc_device_unregister(imxdi->rtc); - clk_disable(imxdi->clk); + clk_disable_unprepare(imxdi->clk); clk_put(imxdi->clk); return 0; diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 69c9a6601f45..47877d687614 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -86,7 +86,8 @@ struct spi_imx_data { struct completion xfer_done; void __iomem *base; int irq; - struct clk *clk; + struct clk *clk_per; + struct clk *clk_ipg; unsigned long spi_clk; unsigned int count; @@ -853,15 +854,22 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) goto out_free_irq; } - spi_imx->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(spi_imx->clk)) { - dev_err(&pdev->dev, "unable to get clock\n"); - ret = PTR_ERR(spi_imx->clk); + spi_imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(spi_imx->clk_ipg)) { + ret = PTR_ERR(spi_imx->clk_ipg); goto out_free_irq; } - clk_enable(spi_imx->clk); - spi_imx->spi_clk = clk_get_rate(spi_imx->clk); + spi_imx->clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(spi_imx->clk_per)) { + ret = PTR_ERR(spi_imx->clk_per); + goto out_free_irq; + } + + clk_prepare_enable(spi_imx->clk_per); + clk_prepare_enable(spi_imx->clk_ipg); + + spi_imx->spi_clk = clk_get_rate(spi_imx->clk_per); spi_imx->devtype_data->reset(spi_imx); @@ -879,8 +887,8 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) return ret; out_clk_put: - clk_disable(spi_imx->clk); - clk_put(spi_imx->clk); + clk_disable_unprepare(spi_imx->clk_per); + clk_disable_unprepare(spi_imx->clk_ipg); out_free_irq: free_irq(spi_imx->irq, spi_imx); out_iounmap: @@ -908,8 +916,8 @@ static int __devexit spi_imx_remove(struct platform_device *pdev) spi_bitbang_stop(&spi_imx->bitbang); writel(0, spi_imx->base + MXC_CSPICTRL); - clk_disable(spi_imx->clk); - clk_put(spi_imx->clk); + clk_disable_unprepare(spi_imx->clk_per); + clk_disable_unprepare(spi_imx->clk_ipg); free_irq(spi_imx->irq, spi_imx); iounmap(spi_imx->base); diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index e496f799b7a9..dfd04e91fa6d 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -16,8 +16,8 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/spi/spi.h> -#include <linux/spi/orion_spi.h> #include <linux/module.h> +#include <linux/clk.h> #include <asm/unaligned.h> #define DRIVER_NAME "orion_spi" @@ -46,6 +46,7 @@ struct orion_spi { unsigned int max_speed; unsigned int min_speed; struct orion_spi_info *spi_info; + struct clk *clk; }; static struct workqueue_struct *orion_spi_wq; @@ -104,7 +105,7 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed) orion_spi = spi_master_get_devdata(spi->master); - tclk_hz = orion_spi->spi_info->tclk; + tclk_hz = clk_get_rate(orion_spi->clk); /* * the supported rates are: 4,6,8...30 @@ -450,6 +451,7 @@ static int __init orion_spi_probe(struct platform_device *pdev) struct orion_spi *spi; struct resource *r; struct orion_spi_info *spi_info; + unsigned long tclk_hz; int status = 0; spi_info = pdev->dev.platform_data; @@ -476,19 +478,28 @@ static int __init orion_spi_probe(struct platform_device *pdev) spi->master = master; spi->spi_info = spi_info; - spi->max_speed = DIV_ROUND_UP(spi_info->tclk, 4); - spi->min_speed = DIV_ROUND_UP(spi_info->tclk, 30); + spi->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(spi->clk)) { + status = PTR_ERR(spi->clk); + goto out; + } + + clk_prepare(spi->clk); + clk_enable(spi->clk); + tclk_hz = clk_get_rate(spi->clk); + spi->max_speed = DIV_ROUND_UP(tclk_hz, 4); + spi->min_speed = DIV_ROUND_UP(tclk_hz, 30); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { status = -ENODEV; - goto out; + goto out_rel_clk; } if (!request_mem_region(r->start, resource_size(r), dev_name(&pdev->dev))) { status = -EBUSY; - goto out; + goto out_rel_clk; } spi->base = ioremap(r->start, SZ_1K); @@ -508,7 +519,9 @@ static int __init orion_spi_probe(struct platform_device *pdev) out_rel_mem: release_mem_region(r->start, resource_size(r)); - +out_rel_clk: + clk_disable_unprepare(spi->clk); + clk_put(spi->clk); out: spi_master_put(master); return status; @@ -526,6 +539,9 @@ static int __exit orion_spi_remove(struct platform_device *pdev) cancel_work_sync(&spi->work); + clk_disable_unprepare(spi->clk); + clk_put(spi->clk); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(r->start, resource_size(r)); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index ec206732f68c..4ef747307ecb 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -205,7 +205,8 @@ struct imx_port { unsigned int irda_inv_rx:1; unsigned int irda_inv_tx:1; unsigned short trcv_delay; /* transceiver delay */ - struct clk *clk; + struct clk *clk_ipg; + struct clk *clk_per; struct imx_uart_data *devdata; }; @@ -673,7 +674,7 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode) * RFDIV is set such way to satisfy requested uartclk value */ val = TXTL << 10 | RXTL; - ufcr_rfdiv = (clk_get_rate(sport->clk) + sport->port.uartclk / 2) + ufcr_rfdiv = (clk_get_rate(sport->clk_per) + sport->port.uartclk / 2) / sport->port.uartclk; if(!ufcr_rfdiv) @@ -1286,7 +1287,7 @@ imx_console_get_options(struct imx_port *sport, int *baud, else ucfr_rfdiv = 6 - ucfr_rfdiv; - uartclk = clk_get_rate(sport->clk); + uartclk = clk_get_rate(sport->clk_per); uartclk /= ucfr_rfdiv; { /* @@ -1511,14 +1512,22 @@ static int serial_imx_probe(struct platform_device *pdev) goto unmap; } - sport->clk = clk_get(&pdev->dev, "uart"); - if (IS_ERR(sport->clk)) { - ret = PTR_ERR(sport->clk); + sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(sport->clk_ipg)) { + ret = PTR_ERR(sport->clk_ipg); goto unmap; } - clk_prepare_enable(sport->clk); - sport->port.uartclk = clk_get_rate(sport->clk); + sport->clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(sport->clk_per)) { + ret = PTR_ERR(sport->clk_per); + goto unmap; + } + + clk_prepare_enable(sport->clk_per); + clk_prepare_enable(sport->clk_ipg); + + sport->port.uartclk = clk_get_rate(sport->clk_per); imx_ports[sport->port.line] = sport; @@ -1539,8 +1548,8 @@ deinit: if (pdata && pdata->exit) pdata->exit(pdev); clkput: - clk_disable_unprepare(sport->clk); - clk_put(sport->clk); + clk_disable_unprepare(sport->clk_per); + clk_disable_unprepare(sport->clk_ipg); unmap: iounmap(sport->port.membase); free: @@ -1558,11 +1567,10 @@ static int serial_imx_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - if (sport) { - uart_remove_one_port(&imx_reg, &sport->port); - clk_disable_unprepare(sport->clk); - clk_put(sport->clk); - } + uart_remove_one_port(&imx_reg, &sport->port); + + clk_disable_unprepare(sport->clk_per); + clk_disable_unprepare(sport->clk_ipg); if (pdata && pdata->exit) pdata->exit(pdev); diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index a797d51ecbe8..c778ffe4e4e5 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -32,7 +32,7 @@ #define ULPI_VIEWPORT_OFFSET 0x170 struct ehci_mxc_priv { - struct clk *usbclk, *ahbclk, *phy1clk; + struct clk *usbclk, *ahbclk, *phyclk; struct usb_hcd *hcd; }; @@ -166,31 +166,26 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) } /* enable clocks */ - priv->usbclk = clk_get(dev, "usb"); + priv->usbclk = clk_get(dev, "ipg"); if (IS_ERR(priv->usbclk)) { ret = PTR_ERR(priv->usbclk); goto err_clk; } - clk_enable(priv->usbclk); + clk_prepare_enable(priv->usbclk); - if (!cpu_is_mx35() && !cpu_is_mx25()) { - priv->ahbclk = clk_get(dev, "usb_ahb"); - if (IS_ERR(priv->ahbclk)) { - ret = PTR_ERR(priv->ahbclk); - goto err_clk_ahb; - } - clk_enable(priv->ahbclk); + priv->ahbclk = clk_get(dev, "ahb"); + if (IS_ERR(priv->ahbclk)) { + ret = PTR_ERR(priv->ahbclk); + goto err_clk_ahb; } + clk_prepare_enable(priv->ahbclk); /* "dr" device has its own clock on i.MX51 */ - if (cpu_is_mx51() && (pdev->id == 0)) { - priv->phy1clk = clk_get(dev, "usb_phy1"); - if (IS_ERR(priv->phy1clk)) { - ret = PTR_ERR(priv->phy1clk); - goto err_clk_phy; - } - clk_enable(priv->phy1clk); - } + priv->phyclk = clk_get(dev, "phy"); + if (IS_ERR(priv->phyclk)) + priv->phyclk = NULL; + if (priv->phyclk) + clk_prepare_enable(priv->phyclk); /* call platform specific init function */ @@ -265,17 +260,15 @@ err_add: if (pdata && pdata->exit) pdata->exit(pdev); err_init: - if (priv->phy1clk) { - clk_disable(priv->phy1clk); - clk_put(priv->phy1clk); - } -err_clk_phy: - if (priv->ahbclk) { - clk_disable(priv->ahbclk); - clk_put(priv->ahbclk); + if (priv->phyclk) { + clk_disable_unprepare(priv->phyclk); + clk_put(priv->phyclk); } + + clk_disable_unprepare(priv->ahbclk); + clk_put(priv->ahbclk); err_clk_ahb: - clk_disable(priv->usbclk); + clk_disable_unprepare(priv->usbclk); clk_put(priv->usbclk); err_clk: iounmap(hcd->regs); @@ -307,15 +300,14 @@ static int __exit ehci_mxc_drv_remove(struct platform_device *pdev) usb_put_hcd(hcd); platform_set_drvdata(pdev, NULL); - clk_disable(priv->usbclk); + clk_disable_unprepare(priv->usbclk); clk_put(priv->usbclk); - if (priv->ahbclk) { - clk_disable(priv->ahbclk); - clk_put(priv->ahbclk); - } - if (priv->phy1clk) { - clk_disable(priv->phy1clk); - clk_put(priv->phy1clk); + clk_disable_unprepare(priv->ahbclk); + clk_put(priv->ahbclk); + + if (priv->phyclk) { + clk_disable_unprepare(priv->phyclk); + clk_put(priv->phyclk); } kfree(priv); diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 6c6a5a3b4ea7..82de1073aa52 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/mbus.h> +#include <linux/clk.h> #include <plat/ehci-orion.h> #define rdl(off) __raw_readl(hcd->regs + (off)) @@ -198,6 +199,7 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) struct resource *res; struct usb_hcd *hcd; struct ehci_hcd *ehci; + struct clk *clk; void __iomem *regs; int irq, err; @@ -238,6 +240,14 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) goto err2; } + /* Not all platforms can gate the clock, so it is not + an error if the clock does not exists. */ + clk = clk_get(&pdev->dev, NULL); + if (!IS_ERR(clk)) { + clk_prepare_enable(clk); + clk_put(clk); + } + hcd = usb_create_hcd(&ehci_orion_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { @@ -301,12 +311,18 @@ err1: static int __exit ehci_orion_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct clk *clk; usb_remove_hcd(hcd); iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); + clk = clk_get(&pdev->dev, NULL); + if (!IS_ERR(clk)) { + clk_disable_unprepare(clk); + clk_put(clk); + } return 0; } diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index f135dbead07d..caad3689b4e6 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -131,7 +131,9 @@ struct imxfb_rgb { struct imxfb_info { struct platform_device *pdev; void __iomem *regs; - struct clk *clk; + struct clk *clk_ipg; + struct clk *clk_ahb; + struct clk *clk_per; /* * These are the addresses we mapped @@ -340,7 +342,7 @@ static int imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel); - lcd_clk = clk_get_rate(fbi->clk); + lcd_clk = clk_get_rate(fbi->clk_per); tmp = var->pixclock * (unsigned long long)lcd_clk; @@ -455,11 +457,17 @@ static int imxfb_bl_update_status(struct backlight_device *bl) fbi->pwmr = (fbi->pwmr & ~0xFF) | brightness; - if (bl->props.fb_blank != FB_BLANK_UNBLANK) - clk_enable(fbi->clk); + if (bl->props.fb_blank != FB_BLANK_UNBLANK) { + clk_prepare_enable(fbi->clk_ipg); + clk_prepare_enable(fbi->clk_ahb); + clk_prepare_enable(fbi->clk_per); + } writel(fbi->pwmr, fbi->regs + LCDC_PWMR); - if (bl->props.fb_blank != FB_BLANK_UNBLANK) - clk_disable(fbi->clk); + if (bl->props.fb_blank != FB_BLANK_UNBLANK) { + clk_disable_unprepare(fbi->clk_per); + clk_disable_unprepare(fbi->clk_ahb); + clk_disable_unprepare(fbi->clk_ipg); + } return 0; } @@ -522,7 +530,9 @@ static void imxfb_enable_controller(struct imxfb_info *fbi) */ writel(RMCR_LCDC_EN_MX1, fbi->regs + LCDC_RMCR); - clk_enable(fbi->clk); + clk_prepare_enable(fbi->clk_ipg); + clk_prepare_enable(fbi->clk_ahb); + clk_prepare_enable(fbi->clk_per); if (fbi->backlight_power) fbi->backlight_power(1); @@ -539,7 +549,9 @@ static void imxfb_disable_controller(struct imxfb_info *fbi) if (fbi->lcd_power) fbi->lcd_power(0); - clk_disable(fbi->clk); + clk_disable_unprepare(fbi->clk_per); + clk_disable_unprepare(fbi->clk_ipg); + clk_disable_unprepare(fbi->clk_ahb); writel(0, fbi->regs + LCDC_RMCR); } @@ -770,10 +782,21 @@ static int __init imxfb_probe(struct platform_device *pdev) goto failed_req; } - fbi->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(fbi->clk)) { - ret = PTR_ERR(fbi->clk); - dev_err(&pdev->dev, "unable to get clock: %d\n", ret); + fbi->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(fbi->clk_ipg)) { + ret = PTR_ERR(fbi->clk_ipg); + goto failed_getclock; + } + + fbi->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(fbi->clk_ahb)) { + ret = PTR_ERR(fbi->clk_ahb); + goto failed_getclock; + } + + fbi->clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(fbi->clk_per)) { + ret = PTR_ERR(fbi->clk_per); goto failed_getclock; } @@ -858,7 +881,6 @@ failed_platform_init: failed_map: iounmap(fbi->regs); failed_ioremap: - clk_put(fbi->clk); failed_getclock: release_mem_region(res->start, resource_size(res)); failed_req: @@ -895,8 +917,6 @@ static int __devexit imxfb_remove(struct platform_device *pdev) iounmap(fbi->regs); release_mem_region(res->start, resource_size(res)); - clk_disable(fbi->clk); - clk_put(fbi->clk); platform_set_drvdata(pdev, NULL); diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c index a3b6a74c67a7..1cc61a700fa8 100644 --- a/drivers/w1/masters/mxc_w1.c +++ b/drivers/w1/masters/mxc_w1.c @@ -138,7 +138,7 @@ static int __devinit mxc_w1_probe(struct platform_device *pdev) goto failed_ioremap; } - clk_enable(mdev->clk); + clk_prepare_enable(mdev->clk); __raw_writeb(mdev->clkdiv, mdev->regs + MXC_W1_TIME_DIVIDER); mdev->bus_master.data = mdev; @@ -178,7 +178,7 @@ static int __devexit mxc_w1_remove(struct platform_device *pdev) iounmap(mdev->regs); release_mem_region(res->start, resource_size(res)); - clk_disable(mdev->clk); + clk_disable_unprepare(mdev->clk); clk_put(mdev->clk); platform_set_drvdata(pdev, NULL); diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 7a2b734fcdc7..bcfab2b00ad2 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -121,7 +121,7 @@ static void imx2_wdt_start(void) { if (!test_and_set_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) { /* at our first start we enable clock and do initialisations */ - clk_enable(imx2_wdt.clk); + clk_prepare_enable(imx2_wdt.clk); imx2_wdt_setup(); } else /* delete the timer that pings the watchdog after close */ diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index 788aa158e78c..0f5736949c61 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -24,8 +24,8 @@ #include <linux/uaccess.h> #include <linux/io.h> #include <linux/spinlock.h> +#include <linux/clk.h> #include <mach/bridge-regs.h> -#include <plat/orion_wdt.h> /* * Watchdog timer block registers. @@ -41,6 +41,7 @@ static bool nowayout = WATCHDOG_NOWAYOUT; static int heartbeat = -1; /* module parameter (seconds) */ static unsigned int wdt_max_duration; /* (seconds) */ +static struct clk *clk; static unsigned int wdt_tclk; static void __iomem *wdt_reg; static unsigned long wdt_status; @@ -237,16 +238,16 @@ static struct miscdevice orion_wdt_miscdev = { static int __devinit orion_wdt_probe(struct platform_device *pdev) { - struct orion_wdt_platform_data *pdata = pdev->dev.platform_data; struct resource *res; int ret; - if (pdata) { - wdt_tclk = pdata->tclk; - } else { - pr_err("misses platform data\n"); + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + printk(KERN_ERR "Orion Watchdog missing clock\n"); return -ENODEV; } + clk_prepare_enable(clk); + wdt_tclk = clk_get_rate(clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -282,6 +283,9 @@ static int __devexit orion_wdt_remove(struct platform_device *pdev) if (!ret) orion_wdt_miscdev.parent = NULL; + clk_disable_unprepare(clk); + clk_put(clk); + return ret; } |