diff options
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/Kconfig | 14 | ||||
-rw-r--r-- | drivers/clk/Makefile | 3 | ||||
-rw-r--r-- | drivers/clk/clk-bcm2835.c | 8 | ||||
-rw-r--r-- | drivers/clk/clk-efm32gg.c | 81 | ||||
-rw-r--r-- | drivers/clk/clk-fixed-factor.c | 2 | ||||
-rw-r--r-- | drivers/clk/clk-highbank.c | 10 | ||||
-rw-r--r-- | drivers/clk/clk-nomadik.c | 187 | ||||
-rw-r--r-- | drivers/clk/clk-ppc-corenet.c | 1 | ||||
-rw-r--r-- | drivers/clk/clk-prima2.c | 29 | ||||
-rw-r--r-- | drivers/clk/clk-vt8500.c | 34 | ||||
-rw-r--r-- | drivers/clk/clk-wm831x.c | 6 | ||||
-rw-r--r-- | drivers/clk/clk-xgene.c | 521 | ||||
-rw-r--r-- | drivers/clk/clk.c | 56 | ||||
-rw-r--r-- | drivers/clk/keystone/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/keystone/gate.c | 264 | ||||
-rw-r--r-- | drivers/clk/keystone/pll.c | 305 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-imx23.c | 15 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-imx28.c | 16 | ||||
-rw-r--r-- | drivers/clk/samsung/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-sunxi.c | 11 | ||||
-rw-r--r-- | drivers/clk/ux500/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/ux500/u8500_of_clk.c | 559 | ||||
-rw-r--r-- | drivers/clk/ux500/u8540_clk.c | 2 | ||||
-rw-r--r-- | drivers/clk/zynq/clkc.c | 16 |
24 files changed, 1948 insertions, 196 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 279407a36391..5c51115081b3 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -93,6 +93,20 @@ config CLK_PPC_CORENET This adds the clock driver support for Freescale PowerPC corenet platforms using common clock framework. +config COMMON_CLK_XGENE + bool "Clock driver for APM XGene SoC" + default y + depends on ARM64 + ---help--- + Sypport for the APM X-Gene SoC reference, PLL, and device clocks. + +config COMMON_CLK_KEYSTONE + tristate "Clock drivers for Keystone based SOCs" + depends on ARCH_KEYSTONE && OF + ---help--- + Supports clock drivers for Keystone based SOCs. These SOCs have local + a power sleep control module that gate the clock to the IPs and PLLs. + endmenu source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 7b111062ccba..7a10bc9a23e7 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-composite.o # SoCs specific obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o +obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o @@ -32,6 +33,8 @@ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o obj-$(CONFIG_ARCH_ZYNQ) += zynq/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_PLAT_SAMSUNG) += samsung/ +obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o +obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ obj-$(CONFIG_X86) += x86/ diff --git a/drivers/clk/clk-bcm2835.c b/drivers/clk/clk-bcm2835.c index 5fb4ff53d088..6b950ca8b711 100644 --- a/drivers/clk/clk-bcm2835.c +++ b/drivers/clk/clk-bcm2835.c @@ -20,14 +20,8 @@ #include <linux/clk-provider.h> #include <linux/clkdev.h> #include <linux/clk/bcm2835.h> -#include <linux/clk-provider.h> #include <linux/of.h> -static const struct of_device_id clk_match[] __initconst = { - { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, - { } -}; - /* * These are fixed clocks. They're probably not all root clocks and it may * be possible to turn them on and off but until this is mapped out better @@ -63,6 +57,4 @@ void __init bcm2835_init_clocks(void) ret = clk_register_clkdev(clk, NULL, "20215000.uart"); if (ret) pr_err("uart1_pclk alias not registered\n"); - - of_clk_init(clk_match); } diff --git a/drivers/clk/clk-efm32gg.c b/drivers/clk/clk-efm32gg.c new file mode 100644 index 000000000000..bac2ddf49d02 --- /dev/null +++ b/drivers/clk/clk-efm32gg.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013 Pengutronix + * Uwe Kleine-Koenig <u.kleine-koenig@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. + */ +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include <dt-bindings/clock/efm32-cmu.h> + +#define CMU_HFPERCLKEN0 0x44 + +static struct clk *clk[37]; +static struct clk_onecell_data clk_data = { + .clks = clk, + .clk_num = ARRAY_SIZE(clk), +}; + +static int __init efm32gg_cmu_init(struct device_node *np) +{ + int i; + void __iomem *base; + + for (i = 0; i < ARRAY_SIZE(clk); ++i) + clk[i] = ERR_PTR(-ENOENT); + + base = of_iomap(np, 0); + if (!base) { + pr_warn("Failed to map address range for efm32gg,cmu node\n"); + return -EADDRNOTAVAIL; + } + + clk[clk_HFXO] = clk_register_fixed_rate(NULL, "HFXO", NULL, + CLK_IS_ROOT, 48000000); + + clk[clk_HFPERCLKUSART0] = clk_register_gate(NULL, "HFPERCLK.USART0", + "HFXO", 0, base + CMU_HFPERCLKEN0, 0, 0, NULL); + clk[clk_HFPERCLKUSART1] = clk_register_gate(NULL, "HFPERCLK.USART1", + "HFXO", 0, base + CMU_HFPERCLKEN0, 1, 0, NULL); + clk[clk_HFPERCLKUSART2] = clk_register_gate(NULL, "HFPERCLK.USART2", + "HFXO", 0, base + CMU_HFPERCLKEN0, 2, 0, NULL); + clk[clk_HFPERCLKUART0] = clk_register_gate(NULL, "HFPERCLK.UART0", + "HFXO", 0, base + CMU_HFPERCLKEN0, 3, 0, NULL); + clk[clk_HFPERCLKUART1] = clk_register_gate(NULL, "HFPERCLK.UART1", + "HFXO", 0, base + CMU_HFPERCLKEN0, 4, 0, NULL); + clk[clk_HFPERCLKTIMER0] = clk_register_gate(NULL, "HFPERCLK.TIMER0", + "HFXO", 0, base + CMU_HFPERCLKEN0, 5, 0, NULL); + clk[clk_HFPERCLKTIMER1] = clk_register_gate(NULL, "HFPERCLK.TIMER1", + "HFXO", 0, base + CMU_HFPERCLKEN0, 6, 0, NULL); + clk[clk_HFPERCLKTIMER2] = clk_register_gate(NULL, "HFPERCLK.TIMER2", + "HFXO", 0, base + CMU_HFPERCLKEN0, 7, 0, NULL); + clk[clk_HFPERCLKTIMER3] = clk_register_gate(NULL, "HFPERCLK.TIMER3", + "HFXO", 0, base + CMU_HFPERCLKEN0, 8, 0, NULL); + clk[clk_HFPERCLKACMP0] = clk_register_gate(NULL, "HFPERCLK.ACMP0", + "HFXO", 0, base + CMU_HFPERCLKEN0, 9, 0, NULL); + clk[clk_HFPERCLKACMP1] = clk_register_gate(NULL, "HFPERCLK.ACMP1", + "HFXO", 0, base + CMU_HFPERCLKEN0, 10, 0, NULL); + clk[clk_HFPERCLKI2C0] = clk_register_gate(NULL, "HFPERCLK.I2C0", + "HFXO", 0, base + CMU_HFPERCLKEN0, 11, 0, NULL); + clk[clk_HFPERCLKI2C1] = clk_register_gate(NULL, "HFPERCLK.I2C1", + "HFXO", 0, base + CMU_HFPERCLKEN0, 12, 0, NULL); + clk[clk_HFPERCLKGPIO] = clk_register_gate(NULL, "HFPERCLK.GPIO", + "HFXO", 0, base + CMU_HFPERCLKEN0, 13, 0, NULL); + clk[clk_HFPERCLKVCMP] = clk_register_gate(NULL, "HFPERCLK.VCMP", + "HFXO", 0, base + CMU_HFPERCLKEN0, 14, 0, NULL); + clk[clk_HFPERCLKPRS] = clk_register_gate(NULL, "HFPERCLK.PRS", + "HFXO", 0, base + CMU_HFPERCLKEN0, 15, 0, NULL); + clk[clk_HFPERCLKADC0] = clk_register_gate(NULL, "HFPERCLK.ADC0", + "HFXO", 0, base + CMU_HFPERCLKEN0, 16, 0, NULL); + clk[clk_HFPERCLKDAC0] = clk_register_gate(NULL, "HFPERCLK.DAC0", + "HFXO", 0, base + CMU_HFPERCLKEN0, 17, 0, NULL); + + return of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} +CLK_OF_DECLARE(efm32ggcmu, "efm32gg,cmu", efm32gg_cmu_init); diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index 0e1d89b4321b..d9e3f671c2ea 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -117,7 +117,7 @@ void __init of_fixed_factor_clk_setup(struct device_node *node) } if (of_property_read_u32(node, "clock-mult", &mult)) { - pr_err("%s Fixed factor clock <%s> must have a clokc-mult property\n", + pr_err("%s Fixed factor clock <%s> must have a clock-mult property\n", __func__, node->name); return; } diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 2e08cb001936..2e7e9d9798cb 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -20,8 +20,7 @@ #include <linux/clk-provider.h> #include <linux/io.h> #include <linux/of.h> - -extern void __iomem *sregs_base; +#include <linux/of_address.h> #define HB_PLL_LOCK_500 0x20000000 #define HB_PLL_LOCK 0x10000000 @@ -280,6 +279,7 @@ static __init struct clk *hb_clk_init(struct device_node *node, const struct clk const char *clk_name = node->name; const char *parent_name; struct clk_init_data init; + struct device_node *srnp; int rc; rc = of_property_read_u32(node, "reg", ®); @@ -290,7 +290,11 @@ static __init struct clk *hb_clk_init(struct device_node *node, const struct clk if (WARN_ON(!hb_clk)) return NULL; - hb_clk->reg = sregs_base + reg; + /* Map system registers */ + srnp = of_find_compatible_node(NULL, NULL, "calxeda,hb-sregs"); + hb_clk->reg = of_iomap(srnp, 0); + BUG_ON(!hb_clk->reg); + hb_clk->reg += reg; of_property_read_string(node, "clock-output-names", &clk_name); diff --git a/drivers/clk/clk-nomadik.c b/drivers/clk/clk-nomadik.c index 4d978a3c88f7..6a934a5296bd 100644 --- a/drivers/clk/clk-nomadik.c +++ b/drivers/clk/clk-nomadik.c @@ -62,6 +62,79 @@ static DEFINE_SPINLOCK(src_lock); /* Base address of the SRC */ static void __iomem *src_base; +static int nomadik_clk_reboot_handler(struct notifier_block *this, + unsigned long code, + void *unused) +{ + u32 val; + + /* The main chrystal need to be enabled for reboot to work */ + val = readl(src_base + SRC_XTALCR); + val &= ~SRC_XTALCR_MXTALOVER; + val |= SRC_XTALCR_MXTALEN; + pr_crit("force-enabling MXTALO\n"); + writel(val, src_base + SRC_XTALCR); + return NOTIFY_OK; +} + +static struct notifier_block nomadik_clk_reboot_notifier = { + .notifier_call = nomadik_clk_reboot_handler, +}; + +static const struct of_device_id nomadik_src_match[] __initconst = { + { .compatible = "stericsson,nomadik-src" }, + { /* sentinel */ } +}; + +static void __init nomadik_src_init(void) +{ + struct device_node *np; + u32 val; + + np = of_find_matching_node(NULL, nomadik_src_match); + if (!np) { + pr_crit("no matching node for SRC, aborting clock init\n"); + return; + } + src_base = of_iomap(np, 0); + if (!src_base) { + pr_err("%s: must have src parent node with REGS (%s)\n", + __func__, np->name); + return; + } + + /* Set all timers to use the 2.4 MHz TIMCLK */ + val = readl(src_base + SRC_CR); + val |= SRC_CR_T0_ENSEL; + val |= SRC_CR_T1_ENSEL; + val |= SRC_CR_T2_ENSEL; + val |= SRC_CR_T3_ENSEL; + val |= SRC_CR_T4_ENSEL; + val |= SRC_CR_T5_ENSEL; + val |= SRC_CR_T6_ENSEL; + val |= SRC_CR_T7_ENSEL; + writel(val, src_base + SRC_CR); + + val = readl(src_base + SRC_XTALCR); + pr_info("SXTALO is %s\n", + (val & SRC_XTALCR_SXTALDIS) ? "disabled" : "enabled"); + pr_info("MXTAL is %s\n", + (val & SRC_XTALCR_MXTALSTAT) ? "enabled" : "disabled"); + if (of_property_read_bool(np, "disable-sxtalo")) { + /* The machine uses an external oscillator circuit */ + val |= SRC_XTALCR_SXTALDIS; + pr_info("disabling SXTALO\n"); + } + if (of_property_read_bool(np, "disable-mxtalo")) { + /* Disable this too: also run by external oscillator */ + val |= SRC_XTALCR_MXTALOVER; + val &= ~SRC_XTALCR_MXTALEN; + pr_info("disabling MXTALO\n"); + } + writel(val, src_base + SRC_XTALCR); + register_reboot_notifier(&nomadik_clk_reboot_notifier); +} + /** * struct clk_pll1 - Nomadik PLL1 clock * @hw: corresponding clock hardware entry @@ -439,6 +512,9 @@ static void __init of_nomadik_pll_setup(struct device_node *np) const char *parent_name; u32 pll_id; + if (!src_base) + nomadik_src_init(); + if (of_property_read_u32(np, "pll-id", &pll_id)) { pr_err("%s: PLL \"%s\" missing pll-id property\n", __func__, clk_name); @@ -449,6 +525,8 @@ static void __init of_nomadik_pll_setup(struct device_node *np) if (!IS_ERR(clk)) of_clk_add_provider(np, of_clk_src_simple_get, clk); } +CLK_OF_DECLARE(nomadik_pll_clk, + "st,nomadik-pll-clock", of_nomadik_pll_setup); static void __init of_nomadik_hclk_setup(struct device_node *np) { @@ -456,6 +534,9 @@ static void __init of_nomadik_hclk_setup(struct device_node *np) const char *clk_name = np->name; const char *parent_name; + if (!src_base) + nomadik_src_init(); + parent_name = of_clk_get_parent_name(np, 0); /* * The HCLK divides PLL1 with 1 (passthru), 2, 3 or 4. @@ -468,6 +549,8 @@ static void __init of_nomadik_hclk_setup(struct device_node *np) if (!IS_ERR(clk)) of_clk_add_provider(np, of_clk_src_simple_get, clk); } +CLK_OF_DECLARE(nomadik_hclk_clk, + "st,nomadik-hclk-clock", of_nomadik_hclk_setup); static void __init of_nomadik_src_clk_setup(struct device_node *np) { @@ -476,6 +559,9 @@ static void __init of_nomadik_src_clk_setup(struct device_node *np) const char *parent_name; u32 clk_id; + if (!src_base) + nomadik_src_init(); + if (of_property_read_u32(np, "clock-id", &clk_id)) { pr_err("%s: SRC clock \"%s\" missing clock-id property\n", __func__, clk_name); @@ -486,102 +572,5 @@ static void __init of_nomadik_src_clk_setup(struct device_node *np) if (!IS_ERR(clk)) of_clk_add_provider(np, of_clk_src_simple_get, clk); } - -static const struct of_device_id nomadik_src_match[] __initconst = { - { .compatible = "stericsson,nomadik-src" }, - { /* sentinel */ } -}; - -static const struct of_device_id nomadik_src_clk_match[] __initconst = { - { - .compatible = "fixed-clock", - .data = of_fixed_clk_setup, - }, - { - .compatible = "fixed-factor-clock", - .data = of_fixed_factor_clk_setup, - }, - { - .compatible = "st,nomadik-pll-clock", - .data = of_nomadik_pll_setup, - }, - { - .compatible = "st,nomadik-hclk-clock", - .data = of_nomadik_hclk_setup, - }, - { - .compatible = "st,nomadik-src-clock", - .data = of_nomadik_src_clk_setup, - }, - { /* sentinel */ } -}; - -static int nomadik_clk_reboot_handler(struct notifier_block *this, - unsigned long code, - void *unused) -{ - u32 val; - - /* The main chrystal need to be enabled for reboot to work */ - val = readl(src_base + SRC_XTALCR); - val &= ~SRC_XTALCR_MXTALOVER; - val |= SRC_XTALCR_MXTALEN; - pr_crit("force-enabling MXTALO\n"); - writel(val, src_base + SRC_XTALCR); - return NOTIFY_OK; -} - -static struct notifier_block nomadik_clk_reboot_notifier = { - .notifier_call = nomadik_clk_reboot_handler, -}; - -void __init nomadik_clk_init(void) -{ - struct device_node *np; - u32 val; - - np = of_find_matching_node(NULL, nomadik_src_match); - if (!np) { - pr_crit("no matching node for SRC, aborting clock init\n"); - return; - } - src_base = of_iomap(np, 0); - if (!src_base) { - pr_err("%s: must have src parent node with REGS (%s)\n", - __func__, np->name); - return; - } - - /* Set all timers to use the 2.4 MHz TIMCLK */ - val = readl(src_base + SRC_CR); - val |= SRC_CR_T0_ENSEL; - val |= SRC_CR_T1_ENSEL; - val |= SRC_CR_T2_ENSEL; - val |= SRC_CR_T3_ENSEL; - val |= SRC_CR_T4_ENSEL; - val |= SRC_CR_T5_ENSEL; - val |= SRC_CR_T6_ENSEL; - val |= SRC_CR_T7_ENSEL; - writel(val, src_base + SRC_CR); - - val = readl(src_base + SRC_XTALCR); - pr_info("SXTALO is %s\n", - (val & SRC_XTALCR_SXTALDIS) ? "disabled" : "enabled"); - pr_info("MXTAL is %s\n", - (val & SRC_XTALCR_MXTALSTAT) ? "enabled" : "disabled"); - if (of_property_read_bool(np, "disable-sxtalo")) { - /* The machine uses an external oscillator circuit */ - val |= SRC_XTALCR_SXTALDIS; - pr_info("disabling SXTALO\n"); - } - if (of_property_read_bool(np, "disable-mxtalo")) { - /* Disable this too: also run by external oscillator */ - val |= SRC_XTALCR_MXTALOVER; - val &= ~SRC_XTALCR_MXTALEN; - pr_info("disabling MXTALO\n"); - } - writel(val, src_base + SRC_XTALCR); - register_reboot_notifier(&nomadik_clk_reboot_notifier); - - of_clk_init(nomadik_src_clk_match); -} +CLK_OF_DECLARE(nomadik_src_clk, + "st,nomadik-src-clock", of_nomadik_src_clk_setup); diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c index e9587073bd32..c4f76ed914b0 100644 --- a/drivers/clk/clk-ppc-corenet.c +++ b/drivers/clk/clk-ppc-corenet.c @@ -11,6 +11,7 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/of.h> #include <linux/slab.h> diff --git a/drivers/clk/clk-prima2.c b/drivers/clk/clk-prima2.c index 5ab95f1ad579..6c15e3316137 100644 --- a/drivers/clk/clk-prima2.c +++ b/drivers/clk/clk-prima2.c @@ -1015,16 +1015,6 @@ static struct clk_std clk_usb1 = { }, }; -static struct of_device_id clkc_ids[] = { - { .compatible = "sirf,prima2-clkc" }, - {}, -}; - -static struct of_device_id rsc_ids[] = { - { .compatible = "sirf,prima2-rsc" }, - {}, -}; - enum prima2_clk_index { /* 0 1 2 3 4 5 6 7 8 9 */ rtc, osc, pll1, pll2, pll3, mem, sys, security, dsp, gps, @@ -1082,24 +1072,16 @@ static struct clk_hw *prima2_clk_hw_array[maxclk] __initdata = { static struct clk *prima2_clks[maxclk]; static struct clk_onecell_data clk_data; -void __init sirfsoc_of_clk_init(void) +static void __init sirfsoc_clk_init(struct device_node *np) { - struct device_node *np; + struct device_node *rscnp; int i; - np = of_find_matching_node(NULL, rsc_ids); - if (!np) - panic("unable to find compatible rsc node in dtb\n"); - - sirfsoc_rsc_vbase = of_iomap(np, 0); + rscnp = of_find_compatible_node(NULL, NULL, "sirf,prima2-rsc"); + sirfsoc_rsc_vbase = of_iomap(rscnp, 0); if (!sirfsoc_rsc_vbase) panic("unable to map rsc registers\n"); - - of_node_put(np); - - np = of_find_matching_node(NULL, clkc_ids); - if (!np) - return; + of_node_put(rscnp); sirfsoc_clk_vbase = of_iomap(np, 0); if (!sirfsoc_clk_vbase) @@ -1124,3 +1106,4 @@ void __init sirfsoc_of_clk_init(void) of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); } +CLK_OF_DECLARE(sirfsoc_clk, "sirf,prima2-clkc", sirfsoc_clk_init); diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c index 82306f5fb9c2..7fd5c5e9e25d 100644 --- a/drivers/clk/clk-vt8500.c +++ b/drivers/clk/clk-vt8500.c @@ -15,11 +15,14 @@ #include <linux/io.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/slab.h> #include <linux/bitops.h> #include <linux/clkdev.h> #include <linux/clk-provider.h> +#define LEGACY_PMC_BASE 0xD8130000 + /* All clocks share the same lock as none can be changed concurrently */ static DEFINE_SPINLOCK(_lock); @@ -53,6 +56,21 @@ struct clk_pll { static void __iomem *pmc_base; +static __init void vtwm_set_pmc_base(void) +{ + struct device_node *np = + of_find_compatible_node(NULL, NULL, "via,vt8500-pmc"); + + if (np) + pmc_base = of_iomap(np, 0); + else + pmc_base = ioremap(LEGACY_PMC_BASE, 0x1000); + of_node_put(np); + + if (!pmc_base) + pr_err("%s:of_iomap(pmc) failed\n", __func__); +} + #define to_clk_device(_hw) container_of(_hw, struct clk_device, hw) #define VT8500_PMC_BUSY_MASK 0x18 @@ -222,6 +240,9 @@ static __init void vtwm_device_clk_init(struct device_node *node) int rc; int clk_init_flags = 0; + if (!pmc_base) + vtwm_set_pmc_base(); + dev_clk = kzalloc(sizeof(*dev_clk), GFP_KERNEL); if (WARN_ON(!dev_clk)) return; @@ -636,6 +657,9 @@ static __init void vtwm_pll_clk_init(struct device_node *node, int pll_type) struct clk_init_data init; int rc; + if (!pmc_base) + vtwm_set_pmc_base(); + rc = of_property_read_u32(node, "reg", ®); if (WARN_ON(rc)) return; @@ -694,13 +718,3 @@ static void __init wm8850_pll_init(struct device_node *node) vtwm_pll_clk_init(node, PLL_TYPE_WM8850); } CLK_OF_DECLARE(wm8850_pll, "wm,wm8850-pll-clock", wm8850_pll_init); - -void __init vtwm_clk_init(void __iomem *base) -{ - if (!base) - return; - - pmc_base = base; - - of_clk_init(NULL); -} diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index 805b4c344006..b131041c8f48 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -391,14 +391,8 @@ static int wm831x_clk_probe(struct platform_device *pdev) return 0; } -static int wm831x_clk_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver wm831x_clk_driver = { .probe = wm831x_clk_probe, - .remove = wm831x_clk_remove, .driver = { .name = "wm831x-clk", .owner = THIS_MODULE, diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c new file mode 100644 index 000000000000..dd8a62d8f11f --- /dev/null +++ b/drivers/clk/clk-xgene.c @@ -0,0 +1,521 @@ +/* + * clk-xgene.c - AppliedMicro X-Gene Clock Interface + * + * Copyright (c) 2013, Applied Micro Circuits Corporation + * Author: Loc Ho <lho@apm.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/of_address.h> +#include <asm/setup.h> + +/* Register SCU_PCPPLL bit fields */ +#define N_DIV_RD(src) (((src) & 0x000001ff)) + +/* Register SCU_SOCPLL bit fields */ +#define CLKR_RD(src) (((src) & 0x07000000)>>24) +#define CLKOD_RD(src) (((src) & 0x00300000)>>20) +#define REGSPEC_RESET_F1_MASK 0x00010000 +#define CLKF_RD(src) (((src) & 0x000001ff)) + +#define XGENE_CLK_DRIVER_VER "0.1" + +static DEFINE_SPINLOCK(clk_lock); + +static inline u32 xgene_clk_read(void *csr) +{ + return readl_relaxed(csr); +} + +static inline void xgene_clk_write(u32 data, void *csr) +{ + return writel_relaxed(data, csr); +} + +/* PLL Clock */ +enum xgene_pll_type { + PLL_TYPE_PCP = 0, + PLL_TYPE_SOC = 1, +}; + +struct xgene_clk_pll { + struct clk_hw hw; + const char *name; + void __iomem *reg; + spinlock_t *lock; + u32 pll_offset; + enum xgene_pll_type type; +}; + +#define to_xgene_clk_pll(_hw) container_of(_hw, struct xgene_clk_pll, hw) + +static int xgene_clk_pll_is_enabled(struct clk_hw *hw) +{ + struct xgene_clk_pll *pllclk = to_xgene_clk_pll(hw); + u32 data; + + data = xgene_clk_read(pllclk->reg + pllclk->pll_offset); + pr_debug("%s pll %s\n", pllclk->name, + data & REGSPEC_RESET_F1_MASK ? "disabled" : "enabled"); + + return data & REGSPEC_RESET_F1_MASK ? 0 : 1; +} + +static unsigned long xgene_clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct xgene_clk_pll *pllclk = to_xgene_clk_pll(hw); + unsigned long fref; + unsigned long fvco; + u32 pll; + u32 nref; + u32 nout; + u32 nfb; + + pll = xgene_clk_read(pllclk->reg + pllclk->pll_offset); + + if (pllclk->type == PLL_TYPE_PCP) { + /* + * PLL VCO = Reference clock * NF + * PCP PLL = PLL_VCO / 2 + */ + nout = 2; + fvco = parent_rate * (N_DIV_RD(pll) + 4); + } else { + /* + * Fref = Reference Clock / NREF; + * Fvco = Fref * NFB; + * Fout = Fvco / NOUT; + */ + nref = CLKR_RD(pll) + 1; + nout = CLKOD_RD(pll) + 1; + nfb = CLKF_RD(pll); + fref = parent_rate / nref; + fvco = fref * nfb; + } + pr_debug("%s pll recalc rate %ld parent %ld\n", pllclk->name, + fvco / nout, parent_rate); + + return fvco / nout; +} + +const struct clk_ops xgene_clk_pll_ops = { + .is_enabled = xgene_clk_pll_is_enabled, + .recalc_rate = xgene_clk_pll_recalc_rate, +}; + +static struct clk *xgene_register_clk_pll(struct device *dev, + const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, u32 pll_offset, + u32 type, spinlock_t *lock) +{ + struct xgene_clk_pll *apmclk; + struct clk *clk; + struct clk_init_data init; + + /* allocate the APM clock structure */ + apmclk = kzalloc(sizeof(*apmclk), GFP_KERNEL); + if (!apmclk) { + pr_err("%s: could not allocate APM clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &xgene_clk_pll_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + apmclk->name = name; + apmclk->reg = reg; + apmclk->lock = lock; + apmclk->pll_offset = pll_offset; + apmclk->type = type; + apmclk->hw.init = &init; + + /* Register the clock */ + clk = clk_register(dev, &apmclk->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register clk %s\n", __func__, name); + kfree(apmclk); + return NULL; + } + return clk; +} + +static void xgene_pllclk_init(struct device_node *np, enum xgene_pll_type pll_type) +{ + const char *clk_name = np->full_name; + struct clk *clk; + void *reg; + + reg = of_iomap(np, 0); + if (reg == NULL) { + pr_err("Unable to map CSR register for %s\n", np->full_name); + return; + } + of_property_read_string(np, "clock-output-names", &clk_name); + clk = xgene_register_clk_pll(NULL, + clk_name, of_clk_get_parent_name(np, 0), + CLK_IS_ROOT, reg, 0, pll_type, &clk_lock); + if (!IS_ERR(clk)) { + of_clk_add_provider(np, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + pr_debug("Add %s clock PLL\n", clk_name); + } +} + +static void xgene_socpllclk_init(struct device_node *np) +{ + xgene_pllclk_init(np, PLL_TYPE_SOC); +} + +static void xgene_pcppllclk_init(struct device_node *np) +{ + xgene_pllclk_init(np, PLL_TYPE_PCP); +} + +/* IP Clock */ +struct xgene_dev_parameters { + void __iomem *csr_reg; /* CSR for IP clock */ + u32 reg_clk_offset; /* Offset to clock enable CSR */ + u32 reg_clk_mask; /* Mask bit for clock enable */ + u32 reg_csr_offset; /* Offset to CSR reset */ + u32 reg_csr_mask; /* Mask bit for disable CSR reset */ + void __iomem *divider_reg; /* CSR for divider */ + u32 reg_divider_offset; /* Offset to divider register */ + u32 reg_divider_shift; /* Bit shift to divider field */ + u32 reg_divider_width; /* Width of the bit to divider field */ +}; + +struct xgene_clk { + struct clk_hw hw; + const char *name; + spinlock_t *lock; + struct xgene_dev_parameters param; +}; + +#define to_xgene_clk(_hw) container_of(_hw, struct xgene_clk, hw) + +static int xgene_clk_enable(struct clk_hw *hw) +{ + struct xgene_clk *pclk = to_xgene_clk(hw); + unsigned long flags = 0; + u32 data; + + if (pclk->lock) + spin_lock_irqsave(pclk->lock, flags); + + if (pclk->param.csr_reg != NULL) { + pr_debug("%s clock enabled\n", pclk->name); + /* First enable the clock */ + data = xgene_clk_read(pclk->param.csr_reg + + pclk->param.reg_clk_offset); + data |= pclk->param.reg_clk_mask; + xgene_clk_write(data, pclk->param.csr_reg + + pclk->param.reg_clk_offset); + pr_debug("%s clock PADDR base 0x%016LX clk offset 0x%08X mask 0x%08X value 0x%08X\n", + pclk->name, __pa(pclk->param.csr_reg), + pclk->param.reg_clk_offset, pclk->param.reg_clk_mask, + data); + + /* Second enable the CSR */ + data = xgene_clk_read(pclk->param.csr_reg + + pclk->param.reg_csr_offset); + data &= ~pclk->param.reg_csr_mask; + xgene_clk_write(data, pclk->param.csr_reg + + pclk->param.reg_csr_offset); + pr_debug("%s CSR RESET PADDR base 0x%016LX csr offset 0x%08X mask 0x%08X value 0x%08X\n", + pclk->name, __pa(pclk->param.csr_reg), + pclk->param.reg_csr_offset, pclk->param.reg_csr_mask, + data); + } + + if (pclk->lock) + spin_unlock_irqrestore(pclk->lock, flags); + + return 0; +} + +static void xgene_clk_disable(struct clk_hw *hw) +{ + struct xgene_clk *pclk = to_xgene_clk(hw); + unsigned long flags = 0; + u32 data; + + if (pclk->lock) + spin_lock_irqsave(pclk->lock, flags); + + if (pclk->param.csr_reg != NULL) { + pr_debug("%s clock disabled\n", pclk->name); + /* First put the CSR in reset */ + data = xgene_clk_read(pclk->param.csr_reg + + pclk->param.reg_csr_offset); + data |= pclk->param.reg_csr_mask; + xgene_clk_write(data, pclk->param.csr_reg + + pclk->param.reg_csr_offset); + + /* Second disable the clock */ + data = xgene_clk_read(pclk->param.csr_reg + + pclk->param.reg_clk_offset); + data &= ~pclk->param.reg_clk_mask; + xgene_clk_write(data, pclk->param.csr_reg + + pclk->param.reg_clk_offset); + } + + if (pclk->lock) + spin_unlock_irqrestore(pclk->lock, flags); +} + +static int xgene_clk_is_enabled(struct clk_hw *hw) +{ + struct xgene_clk *pclk = to_xgene_clk(hw); + u32 data = 0; + + if (pclk->param.csr_reg != NULL) { + pr_debug("%s clock checking\n", pclk->name); + data = xgene_clk_read(pclk->param.csr_reg + + pclk->param.reg_clk_offset); + pr_debug("%s clock is %s\n", pclk->name, + data & pclk->param.reg_clk_mask ? "enabled" : + "disabled"); + } + + if (pclk->param.csr_reg == NULL) + return 1; + return data & pclk->param.reg_clk_mask ? 1 : 0; +} + +static unsigned long xgene_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct xgene_clk *pclk = to_xgene_clk(hw); + u32 data; + + if (pclk->param.divider_reg) { + data = xgene_clk_read(pclk->param.divider_reg + + pclk->param.reg_divider_offset); + data >>= pclk->param.reg_divider_shift; + data &= (1 << pclk->param.reg_divider_width) - 1; + + pr_debug("%s clock recalc rate %ld parent %ld\n", + pclk->name, parent_rate / data, parent_rate); + return parent_rate / data; + } else { + pr_debug("%s clock recalc rate %ld parent %ld\n", + pclk->name, parent_rate, parent_rate); + return parent_rate; + } +} + +static int xgene_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct xgene_clk *pclk = to_xgene_clk(hw); + unsigned long flags = 0; + u32 data; + u32 divider; + u32 divider_save; + + if (pclk->lock) + spin_lock_irqsave(pclk->lock, flags); + + if (pclk->param.divider_reg) { + /* Let's compute the divider */ + if (rate > parent_rate) + rate = parent_rate; + divider_save = divider = parent_rate / rate; /* Rounded down */ + divider &= (1 << pclk->param.reg_divider_width) - 1; + divider <<= pclk->param.reg_divider_shift; + + /* Set new divider */ + data = xgene_clk_read(pclk->param.divider_reg + + pclk->param.reg_divider_offset); + data &= ~((1 << pclk->param.reg_divider_width) - 1); + data |= divider; + xgene_clk_write(data, pclk->param.divider_reg + + pclk->param.reg_divider_offset); + pr_debug("%s clock set rate %ld\n", pclk->name, + parent_rate / divider_save); + } else { + divider_save = 1; + } + + if (pclk->lock) + spin_unlock_irqrestore(pclk->lock, flags); + + return parent_rate / divider_save; +} + +static long xgene_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct xgene_clk *pclk = to_xgene_clk(hw); + unsigned long parent_rate = *prate; + u32 divider; + + if (pclk->param.divider_reg) { + /* Let's compute the divider */ + if (rate > parent_rate) + rate = parent_rate; + divider = parent_rate / rate; /* Rounded down */ + } else { + divider = 1; + } + + return parent_rate / divider; +} + +const struct clk_ops xgene_clk_ops = { + .enable = xgene_clk_enable, + .disable = xgene_clk_disable, + .is_enabled = xgene_clk_is_enabled, + .recalc_rate = xgene_clk_recalc_rate, + .set_rate = xgene_clk_set_rate, + .round_rate = xgene_clk_round_rate, +}; + +static struct clk *xgene_register_clk(struct device *dev, + const char *name, const char *parent_name, + struct xgene_dev_parameters *parameters, spinlock_t *lock) +{ + struct xgene_clk *apmclk; + struct clk *clk; + struct clk_init_data init; + int rc; + + /* allocate the APM clock structure */ + apmclk = kzalloc(sizeof(*apmclk), GFP_KERNEL); + if (!apmclk) { + pr_err("%s: could not allocate APM clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &xgene_clk_ops; + init.flags = 0; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + apmclk->name = name; + apmclk->lock = lock; + apmclk->hw.init = &init; + apmclk->param = *parameters; + + /* Register the clock */ + clk = clk_register(dev, &apmclk->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register clk %s\n", __func__, name); + kfree(apmclk); + return clk; + } + + /* Register the clock for lookup */ + rc = clk_register_clkdev(clk, name, NULL); + if (rc != 0) { + pr_err("%s: could not register lookup clk %s\n", + __func__, name); + } + return clk; +} + +static void __init xgene_devclk_init(struct device_node *np) +{ + const char *clk_name = np->full_name; + struct clk *clk; + struct resource res; + int rc; + struct xgene_dev_parameters parameters; + int i; + + /* Check if the entry is disabled */ + if (!of_device_is_available(np)) + return; + + /* Parse the DTS register for resource */ + parameters.csr_reg = NULL; + parameters.divider_reg = NULL; + for (i = 0; i < 2; i++) { + void *map_res; + rc = of_address_to_resource(np, i, &res); + if (rc != 0) { + if (i == 0) { + pr_err("no DTS register for %s\n", + np->full_name); + return; + } + break; + } + map_res = of_iomap(np, i); + if (map_res == NULL) { + pr_err("Unable to map resource %d for %s\n", + i, np->full_name); + goto err; + } + if (strcmp(res.name, "div-reg") == 0) + parameters.divider_reg = map_res; + else /* if (strcmp(res->name, "csr-reg") == 0) */ + parameters.csr_reg = map_res; + } + if (of_property_read_u32(np, "csr-offset", ¶meters.reg_csr_offset)) + parameters.reg_csr_offset = 0; + if (of_property_read_u32(np, "csr-mask", ¶meters.reg_csr_mask)) + parameters.reg_csr_mask = 0xF; + if (of_property_read_u32(np, "enable-offset", + ¶meters.reg_clk_offset)) + parameters.reg_clk_offset = 0x8; + if (of_property_read_u32(np, "enable-mask", ¶meters.reg_clk_mask)) + parameters.reg_clk_mask = 0xF; + if (of_property_read_u32(np, "divider-offset", + ¶meters.reg_divider_offset)) + parameters.reg_divider_offset = 0; + if (of_property_read_u32(np, "divider-width", + ¶meters.reg_divider_width)) + parameters.reg_divider_width = 0; + if (of_property_read_u32(np, "divider-shift", + ¶meters.reg_divider_shift)) + parameters.reg_divider_shift = 0; + of_property_read_string(np, "clock-output-names", &clk_name); + + clk = xgene_register_clk(NULL, clk_name, + of_clk_get_parent_name(np, 0), ¶meters, &clk_lock); + if (IS_ERR(clk)) + goto err; + pr_debug("Add %s clock\n", clk_name); + rc = of_clk_add_provider(np, of_clk_src_simple_get, clk); + if (rc != 0) + pr_err("%s: could register provider clk %s\n", __func__, + np->full_name); + + return; + +err: + if (parameters.csr_reg) + iounmap(parameters.csr_reg); + if (parameters.divider_reg) + iounmap(parameters.divider_reg); +} + +CLK_OF_DECLARE(xgene_socpll_clock, "apm,xgene-socpll-clock", xgene_socpllclk_init); +CLK_OF_DECLARE(xgene_pcppll_clock, "apm,xgene-pcppll-clock", xgene_pcppllclk_init); +CLK_OF_DECLARE(xgene_dev_clock, "apm,xgene-device-clock", xgene_devclk_init); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index a004769528e6..2cf2ea6b77a1 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1080,13 +1080,16 @@ unsigned long clk_get_rate(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_get_rate); -static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent) +static int clk_fetch_parent_index(struct clk *clk, struct clk *parent) { - u8 i; + int i; - if (!clk->parents) - clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), - GFP_KERNEL); + if (!clk->parents) { + clk->parents = kcalloc(clk->num_parents, + sizeof(struct clk *), GFP_KERNEL); + if (!clk->parents) + return -ENOMEM; + } /* * find index of new parent clock using cached parent ptrs, @@ -1094,16 +1097,19 @@ static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent) * them now to avoid future calls to __clk_lookup. */ for (i = 0; i < clk->num_parents; i++) { - if (clk->parents && clk->parents[i] == parent) - break; - else if (!strcmp(clk->parent_names[i], parent->name)) { - if (clk->parents) - clk->parents[i] = __clk_lookup(parent->name); - break; + if (clk->parents[i] == parent) + return i; + + if (clk->parents[i]) + continue; + + if (!strcmp(clk->parent_names[i], parent->name)) { + clk->parents[i] = __clk_lookup(parent->name); + return i; } } - return i; + return -EINVAL; } static void clk_reparent(struct clk *clk, struct clk *new_parent) @@ -1265,7 +1271,7 @@ static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) struct clk *old_parent, *parent; unsigned long best_parent_rate = 0; unsigned long new_rate; - u8 p_index = 0; + int p_index = 0; /* sanity */ if (IS_ERR_OR_NULL(clk)) @@ -1306,7 +1312,7 @@ static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) /* try finding the new parent index */ if (parent) { p_index = clk_fetch_parent_index(clk, parent); - if (p_index == clk->num_parents) { + if (p_index < 0) { pr_debug("%s: clk %s can not be parent of clk %s\n", __func__, parent->name, clk->name); return NULL; @@ -1532,7 +1538,7 @@ static struct clk *__clk_init_parent(struct clk *clk) if (!clk->parents) clk->parents = - kzalloc((sizeof(struct clk*) * clk->num_parents), + kcalloc(clk->num_parents, sizeof(struct clk *), GFP_KERNEL); ret = clk_get_parent_by_index(clk, index); @@ -1568,7 +1574,7 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent) int clk_set_parent(struct clk *clk, struct clk *parent) { int ret = 0; - u8 p_index = 0; + int p_index = 0; unsigned long p_rate = 0; if (!clk) @@ -1597,10 +1603,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (parent) { p_index = clk_fetch_parent_index(clk, parent); p_rate = parent->rate; - if (p_index == clk->num_parents) { + if (p_index < 0) { pr_debug("%s: clk %s can not be parent of clk %s\n", __func__, parent->name, clk->name); - ret = -EINVAL; + ret = p_index; goto out; } } @@ -1689,8 +1695,8 @@ int __clk_init(struct device *dev, struct clk *clk) * for clock drivers to statically initialize clk->parents. */ if (clk->num_parents > 1 && !clk->parents) { - clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), - GFP_KERNEL); + clk->parents = kcalloc(clk->num_parents, sizeof(struct clk *), + GFP_KERNEL); /* * __clk_lookup returns NULL for parents that have not been * clk_init'd; thus any access to clk->parents[] must check @@ -1830,8 +1836,8 @@ static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk) hw->clk = clk; /* allocate local copy in case parent_names is __initdata */ - clk->parent_names = kzalloc((sizeof(char*) * clk->num_parents), - GFP_KERNEL); + clk->parent_names = kcalloc(clk->num_parents, sizeof(char *), + GFP_KERNEL); if (!clk->parent_names) { pr_err("%s: could not allocate clk->parent_names\n", __func__); @@ -2196,6 +2202,12 @@ struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec) return clk; } +int of_clk_get_parent_count(struct device_node *np) +{ + return of_count_phandle_with_args(np, "clocks", "#clock-cells"); +} +EXPORT_SYMBOL_GPL(of_clk_get_parent_count); + const char *of_clk_get_parent_name(struct device_node *np, int index) { struct of_phandle_args clkspec; diff --git a/drivers/clk/keystone/Makefile b/drivers/clk/keystone/Makefile new file mode 100644 index 000000000000..0477cf63f132 --- /dev/null +++ b/drivers/clk/keystone/Makefile @@ -0,0 +1 @@ +obj-y += pll.o gate.o diff --git a/drivers/clk/keystone/gate.c b/drivers/clk/keystone/gate.c new file mode 100644 index 000000000000..1f333bcfc22e --- /dev/null +++ b/drivers/clk/keystone/gate.c @@ -0,0 +1,264 @@ +/* + * Clock driver for Keystone 2 based devices + * + * Copyright (C) 2013 Texas Instruments. + * Murali Karicheri <m-karicheri2@ti.com> + * Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/of_address.h> +#include <linux/of.h> +#include <linux/module.h> + +/* PSC register offsets */ +#define PTCMD 0x120 +#define PTSTAT 0x128 +#define PDSTAT 0x200 +#define PDCTL 0x300 +#define MDSTAT 0x800 +#define MDCTL 0xa00 + +/* PSC module states */ +#define PSC_STATE_SWRSTDISABLE 0 +#define PSC_STATE_SYNCRST 1 +#define PSC_STATE_DISABLE 2 +#define PSC_STATE_ENABLE 3 + +#define MDSTAT_STATE_MASK 0x3f +#define MDSTAT_MCKOUT BIT(12) +#define PDSTAT_STATE_MASK 0x1f +#define MDCTL_FORCE BIT(31) +#define MDCTL_LRESET BIT(8) +#define PDCTL_NEXT BIT(0) + +/* Maximum timeout to bail out state transition for module */ +#define STATE_TRANS_MAX_COUNT 0xffff + +static void __iomem *domain_transition_base; + +/** + * struct clk_psc_data - PSC data + * @control_base: Base address for a PSC control + * @domain_base: Base address for a PSC domain + * @domain_id: PSC domain id number + */ +struct clk_psc_data { + void __iomem *control_base; + void __iomem *domain_base; + u32 domain_id; +}; + +/** + * struct clk_psc - PSC clock structure + * @hw: clk_hw for the psc + * @psc_data: PSC driver specific data + * @lock: Spinlock used by the driver + */ +struct clk_psc { + struct clk_hw hw; + struct clk_psc_data *psc_data; + spinlock_t *lock; +}; + +static DEFINE_SPINLOCK(psc_lock); + +#define to_clk_psc(_hw) container_of(_hw, struct clk_psc, hw) + +static void psc_config(void __iomem *control_base, void __iomem *domain_base, + u32 next_state, u32 domain_id) +{ + u32 ptcmd, pdstat, pdctl, mdstat, mdctl, ptstat; + u32 count = STATE_TRANS_MAX_COUNT; + + mdctl = readl(control_base + MDCTL); + mdctl &= ~MDSTAT_STATE_MASK; + mdctl |= next_state; + /* For disable, we always put the module in local reset */ + if (next_state == PSC_STATE_DISABLE) + mdctl &= ~MDCTL_LRESET; + writel(mdctl, control_base + MDCTL); + + pdstat = readl(domain_base + PDSTAT); + if (!(pdstat & PDSTAT_STATE_MASK)) { + pdctl = readl(domain_base + PDCTL); + pdctl |= PDCTL_NEXT; + writel(pdctl, domain_base + PDCTL); + } + + ptcmd = 1 << domain_id; + writel(ptcmd, domain_transition_base + PTCMD); + do { + ptstat = readl(domain_transition_base + PTSTAT); + } while (((ptstat >> domain_id) & 1) && count--); + + count = STATE_TRANS_MAX_COUNT; + do { + mdstat = readl(control_base + MDSTAT); + } while (!((mdstat & MDSTAT_STATE_MASK) == next_state) && count--); +} + +static int keystone_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_psc *psc = to_clk_psc(hw); + struct clk_psc_data *data = psc->psc_data; + u32 mdstat = readl(data->control_base + MDSTAT); + + return (mdstat & MDSTAT_MCKOUT) ? 1 : 0; +} + +static int keystone_clk_enable(struct clk_hw *hw) +{ + struct clk_psc *psc = to_clk_psc(hw); + struct clk_psc_data *data = psc->psc_data; + unsigned long flags = 0; + + if (psc->lock) + spin_lock_irqsave(psc->lock, flags); + + psc_config(data->control_base, data->domain_base, + PSC_STATE_ENABLE, data->domain_id); + + if (psc->lock) + spin_unlock_irqrestore(psc->lock, flags); + + return 0; +} + +static void keystone_clk_disable(struct clk_hw *hw) +{ + struct clk_psc *psc = to_clk_psc(hw); + struct clk_psc_data *data = psc->psc_data; + unsigned long flags = 0; + + if (psc->lock) + spin_lock_irqsave(psc->lock, flags); + + psc_config(data->control_base, data->domain_base, + PSC_STATE_DISABLE, data->domain_id); + + if (psc->lock) + spin_unlock_irqrestore(psc->lock, flags); +} + +static const struct clk_ops clk_psc_ops = { + .enable = keystone_clk_enable, + .disable = keystone_clk_disable, + .is_enabled = keystone_clk_is_enabled, +}; + +/** + * clk_register_psc - register psc clock + * @dev: device that is registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @psc_data: platform data to configure this clock + * @lock: spinlock used by this clock + */ +static struct clk *clk_register_psc(struct device *dev, + const char *name, + const char *parent_name, + struct clk_psc_data *psc_data, + spinlock_t *lock) +{ + struct clk_init_data init; + struct clk_psc *psc; + struct clk *clk; + + psc = kzalloc(sizeof(*psc), GFP_KERNEL); + if (!psc) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_psc_ops; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + psc->psc_data = psc_data; + psc->lock = lock; + psc->hw.init = &init; + + clk = clk_register(NULL, &psc->hw); + if (IS_ERR(clk)) + kfree(psc); + + return clk; +} + +/** + * of_psc_clk_init - initialize psc clock through DT + * @node: device tree node for this clock + * @lock: spinlock used by this clock + */ +static void __init of_psc_clk_init(struct device_node *node, spinlock_t *lock) +{ + const char *clk_name = node->name; + const char *parent_name; + struct clk_psc_data *data; + struct clk *clk; + int i; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + pr_err("%s: Out of memory\n", __func__); + return; + } + + i = of_property_match_string(node, "reg-names", "control"); + data->control_base = of_iomap(node, i); + if (!data->control_base) { + pr_err("%s: control ioremap failed\n", __func__); + goto out; + } + + i = of_property_match_string(node, "reg-names", "domain"); + data->domain_base = of_iomap(node, i); + if (!data->domain_base) { + pr_err("%s: domain ioremap failed\n", __func__); + iounmap(data->control_base); + goto out; + } + + of_property_read_u32(node, "domain-id", &data->domain_id); + + /* Domain transition registers at fixed address space of domain_id 0 */ + if (!domain_transition_base && !data->domain_id) + domain_transition_base = data->domain_base; + + of_property_read_string(node, "clock-output-names", &clk_name); + parent_name = of_clk_get_parent_name(node, 0); + if (!parent_name) { + pr_err("%s: Parent clock not found\n", __func__); + goto out; + } + + clk = clk_register_psc(NULL, clk_name, parent_name, data, lock); + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + return; + } + + pr_err("%s: error registering clk %s\n", __func__, node->name); +out: + kfree(data); + return; +} + +/** + * of_keystone_psc_clk_init - initialize psc clock through DT + * @node: device tree node for this clock + */ +static void __init of_keystone_psc_clk_init(struct device_node *node) +{ + of_psc_clk_init(node, &psc_lock); +} +CLK_OF_DECLARE(keystone_gate_clk, "ti,keystone,psc-clock", + of_keystone_psc_clk_init); diff --git a/drivers/clk/keystone/pll.c b/drivers/clk/keystone/pll.c new file mode 100644 index 000000000000..47a1bd9f1726 --- /dev/null +++ b/drivers/clk/keystone/pll.c @@ -0,0 +1,305 @@ +/* + * PLL clock driver for Keystone devices + * + * Copyright (C) 2013 Texas Instruments Inc. + * Murali Karicheri <m-karicheri2@ti.com> + * Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/of_address.h> +#include <linux/of.h> +#include <linux/module.h> + +#define PLLM_LOW_MASK 0x3f +#define PLLM_HIGH_MASK 0x7ffc0 +#define MAIN_PLLM_HIGH_MASK 0x7f000 +#define PLLM_HIGH_SHIFT 6 +#define PLLD_MASK 0x3f + +/** + * struct clk_pll_data - pll data structure + * @has_pllctrl: If set to non zero, lower 6 bits of multiplier is in pllm + * register of pll controller, else it is in the pll_ctrl0((bit 11-6) + * @phy_pllm: Physical address of PLLM in pll controller. Used when + * has_pllctrl is non zero. + * @phy_pll_ctl0: Physical address of PLL ctrl0. This could be that of + * Main PLL or any other PLLs in the device such as ARM PLL, DDR PLL + * or PA PLL available on keystone2. These PLLs are controlled by + * this register. Main PLL is controlled by a PLL controller. + * @pllm: PLL register map address + * @pll_ctl0: PLL controller map address + * @pllm_lower_mask: multiplier lower mask + * @pllm_upper_mask: multiplier upper mask + * @pllm_upper_shift: multiplier upper shift + * @plld_mask: divider mask + * @postdiv: Post divider + */ +struct clk_pll_data { + bool has_pllctrl; + u32 phy_pllm; + u32 phy_pll_ctl0; + void __iomem *pllm; + void __iomem *pll_ctl0; + u32 pllm_lower_mask; + u32 pllm_upper_mask; + u32 pllm_upper_shift; + u32 plld_mask; + u32 postdiv; +}; + +/** + * struct clk_pll - Main pll clock + * @hw: clk_hw for the pll + * @pll_data: PLL driver specific data + */ +struct clk_pll { + struct clk_hw hw; + struct clk_pll_data *pll_data; +}; + +#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw) + +static unsigned long clk_pllclk_recalc(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pll *pll = to_clk_pll(hw); + struct clk_pll_data *pll_data = pll->pll_data; + unsigned long rate = parent_rate; + u32 mult = 0, prediv, postdiv, val; + + /* + * get bits 0-5 of multiplier from pllctrl PLLM register + * if has_pllctrl is non zero + */ + if (pll_data->has_pllctrl) { + val = readl(pll_data->pllm); + mult = (val & pll_data->pllm_lower_mask); + } + + /* bit6-12 of PLLM is in Main PLL control register */ + val = readl(pll_data->pll_ctl0); + mult |= ((val & pll_data->pllm_upper_mask) + >> pll_data->pllm_upper_shift); + prediv = (val & pll_data->plld_mask); + postdiv = pll_data->postdiv; + + rate /= (prediv + 1); + rate = (rate * (mult + 1)); + rate /= postdiv; + + return rate; +} + +static const struct clk_ops clk_pll_ops = { + .recalc_rate = clk_pllclk_recalc, +}; + +static struct clk *clk_register_pll(struct device *dev, + const char *name, + const char *parent_name, + struct clk_pll_data *pll_data) +{ + struct clk_init_data init; + struct clk_pll *pll; + struct clk *clk; + + 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->pll_data = pll_data; + pll->hw.init = &init; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) + goto out; + + return clk; +out: + kfree(pll); + return NULL; +} + +/** + * _of_clk_init - PLL initialisation via DT + * @node: device tree node for this clock + * @pllctrl: If true, lower 6 bits of multiplier is in pllm register of + * pll controller, else it is in the control regsiter0(bit 11-6) + */ +static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl) +{ + struct clk_pll_data *pll_data; + const char *parent_name; + struct clk *clk; + int i; + + pll_data = kzalloc(sizeof(*pll_data), GFP_KERNEL); + if (!pll_data) { + pr_err("%s: Out of memory\n", __func__); + return; + } + + parent_name = of_clk_get_parent_name(node, 0); + if (of_property_read_u32(node, "fixed-postdiv", &pll_data->postdiv)) + goto out; + + i = of_property_match_string(node, "reg-names", "control"); + pll_data->pll_ctl0 = of_iomap(node, i); + if (!pll_data->pll_ctl0) { + pr_err("%s: ioremap failed\n", __func__); + goto out; + } + + pll_data->pllm_lower_mask = PLLM_LOW_MASK; + pll_data->pllm_upper_shift = PLLM_HIGH_SHIFT; + pll_data->plld_mask = PLLD_MASK; + pll_data->has_pllctrl = pllctrl; + if (!pll_data->has_pllctrl) { + pll_data->pllm_upper_mask = PLLM_HIGH_MASK; + } else { + pll_data->pllm_upper_mask = MAIN_PLLM_HIGH_MASK; + i = of_property_match_string(node, "reg-names", "multiplier"); + pll_data->pllm = of_iomap(node, i); + if (!pll_data->pllm) { + iounmap(pll_data->pll_ctl0); + goto out; + } + } + + clk = clk_register_pll(NULL, node->name, parent_name, pll_data); + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + return; + } + +out: + pr_err("%s: error initializing pll %s\n", __func__, node->name); + kfree(pll_data); +} + +/** + * of_keystone_pll_clk_init - PLL initialisation DT wrapper + * @node: device tree node for this clock + */ +static void __init of_keystone_pll_clk_init(struct device_node *node) +{ + _of_pll_clk_init(node, false); +} +CLK_OF_DECLARE(keystone_pll_clock, "ti,keystone,pll-clock", + of_keystone_pll_clk_init); + +/** + * of_keystone_pll_main_clk_init - Main PLL initialisation DT wrapper + * @node: device tree node for this clock + */ +static void __init of_keystone_main_pll_clk_init(struct device_node *node) +{ + _of_pll_clk_init(node, true); +} +CLK_OF_DECLARE(keystone_main_pll_clock, "ti,keystone,main-pll-clock", + of_keystone_main_pll_clk_init); + +/** + * of_pll_div_clk_init - PLL divider setup function + * @node: device tree node for this clock + */ +static void __init of_pll_div_clk_init(struct device_node *node) +{ + const char *parent_name; + void __iomem *reg; + u32 shift, mask; + struct clk *clk; + const char *clk_name = node->name; + + of_property_read_string(node, "clock-output-names", &clk_name); + reg = of_iomap(node, 0); + if (!reg) { + pr_err("%s: ioremap failed\n", __func__); + return; + } + + parent_name = of_clk_get_parent_name(node, 0); + if (!parent_name) { + pr_err("%s: missing parent clock\n", __func__); + return; + } + + if (of_property_read_u32(node, "bit-shift", &shift)) { + pr_err("%s: missing 'shift' property\n", __func__); + return; + } + + if (of_property_read_u32(node, "bit-mask", &mask)) { + pr_err("%s: missing 'bit-mask' property\n", __func__); + return; + } + + clk = clk_register_divider(NULL, clk_name, parent_name, 0, reg, shift, + mask, 0, NULL); + if (clk) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + else + pr_err("%s: error registering divider %s\n", __func__, clk_name); +} +CLK_OF_DECLARE(pll_divider_clock, "ti,keystone,pll-divider-clock", of_pll_div_clk_init); + +/** + * of_pll_mux_clk_init - PLL mux setup function + * @node: device tree node for this clock + */ +static void __init of_pll_mux_clk_init(struct device_node *node) +{ + void __iomem *reg; + u32 shift, mask; + struct clk *clk; + const char *parents[2]; + const char *clk_name = node->name; + + of_property_read_string(node, "clock-output-names", &clk_name); + reg = of_iomap(node, 0); + if (!reg) { + pr_err("%s: ioremap failed\n", __func__); + return; + } + + parents[0] = of_clk_get_parent_name(node, 0); + parents[1] = of_clk_get_parent_name(node, 1); + if (!parents[0] || !parents[1]) { + pr_err("%s: missing parent clocks\n", __func__); + return; + } + + if (of_property_read_u32(node, "bit-shift", &shift)) { + pr_err("%s: missing 'shift' property\n", __func__); + return; + } + + if (of_property_read_u32(node, "bit-mask", &mask)) { + pr_err("%s: missing 'bit-mask' property\n", __func__); + return; + } + + clk = clk_register_mux(NULL, clk_name, (const char **)&parents, + ARRAY_SIZE(parents) , 0, reg, shift, mask, + 0, NULL); + if (clk) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + else + pr_err("%s: error registering mux %s\n", __func__, clk_name); +} +CLK_OF_DECLARE(pll_mux_clock, "ti,keystone,pll-mux-clock", of_pll_mux_clk_init); diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c index c396fe361589..9fc9359f5133 100644 --- a/drivers/clk/mxs/clk-imx23.c +++ b/drivers/clk/mxs/clk-imx23.c @@ -12,6 +12,7 @@ #include <linux/clk.h> #include <linux/clk/mxs.h> #include <linux/clkdev.h> +#include <linux/clk-provider.h> #include <linux/err.h> #include <linux/init.h> #include <linux/io.h> @@ -100,16 +101,16 @@ static enum imx23_clk clks_init_on[] __initdata = { cpu, hbus, xbus, emi, uart, }; -int __init mx23_clocks_init(void) +static void __init mx23_clocks_init(struct device_node *np) { - struct device_node *np; + struct device_node *dcnp; u32 i; - np = of_find_compatible_node(NULL, NULL, "fsl,imx23-digctl"); - digctrl = of_iomap(np, 0); + dcnp = of_find_compatible_node(NULL, NULL, "fsl,imx23-digctl"); + digctrl = of_iomap(dcnp, 0); WARN_ON(!digctrl); + of_node_put(dcnp); - np = of_find_compatible_node(NULL, NULL, "fsl,imx23-clkctrl"); clkctrl = of_iomap(np, 0); WARN_ON(!clkctrl); @@ -162,7 +163,7 @@ int __init mx23_clocks_init(void) 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]); + return; } clk_data.clks = clks; @@ -172,5 +173,5 @@ int __init mx23_clocks_init(void) for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) clk_prepare_enable(clks[clks_init_on[i]]); - return 0; } +CLK_OF_DECLARE(imx23_clkctrl, "fsl,imx23-clkctrl", mx23_clocks_init); diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index 4faf0afc44cd..a6c35010e4e5 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -12,6 +12,7 @@ #include <linux/clk.h> #include <linux/clk/mxs.h> #include <linux/clkdev.h> +#include <linux/clk-provider.h> #include <linux/err.h> #include <linux/init.h> #include <linux/io.h> @@ -154,16 +155,16 @@ static enum imx28_clk clks_init_on[] __initdata = { cpu, hbus, xbus, emi, uart, }; -int __init mx28_clocks_init(void) +static void __init mx28_clocks_init(struct device_node *np) { - struct device_node *np; + struct device_node *dcnp; u32 i; - np = of_find_compatible_node(NULL, NULL, "fsl,imx28-digctl"); - digctrl = of_iomap(np, 0); + dcnp = of_find_compatible_node(NULL, NULL, "fsl,imx28-digctl"); + digctrl = of_iomap(dcnp, 0); WARN_ON(!digctrl); + of_node_put(dcnp); - np = of_find_compatible_node(NULL, NULL, "fsl,imx28-clkctrl"); clkctrl = of_iomap(np, 0); WARN_ON(!clkctrl); @@ -239,7 +240,7 @@ int __init mx28_clocks_init(void) 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]); + return; } clk_data.clks = clks; @@ -250,6 +251,5 @@ int __init mx28_clocks_init(void) for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) clk_prepare_enable(clks[clks_init_on[i]]); - - return 0; } +CLK_OF_DECLARE(imx28_clkctrl, "fsl,imx28-clkctrl", mx28_clocks_init); diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 3413380086d5..8eb4799237f0 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -8,6 +8,4 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o -ifdef CONFIG_COMMON_CLK obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o -endif diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 34ee69f4d50c..9bbd03514540 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -16,7 +16,6 @@ #include <linux/clk-provider.h> #include <linux/clkdev.h> -#include <linux/clk/sunxi.h> #include <linux/of.h> #include <linux/of_address.h> @@ -617,11 +616,8 @@ static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_mat } } -void __init sunxi_init_clocks(void) +static void __init sunxi_init_clocks(struct device_node *np) { - /* Register all the simple and basic clocks on DT */ - of_clk_init(NULL); - /* Register factor clocks */ of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup); @@ -634,3 +630,8 @@ void __init sunxi_init_clocks(void) /* Register gate clocks */ of_sunxi_table_clock_setup(clk_gates_match, sunxi_gates_clk_setup); } +CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sunxi_init_clocks); +CLK_OF_DECLARE(sun5i_a10s_clk_init, "allwinner,sun5i-a10s", sunxi_init_clocks); +CLK_OF_DECLARE(sun5i_a13_clk_init, "allwinner,sun5i-a13", sunxi_init_clocks); +CLK_OF_DECLARE(sun6i_a31_clk_init, "allwinner,sun6i-a31", sunxi_init_clocks); +CLK_OF_DECLARE(sun7i_a20_clk_init, "allwinner,sun7i-a20", sunxi_init_clocks); diff --git a/drivers/clk/ux500/Makefile b/drivers/clk/ux500/Makefile index c6a806ed0e8c..521483f0ba33 100644 --- a/drivers/clk/ux500/Makefile +++ b/drivers/clk/ux500/Makefile @@ -8,6 +8,7 @@ obj-y += clk-prcmu.o obj-y += clk-sysctrl.o # Clock definitions +obj-y += u8500_of_clk.o obj-y += u8500_clk.o obj-y += u9540_clk.o obj-y += u8540_clk.o diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c new file mode 100644 index 000000000000..cdeff299de26 --- /dev/null +++ b/drivers/clk/ux500/u8500_of_clk.c @@ -0,0 +1,559 @@ +/* + * Clock definitions for u8500 platform. + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson <ulf.hansson@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/of.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/mfd/dbx500-prcmu.h> +#include <linux/platform_data/clk-ux500.h> +#include "clk.h" + +#define PRCC_NUM_PERIPH_CLUSTERS 6 +#define PRCC_PERIPHS_PER_CLUSTER 32 + +static struct clk *prcmu_clk[PRCMU_NUM_CLKS]; +static struct clk *prcc_pclk[(PRCC_NUM_PERIPH_CLUSTERS + 1) * PRCC_PERIPHS_PER_CLUSTER]; +static struct clk *prcc_kclk[(PRCC_NUM_PERIPH_CLUSTERS + 1) * PRCC_PERIPHS_PER_CLUSTER]; + +#define PRCC_SHOW(clk, base, bit) \ + clk[(base * PRCC_PERIPHS_PER_CLUSTER) + bit] +#define PRCC_PCLK_STORE(clk, base, bit) \ + prcc_pclk[(base * PRCC_PERIPHS_PER_CLUSTER) + bit] = clk +#define PRCC_KCLK_STORE(clk, base, bit) \ + prcc_kclk[(base * PRCC_PERIPHS_PER_CLUSTER) + bit] = clk + +struct clk *ux500_twocell_get(struct of_phandle_args *clkspec, void *data) +{ + struct clk **clk_data = data; + unsigned int base, bit; + + if (clkspec->args_count != 2) + return ERR_PTR(-EINVAL); + + base = clkspec->args[0]; + bit = clkspec->args[1]; + + if (base != 1 && base != 2 && base != 3 && base != 5 && base != 6) { + pr_err("%s: invalid PRCC base %d\n", __func__, base); + return ERR_PTR(-EINVAL); + } + + return PRCC_SHOW(clk_data, base, bit); +} + +static const struct of_device_id u8500_clk_of_match[] = { + { .compatible = "stericsson,u8500-clks", }, + { }, +}; + +void u8500_of_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base) +{ + struct prcmu_fw_version *fw_version; + struct device_node *np = NULL; + struct device_node *child = NULL; + const char *sgaclk_parent = NULL; + struct clk *clk, *rtc_clk, *twd_clk; + + if (of_have_populated_dt()) + np = of_find_matching_node(NULL, u8500_clk_of_match); + if (!np) { + pr_err("Either DT or U8500 Clock node not found\n"); + return; + } + + /* Clock sources */ + clk = clk_reg_prcmu_gate("soc0_pll", NULL, PRCMU_PLLSOC0, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + prcmu_clk[PRCMU_PLLSOC0] = clk; + + clk = clk_reg_prcmu_gate("soc1_pll", NULL, PRCMU_PLLSOC1, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + prcmu_clk[PRCMU_PLLSOC1] = clk; + + clk = clk_reg_prcmu_gate("ddr_pll", NULL, PRCMU_PLLDDR, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + prcmu_clk[PRCMU_PLLDDR] = clk; + + /* FIXME: Add sys, ulp and int clocks here. */ + + rtc_clk = clk_register_fixed_rate(NULL, "rtc32k", "NULL", + CLK_IS_ROOT|CLK_IGNORE_UNUSED, + 32768); + + /* PRCMU clocks */ + fw_version = prcmu_get_fw_version(); + if (fw_version != NULL) { + switch (fw_version->project) { + case PRCMU_FW_PROJECT_U8500_C2: + case PRCMU_FW_PROJECT_U8520: + case PRCMU_FW_PROJECT_U8420: + sgaclk_parent = "soc0_pll"; + break; + default: + break; + } + } + + if (sgaclk_parent) + clk = clk_reg_prcmu_gate("sgclk", sgaclk_parent, + PRCMU_SGACLK, 0); + else + clk = clk_reg_prcmu_gate("sgclk", NULL, + PRCMU_SGACLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_SGACLK] = clk; + + clk = clk_reg_prcmu_gate("uartclk", NULL, PRCMU_UARTCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_UARTCLK] = clk; + + clk = clk_reg_prcmu_gate("msp02clk", NULL, PRCMU_MSP02CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_MSP02CLK] = clk; + + clk = clk_reg_prcmu_gate("msp1clk", NULL, PRCMU_MSP1CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_MSP1CLK] = clk; + + clk = clk_reg_prcmu_gate("i2cclk", NULL, PRCMU_I2CCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_I2CCLK] = clk; + + clk = clk_reg_prcmu_gate("slimclk", NULL, PRCMU_SLIMCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_SLIMCLK] = clk; + + clk = clk_reg_prcmu_gate("per1clk", NULL, PRCMU_PER1CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_PER1CLK] = clk; + + clk = clk_reg_prcmu_gate("per2clk", NULL, PRCMU_PER2CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_PER2CLK] = clk; + + clk = clk_reg_prcmu_gate("per3clk", NULL, PRCMU_PER3CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_PER3CLK] = clk; + + clk = clk_reg_prcmu_gate("per5clk", NULL, PRCMU_PER5CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_PER5CLK] = clk; + + clk = clk_reg_prcmu_gate("per6clk", NULL, PRCMU_PER6CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_PER6CLK] = clk; + + clk = clk_reg_prcmu_gate("per7clk", NULL, PRCMU_PER7CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_PER7CLK] = clk; + + clk = clk_reg_prcmu_scalable("lcdclk", NULL, PRCMU_LCDCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_LCDCLK] = clk; + + clk = clk_reg_prcmu_opp_gate("bmlclk", NULL, PRCMU_BMLCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_BMLCLK] = clk; + + clk = clk_reg_prcmu_scalable("hsitxclk", NULL, PRCMU_HSITXCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_HSITXCLK] = clk; + + clk = clk_reg_prcmu_scalable("hsirxclk", NULL, PRCMU_HSIRXCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_HSIRXCLK] = clk; + + clk = clk_reg_prcmu_scalable("hdmiclk", NULL, PRCMU_HDMICLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_HDMICLK] = clk; + + clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_APEATCLK] = clk; + + clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK, + CLK_IS_ROOT); + prcmu_clk[PRCMU_APETRACECLK] = clk; + + clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_MCDECLK] = clk; + + clk = clk_reg_prcmu_opp_gate("ipi2cclk", NULL, PRCMU_IPI2CCLK, + CLK_IS_ROOT); + prcmu_clk[PRCMU_IPI2CCLK] = clk; + + clk = clk_reg_prcmu_gate("dsialtclk", NULL, PRCMU_DSIALTCLK, + CLK_IS_ROOT); + prcmu_clk[PRCMU_DSIALTCLK] = clk; + + clk = clk_reg_prcmu_gate("dmaclk", NULL, PRCMU_DMACLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_DMACLK] = clk; + + clk = clk_reg_prcmu_gate("b2r2clk", NULL, PRCMU_B2R2CLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_B2R2CLK] = clk; + + clk = clk_reg_prcmu_scalable("tvclk", NULL, PRCMU_TVCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_TVCLK] = clk; + + clk = clk_reg_prcmu_gate("sspclk", NULL, PRCMU_SSPCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_SSPCLK] = clk; + + clk = clk_reg_prcmu_gate("rngclk", NULL, PRCMU_RNGCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_RNGCLK] = clk; + + clk = clk_reg_prcmu_gate("uiccclk", NULL, PRCMU_UICCCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_UICCCLK] = clk; + + clk = clk_reg_prcmu_gate("timclk", NULL, PRCMU_TIMCLK, CLK_IS_ROOT); + prcmu_clk[PRCMU_TIMCLK] = clk; + + clk = clk_reg_prcmu_opp_volt_scalable("sdmmcclk", NULL, PRCMU_SDMMCCLK, + 100000000, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_SDMMCCLK] = clk; + + clk = clk_reg_prcmu_scalable("dsi_pll", "hdmiclk", + PRCMU_PLLDSI, 0, CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_PLLDSI] = clk; + + clk = clk_reg_prcmu_scalable("dsi0clk", "dsi_pll", + PRCMU_DSI0CLK, 0, CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_DSI0CLK] = clk; + + clk = clk_reg_prcmu_scalable("dsi1clk", "dsi_pll", + PRCMU_DSI1CLK, 0, CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_DSI1CLK] = clk; + + clk = clk_reg_prcmu_scalable("dsi0escclk", "tvclk", + PRCMU_DSI0ESCCLK, 0, CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_DSI0ESCCLK] = clk; + + clk = clk_reg_prcmu_scalable("dsi1escclk", "tvclk", + PRCMU_DSI1ESCCLK, 0, CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_DSI1ESCCLK] = clk; + + clk = clk_reg_prcmu_scalable("dsi2escclk", "tvclk", + PRCMU_DSI2ESCCLK, 0, CLK_SET_RATE_GATE); + prcmu_clk[PRCMU_DSI2ESCCLK] = clk; + + clk = clk_reg_prcmu_scalable_rate("armss", NULL, + PRCMU_ARMSS, 0, CLK_IS_ROOT|CLK_IGNORE_UNUSED); + prcmu_clk[PRCMU_ARMSS] = clk; + + twd_clk = clk_register_fixed_factor(NULL, "smp_twd", "armss", + CLK_IGNORE_UNUSED, 1, 2); + + /* + * FIXME: Add special handled PRCMU clocks here: + * 1. clkout0yuv, use PRCMU as parent + need regulator + pinctrl. + * 2. ab9540_clkout1yuv, see clkout0yuv + */ + + /* PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p1_pclk0", "per1clk", clkrst1_base, + BIT(0), 0); + PRCC_PCLK_STORE(clk, 1, 0); + + clk = clk_reg_prcc_pclk("p1_pclk1", "per1clk", clkrst1_base, + BIT(1), 0); + PRCC_PCLK_STORE(clk, 1, 1); + + clk = clk_reg_prcc_pclk("p1_pclk2", "per1clk", clkrst1_base, + BIT(2), 0); + PRCC_PCLK_STORE(clk, 1, 2); + + clk = clk_reg_prcc_pclk("p1_pclk3", "per1clk", clkrst1_base, + BIT(3), 0); + PRCC_PCLK_STORE(clk, 1, 3); + + clk = clk_reg_prcc_pclk("p1_pclk4", "per1clk", clkrst1_base, + BIT(4), 0); + PRCC_PCLK_STORE(clk, 1, 4); + + clk = clk_reg_prcc_pclk("p1_pclk5", "per1clk", clkrst1_base, + BIT(5), 0); + PRCC_PCLK_STORE(clk, 1, 5); + + clk = clk_reg_prcc_pclk("p1_pclk6", "per1clk", clkrst1_base, + BIT(6), 0); + PRCC_PCLK_STORE(clk, 1, 6); + + clk = clk_reg_prcc_pclk("p1_pclk7", "per1clk", clkrst1_base, + BIT(7), 0); + PRCC_PCLK_STORE(clk, 1, 7); + + clk = clk_reg_prcc_pclk("p1_pclk8", "per1clk", clkrst1_base, + BIT(8), 0); + PRCC_PCLK_STORE(clk, 1, 8); + + clk = clk_reg_prcc_pclk("p1_pclk9", "per1clk", clkrst1_base, + BIT(9), 0); + PRCC_PCLK_STORE(clk, 1, 9); + + clk = clk_reg_prcc_pclk("p1_pclk10", "per1clk", clkrst1_base, + BIT(10), 0); + PRCC_PCLK_STORE(clk, 1, 10); + + clk = clk_reg_prcc_pclk("p1_pclk11", "per1clk", clkrst1_base, + BIT(11), 0); + PRCC_PCLK_STORE(clk, 1, 11); + + clk = clk_reg_prcc_pclk("p2_pclk0", "per2clk", clkrst2_base, + BIT(0), 0); + PRCC_PCLK_STORE(clk, 2, 0); + + clk = clk_reg_prcc_pclk("p2_pclk1", "per2clk", clkrst2_base, + BIT(1), 0); + PRCC_PCLK_STORE(clk, 2, 1); + + clk = clk_reg_prcc_pclk("p2_pclk2", "per2clk", clkrst2_base, + BIT(2), 0); + PRCC_PCLK_STORE(clk, 2, 2); + + clk = clk_reg_prcc_pclk("p2_pclk3", "per2clk", clkrst2_base, + BIT(3), 0); + PRCC_PCLK_STORE(clk, 2, 3); + + clk = clk_reg_prcc_pclk("p2_pclk4", "per2clk", clkrst2_base, + BIT(4), 0); + PRCC_PCLK_STORE(clk, 2, 4); + + clk = clk_reg_prcc_pclk("p2_pclk5", "per2clk", clkrst2_base, + BIT(5), 0); + PRCC_PCLK_STORE(clk, 2, 5); + + clk = clk_reg_prcc_pclk("p2_pclk6", "per2clk", clkrst2_base, + BIT(6), 0); + PRCC_PCLK_STORE(clk, 2, 6); + + clk = clk_reg_prcc_pclk("p2_pclk7", "per2clk", clkrst2_base, + BIT(7), 0); + PRCC_PCLK_STORE(clk, 2, 7); + + clk = clk_reg_prcc_pclk("p2_pclk8", "per2clk", clkrst2_base, + BIT(8), 0); + PRCC_PCLK_STORE(clk, 2, 8); + + clk = clk_reg_prcc_pclk("p2_pclk9", "per2clk", clkrst2_base, + BIT(9), 0); + PRCC_PCLK_STORE(clk, 2, 9); + + clk = clk_reg_prcc_pclk("p2_pclk10", "per2clk", clkrst2_base, + BIT(10), 0); + PRCC_PCLK_STORE(clk, 2, 10); + + clk = clk_reg_prcc_pclk("p2_pclk11", "per2clk", clkrst2_base, + BIT(11), 0); + PRCC_PCLK_STORE(clk, 2, 11); + + clk = clk_reg_prcc_pclk("p2_pclk12", "per2clk", clkrst2_base, + BIT(12), 0); + PRCC_PCLK_STORE(clk, 2, 12); + + clk = clk_reg_prcc_pclk("p3_pclk0", "per3clk", clkrst3_base, + BIT(0), 0); + PRCC_PCLK_STORE(clk, 3, 0); + + clk = clk_reg_prcc_pclk("p3_pclk1", "per3clk", clkrst3_base, + BIT(1), 0); + PRCC_PCLK_STORE(clk, 3, 1); + + clk = clk_reg_prcc_pclk("p3_pclk2", "per3clk", clkrst3_base, + BIT(2), 0); + PRCC_PCLK_STORE(clk, 3, 2); + + clk = clk_reg_prcc_pclk("p3_pclk3", "per3clk", clkrst3_base, + BIT(3), 0); + PRCC_PCLK_STORE(clk, 3, 3); + + clk = clk_reg_prcc_pclk("p3_pclk4", "per3clk", clkrst3_base, + BIT(4), 0); + PRCC_PCLK_STORE(clk, 3, 4); + + clk = clk_reg_prcc_pclk("p3_pclk5", "per3clk", clkrst3_base, + BIT(5), 0); + PRCC_PCLK_STORE(clk, 3, 5); + + clk = clk_reg_prcc_pclk("p3_pclk6", "per3clk", clkrst3_base, + BIT(6), 0); + PRCC_PCLK_STORE(clk, 3, 6); + + clk = clk_reg_prcc_pclk("p3_pclk7", "per3clk", clkrst3_base, + BIT(7), 0); + PRCC_PCLK_STORE(clk, 3, 7); + + clk = clk_reg_prcc_pclk("p3_pclk8", "per3clk", clkrst3_base, + BIT(8), 0); + PRCC_PCLK_STORE(clk, 3, 8); + + clk = clk_reg_prcc_pclk("p5_pclk0", "per5clk", clkrst5_base, + BIT(0), 0); + PRCC_PCLK_STORE(clk, 5, 0); + + clk = clk_reg_prcc_pclk("p5_pclk1", "per5clk", clkrst5_base, + BIT(1), 0); + PRCC_PCLK_STORE(clk, 5, 1); + + clk = clk_reg_prcc_pclk("p6_pclk0", "per6clk", clkrst6_base, + BIT(0), 0); + PRCC_PCLK_STORE(clk, 6, 0); + + clk = clk_reg_prcc_pclk("p6_pclk1", "per6clk", clkrst6_base, + BIT(1), 0); + PRCC_PCLK_STORE(clk, 6, 1); + + clk = clk_reg_prcc_pclk("p6_pclk2", "per6clk", clkrst6_base, + BIT(2), 0); + PRCC_PCLK_STORE(clk, 6, 2); + + clk = clk_reg_prcc_pclk("p6_pclk3", "per6clk", clkrst6_base, + BIT(3), 0); + PRCC_PCLK_STORE(clk, 6, 3); + + clk = clk_reg_prcc_pclk("p6_pclk4", "per6clk", clkrst6_base, + BIT(4), 0); + PRCC_PCLK_STORE(clk, 6, 4); + + clk = clk_reg_prcc_pclk("p6_pclk5", "per6clk", clkrst6_base, + BIT(5), 0); + PRCC_PCLK_STORE(clk, 6, 5); + + clk = clk_reg_prcc_pclk("p6_pclk6", "per6clk", clkrst6_base, + BIT(6), 0); + PRCC_PCLK_STORE(clk, 6, 6); + + clk = clk_reg_prcc_pclk("p6_pclk7", "per6clk", clkrst6_base, + BIT(7), 0); + PRCC_PCLK_STORE(clk, 6, 7); + + /* PRCC K-clocks + * + * FIXME: Some drivers requires PERPIH[n| to be automatically enabled + * by enabling just the K-clock, even if it is not a valid parent to + * the K-clock. Until drivers get fixed we might need some kind of + * "parent muxed join". + */ + + /* Periph1 */ + clk = clk_reg_prcc_kclk("p1_uart0_kclk", "uartclk", + clkrst1_base, BIT(0), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 0); + + clk = clk_reg_prcc_kclk("p1_uart1_kclk", "uartclk", + clkrst1_base, BIT(1), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 1); + + clk = clk_reg_prcc_kclk("p1_i2c1_kclk", "i2cclk", + clkrst1_base, BIT(2), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 2); + + clk = clk_reg_prcc_kclk("p1_msp0_kclk", "msp02clk", + clkrst1_base, BIT(3), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 3); + + clk = clk_reg_prcc_kclk("p1_msp1_kclk", "msp1clk", + clkrst1_base, BIT(4), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 4); + + clk = clk_reg_prcc_kclk("p1_sdi0_kclk", "sdmmcclk", + clkrst1_base, BIT(5), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 5); + + clk = clk_reg_prcc_kclk("p1_i2c2_kclk", "i2cclk", + clkrst1_base, BIT(6), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 6); + + clk = clk_reg_prcc_kclk("p1_slimbus0_kclk", "slimclk", + clkrst1_base, BIT(8), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 8); + + clk = clk_reg_prcc_kclk("p1_i2c4_kclk", "i2cclk", + clkrst1_base, BIT(9), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 9); + + clk = clk_reg_prcc_kclk("p1_msp3_kclk", "msp1clk", + clkrst1_base, BIT(10), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 1, 10); + + /* Periph2 */ + clk = clk_reg_prcc_kclk("p2_i2c3_kclk", "i2cclk", + clkrst2_base, BIT(0), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 2, 0); + + clk = clk_reg_prcc_kclk("p2_sdi4_kclk", "sdmmcclk", + clkrst2_base, BIT(2), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 2, 2); + + clk = clk_reg_prcc_kclk("p2_msp2_kclk", "msp02clk", + clkrst2_base, BIT(3), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 2, 3); + + clk = clk_reg_prcc_kclk("p2_sdi1_kclk", "sdmmcclk", + clkrst2_base, BIT(4), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 2, 4); + + clk = clk_reg_prcc_kclk("p2_sdi3_kclk", "sdmmcclk", + clkrst2_base, BIT(5), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 2, 5); + + /* Note that rate is received from parent. */ + clk = clk_reg_prcc_kclk("p2_ssirx_kclk", "hsirxclk", + clkrst2_base, BIT(6), + CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT); + PRCC_KCLK_STORE(clk, 2, 6); + + clk = clk_reg_prcc_kclk("p2_ssitx_kclk", "hsitxclk", + clkrst2_base, BIT(7), + CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT); + PRCC_KCLK_STORE(clk, 2, 7); + + /* Periph3 */ + clk = clk_reg_prcc_kclk("p3_ssp0_kclk", "sspclk", + clkrst3_base, BIT(1), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 3, 1); + + clk = clk_reg_prcc_kclk("p3_ssp1_kclk", "sspclk", + clkrst3_base, BIT(2), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 3, 2); + + clk = clk_reg_prcc_kclk("p3_i2c0_kclk", "i2cclk", + clkrst3_base, BIT(3), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 3, 3); + + clk = clk_reg_prcc_kclk("p3_sdi2_kclk", "sdmmcclk", + clkrst3_base, BIT(4), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 3, 4); + + clk = clk_reg_prcc_kclk("p3_ske_kclk", "rtc32k", + clkrst3_base, BIT(5), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 3, 5); + + clk = clk_reg_prcc_kclk("p3_uart2_kclk", "uartclk", + clkrst3_base, BIT(6), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 3, 6); + + clk = clk_reg_prcc_kclk("p3_sdi5_kclk", "sdmmcclk", + clkrst3_base, BIT(7), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 3, 7); + + /* Periph6 */ + clk = clk_reg_prcc_kclk("p3_rng_kclk", "rngclk", + clkrst6_base, BIT(0), CLK_SET_RATE_GATE); + PRCC_KCLK_STORE(clk, 6, 0); + + for_each_child_of_node(np, child) { + static struct clk_onecell_data clk_data; + + if (!of_node_cmp(child->name, "prcmu-clock")) { + clk_data.clks = prcmu_clk; + clk_data.clk_num = ARRAY_SIZE(prcmu_clk); + of_clk_add_provider(child, of_clk_src_onecell_get, &clk_data); + } + if (!of_node_cmp(child->name, "prcc-periph-clock")) + of_clk_add_provider(child, ux500_twocell_get, prcc_pclk); + + if (!of_node_cmp(child->name, "prcc-kernel-clock")) + of_clk_add_provider(child, ux500_twocell_get, prcc_kclk); + + if (!of_node_cmp(child->name, "rtc32k-clock")) + of_clk_add_provider(child, of_clk_src_simple_get, rtc_clk); + + if (!of_node_cmp(child->name, "smp-twd-clock")) + of_clk_add_provider(child, of_clk_src_simple_get, twd_clk); + } +} diff --git a/drivers/clk/ux500/u8540_clk.c b/drivers/clk/ux500/u8540_clk.c index f26258869deb..20c8add90d11 100644 --- a/drivers/clk/ux500/u8540_clk.c +++ b/drivers/clk/ux500/u8540_clk.c @@ -83,7 +83,7 @@ void u8540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, clk_register_clkdev(clk, NULL, "lcd"); clk_register_clkdev(clk, "lcd", "mcde"); - clk = clk_reg_prcmu_opp_gate("bmlclk", NULL, PRCMU_BML8580CLK, + clk = clk_reg_prcmu_opp_gate("bmlclk", NULL, PRCMU_BMLCLK, CLK_IS_ROOT); clk_register_clkdev(clk, NULL, "bml"); diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index cc40fe64f2dc..10772aa72e4e 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -117,13 +117,19 @@ static void __init zynq_clk_register_fclk(enum zynq_clk fclk, goto err; fclk_gate_lock = kmalloc(sizeof(*fclk_gate_lock), GFP_KERNEL); if (!fclk_gate_lock) - goto err; + goto err_fclk_gate_lock; spin_lock_init(fclk_lock); spin_lock_init(fclk_gate_lock); mux_name = kasprintf(GFP_KERNEL, "%s_mux", clk_name); + if (!mux_name) + goto err_mux_name; div0_name = kasprintf(GFP_KERNEL, "%s_div0", clk_name); + if (!div0_name) + goto err_div0_name; div1_name = kasprintf(GFP_KERNEL, "%s_div1", clk_name); + if (!div1_name) + goto err_div1_name; clk = clk_register_mux(NULL, mux_name, parents, 4, CLK_SET_RATE_NO_REPARENT, fclk_ctrl_reg, 4, 2, 0, @@ -147,6 +153,14 @@ static void __init zynq_clk_register_fclk(enum zynq_clk fclk, return; +err_div1_name: + kfree(div0_name); +err_div0_name: + kfree(mux_name); +err_mux_name: + kfree(fclk_gate_lock); +err_fclk_gate_lock: + kfree(fclk_lock); err: clks[fclk] = ERR_PTR(-ENOMEM); } |