diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-14 14:54:26 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-14 14:54:26 -0800 |
commit | c2714334b944abbeaaadda8cddde619eff0292a1 (patch) | |
tree | b45be97a313f58aa62933040230d51aa3a8592b4 /drivers/clk/mvebu/clk-gating-ctrl.c | |
parent | 0beb58783f2168354e2b5297af45fc7db70adf12 (diff) | |
parent | 5e5d8999a316d596f2012fe1cf4c59e0de693dab (diff) | |
download | kernel_goldelico_gta04-c2714334b944abbeaaadda8cddde619eff0292a1.zip kernel_goldelico_gta04-c2714334b944abbeaaadda8cddde619eff0292a1.tar.gz kernel_goldelico_gta04-c2714334b944abbeaaadda8cddde619eff0292a1.tar.bz2 |
Merge tag 'mvebu' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC updates for Marvell mvebu/kirkwood from Olof Johansson:
"This is a branch with updates for Marvell's mvebu/kirkwood platforms.
They came in late-ish, and were heavily interdependent such that it
didn't make sense to split them up across the cross-platform topic
branches. So here they are (for the second release in a row) in a
branch on their own."
* tag 'mvebu' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (88 commits)
arm: l2x0: add aurora related properties to OF binding
arm: mvebu: add Aurora L2 Cache Controller to the DT
arm: mvebu: add L2 cache support
dma: mv_xor: fix error handling path
dma: mv_xor: fix error checking of irq_of_parse_and_map()
dma: mv_xor: use request_irq() instead of devm_request_irq()
dma: mv_xor: clear the window override control registers
arm: mvebu: fix address decoding armada_cfg_base() function
ARM: mvebu: update defconfig with I2C and RTC support
ARM: mvebu: Add SATA support for OpenBlocks AX3-4
ARM: mvebu: Add support for the RTC in OpenBlocks AX3-4
ARM: mvebu: Add support for I2C on OpenBlocks AX3-4
ARM: mvebu: Add support for I2C controllers in Armada 370/XP
arm: mvebu: Add hardware I/O Coherency support
arm: plat-orion: Add coherency attribute when setup mbus target
arm: dma mapping: Export a dma ops function arm_dma_set_mask
arm: mvebu: Add SMP support for Armada XP
arm: mm: Add support for PJ4B cpu and init routines
arm: mvebu: Add IPI support via doorbells
arm: mvebu: Add initial support for power managmement service unit
...
Diffstat (limited to 'drivers/clk/mvebu/clk-gating-ctrl.c')
-rw-r--r-- | drivers/clk/mvebu/clk-gating-ctrl.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/drivers/clk/mvebu/clk-gating-ctrl.c b/drivers/clk/mvebu/clk-gating-ctrl.c new file mode 100644 index 0000000..c6d3c26 --- /dev/null +++ b/drivers/clk/mvebu/clk-gating-ctrl.c @@ -0,0 +1,249 @@ +/* + * Marvell MVEBU clock gating control. + * + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Andrew Lunn <andrew@lunn.ch> + * + * 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/kernel.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/clk/mvebu.h> +#include <linux/of.h> +#include <linux/of_address.h> + +struct mvebu_gating_ctrl { + spinlock_t lock; + struct clk **gates; + int num_gates; +}; + +struct mvebu_soc_descr { + const char *name; + const char *parent; + int bit_idx; +}; + +#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) + +static struct clk __init *mvebu_clk_gating_get_src( + struct of_phandle_args *clkspec, void *data) +{ + struct mvebu_gating_ctrl *ctrl = (struct mvebu_gating_ctrl *)data; + int n; + + if (clkspec->args_count < 1) + return ERR_PTR(-EINVAL); + + for (n = 0; n < ctrl->num_gates; n++) { + struct clk_gate *gate = + to_clk_gate(__clk_get_hw(ctrl->gates[n])); + if (clkspec->args[0] == gate->bit_idx) + return ctrl->gates[n]; + } + return ERR_PTR(-ENODEV); +} + +static void __init mvebu_clk_gating_setup( + struct device_node *np, const struct mvebu_soc_descr *descr) +{ + struct mvebu_gating_ctrl *ctrl; + struct clk *clk; + void __iomem *base; + const char *default_parent = NULL; + int n; + + base = of_iomap(np, 0); + + clk = of_clk_get(np, 0); + if (!IS_ERR(clk)) { + default_parent = __clk_get_name(clk); + clk_put(clk); + } + + ctrl = kzalloc(sizeof(struct mvebu_gating_ctrl), GFP_KERNEL); + if (WARN_ON(!ctrl)) + return; + + spin_lock_init(&ctrl->lock); + + /* + * Count, allocate, and register clock gates + */ + for (n = 0; descr[n].name;) + n++; + + ctrl->num_gates = n; + ctrl->gates = kzalloc(ctrl->num_gates * sizeof(struct clk *), + GFP_KERNEL); + if (WARN_ON(!ctrl->gates)) { + kfree(ctrl); + return; + } + + for (n = 0; n < ctrl->num_gates; n++) { + u8 flags = 0; + const char *parent = + (descr[n].parent) ? descr[n].parent : default_parent; + + /* + * On Armada 370, the DDR clock is a special case: it + * isn't taken by any driver, but should anyway be + * kept enabled, so we mark it as IGNORE_UNUSED for + * now. + */ + if (!strcmp(descr[n].name, "ddr")) + flags |= CLK_IGNORE_UNUSED; + + ctrl->gates[n] = clk_register_gate(NULL, descr[n].name, parent, + flags, base, descr[n].bit_idx, 0, &ctrl->lock); + WARN_ON(IS_ERR(ctrl->gates[n])); + } + of_clk_add_provider(np, mvebu_clk_gating_get_src, ctrl); +} + +/* + * SoC specific clock gating control + */ + +#ifdef CONFIG_MACH_ARMADA_370 +static const struct mvebu_soc_descr __initconst armada_370_gating_descr[] = { + { "audio", NULL, 0 }, + { "pex0_en", NULL, 1 }, + { "pex1_en", NULL, 2 }, + { "ge1", NULL, 3 }, + { "ge0", NULL, 4 }, + { "pex0", NULL, 5 }, + { "pex1", NULL, 9 }, + { "sata0", NULL, 15 }, + { "sdio", NULL, 17 }, + { "tdm", NULL, 25 }, + { "ddr", NULL, 28 }, + { "sata1", NULL, 30 }, + { } +}; +#endif + +#ifdef CONFIG_MACH_ARMADA_XP +static const struct mvebu_soc_descr __initconst armada_xp_gating_descr[] = { + { "audio", NULL, 0 }, + { "ge3", NULL, 1 }, + { "ge2", NULL, 2 }, + { "ge1", NULL, 3 }, + { "ge0", NULL, 4 }, + { "pex0", NULL, 5 }, + { "pex1", NULL, 6 }, + { "pex2", NULL, 7 }, + { "pex3", NULL, 8 }, + { "bp", NULL, 13 }, + { "sata0lnk", NULL, 14 }, + { "sata0", "sata0lnk", 15 }, + { "lcd", NULL, 16 }, + { "sdio", NULL, 17 }, + { "usb0", NULL, 18 }, + { "usb1", NULL, 19 }, + { "usb2", NULL, 20 }, + { "xor0", NULL, 22 }, + { "crypto", NULL, 23 }, + { "tdm", NULL, 25 }, + { "xor1", NULL, 28 }, + { "sata1lnk", NULL, 29 }, + { "sata1", "sata1lnk", 30 }, + { } +}; +#endif + +#ifdef CONFIG_ARCH_DOVE +static const struct mvebu_soc_descr __initconst dove_gating_descr[] = { + { "usb0", NULL, 0 }, + { "usb1", NULL, 1 }, + { "ge", "gephy", 2 }, + { "sata", NULL, 3 }, + { "pex0", NULL, 4 }, + { "pex1", NULL, 5 }, + { "sdio0", NULL, 8 }, + { "sdio1", NULL, 9 }, + { "nand", NULL, 10 }, + { "camera", NULL, 11 }, + { "i2s0", NULL, 12 }, + { "i2s1", NULL, 13 }, + { "crypto", NULL, 15 }, + { "ac97", NULL, 21 }, + { "pdma", NULL, 22 }, + { "xor0", NULL, 23 }, + { "xor1", NULL, 24 }, + { "gephy", NULL, 30 }, + { } +}; +#endif + +#ifdef CONFIG_ARCH_KIRKWOOD +static const struct mvebu_soc_descr __initconst kirkwood_gating_descr[] = { + { "ge0", NULL, 0 }, + { "pex0", NULL, 2 }, + { "usb0", NULL, 3 }, + { "sdio", NULL, 4 }, + { "tsu", NULL, 5 }, + { "runit", NULL, 7 }, + { "xor0", NULL, 8 }, + { "audio", NULL, 9 }, + { "sata0", NULL, 14 }, + { "sata1", NULL, 15 }, + { "xor1", NULL, 16 }, + { "crypto", NULL, 17 }, + { "pex1", NULL, 18 }, + { "ge1", NULL, 19 }, + { "tdm", NULL, 20 }, + { } +}; +#endif + +static const __initdata struct of_device_id clk_gating_match[] = { +#ifdef CONFIG_MACH_ARMADA_370 + { + .compatible = "marvell,armada-370-gating-clock", + .data = armada_370_gating_descr, + }, +#endif + +#ifdef CONFIG_MACH_ARMADA_XP + { + .compatible = "marvell,armada-xp-gating-clock", + .data = armada_xp_gating_descr, + }, +#endif + +#ifdef CONFIG_ARCH_DOVE + { + .compatible = "marvell,dove-gating-clock", + .data = dove_gating_descr, + }, +#endif + +#ifdef CONFIG_ARCH_KIRKWOOD + { + .compatible = "marvell,kirkwood-gating-clock", + .data = kirkwood_gating_descr, + }, +#endif + + { } +}; + +void __init mvebu_gating_clk_init(void) +{ + struct device_node *np; + + for_each_matching_node(np, clk_gating_match) { + const struct of_device_id *match = + of_match_node(clk_gating_match, np); + mvebu_clk_gating_setup(np, + (const struct mvebu_soc_descr *)match->data); + } +} |