diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-08 20:14:29 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-08 20:14:29 +0200 |
commit | b3345d7c57d70e6cb6749af25cdbe80515582e99 (patch) | |
tree | 04cce706bc7e944ad1fb257108a8ae735948f97f /drivers | |
parent | Merge tag 'cleanup-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git... (diff) | |
parent | ARM: hisi: remove smp from machine descriptor (diff) | |
download | linux-b3345d7c57d70e6cb6749af25cdbe80515582e99.tar.xz linux-b3345d7c57d70e6cb6749af25cdbe80515582e99.zip |
Merge tag 'soc-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC platform changes from Olof Johansson:
"This is the bulk of new SoC enablement and other platform changes for
3.17:
- Samsung S5PV210 has been converted to DT and multiplatform
- Clock drivers and bindings for some of the lower-end i.MX 1/2
platforms
- Kirkwood, one of the popular Marvell platforms, is folded into the
mvebu platform code, removing mach-kirkwood
- Hwmod data for TI AM43xx and DRA7 platforms
- More additions of Renesas shmobile platform support
- Removal of plat-samsung contents that can be removed with S5PV210
being multiplatform/DT-enabled and the other two old platforms
being removed
New platforms (most with only basic support right now):
- Hisilicon X5HD2 settop box chipset is introduced
- Mediatek MT6589 (mobile chipset) is introduced
- Broadcom BCM7xxx settop box chipset is introduced
+ as usual a lot other pieces all over the platform code"
* tag 'soc-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (240 commits)
ARM: hisi: remove smp from machine descriptor
power: reset: move hisilicon reboot code
ARM: dts: Add hix5hd2-dkb dts file.
ARM: debug: Rename Hi3716 to HIX5HD2
ARM: hisi: enable hix5hd2 SoC
ARM: hisi: add ARCH_HISI
MAINTAINERS: add entry for Broadcom ARM STB architecture
ARM: brcmstb: select GISB arbiter and interrupt drivers
ARM: brcmstb: add infrastructure for ARM-based Broadcom STB SoCs
ARM: configs: enable SMP in bcm_defconfig
ARM: add SMP support for Broadcom mobile SoCs
Documentation: arm: misc updates to Marvell EBU SoC status
Documentation: arm: add URLs to public datasheets for the Marvell Armada XP SoC
ARM: mvebu: fix build without platforms selected
ARM: mvebu: add cpuidle support for Armada 38x
ARM: mvebu: add cpuidle support for Armada 370
cpuidle: mvebu: add Armada 38x support
cpuidle: mvebu: add Armada 370 support
cpuidle: mvebu: rename the driver from armada-370-xp to mvebu-v7
ARM: mvebu: export the SCU address
...
Diffstat (limited to 'drivers')
29 files changed, 1812 insertions, 376 deletions
diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index f8ee13c7bf7b..75c9681f8021 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c @@ -162,7 +162,9 @@ static int __init weim_parse_dt(struct platform_device *pdev, } } - ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + ret = of_platform_populate(pdev->dev.of_node, + of_default_bus_match_table, + NULL, &pdev->dev); if (ret) dev_err(&pdev->dev, "%s fail to create devices.\n", pdev->dev.of_node->full_name); diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c index 8ebf757d29e2..3821a88077ea 100644 --- a/drivers/clk/mvebu/clk-cpu.c +++ b/drivers/clk/mvebu/clk-cpu.c @@ -16,10 +16,19 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/delay.h> +#include <linux/mvebu-pmsu.h> +#include <asm/smp_plat.h> -#define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0 -#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC -#define SYS_CTRL_CLK_DIVIDER_MASK 0x3F +#define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0 +#define SYS_CTRL_CLK_DIVIDER_CTRL_RESET_ALL 0xff +#define SYS_CTRL_CLK_DIVIDER_CTRL_RESET_SHIFT 8 +#define SYS_CTRL_CLK_DIVIDER_CTRL2_OFFSET 0x8 +#define SYS_CTRL_CLK_DIVIDER_CTRL2_NBCLK_RATIO_SHIFT 16 +#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC +#define SYS_CTRL_CLK_DIVIDER_MASK 0x3F + +#define PMU_DFS_RATIO_SHIFT 16 +#define PMU_DFS_RATIO_MASK 0x3F #define MAX_CPU 4 struct cpu_clk { @@ -28,6 +37,7 @@ struct cpu_clk { const char *clk_name; const char *parent_name; void __iomem *reg_base; + void __iomem *pmu_dfs; }; static struct clk **clks; @@ -62,8 +72,9 @@ static long clk_cpu_round_rate(struct clk_hw *hwclk, unsigned long rate, return *parent_rate / div; } -static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, - unsigned long parent_rate) +static int clk_cpu_off_set_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long parent_rate) + { struct cpu_clk *cpuclk = to_cpu_clk(hwclk); u32 reg, div; @@ -95,6 +106,58 @@ static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, return 0; } +static int clk_cpu_on_set_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long parent_rate) +{ + u32 reg; + unsigned long fabric_div, target_div, cur_rate; + struct cpu_clk *cpuclk = to_cpu_clk(hwclk); + + /* + * PMU DFS registers are not mapped, Device Tree does not + * describes them. We cannot change the frequency dynamically. + */ + if (!cpuclk->pmu_dfs) + return -ENODEV; + + cur_rate = __clk_get_rate(hwclk->clk); + + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL2_OFFSET); + fabric_div = (reg >> SYS_CTRL_CLK_DIVIDER_CTRL2_NBCLK_RATIO_SHIFT) & + SYS_CTRL_CLK_DIVIDER_MASK; + + /* Frequency is going up */ + if (rate == 2 * cur_rate) + target_div = fabric_div / 2; + /* Frequency is going down */ + else + target_div = fabric_div; + + if (target_div == 0) + target_div = 1; + + reg = readl(cpuclk->pmu_dfs); + reg &= ~(PMU_DFS_RATIO_MASK << PMU_DFS_RATIO_SHIFT); + reg |= (target_div << PMU_DFS_RATIO_SHIFT); + writel(reg, cpuclk->pmu_dfs); + + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + reg |= (SYS_CTRL_CLK_DIVIDER_CTRL_RESET_ALL << + SYS_CTRL_CLK_DIVIDER_CTRL_RESET_SHIFT); + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + + return mvebu_pmsu_dfs_request(cpuclk->cpu); +} + +static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long parent_rate) +{ + if (__clk_is_enabled(hwclk->clk)) + return clk_cpu_on_set_rate(hwclk, rate, parent_rate); + else + return clk_cpu_off_set_rate(hwclk, rate, parent_rate); +} + static const struct clk_ops cpu_ops = { .recalc_rate = clk_cpu_recalc_rate, .round_rate = clk_cpu_round_rate, @@ -105,6 +168,7 @@ static void __init of_cpu_clk_setup(struct device_node *node) { struct cpu_clk *cpuclk; void __iomem *clock_complex_base = of_iomap(node, 0); + void __iomem *pmu_dfs_base = of_iomap(node, 1); int ncpus = 0; struct device_node *dn; @@ -114,6 +178,10 @@ static void __init of_cpu_clk_setup(struct device_node *node) return; } + if (pmu_dfs_base == NULL) + pr_warn("%s: pmu-dfs base register not set, dynamic frequency scaling not available\n", + __func__); + for_each_node_by_type(dn, "cpu") ncpus++; @@ -146,6 +214,8 @@ static void __init of_cpu_clk_setup(struct device_node *node) cpuclk[cpu].clk_name = clk_name; cpuclk[cpu].cpu = cpu; cpuclk[cpu].reg_base = clock_complex_base; + if (pmu_dfs_base) + cpuclk[cpu].pmu_dfs = pmu_dfs_base + 4 * cpu; cpuclk[cpu].hw.init = &init; init.name = cpuclk[cpu].clk_name; diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 2949a556af8f..6fb4bc602e8a 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_S3C2410_COMMON_DCLK)+= clk-s3c2410-dclk.o obj-$(CONFIG_S3C2412_COMMON_CLK)+= clk-s3c2412.o obj-$(CONFIG_S3C2443_COMMON_CLK)+= clk-s3c2443.o obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o +obj-$(CONFIG_ARCH_S5PV210) += clk-s5pv210.o clk-s5pv210-audss.o diff --git a/drivers/clk/samsung/clk-s5pv210-audss.c b/drivers/clk/samsung/clk-s5pv210-audss.c new file mode 100644 index 000000000000..a8053b4aca56 --- /dev/null +++ b/drivers/clk/samsung/clk-s5pv210-audss.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2014 Tomasz Figa <t.figa@samsung.com> + * + * Based on Exynos Audio Subsystem Clock Controller driver: + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Author: Padmavathi Venna <padma.v@samsung.com> + * + * 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. + * + * Driver for Audio Subsystem Clock Controller of S5PV210-compatible SoCs. +*/ + +#include <linux/clkdev.h> +#include <linux/io.h> +#include <linux/clk-provider.h> +#include <linux/of_address.h> +#include <linux/syscore_ops.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <dt-bindings/clock/s5pv210-audss.h> + +static DEFINE_SPINLOCK(lock); +static struct clk **clk_table; +static void __iomem *reg_base; +static struct clk_onecell_data clk_data; + +#define ASS_CLK_SRC 0x0 +#define ASS_CLK_DIV 0x4 +#define ASS_CLK_GATE 0x8 + +#ifdef CONFIG_PM_SLEEP +static unsigned long reg_save[][2] = { + {ASS_CLK_SRC, 0}, + {ASS_CLK_DIV, 0}, + {ASS_CLK_GATE, 0}, +}; + +static int s5pv210_audss_clk_suspend(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(reg_save); i++) + reg_save[i][1] = readl(reg_base + reg_save[i][0]); + + return 0; +} + +static void s5pv210_audss_clk_resume(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(reg_save); i++) + writel(reg_save[i][1], reg_base + reg_save[i][0]); +} + +static struct syscore_ops s5pv210_audss_clk_syscore_ops = { + .suspend = s5pv210_audss_clk_suspend, + .resume = s5pv210_audss_clk_resume, +}; +#endif /* CONFIG_PM_SLEEP */ + +/* register s5pv210_audss clocks */ +static int s5pv210_audss_clk_probe(struct platform_device *pdev) +{ + int i, ret = 0; + struct resource *res; + const char *mout_audss_p[2]; + const char *mout_i2s_p[3]; + const char *hclk_p; + struct clk *hclk, *pll_ref, *pll_in, *cdclk, *sclk_audio; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(reg_base)) { + dev_err(&pdev->dev, "failed to map audss registers\n"); + return PTR_ERR(reg_base); + } + + clk_table = devm_kzalloc(&pdev->dev, + sizeof(struct clk *) * AUDSS_MAX_CLKS, + GFP_KERNEL); + if (!clk_table) + return -ENOMEM; + + clk_data.clks = clk_table; + clk_data.clk_num = AUDSS_MAX_CLKS; + + hclk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(hclk)) { + dev_err(&pdev->dev, "failed to get hclk clock\n"); + return PTR_ERR(hclk); + } + + pll_in = devm_clk_get(&pdev->dev, "fout_epll"); + if (IS_ERR(pll_in)) { + dev_err(&pdev->dev, "failed to get fout_epll clock\n"); + return PTR_ERR(pll_in); + } + + sclk_audio = devm_clk_get(&pdev->dev, "sclk_audio0"); + if (IS_ERR(sclk_audio)) { + dev_err(&pdev->dev, "failed to get sclk_audio0 clock\n"); + return PTR_ERR(sclk_audio); + } + + /* iiscdclk0 is an optional external I2S codec clock */ + cdclk = devm_clk_get(&pdev->dev, "iiscdclk0"); + pll_ref = devm_clk_get(&pdev->dev, "xxti"); + + if (!IS_ERR(pll_ref)) + mout_audss_p[0] = __clk_get_name(pll_ref); + else + mout_audss_p[0] = "xxti"; + mout_audss_p[1] = __clk_get_name(pll_in); + clk_table[CLK_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss", + mout_audss_p, ARRAY_SIZE(mout_audss_p), + CLK_SET_RATE_NO_REPARENT, + reg_base + ASS_CLK_SRC, 0, 1, 0, &lock); + + mout_i2s_p[0] = "mout_audss"; + if (!IS_ERR(cdclk)) + mout_i2s_p[1] = __clk_get_name(cdclk); + else + mout_i2s_p[1] = "iiscdclk0"; + mout_i2s_p[2] = __clk_get_name(sclk_audio); + clk_table[CLK_MOUT_I2S_A] = clk_register_mux(NULL, "mout_i2s_audss", + mout_i2s_p, ARRAY_SIZE(mout_i2s_p), + CLK_SET_RATE_NO_REPARENT, + reg_base + ASS_CLK_SRC, 2, 2, 0, &lock); + + clk_table[CLK_DOUT_AUD_BUS] = clk_register_divider(NULL, + "dout_aud_bus", "mout_audss", 0, + reg_base + ASS_CLK_DIV, 0, 4, 0, &lock); + clk_table[CLK_DOUT_I2S_A] = clk_register_divider(NULL, "dout_i2s_audss", + "mout_i2s_audss", 0, reg_base + ASS_CLK_DIV, + 4, 4, 0, &lock); + + clk_table[CLK_I2S] = clk_register_gate(NULL, "i2s_audss", + "dout_i2s_audss", CLK_SET_RATE_PARENT, + reg_base + ASS_CLK_GATE, 6, 0, &lock); + + hclk_p = __clk_get_name(hclk); + + clk_table[CLK_HCLK_I2S] = clk_register_gate(NULL, "hclk_i2s_audss", + hclk_p, CLK_IGNORE_UNUSED, + reg_base + ASS_CLK_GATE, 5, 0, &lock); + clk_table[CLK_HCLK_UART] = clk_register_gate(NULL, "hclk_uart_audss", + hclk_p, CLK_IGNORE_UNUSED, + reg_base + ASS_CLK_GATE, 4, 0, &lock); + clk_table[CLK_HCLK_HWA] = clk_register_gate(NULL, "hclk_hwa_audss", + hclk_p, CLK_IGNORE_UNUSED, + reg_base + ASS_CLK_GATE, 3, 0, &lock); + clk_table[CLK_HCLK_DMA] = clk_register_gate(NULL, "hclk_dma_audss", + hclk_p, CLK_IGNORE_UNUSED, + reg_base + ASS_CLK_GATE, 2, 0, &lock); + clk_table[CLK_HCLK_BUF] = clk_register_gate(NULL, "hclk_buf_audss", + hclk_p, CLK_IGNORE_UNUSED, + reg_base + ASS_CLK_GATE, 1, 0, &lock); + clk_table[CLK_HCLK_RP] = clk_register_gate(NULL, "hclk_rp_audss", + hclk_p, CLK_IGNORE_UNUSED, + reg_base + ASS_CLK_GATE, 0, 0, &lock); + + for (i = 0; i < clk_data.clk_num; i++) { + if (IS_ERR(clk_table[i])) { + dev_err(&pdev->dev, "failed to register clock %d\n", i); + ret = PTR_ERR(clk_table[i]); + goto unregister; + } + } + + ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, + &clk_data); + if (ret) { + dev_err(&pdev->dev, "failed to add clock provider\n"); + goto unregister; + } + +#ifdef CONFIG_PM_SLEEP + register_syscore_ops(&s5pv210_audss_clk_syscore_ops); +#endif + + return 0; + +unregister: + for (i = 0; i < clk_data.clk_num; i++) { + if (!IS_ERR(clk_table[i])) + clk_unregister(clk_table[i]); + } + + return ret; +} + +static int s5pv210_audss_clk_remove(struct platform_device *pdev) +{ + int i; + + of_clk_del_provider(pdev->dev.of_node); + + for (i = 0; i < clk_data.clk_num; i++) { + if (!IS_ERR(clk_table[i])) + clk_unregister(clk_table[i]); + } + + return 0; +} + +static const struct of_device_id s5pv210_audss_clk_of_match[] = { + { .compatible = "samsung,s5pv210-audss-clock", }, + {}, +}; + +static struct platform_driver s5pv210_audss_clk_driver = { + .driver = { + .name = "s5pv210-audss-clk", + .owner = THIS_MODULE, + .of_match_table = s5pv210_audss_clk_of_match, + }, + .probe = s5pv210_audss_clk_probe, + .remove = s5pv210_audss_clk_remove, +}; + +static int __init s5pv210_audss_clk_init(void) +{ + return platform_driver_register(&s5pv210_audss_clk_driver); +} +core_initcall(s5pv210_audss_clk_init); + +static void __exit s5pv210_audss_clk_exit(void) +{ + platform_driver_unregister(&s5pv210_audss_clk_driver); +} +module_exit(s5pv210_audss_clk_exit); + +MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>"); +MODULE_DESCRIPTION("S5PV210 Audio Subsystem Clock Controller"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:s5pv210-audss-clk"); diff --git a/drivers/clk/samsung/clk-s5pv210.c b/drivers/clk/samsung/clk-s5pv210.c new file mode 100644 index 000000000000..d270a2084644 --- /dev/null +++ b/drivers/clk/samsung/clk-s5pv210.c @@ -0,0 +1,856 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Author: Mateusz Krawczuk <m.krawczuk@partner.samsung.com> + * + * Based on clock drivers for S3C64xx and Exynos4 SoCs. + * + * 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. + * + * Common Clock Framework support for all S5PC110/S5PV210 SoCs. + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/syscore_ops.h> + +#include "clk.h" +#include "clk-pll.h" + +#include <dt-bindings/clock/s5pv210.h> + +/* S5PC110/S5PV210 clock controller register offsets */ +#define APLL_LOCK 0x0000 +#define MPLL_LOCK 0x0008 +#define EPLL_LOCK 0x0010 +#define VPLL_LOCK 0x0020 +#define APLL_CON0 0x0100 +#define APLL_CON1 0x0104 +#define MPLL_CON 0x0108 +#define EPLL_CON0 0x0110 +#define EPLL_CON1 0x0114 +#define VPLL_CON 0x0120 +#define CLK_SRC0 0x0200 +#define CLK_SRC1 0x0204 +#define CLK_SRC2 0x0208 +#define CLK_SRC3 0x020c +#define CLK_SRC4 0x0210 +#define CLK_SRC5 0x0214 +#define CLK_SRC6 0x0218 +#define CLK_SRC_MASK0 0x0280 +#define CLK_SRC_MASK1 0x0284 +#define CLK_DIV0 0x0300 +#define CLK_DIV1 0x0304 +#define CLK_DIV2 0x0308 +#define CLK_DIV3 0x030c +#define CLK_DIV4 0x0310 +#define CLK_DIV5 0x0314 +#define CLK_DIV6 0x0318 +#define CLK_DIV7 0x031c +#define CLK_GATE_MAIN0 0x0400 +#define CLK_GATE_MAIN1 0x0404 +#define CLK_GATE_MAIN2 0x0408 +#define CLK_GATE_PERI0 0x0420 +#define CLK_GATE_PERI1 0x0424 +#define CLK_GATE_SCLK0 0x0440 +#define CLK_GATE_SCLK1 0x0444 +#define CLK_GATE_IP0 0x0460 +#define CLK_GATE_IP1 0x0464 +#define CLK_GATE_IP2 0x0468 +#define CLK_GATE_IP3 0x046c +#define CLK_GATE_IP4 0x0470 +#define CLK_GATE_BLOCK 0x0480 +#define CLK_GATE_IP5 0x0484 +#define CLK_OUT 0x0500 +#define MISC 0xe000 +#define OM_STAT 0xe100 + +/* IDs of PLLs available on S5PV210/S5P6442 SoCs */ +enum { + apll, + mpll, + epll, + vpll, +}; + +/* IDs of external clocks (used for legacy boards) */ +enum { + xxti, + xusbxti, +}; + +static void __iomem *reg_base; + +#ifdef CONFIG_PM_SLEEP +static struct samsung_clk_reg_dump *s5pv210_clk_dump; + +/* List of registers that need to be preserved across suspend/resume. */ +static unsigned long s5pv210_clk_regs[] __initdata = { + CLK_SRC0, + CLK_SRC1, + CLK_SRC2, + CLK_SRC3, + CLK_SRC4, + CLK_SRC5, + CLK_SRC6, + CLK_SRC_MASK0, + CLK_SRC_MASK1, + CLK_DIV0, + CLK_DIV1, + CLK_DIV2, + CLK_DIV3, + CLK_DIV4, + CLK_DIV5, + CLK_DIV6, + CLK_DIV7, + CLK_GATE_MAIN0, + CLK_GATE_MAIN1, + CLK_GATE_MAIN2, + CLK_GATE_PERI0, + CLK_GATE_PERI1, + CLK_GATE_SCLK0, + CLK_GATE_SCLK1, + CLK_GATE_IP0, + CLK_GATE_IP1, + CLK_GATE_IP2, + CLK_GATE_IP3, + CLK_GATE_IP4, + CLK_GATE_IP5, + CLK_GATE_BLOCK, + APLL_LOCK, + MPLL_LOCK, + EPLL_LOCK, + VPLL_LOCK, + APLL_CON0, + APLL_CON1, + MPLL_CON, + EPLL_CON0, + EPLL_CON1, + VPLL_CON, + CLK_OUT, +}; + +static int s5pv210_clk_suspend(void) +{ + samsung_clk_save(reg_base, s5pv210_clk_dump, + ARRAY_SIZE(s5pv210_clk_regs)); + return 0; +} + +static void s5pv210_clk_resume(void) +{ + samsung_clk_restore(reg_base, s5pv210_clk_dump, + ARRAY_SIZE(s5pv210_clk_regs)); +} + +static struct syscore_ops s5pv210_clk_syscore_ops = { + .suspend = s5pv210_clk_suspend, + .resume = s5pv210_clk_resume, +}; + +static void s5pv210_clk_sleep_init(void) +{ + s5pv210_clk_dump = + samsung_clk_alloc_reg_dump(s5pv210_clk_regs, + ARRAY_SIZE(s5pv210_clk_regs)); + if (!s5pv210_clk_dump) { + pr_warn("%s: Failed to allocate sleep save data\n", __func__); + return; + } + + register_syscore_ops(&s5pv210_clk_syscore_ops); +} +#else +static inline void s5pv210_clk_sleep_init(void) { } +#endif + +/* Mux parent lists. */ +static const char *fin_pll_p[] __initconst = { + "xxti", + "xusbxti" +}; + +static const char *mout_apll_p[] __initconst = { + "fin_pll", + "fout_apll" +}; + +static const char *mout_mpll_p[] __initconst = { + "fin_pll", + "fout_mpll" +}; + +static const char *mout_epll_p[] __initconst = { + "fin_pll", + "fout_epll" +}; + +static const char *mout_vpllsrc_p[] __initconst = { + "fin_pll", + "sclk_hdmi27m" +}; + +static const char *mout_vpll_p[] __initconst = { + "mout_vpllsrc", + "fout_vpll" +}; + +static const char *mout_group1_p[] __initconst = { + "dout_a2m", + "mout_mpll", + "mout_epll", + "mout_vpll" +}; + +static const char *mout_group2_p[] __initconst = { + "xxti", + "xusbxti", + "sclk_hdmi27m", + "sclk_usbphy0", + "sclk_usbphy1", + "sclk_hdmiphy", + "mout_mpll", + "mout_epll", + "mout_vpll", +}; + +static const char *mout_audio0_p[] __initconst = { + "xxti", + "pcmcdclk0", + "sclk_hdmi27m", + "sclk_usbphy0", + "sclk_usbphy1", + "sclk_hdmiphy", + "mout_mpll", + "mout_epll", + "mout_vpll", +}; + +static const char *mout_audio1_p[] __initconst = { + "i2scdclk1", + "pcmcdclk1", + "sclk_hdmi27m", + "sclk_usbphy0", + "sclk_usbphy1", + "sclk_hdmiphy", + "mout_mpll", + "mout_epll", + "mout_vpll", +}; + +static const char *mout_audio2_p[] __initconst = { + "i2scdclk2", + "pcmcdclk2", + "sclk_hdmi27m", + "sclk_usbphy0", + "sclk_usbphy1", + "sclk_hdmiphy", + "mout_mpll", + "mout_epll", + "mout_vpll", +}; + +static const char *mout_spdif_p[] __initconst = { + "dout_audio0", + "dout_audio1", + "dout_audio3", +}; + +static const char *mout_group3_p[] __initconst = { + "mout_apll", + "mout_mpll" +}; + +static const char *mout_group4_p[] __initconst = { + "mout_mpll", + "dout_a2m" +}; + +static const char *mout_flash_p[] __initconst = { + "dout_hclkd", + "dout_hclkp" +}; + +static const char *mout_dac_p[] __initconst = { + "mout_vpll", + "sclk_hdmiphy" +}; + +static const char *mout_hdmi_p[] __initconst = { + "sclk_hdmiphy", + "dout_tblk" +}; + +static const char *mout_mixer_p[] __initconst = { + "mout_dac", + "mout_hdmi" +}; + +static const char *mout_vpll_6442_p[] __initconst = { + "fin_pll", + "fout_vpll" +}; + +static const char *mout_mixer_6442_p[] __initconst = { + "mout_vpll", + "dout_mixer" +}; + +static const char *mout_d0sync_6442_p[] __initconst = { + "mout_dsys", + "div_apll" +}; + +static const char *mout_d1sync_6442_p[] __initconst = { + "mout_psys", + "div_apll" +}; + +static const char *mout_group2_6442_p[] __initconst = { + "fin_pll", + "none", + "none", + "sclk_usbphy0", + "none", + "none", + "mout_mpll", + "mout_epll", + "mout_vpll", +}; + +static const char *mout_audio0_6442_p[] __initconst = { + "fin_pll", + "pcmcdclk0", + "none", + "sclk_usbphy0", + "none", + "none", + "mout_mpll", + "mout_epll", + "mout_vpll", +}; + +static const char *mout_audio1_6442_p[] __initconst = { + "i2scdclk1", + "pcmcdclk1", + "none", + "sclk_usbphy0", + "none", + "none", + "mout_mpll", + "mout_epll", + "mout_vpll", + "fin_pll", +}; + +static const char *mout_clksel_p[] __initconst = { + "fout_apll_clkout", + "fout_mpll_clkout", + "fout_epll", + "fout_vpll", + "sclk_usbphy0", + "sclk_usbphy1", + "sclk_hdmiphy", + "rtc", + "rtc_tick", + "dout_hclkm", + "dout_pclkm", + "dout_hclkd", + "dout_pclkd", + "dout_hclkp", + "dout_pclkp", + "dout_apll_clkout", + "dout_hpm", + "xxti", + "xusbxti", + "div_dclk" +}; + +static const char *mout_clksel_6442_p[] __initconst = { + "fout_apll_clkout", + "fout_mpll_clkout", + "fout_epll", + "fout_vpll", + "sclk_usbphy0", + "none", + "none", + "rtc", + "rtc_tick", + "none", + "none", + "dout_hclkd", + "dout_pclkd", + "dout_hclkp", + "dout_pclkp", + "dout_apll_clkout", + "none", + "fin_pll", + "none", + "div_dclk" +}; + +static const char *mout_clkout_p[] __initconst = { + "dout_clkout", + "none", + "xxti", + "xusbxti" +}; + +/* Common fixed factor clocks. */ +static struct samsung_fixed_factor_clock ffactor_clks[] __initdata = { + FFACTOR(FOUT_APLL_CLKOUT, "fout_apll_clkout", "fout_apll", 1, 4, 0), + FFACTOR(FOUT_MPLL_CLKOUT, "fout_mpll_clkout", "fout_mpll", 1, 2, 0), + FFACTOR(DOUT_APLL_CLKOUT, "dout_apll_clkout", "dout_apll", 1, 4, 0), +}; + +/* PLL input mux (fin_pll), which needs to be registered before PLLs. */ +static struct samsung_mux_clock early_mux_clks[] __initdata = { + MUX_F(FIN_PLL, "fin_pll", fin_pll_p, OM_STAT, 0, 1, + CLK_MUX_READ_ONLY, 0), +}; + +/* Common clock muxes. */ +static struct samsung_mux_clock mux_clks[] __initdata = { + MUX(MOUT_FLASH, "mout_flash", mout_flash_p, CLK_SRC0, 28, 1), + MUX(MOUT_PSYS, "mout_psys", mout_group4_p, CLK_SRC0, 24, 1), + MUX(MOUT_DSYS, "mout_dsys", mout_group4_p, CLK_SRC0, 20, 1), + MUX(MOUT_MSYS, "mout_msys", mout_group3_p, CLK_SRC0, 16, 1), + MUX(MOUT_EPLL, "mout_epll", mout_epll_p, CLK_SRC0, 8, 1), + MUX(MOUT_MPLL, "mout_mpll", mout_mpll_p, CLK_SRC0, 4, 1), + MUX(MOUT_APLL, "mout_apll", mout_apll_p, CLK_SRC0, 0, 1), + + MUX(MOUT_CLKOUT, "mout_clkout", mout_clkout_p, MISC, 8, 2), +}; + +/* S5PV210-specific clock muxes. */ +static struct samsung_mux_clock s5pv210_mux_clks[] __initdata = { + MUX(MOUT_VPLL, "mout_vpll", mout_vpll_p, CLK_SRC0, 12, 1), + + MUX(MOUT_VPLLSRC, "mout_vpllsrc", mout_vpllsrc_p, CLK_SRC1, 28, 1), + MUX(MOUT_CSIS, "mout_csis", mout_group2_p, CLK_SRC1, 24, 4), + MUX(MOUT_FIMD, "mout_fimd", mout_group2_p, CLK_SRC1, 20, 4), + MUX(MOUT_CAM1, "mout_cam1", mout_group2_p, CLK_SRC1, 16, 4), + MUX(MOUT_CAM0, "mout_cam0", mout_group2_p, CLK_SRC1, 12, 4), + MUX(MOUT_DAC, "mout_dac", mout_dac_p, CLK_SRC1, 8, 1), + MUX(MOUT_MIXER, "mout_mixer", mout_mixer_p, CLK_SRC1, 4, 1), + MUX(MOUT_HDMI, "mout_hdmi", mout_hdmi_p, CLK_SRC1, 0, 1), + + MUX(MOUT_G2D, "mout_g2d", mout_group1_p, CLK_SRC2, 8, 2), + MUX(MOUT_MFC, "mout_mfc", mout_group1_p, CLK_SRC2, 4, 2), + MUX(MOUT_G3D, "mout_g3d", mout_group1_p, CLK_SRC2, 0, 2), + + MUX(MOUT_FIMC2, "mout_fimc2", mout_group2_p, CLK_SRC3, 20, 4), + MUX(MOUT_FIMC1, "mout_fimc1", mout_group2_p, CLK_SRC3, 16, 4), + MUX(MOUT_FIMC0, "mout_fimc0", mout_group2_p, CLK_SRC3, 12, 4), + + MUX(MOUT_UART3, "mout_uart3", mout_group2_p, CLK_SRC4, 28, 4), + MUX(MOUT_UART2, "mout_uart2", mout_group2_p, CLK_SRC4, 24, 4), + MUX(MOUT_UART1, "mout_uart1", mout_group2_p, CLK_SRC4, 20, 4), + MUX(MOUT_UART0, "mout_uart0", mout_group2_p, CLK_SRC4, 16, 4), + MUX(MOUT_MMC3, "mout_mmc3", mout_group2_p, CLK_SRC4, 12, 4), + MUX(MOUT_MMC2, "mout_mmc2", mout_group2_p, CLK_SRC4, 8, 4), + MUX(MOUT_MMC1, "mout_mmc1", mout_group2_p, CLK_SRC4, 4, 4), + MUX(MOUT_MMC0, "mout_mmc0", mout_group2_p, CLK_SRC4, 0, 4), + + MUX(MOUT_PWM, "mout_pwm", mout_group2_p, CLK_SRC5, 12, 4), + MUX(MOUT_SPI1, "mout_spi1", mout_group2_p, CLK_SRC5, 4, 4), + MUX(MOUT_SPI0, "mout_spi0", mout_group2_p, CLK_SRC5, 0, 4), + + MUX(MOUT_DMC0, "mout_dmc0", mout_group1_p, CLK_SRC6, 24, 2), + MUX(MOUT_PWI, "mout_pwi", mout_group2_p, CLK_SRC6, 20, 4), + MUX(MOUT_HPM, "mout_hpm", mout_group3_p, CLK_SRC6, 16, 1), + MUX(MOUT_SPDIF, "mout_spdif", mout_spdif_p, CLK_SRC6, 12, 2), + MUX(MOUT_AUDIO2, "mout_audio2", mout_audio2_p, CLK_SRC6, 8, 4), + MUX(MOUT_AUDIO1, "mout_audio1", mout_audio1_p, CLK_SRC6, 4, 4), + MUX(MOUT_AUDIO0, "mout_audio0", mout_audio0_p, CLK_SRC6, 0, 4), + + MUX(MOUT_CLKSEL, "mout_clksel", mout_clksel_p, CLK_OUT, 12, 5), +}; + +/* S5P6442-specific clock muxes. */ +static struct samsung_mux_clock s5p6442_mux_clks[] __initdata = { + MUX(MOUT_VPLL, "mout_vpll", mout_vpll_6442_p, CLK_SRC0, 12, 1), + + MUX(MOUT_FIMD, "mout_fimd", mout_group2_6442_p, CLK_SRC1, 20, 4), + MUX(MOUT_CAM1, "mout_cam1", mout_group2_6442_p, CLK_SRC1, 16, 4), + MUX(MOUT_CAM0, "mout_cam0", mout_group2_6442_p, CLK_SRC1, 12, 4), + MUX(MOUT_MIXER, "mout_mixer", mout_mixer_6442_p, CLK_SRC1, 4, 1), + + MUX(MOUT_D0SYNC, "mout_d0sync", mout_d0sync_6442_p, CLK_SRC2, 28, 1), + MUX(MOUT_D1SYNC, "mout_d1sync", mout_d1sync_6442_p, CLK_SRC2, 24, 1), + + MUX(MOUT_FIMC2, "mout_fimc2", mout_group2_6442_p, CLK_SRC3, 20, 4), + MUX(MOUT_FIMC1, "mout_fimc1", mout_group2_6442_p, CLK_SRC3, 16, 4), + MUX(MOUT_FIMC0, "mout_fimc0", mout_group2_6442_p, CLK_SRC3, 12, 4), + + MUX(MOUT_UART2, "mout_uart2", mout_group2_6442_p, CLK_SRC4, 24, 4), + MUX(MOUT_UART1, "mout_uart1", mout_group2_6442_p, CLK_SRC4, 20, 4), + MUX(MOUT_UART0, "mout_uart0", mout_group2_6442_p, CLK_SRC4, 16, 4), + MUX(MOUT_MMC2, "mout_mmc2", mout_group2_6442_p, CLK_SRC4, 8, 4), + MUX(MOUT_MMC1, "mout_mmc1", mout_group2_6442_p, CLK_SRC4, 4, 4), + MUX(MOUT_MMC0, "mout_mmc0", mout_group2_6442_p, CLK_SRC4, 0, 4), + + MUX(MOUT_PWM, "mout_pwm", mout_group2_6442_p, CLK_SRC5, 12, 4), + MUX(MOUT_SPI0, "mout_spi0", mout_group2_6442_p, CLK_SRC5, 0, 4), + + MUX(MOUT_AUDIO1, "mout_audio1", mout_audio1_6442_p, CLK_SRC6, 4, 4), + MUX(MOUT_AUDIO0, "mout_audio0", mout_audio0_6442_p, CLK_SRC6, 0, 4), + + MUX(MOUT_CLKSEL, "mout_clksel", mout_clksel_6442_p, CLK_OUT, 12, 5), +}; + +/* S5PV210-specific fixed rate clocks generated inside the SoC. */ +static struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initdata = { + FRATE(SCLK_HDMI27M, "sclk_hdmi27m", NULL, CLK_IS_ROOT, 27000000), + FRATE(SCLK_HDMIPHY, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 27000000), + FRATE(SCLK_USBPHY0, "sclk_usbphy0", NULL, CLK_IS_ROOT, 48000000), + FRATE(SCLK_USBPHY1, "sclk_usbphy1", NULL, CLK_IS_ROOT, 48000000), +}; + +/* S5P6442-specific fixed rate clocks generated inside the SoC. */ +static struct samsung_fixed_rate_clock s5p6442_frate_clks[] __initdata = { + FRATE(SCLK_USBPHY0, "sclk_usbphy0", NULL, CLK_IS_ROOT, 30000000), +}; + +/* Common clock dividers. */ +static struct samsung_div_clock div_clks[] __initdata = { + DIV(DOUT_PCLKP, "dout_pclkp", "dout_hclkp", CLK_DIV0, 28, 3), + DIV(DOUT_PCLKD, "dout_pclkd", "dout_hclkd", CLK_DIV0, 20, 3), + DIV(DOUT_A2M, "dout_a2m", "mout_apll", CLK_DIV0, 4, 3), + DIV(DOUT_APLL, "dout_apll", "mout_msys", CLK_DIV0, 0, 3), + + DIV(DOUT_FIMD, "dout_fimd", "mout_fimd", CLK_DIV1, 20, 4), + DIV(DOUT_CAM1, "dout_cam1", "mout_cam1", CLK_DIV1, 16, 4), + DIV(DOUT_CAM0, "dout_cam0", "mout_cam0", CLK_DIV1, 12, 4), + + DIV(DOUT_FIMC2, "dout_fimc2", "mout_fimc2", CLK_DIV3, 20, 4), + DIV(DOUT_FIMC1, "dout_fimc1", "mout_fimc1", CLK_DIV3, 16, 4), + DIV(DOUT_FIMC0, "dout_fimc0", "mout_fimc0", CLK_DIV3, 12, 4), + + DIV(DOUT_UART2, "dout_uart2", "mout_uart2", CLK_DIV4, 24, 4), + DIV(DOUT_UART1, "dout_uart1", "mout_uart1", CLK_DIV4, 20, 4), + DIV(DOUT_UART0, "dout_uart0", "mout_uart0", CLK_DIV4, 16, 4), + DIV(DOUT_MMC2, "dout_mmc2", "mout_mmc2", CLK_DIV4, 8, 4), + DIV(DOUT_MMC1, "dout_mmc1", "mout_mmc1", CLK_DIV4, 4, 4), + DIV(DOUT_MMC0, "dout_mmc0", "mout_mmc0", CLK_DIV4, 0, 4), + + DIV(DOUT_PWM, "dout_pwm", "mout_pwm", CLK_DIV5, 12, 4), + DIV(DOUT_SPI0, "dout_spi0", "mout_spi0", CLK_DIV5, 0, 4), + + DIV(DOUT_FLASH, "dout_flash", "mout_flash", CLK_DIV6, 12, 3), + DIV(DOUT_AUDIO1, "dout_audio1", "mout_audio1", CLK_DIV6, 4, 4), + DIV(DOUT_AUDIO0, "dout_audio0", "mout_audio0", CLK_DIV6, 0, 4), + + DIV(DOUT_CLKOUT, "dout_clkout", "mout_clksel", CLK_OUT, 20, 4), +}; + +/* S5PV210-specific clock dividers. */ +static struct samsung_div_clock s5pv210_div_clks[] __initdata = { + DIV(DOUT_HCLKP, "dout_hclkp", "mout_psys", CLK_DIV0, 24, 4), + DIV(DOUT_HCLKD, "dout_hclkd", "mout_dsys", CLK_DIV0, 16, 4), + DIV(DOUT_PCLKM, "dout_pclkm", "dout_hclkm", CLK_DIV0, 12, 3), + DIV(DOUT_HCLKM, "dout_hclkm", "dout_apll", CLK_DIV0, 8, 3), + + DIV(DOUT_CSIS, "dout_csis", "mout_csis", CLK_DIV1, 28, 4), + DIV(DOUT_TBLK, "dout_tblk", "mout_vpll", CLK_DIV1, 0, 4), + + DIV(DOUT_G2D, "dout_g2d", "mout_g2d", CLK_DIV2, 8, 4), + DIV(DOUT_MFC, "dout_mfc", "mout_mfc", CLK_DIV2, 4, 4), + DIV(DOUT_G3D, "dout_g3d", "mout_g3d", CLK_DIV2, 0, 4), + + DIV(DOUT_UART3, "dout_uart3", "mout_uart3", CLK_DIV4, 28, 4), + DIV(DOUT_MMC3, "dout_mmc3", "mout_mmc3", CLK_DIV4, 12, 4), + + DIV(DOUT_SPI1, "dout_spi1", "mout_spi1", CLK_DIV5, 4, 4), + + DIV(DOUT_DMC0, "dout_dmc0", "mout_dmc0", CLK_DIV6, 28, 4), + DIV(DOUT_PWI, "dout_pwi", "mout_pwi", CLK_DIV6, 24, 4), + DIV(DOUT_HPM, "dout_hpm", "dout_copy", CLK_DIV6, 20, 3), + DIV(DOUT_COPY, "dout_copy", "mout_hpm", CLK_DIV6, 16, 3), + DIV(DOUT_AUDIO2, "dout_audio2", "mout_audio2", CLK_DIV6, 8, 4), + + DIV(DOUT_DPM, "dout_dpm", "dout_pclkp", CLK_DIV7, 8, 7), + DIV(DOUT_DVSEM, "dout_dvsem", "dout_pclkp", CLK_DIV7, 0, 7), +}; + +/* S5P6442-specific clock dividers. */ +static struct samsung_div_clock s5p6442_div_clks[] __initdata = { + DIV(DOUT_HCLKP, "dout_hclkp", "mout_d1sync", CLK_DIV0, 24, 4), + DIV(DOUT_HCLKD, "dout_hclkd", "mout_d0sync", CLK_DIV0, 16, 4), + + DIV(DOUT_MIXER, "dout_mixer", "mout_vpll", CLK_DIV1, 0, 4), +}; + +/* Common clock gates. */ +static struct samsung_gate_clock gate_clks[] __initdata = { + GATE(CLK_ROTATOR, "rotator", "dout_hclkd", CLK_GATE_IP0, 29, 0, 0), + GATE(CLK_FIMC2, "fimc2", "dout_hclkd", CLK_GATE_IP0, 26, 0, 0), + GATE(CLK_FIMC1, "fimc1", "dout_hclkd", CLK_GATE_IP0, 25, 0, 0), + GATE(CLK_FIMC0, "fimc0", "dout_hclkd", CLK_GATE_IP0, 24, 0, 0), + GATE(CLK_PDMA0, "pdma0", "dout_hclkp", CLK_GATE_IP0, 3, 0, 0), + GATE(CLK_MDMA, "mdma", "dout_hclkd", CLK_GATE_IP0, 2, 0, 0), + + GATE(CLK_SROMC, "sromc", "dout_hclkp", CLK_GATE_IP1, 26, 0, 0), + GATE(CLK_NANDXL, "nandxl", "dout_hclkp", CLK_GATE_IP1, 24, 0, 0), + GATE(CLK_USB_OTG, "usb_otg", "dout_hclkp", CLK_GATE_IP1, 16, 0, 0), + GATE(CLK_TVENC, "tvenc", "dout_hclkd", CLK_GATE_IP1, 10, 0, 0), + GATE(CLK_MIXER, "mixer", "dout_hclkd", CLK_GATE_IP1, 9, 0, 0), + GATE(CLK_VP, "vp", "dout_hclkd", CLK_GATE_IP1, 8, 0, 0), + GATE(CLK_FIMD, "fimd", "dout_hclkd", CLK_GATE_IP1, 0, 0, 0), + + GATE(CLK_HSMMC2, "hsmmc2", "dout_hclkp", CLK_GATE_IP2, 18, 0, 0), + GATE(CLK_HSMMC1, "hsmmc1", "dout_hclkp", CLK_GATE_IP2, 17, 0, 0), + GATE(CLK_HSMMC0, "hsmmc0", "dout_hclkp", CLK_GATE_IP2, 16, 0, 0), + GATE(CLK_MODEMIF, "modemif", "dout_hclkp", CLK_GATE_IP2, 9, 0, 0), + GATE(CLK_SECSS, "secss", "dout_hclkp", CLK_GATE_IP2, 0, 0, 0), + + GATE(CLK_PCM1, "pcm1", "dout_pclkp", CLK_GATE_IP3, 29, 0, 0), + GATE(CLK_PCM0, "pcm0", "dout_pclkp", CLK_GATE_IP3, 28, 0, 0), + GATE(CLK_TSADC, "tsadc", "dout_pclkp", CLK_GATE_IP3, 24, 0, 0), + GATE(CLK_PWM, "pwm", "dout_pclkp", CLK_GATE_IP3, 23, 0, 0), + GATE(CLK_WDT, "watchdog", "dout_pclkp", CLK_GATE_IP3, 22, 0, 0), + GATE(CLK_KEYIF, "keyif", "dout_pclkp", CLK_GATE_IP3, 21, 0, 0), + GATE(CLK_UART2, "uart2", "dout_pclkp", CLK_GATE_IP3, 19, 0, 0), + GATE(CLK_UART1, "uart1", "dout_pclkp", CLK_GATE_IP3, 18, 0, 0), + GATE(CLK_UART0, "uart0", "dout_pclkp", CLK_GATE_IP3, 17, 0, 0), + GATE(CLK_SYSTIMER, "systimer", "dout_pclkp", CLK_GATE_IP3, 16, 0, 0), + GATE(CLK_RTC, "rtc", "dout_pclkp", CLK_GATE_IP3, 15, 0, 0), + GATE(CLK_SPI0, "spi0", "dout_pclkp", CLK_GATE_IP3, 12, 0, 0), + GATE(CLK_I2C2, "i2c2", "dout_pclkp", CLK_GATE_IP3, 9, 0, 0), + GATE(CLK_I2C0, "i2c0", "dout_pclkp", CLK_GATE_IP3, 7, 0, 0), + GATE(CLK_I2S1, "i2s1", "dout_pclkp", CLK_GATE_IP3, 5, 0, 0), + GATE(CLK_I2S0, "i2s0", "dout_pclkp", CLK_GATE_IP3, 4, 0, 0), + + GATE(CLK_SECKEY, "seckey", "dout_pclkp", CLK_GATE_IP4, 3, 0, 0), + GATE(CLK_CHIPID, "chipid", "dout_pclkp", CLK_GATE_IP4, 0, 0, 0), + + GATE(SCLK_AUDIO1, "sclk_audio1", "dout_audio1", CLK_SRC_MASK0, 25, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_AUDIO0, "sclk_audio0", "dout_audio0", CLK_SRC_MASK0, 24, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_PWM, "sclk_pwm", "dout_pwm", CLK_SRC_MASK0, 19, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_SPI0, "sclk_spi0", "dout_spi0", CLK_SRC_MASK0, 16, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_UART2, "sclk_uart2", "dout_uart2", CLK_SRC_MASK0, 14, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_UART1, "sclk_uart1", "dout_uart1", CLK_SRC_MASK0, 13, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_UART0, "sclk_uart0", "dout_uart0", CLK_SRC_MASK0, 12, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_MMC2, "sclk_mmc2", "dout_mmc2", CLK_SRC_MASK0, 10, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_MMC1, "sclk_mmc1", "dout_mmc1", CLK_SRC_MASK0, 9, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_MMC0, "sclk_mmc0", "dout_mmc0", CLK_SRC_MASK0, 8, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_FIMD, "sclk_fimd", "dout_fimd", CLK_SRC_MASK0, 5, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_CAM1, "sclk_cam1", "dout_cam1", CLK_SRC_MASK0, 4, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_CAM0, "sclk_cam0", "dout_cam0", CLK_SRC_MASK0, 3, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_MIXER, "sclk_mixer", "mout_mixer", CLK_SRC_MASK0, 1, + CLK_SET_RATE_PARENT, 0), + + GATE(SCLK_FIMC2, "sclk_fimc2", "dout_fimc2", CLK_SRC_MASK1, 4, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_FIMC1, "sclk_fimc1", "dout_fimc1", CLK_SRC_MASK1, 3, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_FIMC0, "sclk_fimc0", "dout_fimc0", CLK_SRC_MASK1, 2, + CLK_SET_RATE_PARENT, 0), +}; + +/* S5PV210-specific clock gates. */ +static struct samsung_gate_clock s5pv210_gate_clks[] __initdata = { + GATE(CLK_CSIS, "clk_csis", "dout_hclkd", CLK_GATE_IP0, 31, 0, 0), + GATE(CLK_MFC, "mfc", "dout_hclkm", CLK_GATE_IP0, 16, 0, 0), + GATE(CLK_G2D, "g2d", "dout_hclkd", CLK_GATE_IP0, 12, 0, 0), + GATE(CLK_G3D, "g3d", "dout_hclkm", CLK_GATE_IP0, 8, 0, 0), + GATE(CLK_IMEM, "imem", "dout_hclkm", CLK_GATE_IP0, 5, 0, 0), + GATE(CLK_PDMA1, "pdma1", "dout_hclkp", CLK_GATE_IP0, 4, 0, 0), + + GATE(CLK_NFCON, "nfcon", "dout_hclkp", CLK_GATE_IP1, 28, 0, 0), + GATE(CLK_CFCON, "cfcon", "dout_hclkp", CLK_GATE_IP1, 25, 0, 0), + GATE(CLK_USB_HOST, "usb_host", "dout_hclkp", CLK_GATE_IP1, 17, 0, 0), + GATE(CLK_HDMI, "hdmi", "dout_hclkd", CLK_GATE_IP1, 11, 0, 0), + GATE(CLK_DSIM, "dsim", "dout_pclkd", CLK_GATE_IP1, 2, 0, 0), + + GATE(CLK_TZIC3, "tzic3", "dout_hclkm", CLK_GATE_IP2, 31, 0, 0), + GATE(CLK_TZIC2, "tzic2", "dout_hclkm", CLK_GATE_IP2, 30, 0, 0), + GATE(CLK_TZIC1, "tzic1", "dout_hclkm", CLK_GATE_IP2, 29, 0, 0), + GATE(CLK_TZIC0, "tzic0", "dout_hclkm", CLK_GATE_IP2, 28, 0, 0), + GATE(CLK_TSI, "tsi", "dout_hclkd", CLK_GATE_IP2, 20, 0, 0), + GATE(CLK_HSMMC3, "hsmmc3", "dout_hclkp", CLK_GATE_IP2, 19, 0, 0), + GATE(CLK_JTAG, "jtag", "dout_hclkp", CLK_GATE_IP2, 11, 0, 0), + GATE(CLK_CORESIGHT, "coresight", "dout_pclkp", CLK_GATE_IP2, 8, 0, 0), + GATE(CLK_SDM, "sdm", "dout_pclkm", CLK_GATE_IP2, 1, 0, 0), + + GATE(CLK_PCM2, "pcm2", "dout_pclkp", CLK_GATE_IP3, 30, 0, 0), + GATE(CLK_UART3, "uart3", "dout_pclkp", CLK_GATE_IP3, 20, 0, 0), + GATE(CLK_SPI1, "spi1", "dout_pclkp", CLK_GATE_IP3, 13, 0, 0), + GATE(CLK_I2C_HDMI_PHY, "i2c_hdmi_phy", "dout_pclkd", + CLK_GATE_IP3, 11, 0, 0), + GATE(CLK_I2C1, "i2c1", "dout_pclkd", CLK_GATE_IP3, 10, 0, 0), + GATE(CLK_I2S2, "i2s2", "dout_pclkp", CLK_GATE_IP3, 6, 0, 0), + GATE(CLK_AC97, "ac97", "dout_pclkp", CLK_GATE_IP3, 1, 0, 0), + GATE(CLK_SPDIF, "spdif", "dout_pclkp", CLK_GATE_IP3, 0, 0, 0), + + GATE(CLK_TZPC3, "tzpc.3", "dout_pclkd", CLK_GATE_IP4, 8, 0, 0), + GATE(CLK_TZPC2, "tzpc.2", "dout_pclkd", CLK_GATE_IP4, 7, 0, 0), + GATE(CLK_TZPC1, "tzpc.1", "dout_pclkp", CLK_GATE_IP4, 6, 0, 0), + GATE(CLK_TZPC0, "tzpc.0", "dout_pclkm", CLK_GATE_IP4, 5, 0, 0), + GATE(CLK_IEM_APC, "iem_apc", "dout_pclkp", CLK_GATE_IP4, 2, 0, 0), + GATE(CLK_IEM_IEC, "iem_iec", "dout_pclkp", CLK_GATE_IP4, 1, 0, 0), + + GATE(CLK_JPEG, "jpeg", "dout_hclkd", CLK_GATE_IP5, 29, 0, 0), + + GATE(SCLK_SPDIF, "sclk_spdif", "mout_spdif", CLK_SRC_MASK0, 27, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_AUDIO2, "sclk_audio2", "dout_audio2", CLK_SRC_MASK0, 26, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_SPI1, "sclk_spi1", "dout_spi1", CLK_SRC_MASK0, 17, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_UART3, "sclk_uart3", "dout_uart3", CLK_SRC_MASK0, 15, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_MMC3, "sclk_mmc3", "dout_mmc3", CLK_SRC_MASK0, 11, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_CSIS, "sclk_csis", "dout_csis", CLK_SRC_MASK0, 6, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_DAC, "sclk_dac", "mout_dac", CLK_SRC_MASK0, 2, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_HDMI, "sclk_hdmi", "mout_hdmi", CLK_SRC_MASK0, 0, + CLK_SET_RATE_PARENT, 0), +}; + +/* S5P6442-specific clock gates. */ +static struct samsung_gate_clock s5p6442_gate_clks[] __initdata = { + GATE(CLK_JPEG, "jpeg", "dout_hclkd", CLK_GATE_IP0, 28, 0, 0), + GATE(CLK_MFC, "mfc", "dout_hclkd", CLK_GATE_IP0, 16, 0, 0), + GATE(CLK_G2D, "g2d", "dout_hclkd", CLK_GATE_IP0, 12, 0, 0), + GATE(CLK_G3D, "g3d", "dout_hclkd", CLK_GATE_IP0, 8, 0, 0), + GATE(CLK_IMEM, "imem", "dout_hclkd", CLK_GATE_IP0, 5, 0, 0), + + GATE(CLK_ETB, "etb", "dout_hclkd", CLK_GATE_IP1, 31, 0, 0), + GATE(CLK_ETM, "etm", "dout_hclkd", CLK_GATE_IP1, 30, 0, 0), + + GATE(CLK_I2C1, "i2c1", "dout_pclkp", CLK_GATE_IP3, 8, 0, 0), + + GATE(SCLK_DAC, "sclk_dac", "mout_vpll", CLK_SRC_MASK0, 2, + CLK_SET_RATE_PARENT, 0), +}; + +/* + * Clock aliases for legacy clkdev look-up. + * NOTE: Needed only to support legacy board files. + */ +static struct samsung_clock_alias s5pv210_aliases[] = { + ALIAS(DOUT_APLL, NULL, "armclk"), + ALIAS(DOUT_HCLKM, NULL, "hclk_msys"), + ALIAS(MOUT_DMC0, NULL, "sclk_dmc0"), +}; + +/* S5PV210-specific PLLs. */ +static struct samsung_pll_clock s5pv210_pll_clks[] __initdata = { + [apll] = PLL(pll_4508, FOUT_APLL, "fout_apll", "fin_pll", + APLL_LOCK, APLL_CON0, NULL), + [mpll] = PLL(pll_4502, FOUT_MPLL, "fout_mpll", "fin_pll", + MPLL_LOCK, MPLL_CON, NULL), + [epll] = PLL(pll_4600, FOUT_EPLL, "fout_epll", "fin_pll", + EPLL_LOCK, EPLL_CON0, NULL), + [vpll] = PLL(pll_4502, FOUT_VPLL, "fout_vpll", "mout_vpllsrc", + VPLL_LOCK, VPLL_CON, NULL), +}; + +/* S5P6442-specific PLLs. */ +static struct samsung_pll_clock s5p6442_pll_clks[] __initdata = { + [apll] = PLL(pll_4502, FOUT_APLL, "fout_apll", "fin_pll", + APLL_LOCK, APLL_CON0, NULL), + [mpll] = PLL(pll_4502, FOUT_MPLL, "fout_mpll", "fin_pll", + MPLL_LOCK, MPLL_CON, NULL), + [epll] = PLL(pll_4500, FOUT_EPLL, "fout_epll", "fin_pll", + EPLL_LOCK, EPLL_CON0, NULL), + [vpll] = PLL(pll_4500, FOUT_VPLL, "fout_vpll", "fin_pll", + VPLL_LOCK, VPLL_CON, NULL), +}; + +static void __init __s5pv210_clk_init(struct device_node *np, + unsigned long xxti_f, + unsigned long xusbxti_f, + bool is_s5p6442) +{ + struct samsung_clk_provider *ctx; + + ctx = samsung_clk_init(np, reg_base, NR_CLKS); + if (!ctx) + panic("%s: unable to allocate context.\n", __func__); + + samsung_clk_register_mux(ctx, early_mux_clks, + ARRAY_SIZE(early_mux_clks)); + + if (is_s5p6442) { + samsung_clk_register_fixed_rate(ctx, s5p6442_frate_clks, + ARRAY_SIZE(s5p6442_frate_clks)); + samsung_clk_register_pll(ctx, s5p6442_pll_clks, + ARRAY_SIZE(s5p6442_pll_clks), reg_base); + samsung_clk_register_mux(ctx, s5p6442_mux_clks, + ARRAY_SIZE(s5p6442_mux_clks)); + samsung_clk_register_div(ctx, s5p6442_div_clks, + ARRAY_SIZE(s5p6442_div_clks)); + samsung_clk_register_gate(ctx, s5p6442_gate_clks, + ARRAY_SIZE(s5p6442_gate_clks)); + } else { + samsung_clk_register_fixed_rate(ctx, s5pv210_frate_clks, + ARRAY_SIZE(s5pv210_frate_clks)); + samsung_clk_register_pll(ctx, s5pv210_pll_clks, + ARRAY_SIZE(s5pv210_pll_clks), reg_base); + samsung_clk_register_mux(ctx, s5pv210_mux_clks, + ARRAY_SIZE(s5pv210_mux_clks)); + samsung_clk_register_div(ctx, s5pv210_div_clks, + ARRAY_SIZE(s5pv210_div_clks)); + samsung_clk_register_gate(ctx, s5pv210_gate_clks, + ARRAY_SIZE(s5pv210_gate_clks)); + } + + samsung_clk_register_mux(ctx, mux_clks, ARRAY_SIZE(mux_clks)); + samsung_clk_register_div(ctx, div_clks, ARRAY_SIZE(div_clks)); + samsung_clk_register_gate(ctx, gate_clks, ARRAY_SIZE(gate_clks)); + + samsung_clk_register_fixed_factor(ctx, ffactor_clks, + ARRAY_SIZE(ffactor_clks)); + + samsung_clk_register_alias(ctx, s5pv210_aliases, + ARRAY_SIZE(s5pv210_aliases)); + + s5pv210_clk_sleep_init(); + + pr_info("%s clocks: mout_apll = %ld, mout_mpll = %ld\n" + "\tmout_epll = %ld, mout_vpll = %ld\n", + is_s5p6442 ? "S5P6442" : "S5PV210", + _get_rate("mout_apll"), _get_rate("mout_mpll"), + _get_rate("mout_epll"), _get_rate("mout_vpll")); +} + +static void __init s5pv210_clk_dt_init(struct device_node *np) +{ + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + + __s5pv210_clk_init(np, 0, 0, false); +} +CLK_OF_DECLARE(s5pv210_clk, "samsung,s5pv210-clock", s5pv210_clk_dt_init); + +static void __init s5p6442_clk_dt_init(struct device_node *np) +{ + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + + __s5pv210_clk_init(np, 0, 0, true); +} +CLK_OF_DECLARE(s5p6442_clk, "samsung,s5p6442-clock", s5p6442_clk_dt_init); diff --git a/drivers/clk/versatile/Makefile b/drivers/clk/versatile/Makefile index fd449f9b006d..162e519cb0f9 100644 --- a/drivers/clk/versatile/Makefile +++ b/drivers/clk/versatile/Makefile @@ -1,6 +1,5 @@ # Makefile for Versatile-specific clocks -obj-$(CONFIG_ICST) += clk-icst.o -obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o +obj-$(CONFIG_ICST) += clk-icst.o clk-versatile.o obj-$(CONFIG_INTEGRATOR_IMPD1) += clk-impd1.o obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o diff --git a/drivers/clk/versatile/clk-integrator.c b/drivers/clk/versatile/clk-versatile.c index 734c4b8fe6ab..a76981e88cb6 100644 --- a/drivers/clk/versatile/clk-integrator.c +++ b/drivers/clk/versatile/clk-versatile.c @@ -1,5 +1,6 @@ /* - * Clock driver for the ARM Integrator/AP and Integrator/CP boards + * Clock driver for the ARM Integrator/AP, Integrator/CP, Versatile AB and + * Versatile PB boards. * Copyright (C) 2012 Linus Walleij * * This program is free software; you can redistribute it and/or modify @@ -17,6 +18,9 @@ #define INTEGRATOR_HDR_LOCK_OFFSET 0x14 +#define VERSATILE_SYS_OSCCLCD_OFFSET 0x1c +#define VERSATILE_SYS_LOCK_OFFSET 0x20 + /* Base offset for the core module */ static void __iomem *cm_base; @@ -37,11 +41,27 @@ static const struct clk_icst_desc __initdata cm_auxosc_desc = { .lock_offset = INTEGRATOR_HDR_LOCK_OFFSET, }; -static void __init of_integrator_cm_osc_setup(struct device_node *np) +static const struct icst_params versatile_auxosc_params = { + .vco_max = ICST307_VCO_MAX, + .vco_min = ICST307_VCO_MIN, + .vd_min = 4 + 8, + .vd_max = 511 + 8, + .rd_min = 1 + 2, + .rd_max = 127 + 2, + .s2div = icst307_s2div, + .idx2s = icst307_idx2s, +}; + +static const struct clk_icst_desc versatile_auxosc_desc __initconst = { + .params = &versatile_auxosc_params, + .vco_offset = VERSATILE_SYS_OSCCLCD_OFFSET, + .lock_offset = VERSATILE_SYS_LOCK_OFFSET, +}; +static void __init cm_osc_setup(struct device_node *np, + const struct clk_icst_desc *desc) { struct clk *clk = ERR_PTR(-EINVAL); const char *clk_name = np->name; - const struct clk_icst_desc *desc = &cm_auxosc_desc; const char *parent_name; if (!cm_base) { @@ -65,5 +85,17 @@ static void __init of_integrator_cm_osc_setup(struct device_node *np) if (!IS_ERR(clk)) of_clk_add_provider(np, of_clk_src_simple_get, clk); } + +static void __init of_integrator_cm_osc_setup(struct device_node *np) +{ + cm_osc_setup(np, &cm_auxosc_desc); +} CLK_OF_DECLARE(integrator_cm_auxosc_clk, "arm,integrator-cm-auxosc", of_integrator_cm_osc_setup); + +static void __init of_versatile_cm_osc_setup(struct device_node *np) +{ + cm_osc_setup(np, &versatile_auxosc_desc); +} +CLK_OF_DECLARE(versatile_cm_auxosc_clk, + "arm,versatile-cm-auxosc", of_versatile_cm_osc_setup); diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index d1869f02051c..d2616ef16770 100644 --- a/drivers/clocksource/tegra20_timer.c +++ b/drivers/clocksource/tegra20_timer.c @@ -27,6 +27,7 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/sched_clock.h> +#include <linux/delay.h> #include <asm/mach/time.h> #include <asm/smp_twd.h> @@ -53,6 +54,8 @@ static void __iomem *rtc_base; static struct timespec persistent_ts; static u64 persistent_ms, last_persistent_ms; +static struct delay_timer tegra_delay_timer; + #define timer_writel(value, reg) \ __raw_writel(value, timer_reg_base + (reg)) #define timer_readl(reg) \ @@ -139,6 +142,11 @@ static void tegra_read_persistent_clock(struct timespec *ts) *ts = *tsp; } +static unsigned long tegra_delay_timer_read_counter_long(void) +{ + return readl(timer_reg_base + TIMERUS_CNTR_1US); +} + static irqreturn_t tegra_timer_interrupt(int irq, void *dev_id) { struct clock_event_device *evt = (struct clock_event_device *)dev_id; @@ -206,6 +214,11 @@ static void __init tegra20_init_timer(struct device_node *np) BUG(); } + tegra_delay_timer.read_current_timer = + tegra_delay_timer_read_counter_long; + tegra_delay_timer.freq = 1000000; + register_current_timer_delay(&tegra_delay_timer); + ret = setup_irq(tegra_timer_irq.irq, &tegra_timer_irq); if (ret) { pr_err("Failed to register timer IRQ: %d\n", ret); diff --git a/drivers/cpufreq/s3c2410-cpufreq.c b/drivers/cpufreq/s3c2410-cpufreq.c index cfa0dd8723ec..b8e5da8e188b 100644 --- a/drivers/cpufreq/s3c2410-cpufreq.c +++ b/drivers/cpufreq/s3c2410-cpufreq.c @@ -26,7 +26,6 @@ #include <mach/regs-clock.h> #include <plat/cpu.h> -#include <plat/clock.h> #include <plat/cpu-freq-core.h> /* Note, 2410A has an extra mode for 1:4:4 ratio, bit 2 of CLKDIV */ @@ -104,7 +103,6 @@ static struct s3c_cpufreq_info s3c2410_cpufreq_info = { .calc_iotiming = s3c2410_iotiming_calc, .set_iotiming = s3c2410_iotiming_set, .get_iotiming = s3c2410_iotiming_get, - .resume_clocks = s3c2410_setup_clocks, .set_fvco = s3c2410_set_fvco, .set_refresh = s3c2410_cpufreq_setrefresh, diff --git a/drivers/cpufreq/s3c2412-cpufreq.c b/drivers/cpufreq/s3c2412-cpufreq.c index 4645b4898996..eb262133fef2 100644 --- a/drivers/cpufreq/s3c2412-cpufreq.c +++ b/drivers/cpufreq/s3c2412-cpufreq.c @@ -28,7 +28,6 @@ #include <mach/s3c2412.h> #include <plat/cpu.h> -#include <plat/clock.h> #include <plat/cpu-freq-core.h> /* our clock resources. */ @@ -188,8 +187,6 @@ static struct s3c_cpufreq_info s3c2412_cpufreq_info = { .set_iotiming = s3c2412_iotiming_set, .get_iotiming = s3c2412_iotiming_get, - .resume_clocks = s3c2412_setup_clocks, - .debug_io_show = s3c_cpufreq_debugfs_call(s3c2412_iotiming_debugfs), }; diff --git a/drivers/cpufreq/s3c2440-cpufreq.c b/drivers/cpufreq/s3c2440-cpufreq.c index f84ed10755b5..0129f5c70a61 100644 --- a/drivers/cpufreq/s3c2440-cpufreq.c +++ b/drivers/cpufreq/s3c2440-cpufreq.c @@ -29,7 +29,6 @@ #include <plat/cpu.h> #include <plat/cpu-freq-core.h> -#include <plat/clock.h> static struct clk *xtal; static struct clk *fclk; @@ -262,8 +261,6 @@ static struct s3c_cpufreq_info s3c2440_cpufreq_info = { .calc_divs = s3c2440_cpufreq_calcdivs, .calc_freqtable = s3c2440_cpufreq_calctable, - .resume_clocks = s3c244x_setup_clocks, - .debug_io_show = s3c_cpufreq_debugfs_call(s3c2410_iotiming_debugfs), }; diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index 227ebf7c1eea..d00f1cee4509 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -27,7 +27,6 @@ #include <asm/mach/map.h> #include <plat/cpu.h> -#include <plat/clock.h> #include <plat/cpu-freq-core.h> #include <mach/regs-clock.h> diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index 19a10b89fef7..9a68225a757e 100644 --- a/drivers/cpufreq/s5pv210-cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c @@ -16,11 +16,70 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/cpufreq.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> #include <linux/reboot.h> #include <linux/regulator/consumer.h> -#include <mach/map.h> -#include <mach/regs-clock.h> +static void __iomem *clk_base; +static void __iomem *dmc_base[2]; + +#define S5P_CLKREG(x) (clk_base + (x)) + +#define S5P_APLL_LOCK S5P_CLKREG(0x00) +#define S5P_APLL_CON S5P_CLKREG(0x100) +#define S5P_CLK_SRC0 S5P_CLKREG(0x200) +#define S5P_CLK_SRC2 S5P_CLKREG(0x208) +#define S5P_CLK_DIV0 S5P_CLKREG(0x300) +#define S5P_CLK_DIV2 S5P_CLKREG(0x308) +#define S5P_CLK_DIV6 S5P_CLKREG(0x318) +#define S5P_CLKDIV_STAT0 S5P_CLKREG(0x1000) +#define S5P_CLKDIV_STAT1 S5P_CLKREG(0x1004) +#define S5P_CLKMUX_STAT0 S5P_CLKREG(0x1100) +#define S5P_CLKMUX_STAT1 S5P_CLKREG(0x1104) + +#define S5P_ARM_MCS_CON S5P_CLKREG(0x6100) + +/* CLKSRC0 */ +#define S5P_CLKSRC0_MUX200_SHIFT (16) +#define S5P_CLKSRC0_MUX200_MASK (0x1 << S5P_CLKSRC0_MUX200_SHIFT) +#define S5P_CLKSRC0_MUX166_MASK (0x1<<20) +#define S5P_CLKSRC0_MUX133_MASK (0x1<<24) + +/* CLKSRC2 */ +#define S5P_CLKSRC2_G3D_SHIFT (0) +#define S5P_CLKSRC2_G3D_MASK (0x3 << S5P_CLKSRC2_G3D_SHIFT) +#define S5P_CLKSRC2_MFC_SHIFT (4) +#define S5P_CLKSRC2_MFC_MASK (0x3 << S5P_CLKSRC2_MFC_SHIFT) + +/* CLKDIV0 */ +#define S5P_CLKDIV0_APLL_SHIFT (0) +#define S5P_CLKDIV0_APLL_MASK (0x7 << S5P_CLKDIV0_APLL_SHIFT) +#define S5P_CLKDIV0_A2M_SHIFT (4) +#define S5P_CLKDIV0_A2M_MASK (0x7 << S5P_CLKDIV0_A2M_SHIFT) +#define S5P_CLKDIV0_HCLK200_SHIFT (8) +#define S5P_CLKDIV0_HCLK200_MASK (0x7 << S5P_CLKDIV0_HCLK200_SHIFT) +#define S5P_CLKDIV0_PCLK100_SHIFT (12) +#define S5P_CLKDIV0_PCLK100_MASK (0x7 << S5P_CLKDIV0_PCLK100_SHIFT) +#define S5P_CLKDIV0_HCLK166_SHIFT (16) +#define S5P_CLKDIV0_HCLK166_MASK (0xF << S5P_CLKDIV0_HCLK166_SHIFT) +#define S5P_CLKDIV0_PCLK83_SHIFT (20) +#define S5P_CLKDIV0_PCLK83_MASK (0x7 << S5P_CLKDIV0_PCLK83_SHIFT) +#define S5P_CLKDIV0_HCLK133_SHIFT (24) +#define S5P_CLKDIV0_HCLK133_MASK (0xF << S5P_CLKDIV0_HCLK133_SHIFT) +#define S5P_CLKDIV0_PCLK66_SHIFT (28) +#define S5P_CLKDIV0_PCLK66_MASK (0x7 << S5P_CLKDIV0_PCLK66_SHIFT) + +/* CLKDIV2 */ +#define S5P_CLKDIV2_G3D_SHIFT (0) +#define S5P_CLKDIV2_G3D_MASK (0xF << S5P_CLKDIV2_G3D_SHIFT) +#define S5P_CLKDIV2_MFC_SHIFT (4) +#define S5P_CLKDIV2_MFC_MASK (0xF << S5P_CLKDIV2_MFC_SHIFT) + +/* CLKDIV6 */ +#define S5P_CLKDIV6_ONEDRAM_SHIFT (28) +#define S5P_CLKDIV6_ONEDRAM_MASK (0xF << S5P_CLKDIV6_ONEDRAM_SHIFT) static struct clk *dmc0_clk; static struct clk *dmc1_clk; @@ -142,9 +201,9 @@ static void s5pv210_set_refresh(enum s5pv210_dmc_port ch, unsigned long freq) void __iomem *reg = NULL; if (ch == DMC0) { - reg = (S5P_VA_DMC0 + 0x30); + reg = (dmc_base[0] + 0x30); } else if (ch == DMC1) { - reg = (S5P_VA_DMC1 + 0x30); + reg = (dmc_base[1] + 0x30); } else { printk(KERN_ERR "Cannot find DMC port\n"); return; @@ -472,7 +531,7 @@ static int __init s5pv210_cpu_init(struct cpufreq_policy *policy) * check_mem_type : This driver only support LPDDR & LPDDR2. * other memory type is not supported. */ - mem_type = check_mem_type(S5P_VA_DMC0); + mem_type = check_mem_type(dmc_base[0]); if ((mem_type != LPDDR) && (mem_type != LPDDR2)) { printk(KERN_ERR "CPUFreq doesn't support this memory type\n"); @@ -481,10 +540,10 @@ static int __init s5pv210_cpu_init(struct cpufreq_policy *policy) } /* Find current refresh counter and frequency each DMC */ - s5pv210_dram_conf[0].refresh = (__raw_readl(S5P_VA_DMC0 + 0x30) * 1000); + s5pv210_dram_conf[0].refresh = (__raw_readl(dmc_base[0] + 0x30) * 1000); s5pv210_dram_conf[0].freq = clk_get_rate(dmc0_clk); - s5pv210_dram_conf[1].refresh = (__raw_readl(S5P_VA_DMC1 + 0x30) * 1000); + s5pv210_dram_conf[1].refresh = (__raw_readl(dmc_base[1] + 0x30) * 1000); s5pv210_dram_conf[1].freq = clk_get_rate(dmc1_clk); policy->suspend_freq = SLEEP_FREQ; @@ -527,8 +586,55 @@ static struct notifier_block s5pv210_cpufreq_reboot_notifier = { .notifier_call = s5pv210_cpufreq_reboot_notifier_event, }; -static int __init s5pv210_cpufreq_init(void) +static int s5pv210_cpufreq_probe(struct platform_device *pdev) { + struct device_node *np; + int id; + + /* + * HACK: This is a temporary workaround to get access to clock + * and DMC controller registers directly and remove static mappings + * and dependencies on platform headers. It is necessary to enable + * S5PV210 multi-platform support and will be removed together with + * this whole driver as soon as S5PV210 gets migrated to use + * cpufreq-cpu0 driver. + */ + np = of_find_compatible_node(NULL, NULL, "samsung,s5pv210-clock"); + if (!np) { + pr_err("%s: failed to find clock controller DT node\n", + __func__); + return -ENODEV; + } + + clk_base = of_iomap(np, 0); + if (!clk_base) { + pr_err("%s: failed to map clock registers\n", __func__); + return -EFAULT; + } + + for_each_compatible_node(np, NULL, "samsung,s5pv210-dmc") { + id = of_alias_get_id(np, "dmc"); + if (id < 0 || id >= ARRAY_SIZE(dmc_base)) { + pr_err("%s: failed to get alias of dmc node '%s'\n", + __func__, np->name); + return id; + } + + dmc_base[id] = of_iomap(np, 0); + if (!dmc_base[id]) { + pr_err("%s: failed to map dmc%d registers\n", + __func__, id); + return -EFAULT; + } + } + + for (id = 0; id < ARRAY_SIZE(dmc_base); ++id) { + if (!dmc_base[id]) { + pr_err("%s: failed to find dmc%d node\n", __func__, id); + return -ENODEV; + } + } + arm_regulator = regulator_get(NULL, "vddarm"); if (IS_ERR(arm_regulator)) { pr_err("failed to get regulator vddarm"); @@ -547,4 +653,11 @@ static int __init s5pv210_cpufreq_init(void) return cpufreq_register_driver(&s5pv210_driver); } -late_initcall(s5pv210_cpufreq_init); +static struct platform_driver s5pv210_cpufreq_platdrv = { + .driver = { + .name = "s5pv210-cpufreq", + .owner = THIS_MODULE, + }, + .probe = s5pv210_cpufreq_probe, +}; +module_platform_driver(s5pv210_cpufreq_platdrv); diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index a186dec8e5df..38cff69ffe06 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -1,15 +1,9 @@ # # ARM CPU Idle drivers # -config ARM_ARMADA_370_XP_CPUIDLE - bool "CPU Idle Driver for Armada 370/XP family processors" - depends on ARCH_MVEBU - help - Select this to enable cpuidle on Armada 370/XP processors. - config ARM_BIG_LITTLE_CPUIDLE bool "Support for ARM big.LITTLE processors" - depends on ARCH_VEXPRESS_TC2_PM + depends on ARCH_VEXPRESS_TC2_PM || ARCH_EXYNOS depends on MCPM select ARM_CPU_SUSPEND select CPU_IDLE_MULTIPLE_DRIVERS @@ -62,3 +56,9 @@ config ARM_EXYNOS_CPUIDLE depends on ARCH_EXYNOS help Select this to enable cpuidle for Exynos processors + +config ARM_MVEBU_V7_CPUIDLE + bool "CPU Idle Driver for mvebu v7 family processors" + depends on ARCH_MVEBU + help + Select this to enable cpuidle on Armada 370, 38x and XP processors. diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index d8bb1ff72561..11edb31c55e9 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o ################################################################################## # ARM SoC drivers -obj-$(CONFIG_ARM_ARMADA_370_XP_CPUIDLE) += cpuidle-armada-370-xp.o +obj-$(CONFIG_ARM_MVEBU_V7_CPUIDLE) += cpuidle-mvebu-v7.o obj-$(CONFIG_ARM_BIG_LITTLE_CPUIDLE) += cpuidle-big_little.o obj-$(CONFIG_ARM_CLPS711X_CPUIDLE) += cpuidle-clps711x.o obj-$(CONFIG_ARM_HIGHBANK_CPUIDLE) += cpuidle-calxeda.o diff --git a/drivers/cpuidle/cpuidle-armada-370-xp.c b/drivers/cpuidle/cpuidle-armada-370-xp.c deleted file mode 100644 index a5fba0287bfb..000000000000 --- a/drivers/cpuidle/cpuidle-armada-370-xp.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Marvell Armada 370 and Armada XP SoC cpuidle driver - * - * Copyright (C) 2014 Marvell - * - * Nadav Haklai <nadavh@marvell.com> - * Gregory CLEMENT <gregory.clement@free-electrons.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. - * - * Maintainer: Gregory CLEMENT <gregory.clement@free-electrons.com> - */ - -#include <linux/cpu_pm.h> -#include <linux/cpuidle.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/suspend.h> -#include <linux/platform_device.h> -#include <asm/cpuidle.h> - -#define ARMADA_370_XP_MAX_STATES 3 -#define ARMADA_370_XP_FLAG_DEEP_IDLE 0x10000 - -static int (*armada_370_xp_cpu_suspend)(int); - -static int armada_370_xp_enter_idle(struct cpuidle_device *dev, - struct cpuidle_driver *drv, - int index) -{ - int ret; - bool deepidle = false; - cpu_pm_enter(); - - if (drv->states[index].flags & ARMADA_370_XP_FLAG_DEEP_IDLE) - deepidle = true; - - ret = armada_370_xp_cpu_suspend(deepidle); - if (ret) - return ret; - - cpu_pm_exit(); - - return index; -} - -static struct cpuidle_driver armada_370_xp_idle_driver = { - .name = "armada_370_xp_idle", - .states[0] = ARM_CPUIDLE_WFI_STATE, - .states[1] = { - .enter = armada_370_xp_enter_idle, - .exit_latency = 10, - .power_usage = 50, - .target_residency = 100, - .flags = CPUIDLE_FLAG_TIME_VALID, - .name = "Idle", - .desc = "CPU power down", - }, - .states[2] = { - .enter = armada_370_xp_enter_idle, - .exit_latency = 100, - .power_usage = 5, - .target_residency = 1000, - .flags = CPUIDLE_FLAG_TIME_VALID | - ARMADA_370_XP_FLAG_DEEP_IDLE, - .name = "Deep idle", - .desc = "CPU and L2 Fabric power down", - }, - .state_count = ARMADA_370_XP_MAX_STATES, -}; - -static int armada_370_xp_cpuidle_probe(struct platform_device *pdev) -{ - - armada_370_xp_cpu_suspend = (void *)(pdev->dev.platform_data); - return cpuidle_register(&armada_370_xp_idle_driver, NULL); -} - -static struct platform_driver armada_370_xp_cpuidle_plat_driver = { - .driver = { - .name = "cpuidle-armada-370-xp", - .owner = THIS_MODULE, - }, - .probe = armada_370_xp_cpuidle_probe, -}; - -module_platform_driver(armada_370_xp_cpuidle_plat_driver); - -MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>"); -MODULE_DESCRIPTION("Armada 370/XP cpu idle driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/cpuidle/cpuidle-big_little.c b/drivers/cpuidle/cpuidle-big_little.c index b45fc6249041..344d79fa3407 100644 --- a/drivers/cpuidle/cpuidle-big_little.c +++ b/drivers/cpuidle/cpuidle-big_little.c @@ -163,14 +163,24 @@ static int __init bl_idle_driver_init(struct cpuidle_driver *drv, int cpu_id) return 0; } +static const struct of_device_id compatible_machine_match[] = { + { .compatible = "arm,vexpress,v2p-ca15_a7" }, + { .compatible = "samsung,exynos5420" }, + {}, +}; + static int __init bl_idle_init(void) { int ret; + struct device_node *root = of_find_node_by_path("/"); + + if (!root) + return -ENODEV; /* * Initialize the driver just for a compliant set of machines */ - if (!of_machine_is_compatible("arm,vexpress,v2p-ca15_a7")) + if (!of_match_node(compatible_machine_match, root)) return -ENODEV; /* * For now the differentiation between little and big cores diff --git a/drivers/cpuidle/cpuidle-mvebu-v7.c b/drivers/cpuidle/cpuidle-mvebu-v7.c new file mode 100644 index 000000000000..45371bb16214 --- /dev/null +++ b/drivers/cpuidle/cpuidle-mvebu-v7.c @@ -0,0 +1,150 @@ +/* + * Marvell Armada 370, 38x and XP SoC cpuidle driver + * + * Copyright (C) 2014 Marvell + * + * Nadav Haklai <nadavh@marvell.com> + * Gregory CLEMENT <gregory.clement@free-electrons.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. + * + * Maintainer: Gregory CLEMENT <gregory.clement@free-electrons.com> + */ + +#include <linux/cpu_pm.h> +#include <linux/cpuidle.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/suspend.h> +#include <linux/platform_device.h> +#include <asm/cpuidle.h> + +#define MVEBU_V7_FLAG_DEEP_IDLE 0x10000 + +static int (*mvebu_v7_cpu_suspend)(int); + +static int mvebu_v7_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + int ret; + bool deepidle = false; + cpu_pm_enter(); + + if (drv->states[index].flags & MVEBU_V7_FLAG_DEEP_IDLE) + deepidle = true; + + ret = mvebu_v7_cpu_suspend(deepidle); + if (ret) + return ret; + + cpu_pm_exit(); + + return index; +} + +static struct cpuidle_driver armadaxp_idle_driver = { + .name = "armada_xp_idle", + .states[0] = ARM_CPUIDLE_WFI_STATE, + .states[1] = { + .enter = mvebu_v7_enter_idle, + .exit_latency = 10, + .power_usage = 50, + .target_residency = 100, + .flags = CPUIDLE_FLAG_TIME_VALID, + .name = "MV CPU IDLE", + .desc = "CPU power down", + }, + .states[2] = { + .enter = mvebu_v7_enter_idle, + .exit_latency = 100, + .power_usage = 5, + .target_residency = 1000, + .flags = CPUIDLE_FLAG_TIME_VALID | + MVEBU_V7_FLAG_DEEP_IDLE, + .name = "MV CPU DEEP IDLE", + .desc = "CPU and L2 Fabric power down", + }, + .state_count = 3, +}; + +static struct cpuidle_driver armada370_idle_driver = { + .name = "armada_370_idle", + .states[0] = ARM_CPUIDLE_WFI_STATE, + .states[1] = { + .enter = mvebu_v7_enter_idle, + .exit_latency = 100, + .power_usage = 5, + .target_residency = 1000, + .flags = (CPUIDLE_FLAG_TIME_VALID | + MVEBU_V7_FLAG_DEEP_IDLE), + .name = "Deep Idle", + .desc = "CPU and L2 Fabric power down", + }, + .state_count = 2, +}; + +static struct cpuidle_driver armada38x_idle_driver = { + .name = "armada_38x_idle", + .states[0] = ARM_CPUIDLE_WFI_STATE, + .states[1] = { + .enter = mvebu_v7_enter_idle, + .exit_latency = 10, + .power_usage = 5, + .target_residency = 100, + .flags = CPUIDLE_FLAG_TIME_VALID, + .name = "Idle", + .desc = "CPU and SCU power down", + }, + .state_count = 2, +}; + +static int mvebu_v7_cpuidle_probe(struct platform_device *pdev) +{ + mvebu_v7_cpu_suspend = pdev->dev.platform_data; + + if (!strcmp(pdev->dev.driver->name, "cpuidle-armada-xp")) + return cpuidle_register(&armadaxp_idle_driver, NULL); + else if (!strcmp(pdev->dev.driver->name, "cpuidle-armada-370")) + return cpuidle_register(&armada370_idle_driver, NULL); + else if (!strcmp(pdev->dev.driver->name, "cpuidle-armada-38x")) + return cpuidle_register(&armada38x_idle_driver, NULL); + else + return -EINVAL; +} + +static struct platform_driver armadaxp_cpuidle_plat_driver = { + .driver = { + .name = "cpuidle-armada-xp", + .owner = THIS_MODULE, + }, + .probe = mvebu_v7_cpuidle_probe, +}; + +module_platform_driver(armadaxp_cpuidle_plat_driver); + +static struct platform_driver armada370_cpuidle_plat_driver = { + .driver = { + .name = "cpuidle-armada-370", + .owner = THIS_MODULE, + }, + .probe = mvebu_v7_cpuidle_probe, +}; + +module_platform_driver(armada370_cpuidle_plat_driver); + +static struct platform_driver armada38x_cpuidle_plat_driver = { + .driver = { + .name = "cpuidle-armada-38x", + .owner = THIS_MODULE, + }, + .probe = mvebu_v7_cpuidle_probe, +}; + +module_platform_driver(armada38x_cpuidle_plat_driver); + +MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>"); +MODULE_DESCRIPTION("Marvell EBU v7 cpuidle driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index d9cff026827e..3810da47043f 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -1166,234 +1166,9 @@ static struct samsung_gpio_chip s3c64xx_gpios_2bit[] = { #endif }; -/* - * Followings are the gpio banks in S5PV210/S5PC110 - * - * The 'config' member when left to NULL, is initialized to the default - * structure samsung_gpio_cfgs[3] in the init function below. - * - * The 'base' member is also initialized in the init function below. - * Note: The initialization of 'base' member of samsung_gpio_chip structure - * uses the above macro and depends on the banks being listed in order here. - */ - -static struct samsung_gpio_chip s5pv210_gpios_4bit[] = { -#ifdef CONFIG_CPU_S5PV210 - { - .chip = { - .base = S5PV210_GPA0(0), - .ngpio = S5PV210_GPIO_A0_NR, - .label = "GPA0", - }, - }, { - .chip = { - .base = S5PV210_GPA1(0), - .ngpio = S5PV210_GPIO_A1_NR, - .label = "GPA1", - }, - }, { - .chip = { - .base = S5PV210_GPB(0), - .ngpio = S5PV210_GPIO_B_NR, - .label = "GPB", - }, - }, { - .chip = { - .base = S5PV210_GPC0(0), - .ngpio = S5PV210_GPIO_C0_NR, - .label = "GPC0", - }, - }, { - .chip = { - .base = S5PV210_GPC1(0), - .ngpio = S5PV210_GPIO_C1_NR, - .label = "GPC1", - }, - }, { - .chip = { - .base = S5PV210_GPD0(0), - .ngpio = S5PV210_GPIO_D0_NR, - .label = "GPD0", - }, - }, { - .chip = { - .base = S5PV210_GPD1(0), - .ngpio = S5PV210_GPIO_D1_NR, - .label = "GPD1", - }, - }, { - .chip = { - .base = S5PV210_GPE0(0), - .ngpio = S5PV210_GPIO_E0_NR, - .label = "GPE0", - }, - }, { - .chip = { - .base = S5PV210_GPE1(0), - .ngpio = S5PV210_GPIO_E1_NR, - .label = "GPE1", - }, - }, { - .chip = { - .base = S5PV210_GPF0(0), - .ngpio = S5PV210_GPIO_F0_NR, - .label = "GPF0", - }, - }, { - .chip = { - .base = S5PV210_GPF1(0), - .ngpio = S5PV210_GPIO_F1_NR, - .label = "GPF1", - }, - }, { - .chip = { - .base = S5PV210_GPF2(0), - .ngpio = S5PV210_GPIO_F2_NR, - .label = "GPF2", - }, - }, { - .chip = { - .base = S5PV210_GPF3(0), - .ngpio = S5PV210_GPIO_F3_NR, - .label = "GPF3", - }, - }, { - .chip = { - .base = S5PV210_GPG0(0), - .ngpio = S5PV210_GPIO_G0_NR, - .label = "GPG0", - }, - }, { - .chip = { - .base = S5PV210_GPG1(0), - .ngpio = S5PV210_GPIO_G1_NR, - .label = "GPG1", - }, - }, { - .chip = { - .base = S5PV210_GPG2(0), - .ngpio = S5PV210_GPIO_G2_NR, - .label = "GPG2", - }, - }, { - .chip = { - .base = S5PV210_GPG3(0), - .ngpio = S5PV210_GPIO_G3_NR, - .label = "GPG3", - }, - }, { - .chip = { - .base = S5PV210_GPI(0), - .ngpio = S5PV210_GPIO_I_NR, - .label = "GPI", - }, - }, { - .chip = { - .base = S5PV210_GPJ0(0), - .ngpio = S5PV210_GPIO_J0_NR, - .label = "GPJ0", - }, - }, { - .chip = { - .base = S5PV210_GPJ1(0), - .ngpio = S5PV210_GPIO_J1_NR, - .label = "GPJ1", - }, - }, { - .chip = { - .base = S5PV210_GPJ2(0), - .ngpio = S5PV210_GPIO_J2_NR, - .label = "GPJ2", - }, - }, { - .chip = { - .base = S5PV210_GPJ3(0), - .ngpio = S5PV210_GPIO_J3_NR, - .label = "GPJ3", - }, - }, { - .chip = { - .base = S5PV210_GPJ4(0), - .ngpio = S5PV210_GPIO_J4_NR, - .label = "GPJ4", - }, - }, { - .chip = { - .base = S5PV210_MP01(0), - .ngpio = S5PV210_GPIO_MP01_NR, - .label = "MP01", - }, - }, { - .chip = { - .base = S5PV210_MP02(0), - .ngpio = S5PV210_GPIO_MP02_NR, - .label = "MP02", - }, - }, { - .chip = { - .base = S5PV210_MP03(0), - .ngpio = S5PV210_GPIO_MP03_NR, - .label = "MP03", - }, - }, { - .chip = { - .base = S5PV210_MP04(0), - .ngpio = S5PV210_GPIO_MP04_NR, - .label = "MP04", - }, - }, { - .chip = { - .base = S5PV210_MP05(0), - .ngpio = S5PV210_GPIO_MP05_NR, - .label = "MP05", - }, - }, { - .base = (S5P_VA_GPIO + 0xC00), - .irq_base = IRQ_EINT(0), - .chip = { - .base = S5PV210_GPH0(0), - .ngpio = S5PV210_GPIO_H0_NR, - .label = "GPH0", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC20), - .irq_base = IRQ_EINT(8), - .chip = { - .base = S5PV210_GPH1(0), - .ngpio = S5PV210_GPIO_H1_NR, - .label = "GPH1", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC40), - .irq_base = IRQ_EINT(16), - .chip = { - .base = S5PV210_GPH2(0), - .ngpio = S5PV210_GPIO_H2_NR, - .label = "GPH2", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC60), - .irq_base = IRQ_EINT(24), - .chip = { - .base = S5PV210_GPH3(0), - .ngpio = S5PV210_GPIO_H3_NR, - .label = "GPH3", - .to_irq = samsung_gpiolib_to_irq, - }, - }, -#endif -}; - /* TODO: cleanup soc_is_* */ static __init int samsung_gpiolib_init(void) { - struct samsung_gpio_chip *chip; - int i, nr_chips; - int group = 0; - /* * Currently there are two drivers that can provide GPIO support for * Samsung SoCs. For device tree enabled platforms, the new @@ -1417,21 +1192,6 @@ static __init int samsung_gpiolib_init(void) S3C64XX_VA_GPIO); samsung_gpiolib_add_4bit2_chips(s3c64xx_gpios_4bit2, ARRAY_SIZE(s3c64xx_gpios_4bit2)); - } else if (soc_is_s5pv210()) { - group = 0; - chip = s5pv210_gpios_4bit; - nr_chips = ARRAY_SIZE(s5pv210_gpios_4bit); - - for (i = 0; i < nr_chips; i++, chip++) { - if (!chip->config) { - chip->config = &samsung_gpio_cfgs[3]; - chip->group = group++; - } - } - samsung_gpiolib_add_4bit_chips(s5pv210_gpios_4bit, nr_chips, S5P_VA_GPIO); -#if defined(CONFIG_CPU_S5PV210) && defined(CONFIG_S5P_GPIO_INT) - s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR); -#endif } else { WARN(1, "Unknown SoC in gpio-samsung, no GPIOs added\n"); return -ENODEV; diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c index 3ae2bb8d9cf2..ccf58548b161 100644 --- a/drivers/irqchip/irq-versatile-fpga.c +++ b/drivers/irqchip/irq-versatile-fpga.c @@ -14,6 +14,8 @@ #include <asm/exception.h> #include <asm/mach/irq.h> +#include "irqchip.h" + #define IRQ_STATUS 0x00 #define IRQ_RAW_STATUS 0x04 #define IRQ_ENABLE_SET 0x08 @@ -26,6 +28,8 @@ #define FIQ_ENABLE_SET 0x28 #define FIQ_ENABLE_CLEAR 0x2C +#define PIC_ENABLES 0x20 /* set interrupt pass through bits */ + /** * struct fpga_irq_data - irq data container for the FPGA IRQ controller * @base: memory offset in virtual memory @@ -201,14 +205,26 @@ int __init fpga_irq_of_init(struct device_node *node, /* Some chips are cascaded from a parent IRQ */ parent_irq = irq_of_parse_and_map(node, 0); - if (!parent_irq) + if (!parent_irq) { + set_handle_irq(fpga_handle_irq); parent_irq = -1; + } fpga_irq_init(base, node->name, 0, parent_irq, valid_mask, node); writel(clear_mask, base + IRQ_ENABLE_CLEAR); writel(clear_mask, base + FIQ_ENABLE_CLEAR); + /* + * On Versatile AB/PB, some secondary interrupts have a direct + * pass-thru to the primary controller for IRQs 20 and 22-31 which need + * to be enabled. See section 3.10 of the Versatile AB user guide. + */ + if (of_device_is_compatible(node, "arm,versatile-sic")) + writel(0xffd00000, base + PIC_ENABLES); + return 0; } +IRQCHIP_DECLARE(arm_fpga, "arm,versatile-fpga-irq", fpga_irq_of_init); +IRQCHIP_DECLARE(arm_fpga_sic, "arm,versatile-sic", fpga_irq_of_init); #endif diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index cc97c897945a..d1f5fc924c93 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -159,6 +159,16 @@ config PHY_SAMSUNG_USB2 for particular PHYs will be enabled based on the SoC type in addition to this driver. +config PHY_S5PV210_USB2 + bool "Support for S5PV210" + depends on PHY_SAMSUNG_USB2 + depends on ARCH_S5PV210 + help + Enable USB PHY support for S5PV210. This option requires that Samsung + USB 2.0 PHY driver is enabled and means that support for this + particular SoC is compiled in the driver. In case of S5PV210 two phys + are available - device and host. + config PHY_EXYNOS4210_USB2 bool depends on PHY_SAMSUNG_USB2 diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 971ad0aac388..ec24e915349b 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -21,6 +21,7 @@ phy-exynos-usb2-y += phy-samsung-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o +phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o diff --git a/drivers/phy/phy-s5pv210-usb2.c b/drivers/phy/phy-s5pv210-usb2.c new file mode 100644 index 000000000000..004d320767e4 --- /dev/null +++ b/drivers/phy/phy-s5pv210-usb2.c @@ -0,0 +1,187 @@ +/* + * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Authors: Kamil Debski <k.debski@samsung.com> + * + * 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/delay.h> +#include <linux/io.h> +#include <linux/phy/phy.h> +#include "phy-samsung-usb2.h" + +/* Exynos USB PHY registers */ + +/* PHY power control */ +#define S5PV210_UPHYPWR 0x0 + +#define S5PV210_UPHYPWR_PHY0_SUSPEND BIT(0) +#define S5PV210_UPHYPWR_PHY0_PWR BIT(3) +#define S5PV210_UPHYPWR_PHY0_OTG_PWR BIT(4) +#define S5PV210_UPHYPWR_PHY0 ( \ + S5PV210_UPHYPWR_PHY0_SUSPEND | \ + S5PV210_UPHYPWR_PHY0_PWR | \ + S5PV210_UPHYPWR_PHY0_OTG_PWR) + +#define S5PV210_UPHYPWR_PHY1_SUSPEND BIT(6) +#define S5PV210_UPHYPWR_PHY1_PWR BIT(7) +#define S5PV210_UPHYPWR_PHY1 ( \ + S5PV210_UPHYPWR_PHY1_SUSPEND | \ + S5PV210_UPHYPWR_PHY1_PWR) + +/* PHY clock control */ +#define S5PV210_UPHYCLK 0x4 + +#define S5PV210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) +#define S5PV210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) +#define S5PV210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) +#define S5PV210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) + +#define S5PV210_UPHYCLK_PHY0_ID_PULLUP BIT(2) +#define S5PV210_UPHYCLK_PHY0_COMMON_ON BIT(4) +#define S5PV210_UPHYCLK_PHY1_COMMON_ON BIT(7) + +/* PHY reset control */ +#define S5PV210_UPHYRST 0x8 + +#define S5PV210_URSTCON_PHY0 BIT(0) +#define S5PV210_URSTCON_OTG_HLINK BIT(1) +#define S5PV210_URSTCON_OTG_PHYLINK BIT(2) +#define S5PV210_URSTCON_PHY1_ALL BIT(3) +#define S5PV210_URSTCON_HOST_LINK_ALL BIT(4) + +/* Isolation, configured in the power management unit */ +#define S5PV210_USB_ISOL_OFFSET 0x680c +#define S5PV210_USB_ISOL_DEVICE BIT(0) +#define S5PV210_USB_ISOL_HOST BIT(1) + + +enum s5pv210_phy_id { + S5PV210_DEVICE, + S5PV210_HOST, + S5PV210_NUM_PHYS, +}; + +/* + * s5pv210_rate_to_clk() converts the supplied clock rate to the value that + * can be written to the phy register. + */ +static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg) +{ + switch (rate) { + case 12 * MHZ: + *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ; + break; + case 24 * MHZ: + *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ; + break; + case 48 * MHZ: + *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + u32 mask; + + switch (inst->cfg->id) { + case S5PV210_DEVICE: + mask = S5PV210_USB_ISOL_DEVICE; + break; + case S5PV210_HOST: + mask = S5PV210_USB_ISOL_HOST; + break; + default: + return; + }; + + regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET, + mask, on ? 0 : mask); +} + +static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + u32 rstbits = 0; + u32 phypwr = 0; + u32 rst; + u32 pwr; + + switch (inst->cfg->id) { + case S5PV210_DEVICE: + phypwr = S5PV210_UPHYPWR_PHY0; + rstbits = S5PV210_URSTCON_PHY0; + break; + case S5PV210_HOST: + phypwr = S5PV210_UPHYPWR_PHY1; + rstbits = S5PV210_URSTCON_PHY1_ALL | + S5PV210_URSTCON_HOST_LINK_ALL; + break; + }; + + if (on) { + writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK); + + pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); + pwr &= ~phypwr; + writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); + + rst = readl(drv->reg_phy + S5PV210_UPHYRST); + rst |= rstbits; + writel(rst, drv->reg_phy + S5PV210_UPHYRST); + udelay(10); + rst &= ~rstbits; + writel(rst, drv->reg_phy + S5PV210_UPHYRST); + } else { + pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); + pwr |= phypwr; + writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); + } +} + +static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst) +{ + s5pv210_isol(inst, 0); + s5pv210_phy_pwr(inst, 1); + + return 0; +} + +static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst) +{ + s5pv210_phy_pwr(inst, 0); + s5pv210_isol(inst, 1); + + return 0; +} + +static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = { + [S5PV210_DEVICE] = { + .label = "device", + .id = S5PV210_DEVICE, + .power_on = s5pv210_power_on, + .power_off = s5pv210_power_off, + }, + [S5PV210_HOST] = { + .label = "host", + .id = S5PV210_HOST, + .power_on = s5pv210_power_on, + .power_off = s5pv210_power_off, + }, +}; + +const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = { + .num_phys = ARRAY_SIZE(s5pv210_phys), + .phys = s5pv210_phys, + .rate_to_clk = s5pv210_rate_to_clk, +}; diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c index ae30640a411d..3732ca25e09f 100644 --- a/drivers/phy/phy-samsung-usb2.c +++ b/drivers/phy/phy-samsung-usb2.c @@ -111,6 +111,12 @@ static const struct of_device_id samsung_usb2_phy_of_match[] = { .data = &exynos5250_usb2_phy_config, }, #endif +#ifdef CONFIG_PHY_S5PV210_USB2 + { + .compatible = "samsung,s5pv210-usb2-phy", + .data = &s5pv210_usb2_phy_config, + }, +#endif { }, }; MODULE_DEVICE_TABLE(of, samsung_usb2_phy_of_match); diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h index b03da0ef39ac..44bead9b8f34 100644 --- a/drivers/phy/phy-samsung-usb2.h +++ b/drivers/phy/phy-samsung-usb2.h @@ -67,4 +67,5 @@ extern const struct samsung_usb2_phy_config exynos3250_usb2_phy_config; extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config; extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config; extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config; +extern const struct samsung_usb2_phy_config s5pv210_usb2_phy_config; #endif diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index f2ac54df496f..ca41523bbebf 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -39,6 +39,12 @@ config POWER_RESET_GPIO If your board needs a GPIO high/low to power down, say Y and create a binding in your devicetree. +config POWER_RESET_HISI + bool "Hisilicon power-off driver" + depends on POWER_RESET && ARCH_HISI + help + Reboot support for Hisilicon boards. + config POWER_RESET_MSM bool "Qualcomm MSM power-off driver" depends on POWER_RESET && ARCH_QCOM diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 7379818ca69d..a42e70edd037 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o +obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o diff --git a/drivers/power/reset/hisi-reboot.c b/drivers/power/reset/hisi-reboot.c new file mode 100644 index 000000000000..0c91d0231d36 --- /dev/null +++ b/drivers/power/reset/hisi-reboot.c @@ -0,0 +1,67 @@ +/* + * Hisilicon SoC reset code + * + * Copyright (c) 2014 Hisilicon Ltd. + * Copyright (c) 2014 Linaro Ltd. + * + * Author: Haojian Zhuang <haojian.zhuang@linaro.org> + * + * 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/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> + +#include <asm/proc-fns.h> +#include <asm/system_misc.h> + +static void __iomem *base; +static u32 reboot_offset; + +static void hisi_restart(enum reboot_mode mode, const char *cmd) +{ + writel_relaxed(0xdeadbeef, base + reboot_offset); + + while (1) + cpu_do_idle(); +} + +static int hisi_reboot_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + + base = of_iomap(np, 0); + if (!base) { + WARN(1, "failed to map base address"); + return -ENODEV; + } + + if (of_property_read_u32(np, "reboot-offset", &reboot_offset) < 0) { + pr_err("failed to find reboot-offset property\n"); + return -EINVAL; + } + + arm_pm_restart = hisi_restart; + + return 0; +} + +static struct of_device_id hisi_reboot_of_match[] = { + { .compatible = "hisilicon,sysctrl" }, + {} +}; + +static struct platform_driver hisi_reboot_driver = { + .probe = hisi_reboot_probe, + .driver = { + .name = "hisi-reboot", + .of_match_table = hisi_reboot_of_match, + }, +}; +module_platform_driver(hisi_reboot_driver); diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 4aff02d6712e..c78f43a481ce 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -47,10 +47,6 @@ #include <asm/irq.h> -#ifdef CONFIG_SAMSUNG_CLOCK -#include <plat/clock.h> -#endif - #include "samsung.h" #if defined(CONFIG_SERIAL_SAMSUNG_DEBUG) && \ |