From 30e1e28598c2674c133148d8aec6d431d7acd314 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Mon, 13 May 2013 10:46:38 -0700 Subject: arm: zynq: Migrate platform to clock controller Migrate the Zynq platform and its drivers to use the new clock controller driver. Signed-off-by: Soren Brinkmann Cc: John Stultz Cc: Thomas Gleixner Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-serial@vger.kernel.org Signed-off-by: Michal Simek Acked-by: Mike Turquette --- include/linux/clk/zynq.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/clk/zynq.h b/include/linux/clk/zynq.h index 56be7cd9aa8b..e062d317ccce 100644 --- a/include/linux/clk/zynq.h +++ b/include/linux/clk/zynq.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2013 Xilinx Inc. * Copyright (C) 2012 National Instruments * * This program is free software; you can redistribute it and/or modify @@ -19,6 +20,11 @@ #ifndef __LINUX_CLK_ZYNQ_H_ #define __LINUX_CLK_ZYNQ_H_ -void __init xilinx_zynq_clocks_init(void __iomem *slcr); +#include +void zynq_clock_init(void __iomem *slcr); + +struct clk *clk_register_zynq_pll(const char *name, const char *parent, + void __iomem *pll_ctrl, void __iomem *pll_status, u8 lock_index, + spinlock_t *lock); #endif -- cgit v1.2.3 From ea7e0bd7e78868bbe6df6834595ef96166dd296a Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Tue, 28 May 2013 15:13:18 +0000 Subject: clk: mvebu: disintegrate obsolete file Switch from function-centric to soc-centric clock drivers now makes a bunch of files obsolete. This deletes all files and Kconfig options that are not required anymore. Signed-off-by: Sebastian Hesselbarth Acked-by: Mike Turquette Signed-off-by: Jason Cooper --- drivers/clk/mvebu/Kconfig | 10 +- drivers/clk/mvebu/Makefile | 2 - drivers/clk/mvebu/clk-core.c | 675 ------------------------------------ drivers/clk/mvebu/clk-core.h | 18 - drivers/clk/mvebu/clk-gating-ctrl.c | 247 ------------- drivers/clk/mvebu/clk-gating-ctrl.h | 22 -- drivers/clk/mvebu/clk.c | 23 -- include/linux/clk/mvebu.h | 22 -- 8 files changed, 2 insertions(+), 1017 deletions(-) delete mode 100644 drivers/clk/mvebu/clk-core.c delete mode 100644 drivers/clk/mvebu/clk-core.h delete mode 100644 drivers/clk/mvebu/clk-gating-ctrl.c delete mode 100644 drivers/clk/mvebu/clk-gating-ctrl.h delete mode 100644 drivers/clk/mvebu/clk.c delete mode 100644 include/linux/clk/mvebu.h (limited to 'include') diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig index 8740d34e959b..0b0f3e729cf7 100644 --- a/drivers/clk/mvebu/Kconfig +++ b/drivers/clk/mvebu/Kconfig @@ -1,13 +1,7 @@ -config MVEBU_CLK_CORE - bool +config MVEBU_CLK_COMMON + bool config MVEBU_CLK_CPU - bool - -config MVEBU_CLK_GATING - bool - -config MVEBU_CLK_COMMON bool config ARMADA_370_CLK diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index 13f3d22ccf4e..1c7e70c63fb2 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile @@ -1,7 +1,5 @@ obj-$(CONFIG_MVEBU_CLK_COMMON) += common.o -obj-$(CONFIG_MVEBU_CLK_CORE) += clk.o clk-core.o obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o -obj-$(CONFIG_MVEBU_CLK_GATING) += clk-gating-ctrl.o obj-$(CONFIG_ARMADA_370_CLK) += armada-370.o obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o diff --git a/drivers/clk/mvebu/clk-core.c b/drivers/clk/mvebu/clk-core.c deleted file mode 100644 index 0a53edbae8b8..000000000000 --- a/drivers/clk/mvebu/clk-core.c +++ /dev/null @@ -1,675 +0,0 @@ -/* - * Marvell EBU clock core handling defined at reset - * - * Copyright (C) 2012 Marvell - * - * Gregory CLEMENT - * Sebastian Hesselbarth - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ -#include -#include -#include -#include -#include -#include -#include -#include "clk-core.h" - -struct core_ratio { - int id; - const char *name; -}; - -struct core_clocks { - u32 (*get_tclk_freq)(void __iomem *sar); - u32 (*get_cpu_freq)(void __iomem *sar); - void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div); - const struct core_ratio *ratios; - int num_ratios; -}; - -static struct clk_onecell_data clk_data; - -static void __init mvebu_clk_core_setup(struct device_node *np, - struct core_clocks *coreclk) -{ - const char *tclk_name = "tclk"; - const char *cpuclk_name = "cpuclk"; - void __iomem *base; - unsigned long rate; - int n; - - base = of_iomap(np, 0); - if (WARN_ON(!base)) - return; - - /* - * Allocate struct for TCLK, cpu clk, and core ratio clocks - */ - clk_data.clk_num = 2 + coreclk->num_ratios; - clk_data.clks = kzalloc(clk_data.clk_num * sizeof(struct clk *), - GFP_KERNEL); - if (WARN_ON(!clk_data.clks)) - return; - - /* - * Register TCLK - */ - of_property_read_string_index(np, "clock-output-names", 0, - &tclk_name); - rate = coreclk->get_tclk_freq(base); - clk_data.clks[0] = clk_register_fixed_rate(NULL, tclk_name, NULL, - CLK_IS_ROOT, rate); - WARN_ON(IS_ERR(clk_data.clks[0])); - - /* - * Register CPU clock - */ - of_property_read_string_index(np, "clock-output-names", 1, - &cpuclk_name); - rate = coreclk->get_cpu_freq(base); - clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL, - CLK_IS_ROOT, rate); - WARN_ON(IS_ERR(clk_data.clks[1])); - - /* - * Register fixed-factor clocks derived from CPU clock - */ - for (n = 0; n < coreclk->num_ratios; n++) { - const char *rclk_name = coreclk->ratios[n].name; - int mult, div; - - of_property_read_string_index(np, "clock-output-names", - 2+n, &rclk_name); - coreclk->get_clk_ratio(base, coreclk->ratios[n].id, - &mult, &div); - clk_data.clks[2+n] = clk_register_fixed_factor(NULL, rclk_name, - cpuclk_name, 0, mult, div); - WARN_ON(IS_ERR(clk_data.clks[2+n])); - }; - - /* - * SAR register isn't needed anymore - */ - iounmap(base); - - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); -} - -#ifdef CONFIG_MACH_ARMADA_370_XP -/* - * Armada 370/XP Sample At Reset is a 64 bit bitfiled split in two - * register of 32 bits - */ - -#define SARL 0 /* Low part [0:31] */ -#define SARL_AXP_PCLK_FREQ_OPT 21 -#define SARL_AXP_PCLK_FREQ_OPT_MASK 0x7 -#define SARL_A370_PCLK_FREQ_OPT 11 -#define SARL_A370_PCLK_FREQ_OPT_MASK 0xF -#define SARL_AXP_FAB_FREQ_OPT 24 -#define SARL_AXP_FAB_FREQ_OPT_MASK 0xF -#define SARL_A370_FAB_FREQ_OPT 15 -#define SARL_A370_FAB_FREQ_OPT_MASK 0x1F -#define SARL_A370_TCLK_FREQ_OPT 20 -#define SARL_A370_TCLK_FREQ_OPT_MASK 0x1 -#define SARH 4 /* High part [32:63] */ -#define SARH_AXP_PCLK_FREQ_OPT (52-32) -#define SARH_AXP_PCLK_FREQ_OPT_MASK 0x1 -#define SARH_AXP_PCLK_FREQ_OPT_SHIFT 3 -#define SARH_AXP_FAB_FREQ_OPT (51-32) -#define SARH_AXP_FAB_FREQ_OPT_MASK 0x1 -#define SARH_AXP_FAB_FREQ_OPT_SHIFT 4 - -static const u32 __initconst armada_370_tclk_frequencies[] = { - 16600000, - 20000000, -}; - -static u32 __init armada_370_get_tclk_freq(void __iomem *sar) -{ - u8 tclk_freq_select = 0; - - tclk_freq_select = ((readl(sar) >> SARL_A370_TCLK_FREQ_OPT) & - SARL_A370_TCLK_FREQ_OPT_MASK); - return armada_370_tclk_frequencies[tclk_freq_select]; -} - -static const u32 __initconst armada_370_cpu_frequencies[] = { - 400000000, - 533000000, - 667000000, - 800000000, - 1000000000, - 1067000000, - 1200000000, -}; - -static u32 __init armada_370_get_cpu_freq(void __iomem *sar) -{ - u32 cpu_freq; - u8 cpu_freq_select = 0; - - cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) & - SARL_A370_PCLK_FREQ_OPT_MASK); - if (cpu_freq_select >= ARRAY_SIZE(armada_370_cpu_frequencies)) { - pr_err("CPU freq select unsupported %d\n", cpu_freq_select); - cpu_freq = 0; - } else - cpu_freq = armada_370_cpu_frequencies[cpu_freq_select]; - - return cpu_freq; -} - -enum { A370_XP_NBCLK, A370_XP_HCLK, A370_XP_DRAMCLK }; - -static const struct core_ratio __initconst armada_370_xp_core_ratios[] = { - { .id = A370_XP_NBCLK, .name = "nbclk" }, - { .id = A370_XP_HCLK, .name = "hclk" }, - { .id = A370_XP_DRAMCLK, .name = "dramclk" }, -}; - -static const int __initconst armada_370_xp_nbclk_ratios[32][2] = { - {0, 1}, {1, 2}, {2, 2}, {2, 2}, - {1, 2}, {1, 2}, {1, 1}, {2, 3}, - {0, 1}, {1, 2}, {2, 4}, {0, 1}, - {1, 2}, {0, 1}, {0, 1}, {2, 2}, - {0, 1}, {0, 1}, {0, 1}, {1, 1}, - {2, 3}, {0, 1}, {0, 1}, {0, 1}, - {0, 1}, {0, 1}, {0, 1}, {1, 1}, - {0, 1}, {0, 1}, {0, 1}, {0, 1}, -}; - -static const int __initconst armada_370_xp_hclk_ratios[32][2] = { - {0, 1}, {1, 2}, {2, 6}, {2, 3}, - {1, 3}, {1, 4}, {1, 2}, {2, 6}, - {0, 1}, {1, 6}, {2, 10}, {0, 1}, - {1, 4}, {0, 1}, {0, 1}, {2, 5}, - {0, 1}, {0, 1}, {0, 1}, {1, 2}, - {2, 6}, {0, 1}, {0, 1}, {0, 1}, - {0, 1}, {0, 1}, {0, 1}, {1, 1}, - {0, 1}, {0, 1}, {0, 1}, {0, 1}, -}; - -static const int __initconst armada_370_xp_dramclk_ratios[32][2] = { - {0, 1}, {1, 2}, {2, 3}, {2, 3}, - {1, 3}, {1, 2}, {1, 2}, {2, 6}, - {0, 1}, {1, 3}, {2, 5}, {0, 1}, - {1, 4}, {0, 1}, {0, 1}, {2, 5}, - {0, 1}, {0, 1}, {0, 1}, {1, 1}, - {2, 3}, {0, 1}, {0, 1}, {0, 1}, - {0, 1}, {0, 1}, {0, 1}, {1, 1}, - {0, 1}, {0, 1}, {0, 1}, {0, 1}, -}; - -static void __init armada_370_xp_get_clk_ratio(u32 opt, - void __iomem *sar, int id, int *mult, int *div) -{ - switch (id) { - case A370_XP_NBCLK: - *mult = armada_370_xp_nbclk_ratios[opt][0]; - *div = armada_370_xp_nbclk_ratios[opt][1]; - break; - case A370_XP_HCLK: - *mult = armada_370_xp_hclk_ratios[opt][0]; - *div = armada_370_xp_hclk_ratios[opt][1]; - break; - case A370_XP_DRAMCLK: - *mult = armada_370_xp_dramclk_ratios[opt][0]; - *div = armada_370_xp_dramclk_ratios[opt][1]; - break; - } -} - -static void __init armada_370_get_clk_ratio( - void __iomem *sar, int id, int *mult, int *div) -{ - u32 opt = ((readl(sar) >> SARL_A370_FAB_FREQ_OPT) & - SARL_A370_FAB_FREQ_OPT_MASK); - - armada_370_xp_get_clk_ratio(opt, sar, id, mult, div); -} - - -static const struct core_clocks armada_370_core_clocks = { - .get_tclk_freq = armada_370_get_tclk_freq, - .get_cpu_freq = armada_370_get_cpu_freq, - .get_clk_ratio = armada_370_get_clk_ratio, - .ratios = armada_370_xp_core_ratios, - .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios), -}; - -static const u32 __initconst armada_xp_cpu_frequencies[] = { - 1000000000, - 1066000000, - 1200000000, - 1333000000, - 1500000000, - 1666000000, - 1800000000, - 2000000000, - 667000000, - 0, - 800000000, - 1600000000, -}; - -/* For Armada XP TCLK frequency is fix: 250MHz */ -static u32 __init armada_xp_get_tclk_freq(void __iomem *sar) -{ - return 250 * 1000 * 1000; -} - -static u32 __init armada_xp_get_cpu_freq(void __iomem *sar) -{ - u32 cpu_freq; - u8 cpu_freq_select = 0; - - cpu_freq_select = ((readl(sar) >> SARL_AXP_PCLK_FREQ_OPT) & - SARL_AXP_PCLK_FREQ_OPT_MASK); - /* - * The upper bit is not contiguous to the other ones and - * located in the high part of the SAR registers - */ - cpu_freq_select |= (((readl(sar+4) >> SARH_AXP_PCLK_FREQ_OPT) & - SARH_AXP_PCLK_FREQ_OPT_MASK) - << SARH_AXP_PCLK_FREQ_OPT_SHIFT); - if (cpu_freq_select >= ARRAY_SIZE(armada_xp_cpu_frequencies)) { - pr_err("CPU freq select unsupported: %d\n", cpu_freq_select); - cpu_freq = 0; - } else - cpu_freq = armada_xp_cpu_frequencies[cpu_freq_select]; - - return cpu_freq; -} - -static void __init armada_xp_get_clk_ratio( - void __iomem *sar, int id, int *mult, int *div) -{ - - u32 opt = ((readl(sar) >> SARL_AXP_FAB_FREQ_OPT) & - SARL_AXP_FAB_FREQ_OPT_MASK); - /* - * The upper bit is not contiguous to the other ones and - * located in the high part of the SAR registers - */ - opt |= (((readl(sar+4) >> SARH_AXP_FAB_FREQ_OPT) & - SARH_AXP_FAB_FREQ_OPT_MASK) - << SARH_AXP_FAB_FREQ_OPT_SHIFT); - - armada_370_xp_get_clk_ratio(opt, sar, id, mult, div); -} - -static const struct core_clocks armada_xp_core_clocks = { - .get_tclk_freq = armada_xp_get_tclk_freq, - .get_cpu_freq = armada_xp_get_cpu_freq, - .get_clk_ratio = armada_xp_get_clk_ratio, - .ratios = armada_370_xp_core_ratios, - .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios), -}; - -#endif /* CONFIG_MACH_ARMADA_370_XP */ - -/* - * Dove PLL sample-at-reset configuration - * - * SAR0[8:5] : CPU frequency - * 5 = 1000 MHz - * 6 = 933 MHz - * 7 = 933 MHz - * 8 = 800 MHz - * 9 = 800 MHz - * 10 = 800 MHz - * 11 = 1067 MHz - * 12 = 667 MHz - * 13 = 533 MHz - * 14 = 400 MHz - * 15 = 333 MHz - * others reserved. - * - * SAR0[11:9] : CPU to L2 Clock divider ratio - * 0 = (1/1) * CPU - * 2 = (1/2) * CPU - * 4 = (1/3) * CPU - * 6 = (1/4) * CPU - * others reserved. - * - * SAR0[15:12] : CPU to DDR DRAM Clock divider ratio - * 0 = (1/1) * CPU - * 2 = (1/2) * CPU - * 3 = (2/5) * CPU - * 4 = (1/3) * CPU - * 6 = (1/4) * CPU - * 8 = (1/5) * CPU - * 10 = (1/6) * CPU - * 12 = (1/7) * CPU - * 14 = (1/8) * CPU - * 15 = (1/10) * CPU - * others reserved. - * - * SAR0[24:23] : TCLK frequency - * 0 = 166 MHz - * 1 = 125 MHz - * others reserved. - */ -#ifdef CONFIG_ARCH_DOVE -#define SAR_DOVE_CPU_FREQ 5 -#define SAR_DOVE_CPU_FREQ_MASK 0xf -#define SAR_DOVE_L2_RATIO 9 -#define SAR_DOVE_L2_RATIO_MASK 0x7 -#define SAR_DOVE_DDR_RATIO 12 -#define SAR_DOVE_DDR_RATIO_MASK 0xf -#define SAR_DOVE_TCLK_FREQ 23 -#define SAR_DOVE_TCLK_FREQ_MASK 0x3 - -static const u32 __initconst dove_tclk_frequencies[] = { - 166666667, - 125000000, - 0, 0 -}; - -static u32 __init dove_get_tclk_freq(void __iomem *sar) -{ - u32 opt = (readl(sar) >> SAR_DOVE_TCLK_FREQ) & - SAR_DOVE_TCLK_FREQ_MASK; - return dove_tclk_frequencies[opt]; -} - -static const u32 __initconst dove_cpu_frequencies[] = { - 0, 0, 0, 0, 0, - 1000000000, - 933333333, 933333333, - 800000000, 800000000, 800000000, - 1066666667, - 666666667, - 533333333, - 400000000, - 333333333 -}; - -static u32 __init dove_get_cpu_freq(void __iomem *sar) -{ - u32 opt = (readl(sar) >> SAR_DOVE_CPU_FREQ) & - SAR_DOVE_CPU_FREQ_MASK; - return dove_cpu_frequencies[opt]; -} - -enum { DOVE_CPU_TO_L2, DOVE_CPU_TO_DDR }; - -static const struct core_ratio __initconst dove_core_ratios[] = { - { .id = DOVE_CPU_TO_L2, .name = "l2clk", }, - { .id = DOVE_CPU_TO_DDR, .name = "ddrclk", } -}; - -static const int __initconst dove_cpu_l2_ratios[8][2] = { - { 1, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, - { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 } -}; - -static const int __initconst dove_cpu_ddr_ratios[16][2] = { - { 1, 1 }, { 0, 1 }, { 1, 2 }, { 2, 5 }, - { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 }, - { 1, 5 }, { 0, 1 }, { 1, 6 }, { 0, 1 }, - { 1, 7 }, { 0, 1 }, { 1, 8 }, { 1, 10 } -}; - -static void __init dove_get_clk_ratio( - void __iomem *sar, int id, int *mult, int *div) -{ - switch (id) { - case DOVE_CPU_TO_L2: - { - u32 opt = (readl(sar) >> SAR_DOVE_L2_RATIO) & - SAR_DOVE_L2_RATIO_MASK; - *mult = dove_cpu_l2_ratios[opt][0]; - *div = dove_cpu_l2_ratios[opt][1]; - break; - } - case DOVE_CPU_TO_DDR: - { - u32 opt = (readl(sar) >> SAR_DOVE_DDR_RATIO) & - SAR_DOVE_DDR_RATIO_MASK; - *mult = dove_cpu_ddr_ratios[opt][0]; - *div = dove_cpu_ddr_ratios[opt][1]; - break; - } - } -} - -static const struct core_clocks dove_core_clocks = { - .get_tclk_freq = dove_get_tclk_freq, - .get_cpu_freq = dove_get_cpu_freq, - .get_clk_ratio = dove_get_clk_ratio, - .ratios = dove_core_ratios, - .num_ratios = ARRAY_SIZE(dove_core_ratios), -}; -#endif /* CONFIG_ARCH_DOVE */ - -/* - * Kirkwood PLL sample-at-reset configuration - * (6180 has different SAR layout than other Kirkwood SoCs) - * - * SAR0[4:3,22,1] : CPU frequency (6281,6292,6282) - * 4 = 600 MHz - * 6 = 800 MHz - * 7 = 1000 MHz - * 9 = 1200 MHz - * 12 = 1500 MHz - * 13 = 1600 MHz - * 14 = 1800 MHz - * 15 = 2000 MHz - * others reserved. - * - * SAR0[19,10:9] : CPU to L2 Clock divider ratio (6281,6292,6282) - * 1 = (1/2) * CPU - * 3 = (1/3) * CPU - * 5 = (1/4) * CPU - * others reserved. - * - * SAR0[8:5] : CPU to DDR DRAM Clock divider ratio (6281,6292,6282) - * 2 = (1/2) * CPU - * 4 = (1/3) * CPU - * 6 = (1/4) * CPU - * 7 = (2/9) * CPU - * 8 = (1/5) * CPU - * 9 = (1/6) * CPU - * others reserved. - * - * SAR0[4:2] : Kirkwood 6180 cpu/l2/ddr clock configuration (6180 only) - * 5 = [CPU = 600 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/3) * CPU] - * 6 = [CPU = 800 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/4) * CPU] - * 7 = [CPU = 1000 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/5) * CPU] - * others reserved. - * - * SAR0[21] : TCLK frequency - * 0 = 200 MHz - * 1 = 166 MHz - * others reserved. - */ -#ifdef CONFIG_ARCH_KIRKWOOD -#define SAR_KIRKWOOD_CPU_FREQ(x) \ - (((x & (1 << 1)) >> 1) | \ - ((x & (1 << 22)) >> 21) | \ - ((x & (3 << 3)) >> 1)) -#define SAR_KIRKWOOD_L2_RATIO(x) \ - (((x & (3 << 9)) >> 9) | \ - (((x & (1 << 19)) >> 17))) -#define SAR_KIRKWOOD_DDR_RATIO 5 -#define SAR_KIRKWOOD_DDR_RATIO_MASK 0xf -#define SAR_MV88F6180_CLK 2 -#define SAR_MV88F6180_CLK_MASK 0x7 -#define SAR_KIRKWOOD_TCLK_FREQ 21 -#define SAR_KIRKWOOD_TCLK_FREQ_MASK 0x1 - -enum { KIRKWOOD_CPU_TO_L2, KIRKWOOD_CPU_TO_DDR }; - -static const struct core_ratio __initconst kirkwood_core_ratios[] = { - { .id = KIRKWOOD_CPU_TO_L2, .name = "l2clk", }, - { .id = KIRKWOOD_CPU_TO_DDR, .name = "ddrclk", } -}; - -static u32 __init kirkwood_get_tclk_freq(void __iomem *sar) -{ - u32 opt = (readl(sar) >> SAR_KIRKWOOD_TCLK_FREQ) & - SAR_KIRKWOOD_TCLK_FREQ_MASK; - return (opt) ? 166666667 : 200000000; -} - -static const u32 __initconst kirkwood_cpu_frequencies[] = { - 0, 0, 0, 0, - 600000000, - 0, - 800000000, - 1000000000, - 0, - 1200000000, - 0, 0, - 1500000000, - 1600000000, - 1800000000, - 2000000000 -}; - -static u32 __init kirkwood_get_cpu_freq(void __iomem *sar) -{ - u32 opt = SAR_KIRKWOOD_CPU_FREQ(readl(sar)); - return kirkwood_cpu_frequencies[opt]; -} - -static const int __initconst kirkwood_cpu_l2_ratios[8][2] = { - { 0, 1 }, { 1, 2 }, { 0, 1 }, { 1, 3 }, - { 0, 1 }, { 1, 4 }, { 0, 1 }, { 0, 1 } -}; - -static const int __initconst kirkwood_cpu_ddr_ratios[16][2] = { - { 0, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, - { 1, 3 }, { 0, 1 }, { 1, 4 }, { 2, 9 }, - { 1, 5 }, { 1, 6 }, { 0, 1 }, { 0, 1 }, - { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 } -}; - -static void __init kirkwood_get_clk_ratio( - void __iomem *sar, int id, int *mult, int *div) -{ - switch (id) { - case KIRKWOOD_CPU_TO_L2: - { - u32 opt = SAR_KIRKWOOD_L2_RATIO(readl(sar)); - *mult = kirkwood_cpu_l2_ratios[opt][0]; - *div = kirkwood_cpu_l2_ratios[opt][1]; - break; - } - case KIRKWOOD_CPU_TO_DDR: - { - u32 opt = (readl(sar) >> SAR_KIRKWOOD_DDR_RATIO) & - SAR_KIRKWOOD_DDR_RATIO_MASK; - *mult = kirkwood_cpu_ddr_ratios[opt][0]; - *div = kirkwood_cpu_ddr_ratios[opt][1]; - break; - } - } -} - -static const struct core_clocks kirkwood_core_clocks = { - .get_tclk_freq = kirkwood_get_tclk_freq, - .get_cpu_freq = kirkwood_get_cpu_freq, - .get_clk_ratio = kirkwood_get_clk_ratio, - .ratios = kirkwood_core_ratios, - .num_ratios = ARRAY_SIZE(kirkwood_core_ratios), -}; - -static const u32 __initconst mv88f6180_cpu_frequencies[] = { - 0, 0, 0, 0, 0, - 600000000, - 800000000, - 1000000000 -}; - -static u32 __init mv88f6180_get_cpu_freq(void __iomem *sar) -{ - u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & SAR_MV88F6180_CLK_MASK; - return mv88f6180_cpu_frequencies[opt]; -} - -static const int __initconst mv88f6180_cpu_ddr_ratios[8][2] = { - { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, - { 0, 1 }, { 1, 3 }, { 1, 4 }, { 1, 5 } -}; - -static void __init mv88f6180_get_clk_ratio( - void __iomem *sar, int id, int *mult, int *div) -{ - switch (id) { - case KIRKWOOD_CPU_TO_L2: - { - /* mv88f6180 has a fixed 1:2 CPU-to-L2 ratio */ - *mult = 1; - *div = 2; - break; - } - case KIRKWOOD_CPU_TO_DDR: - { - u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & - SAR_MV88F6180_CLK_MASK; - *mult = mv88f6180_cpu_ddr_ratios[opt][0]; - *div = mv88f6180_cpu_ddr_ratios[opt][1]; - break; - } - } -} - -static const struct core_clocks mv88f6180_core_clocks = { - .get_tclk_freq = kirkwood_get_tclk_freq, - .get_cpu_freq = mv88f6180_get_cpu_freq, - .get_clk_ratio = mv88f6180_get_clk_ratio, - .ratios = kirkwood_core_ratios, - .num_ratios = ARRAY_SIZE(kirkwood_core_ratios), -}; -#endif /* CONFIG_ARCH_KIRKWOOD */ - -static const __initdata struct of_device_id clk_core_match[] = { -#ifdef CONFIG_MACH_ARMADA_370_XP - { - .compatible = "marvell,armada-370-core-clock", - .data = &armada_370_core_clocks, - }, - { - .compatible = "marvell,armada-xp-core-clock", - .data = &armada_xp_core_clocks, - }, -#endif -#ifdef CONFIG_ARCH_DOVE - { - .compatible = "marvell,dove-core-clock", - .data = &dove_core_clocks, - }, -#endif - -#ifdef CONFIG_ARCH_KIRKWOOD - { - .compatible = "marvell,kirkwood-core-clock", - .data = &kirkwood_core_clocks, - }, - { - .compatible = "marvell,mv88f6180-core-clock", - .data = &mv88f6180_core_clocks, - }, -#endif - - { } -}; - -void __init mvebu_core_clk_init(void) -{ - struct device_node *np; - - for_each_matching_node(np, clk_core_match) { - const struct of_device_id *match = - of_match_node(clk_core_match, np); - mvebu_clk_core_setup(np, (struct core_clocks *)match->data); - } -} diff --git a/drivers/clk/mvebu/clk-core.h b/drivers/clk/mvebu/clk-core.h deleted file mode 100644 index 28b5e02e9885..000000000000 --- a/drivers/clk/mvebu/clk-core.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * * Marvell EBU clock core handling defined at reset - * - * Copyright (C) 2012 Marvell - * - * Gregory CLEMENT - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#ifndef __MVEBU_CLK_CORE_H -#define __MVEBU_CLK_CORE_H - -void __init mvebu_core_clk_init(void); - -#endif diff --git a/drivers/clk/mvebu/clk-gating-ctrl.c b/drivers/clk/mvebu/clk-gating-ctrl.c deleted file mode 100644 index 1df6c4e4c200..000000000000 --- a/drivers/clk/mvebu/clk-gating-ctrl.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Marvell MVEBU clock gating control. - * - * Sebastian Hesselbarth - * Andrew Lunn - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct mvebu_gating_ctrl { - spinlock_t lock; - struct clk **gates; - int num_gates; -}; - -struct mvebu_soc_descr { - const char *name; - const char *parent; - int bit_idx; - unsigned long flags; -}; - -#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) - -static struct clk *mvebu_clk_gating_get_src( - struct of_phandle_args *clkspec, void *data) -{ - struct mvebu_gating_ctrl *ctrl = (struct mvebu_gating_ctrl *)data; - int n; - - if (clkspec->args_count < 1) - return ERR_PTR(-EINVAL); - - for (n = 0; n < ctrl->num_gates; n++) { - struct clk_gate *gate = - to_clk_gate(__clk_get_hw(ctrl->gates[n])); - if (clkspec->args[0] == gate->bit_idx) - return ctrl->gates[n]; - } - return ERR_PTR(-ENODEV); -} - -static void __init mvebu_clk_gating_setup( - struct device_node *np, const struct mvebu_soc_descr *descr) -{ - struct mvebu_gating_ctrl *ctrl; - struct clk *clk; - void __iomem *base; - const char *default_parent = NULL; - int n; - - base = of_iomap(np, 0); - - clk = of_clk_get(np, 0); - if (!IS_ERR(clk)) { - default_parent = __clk_get_name(clk); - clk_put(clk); - } - - ctrl = kzalloc(sizeof(struct mvebu_gating_ctrl), GFP_KERNEL); - if (WARN_ON(!ctrl)) - return; - - spin_lock_init(&ctrl->lock); - - /* - * Count, allocate, and register clock gates - */ - for (n = 0; descr[n].name;) - n++; - - ctrl->num_gates = n; - ctrl->gates = kzalloc(ctrl->num_gates * sizeof(struct clk *), - GFP_KERNEL); - if (WARN_ON(!ctrl->gates)) { - kfree(ctrl); - return; - } - - for (n = 0; n < ctrl->num_gates; n++) { - const char *parent = - (descr[n].parent) ? descr[n].parent : default_parent; - ctrl->gates[n] = clk_register_gate(NULL, descr[n].name, parent, - descr[n].flags, base, descr[n].bit_idx, - 0, &ctrl->lock); - WARN_ON(IS_ERR(ctrl->gates[n])); - } - of_clk_add_provider(np, mvebu_clk_gating_get_src, ctrl); -} - -/* - * SoC specific clock gating control - */ - -#ifdef CONFIG_MACH_ARMADA_370 -static const struct mvebu_soc_descr __initconst armada_370_gating_descr[] = { - { "audio", NULL, 0, 0 }, - { "pex0_en", NULL, 1, 0 }, - { "pex1_en", NULL, 2, 0 }, - { "ge1", NULL, 3, 0 }, - { "ge0", NULL, 4, 0 }, - { "pex0", "pex0_en", 5, 0 }, - { "pex1", "pex1_en", 9, 0 }, - { "sata0", NULL, 15, 0 }, - { "sdio", NULL, 17, 0 }, - { "tdm", NULL, 25, 0 }, - { "ddr", NULL, 28, CLK_IGNORE_UNUSED }, - { "sata1", NULL, 30, 0 }, - { } -}; -#endif - -#ifdef CONFIG_MACH_ARMADA_XP -static const struct mvebu_soc_descr __initconst armada_xp_gating_descr[] = { - { "audio", NULL, 0, 0 }, - { "ge3", NULL, 1, 0 }, - { "ge2", NULL, 2, 0 }, - { "ge1", NULL, 3, 0 }, - { "ge0", NULL, 4, 0 }, - { "pex00", NULL, 5, 0 }, - { "pex01", NULL, 6, 0 }, - { "pex02", NULL, 7, 0 }, - { "pex03", NULL, 8, 0 }, - { "pex10", NULL, 9, 0 }, - { "pex11", NULL, 10, 0 }, - { "pex12", NULL, 11, 0 }, - { "pex13", NULL, 12, 0 }, - { "bp", NULL, 13, 0 }, - { "sata0lnk", NULL, 14, 0 }, - { "sata0", "sata0lnk", 15, 0 }, - { "lcd", NULL, 16, 0 }, - { "sdio", NULL, 17, 0 }, - { "usb0", NULL, 18, 0 }, - { "usb1", NULL, 19, 0 }, - { "usb2", NULL, 20, 0 }, - { "xor0", NULL, 22, 0 }, - { "crypto", NULL, 23, 0 }, - { "tdm", NULL, 25, 0 }, - { "pex20", NULL, 26, 0 }, - { "pex30", NULL, 27, 0 }, - { "xor1", NULL, 28, 0 }, - { "sata1lnk", NULL, 29, 0 }, - { "sata1", "sata1lnk", 30, 0 }, - { } -}; -#endif - -#ifdef CONFIG_ARCH_DOVE -static const struct mvebu_soc_descr __initconst dove_gating_descr[] = { - { "usb0", NULL, 0, 0 }, - { "usb1", NULL, 1, 0 }, - { "ge", "gephy", 2, 0 }, - { "sata", NULL, 3, 0 }, - { "pex0", NULL, 4, 0 }, - { "pex1", NULL, 5, 0 }, - { "sdio0", NULL, 8, 0 }, - { "sdio1", NULL, 9, 0 }, - { "nand", NULL, 10, 0 }, - { "camera", NULL, 11, 0 }, - { "i2s0", NULL, 12, 0 }, - { "i2s1", NULL, 13, 0 }, - { "crypto", NULL, 15, 0 }, - { "ac97", NULL, 21, 0 }, - { "pdma", NULL, 22, 0 }, - { "xor0", NULL, 23, 0 }, - { "xor1", NULL, 24, 0 }, - { "gephy", NULL, 30, 0 }, - { } -}; -#endif - -#ifdef CONFIG_ARCH_KIRKWOOD -static const struct mvebu_soc_descr __initconst kirkwood_gating_descr[] = { - { "ge0", NULL, 0, 0 }, - { "pex0", NULL, 2, 0 }, - { "usb0", NULL, 3, 0 }, - { "sdio", NULL, 4, 0 }, - { "tsu", NULL, 5, 0 }, - { "runit", NULL, 7, 0 }, - { "xor0", NULL, 8, 0 }, - { "audio", NULL, 9, 0 }, - { "powersave", "cpuclk", 11, 0 }, - { "sata0", NULL, 14, 0 }, - { "sata1", NULL, 15, 0 }, - { "xor1", NULL, 16, 0 }, - { "crypto", NULL, 17, 0 }, - { "pex1", NULL, 18, 0 }, - { "ge1", NULL, 19, 0 }, - { "tdm", NULL, 20, 0 }, - { } -}; -#endif - -static const __initdata struct of_device_id clk_gating_match[] = { -#ifdef CONFIG_MACH_ARMADA_370 - { - .compatible = "marvell,armada-370-gating-clock", - .data = armada_370_gating_descr, - }, -#endif - -#ifdef CONFIG_MACH_ARMADA_XP - { - .compatible = "marvell,armada-xp-gating-clock", - .data = armada_xp_gating_descr, - }, -#endif - -#ifdef CONFIG_ARCH_DOVE - { - .compatible = "marvell,dove-gating-clock", - .data = dove_gating_descr, - }, -#endif - -#ifdef CONFIG_ARCH_KIRKWOOD - { - .compatible = "marvell,kirkwood-gating-clock", - .data = kirkwood_gating_descr, - }, -#endif - - { } -}; - -void __init mvebu_gating_clk_init(void) -{ - struct device_node *np; - - for_each_matching_node(np, clk_gating_match) { - const struct of_device_id *match = - of_match_node(clk_gating_match, np); - mvebu_clk_gating_setup(np, - (const struct mvebu_soc_descr *)match->data); - } -} diff --git a/drivers/clk/mvebu/clk-gating-ctrl.h b/drivers/clk/mvebu/clk-gating-ctrl.h deleted file mode 100644 index 9275d1e51f1b..000000000000 --- a/drivers/clk/mvebu/clk-gating-ctrl.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Marvell EBU gating clock handling - * - * Copyright (C) 2012 Marvell - * - * Thomas Petazzoni - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#ifndef __MVEBU_CLK_GATING_H -#define __MVEBU_CLK_GATING_H - -#ifdef CONFIG_MVEBU_CLK_GATING -void __init mvebu_gating_clk_init(void); -#else -void mvebu_gating_clk_init(void) {} -#endif - -#endif diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c deleted file mode 100644 index 29f10fb3006c..000000000000 --- a/drivers/clk/mvebu/clk.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Marvell EBU SoC clock handling. - * - * Copyright (C) 2012 Marvell - * - * Gregory CLEMENT - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ -#include -#include -#include -#include "clk-core.h" -#include "clk-gating-ctrl.h" - -void __init mvebu_clocks_init(void) -{ - mvebu_core_clk_init(); - mvebu_gating_clk_init(); - of_clk_init(NULL); -} diff --git a/include/linux/clk/mvebu.h b/include/linux/clk/mvebu.h deleted file mode 100644 index 8c4ae713b063..000000000000 --- a/include/linux/clk/mvebu.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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 - */ - -#ifndef __CLK_MVEBU_H_ -#define __CLK_MVEBU_H_ - -void __init mvebu_clocks_init(void); - -#endif -- cgit v1.2.3 From ed69bdd8fd9b2db68b915ce5f60fc51d4744a9b1 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Fri, 13 Jul 2012 15:55:52 +0100 Subject: drivers: bus: add ARM CCI support On ARM multi-cluster systems coherency between cores running on different clusters is managed by the cache-coherent interconnect (CCI). It allows broadcasting of TLB invalidates and memory barriers and it guarantees cache coherency at system level through snooping of slave interfaces connected to it. This patch enables the basic infrastructure required in Linux to handle and programme the CCI component. Non-local variables used by the CCI management functions called by power down function calls after disabling the cache must be flushed out to main memory in advance, otherwise incoherency of those values may occur if they are sitting in the cache of some other CPU when power down functions execute. Driver code ensures that relevant data structures are flushed from inner and outer caches after the driver probe is completed. CCI slave port resources are linked to set of CPUs through bus masters phandle properties that link the interface resources to masters node in the device tree. Documentation describing the CCI DT bindings is provided with the patch. Signed-off-by: Lorenzo Pieralisi Signed-off-by: Nicolas Pitre --- Documentation/devicetree/bindings/arm/cci.txt | 172 +++++++++++ drivers/bus/Kconfig | 7 + drivers/bus/Makefile | 2 + drivers/bus/arm-cci.c | 426 ++++++++++++++++++++++++++ include/linux/arm-cci.h | 61 ++++ 5 files changed, 668 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/cci.txt create mode 100644 drivers/bus/arm-cci.c create mode 100644 include/linux/arm-cci.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/arm/cci.txt b/Documentation/devicetree/bindings/arm/cci.txt new file mode 100644 index 000000000000..92d36e2aa877 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/cci.txt @@ -0,0 +1,172 @@ +======================================================= +ARM CCI cache coherent interconnect binding description +======================================================= + +ARM multi-cluster systems maintain intra-cluster coherency through a +cache coherent interconnect (CCI) that is capable of monitoring bus +transactions and manage coherency, TLB invalidations and memory barriers. + +It allows snooping and distributed virtual memory message broadcast across +clusters, through memory mapped interface, with a global control register +space and multiple sets of interface control registers, one per slave +interface. + +Bindings for the CCI node follow the ePAPR standard, available from: + +www.power.org/documentation/epapr-version-1-1/ + +with the addition of the bindings described in this document which are +specific to ARM. + +* CCI interconnect node + + Description: Describes a CCI cache coherent Interconnect component + + Node name must be "cci". + Node's parent must be the root node /, and the address space visible + through the CCI interconnect is the same as the one seen from the + root node (ie from CPUs perspective as per DT standard). + Every CCI node has to define the following properties: + + - compatible + Usage: required + Value type: + Definition: must be set to + "arm,cci-400" + + - reg + Usage: required + Value type: + Definition: A standard property. Specifies base physical + address of CCI control registers common to all + interfaces. + + - ranges: + Usage: required + Value type: + Definition: A standard property. Follow rules in the ePAPR for + hierarchical bus addressing. CCI interfaces + addresses refer to the parent node addressing + scheme to declare their register bases. + + CCI interconnect node can define the following child nodes: + + - CCI control interface nodes + + Node name must be "slave-if". + Parent node must be CCI interconnect node. + + A CCI control interface node must contain the following + properties: + + - compatible + Usage: required + Value type: + Definition: must be set to + "arm,cci-400-ctrl-if" + + - interface-type: + Usage: required + Value type: + Definition: must be set to one of {"ace", "ace-lite"} + depending on the interface type the node + represents. + + - reg: + Usage: required + Value type: + Definition: the base address and size of the + corresponding interface programming + registers. + +* CCI interconnect bus masters + + Description: masters in the device tree connected to a CCI port + (inclusive of CPUs and their cpu nodes). + + A CCI interconnect bus master node must contain the following + properties: + + - cci-control-port: + Usage: required + Value type: + Definition: a phandle containing the CCI control interface node + the master is connected to. + +Example: + + cpus { + #size-cells = <0>; + #address-cells = <1>; + + CPU0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + cci-control-port = <&cci_control1>; + reg = <0x0>; + }; + + CPU1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + cci-control-port = <&cci_control1>; + reg = <0x1>; + }; + + CPU2: cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + cci-control-port = <&cci_control2>; + reg = <0x100>; + }; + + CPU3: cpu@101 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + cci-control-port = <&cci_control2>; + reg = <0x101>; + }; + + }; + + dma0: dma@3000000 { + compatible = "arm,pl330", "arm,primecell"; + cci-control-port = <&cci_control0>; + reg = <0x0 0x3000000 0x0 0x1000>; + interrupts = <10>; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <32>; + }; + + cci@2c090000 { + compatible = "arm,cci-400"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x2c090000 0 0x1000>; + ranges = <0x0 0x0 0x2c090000 0x6000>; + + cci_control0: slave-if@1000 { + compatible = "arm,cci-400-ctrl-if"; + interface-type = "ace-lite"; + reg = <0x1000 0x1000>; + }; + + cci_control1: slave-if@4000 { + compatible = "arm,cci-400-ctrl-if"; + interface-type = "ace"; + reg = <0x4000 0x1000>; + }; + + cci_control2: slave-if@5000 { + compatible = "arm,cci-400-ctrl-if"; + interface-type = "ace"; + reg = <0x5000 0x1000>; + }; + }; + +This CCI node corresponds to a CCI component whose control registers sits +at address 0x000000002c090000. +CCI slave interface @0x000000002c091000 is connected to dma controller dma0. +CCI slave interface @0x000000002c094000 is connected to CPUs {CPU0, CPU1}; +CCI slave interface @0x000000002c095000 is connected to CPUs {CPU2, CPU3}; diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index b05ecab915c4..5286e2d333b0 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -26,4 +26,11 @@ config OMAP_INTERCONNECT help Driver to enable OMAP interconnect error handling driver. + +config ARM_CCI + bool "ARM CCI driver support" + depends on ARM + help + Driver supporting the CCI cache coherent interconnect for ARM + platforms. endmenu diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 3c7b53c12091..670cea443802 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o # Interconnect bus driver for OMAP SoCs. obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o +# CCI cache coherent interconnect for ARM platforms +obj-$(CONFIG_ARM_CCI) += arm-cci.o diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c new file mode 100644 index 000000000000..ea81fa4a28db --- /dev/null +++ b/drivers/bus/arm-cci.c @@ -0,0 +1,426 @@ +/* + * CCI cache coherent interconnect driver + * + * Copyright (C) 2013 ARM Ltd. + * Author: Lorenzo Pieralisi + * + * 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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define CCI_PORT_CTRL 0x0 +#define CCI_CTRL_STATUS 0xc + +#define CCI_ENABLE_SNOOP_REQ 0x1 +#define CCI_ENABLE_DVM_REQ 0x2 +#define CCI_ENABLE_REQ (CCI_ENABLE_SNOOP_REQ | CCI_ENABLE_DVM_REQ) + +struct cci_nb_ports { + unsigned int nb_ace; + unsigned int nb_ace_lite; +}; + +enum cci_ace_port_type { + ACE_INVALID_PORT = 0x0, + ACE_PORT, + ACE_LITE_PORT, +}; + +struct cci_ace_port { + void __iomem *base; + enum cci_ace_port_type type; + struct device_node *dn; +}; + +static struct cci_ace_port *ports; +static unsigned int nb_cci_ports; + +static void __iomem *cci_ctrl_base; + +struct cpu_port { + u64 mpidr; + u32 port; +}; +/* + * Use the port MSB as valid flag, shift can be made dynamic + * by computing number of bits required for port indexes. + * Code disabling CCI cpu ports runs with D-cache invalidated + * and SCTLR bit clear so data accesses must be kept to a minimum + * to improve performance; for now shift is left static to + * avoid one more data access while disabling the CCI port. + */ +#define PORT_VALID_SHIFT 31 +#define PORT_VALID (0x1 << PORT_VALID_SHIFT) + +static inline void init_cpu_port(struct cpu_port *port, u32 index, u64 mpidr) +{ + port->port = PORT_VALID | index; + port->mpidr = mpidr; +} + +static inline bool cpu_port_is_valid(struct cpu_port *port) +{ + return !!(port->port & PORT_VALID); +} + +static inline bool cpu_port_match(struct cpu_port *port, u64 mpidr) +{ + return port->mpidr == (mpidr & MPIDR_HWID_BITMASK); +} + +static struct cpu_port cpu_port[NR_CPUS]; + +/** + * __cci_ace_get_port - Function to retrieve the port index connected to + * a cpu or device. + * + * @dn: device node of the device to look-up + * @type: port type + * + * Return value: + * - CCI port index if success + * - -ENODEV if failure + */ +static int __cci_ace_get_port(struct device_node *dn, int type) +{ + int i; + bool ace_match; + struct device_node *cci_portn; + + cci_portn = of_parse_phandle(dn, "cci-control-port", 0); + for (i = 0; i < nb_cci_ports; i++) { + ace_match = ports[i].type == type; + if (ace_match && cci_portn == ports[i].dn) + return i; + } + return -ENODEV; +} + +int cci_ace_get_port(struct device_node *dn) +{ + return __cci_ace_get_port(dn, ACE_LITE_PORT); +} +EXPORT_SYMBOL_GPL(cci_ace_get_port); + +static void __init cci_ace_init_ports(void) +{ + int port, ac, cpu; + u64 hwid; + const u32 *cell; + struct device_node *cpun, *cpus; + + cpus = of_find_node_by_path("/cpus"); + if (WARN(!cpus, "Missing cpus node, bailing out\n")) + return; + + if (WARN_ON(of_property_read_u32(cpus, "#address-cells", &ac))) + ac = of_n_addr_cells(cpus); + + /* + * Port index look-up speeds up the function disabling ports by CPU, + * since the logical to port index mapping is done once and does + * not change after system boot. + * The stashed index array is initialized for all possible CPUs + * at probe time. + */ + for_each_child_of_node(cpus, cpun) { + if (of_node_cmp(cpun->type, "cpu")) + continue; + cell = of_get_property(cpun, "reg", NULL); + if (WARN(!cell, "%s: missing reg property\n", cpun->full_name)) + continue; + + hwid = of_read_number(cell, ac); + cpu = get_logical_index(hwid & MPIDR_HWID_BITMASK); + + if (cpu < 0 || !cpu_possible(cpu)) + continue; + port = __cci_ace_get_port(cpun, ACE_PORT); + if (port < 0) + continue; + + init_cpu_port(&cpu_port[cpu], port, cpu_logical_map(cpu)); + } + + for_each_possible_cpu(cpu) { + WARN(!cpu_port_is_valid(&cpu_port[cpu]), + "CPU %u does not have an associated CCI port\n", + cpu); + } +} +/* + * Functions to enable/disable a CCI interconnect slave port + * + * They are called by low-level power management code to disable slave + * interfaces snoops and DVM broadcast. + * Since they may execute with cache data allocation disabled and + * after the caches have been cleaned and invalidated the functions provide + * no explicit locking since they may run with D-cache disabled, so normal + * cacheable kernel locks based on ldrex/strex may not work. + * Locking has to be provided by BSP implementations to ensure proper + * operations. + */ + +/** + * cci_port_control() - function to control a CCI port + * + * @port: index of the port to setup + * @enable: if true enables the port, if false disables it + */ +static void notrace cci_port_control(unsigned int port, bool enable) +{ + void __iomem *base = ports[port].base; + + writel_relaxed(enable ? CCI_ENABLE_REQ : 0, base + CCI_PORT_CTRL); + /* + * This function is called from power down procedures + * and must not execute any instruction that might + * cause the processor to be put in a quiescent state + * (eg wfi). Hence, cpu_relax() can not be added to this + * read loop to optimize power, since it might hide possibly + * disruptive operations. + */ + while (readl_relaxed(cci_ctrl_base + CCI_CTRL_STATUS) & 0x1) + ; +} + +/** + * cci_disable_port_by_cpu() - function to disable a CCI port by CPU + * reference + * + * @mpidr: mpidr of the CPU whose CCI port should be disabled + * + * Disabling a CCI port for a CPU implies disabling the CCI port + * controlling that CPU cluster. Code disabling CPU CCI ports + * must make sure that the CPU running the code is the last active CPU + * in the cluster ie all other CPUs are quiescent in a low power state. + * + * Return: + * 0 on success + * -ENODEV on port look-up failure + */ +int notrace cci_disable_port_by_cpu(u64 mpidr) +{ + int cpu; + bool is_valid; + for (cpu = 0; cpu < nr_cpu_ids; cpu++) { + is_valid = cpu_port_is_valid(&cpu_port[cpu]); + if (is_valid && cpu_port_match(&cpu_port[cpu], mpidr)) { + cci_port_control(cpu_port[cpu].port, false); + return 0; + } + } + return -ENODEV; +} +EXPORT_SYMBOL_GPL(cci_disable_port_by_cpu); + +/** + * __cci_control_port_by_device() - function to control a CCI port by device + * reference + * + * @dn: device node pointer of the device whose CCI port should be + * controlled + * @enable: if true enables the port, if false disables it + * + * Return: + * 0 on success + * -ENODEV on port look-up failure + */ +int notrace __cci_control_port_by_device(struct device_node *dn, bool enable) +{ + int port; + + if (!dn) + return -ENODEV; + + port = __cci_ace_get_port(dn, ACE_LITE_PORT); + if (WARN_ONCE(port < 0, "node %s ACE lite port look-up failure\n", + dn->full_name)) + return -ENODEV; + cci_port_control(port, enable); + return 0; +} +EXPORT_SYMBOL_GPL(__cci_control_port_by_device); + +/** + * __cci_control_port_by_index() - function to control a CCI port by port index + * + * @port: port index previously retrieved with cci_ace_get_port() + * @enable: if true enables the port, if false disables it + * + * Return: + * 0 on success + * -ENODEV on port index out of range + * -EPERM if operation carried out on an ACE PORT + */ +int notrace __cci_control_port_by_index(u32 port, bool enable) +{ + if (port >= nb_cci_ports || ports[port].type == ACE_INVALID_PORT) + return -ENODEV; + /* + * CCI control for ports connected to CPUS is extremely fragile + * and must be made to go through a specific and controlled + * interface (ie cci_disable_port_by_cpu(); control by general purpose + * indexing is therefore disabled for ACE ports. + */ + if (ports[port].type == ACE_PORT) + return -EPERM; + + cci_port_control(port, enable); + return 0; +} +EXPORT_SYMBOL_GPL(__cci_control_port_by_index); + +static const struct cci_nb_ports cci400_ports = { + .nb_ace = 2, + .nb_ace_lite = 3 +}; + +static const struct of_device_id arm_cci_matches[] = { + {.compatible = "arm,cci-400", .data = &cci400_ports }, + {}, +}; + +static const struct of_device_id arm_cci_ctrl_if_matches[] = { + {.compatible = "arm,cci-400-ctrl-if", }, + {}, +}; + +static int __init cci_probe(void) +{ + struct cci_nb_ports const *cci_config; + int ret, i, nb_ace = 0, nb_ace_lite = 0; + struct device_node *np, *cp; + const char *match_str; + bool is_ace; + + np = of_find_matching_node(NULL, arm_cci_matches); + if (!np) + return -ENODEV; + + cci_config = of_match_node(arm_cci_matches, np)->data; + if (!cci_config) + return -ENODEV; + + nb_cci_ports = cci_config->nb_ace + cci_config->nb_ace_lite; + + ports = kcalloc(sizeof(*ports), nb_cci_ports, GFP_KERNEL); + if (!ports) + return -ENOMEM; + + cci_ctrl_base = of_iomap(np, 0); + + if (!cci_ctrl_base) { + WARN(1, "unable to ioremap CCI ctrl\n"); + ret = -ENXIO; + goto memalloc_err; + } + + for_each_child_of_node(np, cp) { + if (!of_match_node(arm_cci_ctrl_if_matches, cp)) + continue; + + i = nb_ace + nb_ace_lite; + + if (i >= nb_cci_ports) + break; + + if (of_property_read_string(cp, "interface-type", + &match_str)) { + WARN(1, "node %s missing interface-type property\n", + cp->full_name); + continue; + } + is_ace = strcmp(match_str, "ace") == 0; + if (!is_ace && strcmp(match_str, "ace-lite")) { + WARN(1, "node %s containing invalid interface-type property, skipping it\n", + cp->full_name); + continue; + } + + ports[i].base = of_iomap(cp, 0); + + if (!ports[i].base) { + WARN(1, "unable to ioremap CCI port %d\n", i); + continue; + } + + if (is_ace) { + if (WARN_ON(nb_ace >= cci_config->nb_ace)) + continue; + ports[i].type = ACE_PORT; + ++nb_ace; + } else { + if (WARN_ON(nb_ace_lite >= cci_config->nb_ace_lite)) + continue; + ports[i].type = ACE_LITE_PORT; + ++nb_ace_lite; + } + ports[i].dn = cp; + } + + /* initialize a stashed array of ACE ports to speed-up look-up */ + cci_ace_init_ports(); + + /* + * Multi-cluster systems may need this data when non-coherent, during + * cluster power-up/power-down. Make sure it reaches main memory. + */ + sync_cache_w(&cci_ctrl_base); + sync_cache_w(&ports); + sync_cache_w(&cpu_port); + __sync_cache_range_w(ports, sizeof(*ports) * nb_cci_ports); + pr_info("ARM CCI driver probed\n"); + return 0; + +memalloc_err: + + kfree(ports); + return ret; +} + +static int cci_init_status = -EAGAIN; +static DEFINE_MUTEX(cci_probing); + +static int __init cci_init(void) +{ + if (cci_init_status != -EAGAIN) + return cci_init_status; + + mutex_lock(&cci_probing); + if (cci_init_status == -EAGAIN) + cci_init_status = cci_probe(); + mutex_unlock(&cci_probing); + return cci_init_status; +} + +/* + * To sort out early init calls ordering a helper function is provided to + * check if the CCI driver has beed initialized. Function check if the driver + * has been initialized, if not it calls the init function that probes + * the driver and updates the return value. + */ +bool __init cci_probed(void) +{ + return cci_init() == 0; +} +EXPORT_SYMBOL_GPL(cci_probed); + +early_initcall(cci_init); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ARM CCI support"); diff --git a/include/linux/arm-cci.h b/include/linux/arm-cci.h new file mode 100644 index 000000000000..79d6edf446d5 --- /dev/null +++ b/include/linux/arm-cci.h @@ -0,0 +1,61 @@ +/* + * CCI cache coherent interconnect support + * + * Copyright (C) 2013 ARM Ltd. + * + * 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 + */ + +#ifndef __LINUX_ARM_CCI_H +#define __LINUX_ARM_CCI_H + +#include +#include + +struct device_node; + +#ifdef CONFIG_ARM_CCI +extern bool cci_probed(void); +extern int cci_ace_get_port(struct device_node *dn); +extern int cci_disable_port_by_cpu(u64 mpidr); +extern int __cci_control_port_by_device(struct device_node *dn, bool enable); +extern int __cci_control_port_by_index(u32 port, bool enable); +#else +static inline bool cci_probed(void) { return false; } +static inline int cci_ace_get_port(struct device_node *dn) +{ + return -ENODEV; +} +static inline int cci_disable_port_by_cpu(u64 mpidr) { return -ENODEV; } +static inline int __cci_control_port_by_device(struct device_node *dn, + bool enable) +{ + return -ENODEV; +} +static inline int __cci_control_port_by_index(u32 port, bool enable) +{ + return -ENODEV; +} +#endif +#define cci_disable_port_by_device(dev) \ + __cci_control_port_by_device(dev, false) +#define cci_enable_port_by_device(dev) \ + __cci_control_port_by_device(dev, true) +#define cci_disable_port_by_index(dev) \ + __cci_control_port_by_index(dev, false) +#define cci_enable_port_by_index(dev) \ + __cci_control_port_by_index(dev, true) + +#endif -- cgit v1.2.3 From 36cb0066ffc55fd326c8a21ffe80aaa5bd16021b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 10 May 2013 16:48:36 +0200 Subject: gpio-rcar: Make the platform data gpio_base field signed The gpio_base field is used to specify the desired GPIO base for the GPIO controller. The GPIO core can automatically allocate a GPIO number range when the base is set to -1. To make this possible, make the field signed. Signed-off-by: Laurent Pinchart Acked-by: Linus Walleij Signed-off-by: Simon Horman --- include/linux/platform_data/gpio-rcar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/platform_data/gpio-rcar.h b/include/linux/platform_data/gpio-rcar.h index b253f77a7ddf..aba7079ccc95 100644 --- a/include/linux/platform_data/gpio-rcar.h +++ b/include/linux/platform_data/gpio-rcar.h @@ -17,7 +17,7 @@ #define __GPIO_RCAR_H__ struct gpio_rcar_config { - unsigned int gpio_base; + int gpio_base; unsigned int irq_base; unsigned int number_of_pins; const char *pctl_name; -- cgit v1.2.3 From 7e1092b5a264c484001b0cdd1f49bea7884e3366 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 24 May 2013 18:47:24 +0900 Subject: gpio-rcar: Add support for IRQ_TYPE_EDGE_BOTH As hardware support for this feature is not universal for all SoCs a flag, has_both_edge_trigger, has been added to the platform data of the driver to allow this feature to be enabled. The motivation for this is to allow use of the gpio-keys driver on the lager board which is based on the r8a7790 SoC. The V2 of this patch has been fully exercised using that driver on that board. Signed-off-by: Magnus Damm Signed-off-by: Simon Horman --- drivers/gpio/gpio-rcar.c | 26 +++++++++++++++++++++----- include/linux/platform_data/gpio-rcar.h | 1 + 2 files changed, 22 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 0f3d6473bf89..d173d56dbb8c 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -49,6 +49,7 @@ struct gpio_rcar_priv { #define POSNEG 0x20 #define EDGLEVEL 0x24 #define FILONOFF 0x28 +#define BOTHEDGE 0x4c static inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, int offs) { @@ -91,7 +92,8 @@ static void gpio_rcar_irq_enable(struct irq_data *d) static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p, unsigned int hwirq, bool active_high_rising_edge, - bool level_trigger) + bool level_trigger, + bool both) { unsigned long flags; @@ -108,6 +110,10 @@ static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p, /* Configure edge or level trigger in EDGLEVEL */ gpio_rcar_modify_bit(p, EDGLEVEL, hwirq, !level_trigger); + /* Select one edge or both edges in BOTHEDGE */ + if (p->config.has_both_edge_trigger) + gpio_rcar_modify_bit(p, BOTHEDGE, hwirq, both); + /* Select "Interrupt Input Mode" in IOINTSEL */ gpio_rcar_modify_bit(p, IOINTSEL, hwirq, true); @@ -127,16 +133,26 @@ static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type) switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_LEVEL_HIGH: - gpio_rcar_config_interrupt_input_mode(p, hwirq, true, true); + gpio_rcar_config_interrupt_input_mode(p, hwirq, true, true, + false); break; case IRQ_TYPE_LEVEL_LOW: - gpio_rcar_config_interrupt_input_mode(p, hwirq, false, true); + gpio_rcar_config_interrupt_input_mode(p, hwirq, false, true, + false); break; case IRQ_TYPE_EDGE_RISING: - gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false); + gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false, + false); break; case IRQ_TYPE_EDGE_FALLING: - gpio_rcar_config_interrupt_input_mode(p, hwirq, false, false); + gpio_rcar_config_interrupt_input_mode(p, hwirq, false, false, + false); + break; + case IRQ_TYPE_EDGE_BOTH: + if (!p->config.has_both_edge_trigger) + return -EINVAL; + gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false, + true); break; default: return -EINVAL; diff --git a/include/linux/platform_data/gpio-rcar.h b/include/linux/platform_data/gpio-rcar.h index aba7079ccc95..6c0027a3c370 100644 --- a/include/linux/platform_data/gpio-rcar.h +++ b/include/linux/platform_data/gpio-rcar.h @@ -21,6 +21,7 @@ struct gpio_rcar_config { unsigned int irq_base; unsigned int number_of_pins; const char *pctl_name; + unsigned has_both_edge_trigger:1; }; #endif /* __GPIO_RCAR_H__ */ -- cgit v1.2.3 From 11df28ab76cd9e98e3e0bbbff8648d0a02509507 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 8 Apr 2013 11:36:13 +0200 Subject: gpio-rcar: Add RCAR_GP_PIN macro Pins are numbered in the R-Car family documentation using a bank number and a pin number in the bank. As the Linux pin number space is linear, we need to flatten this by multiplying the bank number by 32 and adding the pin number. The resulting number bear no directly visible relationship to the documentation, making it error-prone. Add a RCAR_GP_PIN macro to convert from the documentation pin number space to the linear Linux space. Signed-off-by: Laurent Pinchart [horms+renesas@verge.net.au: non-trivial rebase on top of "sh-pfc: r8a7779: Don't group USB OVC and PENC pins"] Signed-off-by: Simon Horman --- include/linux/platform_data/gpio-rcar.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/platform_data/gpio-rcar.h b/include/linux/platform_data/gpio-rcar.h index 6c0027a3c370..2d8d69432813 100644 --- a/include/linux/platform_data/gpio-rcar.h +++ b/include/linux/platform_data/gpio-rcar.h @@ -24,4 +24,6 @@ struct gpio_rcar_config { unsigned has_both_edge_trigger:1; }; +#define RCAR_GP_PIN(bank, pin) (((bank) * 32) + (pin)) + #endif /* __GPIO_RCAR_H__ */ -- cgit v1.2.3 From 743fcce0a89e04dc511b4ea40eba8e3f7cec92d4 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sun, 2 Jun 2013 01:33:56 +0400 Subject: ehci-platform: add pre_setup() method to platform data Sometimes there is a need to initialize some non-standard registers mapped to the EHCI region before accessing the standard EHCI registers. Add pre_setup() method with 'struct usb_hcd *' parameter to be called just before ehci_setup() to the 'ehci-platform' driver's platform data for this purpose... While at it, add the missing incomplete declaration of 'struct platform_device' to ... The patch has been tested on the Marzen and BOCK-W boards. Suggested-by: Alan Stern Signed-off-by: Sergei Shtylyov Acked-by: Kuninori Morimoto Acked-by: Alan Stern Signed-off-by: Simon Horman --- drivers/usb/host/ehci-platform.c | 6 ++++++ include/linux/usb/ehci_pdriver.h | 4 ++++ 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index f47f2594c9d4..d1f5cea435aa 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -48,6 +48,12 @@ static int ehci_platform_reset(struct usb_hcd *hcd) ehci->big_endian_desc = pdata->big_endian_desc; ehci->big_endian_mmio = pdata->big_endian_mmio; + if (pdata->pre_setup) { + retval = pdata->pre_setup(hcd); + if (retval < 0) + return retval; + } + ehci->caps = hcd->regs + pdata->caps_offset; retval = ehci_setup(hcd); if (retval) diff --git a/include/linux/usb/ehci_pdriver.h b/include/linux/usb/ehci_pdriver.h index 99238b096f7e..7eb4dcd0d386 100644 --- a/include/linux/usb/ehci_pdriver.h +++ b/include/linux/usb/ehci_pdriver.h @@ -19,6 +19,9 @@ #ifndef __USB_CORE_EHCI_PDRIVER_H #define __USB_CORE_EHCI_PDRIVER_H +struct platform_device; +struct usb_hcd; + /** * struct usb_ehci_pdata - platform_data for generic ehci driver * @@ -50,6 +53,7 @@ struct usb_ehci_pdata { /* Turn on only VBUS suspend power and hotplug detection, * turn off everything else */ void (*power_suspend)(struct platform_device *pdev); + int (*pre_setup)(struct usb_hcd *hcd); }; #endif /* __USB_CORE_EHCI_PDRIVER_H */ -- cgit v1.2.3 From 6a82e2a83e568dc121326b4bab24035ce7a2f50e Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sun, 2 Jun 2013 01:52:28 +0400 Subject: phy-rcar-usb: add platform data Currently the driver hard-codes USBPCTRL0 register to 0. It is wrong since this register contains board-specific USB ports configuration and so its value should be somehow passed via the platform data. Add the global header file containing 'struct rcar_phy_platform_data' consisting of the various bit fields describing USB ports' pin configuration. Signed-off-by: Sergei Shtylyov Acked-by: Kuninori Morimoto Acked-by: Felipe Balbi Signed-off-by: Simon Horman --- include/linux/platform_data/usb-rcar-phy.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 include/linux/platform_data/usb-rcar-phy.h (limited to 'include') diff --git a/include/linux/platform_data/usb-rcar-phy.h b/include/linux/platform_data/usb-rcar-phy.h new file mode 100644 index 000000000000..c49f35ab14c7 --- /dev/null +++ b/include/linux/platform_data/usb-rcar-phy.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc. + * + * 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. + */ + +#ifndef __USB_RCAR_PHY_H +#define __USB_RCAR_PHY_H + +#include + +struct rcar_phy_platform_data { + bool port1_func:1; /* true: port 1 used by function, false: host */ + unsigned penc1:1; /* Output of the PENC1 pin in function mode */ + struct { /* Overcurrent pin control for ports 0..2 */ + bool select_3_3v:1; /* true: USB_OVCn pin, false: OVCn pin */ + /* Set to false on port 1 in function mode */ + bool active_high:1; /* true: active high, false: active low */ + /* Set to true on port 1 in function mode */ + } ovc_pin[3]; +}; + +#endif /* __USB_RCAR_PHY_H */ -- cgit v1.2.3 From 54407f190c8d542572a9547ba5460d811810b6e4 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sun, 9 Jun 2013 00:34:36 +0400 Subject: phy-rcar-usb: add R8A7778 support The driver currently only supports R8A7779 SoC. Compared to it, R8A7778 USB-PHY has extra register range containing two high-speed signal quality characteristic control registers which should be set up during USB-PHY startup depending on whether a ferrite bead is in use or not. So, we now handle an optional second memory range in the driver's probe method, add the 'ferrite_bead' field to the driver's platform data, and add an extra (optional) step to the USB-PHY startup routine which sets up the extended registers. Also mark in the driver's Kconfig section that R8A7778 is now supported and generally clarify that section, uppercasing the word "phy" and also changing the module name that got lost in the big driver rename, while at it... The patch has been tested on the Marzen and BOCK-W boards. Signed-off-by: Sergei Shtylyov Acked-by: Felipe Balbi Signed-off-by: Simon Horman --- drivers/usb/phy/Kconfig | 10 ++++---- drivers/usb/phy/phy-rcar-usb.c | 37 +++++++++++++++++++++++++----- include/linux/platform_data/usb-rcar-phy.h | 4 +++- 3 files changed, 39 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 371d0e74e909..ac0b98f78c70 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -181,15 +181,15 @@ config USB_MXS_PHY MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x. config USB_RCAR_PHY - tristate "Renesas R-Car USB phy support" + tristate "Renesas R-Car USB PHY support" depends on USB || USB_GADGET help - Say Y here to add support for the Renesas R-Car USB phy driver. - This chip is typically used as USB phy for USB host, gadget. - This driver supports: R8A7779 + Say Y here to add support for the Renesas R-Car USB common PHY driver. + This chip is typically used as USB PHY for USB host, gadget. + This driver supports R8A7778 and R8A7779. To compile this driver as a module, choose M here: the - module will be called rcar-phy. + module will be called phy-rcar-usb. config USB_ULPI bool "Generic ULPI Transceiver Driver" diff --git a/drivers/usb/phy/phy-rcar-usb.c b/drivers/usb/phy/phy-rcar-usb.c index 823b2bb807d2..ae909408958d 100644 --- a/drivers/usb/phy/phy-rcar-usb.c +++ b/drivers/usb/phy/phy-rcar-usb.c @@ -26,15 +26,21 @@ #define USBOH0 0x1C #define USBCTL0 0x58 +/* High-speed signal quality characteristic control registers (R8A7778 only) */ +#define HSQCTL1 0x24 +#define HSQCTL2 0x28 + /* USBPCTRL0 */ -#define OVC2 (1 << 10) /* Switches the OVC input pin for port 2: */ +#define OVC2 (1 << 10) /* (R8A7779 only) */ + /* Switches the OVC input pin for port 2: */ /* 1: USB_OVC2, 0: OVC2 */ #define OVC1_VBUS1 (1 << 9) /* Switches the OVC input pin for port 1: */ /* 1: USB_OVC1, 0: OVC1/VBUS1 */ /* Function mode: set to 0 */ #define OVC0 (1 << 8) /* Switches the OVC input pin for port 0: */ /* 1: USB_OVC0 pin, 0: OVC0 */ -#define OVC2_ACT (1 << 6) /* Host mode: OVC2 polarity: */ +#define OVC2_ACT (1 << 6) /* (R8A7779 only) */ + /* Host mode: OVC2 polarity: */ /* 1: active-high, 0: active-low */ #define PENC (1 << 4) /* Function mode: output level of PENC1 pin: */ /* 1: high, 0: low */ @@ -59,6 +65,7 @@ struct rcar_usb_phy_priv { spinlock_t lock; void __iomem *reg0; + void __iomem *reg1; int counter; }; @@ -78,6 +85,7 @@ static int rcar_usb_phy_init(struct usb_phy *phy) struct device *dev = phy->dev; struct rcar_phy_platform_data *pdata = dev->platform_data; void __iomem *reg0 = priv->reg0; + void __iomem *reg1 = priv->reg1; static const u8 ovcn_act[] = { OVC0_ACT, OVC1_ACT, OVC2_ACT }; int i; u32 val; @@ -96,7 +104,16 @@ static int rcar_usb_phy_init(struct usb_phy *phy) /* (2) start USB-PHY internal PLL */ iowrite32(PHY_ENB | PLL_ENB, (reg0 + USBPCTRL1)); - /* (3) USB module status check */ + /* (3) set USB-PHY in accord with the conditions of usage */ + if (reg1) { + u32 hsqctl1 = pdata->ferrite_bead ? 0x41 : 0; + u32 hsqctl2 = pdata->ferrite_bead ? 0x0d : 7; + + iowrite32(hsqctl1, reg1 + HSQCTL1); + iowrite32(hsqctl2, reg1 + HSQCTL2); + } + + /* (4) USB module status check */ for (i = 0; i < 1024; i++) { udelay(10); val = ioread32(reg0 + USBST); @@ -109,7 +126,7 @@ static int rcar_usb_phy_init(struct usb_phy *phy) goto phy_init_end; } - /* (4) USB-PHY reset clear */ + /* (5) USB-PHY reset clear */ iowrite32(PHY_ENB | PLL_ENB | PHY_RST, (reg0 + USBPCTRL1)); /* Board specific port settings */ @@ -162,9 +179,9 @@ static void rcar_usb_phy_shutdown(struct usb_phy *phy) static int rcar_usb_phy_probe(struct platform_device *pdev) { struct rcar_usb_phy_priv *priv; - struct resource *res0; + struct resource *res0, *res1; struct device *dev = &pdev->dev; - void __iomem *reg0; + void __iomem *reg0, *reg1 = NULL; int ret; if (!pdev->dev.platform_data) { @@ -182,6 +199,13 @@ static int rcar_usb_phy_probe(struct platform_device *pdev) if (IS_ERR(reg0)) return PTR_ERR(reg0); + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res1) { + reg1 = devm_ioremap_resource(dev, res1); + if (IS_ERR(reg1)) + return PTR_ERR(reg1); + } + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(dev, "priv data allocation error\n"); @@ -189,6 +213,7 @@ static int rcar_usb_phy_probe(struct platform_device *pdev) } priv->reg0 = reg0; + priv->reg1 = reg1; priv->counter = 0; priv->phy.dev = dev; priv->phy.label = dev_name(dev); diff --git a/include/linux/platform_data/usb-rcar-phy.h b/include/linux/platform_data/usb-rcar-phy.h index c49f35ab14c7..8ec6964a32a5 100644 --- a/include/linux/platform_data/usb-rcar-phy.h +++ b/include/linux/platform_data/usb-rcar-phy.h @@ -13,6 +13,8 @@ #include struct rcar_phy_platform_data { + bool ferrite_bead:1; /* (R8A7778 only) */ + bool port1_func:1; /* true: port 1 used by function, false: host */ unsigned penc1:1; /* Output of the PENC1 pin in function mode */ struct { /* Overcurrent pin control for ports 0..2 */ @@ -20,7 +22,7 @@ struct rcar_phy_platform_data { /* Set to false on port 1 in function mode */ bool active_high:1; /* true: active high, false: active low */ /* Set to true on port 1 in function mode */ - } ovc_pin[3]; + } ovc_pin[3]; /* (R8A7778 only has 2 ports) */ }; #endif /* __USB_RCAR_PHY_H */ -- cgit v1.2.3 From 6597619f9c85a0782d427d6723c96cf768e99086 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 13 May 2013 21:07:36 +0400 Subject: ARM: clps711x: Add support for SYSCON driver This patch adds support for SYSCON driver for CLPS711X targets. At this time there are no users for this driver, but it is will be used as start point to use in CLPS711X drivers and remove dependencies. Signed-off-by: Alexander Shiyan Signed-off-by: Olof Johansson --- arch/arm/Kconfig | 1 + arch/arm/mach-clps711x/devices.c | 20 ++++++ arch/arm/mach-clps711x/include/mach/clps711x.h | 88 +----------------------- drivers/mfd/syscon.c | 3 + include/linux/mfd/syscon/clps711x.h | 94 ++++++++++++++++++++++++++ 5 files changed, 120 insertions(+), 86 deletions(-) create mode 100644 include/linux/mfd/syscon/clps711x.h (limited to 'include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index fc93deb1baaf..4217db7fa883 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -370,6 +370,7 @@ config ARCH_CLPS711X select COMMON_CLK select CPU_ARM720T select GENERIC_CLOCKEVENTS + select MFD_SYSCON select MULTI_IRQ_HANDLER select SPARSE_IRQ help diff --git a/arch/arm/mach-clps711x/devices.c b/arch/arm/mach-clps711x/devices.c index 21161ed4c0c4..856b81cf2f8a 100644 --- a/arch/arm/mach-clps711x/devices.c +++ b/arch/arm/mach-clps711x/devices.c @@ -10,6 +10,7 @@ */ #include +#include #include @@ -42,7 +43,26 @@ static void __init clps711x_add_gpio(void) } } +const struct resource clps711x_syscon_res[] __initconst = { + /* SYSCON1, SYSFLG1 */ + DEFINE_RES_MEM(CLPS711X_PHYS_BASE + SYSCON1, SZ_128), + /* SYSCON2, SYSFLG2 */ + DEFINE_RES_MEM(CLPS711X_PHYS_BASE + SYSCON2, SZ_128), + /* SYSCON3 */ + DEFINE_RES_MEM(CLPS711X_PHYS_BASE + SYSCON3, SZ_64), +}; + +static void __init clps711x_add_syscon(void) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(clps711x_syscon_res); i++) + platform_device_register_simple("clps711x-syscon", i + 1, + &clps711x_syscon_res[i], 1); +} + void __init clps711x_devices_init(void) { clps711x_add_gpio(); + clps711x_add_syscon(); } diff --git a/arch/arm/mach-clps711x/include/mach/clps711x.h b/arch/arm/mach-clps711x/include/mach/clps711x.h index 01d1b9559710..0286f4bf9945 100644 --- a/arch/arm/mach-clps711x/include/mach/clps711x.h +++ b/arch/arm/mach-clps711x/include/mach/clps711x.h @@ -21,6 +21,8 @@ #ifndef __MACH_CLPS711X_H #define __MACH_CLPS711X_H +#include + #define CLPS711X_PHYS_BASE (0x80000000) #define PADR (0x0000) @@ -96,83 +98,9 @@ #define RANDID2 (0x2708) #define RANDID3 (0x270c) -/* common bits: SYSCON1 / SYSCON2 */ -#define SYSCON_UARTEN (1 << 8) - -#define SYSCON1_KBDSCAN(x) ((x) & 15) -#define SYSCON1_KBDSCANMASK (15) -#define SYSCON1_TC1M (1 << 4) -#define SYSCON1_TC1S (1 << 5) -#define SYSCON1_TC2M (1 << 6) -#define SYSCON1_TC2S (1 << 7) -#define SYSCON1_UART1EN SYSCON_UARTEN -#define SYSCON1_BZTOG (1 << 9) -#define SYSCON1_BZMOD (1 << 10) -#define SYSCON1_DBGEN (1 << 11) -#define SYSCON1_LCDEN (1 << 12) -#define SYSCON1_CDENTX (1 << 13) -#define SYSCON1_CDENRX (1 << 14) -#define SYSCON1_SIREN (1 << 15) -#define SYSCON1_ADCKSEL(x) (((x) & 3) << 16) -#define SYSCON1_ADCKSEL_MASK (3 << 16) -#define SYSCON1_EXCKEN (1 << 18) -#define SYSCON1_WAKEDIS (1 << 19) -#define SYSCON1_IRTXM (1 << 20) - -/* common bits: SYSFLG1 / SYSFLG2 */ -#define SYSFLG_UBUSY (1 << 11) -#define SYSFLG_URXFE (1 << 22) -#define SYSFLG_UTXFF (1 << 23) - -#define SYSFLG1_MCDR (1 << 0) -#define SYSFLG1_DCDET (1 << 1) -#define SYSFLG1_WUDR (1 << 2) -#define SYSFLG1_WUON (1 << 3) -#define SYSFLG1_CTS (1 << 8) -#define SYSFLG1_DSR (1 << 9) -#define SYSFLG1_DCD (1 << 10) -#define SYSFLG1_UBUSY SYSFLG_UBUSY -#define SYSFLG1_NBFLG (1 << 12) -#define SYSFLG1_RSTFLG (1 << 13) -#define SYSFLG1_PFFLG (1 << 14) -#define SYSFLG1_CLDFLG (1 << 15) -#define SYSFLG1_URXFE SYSFLG_URXFE -#define SYSFLG1_UTXFF SYSFLG_UTXFF -#define SYSFLG1_CRXFE (1 << 24) -#define SYSFLG1_CTXFF (1 << 25) -#define SYSFLG1_SSIBUSY (1 << 26) -#define SYSFLG1_ID (1 << 29) -#define SYSFLG1_VERID(x) (((x) >> 30) & 3) -#define SYSFLG1_VERID_MASK (3 << 30) - -#define SYSFLG2_SSRXOF (1 << 0) -#define SYSFLG2_RESVAL (1 << 1) -#define SYSFLG2_RESFRM (1 << 2) -#define SYSFLG2_SS2RXFE (1 << 3) -#define SYSFLG2_SS2TXFF (1 << 4) -#define SYSFLG2_SS2TXUF (1 << 5) -#define SYSFLG2_CKMODE (1 << 6) -#define SYSFLG2_UBUSY SYSFLG_UBUSY -#define SYSFLG2_URXFE SYSFLG_URXFE -#define SYSFLG2_UTXFF SYSFLG_UTXFF - #define LCDCON_GSEN (1 << 30) #define LCDCON_GSMD (1 << 31) -#define SYSCON2_SERSEL (1 << 0) -#define SYSCON2_KBD6 (1 << 1) -#define SYSCON2_DRAMZ (1 << 2) -#define SYSCON2_KBWEN (1 << 3) -#define SYSCON2_SS2TXEN (1 << 4) -#define SYSCON2_PCCARD1 (1 << 5) -#define SYSCON2_PCCARD2 (1 << 6) -#define SYSCON2_SS2RXEN (1 << 7) -#define SYSCON2_UART2EN SYSCON_UARTEN -#define SYSCON2_SS2MAEN (1 << 9) -#define SYSCON2_OSTB (1 << 12) -#define SYSCON2_CLKENSL (1 << 13) -#define SYSCON2_BUZFREQ (1 << 14) - /* common bits: UARTDR1 / UARTDR2 */ #define UARTDR_FRMERR (1 << 8) #define UARTDR_PARERR (1 << 9) @@ -228,18 +156,6 @@ #define DAI64FS_MCLK256EN (1 << 3) #define DAI64FS_LOOPBACK (1 << 5) -#define SYSCON3_ADCCON (1 << 0) -#define SYSCON3_CLKCTL0 (1 << 1) -#define SYSCON3_CLKCTL1 (1 << 2) -#define SYSCON3_DAISEL (1 << 3) -#define SYSCON3_ADCCKNSEN (1 << 4) -#define SYSCON3_VERSN(x) (((x) >> 5) & 7) -#define SYSCON3_VERSN_MASK (7 << 5) -#define SYSCON3_FASTWAKE (1 << 8) -#define SYSCON3_DAIEN (1 << 9) -#define SYSCON3_128FS SYSCON3_DAIEN -#define SYSCON3_ENPD67 (1 << 10) - #define SDCONF_ACTIVE (1 << 10) #define SDCONF_CLKCTL (1 << 9) #define SDCONF_WIDTH_4 (0 << 7) diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 962a6e17a01a..1a31512369f9 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -159,6 +159,9 @@ static int syscon_probe(struct platform_device *pdev) static const struct platform_device_id syscon_ids[] = { { "syscon", }, +#ifdef CONFIG_ARCH_CLPS711X + { "clps711x-syscon", }, +#endif { } }; diff --git a/include/linux/mfd/syscon/clps711x.h b/include/linux/mfd/syscon/clps711x.h new file mode 100644 index 000000000000..26355abae515 --- /dev/null +++ b/include/linux/mfd/syscon/clps711x.h @@ -0,0 +1,94 @@ +/* + * CLPS711X system register bits definitions + * + * Copyright (C) 2013 Alexander Shiyan + * + * 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. + */ + +#ifndef _LINUX_MFD_SYSCON_CLPS711X_H_ +#define _LINUX_MFD_SYSCON_CLPS711X_H_ + +#define SYSCON_OFFSET (0x00) +#define SYSFLG_OFFSET (0x40) + +#define SYSCON1_KBDSCAN(x) ((x) & 15) +#define SYSCON1_KBDSCAN_MASK (15) +#define SYSCON1_TC1M (1 << 4) +#define SYSCON1_TC1S (1 << 5) +#define SYSCON1_TC2M (1 << 6) +#define SYSCON1_TC2S (1 << 7) +#define SYSCON1_BZTOG (1 << 9) +#define SYSCON1_BZMOD (1 << 10) +#define SYSCON1_DBGEN (1 << 11) +#define SYSCON1_LCDEN (1 << 12) +#define SYSCON1_CDENTX (1 << 13) +#define SYSCON1_CDENRX (1 << 14) +#define SYSCON1_SIREN (1 << 15) +#define SYSCON1_ADCKSEL(x) (((x) & 3) << 16) +#define SYSCON1_ADCKSEL_MASK (3 << 16) +#define SYSCON1_EXCKEN (1 << 18) +#define SYSCON1_WAKEDIS (1 << 19) +#define SYSCON1_IRTXM (1 << 20) + +#define SYSCON2_SERSEL (1 << 0) +#define SYSCON2_KBD6 (1 << 1) +#define SYSCON2_DRAMZ (1 << 2) +#define SYSCON2_KBWEN (1 << 3) +#define SYSCON2_SS2TXEN (1 << 4) +#define SYSCON2_PCCARD1 (1 << 5) +#define SYSCON2_PCCARD2 (1 << 6) +#define SYSCON2_SS2RXEN (1 << 7) +#define SYSCON2_SS2MAEN (1 << 9) +#define SYSCON2_OSTB (1 << 12) +#define SYSCON2_CLKENSL (1 << 13) +#define SYSCON2_BUZFREQ (1 << 14) + +#define SYSCON3_ADCCON (1 << 0) +#define SYSCON3_CLKCTL0 (1 << 1) +#define SYSCON3_CLKCTL1 (1 << 2) +#define SYSCON3_DAISEL (1 << 3) +#define SYSCON3_ADCCKNSEN (1 << 4) +#define SYSCON3_VERSN(x) (((x) >> 5) & 7) +#define SYSCON3_VERSN_MASK (7 << 5) +#define SYSCON3_FASTWAKE (1 << 8) +#define SYSCON3_DAIEN (1 << 9) +#define SYSCON3_128FS SYSCON3_DAIEN +#define SYSCON3_ENPD67 (1 << 10) + +#define SYSCON_UARTEN (1 << 8) + +#define SYSFLG1_MCDR (1 << 0) +#define SYSFLG1_DCDET (1 << 1) +#define SYSFLG1_WUDR (1 << 2) +#define SYSFLG1_WUON (1 << 3) +#define SYSFLG1_CTS (1 << 8) +#define SYSFLG1_DSR (1 << 9) +#define SYSFLG1_DCD (1 << 10) +#define SYSFLG1_NBFLG (1 << 12) +#define SYSFLG1_RSTFLG (1 << 13) +#define SYSFLG1_PFFLG (1 << 14) +#define SYSFLG1_CLDFLG (1 << 15) +#define SYSFLG1_CRXFE (1 << 24) +#define SYSFLG1_CTXFF (1 << 25) +#define SYSFLG1_SSIBUSY (1 << 26) +#define SYSFLG1_ID (1 << 29) +#define SYSFLG1_VERID(x) (((x) >> 30) & 3) +#define SYSFLG1_VERID_MASK (3 << 30) + +#define SYSFLG2_SSRXOF (1 << 0) +#define SYSFLG2_RESVAL (1 << 1) +#define SYSFLG2_RESFRM (1 << 2) +#define SYSFLG2_SS2RXFE (1 << 3) +#define SYSFLG2_SS2TXFF (1 << 4) +#define SYSFLG2_SS2TXUF (1 << 5) +#define SYSFLG2_CKMODE (1 << 6) + +#define SYSFLG_UBUSY (1 << 11) +#define SYSFLG_URXFE (1 << 22) +#define SYSFLG_UTXFF (1 << 23) + +#endif -- cgit v1.2.3 From 10021488997317d1121505a7ac659124c058efed Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 4 Jun 2013 11:38:42 +0200 Subject: clocksource: dw_apb_timer_of: use clocksource_of_init dw_apb_timer_init used to search the devicetree for matching timer devices, making calls to it from board files necessary. Change the dw_apb_timer_init to work with CLOCKSOURCE_OF_DECLARE. With this change the function gets called once for each timer node and tracks these number of calls to attach clockevent and clocksource devices to the nodes. Also remove the calls to dw_apb_timer_init from all previous users, as clocksource_of_init is the default for init_time now. Tested on the upcoming rk3066 code. Signed-off-by: Heiko Stuebner Acked-by: Rob Herring Acked-by: Arnd Bergmann Acked-by: Jamie Iles Acked-by: Dinh Nguyen --- arch/arm/mach-picoxcell/common.c | 2 -- arch/arm/mach-socfpga/socfpga.c | 2 -- drivers/clocksource/Kconfig | 1 + drivers/clocksource/dw_apb_timer_of.c | 41 +++++++++++++++++------------------ include/linux/dw_apb_timer.h | 1 - 5 files changed, 21 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-picoxcell/common.c b/arch/arm/mach-picoxcell/common.c index 70b441ad1d18..7cde0424d33c 100644 --- a/arch/arm/mach-picoxcell/common.c +++ b/arch/arm/mach-picoxcell/common.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -88,7 +87,6 @@ DT_MACHINE_START(PICOXCELL, "Picochip picoXcell") .map_io = picoxcell_map_io, .nr_irqs = NR_IRQS_LEGACY, .init_irq = irqchip_init, - .init_time = dw_apb_timer_init, .init_machine = picoxcell_init_machine, .dt_compat = picoxcell_dt_match, .restart = picoxcell_wdt_restart, diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index 46a051359f02..8ea11b472b91 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include #include #include #include @@ -120,7 +119,6 @@ DT_MACHINE_START(SOCFPGA, "Altera SOCFPGA") .smp = smp_ops(socfpga_smp_ops), .map_io = socfpga_map_io, .init_irq = socfpga_init_irq, - .init_time = dw_apb_timer_init, .init_machine = socfpga_cyclone5_init, .restart = socfpga_cyclone5_restart, .dt_compat = altera_dt_match, diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 48a0f2e1dd78..5871933c4e51 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -22,6 +22,7 @@ config DW_APB_TIMER config DW_APB_TIMER_OF bool select DW_APB_TIMER + select CLKSRC_OF config ARMADA_370_XP_TIMER bool diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index 1964f8716966..cef554432a33 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c @@ -133,27 +133,26 @@ static void init_sched_clock(void) setup_sched_clock(read_sched_clock, 32, sched_rate); } -static const struct of_device_id osctimer_ids[] __initconst = { - { .compatible = "picochip,pc3x2-timer" }, - { .compatible = "snps,dw-apb-timer-osc" }, - {}, -}; - -void __init dw_apb_timer_init(void) +static int num_called; +static void __init dw_apb_timer_init(struct device_node *timer) { - struct device_node *event_timer, *source_timer; - - event_timer = of_find_matching_node(NULL, osctimer_ids); - if (!event_timer) - panic("No timer for clockevent"); - add_clockevent(event_timer); - - source_timer = of_find_matching_node(event_timer, osctimer_ids); - if (!source_timer) - panic("No timer for clocksource"); - add_clocksource(source_timer); - - of_node_put(source_timer); + switch (num_called) { + case 0: + pr_debug("%s: found clockevent timer\n", __func__); + add_clockevent(timer); + of_node_put(timer); + break; + case 1: + pr_debug("%s: found clocksource timer\n", __func__); + add_clocksource(timer); + of_node_put(timer); + init_sched_clock(); + break; + default: + break; + } - init_sched_clock(); + num_called++; } +CLOCKSOURCE_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init); +CLOCKSOURCE_OF_DECLARE(apb_timer, "snps,dw-apb-timer-osc", dw_apb_timer_init); diff --git a/include/linux/dw_apb_timer.h b/include/linux/dw_apb_timer.h index dd755ce2a5eb..07261d52a6df 100644 --- a/include/linux/dw_apb_timer.h +++ b/include/linux/dw_apb_timer.h @@ -53,5 +53,4 @@ void dw_apb_clocksource_start(struct dw_apb_clocksource *dw_cs); cycle_t dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs); void dw_apb_clocksource_unregister(struct dw_apb_clocksource *dw_cs); -extern void dw_apb_timer_init(void); #endif /* __DW_APB_TIMER_H__ */ -- cgit v1.2.3 From 45fe6810347b0a83561a13d9ee656c899a309fc0 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 3 May 2013 11:06:46 +0800 Subject: ARM: imx: add clock support for imx6sl Add clock support for i.MX6 SoloLite. It uses the dtc marco support to define all clock IDs in imx6sl-clock.h, which will be included by both clock driver and device tree sources, so that the data will stay sync all the time between kernel and DT. Signed-off-by: Shawn Guo --- .../devicetree/bindings/clock/imx6sl-clock.txt | 10 + arch/arm/mach-imx/clk-imx6sl.c | 270 +++++++++++++++++++++ include/dt-bindings/clock/imx6sl-clock.h | 148 +++++++++++ 3 files changed, 428 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/imx6sl-clock.txt create mode 100644 arch/arm/mach-imx/clk-imx6sl.c create mode 100644 include/dt-bindings/clock/imx6sl-clock.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/imx6sl-clock.txt b/Documentation/devicetree/bindings/clock/imx6sl-clock.txt new file mode 100644 index 000000000000..15e40bdf147d --- /dev/null +++ b/Documentation/devicetree/bindings/clock/imx6sl-clock.txt @@ -0,0 +1,10 @@ +* Clock bindings for Freescale i.MX6 SoloLite + +Required properties: +- compatible: Should be "fsl,imx6sl-ccm" +- reg: Address and length of the register set +- #clock-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. See include/dt-bindings/clock/imx6sl-clock.h +for the full list of i.MX6 SoloLite clock IDs. diff --git a/arch/arm/mach-imx/clk-imx6sl.c b/arch/arm/mach-imx/clk-imx6sl.c new file mode 100644 index 000000000000..76835a731db2 --- /dev/null +++ b/arch/arm/mach-imx/clk-imx6sl.c @@ -0,0 +1,270 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "clk.h" +#include "common.h" + +static const char const *step_sels[] = { "osc", "pll2_pfd2", }; +static const char const *pll1_sw_sels[] = { "pll1_sys", "step", }; +static const char const *ocram_alt_sels[] = { "pll2_pfd2", "pll3_pfd1", }; +static const char const *ocram_sels[] = { "periph", "ocram_alt_sels", }; +static const char const *pre_periph_sels[] = { "pll2_bus", "pll2_pfd2", "pll2_pfd0", "pll2_198m", }; +static const char const *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", "dummy", }; +static const char const *periph2_clk2_sels[] = { "pll3_usb_otg", "pll2_bus", }; +static const char const *periph_sels[] = { "pre_periph_sel", "periph_clk2_podf", }; +static const char const *periph2_sels[] = { "pre_periph2_sel", "periph2_clk2_podf", }; +static const char const *csi_lcdif_sels[] = { "mmdc", "pll2_pfd2", "pll3_120m", "pll3_pfd1", }; +static const char const *usdhc_sels[] = { "pll2_pfd2", "pll2_pfd0", }; +static const char const *ssi_sels[] = { "pll3_pfd2", "pll3_pfd3", "pll4_post_div", "dummy", }; +static const char const *perclk_sels[] = { "ipg", "osc", }; +static const char const *epdc_pxp_sels[] = { "mmdc", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd2", "pll3_pfd1", }; +static const char const *gpu2d_ovg_sels[] = { "pll3_pfd1", "pll3_usb_otg", "pll2_bus", "pll2_pfd2", }; +static const char const *gpu2d_sels[] = { "pll2_pfd2", "pll3_usb_otg", "pll3_pfd1", "pll2_bus", }; +static const char const *lcdif_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll3_pfd0", "pll3_pfd1", }; +static const char const *epdc_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd1", "pll3_pfd1", }; +static const char const *audio_sels[] = { "pll4_post_div", "pll3_pfd2", "pll3_pfd3", "pll3_usb_otg", }; +static const char const *ecspi_sels[] = { "pll3_60m", "osc", }; +static const char const *uart_sels[] = { "pll3_80m", "osc", }; + +static struct clk_div_table clk_enet_ref_table[] = { + { .val = 0, .div = 20, }, + { .val = 1, .div = 10, }, + { .val = 2, .div = 5, }, + { .val = 3, .div = 4, }, + { } +}; + +static struct clk_div_table post_div_table[] = { + { .val = 2, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 0, .div = 4, }, + { } +}; + +static struct clk_div_table video_div_table[] = { + { .val = 0, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 2, .div = 1, }, + { .val = 3, .div = 4, }, + { } +}; + +static struct clk *clks[IMX6SL_CLK_CLK_END]; +static struct clk_onecell_data clk_data; + +int __init imx6sl_clocks_init(void) +{ + struct device_node *np; + void __iomem *base; + int irq; + int i; + + of_clk_init(NULL); + + clks[IMX6SL_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clks[IMX6SL_CLK_CKIL] = imx_obtain_fixed_clock("ckil", 0); + clks[IMX6SL_CLK_OSC] = imx_obtain_fixed_clock("osc", 0); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-anatop"); + base = of_iomap(np, 0); + WARN_ON(!base); + + /* type name parent base div_mask */ + clks[IMX6SL_CLK_PLL1_SYS] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f); + clks[IMX6SL_CLK_PLL2_BUS] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1); + clks[IMX6SL_CLK_PLL3_USB_OTG] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x3); + clks[IMX6SL_CLK_PLL4_AUDIO] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "osc", base + 0x70, 0x7f); + clks[IMX6SL_CLK_PLL5_VIDEO] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x7f); + clks[IMX6SL_CLK_PLL6_ENET] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x3); + clks[IMX6SL_CLK_PLL7_USB_HOST] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host", "osc", base + 0x20, 0x3); + + /* + * usbphy1 and usbphy2 are implemented as dummy gates using reserve + * bit 20. They are used by phy driver to keep the refcount of + * parent PLL correct. usbphy1_gate and usbphy2_gate only needs to be + * turned on during boot, and software will not need to control it + * anymore after that. + */ + clks[IMX6SL_CLK_USBPHY1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20); + clks[IMX6SL_CLK_USBPHY2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 20); + clks[IMX6SL_CLK_USBPHY1_GATE] = imx_clk_gate("usbphy1_gate", "dummy", base + 0x10, 6); + clks[IMX6SL_CLK_USBPHY2_GATE] = imx_clk_gate("usbphy2_gate", "dummy", base + 0x20, 6); + + /* dev name parent_name flags reg shift width div: flags, div_table lock */ + clks[IMX6SL_CLK_PLL4_POST_DIV] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); + clks[IMX6SL_CLK_PLL5_POST_DIV] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); + clks[IMX6SL_CLK_PLL5_VIDEO_DIV] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); + clks[IMX6SL_CLK_ENET_REF] = clk_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, base + 0xe0, 0, 2, 0, clk_enet_ref_table, &imx_ccm_lock); + + /* name parent_name reg idx */ + clks[IMX6SL_CLK_PLL2_PFD0] = imx_clk_pfd("pll2_pfd0", "pll2_bus", base + 0x100, 0); + clks[IMX6SL_CLK_PLL2_PFD1] = imx_clk_pfd("pll2_pfd1", "pll2_bus", base + 0x100, 1); + clks[IMX6SL_CLK_PLL2_PFD2] = imx_clk_pfd("pll2_pfd2", "pll2_bus", base + 0x100, 2); + clks[IMX6SL_CLK_PLL3_PFD0] = imx_clk_pfd("pll3_pfd0", "pll3_usb_otg", base + 0xf0, 0); + clks[IMX6SL_CLK_PLL3_PFD1] = imx_clk_pfd("pll3_pfd1", "pll3_usb_otg", base + 0xf0, 1); + clks[IMX6SL_CLK_PLL3_PFD2] = imx_clk_pfd("pll3_pfd2", "pll3_usb_otg", base + 0xf0, 2); + clks[IMX6SL_CLK_PLL3_PFD3] = imx_clk_pfd("pll3_pfd3", "pll3_usb_otg", base + 0xf0, 3); + + /* name parent_name mult div */ + clks[IMX6SL_CLK_PLL2_198M] = imx_clk_fixed_factor("pll2_198m", "pll2_pfd2", 1, 2); + clks[IMX6SL_CLK_PLL3_120M] = imx_clk_fixed_factor("pll3_120m", "pll3_usb_otg", 1, 4); + clks[IMX6SL_CLK_PLL3_80M] = imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); + clks[IMX6SL_CLK_PLL3_60M] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-ccm"); + base = of_iomap(np, 0); + WARN_ON(!base); + + /* name reg shift width parent_names num_parents */ + clks[IMX6SL_CLK_STEP] = imx_clk_mux("step", base + 0xc, 8, 1, step_sels, ARRAY_SIZE(step_sels)); + clks[IMX6SL_CLK_PLL1_SW] = imx_clk_mux("pll1_sw", base + 0xc, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels)); + clks[IMX6SL_CLK_OCRAM_ALT_SEL] = imx_clk_mux("ocram_alt_sel", base + 0x14, 7, 1, ocram_alt_sels, ARRAY_SIZE(ocram_alt_sels)); + clks[IMX6SL_CLK_OCRAM_SEL] = imx_clk_mux("ocram_sel", base + 0x14, 6, 1, ocram_sels, ARRAY_SIZE(ocram_sels)); + clks[IMX6SL_CLK_PRE_PERIPH2_SEL] = imx_clk_mux("pre_periph2_sel", base + 0x18, 21, 2, pre_periph_sels, ARRAY_SIZE(pre_periph_sels)); + clks[IMX6SL_CLK_PRE_PERIPH_SEL] = imx_clk_mux("pre_periph_sel", base + 0x18, 18, 2, pre_periph_sels, ARRAY_SIZE(pre_periph_sels)); + clks[IMX6SL_CLK_PERIPH2_CLK2_SEL] = imx_clk_mux("periph2_clk2_sel", base + 0x18, 20, 1, periph2_clk2_sels, ARRAY_SIZE(periph2_clk2_sels)); + clks[IMX6SL_CLK_PERIPH_CLK2_SEL] = imx_clk_mux("periph_clk2_sel", base + 0x18, 12, 2, periph_clk2_sels, ARRAY_SIZE(periph_clk2_sels)); + clks[IMX6SL_CLK_CSI_SEL] = imx_clk_mux("csi_sel", base + 0x3c, 9, 2, csi_lcdif_sels, ARRAY_SIZE(csi_lcdif_sels)); + clks[IMX6SL_CLK_LCDIF_AXI_SEL] = imx_clk_mux("lcdif_axi_sel", base + 0x3c, 14, 2, csi_lcdif_sels, ARRAY_SIZE(csi_lcdif_sels)); + clks[IMX6SL_CLK_USDHC1_SEL] = imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + clks[IMX6SL_CLK_USDHC2_SEL] = imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + clks[IMX6SL_CLK_USDHC3_SEL] = imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + clks[IMX6SL_CLK_USDHC4_SEL] = imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + clks[IMX6SL_CLK_SSI1_SEL] = imx_clk_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + clks[IMX6SL_CLK_SSI2_SEL] = imx_clk_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + clks[IMX6SL_CLK_SSI3_SEL] = imx_clk_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + clks[IMX6SL_CLK_PERCLK_SEL] = imx_clk_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels)); + clks[IMX6SL_CLK_PXP_AXI_SEL] = imx_clk_mux("pxp_axi_sel", base + 0x34, 6, 3, epdc_pxp_sels, ARRAY_SIZE(epdc_pxp_sels)); + clks[IMX6SL_CLK_EPDC_AXI_SEL] = imx_clk_mux("epdc_axi_sel", base + 0x34, 15, 3, epdc_pxp_sels, ARRAY_SIZE(epdc_pxp_sels)); + clks[IMX6SL_CLK_GPU2D_OVG_SEL] = imx_clk_mux("gpu2d_ovg_sel", base + 0x18, 4, 2, gpu2d_ovg_sels, ARRAY_SIZE(gpu2d_ovg_sels)); + clks[IMX6SL_CLK_GPU2D_SEL] = imx_clk_mux("gpu2d_sel", base + 0x18, 8, 2, gpu2d_sels, ARRAY_SIZE(gpu2d_sels)); + clks[IMX6SL_CLK_LCDIF_PIX_SEL] = imx_clk_mux("lcdif_pix_sel", base + 0x38, 6, 3, lcdif_pix_sels, ARRAY_SIZE(lcdif_pix_sels)); + clks[IMX6SL_CLK_EPDC_PIX_SEL] = imx_clk_mux("epdc_pix_sel", base + 0x38, 15, 3, epdc_pix_sels, ARRAY_SIZE(epdc_pix_sels)); + clks[IMX6SL_CLK_SPDIF0_SEL] = imx_clk_mux("spdif0_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); + clks[IMX6SL_CLK_SPDIF1_SEL] = imx_clk_mux("spdif1_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); + clks[IMX6SL_CLK_EXTERN_AUDIO_SEL] = imx_clk_mux("extern_audio_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels)); + clks[IMX6SL_CLK_ECSPI_SEL] = imx_clk_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); + clks[IMX6SL_CLK_UART_SEL] = imx_clk_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); + + /* name reg shift width busy: reg, shift parent_names num_parents */ + clks[IMX6SL_CLK_PERIPH] = imx_clk_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels)); + clks[IMX6SL_CLK_PERIPH2] = imx_clk_busy_mux("periph2", base + 0x14, 26, 1, base + 0x48, 3, periph2_sels, ARRAY_SIZE(periph2_sels)); + + /* name parent_name reg shift width */ + clks[IMX6SL_CLK_OCRAM_PODF] = imx_clk_divider("ocram_podf", "ocram_sel", base + 0x14, 16, 3); + clks[IMX6SL_CLK_PERIPH_CLK2_PODF] = imx_clk_divider("periph_clk2_podf", "periph_clk2_sel", base + 0x14, 27, 3); + clks[IMX6SL_CLK_PERIPH2_CLK2_PODF] = imx_clk_divider("periph2_clk2_podf", "periph2_clk2_sel", base + 0x14, 0, 3); + clks[IMX6SL_CLK_IPG] = imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2); + clks[IMX6SL_CLK_CSI_PODF] = imx_clk_divider("csi_podf", "csi_sel", base + 0x3c, 11, 3); + clks[IMX6SL_CLK_LCDIF_AXI_PODF] = imx_clk_divider("lcdif_axi_podf", "lcdif_axi_sel", base + 0x3c, 16, 3); + clks[IMX6SL_CLK_USDHC1_PODF] = imx_clk_divider("usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3); + clks[IMX6SL_CLK_USDHC2_PODF] = imx_clk_divider("usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3); + clks[IMX6SL_CLK_USDHC3_PODF] = imx_clk_divider("usdhc3_podf", "usdhc3_sel", base + 0x24, 19, 3); + clks[IMX6SL_CLK_USDHC4_PODF] = imx_clk_divider("usdhc4_podf", "usdhc4_sel", base + 0x24, 22, 3); + clks[IMX6SL_CLK_SSI1_PRED] = imx_clk_divider("ssi1_pred", "ssi1_sel", base + 0x28, 6, 3); + clks[IMX6SL_CLK_SSI1_PODF] = imx_clk_divider("ssi1_podf", "ssi1_pred", base + 0x28, 0, 6); + clks[IMX6SL_CLK_SSI2_PRED] = imx_clk_divider("ssi2_pred", "ssi2_sel", base + 0x2c, 6, 3); + clks[IMX6SL_CLK_SSI2_PODF] = imx_clk_divider("ssi2_podf", "ssi2_pred", base + 0x2c, 0, 6); + clks[IMX6SL_CLK_SSI3_PRED] = imx_clk_divider("ssi3_pred", "ssi3_sel", base + 0x28, 22, 3); + clks[IMX6SL_CLK_SSI3_PODF] = imx_clk_divider("ssi3_podf", "ssi3_pred", base + 0x28, 16, 6); + clks[IMX6SL_CLK_PERCLK] = imx_clk_divider("perclk", "perclk_sel", base + 0x1c, 0, 6); + clks[IMX6SL_CLK_PXP_AXI_PODF] = imx_clk_divider("pxp_axi_podf", "pxp_axi_sel", base + 0x34, 3, 3); + clks[IMX6SL_CLK_EPDC_AXI_PODF] = imx_clk_divider("epdc_axi_podf", "epdc_axi_sel", base + 0x34, 12, 3); + clks[IMX6SL_CLK_GPU2D_OVG_PODF] = imx_clk_divider("gpu2d_ovg_podf", "gpu2d_ovg_sel", base + 0x18, 26, 3); + clks[IMX6SL_CLK_GPU2D_PODF] = imx_clk_divider("gpu2d_podf", "gpu2d_sel", base + 0x18, 29, 3); + clks[IMX6SL_CLK_LCDIF_PIX_PRED] = imx_clk_divider("lcdif_pix_pred", "lcdif_pix_sel", base + 0x38, 3, 3); + clks[IMX6SL_CLK_EPDC_PIX_PRED] = imx_clk_divider("epdc_pix_pred", "epdc_pix_sel", base + 0x38, 12, 3); + clks[IMX6SL_CLK_LCDIF_PIX_PODF] = imx_clk_divider("lcdif_pix_podf", "lcdif_pix_pred", base + 0x1c, 20, 3); + clks[IMX6SL_CLK_EPDC_PIX_PODF] = imx_clk_divider("epdc_pix_podf", "epdc_pix_pred", base + 0x18, 23, 3); + clks[IMX6SL_CLK_SPDIF0_PRED] = imx_clk_divider("spdif0_pred", "spdif0_sel", base + 0x30, 25, 3); + clks[IMX6SL_CLK_SPDIF0_PODF] = imx_clk_divider("spdif0_podf", "spdif0_pred", base + 0x30, 22, 3); + clks[IMX6SL_CLK_SPDIF1_PRED] = imx_clk_divider("spdif1_pred", "spdif1_sel", base + 0x30, 12, 3); + clks[IMX6SL_CLK_SPDIF1_PODF] = imx_clk_divider("spdif1_podf", "spdif1_pred", base + 0x30, 9, 3); + clks[IMX6SL_CLK_EXTERN_AUDIO_PRED] = imx_clk_divider("extern_audio_pred", "extern_audio_sel", base + 0x28, 9, 3); + clks[IMX6SL_CLK_EXTERN_AUDIO_PODF] = imx_clk_divider("extern_audio_podf", "extern_audio_pred", base + 0x28, 25, 3); + clks[IMX6SL_CLK_ECSPI_ROOT] = imx_clk_divider("ecspi_root", "ecspi_sel", base + 0x38, 19, 6); + clks[IMX6SL_CLK_UART_ROOT] = imx_clk_divider("uart_root", "uart_sel", base + 0x24, 0, 6); + + /* name parent_name reg shift width busy: reg, shift */ + clks[IMX6SL_CLK_AHB] = imx_clk_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1); + clks[IMX6SL_CLK_MMDC_ROOT] = imx_clk_busy_divider("mmdc", "periph2", base + 0x14, 3, 3, base + 0x48, 2); + clks[IMX6SL_CLK_ARM] = imx_clk_busy_divider("arm", "pll1_sw", base + 0x10, 0, 3, base + 0x48, 16); + + /* name parent_name reg shift */ + clks[IMX6SL_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0); + clks[IMX6SL_CLK_ECSPI2] = imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2); + clks[IMX6SL_CLK_ECSPI3] = imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4); + clks[IMX6SL_CLK_ECSPI4] = imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6); + clks[IMX6SL_CLK_EPIT1] = imx_clk_gate2("epit1", "perclk", base + 0x6c, 12); + clks[IMX6SL_CLK_EPIT2] = imx_clk_gate2("epit2", "perclk", base + 0x6c, 14); + clks[IMX6SL_CLK_EXTERN_AUDIO] = imx_clk_gate2("extern_audio", "extern_audio_podf", base + 0x6c, 16); + clks[IMX6SL_CLK_GPT] = imx_clk_gate2("gpt", "perclk", base + 0x6c, 20); + clks[IMX6SL_CLK_GPT_SERIAL] = imx_clk_gate2("gpt_serial", "perclk", base + 0x6c, 22); + clks[IMX6SL_CLK_GPU2D_OVG] = imx_clk_gate2("gpu2d_ovg", "gpu2d_ovg_podf", base + 0x6c, 26); + clks[IMX6SL_CLK_I2C1] = imx_clk_gate2("i2c1", "perclk", base + 0x70, 6); + clks[IMX6SL_CLK_I2C2] = imx_clk_gate2("i2c2", "perclk", base + 0x70, 8); + clks[IMX6SL_CLK_I2C3] = imx_clk_gate2("i2c3", "perclk", base + 0x70, 10); + clks[IMX6SL_CLK_OCOTP] = imx_clk_gate2("ocotp", "ipg", base + 0x70, 12); + clks[IMX6SL_CLK_CSI] = imx_clk_gate2("csi", "csi_podf", base + 0x74, 0); + clks[IMX6SL_CLK_PXP_AXI] = imx_clk_gate2("pxp_axi", "pxp_axi_podf", base + 0x74, 2); + clks[IMX6SL_CLK_EPDC_AXI] = imx_clk_gate2("epdc_axi", "epdc_axi_podf", base + 0x74, 4); + clks[IMX6SL_CLK_LCDIF_AXI] = imx_clk_gate2("lcdif_axi", "lcdif_axi_podf", base + 0x74, 6); + clks[IMX6SL_CLK_LCDIF_PIX] = imx_clk_gate2("lcdif_pix", "lcdif_pix_podf", base + 0x74, 8); + clks[IMX6SL_CLK_EPDC_PIX] = imx_clk_gate2("epdc_pix", "epdc_pix_podf", base + 0x74, 10); + clks[IMX6SL_CLK_OCRAM] = imx_clk_gate2("ocram", "ocram_podf", base + 0x74, 28); + clks[IMX6SL_CLK_PWM1] = imx_clk_gate2("pwm1", "perclk", base + 0x78, 16); + clks[IMX6SL_CLK_PWM2] = imx_clk_gate2("pwm2", "perclk", base + 0x78, 18); + clks[IMX6SL_CLK_PWM3] = imx_clk_gate2("pwm3", "perclk", base + 0x78, 20); + clks[IMX6SL_CLK_PWM4] = imx_clk_gate2("pwm4", "perclk", base + 0x78, 22); + clks[IMX6SL_CLK_SDMA] = imx_clk_gate2("sdma", "ipg", base + 0x7c, 6); + clks[IMX6SL_CLK_SPDIF] = imx_clk_gate2("spdif", "spdif0_podf", base + 0x7c, 14); + clks[IMX6SL_CLK_SSI1] = imx_clk_gate2("ssi1", "ssi1_podf", base + 0x7c, 18); + clks[IMX6SL_CLK_SSI2] = imx_clk_gate2("ssi2", "ssi2_podf", base + 0x7c, 20); + clks[IMX6SL_CLK_SSI3] = imx_clk_gate2("ssi3", "ssi3_podf", base + 0x7c, 22); + clks[IMX6SL_CLK_UART] = imx_clk_gate2("uart", "ipg", base + 0x7c, 24); + clks[IMX6SL_CLK_UART_SERIAL] = imx_clk_gate2("uart_serial", "uart_root", base + 0x7c, 26); + clks[IMX6SL_CLK_USBOH3] = imx_clk_gate2("usboh3", "ipg", base + 0x80, 0); + clks[IMX6SL_CLK_USDHC1] = imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2); + clks[IMX6SL_CLK_USDHC2] = imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4); + clks[IMX6SL_CLK_USDHC3] = imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6); + clks[IMX6SL_CLK_USDHC4] = imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8); + + for (i = 0; i < ARRAY_SIZE(clks); i++) + if (IS_ERR(clks[i])) + pr_err("i.MX6SL clk %d: register failed with %ld\n", + i, PTR_ERR(clks[i])); + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + clk_register_clkdev(clks[IMX6SL_CLK_GPT], "ipg", "imx-gpt.0"); + clk_register_clkdev(clks[IMX6SL_CLK_GPT_SERIAL], "per", "imx-gpt.0"); + + if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { + clk_prepare_enable(clks[IMX6SL_CLK_USBPHY1_GATE]); + clk_prepare_enable(clks[IMX6SL_CLK_USBPHY2_GATE]); + } + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-gpt"); + base = of_iomap(np, 0); + WARN_ON(!base); + irq = irq_of_parse_and_map(np, 0); + mxc_timer_init(base, irq); + + return 0; +} diff --git a/include/dt-bindings/clock/imx6sl-clock.h b/include/dt-bindings/clock/imx6sl-clock.h new file mode 100644 index 000000000000..7fcdf90879f2 --- /dev/null +++ b/include/dt-bindings/clock/imx6sl-clock.h @@ -0,0 +1,148 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * 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. + * + */ + +#ifndef __DT_BINDINGS_CLOCK_IMX6SL_H +#define __DT_BINDINGS_CLOCK_IMX6SL_H + +#define IMX6SL_CLK_DUMMY 0 +#define IMX6SL_CLK_CKIL 1 +#define IMX6SL_CLK_OSC 2 +#define IMX6SL_CLK_PLL1_SYS 3 +#define IMX6SL_CLK_PLL2_BUS 4 +#define IMX6SL_CLK_PLL3_USB_OTG 5 +#define IMX6SL_CLK_PLL4_AUDIO 6 +#define IMX6SL_CLK_PLL5_VIDEO 7 +#define IMX6SL_CLK_PLL6_ENET 8 +#define IMX6SL_CLK_PLL7_USB_HOST 9 +#define IMX6SL_CLK_USBPHY1 10 +#define IMX6SL_CLK_USBPHY2 11 +#define IMX6SL_CLK_USBPHY1_GATE 12 +#define IMX6SL_CLK_USBPHY2_GATE 13 +#define IMX6SL_CLK_PLL4_POST_DIV 14 +#define IMX6SL_CLK_PLL5_POST_DIV 15 +#define IMX6SL_CLK_PLL5_VIDEO_DIV 16 +#define IMX6SL_CLK_ENET_REF 17 +#define IMX6SL_CLK_PLL2_PFD0 18 +#define IMX6SL_CLK_PLL2_PFD1 19 +#define IMX6SL_CLK_PLL2_PFD2 20 +#define IMX6SL_CLK_PLL3_PFD0 21 +#define IMX6SL_CLK_PLL3_PFD1 22 +#define IMX6SL_CLK_PLL3_PFD2 23 +#define IMX6SL_CLK_PLL3_PFD3 24 +#define IMX6SL_CLK_PLL2_198M 25 +#define IMX6SL_CLK_PLL3_120M 26 +#define IMX6SL_CLK_PLL3_80M 27 +#define IMX6SL_CLK_PLL3_60M 28 +#define IMX6SL_CLK_STEP 29 +#define IMX6SL_CLK_PLL1_SW 30 +#define IMX6SL_CLK_OCRAM_ALT_SEL 31 +#define IMX6SL_CLK_OCRAM_SEL 32 +#define IMX6SL_CLK_PRE_PERIPH2_SEL 33 +#define IMX6SL_CLK_PRE_PERIPH_SEL 34 +#define IMX6SL_CLK_PERIPH2_CLK2_SEL 35 +#define IMX6SL_CLK_PERIPH_CLK2_SEL 36 +#define IMX6SL_CLK_CSI_SEL 37 +#define IMX6SL_CLK_LCDIF_AXI_SEL 38 +#define IMX6SL_CLK_USDHC1_SEL 39 +#define IMX6SL_CLK_USDHC2_SEL 40 +#define IMX6SL_CLK_USDHC3_SEL 41 +#define IMX6SL_CLK_USDHC4_SEL 42 +#define IMX6SL_CLK_SSI1_SEL 43 +#define IMX6SL_CLK_SSI2_SEL 44 +#define IMX6SL_CLK_SSI3_SEL 45 +#define IMX6SL_CLK_PERCLK_SEL 46 +#define IMX6SL_CLK_PXP_AXI_SEL 47 +#define IMX6SL_CLK_EPDC_AXI_SEL 48 +#define IMX6SL_CLK_GPU2D_OVG_SEL 49 +#define IMX6SL_CLK_GPU2D_SEL 50 +#define IMX6SL_CLK_LCDIF_PIX_SEL 51 +#define IMX6SL_CLK_EPDC_PIX_SEL 52 +#define IMX6SL_CLK_SPDIF0_SEL 53 +#define IMX6SL_CLK_SPDIF1_SEL 54 +#define IMX6SL_CLK_EXTERN_AUDIO_SEL 55 +#define IMX6SL_CLK_ECSPI_SEL 56 +#define IMX6SL_CLK_UART_SEL 57 +#define IMX6SL_CLK_PERIPH 58 +#define IMX6SL_CLK_PERIPH2 59 +#define IMX6SL_CLK_OCRAM_PODF 60 +#define IMX6SL_CLK_PERIPH_CLK2_PODF 61 +#define IMX6SL_CLK_PERIPH2_CLK2_PODF 62 +#define IMX6SL_CLK_IPG 63 +#define IMX6SL_CLK_CSI_PODF 64 +#define IMX6SL_CLK_LCDIF_AXI_PODF 65 +#define IMX6SL_CLK_USDHC1_PODF 66 +#define IMX6SL_CLK_USDHC2_PODF 67 +#define IMX6SL_CLK_USDHC3_PODF 68 +#define IMX6SL_CLK_USDHC4_PODF 69 +#define IMX6SL_CLK_SSI1_PRED 70 +#define IMX6SL_CLK_SSI1_PODF 71 +#define IMX6SL_CLK_SSI2_PRED 72 +#define IMX6SL_CLK_SSI2_PODF 73 +#define IMX6SL_CLK_SSI3_PRED 74 +#define IMX6SL_CLK_SSI3_PODF 75 +#define IMX6SL_CLK_PERCLK 76 +#define IMX6SL_CLK_PXP_AXI_PODF 77 +#define IMX6SL_CLK_EPDC_AXI_PODF 78 +#define IMX6SL_CLK_GPU2D_OVG_PODF 79 +#define IMX6SL_CLK_GPU2D_PODF 80 +#define IMX6SL_CLK_LCDIF_PIX_PRED 81 +#define IMX6SL_CLK_EPDC_PIX_PRED 82 +#define IMX6SL_CLK_LCDIF_PIX_PODF 83 +#define IMX6SL_CLK_EPDC_PIX_PODF 84 +#define IMX6SL_CLK_SPDIF0_PRED 85 +#define IMX6SL_CLK_SPDIF0_PODF 86 +#define IMX6SL_CLK_SPDIF1_PRED 87 +#define IMX6SL_CLK_SPDIF1_PODF 88 +#define IMX6SL_CLK_EXTERN_AUDIO_PRED 89 +#define IMX6SL_CLK_EXTERN_AUDIO_PODF 90 +#define IMX6SL_CLK_ECSPI_ROOT 91 +#define IMX6SL_CLK_UART_ROOT 92 +#define IMX6SL_CLK_AHB 93 +#define IMX6SL_CLK_MMDC_ROOT 94 +#define IMX6SL_CLK_ARM 95 +#define IMX6SL_CLK_ECSPI1 96 +#define IMX6SL_CLK_ECSPI2 97 +#define IMX6SL_CLK_ECSPI3 98 +#define IMX6SL_CLK_ECSPI4 99 +#define IMX6SL_CLK_EPIT1 100 +#define IMX6SL_CLK_EPIT2 101 +#define IMX6SL_CLK_EXTERN_AUDIO 102 +#define IMX6SL_CLK_GPT 103 +#define IMX6SL_CLK_GPT_SERIAL 104 +#define IMX6SL_CLK_GPU2D_OVG 105 +#define IMX6SL_CLK_I2C1 106 +#define IMX6SL_CLK_I2C2 107 +#define IMX6SL_CLK_I2C3 108 +#define IMX6SL_CLK_OCOTP 109 +#define IMX6SL_CLK_CSI 110 +#define IMX6SL_CLK_PXP_AXI 111 +#define IMX6SL_CLK_EPDC_AXI 112 +#define IMX6SL_CLK_LCDIF_AXI 113 +#define IMX6SL_CLK_LCDIF_PIX 114 +#define IMX6SL_CLK_EPDC_PIX 115 +#define IMX6SL_CLK_OCRAM 116 +#define IMX6SL_CLK_PWM1 117 +#define IMX6SL_CLK_PWM2 118 +#define IMX6SL_CLK_PWM3 119 +#define IMX6SL_CLK_PWM4 120 +#define IMX6SL_CLK_SDMA 121 +#define IMX6SL_CLK_SPDIF 122 +#define IMX6SL_CLK_SSI1 123 +#define IMX6SL_CLK_SSI2 124 +#define IMX6SL_CLK_SSI3 125 +#define IMX6SL_CLK_UART 126 +#define IMX6SL_CLK_UART_SERIAL 127 +#define IMX6SL_CLK_USBOH3 128 +#define IMX6SL_CLK_USDHC1 129 +#define IMX6SL_CLK_USDHC2 130 +#define IMX6SL_CLK_USDHC3 131 +#define IMX6SL_CLK_USDHC4 132 +#define IMX6SL_CLK_CLK_END 133 + +#endif /* __DT_BINDINGS_CLOCK_IMX6SL_H */ -- cgit v1.2.3 From 1f2c5fd5f0486566f73aa0149577d5f69df90bcc Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Tue, 28 May 2013 17:12:20 +0800 Subject: ARM: imx: add VF610 clock support Add clock support for Vybrid VF610. It uses dtc macro support to define all clock IDs in vf610-clock.h to keep clock IDs coherence between kernel and DT. Signed-off-by: Jingchang Lu Signed-off-by: Shawn Guo --- .../devicetree/bindings/clock/vf610-clock.txt | 26 ++ arch/arm/mach-imx/clk-vf610.c | 319 +++++++++++++++++++++ include/dt-bindings/clock/vf610-clock.h | 163 +++++++++++ 3 files changed, 508 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/vf610-clock.txt create mode 100644 arch/arm/mach-imx/clk-vf610.c create mode 100644 include/dt-bindings/clock/vf610-clock.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/vf610-clock.txt b/Documentation/devicetree/bindings/clock/vf610-clock.txt new file mode 100644 index 000000000000..c80863d344ac --- /dev/null +++ b/Documentation/devicetree/bindings/clock/vf610-clock.txt @@ -0,0 +1,26 @@ +* Clock bindings for Freescale Vybrid VF610 SOC + +Required properties: +- compatible: Should be "fsl,vf610-ccm" +- reg: Address and length of the register set +- #clock-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. See include/dt-bindings/clock/vf610-clock.h +for the full list of VF610 clock IDs. + +Examples: + +clks: ccm@4006b000 { + compatible = "fsl,vf610-ccm"; + reg = <0x4006b000 0x1000>; + #clock-cells = <1>; +}; + +uart1: serial@40028000 { + compatible = "fsl,vf610-uart"; + reg = <0x40028000 0x1000>; + interrupts = <0 62 0x04>; + clocks = <&clks VF610_CLK_UART1>; + clock-names = "ipg"; +}; diff --git a/arch/arm/mach-imx/clk-vf610.c b/arch/arm/mach-imx/clk-vf610.c new file mode 100644 index 000000000000..d617c0b7c809 --- /dev/null +++ b/arch/arm/mach-imx/clk-vf610.c @@ -0,0 +1,319 @@ +/* + * Copyright 2012-2013 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include + +#include "clk.h" + +#define CCM_CCR (ccm_base + 0x00) +#define CCM_CSR (ccm_base + 0x04) +#define CCM_CCSR (ccm_base + 0x08) +#define CCM_CACRR (ccm_base + 0x0c) +#define CCM_CSCMR1 (ccm_base + 0x10) +#define CCM_CSCDR1 (ccm_base + 0x14) +#define CCM_CSCDR2 (ccm_base + 0x18) +#define CCM_CSCDR3 (ccm_base + 0x1c) +#define CCM_CSCMR2 (ccm_base + 0x20) +#define CCM_CSCDR4 (ccm_base + 0x24) +#define CCM_CLPCR (ccm_base + 0x2c) +#define CCM_CISR (ccm_base + 0x30) +#define CCM_CIMR (ccm_base + 0x34) +#define CCM_CGPR (ccm_base + 0x3c) +#define CCM_CCGR0 (ccm_base + 0x40) +#define CCM_CCGR1 (ccm_base + 0x44) +#define CCM_CCGR2 (ccm_base + 0x48) +#define CCM_CCGR3 (ccm_base + 0x4c) +#define CCM_CCGR4 (ccm_base + 0x50) +#define CCM_CCGR5 (ccm_base + 0x54) +#define CCM_CCGR6 (ccm_base + 0x58) +#define CCM_CCGR7 (ccm_base + 0x5c) +#define CCM_CCGR8 (ccm_base + 0x60) +#define CCM_CCGR9 (ccm_base + 0x64) +#define CCM_CCGR10 (ccm_base + 0x68) +#define CCM_CCGR11 (ccm_base + 0x6c) +#define CCM_CMEOR0 (ccm_base + 0x70) +#define CCM_CMEOR1 (ccm_base + 0x74) +#define CCM_CMEOR2 (ccm_base + 0x78) +#define CCM_CMEOR3 (ccm_base + 0x7c) +#define CCM_CMEOR4 (ccm_base + 0x80) +#define CCM_CMEOR5 (ccm_base + 0x84) +#define CCM_CPPDSR (ccm_base + 0x88) +#define CCM_CCOWR (ccm_base + 0x8c) +#define CCM_CCPGR0 (ccm_base + 0x90) +#define CCM_CCPGR1 (ccm_base + 0x94) +#define CCM_CCPGR2 (ccm_base + 0x98) +#define CCM_CCPGR3 (ccm_base + 0x9c) + +#define CCM_CCGRx_CGn(n) ((n) * 2) + +#define PFD_PLL1_BASE (anatop_base + 0x2b0) +#define PFD_PLL2_BASE (anatop_base + 0x100) +#define PFD_PLL3_BASE (anatop_base + 0xf0) + +static void __iomem *anatop_base; +static void __iomem *ccm_base; + +/* sources for multiplexer clocks, this is used multiple times */ +static const char const *fast_sels[] = { "firc", "fxosc", }; +static const char const *slow_sels[] = { "sirc_32k", "sxosc", }; +static const char const *pll1_sels[] = { "pll1_main", "pll1_pfd1", "pll1_pfd2", "pll1_pfd3", "pll1_pfd4", }; +static const char const *pll2_sels[] = { "pll2_main", "pll2_pfd1", "pll2_pfd2", "pll2_pfd3", "pll2_pfd4", }; +static const char const *sys_sels[] = { "fast_clk_sel", "slow_clk_sel", "pll2_pfd_sel", "pll2_main", "pll1_pfd_sel", "pll3_main", }; +static const char const *ddr_sels[] = { "pll2_pfd2", "sys_sel", }; +static const char const *rmii_sels[] = { "enet_ext", "audio_ext", "enet_50m", "enet_25m", }; +static const char const *enet_ts_sels[] = { "enet_ext", "fxosc", "audio_ext", "usb", "enet_ts", "enet_25m", "enet_50m", }; +static const char const *esai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", }; +static const char const *sai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", }; +static const char const *nfc_sels[] = { "platform_bus", "pll1_pfd1", "pll3_pfd1", "pll3_pfd3", }; +static const char const *qspi_sels[] = { "pll3_main", "pll3_pfd4", "pll2_pfd4", "pll1_pfd4", }; +static const char const *esdhc_sels[] = { "pll3_main", "pll3_pfd3", "pll1_pfd3", "platform_bus", }; +static const char const *dcu_sels[] = { "pll1_pfd2", "pll3_main", }; +static const char const *gpu_sels[] = { "pll2_pfd2", "pll3_pfd2", }; +static const char const *vadc_sels[] = { "pll6_main_div", "pll3_main_div", "pll3_main", }; +/* FTM counter clock source, not module clock */ +static const char const *ftm_ext_sels[] = {"sirc_128k", "sxosc", "fxosc_half", "audio_ext", }; +static const char const *ftm_fix_sels[] = { "sxosc", "ipg_bus", }; + +static struct clk_div_table pll4_main_div_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 6 }, + { .val = 3, .div = 8 }, + { .val = 4, .div = 10 }, + { .val = 5, .div = 12 }, + { .val = 6, .div = 14 }, + { .val = 7, .div = 16 }, + { } +}; + +static struct clk *clk[VF610_CLK_END]; +static struct clk_onecell_data clk_data; + +static void __init vf610_clocks_init(struct device_node *ccm_node) +{ + struct device_node *np; + + clk[VF610_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clk[VF610_CLK_SIRC_128K] = imx_clk_fixed("sirc_128k", 128000); + clk[VF610_CLK_SIRC_32K] = imx_clk_fixed("sirc_32k", 32000); + clk[VF610_CLK_FIRC] = imx_clk_fixed("firc", 24000000); + + clk[VF610_CLK_SXOSC] = imx_obtain_fixed_clock("sxosc", 0); + clk[VF610_CLK_FXOSC] = imx_obtain_fixed_clock("fxosc", 0); + clk[VF610_CLK_AUDIO_EXT] = imx_obtain_fixed_clock("audio_ext", 0); + clk[VF610_CLK_ENET_EXT] = imx_obtain_fixed_clock("enet_ext", 0); + + clk[VF610_CLK_FXOSC_HALF] = imx_clk_fixed_factor("fxosc_half", "fxosc", 1, 2); + + np = of_find_compatible_node(NULL, NULL, "fsl,vf610-anatop"); + anatop_base = of_iomap(np, 0); + BUG_ON(!anatop_base); + + np = ccm_node; + ccm_base = of_iomap(np, 0); + BUG_ON(!ccm_base); + + clk[VF610_CLK_SLOW_CLK_SEL] = imx_clk_mux("slow_clk_sel", CCM_CCSR, 4, 1, slow_sels, ARRAY_SIZE(slow_sels)); + clk[VF610_CLK_FASK_CLK_SEL] = imx_clk_mux("fast_clk_sel", CCM_CCSR, 5, 1, fast_sels, ARRAY_SIZE(fast_sels)); + + clk[VF610_CLK_PLL1_MAIN] = imx_clk_fixed_factor("pll1_main", "fast_clk_sel", 22, 1); + clk[VF610_CLK_PLL1_PFD1] = imx_clk_pfd("pll1_pfd1", "pll1_main", PFD_PLL1_BASE, 0); + clk[VF610_CLK_PLL1_PFD2] = imx_clk_pfd("pll1_pfd2", "pll1_main", PFD_PLL1_BASE, 1); + clk[VF610_CLK_PLL1_PFD3] = imx_clk_pfd("pll1_pfd3", "pll1_main", PFD_PLL1_BASE, 2); + clk[VF610_CLK_PLL1_PFD4] = imx_clk_pfd("pll1_pfd4", "pll1_main", PFD_PLL1_BASE, 3); + + clk[VF610_CLK_PLL2_MAIN] = imx_clk_fixed_factor("pll2_main", "fast_clk_sel", 22, 1); + clk[VF610_CLK_PLL2_PFD1] = imx_clk_pfd("pll2_pfd1", "pll2_main", PFD_PLL2_BASE, 0); + clk[VF610_CLK_PLL2_PFD2] = imx_clk_pfd("pll2_pfd2", "pll2_main", PFD_PLL2_BASE, 1); + clk[VF610_CLK_PLL2_PFD3] = imx_clk_pfd("pll2_pfd3", "pll2_main", PFD_PLL2_BASE, 2); + clk[VF610_CLK_PLL2_PFD4] = imx_clk_pfd("pll2_pfd4", "pll2_main", PFD_PLL2_BASE, 3); + + clk[VF610_CLK_PLL3_MAIN] = imx_clk_fixed_factor("pll3_main", "fast_clk_sel", 20, 1); + clk[VF610_CLK_PLL3_PFD1] = imx_clk_pfd("pll3_pfd1", "pll3_main", PFD_PLL3_BASE, 0); + clk[VF610_CLK_PLL3_PFD2] = imx_clk_pfd("pll3_pfd2", "pll3_main", PFD_PLL3_BASE, 1); + clk[VF610_CLK_PLL3_PFD3] = imx_clk_pfd("pll3_pfd3", "pll3_main", PFD_PLL3_BASE, 2); + clk[VF610_CLK_PLL3_PFD4] = imx_clk_pfd("pll3_pfd4", "pll3_main", PFD_PLL3_BASE, 3); + + clk[VF610_CLK_PLL4_MAIN] = imx_clk_fixed_factor("pll4_main", "fast_clk_sel", 25, 1); + /* Enet pll: fixed 50Mhz */ + clk[VF610_CLK_PLL5_MAIN] = imx_clk_fixed_factor("pll5_main", "fast_clk_sel", 125, 6); + /* pll6: default 960Mhz */ + clk[VF610_CLK_PLL6_MAIN] = imx_clk_fixed_factor("pll6_main", "fast_clk_sel", 40, 1); + clk[VF610_CLK_PLL1_PFD_SEL] = imx_clk_mux("pll1_pfd_sel", CCM_CCSR, 16, 3, pll1_sels, 5); + clk[VF610_CLK_PLL2_PFD_SEL] = imx_clk_mux("pll2_pfd_sel", CCM_CCSR, 19, 3, pll2_sels, 5); + clk[VF610_CLK_SYS_SEL] = imx_clk_mux("sys_sel", CCM_CCSR, 0, 3, sys_sels, ARRAY_SIZE(sys_sels)); + clk[VF610_CLK_DDR_SEL] = imx_clk_mux("ddr_sel", CCM_CCSR, 6, 1, ddr_sels, ARRAY_SIZE(ddr_sels)); + clk[VF610_CLK_SYS_BUS] = imx_clk_divider("sys_bus", "sys_sel", CCM_CACRR, 0, 3); + clk[VF610_CLK_PLATFORM_BUS] = imx_clk_divider("platform_bus", "sys_bus", CCM_CACRR, 3, 3); + clk[VF610_CLK_IPG_BUS] = imx_clk_divider("ipg_bus", "platform_bus", CCM_CACRR, 11, 2); + + clk[VF610_CLK_PLL3_MAIN_DIV] = imx_clk_divider("pll3_main_div", "pll3_main", CCM_CACRR, 20, 1); + clk[VF610_CLK_PLL4_MAIN_DIV] = clk_register_divider_table(NULL, "pll4_main_div", "pll4_main", 0, CCM_CACRR, 6, 3, 0, pll4_main_div_table, &imx_ccm_lock); + clk[VF610_CLK_PLL6_MAIN_DIV] = imx_clk_divider("pll6_main_div", "pll6_main", CCM_CACRR, 21, 1); + + clk[VF610_CLK_USBC0] = imx_clk_gate2("usbc0", "pll3_main", CCM_CCGR1, CCM_CCGRx_CGn(4)); + clk[VF610_CLK_USBC1] = imx_clk_gate2("usbc1", "pll3_main", CCM_CCGR7, CCM_CCGRx_CGn(4)); + + clk[VF610_CLK_QSPI0_SEL] = imx_clk_mux("qspi0_sel", CCM_CSCMR1, 22, 2, qspi_sels, 4); + clk[VF610_CLK_QSPI0_EN] = imx_clk_gate("qspi0_en", "qspi0_sel", CCM_CSCDR3, 4); + clk[VF610_CLK_QSPI0_X4_DIV] = imx_clk_divider("qspi0_x4", "qspi0_en", CCM_CSCDR3, 0, 2); + clk[VF610_CLK_QSPI0_X2_DIV] = imx_clk_divider("qspi0_x2", "qspi0_x4", CCM_CSCDR3, 2, 1); + clk[VF610_CLK_QSPI0_X1_DIV] = imx_clk_divider("qspi0_x1", "qspi0_x2", CCM_CSCDR3, 3, 1); + clk[VF610_CLK_QSPI0] = imx_clk_gate2("qspi0", "qspi0_x1", CCM_CCGR2, CCM_CCGRx_CGn(4)); + + clk[VF610_CLK_QSPI1_SEL] = imx_clk_mux("qspi1_sel", CCM_CSCMR1, 24, 2, qspi_sels, 4); + clk[VF610_CLK_QSPI1_EN] = imx_clk_gate("qspi1_en", "qspi1_sel", CCM_CSCDR3, 12); + clk[VF610_CLK_QSPI1_X4_DIV] = imx_clk_divider("qspi1_x4", "qspi1_en", CCM_CSCDR3, 8, 2); + clk[VF610_CLK_QSPI1_X2_DIV] = imx_clk_divider("qspi1_x2", "qspi1_x4", CCM_CSCDR3, 10, 1); + clk[VF610_CLK_QSPI1_X1_DIV] = imx_clk_divider("qspi1_x1", "qspi1_x2", CCM_CSCDR3, 11, 1); + clk[VF610_CLK_QSPI1] = imx_clk_gate2("qspi1", "qspi1_x1", CCM_CCGR8, CCM_CCGRx_CGn(4)); + + clk[VF610_CLK_ENET_50M] = imx_clk_fixed_factor("enet_50m", "pll5_main", 1, 10); + clk[VF610_CLK_ENET_25M] = imx_clk_fixed_factor("enet_25m", "pll5_main", 1, 20); + clk[VF610_CLK_ENET_SEL] = imx_clk_mux("enet_sel", CCM_CSCMR2, 4, 2, rmii_sels, 4); + clk[VF610_CLK_ENET_TS_SEL] = imx_clk_mux("enet_ts_sel", CCM_CSCMR2, 0, 3, enet_ts_sels, 7); + clk[VF610_CLK_ENET] = imx_clk_gate("enet", "enet_sel", CCM_CSCDR1, 24); + clk[VF610_CLK_ENET_TS] = imx_clk_gate("enet_ts", "enet_ts_sel", CCM_CSCDR1, 23); + + clk[VF610_CLK_PIT] = imx_clk_gate2("pit", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(7)); + + clk[VF610_CLK_UART0] = imx_clk_gate2("uart0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(7)); + clk[VF610_CLK_UART1] = imx_clk_gate2("uart1", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_UART2] = imx_clk_gate2("uart2", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(9)); + clk[VF610_CLK_UART3] = imx_clk_gate2("uart3", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(10)); + + clk[VF610_CLK_I2C0] = imx_clk_gate2("i2c0", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(6)); + clk[VF610_CLK_I2C1] = imx_clk_gate2("i2c1", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(7)); + + clk[VF610_CLK_DSPI0] = imx_clk_gate2("dspi0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(12)); + clk[VF610_CLK_DSPI1] = imx_clk_gate2("dspi1", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(13)); + clk[VF610_CLK_DSPI2] = imx_clk_gate2("dspi2", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(12)); + clk[VF610_CLK_DSPI3] = imx_clk_gate2("dspi3", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(13)); + + clk[VF610_CLK_WDT] = imx_clk_gate2("wdt", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(14)); + + clk[VF610_CLK_ESDHC0_SEL] = imx_clk_mux("esdhc0_sel", CCM_CSCMR1, 16, 2, esdhc_sels, 4); + clk[VF610_CLK_ESDHC0_EN] = imx_clk_gate("esdhc0_en", "esdhc0_sel", CCM_CSCDR2, 28); + clk[VF610_CLK_ESDHC0_DIV] = imx_clk_divider("esdhc0_div", "esdhc0_en", CCM_CSCDR2, 16, 4); + clk[VF610_CLK_ESDHC0] = imx_clk_gate2("eshc0", "esdhc0_div", CCM_CCGR7, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_ESDHC1_SEL] = imx_clk_mux("esdhc1_sel", CCM_CSCMR1, 18, 2, esdhc_sels, 4); + clk[VF610_CLK_ESDHC1_EN] = imx_clk_gate("esdhc1_en", "esdhc1_sel", CCM_CSCDR2, 29); + clk[VF610_CLK_ESDHC1_DIV] = imx_clk_divider("esdhc1_div", "esdhc1_en", CCM_CSCDR2, 20, 4); + clk[VF610_CLK_ESDHC1] = imx_clk_gate2("eshc1", "esdhc1_div", CCM_CCGR7, CCM_CCGRx_CGn(2)); + + /* + * ftm_ext_clk and ftm_fix_clk are FTM timer counter's + * selectable clock sources, both use a common enable bit + * in CCM_CSCDR1, selecting "dummy" clock as parent of + * "ftm0_ext_fix" make it serve only for enable/disable. + */ + clk[VF610_CLK_FTM0_EXT_SEL] = imx_clk_mux("ftm0_ext_sel", CCM_CSCMR2, 6, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM0_FIX_SEL] = imx_clk_mux("ftm0_fix_sel", CCM_CSCMR2, 14, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM0_EXT_FIX_EN] = imx_clk_gate("ftm0_ext_fix_en", "dummy", CCM_CSCDR1, 25); + clk[VF610_CLK_FTM1_EXT_SEL] = imx_clk_mux("ftm1_ext_sel", CCM_CSCMR2, 8, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM1_FIX_SEL] = imx_clk_mux("ftm1_fix_sel", CCM_CSCMR2, 15, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM1_EXT_FIX_EN] = imx_clk_gate("ftm1_ext_fix_en", "dummy", CCM_CSCDR1, 26); + clk[VF610_CLK_FTM2_EXT_SEL] = imx_clk_mux("ftm2_ext_sel", CCM_CSCMR2, 10, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM2_FIX_SEL] = imx_clk_mux("ftm2_fix_sel", CCM_CSCMR2, 16, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM2_EXT_FIX_EN] = imx_clk_gate("ftm2_ext_fix_en", "dummy", CCM_CSCDR1, 27); + clk[VF610_CLK_FTM3_EXT_SEL] = imx_clk_mux("ftm3_ext_sel", CCM_CSCMR2, 12, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM3_FIX_SEL] = imx_clk_mux("ftm3_fix_sel", CCM_CSCMR2, 17, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM3_EXT_FIX_EN] = imx_clk_gate("ftm3_ext_fix_en", "dummy", CCM_CSCDR1, 28); + + /* ftm(n)_clk are FTM module operation clock */ + clk[VF610_CLK_FTM0] = imx_clk_gate2("ftm0", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_FTM1] = imx_clk_gate2("ftm1", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(9)); + clk[VF610_CLK_FTM2] = imx_clk_gate2("ftm2", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_FTM3] = imx_clk_gate2("ftm3", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(9)); + + clk[VF610_CLK_DCU0_SEL] = imx_clk_mux("dcu0_sel", CCM_CSCMR1, 28, 1, dcu_sels, 2); + clk[VF610_CLK_DCU0_EN] = imx_clk_gate("dcu0_en", "dcu0_sel", CCM_CSCDR3, 19); + clk[VF610_CLK_DCU0_DIV] = imx_clk_divider("dcu0_div", "dcu0_en", CCM_CSCDR3, 16, 3); + clk[VF610_CLK_DCU0] = imx_clk_gate2("dcu0", "dcu0_div", CCM_CCGR3, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_DCU1_SEL] = imx_clk_mux("dcu1_sel", CCM_CSCMR1, 29, 1, dcu_sels, 2); + clk[VF610_CLK_DCU1_EN] = imx_clk_gate("dcu1_en", "dcu1_sel", CCM_CSCDR3, 23); + clk[VF610_CLK_DCU1_DIV] = imx_clk_divider("dcu1_div", "dcu1_en", CCM_CSCDR3, 20, 3); + clk[VF610_CLK_DCU1] = imx_clk_gate2("dcu1", "dcu1_div", CCM_CCGR9, CCM_CCGRx_CGn(8)); + + clk[VF610_CLK_ESAI_SEL] = imx_clk_mux("esai_sel", CCM_CSCMR1, 20, 2, esai_sels, 4); + clk[VF610_CLK_ESAI_EN] = imx_clk_gate("esai_en", "esai_sel", CCM_CSCDR2, 30); + clk[VF610_CLK_ESAI_DIV] = imx_clk_divider("esai_div", "esai_en", CCM_CSCDR2, 24, 4); + clk[VF610_CLK_ESAI] = imx_clk_gate2("esai", "esai_div", CCM_CCGR4, CCM_CCGRx_CGn(2)); + + clk[VF610_CLK_SAI0_SEL] = imx_clk_mux("sai0_sel", CCM_CSCMR1, 0, 2, sai_sels, 4); + clk[VF610_CLK_SAI0_EN] = imx_clk_gate("sai0_en", "sai0_sel", CCM_CSCDR1, 16); + clk[VF610_CLK_SAI0_DIV] = imx_clk_divider("sai0_div", "sai0_en", CCM_CSCDR1, 0, 4); + clk[VF610_CLK_SAI0] = imx_clk_gate2("sai0", "sai0_div", CCM_CCGR0, CCM_CCGRx_CGn(15)); + + clk[VF610_CLK_SAI1_SEL] = imx_clk_mux("sai1_sel", CCM_CSCMR1, 2, 2, sai_sels, 4); + clk[VF610_CLK_SAI1_EN] = imx_clk_gate("sai1_en", "sai1_sel", CCM_CSCDR1, 17); + clk[VF610_CLK_SAI1_DIV] = imx_clk_divider("sai1_div", "sai1_en", CCM_CSCDR1, 4, 4); + clk[VF610_CLK_SAI1] = imx_clk_gate2("sai1", "sai1_div", CCM_CCGR1, CCM_CCGRx_CGn(0)); + + clk[VF610_CLK_SAI2_SEL] = imx_clk_mux("sai2_sel", CCM_CSCMR1, 4, 2, sai_sels, 4); + clk[VF610_CLK_SAI2_EN] = imx_clk_gate("sai2_en", "sai2_sel", CCM_CSCDR1, 18); + clk[VF610_CLK_SAI2_DIV] = imx_clk_divider("sai2_div", "sai2_en", CCM_CSCDR1, 8, 4); + clk[VF610_CLK_SAI2] = imx_clk_gate2("sai2", "sai2_div", CCM_CCGR1, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_SAI3_SEL] = imx_clk_mux("sai3_sel", CCM_CSCMR1, 6, 2, sai_sels, 4); + clk[VF610_CLK_SAI3_EN] = imx_clk_gate("sai3_en", "sai3_sel", CCM_CSCDR1, 19); + clk[VF610_CLK_SAI3_DIV] = imx_clk_divider("sai3_div", "sai3_en", CCM_CSCDR1, 12, 4); + clk[VF610_CLK_SAI3] = imx_clk_gate2("sai3", "sai3_div", CCM_CCGR1, CCM_CCGRx_CGn(2)); + + clk[VF610_CLK_NFC_SEL] = imx_clk_mux("nfc_sel", CCM_CSCMR1, 12, 2, nfc_sels, 4); + clk[VF610_CLK_NFC_EN] = imx_clk_gate("nfc_en", "nfc_sel", CCM_CSCDR2, 9); + clk[VF610_CLK_NFC_PRE_DIV] = imx_clk_divider("nfc_pre_div", "nfc_en", CCM_CSCDR3, 13, 3); + clk[VF610_CLK_NFC_FRAC_DIV] = imx_clk_divider("nfc_frac_div", "nfc_pre_div", CCM_CSCDR2, 4, 4); + clk[VF610_CLK_NFC] = imx_clk_gate2("nfc", "nfc_frac_div", CCM_CCGR10, CCM_CCGRx_CGn(0)); + + clk[VF610_CLK_GPU_SEL] = imx_clk_mux("gpu_sel", CCM_CSCMR1, 14, 1, gpu_sels, 2); + clk[VF610_CLK_GPU_EN] = imx_clk_gate("gpu_en", "gpu_sel", CCM_CSCDR2, 10); + clk[VF610_CLK_GPU2D] = imx_clk_gate2("gpu", "gpu_en", CCM_CCGR8, CCM_CCGRx_CGn(15)); + + clk[VF610_CLK_VADC_SEL] = imx_clk_mux("vadc_sel", CCM_CSCMR1, 8, 2, vadc_sels, 3); + clk[VF610_CLK_VADC_EN] = imx_clk_gate("vadc_en", "vadc_sel", CCM_CSCDR1, 22); + clk[VF610_CLK_VADC_DIV] = imx_clk_divider("vadc_div", "vadc_en", CCM_CSCDR1, 20, 2); + clk[VF610_CLK_VADC_DIV_HALF] = imx_clk_fixed_factor("vadc_div_half", "vadc_div", 1, 2); + clk[VF610_CLK_VADC] = imx_clk_gate2("vadc", "vadc_div", CCM_CCGR8, CCM_CCGRx_CGn(7)); + + clk[VF610_CLK_ADC0] = imx_clk_gate2("adc0", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(11)); + clk[VF610_CLK_ADC1] = imx_clk_gate2("adc1", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(11)); + clk[VF610_CLK_DAC0] = imx_clk_gate2("dac0", "ipg_bus", CCM_CCGR8, CCM_CCGRx_CGn(12)); + clk[VF610_CLK_DAC1] = imx_clk_gate2("dac1", "ipg_bus", CCM_CCGR8, CCM_CCGRx_CGn(13)); + + clk[VF610_CLK_ASRC] = imx_clk_gate2("asrc", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_FLEXCAN0] = imx_clk_gate2("flexcan0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(0)); + clk[VF610_CLK_FLEXCAN1] = imx_clk_gate2("flexcan1", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(4)); + + clk_set_parent(clk[VF610_CLK_QSPI0_SEL], clk[VF610_CLK_PLL1_PFD4]); + clk_set_rate(clk[VF610_CLK_QSPI0_X4_DIV], clk_get_rate(clk[VF610_CLK_QSPI0_SEL]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI0_X2_DIV], clk_get_rate(clk[VF610_CLK_QSPI0_X4_DIV]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI0_X1_DIV], clk_get_rate(clk[VF610_CLK_QSPI0_X2_DIV]) / 2); + + clk_set_parent(clk[VF610_CLK_QSPI1_SEL], clk[VF610_CLK_PLL1_PFD4]); + clk_set_rate(clk[VF610_CLK_QSPI1_X4_DIV], clk_get_rate(clk[VF610_CLK_QSPI1_SEL]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI1_X2_DIV], clk_get_rate(clk[VF610_CLK_QSPI1_X4_DIV]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI1_X1_DIV], clk_get_rate(clk[VF610_CLK_QSPI1_X2_DIV]) / 2); + + clk_set_parent(clk[VF610_CLK_SAI0_SEL], clk[VF610_CLK_AUDIO_EXT]); + clk_set_parent(clk[VF610_CLK_SAI1_SEL], clk[VF610_CLK_AUDIO_EXT]); + clk_set_parent(clk[VF610_CLK_SAI2_SEL], clk[VF610_CLK_AUDIO_EXT]); + clk_set_parent(clk[VF610_CLK_SAI3_SEL], clk[VF610_CLK_AUDIO_EXT]); + + /* Add the clocks to provider list */ + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} +CLK_OF_DECLARE(vf610, "fsl,vf610-ccm", vf610_clocks_init); diff --git a/include/dt-bindings/clock/vf610-clock.h b/include/dt-bindings/clock/vf610-clock.h new file mode 100644 index 000000000000..15e997fa78f2 --- /dev/null +++ b/include/dt-bindings/clock/vf610-clock.h @@ -0,0 +1,163 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * 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. + */ + +#ifndef __DT_BINDINGS_CLOCK_VF610_H +#define __DT_BINDINGS_CLOCK_VF610_H + +#define VF610_CLK_DUMMY 0 +#define VF610_CLK_SIRC_128K 1 +#define VF610_CLK_SIRC_32K 2 +#define VF610_CLK_FIRC 3 +#define VF610_CLK_SXOSC 4 +#define VF610_CLK_FXOSC 5 +#define VF610_CLK_FXOSC_HALF 6 +#define VF610_CLK_SLOW_CLK_SEL 7 +#define VF610_CLK_FASK_CLK_SEL 8 +#define VF610_CLK_AUDIO_EXT 9 +#define VF610_CLK_ENET_EXT 10 +#define VF610_CLK_PLL1_MAIN 11 +#define VF610_CLK_PLL1_PFD1 12 +#define VF610_CLK_PLL1_PFD2 13 +#define VF610_CLK_PLL1_PFD3 14 +#define VF610_CLK_PLL1_PFD4 15 +#define VF610_CLK_PLL2_MAIN 16 +#define VF610_CLK_PLL2_PFD1 17 +#define VF610_CLK_PLL2_PFD2 18 +#define VF610_CLK_PLL2_PFD3 19 +#define VF610_CLK_PLL2_PFD4 20 +#define VF610_CLK_PLL3_MAIN 21 +#define VF610_CLK_PLL3_PFD1 22 +#define VF610_CLK_PLL3_PFD2 23 +#define VF610_CLK_PLL3_PFD3 24 +#define VF610_CLK_PLL3_PFD4 25 +#define VF610_CLK_PLL4_MAIN 26 +#define VF610_CLK_PLL5_MAIN 27 +#define VF610_CLK_PLL6_MAIN 28 +#define VF610_CLK_PLL3_MAIN_DIV 29 +#define VF610_CLK_PLL4_MAIN_DIV 30 +#define VF610_CLK_PLL6_MAIN_DIV 31 +#define VF610_CLK_PLL1_PFD_SEL 32 +#define VF610_CLK_PLL2_PFD_SEL 33 +#define VF610_CLK_SYS_SEL 34 +#define VF610_CLK_DDR_SEL 35 +#define VF610_CLK_SYS_BUS 36 +#define VF610_CLK_PLATFORM_BUS 37 +#define VF610_CLK_IPG_BUS 38 +#define VF610_CLK_UART0 39 +#define VF610_CLK_UART1 40 +#define VF610_CLK_UART2 41 +#define VF610_CLK_UART3 42 +#define VF610_CLK_UART4 43 +#define VF610_CLK_UART5 44 +#define VF610_CLK_PIT 45 +#define VF610_CLK_I2C0 46 +#define VF610_CLK_I2C1 47 +#define VF610_CLK_I2C2 48 +#define VF610_CLK_I2C3 49 +#define VF610_CLK_FTM0_EXT_SEL 50 +#define VF610_CLK_FTM0_FIX_SEL 51 +#define VF610_CLK_FTM0_EXT_FIX_EN 52 +#define VF610_CLK_FTM1_EXT_SEL 53 +#define VF610_CLK_FTM1_FIX_SEL 54 +#define VF610_CLK_FTM1_EXT_FIX_EN 55 +#define VF610_CLK_FTM2_EXT_SEL 56 +#define VF610_CLK_FTM2_FIX_SEL 57 +#define VF610_CLK_FTM2_EXT_FIX_EN 58 +#define VF610_CLK_FTM3_EXT_SEL 59 +#define VF610_CLK_FTM3_FIX_SEL 60 +#define VF610_CLK_FTM3_EXT_FIX_EN 61 +#define VF610_CLK_FTM0 62 +#define VF610_CLK_FTM1 63 +#define VF610_CLK_FTM2 64 +#define VF610_CLK_FTM3 65 +#define VF610_CLK_ENET_50M 66 +#define VF610_CLK_ENET_25M 67 +#define VF610_CLK_ENET_SEL 68 +#define VF610_CLK_ENET 69 +#define VF610_CLK_ENET_TS_SEL 70 +#define VF610_CLK_ENET_TS 71 +#define VF610_CLK_DSPI0 72 +#define VF610_CLK_DSPI1 73 +#define VF610_CLK_DSPI2 74 +#define VF610_CLK_DSPI3 75 +#define VF610_CLK_WDT 76 +#define VF610_CLK_ESDHC0_SEL 77 +#define VF610_CLK_ESDHC0_EN 78 +#define VF610_CLK_ESDHC0_DIV 79 +#define VF610_CLK_ESDHC0 80 +#define VF610_CLK_ESDHC1_SEL 81 +#define VF610_CLK_ESDHC1_EN 82 +#define VF610_CLK_ESDHC1_DIV 83 +#define VF610_CLK_ESDHC1 84 +#define VF610_CLK_DCU0_SEL 85 +#define VF610_CLK_DCU0_EN 86 +#define VF610_CLK_DCU0_DIV 87 +#define VF610_CLK_DCU0 88 +#define VF610_CLK_DCU1_SEL 89 +#define VF610_CLK_DCU1_EN 90 +#define VF610_CLK_DCU1_DIV 91 +#define VF610_CLK_DCU1 92 +#define VF610_CLK_ESAI_SEL 93 +#define VF610_CLK_ESAI_EN 94 +#define VF610_CLK_ESAI_DIV 95 +#define VF610_CLK_ESAI 96 +#define VF610_CLK_SAI0_SEL 97 +#define VF610_CLK_SAI0_EN 98 +#define VF610_CLK_SAI0_DIV 99 +#define VF610_CLK_SAI0 100 +#define VF610_CLK_SAI1_SEL 101 +#define VF610_CLK_SAI1_EN 102 +#define VF610_CLK_SAI1_DIV 103 +#define VF610_CLK_SAI1 104 +#define VF610_CLK_SAI2_SEL 105 +#define VF610_CLK_SAI2_EN 106 +#define VF610_CLK_SAI2_DIV 107 +#define VF610_CLK_SAI2 108 +#define VF610_CLK_SAI3_SEL 109 +#define VF610_CLK_SAI3_EN 110 +#define VF610_CLK_SAI3_DIV 111 +#define VF610_CLK_SAI3 112 +#define VF610_CLK_USBC0 113 +#define VF610_CLK_USBC1 114 +#define VF610_CLK_QSPI0_SEL 115 +#define VF610_CLK_QSPI0_EN 116 +#define VF610_CLK_QSPI0_X4_DIV 117 +#define VF610_CLK_QSPI0_X2_DIV 118 +#define VF610_CLK_QSPI0_X1_DIV 119 +#define VF610_CLK_QSPI1_SEL 120 +#define VF610_CLK_QSPI1_EN 121 +#define VF610_CLK_QSPI1_X4_DIV 122 +#define VF610_CLK_QSPI1_X2_DIV 123 +#define VF610_CLK_QSPI1_X1_DIV 124 +#define VF610_CLK_QSPI0 125 +#define VF610_CLK_QSPI1 126 +#define VF610_CLK_NFC_SEL 127 +#define VF610_CLK_NFC_EN 128 +#define VF610_CLK_NFC_PRE_DIV 129 +#define VF610_CLK_NFC_FRAC_DIV 130 +#define VF610_CLK_NFC_INV 131 +#define VF610_CLK_NFC 132 +#define VF610_CLK_VADC_SEL 133 +#define VF610_CLK_VADC_EN 134 +#define VF610_CLK_VADC_DIV 135 +#define VF610_CLK_VADC_DIV_HALF 136 +#define VF610_CLK_VADC 137 +#define VF610_CLK_ADC0 138 +#define VF610_CLK_ADC1 139 +#define VF610_CLK_DAC0 140 +#define VF610_CLK_DAC1 141 +#define VF610_CLK_FLEXCAN0 142 +#define VF610_CLK_FLEXCAN1 143 +#define VF610_CLK_ASRC 144 +#define VF610_CLK_GPU_SEL 145 +#define VF610_CLK_GPU_EN 146 +#define VF610_CLK_GPU2D 147 +#define VF610_CLK_END 148 + +#endif /* __DT_BINDINGS_CLOCK_VF610_H */ -- cgit v1.2.3 From b263e9b88746c6902c36d35a2dd83c119f8510a2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 23 May 2013 20:09:43 +0200 Subject: pinctrl: get rid of all platform data for coh901 This deletes the dependency on any platform data for the COH901 pin controller. There is only one user in the kernel, and if we at some point want to support more variants, they shall provide their variant info through the device tree. Signed-off-by: Linus Walleij --- arch/arm/mach-u300/core.c | 12 +----------- drivers/pinctrl/pinctrl-coh901.c | 23 +++++++++-------------- include/linux/platform_data/pinctrl-coh901.h | 22 ---------------------- 3 files changed, 10 insertions(+), 47 deletions(-) delete mode 100644 include/linux/platform_data/pinctrl-coh901.h (limited to 'include') diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index 6e5744a2b85c..4f7ac2a11452 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -194,15 +193,6 @@ static void __init u300_map_io(void) iotable_init(u300_io_desc, ARRAY_SIZE(u300_io_desc)); } -/* - * The different variants have a few different versions of the - * GPIO block, with different number of ports. - */ -static struct u300_gpio_platform u300_gpio_plat = { - .ports = 7, - .gpio_base = 0, -}; - static unsigned long pin_pullup_conf[] = { PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_UP, 1), }; @@ -332,7 +322,7 @@ static struct of_dev_auxdata u300_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("stericsson,pinctrl-u300", U300_SYSCON_BASE, "pinctrl-u300", NULL), OF_DEV_AUXDATA("stericsson,gpio-coh901", U300_GPIO_BASE, - "u300-gpio", &u300_gpio_plat), + "u300-gpio", NULL), OF_DEV_AUXDATA("stericsson,coh901327", U300_WDOG_BASE, "coh901327_wdog", NULL), OF_DEV_AUXDATA("stericsson,coh901331", U300_RTC_BASE, diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c index d4f8afdad1ad..46ff140cfa76 100644 --- a/drivers/pinctrl/pinctrl-coh901.c +++ b/drivers/pinctrl/pinctrl-coh901.c @@ -22,7 +22,6 @@ #include #include #include -#include #include "pinctrl-coh901.h" #define U300_GPIO_PORT_STRIDE (0x30) @@ -58,8 +57,9 @@ #define U300_GPIO_PXICR_IRQ_CONFIG_RISING_EDGE (0x00000001UL) /* 8 bits per port, no version has more than 7 ports */ +#define U300_GPIO_NUM_PORTS 7 #define U300_GPIO_PINS_PER_PORT 8 -#define U300_GPIO_MAX (U300_GPIO_PINS_PER_PORT * 7) +#define U300_GPIO_MAX (U300_GPIO_PINS_PER_PORT * U300_GPIO_NUM_PORTS) struct u300_gpio { struct gpio_chip chip; @@ -111,9 +111,6 @@ struct u300_gpio_confdata { int outval; }; -/* BS335 has seven ports of 8 bits each = GPIO pins 0..55 */ -#define BS335_GPIO_NUM_PORTS 7 - #define U300_FLOATING_INPUT { \ .bias_mode = PIN_CONFIG_BIAS_HIGH_IMPEDANCE, \ .output = false, \ @@ -136,7 +133,7 @@ struct u300_gpio_confdata { /* Initial configuration */ static const struct __initconst u300_gpio_confdata -bs335_gpio_config[BS335_GPIO_NUM_PORTS][U300_GPIO_PINS_PER_PORT] = { +bs335_gpio_config[U300_GPIO_NUM_PORTS][U300_GPIO_PINS_PER_PORT] = { /* Port 0, pins 0-7 */ { U300_FLOATING_INPUT, @@ -630,13 +627,12 @@ static void __init u300_gpio_init_pin(struct u300_gpio *gpio, } } -static void __init u300_gpio_init_coh901571(struct u300_gpio *gpio, - struct u300_gpio_platform *plat) +static void __init u300_gpio_init_coh901571(struct u300_gpio *gpio) { int i, j; /* Write default config and values to all pins */ - for (i = 0; i < plat->ports; i++) { + for (i = 0; i < U300_GPIO_NUM_PORTS; i++) { for (j = 0; j < 8; j++) { const struct u300_gpio_confdata *conf; int offset = (i*8) + j; @@ -693,7 +689,6 @@ static struct coh901_pinpair coh901_pintable[] = { static int __init u300_gpio_probe(struct platform_device *pdev) { - struct u300_gpio_platform *plat = dev_get_platdata(&pdev->dev); struct u300_gpio *gpio; struct resource *memres; int err = 0; @@ -707,9 +702,9 @@ static int __init u300_gpio_probe(struct platform_device *pdev) return -ENOMEM; gpio->chip = u300_gpio_chip; - gpio->chip.ngpio = plat->ports * U300_GPIO_PINS_PER_PORT; + gpio->chip.ngpio = U300_GPIO_NUM_PORTS * U300_GPIO_PINS_PER_PORT; gpio->chip.dev = &pdev->dev; - gpio->chip.base = plat->gpio_base; + gpio->chip.base = 0; gpio->dev = &pdev->dev; memres = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -755,11 +750,11 @@ static int __init u300_gpio_probe(struct platform_device *pdev) ((val & 0x0000FE00) >> 9) * 8); writel(U300_GPIO_CR_BLOCK_CLKRQ_ENABLE, gpio->base + U300_GPIO_CR); - u300_gpio_init_coh901571(gpio, plat); + u300_gpio_init_coh901571(gpio); /* Add each port with its IRQ separately */ INIT_LIST_HEAD(&gpio->port_list); - for (portno = 0 ; portno < plat->ports; portno++) { + for (portno = 0 ; portno < U300_GPIO_NUM_PORTS; portno++) { struct u300_gpio_port *port = kmalloc(sizeof(struct u300_gpio_port), GFP_KERNEL); diff --git a/include/linux/platform_data/pinctrl-coh901.h b/include/linux/platform_data/pinctrl-coh901.h deleted file mode 100644 index dfbc65d10484..000000000000 --- a/include/linux/platform_data/pinctrl-coh901.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2007-2012 ST-Ericsson AB - * License terms: GNU General Public License (GPL) version 2 - * GPIO block resgister definitions and inline macros for - * U300 GPIO COH 901 335 or COH 901 571/3 - * Author: Linus Walleij - */ - -#ifndef __MACH_U300_GPIO_U300_H -#define __MACH_U300_GPIO_U300_H - -/** - * struct u300_gpio_platform - U300 GPIO platform data - * @ports: number of GPIO block ports - * @gpio_base: first GPIO number for this block (use a free range) - */ -struct u300_gpio_platform { - u8 ports; - int gpio_base; -}; - -#endif /* __MACH_U300_GPIO_U300_H */ -- cgit v1.2.3 From 3ad7a42d5a9c3736cd6d2c6f7e6038d0ca8b316c Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Wed, 6 Mar 2013 11:15:31 -0500 Subject: ARM: davinci: move private EDMA API to arm/common Move mach-davinci/dma.c to common/edma.c so it can be used by OMAP (specifically AM33xx) as well. Signed-off-by: Matt Porter Acked-by: Chris Ball # davinci_mmc.c Acked-by: Mark Brown Acked-by: Olof Johansson [nsekhar@ti.com: dropped davinci sffsdr changes] Signed-off-by: Sekhar Nori --- arch/arm/Kconfig | 1 + arch/arm/common/Kconfig | 3 + arch/arm/common/Makefile | 1 + arch/arm/common/edma.c | 1591 +++++++++++++++++++++++++++ arch/arm/mach-davinci/Makefile | 2 +- arch/arm/mach-davinci/board-tnetv107x-evm.c | 2 +- arch/arm/mach-davinci/davinci.h | 2 +- arch/arm/mach-davinci/devices-tnetv107x.c | 2 +- arch/arm/mach-davinci/devices.c | 6 +- arch/arm/mach-davinci/dm355.c | 2 +- arch/arm/mach-davinci/dm365.c | 2 +- arch/arm/mach-davinci/dm644x.c | 2 +- arch/arm/mach-davinci/dm646x.c | 2 +- arch/arm/mach-davinci/dma.c | 1591 --------------------------- arch/arm/mach-davinci/include/mach/da8xx.h | 2 +- arch/arm/mach-davinci/include/mach/edma.h | 267 ----- drivers/dma/edma.c | 2 +- drivers/mmc/host/davinci_mmc.c | 1 + include/linux/mfd/davinci_voicecodec.h | 3 +- include/linux/platform_data/edma.h | 182 +++ include/linux/platform_data/spi-davinci.h | 2 +- sound/soc/davinci/davinci-evm.c | 1 + sound/soc/davinci/davinci-pcm.c | 1 + sound/soc/davinci/davinci-pcm.h | 2 +- 24 files changed, 1799 insertions(+), 1873 deletions(-) create mode 100644 arch/arm/common/edma.c delete mode 100644 arch/arm/mach-davinci/dma.c delete mode 100644 arch/arm/mach-davinci/include/mach/edma.h create mode 100644 include/linux/platform_data/edma.h (limited to 'include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 49d993cee512..b1c66a48a454 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -840,6 +840,7 @@ config ARCH_DAVINCI select GENERIC_IRQ_CHIP select HAVE_IDE select NEED_MACH_GPIO_H + select TI_PRIV_EDMA select USE_OF select ZONE_DMA help diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index 9353184d730d..c3a4e9ceba34 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -17,3 +17,6 @@ config SHARP_PARAM config SHARP_SCOOP bool + +config TI_PRIV_EDMA + bool diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 48434cbe3e89..8c60f473e976 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o obj-$(CONFIG_MCPM) += mcpm_head.o mcpm_entry.o mcpm_platsmp.o vlock.o AFLAGS_mcpm_head.o := -march=armv7-a AFLAGS_vlock.o := -march=armv7-a +obj-$(CONFIG_TI_PRIV_EDMA) += edma.o diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c new file mode 100644 index 000000000000..dcaeb8ec5d38 --- /dev/null +++ b/arch/arm/common/edma.c @@ -0,0 +1,1591 @@ +/* + * EDMA3 support for DaVinci + * + * Copyright (C) 2006-2009 Texas Instruments. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Offsets matching "struct edmacc_param" */ +#define PARM_OPT 0x00 +#define PARM_SRC 0x04 +#define PARM_A_B_CNT 0x08 +#define PARM_DST 0x0c +#define PARM_SRC_DST_BIDX 0x10 +#define PARM_LINK_BCNTRLD 0x14 +#define PARM_SRC_DST_CIDX 0x18 +#define PARM_CCNT 0x1c + +#define PARM_SIZE 0x20 + +/* Offsets for EDMA CC global channel registers and their shadows */ +#define SH_ER 0x00 /* 64 bits */ +#define SH_ECR 0x08 /* 64 bits */ +#define SH_ESR 0x10 /* 64 bits */ +#define SH_CER 0x18 /* 64 bits */ +#define SH_EER 0x20 /* 64 bits */ +#define SH_EECR 0x28 /* 64 bits */ +#define SH_EESR 0x30 /* 64 bits */ +#define SH_SER 0x38 /* 64 bits */ +#define SH_SECR 0x40 /* 64 bits */ +#define SH_IER 0x50 /* 64 bits */ +#define SH_IECR 0x58 /* 64 bits */ +#define SH_IESR 0x60 /* 64 bits */ +#define SH_IPR 0x68 /* 64 bits */ +#define SH_ICR 0x70 /* 64 bits */ +#define SH_IEVAL 0x78 +#define SH_QER 0x80 +#define SH_QEER 0x84 +#define SH_QEECR 0x88 +#define SH_QEESR 0x8c +#define SH_QSER 0x90 +#define SH_QSECR 0x94 +#define SH_SIZE 0x200 + +/* Offsets for EDMA CC global registers */ +#define EDMA_REV 0x0000 +#define EDMA_CCCFG 0x0004 +#define EDMA_QCHMAP 0x0200 /* 8 registers */ +#define EDMA_DMAQNUM 0x0240 /* 8 registers (4 on OMAP-L1xx) */ +#define EDMA_QDMAQNUM 0x0260 +#define EDMA_QUETCMAP 0x0280 +#define EDMA_QUEPRI 0x0284 +#define EDMA_EMR 0x0300 /* 64 bits */ +#define EDMA_EMCR 0x0308 /* 64 bits */ +#define EDMA_QEMR 0x0310 +#define EDMA_QEMCR 0x0314 +#define EDMA_CCERR 0x0318 +#define EDMA_CCERRCLR 0x031c +#define EDMA_EEVAL 0x0320 +#define EDMA_DRAE 0x0340 /* 4 x 64 bits*/ +#define EDMA_QRAE 0x0380 /* 4 registers */ +#define EDMA_QUEEVTENTRY 0x0400 /* 2 x 16 registers */ +#define EDMA_QSTAT 0x0600 /* 2 registers */ +#define EDMA_QWMTHRA 0x0620 +#define EDMA_QWMTHRB 0x0624 +#define EDMA_CCSTAT 0x0640 + +#define EDMA_M 0x1000 /* global channel registers */ +#define EDMA_ECR 0x1008 +#define EDMA_ECRH 0x100C +#define EDMA_SHADOW0 0x2000 /* 4 regions shadowing global channels */ +#define EDMA_PARM 0x4000 /* 128 param entries */ + +#define PARM_OFFSET(param_no) (EDMA_PARM + ((param_no) << 5)) + +#define EDMA_DCHMAP 0x0100 /* 64 registers */ +#define CHMAP_EXIST BIT(24) + +#define EDMA_MAX_DMACH 64 +#define EDMA_MAX_PARAMENTRY 512 + +/*****************************************************************************/ + +static void __iomem *edmacc_regs_base[EDMA_MAX_CC]; + +static inline unsigned int edma_read(unsigned ctlr, int offset) +{ + return (unsigned int)__raw_readl(edmacc_regs_base[ctlr] + offset); +} + +static inline void edma_write(unsigned ctlr, int offset, int val) +{ + __raw_writel(val, edmacc_regs_base[ctlr] + offset); +} +static inline void edma_modify(unsigned ctlr, int offset, unsigned and, + unsigned or) +{ + unsigned val = edma_read(ctlr, offset); + val &= and; + val |= or; + edma_write(ctlr, offset, val); +} +static inline void edma_and(unsigned ctlr, int offset, unsigned and) +{ + unsigned val = edma_read(ctlr, offset); + val &= and; + edma_write(ctlr, offset, val); +} +static inline void edma_or(unsigned ctlr, int offset, unsigned or) +{ + unsigned val = edma_read(ctlr, offset); + val |= or; + edma_write(ctlr, offset, val); +} +static inline unsigned int edma_read_array(unsigned ctlr, int offset, int i) +{ + return edma_read(ctlr, offset + (i << 2)); +} +static inline void edma_write_array(unsigned ctlr, int offset, int i, + unsigned val) +{ + edma_write(ctlr, offset + (i << 2), val); +} +static inline void edma_modify_array(unsigned ctlr, int offset, int i, + unsigned and, unsigned or) +{ + edma_modify(ctlr, offset + (i << 2), and, or); +} +static inline void edma_or_array(unsigned ctlr, int offset, int i, unsigned or) +{ + edma_or(ctlr, offset + (i << 2), or); +} +static inline void edma_or_array2(unsigned ctlr, int offset, int i, int j, + unsigned or) +{ + edma_or(ctlr, offset + ((i*2 + j) << 2), or); +} +static inline void edma_write_array2(unsigned ctlr, int offset, int i, int j, + unsigned val) +{ + edma_write(ctlr, offset + ((i*2 + j) << 2), val); +} +static inline unsigned int edma_shadow0_read(unsigned ctlr, int offset) +{ + return edma_read(ctlr, EDMA_SHADOW0 + offset); +} +static inline unsigned int edma_shadow0_read_array(unsigned ctlr, int offset, + int i) +{ + return edma_read(ctlr, EDMA_SHADOW0 + offset + (i << 2)); +} +static inline void edma_shadow0_write(unsigned ctlr, int offset, unsigned val) +{ + edma_write(ctlr, EDMA_SHADOW0 + offset, val); +} +static inline void edma_shadow0_write_array(unsigned ctlr, int offset, int i, + unsigned val) +{ + edma_write(ctlr, EDMA_SHADOW0 + offset + (i << 2), val); +} +static inline unsigned int edma_parm_read(unsigned ctlr, int offset, + int param_no) +{ + return edma_read(ctlr, EDMA_PARM + offset + (param_no << 5)); +} +static inline void edma_parm_write(unsigned ctlr, int offset, int param_no, + unsigned val) +{ + edma_write(ctlr, EDMA_PARM + offset + (param_no << 5), val); +} +static inline void edma_parm_modify(unsigned ctlr, int offset, int param_no, + unsigned and, unsigned or) +{ + edma_modify(ctlr, EDMA_PARM + offset + (param_no << 5), and, or); +} +static inline void edma_parm_and(unsigned ctlr, int offset, int param_no, + unsigned and) +{ + edma_and(ctlr, EDMA_PARM + offset + (param_no << 5), and); +} +static inline void edma_parm_or(unsigned ctlr, int offset, int param_no, + unsigned or) +{ + edma_or(ctlr, EDMA_PARM + offset + (param_no << 5), or); +} + +static inline void set_bits(int offset, int len, unsigned long *p) +{ + for (; len > 0; len--) + set_bit(offset + (len - 1), p); +} + +static inline void clear_bits(int offset, int len, unsigned long *p) +{ + for (; len > 0; len--) + clear_bit(offset + (len - 1), p); +} + +/*****************************************************************************/ + +/* actual number of DMA channels and slots on this silicon */ +struct edma { + /* how many dma resources of each type */ + unsigned num_channels; + unsigned num_region; + unsigned num_slots; + unsigned num_tc; + unsigned num_cc; + enum dma_event_q default_queue; + + /* list of channels with no even trigger; terminated by "-1" */ + const s8 *noevent; + + /* The edma_inuse bit for each PaRAM slot is clear unless the + * channel is in use ... by ARM or DSP, for QDMA, or whatever. + */ + DECLARE_BITMAP(edma_inuse, EDMA_MAX_PARAMENTRY); + + /* The edma_unused bit for each channel is clear unless + * it is not being used on this platform. It uses a bit + * of SOC-specific initialization code. + */ + DECLARE_BITMAP(edma_unused, EDMA_MAX_DMACH); + + unsigned irq_res_start; + unsigned irq_res_end; + + struct dma_interrupt_data { + void (*callback)(unsigned channel, unsigned short ch_status, + void *data); + void *data; + } intr_data[EDMA_MAX_DMACH]; +}; + +static struct edma *edma_cc[EDMA_MAX_CC]; +static int arch_num_cc; + +/* dummy param set used to (re)initialize parameter RAM slots */ +static const struct edmacc_param dummy_paramset = { + .link_bcntrld = 0xffff, + .ccnt = 1, +}; + +/*****************************************************************************/ + +static void map_dmach_queue(unsigned ctlr, unsigned ch_no, + enum dma_event_q queue_no) +{ + int bit = (ch_no & 0x7) * 4; + + /* default to low priority queue */ + if (queue_no == EVENTQ_DEFAULT) + queue_no = edma_cc[ctlr]->default_queue; + + queue_no &= 7; + edma_modify_array(ctlr, EDMA_DMAQNUM, (ch_no >> 3), + ~(0x7 << bit), queue_no << bit); +} + +static void __init map_queue_tc(unsigned ctlr, int queue_no, int tc_no) +{ + int bit = queue_no * 4; + edma_modify(ctlr, EDMA_QUETCMAP, ~(0x7 << bit), ((tc_no & 0x7) << bit)); +} + +static void __init assign_priority_to_queue(unsigned ctlr, int queue_no, + int priority) +{ + int bit = queue_no * 4; + edma_modify(ctlr, EDMA_QUEPRI, ~(0x7 << bit), + ((priority & 0x7) << bit)); +} + +/** + * map_dmach_param - Maps channel number to param entry number + * + * This maps the dma channel number to param entry numberter. In + * other words using the DMA channel mapping registers a param entry + * can be mapped to any channel + * + * Callers are responsible for ensuring the channel mapping logic is + * included in that particular EDMA variant (Eg : dm646x) + * + */ +static void __init map_dmach_param(unsigned ctlr) +{ + int i; + for (i = 0; i < EDMA_MAX_DMACH; i++) + edma_write_array(ctlr, EDMA_DCHMAP , i , (i << 5)); +} + +static inline void +setup_dma_interrupt(unsigned lch, + void (*callback)(unsigned channel, u16 ch_status, void *data), + void *data) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(lch); + lch = EDMA_CHAN_SLOT(lch); + + if (!callback) + edma_shadow0_write_array(ctlr, SH_IECR, lch >> 5, + BIT(lch & 0x1f)); + + edma_cc[ctlr]->intr_data[lch].callback = callback; + edma_cc[ctlr]->intr_data[lch].data = data; + + if (callback) { + edma_shadow0_write_array(ctlr, SH_ICR, lch >> 5, + BIT(lch & 0x1f)); + edma_shadow0_write_array(ctlr, SH_IESR, lch >> 5, + BIT(lch & 0x1f)); + } +} + +static int irq2ctlr(int irq) +{ + if (irq >= edma_cc[0]->irq_res_start && irq <= edma_cc[0]->irq_res_end) + return 0; + else if (irq >= edma_cc[1]->irq_res_start && + irq <= edma_cc[1]->irq_res_end) + return 1; + + return -1; +} + +/****************************************************************************** + * + * DMA interrupt handler + * + *****************************************************************************/ +static irqreturn_t dma_irq_handler(int irq, void *data) +{ + int ctlr; + u32 sh_ier; + u32 sh_ipr; + u32 bank; + + ctlr = irq2ctlr(irq); + if (ctlr < 0) + return IRQ_NONE; + + dev_dbg(data, "dma_irq_handler\n"); + + sh_ipr = edma_shadow0_read_array(ctlr, SH_IPR, 0); + if (!sh_ipr) { + sh_ipr = edma_shadow0_read_array(ctlr, SH_IPR, 1); + if (!sh_ipr) + return IRQ_NONE; + sh_ier = edma_shadow0_read_array(ctlr, SH_IER, 1); + bank = 1; + } else { + sh_ier = edma_shadow0_read_array(ctlr, SH_IER, 0); + bank = 0; + } + + do { + u32 slot; + u32 channel; + + dev_dbg(data, "IPR%d %08x\n", bank, sh_ipr); + + slot = __ffs(sh_ipr); + sh_ipr &= ~(BIT(slot)); + + if (sh_ier & BIT(slot)) { + channel = (bank << 5) | slot; + /* Clear the corresponding IPR bits */ + edma_shadow0_write_array(ctlr, SH_ICR, bank, + BIT(slot)); + if (edma_cc[ctlr]->intr_data[channel].callback) + edma_cc[ctlr]->intr_data[channel].callback( + channel, DMA_COMPLETE, + edma_cc[ctlr]->intr_data[channel].data); + } + } while (sh_ipr); + + edma_shadow0_write(ctlr, SH_IEVAL, 1); + return IRQ_HANDLED; +} + +/****************************************************************************** + * + * DMA error interrupt handler + * + *****************************************************************************/ +static irqreturn_t dma_ccerr_handler(int irq, void *data) +{ + int i; + int ctlr; + unsigned int cnt = 0; + + ctlr = irq2ctlr(irq); + if (ctlr < 0) + return IRQ_NONE; + + dev_dbg(data, "dma_ccerr_handler\n"); + + if ((edma_read_array(ctlr, EDMA_EMR, 0) == 0) && + (edma_read_array(ctlr, EDMA_EMR, 1) == 0) && + (edma_read(ctlr, EDMA_QEMR) == 0) && + (edma_read(ctlr, EDMA_CCERR) == 0)) + return IRQ_NONE; + + while (1) { + int j = -1; + if (edma_read_array(ctlr, EDMA_EMR, 0)) + j = 0; + else if (edma_read_array(ctlr, EDMA_EMR, 1)) + j = 1; + if (j >= 0) { + dev_dbg(data, "EMR%d %08x\n", j, + edma_read_array(ctlr, EDMA_EMR, j)); + for (i = 0; i < 32; i++) { + int k = (j << 5) + i; + if (edma_read_array(ctlr, EDMA_EMR, j) & + BIT(i)) { + /* Clear the corresponding EMR bits */ + edma_write_array(ctlr, EDMA_EMCR, j, + BIT(i)); + /* Clear any SER */ + edma_shadow0_write_array(ctlr, SH_SECR, + j, BIT(i)); + if (edma_cc[ctlr]->intr_data[k]. + callback) { + edma_cc[ctlr]->intr_data[k]. + callback(k, + DMA_CC_ERROR, + edma_cc[ctlr]->intr_data + [k].data); + } + } + } + } else if (edma_read(ctlr, EDMA_QEMR)) { + dev_dbg(data, "QEMR %02x\n", + edma_read(ctlr, EDMA_QEMR)); + for (i = 0; i < 8; i++) { + if (edma_read(ctlr, EDMA_QEMR) & BIT(i)) { + /* Clear the corresponding IPR bits */ + edma_write(ctlr, EDMA_QEMCR, BIT(i)); + edma_shadow0_write(ctlr, SH_QSECR, + BIT(i)); + + /* NOTE: not reported!! */ + } + } + } else if (edma_read(ctlr, EDMA_CCERR)) { + dev_dbg(data, "CCERR %08x\n", + edma_read(ctlr, EDMA_CCERR)); + /* FIXME: CCERR.BIT(16) ignored! much better + * to just write CCERRCLR with CCERR value... + */ + for (i = 0; i < 8; i++) { + if (edma_read(ctlr, EDMA_CCERR) & BIT(i)) { + /* Clear the corresponding IPR bits */ + edma_write(ctlr, EDMA_CCERRCLR, BIT(i)); + + /* NOTE: not reported!! */ + } + } + } + if ((edma_read_array(ctlr, EDMA_EMR, 0) == 0) && + (edma_read_array(ctlr, EDMA_EMR, 1) == 0) && + (edma_read(ctlr, EDMA_QEMR) == 0) && + (edma_read(ctlr, EDMA_CCERR) == 0)) + break; + cnt++; + if (cnt > 10) + break; + } + edma_write(ctlr, EDMA_EEVAL, 1); + return IRQ_HANDLED; +} + +/****************************************************************************** + * + * Transfer controller error interrupt handlers + * + *****************************************************************************/ + +#define tc_errs_handled false /* disabled as long as they're NOPs */ + +static irqreturn_t dma_tc0err_handler(int irq, void *data) +{ + dev_dbg(data, "dma_tc0err_handler\n"); + return IRQ_HANDLED; +} + +static irqreturn_t dma_tc1err_handler(int irq, void *data) +{ + dev_dbg(data, "dma_tc1err_handler\n"); + return IRQ_HANDLED; +} + +static int reserve_contiguous_slots(int ctlr, unsigned int id, + unsigned int num_slots, + unsigned int start_slot) +{ + int i, j; + unsigned int count = num_slots; + int stop_slot = start_slot; + DECLARE_BITMAP(tmp_inuse, EDMA_MAX_PARAMENTRY); + + for (i = start_slot; i < edma_cc[ctlr]->num_slots; ++i) { + j = EDMA_CHAN_SLOT(i); + if (!test_and_set_bit(j, edma_cc[ctlr]->edma_inuse)) { + /* Record our current beginning slot */ + if (count == num_slots) + stop_slot = i; + + count--; + set_bit(j, tmp_inuse); + + if (count == 0) + break; + } else { + clear_bit(j, tmp_inuse); + + if (id == EDMA_CONT_PARAMS_FIXED_EXACT) { + stop_slot = i; + break; + } else { + count = num_slots; + } + } + } + + /* + * We have to clear any bits that we set + * if we run out parameter RAM slots, i.e we do find a set + * of contiguous parameter RAM slots but do not find the exact number + * requested as we may reach the total number of parameter RAM slots + */ + if (i == edma_cc[ctlr]->num_slots) + stop_slot = i; + + j = start_slot; + for_each_set_bit_from(j, tmp_inuse, stop_slot) + clear_bit(j, edma_cc[ctlr]->edma_inuse); + + if (count) + return -EBUSY; + + for (j = i - num_slots + 1; j <= i; ++j) + memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(j), + &dummy_paramset, PARM_SIZE); + + return EDMA_CTLR_CHAN(ctlr, i - num_slots + 1); +} + +static int prepare_unused_channel_list(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + int i, ctlr; + + for (i = 0; i < pdev->num_resources; i++) { + if ((pdev->resource[i].flags & IORESOURCE_DMA) && + (int)pdev->resource[i].start >= 0) { + ctlr = EDMA_CTLR(pdev->resource[i].start); + clear_bit(EDMA_CHAN_SLOT(pdev->resource[i].start), + edma_cc[ctlr]->edma_unused); + } + } + + return 0; +} + +/*-----------------------------------------------------------------------*/ + +static bool unused_chan_list_done; + +/* Resource alloc/free: dma channels, parameter RAM slots */ + +/** + * edma_alloc_channel - allocate DMA channel and paired parameter RAM + * @channel: specific channel to allocate; negative for "any unmapped channel" + * @callback: optional; to be issued on DMA completion or errors + * @data: passed to callback + * @eventq_no: an EVENTQ_* constant, used to choose which Transfer + * Controller (TC) executes requests using this channel. Use + * EVENTQ_DEFAULT unless you really need a high priority queue. + * + * This allocates a DMA channel and its associated parameter RAM slot. + * The parameter RAM is initialized to hold a dummy transfer. + * + * Normal use is to pass a specific channel number as @channel, to make + * use of hardware events mapped to that channel. When the channel will + * be used only for software triggering or event chaining, channels not + * mapped to hardware events (or mapped to unused events) are preferable. + * + * DMA transfers start from a channel using edma_start(), or by + * chaining. When the transfer described in that channel's parameter RAM + * slot completes, that slot's data may be reloaded through a link. + * + * DMA errors are only reported to the @callback associated with the + * channel driving that transfer, but transfer completion callbacks can + * be sent to another channel under control of the TCC field in + * the option word of the transfer's parameter RAM set. Drivers must not + * use DMA transfer completion callbacks for channels they did not allocate. + * (The same applies to TCC codes used in transfer chaining.) + * + * Returns the number of the channel, else negative errno. + */ +int edma_alloc_channel(int channel, + void (*callback)(unsigned channel, u16 ch_status, void *data), + void *data, + enum dma_event_q eventq_no) +{ + unsigned i, done = 0, ctlr = 0; + int ret = 0; + + if (!unused_chan_list_done) { + /* + * Scan all the platform devices to find out the EDMA channels + * used and clear them in the unused list, making the rest + * available for ARM usage. + */ + ret = bus_for_each_dev(&platform_bus_type, NULL, NULL, + prepare_unused_channel_list); + if (ret < 0) + return ret; + + unused_chan_list_done = true; + } + + if (channel >= 0) { + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + } + + if (channel < 0) { + for (i = 0; i < arch_num_cc; i++) { + channel = 0; + for (;;) { + channel = find_next_bit(edma_cc[i]->edma_unused, + edma_cc[i]->num_channels, + channel); + if (channel == edma_cc[i]->num_channels) + break; + if (!test_and_set_bit(channel, + edma_cc[i]->edma_inuse)) { + done = 1; + ctlr = i; + break; + } + channel++; + } + if (done) + break; + } + if (!done) + return -ENOMEM; + } else if (channel >= edma_cc[ctlr]->num_channels) { + return -EINVAL; + } else if (test_and_set_bit(channel, edma_cc[ctlr]->edma_inuse)) { + return -EBUSY; + } + + /* ensure access through shadow region 0 */ + edma_or_array2(ctlr, EDMA_DRAE, 0, channel >> 5, BIT(channel & 0x1f)); + + /* ensure no events are pending */ + edma_stop(EDMA_CTLR_CHAN(ctlr, channel)); + memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(channel), + &dummy_paramset, PARM_SIZE); + + if (callback) + setup_dma_interrupt(EDMA_CTLR_CHAN(ctlr, channel), + callback, data); + + map_dmach_queue(ctlr, channel, eventq_no); + + return EDMA_CTLR_CHAN(ctlr, channel); +} +EXPORT_SYMBOL(edma_alloc_channel); + + +/** + * edma_free_channel - deallocate DMA channel + * @channel: dma channel returned from edma_alloc_channel() + * + * This deallocates the DMA channel and associated parameter RAM slot + * allocated by edma_alloc_channel(). + * + * Callers are responsible for ensuring the channel is inactive, and + * will not be reactivated by linking, chaining, or software calls to + * edma_start(). + */ +void edma_free_channel(unsigned channel) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel >= edma_cc[ctlr]->num_channels) + return; + + setup_dma_interrupt(channel, NULL, NULL); + /* REVISIT should probably take out of shadow region 0 */ + + memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(channel), + &dummy_paramset, PARM_SIZE); + clear_bit(channel, edma_cc[ctlr]->edma_inuse); +} +EXPORT_SYMBOL(edma_free_channel); + +/** + * edma_alloc_slot - allocate DMA parameter RAM + * @slot: specific slot to allocate; negative for "any unused slot" + * + * This allocates a parameter RAM slot, initializing it to hold a + * dummy transfer. Slots allocated using this routine have not been + * mapped to a hardware DMA channel, and will normally be used by + * linking to them from a slot associated with a DMA channel. + * + * Normal use is to pass EDMA_SLOT_ANY as the @slot, but specific + * slots may be allocated on behalf of DSP firmware. + * + * Returns the number of the slot, else negative errno. + */ +int edma_alloc_slot(unsigned ctlr, int slot) +{ + if (!edma_cc[ctlr]) + return -EINVAL; + + if (slot >= 0) + slot = EDMA_CHAN_SLOT(slot); + + if (slot < 0) { + slot = edma_cc[ctlr]->num_channels; + for (;;) { + slot = find_next_zero_bit(edma_cc[ctlr]->edma_inuse, + edma_cc[ctlr]->num_slots, slot); + if (slot == edma_cc[ctlr]->num_slots) + return -ENOMEM; + if (!test_and_set_bit(slot, edma_cc[ctlr]->edma_inuse)) + break; + } + } else if (slot < edma_cc[ctlr]->num_channels || + slot >= edma_cc[ctlr]->num_slots) { + return -EINVAL; + } else if (test_and_set_bit(slot, edma_cc[ctlr]->edma_inuse)) { + return -EBUSY; + } + + memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot), + &dummy_paramset, PARM_SIZE); + + return EDMA_CTLR_CHAN(ctlr, slot); +} +EXPORT_SYMBOL(edma_alloc_slot); + +/** + * edma_free_slot - deallocate DMA parameter RAM + * @slot: parameter RAM slot returned from edma_alloc_slot() + * + * This deallocates the parameter RAM slot allocated by edma_alloc_slot(). + * Callers are responsible for ensuring the slot is inactive, and will + * not be activated. + */ +void edma_free_slot(unsigned slot) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot < edma_cc[ctlr]->num_channels || + slot >= edma_cc[ctlr]->num_slots) + return; + + memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot), + &dummy_paramset, PARM_SIZE); + clear_bit(slot, edma_cc[ctlr]->edma_inuse); +} +EXPORT_SYMBOL(edma_free_slot); + + +/** + * edma_alloc_cont_slots- alloc contiguous parameter RAM slots + * The API will return the starting point of a set of + * contiguous parameter RAM slots that have been requested + * + * @id: can only be EDMA_CONT_PARAMS_ANY or EDMA_CONT_PARAMS_FIXED_EXACT + * or EDMA_CONT_PARAMS_FIXED_NOT_EXACT + * @count: number of contiguous Paramter RAM slots + * @slot - the start value of Parameter RAM slot that should be passed if id + * is EDMA_CONT_PARAMS_FIXED_EXACT or EDMA_CONT_PARAMS_FIXED_NOT_EXACT + * + * If id is EDMA_CONT_PARAMS_ANY then the API starts looking for a set of + * contiguous Parameter RAM slots from parameter RAM 64 in the case of + * DaVinci SOCs and 32 in the case of DA8xx SOCs. + * + * If id is EDMA_CONT_PARAMS_FIXED_EXACT then the API starts looking for a + * set of contiguous parameter RAM slots from the "slot" that is passed as an + * argument to the API. + * + * If id is EDMA_CONT_PARAMS_FIXED_NOT_EXACT then the API initially tries + * starts looking for a set of contiguous parameter RAMs from the "slot" + * that is passed as an argument to the API. On failure the API will try to + * find a set of contiguous Parameter RAM slots from the remaining Parameter + * RAM slots + */ +int edma_alloc_cont_slots(unsigned ctlr, unsigned int id, int slot, int count) +{ + /* + * The start slot requested should be greater than + * the number of channels and lesser than the total number + * of slots + */ + if ((id != EDMA_CONT_PARAMS_ANY) && + (slot < edma_cc[ctlr]->num_channels || + slot >= edma_cc[ctlr]->num_slots)) + return -EINVAL; + + /* + * The number of parameter RAM slots requested cannot be less than 1 + * and cannot be more than the number of slots minus the number of + * channels + */ + if (count < 1 || count > + (edma_cc[ctlr]->num_slots - edma_cc[ctlr]->num_channels)) + return -EINVAL; + + switch (id) { + case EDMA_CONT_PARAMS_ANY: + return reserve_contiguous_slots(ctlr, id, count, + edma_cc[ctlr]->num_channels); + case EDMA_CONT_PARAMS_FIXED_EXACT: + case EDMA_CONT_PARAMS_FIXED_NOT_EXACT: + return reserve_contiguous_slots(ctlr, id, count, slot); + default: + return -EINVAL; + } + +} +EXPORT_SYMBOL(edma_alloc_cont_slots); + +/** + * edma_free_cont_slots - deallocate DMA parameter RAM slots + * @slot: first parameter RAM of a set of parameter RAM slots to be freed + * @count: the number of contiguous parameter RAM slots to be freed + * + * This deallocates the parameter RAM slots allocated by + * edma_alloc_cont_slots. + * Callers/applications need to keep track of sets of contiguous + * parameter RAM slots that have been allocated using the edma_alloc_cont_slots + * API. + * Callers are responsible for ensuring the slots are inactive, and will + * not be activated. + */ +int edma_free_cont_slots(unsigned slot, int count) +{ + unsigned ctlr, slot_to_free; + int i; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot < edma_cc[ctlr]->num_channels || + slot >= edma_cc[ctlr]->num_slots || + count < 1) + return -EINVAL; + + for (i = slot; i < slot + count; ++i) { + ctlr = EDMA_CTLR(i); + slot_to_free = EDMA_CHAN_SLOT(i); + + memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot_to_free), + &dummy_paramset, PARM_SIZE); + clear_bit(slot_to_free, edma_cc[ctlr]->edma_inuse); + } + + return 0; +} +EXPORT_SYMBOL(edma_free_cont_slots); + +/*-----------------------------------------------------------------------*/ + +/* Parameter RAM operations (i) -- read/write partial slots */ + +/** + * edma_set_src - set initial DMA source address in parameter RAM slot + * @slot: parameter RAM slot being configured + * @src_port: physical address of source (memory, controller FIFO, etc) + * @addressMode: INCR, except in very rare cases + * @fifoWidth: ignored unless @addressMode is FIFO, else specifies the + * width to use when addressing the fifo (e.g. W8BIT, W32BIT) + * + * Note that the source address is modified during the DMA transfer + * according to edma_set_src_index(). + */ +void edma_set_src(unsigned slot, dma_addr_t src_port, + enum address_mode mode, enum fifo_width width) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot < edma_cc[ctlr]->num_slots) { + unsigned int i = edma_parm_read(ctlr, PARM_OPT, slot); + + if (mode) { + /* set SAM and program FWID */ + i = (i & ~(EDMA_FWID)) | (SAM | ((width & 0x7) << 8)); + } else { + /* clear SAM */ + i &= ~SAM; + } + edma_parm_write(ctlr, PARM_OPT, slot, i); + + /* set the source port address + in source register of param structure */ + edma_parm_write(ctlr, PARM_SRC, slot, src_port); + } +} +EXPORT_SYMBOL(edma_set_src); + +/** + * edma_set_dest - set initial DMA destination address in parameter RAM slot + * @slot: parameter RAM slot being configured + * @dest_port: physical address of destination (memory, controller FIFO, etc) + * @addressMode: INCR, except in very rare cases + * @fifoWidth: ignored unless @addressMode is FIFO, else specifies the + * width to use when addressing the fifo (e.g. W8BIT, W32BIT) + * + * Note that the destination address is modified during the DMA transfer + * according to edma_set_dest_index(). + */ +void edma_set_dest(unsigned slot, dma_addr_t dest_port, + enum address_mode mode, enum fifo_width width) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot < edma_cc[ctlr]->num_slots) { + unsigned int i = edma_parm_read(ctlr, PARM_OPT, slot); + + if (mode) { + /* set DAM and program FWID */ + i = (i & ~(EDMA_FWID)) | (DAM | ((width & 0x7) << 8)); + } else { + /* clear DAM */ + i &= ~DAM; + } + edma_parm_write(ctlr, PARM_OPT, slot, i); + /* set the destination port address + in dest register of param structure */ + edma_parm_write(ctlr, PARM_DST, slot, dest_port); + } +} +EXPORT_SYMBOL(edma_set_dest); + +/** + * edma_get_position - returns the current transfer points + * @slot: parameter RAM slot being examined + * @src: pointer to source port position + * @dst: pointer to destination port position + * + * Returns current source and destination addresses for a particular + * parameter RAM slot. Its channel should not be active when this is called. + */ +void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst) +{ + struct edmacc_param temp; + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + edma_read_slot(EDMA_CTLR_CHAN(ctlr, slot), &temp); + if (src != NULL) + *src = temp.src; + if (dst != NULL) + *dst = temp.dst; +} +EXPORT_SYMBOL(edma_get_position); + +/** + * edma_set_src_index - configure DMA source address indexing + * @slot: parameter RAM slot being configured + * @src_bidx: byte offset between source arrays in a frame + * @src_cidx: byte offset between source frames in a block + * + * Offsets are specified to support either contiguous or discontiguous + * memory transfers, or repeated access to a hardware register, as needed. + * When accessing hardware registers, both offsets are normally zero. + */ +void edma_set_src_index(unsigned slot, s16 src_bidx, s16 src_cidx) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot < edma_cc[ctlr]->num_slots) { + edma_parm_modify(ctlr, PARM_SRC_DST_BIDX, slot, + 0xffff0000, src_bidx); + edma_parm_modify(ctlr, PARM_SRC_DST_CIDX, slot, + 0xffff0000, src_cidx); + } +} +EXPORT_SYMBOL(edma_set_src_index); + +/** + * edma_set_dest_index - configure DMA destination address indexing + * @slot: parameter RAM slot being configured + * @dest_bidx: byte offset between destination arrays in a frame + * @dest_cidx: byte offset between destination frames in a block + * + * Offsets are specified to support either contiguous or discontiguous + * memory transfers, or repeated access to a hardware register, as needed. + * When accessing hardware registers, both offsets are normally zero. + */ +void edma_set_dest_index(unsigned slot, s16 dest_bidx, s16 dest_cidx) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot < edma_cc[ctlr]->num_slots) { + edma_parm_modify(ctlr, PARM_SRC_DST_BIDX, slot, + 0x0000ffff, dest_bidx << 16); + edma_parm_modify(ctlr, PARM_SRC_DST_CIDX, slot, + 0x0000ffff, dest_cidx << 16); + } +} +EXPORT_SYMBOL(edma_set_dest_index); + +/** + * edma_set_transfer_params - configure DMA transfer parameters + * @slot: parameter RAM slot being configured + * @acnt: how many bytes per array (at least one) + * @bcnt: how many arrays per frame (at least one) + * @ccnt: how many frames per block (at least one) + * @bcnt_rld: used only for A-Synchronized transfers; this specifies + * the value to reload into bcnt when it decrements to zero + * @sync_mode: ASYNC or ABSYNC + * + * See the EDMA3 documentation to understand how to configure and link + * transfers using the fields in PaRAM slots. If you are not doing it + * all at once with edma_write_slot(), you will use this routine + * plus two calls each for source and destination, setting the initial + * address and saying how to index that address. + * + * An example of an A-Synchronized transfer is a serial link using a + * single word shift register. In that case, @acnt would be equal to + * that word size; the serial controller issues a DMA synchronization + * event to transfer each word, and memory access by the DMA transfer + * controller will be word-at-a-time. + * + * An example of an AB-Synchronized transfer is a device using a FIFO. + * In that case, @acnt equals the FIFO width and @bcnt equals its depth. + * The controller with the FIFO issues DMA synchronization events when + * the FIFO threshold is reached, and the DMA transfer controller will + * transfer one frame to (or from) the FIFO. It will probably use + * efficient burst modes to access memory. + */ +void edma_set_transfer_params(unsigned slot, + u16 acnt, u16 bcnt, u16 ccnt, + u16 bcnt_rld, enum sync_dimension sync_mode) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot < edma_cc[ctlr]->num_slots) { + edma_parm_modify(ctlr, PARM_LINK_BCNTRLD, slot, + 0x0000ffff, bcnt_rld << 16); + if (sync_mode == ASYNC) + edma_parm_and(ctlr, PARM_OPT, slot, ~SYNCDIM); + else + edma_parm_or(ctlr, PARM_OPT, slot, SYNCDIM); + /* Set the acount, bcount, ccount registers */ + edma_parm_write(ctlr, PARM_A_B_CNT, slot, (bcnt << 16) | acnt); + edma_parm_write(ctlr, PARM_CCNT, slot, ccnt); + } +} +EXPORT_SYMBOL(edma_set_transfer_params); + +/** + * edma_link - link one parameter RAM slot to another + * @from: parameter RAM slot originating the link + * @to: parameter RAM slot which is the link target + * + * The originating slot should not be part of any active DMA transfer. + */ +void edma_link(unsigned from, unsigned to) +{ + unsigned ctlr_from, ctlr_to; + + ctlr_from = EDMA_CTLR(from); + from = EDMA_CHAN_SLOT(from); + ctlr_to = EDMA_CTLR(to); + to = EDMA_CHAN_SLOT(to); + + if (from >= edma_cc[ctlr_from]->num_slots) + return; + if (to >= edma_cc[ctlr_to]->num_slots) + return; + edma_parm_modify(ctlr_from, PARM_LINK_BCNTRLD, from, 0xffff0000, + PARM_OFFSET(to)); +} +EXPORT_SYMBOL(edma_link); + +/** + * edma_unlink - cut link from one parameter RAM slot + * @from: parameter RAM slot originating the link + * + * The originating slot should not be part of any active DMA transfer. + * Its link is set to 0xffff. + */ +void edma_unlink(unsigned from) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(from); + from = EDMA_CHAN_SLOT(from); + + if (from >= edma_cc[ctlr]->num_slots) + return; + edma_parm_or(ctlr, PARM_LINK_BCNTRLD, from, 0xffff); +} +EXPORT_SYMBOL(edma_unlink); + +/*-----------------------------------------------------------------------*/ + +/* Parameter RAM operations (ii) -- read/write whole parameter sets */ + +/** + * edma_write_slot - write parameter RAM data for slot + * @slot: number of parameter RAM slot being modified + * @param: data to be written into parameter RAM slot + * + * Use this to assign all parameters of a transfer at once. This + * allows more efficient setup of transfers than issuing multiple + * calls to set up those parameters in small pieces, and provides + * complete control over all transfer options. + */ +void edma_write_slot(unsigned slot, const struct edmacc_param *param) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot >= edma_cc[ctlr]->num_slots) + return; + memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot), param, + PARM_SIZE); +} +EXPORT_SYMBOL(edma_write_slot); + +/** + * edma_read_slot - read parameter RAM data from slot + * @slot: number of parameter RAM slot being copied + * @param: where to store copy of parameter RAM data + * + * Use this to read data from a parameter RAM slot, perhaps to + * save them as a template for later reuse. + */ +void edma_read_slot(unsigned slot, struct edmacc_param *param) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(slot); + slot = EDMA_CHAN_SLOT(slot); + + if (slot >= edma_cc[ctlr]->num_slots) + return; + memcpy_fromio(param, edmacc_regs_base[ctlr] + PARM_OFFSET(slot), + PARM_SIZE); +} +EXPORT_SYMBOL(edma_read_slot); + +/*-----------------------------------------------------------------------*/ + +/* Various EDMA channel control operations */ + +/** + * edma_pause - pause dma on a channel + * @channel: on which edma_start() has been called + * + * This temporarily disables EDMA hardware events on the specified channel, + * preventing them from triggering new transfers on its behalf + */ +void edma_pause(unsigned channel) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel < edma_cc[ctlr]->num_channels) { + unsigned int mask = BIT(channel & 0x1f); + + edma_shadow0_write_array(ctlr, SH_EECR, channel >> 5, mask); + } +} +EXPORT_SYMBOL(edma_pause); + +/** + * edma_resume - resumes dma on a paused channel + * @channel: on which edma_pause() has been called + * + * This re-enables EDMA hardware events on the specified channel. + */ +void edma_resume(unsigned channel) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel < edma_cc[ctlr]->num_channels) { + unsigned int mask = BIT(channel & 0x1f); + + edma_shadow0_write_array(ctlr, SH_EESR, channel >> 5, mask); + } +} +EXPORT_SYMBOL(edma_resume); + +/** + * edma_start - start dma on a channel + * @channel: channel being activated + * + * Channels with event associations will be triggered by their hardware + * events, and channels without such associations will be triggered by + * software. (At this writing there is no interface for using software + * triggers except with channels that don't support hardware triggers.) + * + * Returns zero on success, else negative errno. + */ +int edma_start(unsigned channel) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel < edma_cc[ctlr]->num_channels) { + int j = channel >> 5; + unsigned int mask = BIT(channel & 0x1f); + + /* EDMA channels without event association */ + if (test_bit(channel, edma_cc[ctlr]->edma_unused)) { + pr_debug("EDMA: ESR%d %08x\n", j, + edma_shadow0_read_array(ctlr, SH_ESR, j)); + edma_shadow0_write_array(ctlr, SH_ESR, j, mask); + return 0; + } + + /* EDMA channel with event association */ + pr_debug("EDMA: ER%d %08x\n", j, + edma_shadow0_read_array(ctlr, SH_ER, j)); + /* Clear any pending event or error */ + edma_write_array(ctlr, EDMA_ECR, j, mask); + edma_write_array(ctlr, EDMA_EMCR, j, mask); + /* Clear any SER */ + edma_shadow0_write_array(ctlr, SH_SECR, j, mask); + edma_shadow0_write_array(ctlr, SH_EESR, j, mask); + pr_debug("EDMA: EER%d %08x\n", j, + edma_shadow0_read_array(ctlr, SH_EER, j)); + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL(edma_start); + +/** + * edma_stop - stops dma on the channel passed + * @channel: channel being deactivated + * + * When @lch is a channel, any active transfer is paused and + * all pending hardware events are cleared. The current transfer + * may not be resumed, and the channel's Parameter RAM should be + * reinitialized before being reused. + */ +void edma_stop(unsigned channel) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel < edma_cc[ctlr]->num_channels) { + int j = channel >> 5; + unsigned int mask = BIT(channel & 0x1f); + + edma_shadow0_write_array(ctlr, SH_EECR, j, mask); + edma_shadow0_write_array(ctlr, SH_ECR, j, mask); + edma_shadow0_write_array(ctlr, SH_SECR, j, mask); + edma_write_array(ctlr, EDMA_EMCR, j, mask); + + pr_debug("EDMA: EER%d %08x\n", j, + edma_shadow0_read_array(ctlr, SH_EER, j)); + + /* REVISIT: consider guarding against inappropriate event + * chaining by overwriting with dummy_paramset. + */ + } +} +EXPORT_SYMBOL(edma_stop); + +/****************************************************************************** + * + * It cleans ParamEntry qand bring back EDMA to initial state if media has + * been removed before EDMA has finished.It is usedful for removable media. + * Arguments: + * ch_no - channel no + * + * Return: zero on success, or corresponding error no on failure + * + * FIXME this should not be needed ... edma_stop() should suffice. + * + *****************************************************************************/ + +void edma_clean_channel(unsigned channel) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel < edma_cc[ctlr]->num_channels) { + int j = (channel >> 5); + unsigned int mask = BIT(channel & 0x1f); + + pr_debug("EDMA: EMR%d %08x\n", j, + edma_read_array(ctlr, EDMA_EMR, j)); + edma_shadow0_write_array(ctlr, SH_ECR, j, mask); + /* Clear the corresponding EMR bits */ + edma_write_array(ctlr, EDMA_EMCR, j, mask); + /* Clear any SER */ + edma_shadow0_write_array(ctlr, SH_SECR, j, mask); + edma_write(ctlr, EDMA_CCERRCLR, BIT(16) | BIT(1) | BIT(0)); + } +} +EXPORT_SYMBOL(edma_clean_channel); + +/* + * edma_clear_event - clear an outstanding event on the DMA channel + * Arguments: + * channel - channel number + */ +void edma_clear_event(unsigned channel) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel >= edma_cc[ctlr]->num_channels) + return; + if (channel < 32) + edma_write(ctlr, EDMA_ECR, BIT(channel)); + else + edma_write(ctlr, EDMA_ECRH, BIT(channel - 32)); +} +EXPORT_SYMBOL(edma_clear_event); + +/*-----------------------------------------------------------------------*/ + +static int __init edma_probe(struct platform_device *pdev) +{ + struct edma_soc_info **info = pdev->dev.platform_data; + const s8 (*queue_priority_mapping)[2]; + const s8 (*queue_tc_mapping)[2]; + int i, j, off, ln, found = 0; + int status = -1; + const s16 (*rsv_chans)[2]; + const s16 (*rsv_slots)[2]; + int irq[EDMA_MAX_CC] = {0, 0}; + int err_irq[EDMA_MAX_CC] = {0, 0}; + struct resource *r[EDMA_MAX_CC] = {NULL}; + resource_size_t len[EDMA_MAX_CC]; + char res_name[10]; + char irq_name[10]; + + if (!info) + return -ENODEV; + + for (j = 0; j < EDMA_MAX_CC; j++) { + sprintf(res_name, "edma_cc%d", j); + r[j] = platform_get_resource_byname(pdev, IORESOURCE_MEM, + res_name); + if (!r[j] || !info[j]) { + if (found) + break; + else + return -ENODEV; + } else { + found = 1; + } + + len[j] = resource_size(r[j]); + + r[j] = request_mem_region(r[j]->start, len[j], + dev_name(&pdev->dev)); + if (!r[j]) { + status = -EBUSY; + goto fail1; + } + + edmacc_regs_base[j] = ioremap(r[j]->start, len[j]); + if (!edmacc_regs_base[j]) { + status = -EBUSY; + goto fail1; + } + + edma_cc[j] = kzalloc(sizeof(struct edma), GFP_KERNEL); + if (!edma_cc[j]) { + status = -ENOMEM; + goto fail1; + } + + edma_cc[j]->num_channels = min_t(unsigned, info[j]->n_channel, + EDMA_MAX_DMACH); + edma_cc[j]->num_slots = min_t(unsigned, info[j]->n_slot, + EDMA_MAX_PARAMENTRY); + edma_cc[j]->num_cc = min_t(unsigned, info[j]->n_cc, + EDMA_MAX_CC); + + edma_cc[j]->default_queue = info[j]->default_queue; + + dev_dbg(&pdev->dev, "DMA REG BASE ADDR=%p\n", + edmacc_regs_base[j]); + + for (i = 0; i < edma_cc[j]->num_slots; i++) + memcpy_toio(edmacc_regs_base[j] + PARM_OFFSET(i), + &dummy_paramset, PARM_SIZE); + + /* Mark all channels as unused */ + memset(edma_cc[j]->edma_unused, 0xff, + sizeof(edma_cc[j]->edma_unused)); + + if (info[j]->rsv) { + + /* Clear the reserved channels in unused list */ + rsv_chans = info[j]->rsv->rsv_chans; + if (rsv_chans) { + for (i = 0; rsv_chans[i][0] != -1; i++) { + off = rsv_chans[i][0]; + ln = rsv_chans[i][1]; + clear_bits(off, ln, + edma_cc[j]->edma_unused); + } + } + + /* Set the reserved slots in inuse list */ + rsv_slots = info[j]->rsv->rsv_slots; + if (rsv_slots) { + for (i = 0; rsv_slots[i][0] != -1; i++) { + off = rsv_slots[i][0]; + ln = rsv_slots[i][1]; + set_bits(off, ln, + edma_cc[j]->edma_inuse); + } + } + } + + sprintf(irq_name, "edma%d", j); + irq[j] = platform_get_irq_byname(pdev, irq_name); + edma_cc[j]->irq_res_start = irq[j]; + status = request_irq(irq[j], dma_irq_handler, 0, "edma", + &pdev->dev); + if (status < 0) { + dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n", + irq[j], status); + goto fail; + } + + sprintf(irq_name, "edma%d_err", j); + err_irq[j] = platform_get_irq_byname(pdev, irq_name); + edma_cc[j]->irq_res_end = err_irq[j]; + status = request_irq(err_irq[j], dma_ccerr_handler, 0, + "edma_error", &pdev->dev); + if (status < 0) { + dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n", + err_irq[j], status); + goto fail; + } + + for (i = 0; i < edma_cc[j]->num_channels; i++) + map_dmach_queue(j, i, info[j]->default_queue); + + queue_tc_mapping = info[j]->queue_tc_mapping; + queue_priority_mapping = info[j]->queue_priority_mapping; + + /* Event queue to TC mapping */ + for (i = 0; queue_tc_mapping[i][0] != -1; i++) + map_queue_tc(j, queue_tc_mapping[i][0], + queue_tc_mapping[i][1]); + + /* Event queue priority mapping */ + for (i = 0; queue_priority_mapping[i][0] != -1; i++) + assign_priority_to_queue(j, + queue_priority_mapping[i][0], + queue_priority_mapping[i][1]); + + /* Map the channel to param entry if channel mapping logic + * exist + */ + if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST) + map_dmach_param(j); + + for (i = 0; i < info[j]->n_region; i++) { + edma_write_array2(j, EDMA_DRAE, i, 0, 0x0); + edma_write_array2(j, EDMA_DRAE, i, 1, 0x0); + edma_write_array(j, EDMA_QRAE, i, 0x0); + } + arch_num_cc++; + } + + if (tc_errs_handled) { + status = request_irq(IRQ_TCERRINT0, dma_tc0err_handler, 0, + "edma_tc0", &pdev->dev); + if (status < 0) { + dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n", + IRQ_TCERRINT0, status); + return status; + } + status = request_irq(IRQ_TCERRINT, dma_tc1err_handler, 0, + "edma_tc1", &pdev->dev); + if (status < 0) { + dev_dbg(&pdev->dev, "request_irq %d --> %d\n", + IRQ_TCERRINT, status); + return status; + } + } + + return 0; + +fail: + for (i = 0; i < EDMA_MAX_CC; i++) { + if (err_irq[i]) + free_irq(err_irq[i], &pdev->dev); + if (irq[i]) + free_irq(irq[i], &pdev->dev); + } +fail1: + for (i = 0; i < EDMA_MAX_CC; i++) { + if (r[i]) + release_mem_region(r[i]->start, len[i]); + if (edmacc_regs_base[i]) + iounmap(edmacc_regs_base[i]); + kfree(edma_cc[i]); + } + return status; +} + + +static struct platform_driver edma_driver = { + .driver.name = "edma", +}; + +static int __init edma_init(void) +{ + return platform_driver_probe(&edma_driver, edma_probe); +} +arch_initcall(edma_init); + diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile index dd1ffccc75e9..63997a1128e6 100644 --- a/arch/arm/mach-davinci/Makefile +++ b/arch/arm/mach-davinci/Makefile @@ -5,7 +5,7 @@ # Common objects obj-y := time.o clock.o serial.o psc.o \ - dma.o usb.o common.o sram.o aemif.o + usb.o common.o sram.o aemif.o obj-$(CONFIG_DAVINCI_MUX) += mux.o diff --git a/arch/arm/mach-davinci/board-tnetv107x-evm.c b/arch/arm/mach-davinci/board-tnetv107x-evm.c index ba798370fc96..78ea395d2aca 100644 --- a/arch/arm/mach-davinci/board-tnetv107x-evm.c +++ b/arch/arm/mach-davinci/board-tnetv107x-evm.c @@ -26,12 +26,12 @@ #include #include #include +#include #include #include #include -#include #include #include #include diff --git a/arch/arm/mach-davinci/davinci.h b/arch/arm/mach-davinci/davinci.h index b4b7451a8434..a883043d0820 100644 --- a/arch/arm/mach-davinci/davinci.h +++ b/arch/arm/mach-davinci/davinci.h @@ -23,9 +23,9 @@ #include #include #include +#include #include #include -#include #include #include diff --git a/arch/arm/mach-davinci/devices-tnetv107x.c b/arch/arm/mach-davinci/devices-tnetv107x.c index cfb194df18ed..612a0856e9c5 100644 --- a/arch/arm/mach-davinci/devices-tnetv107x.c +++ b/arch/arm/mach-davinci/devices-tnetv107x.c @@ -18,10 +18,10 @@ #include #include #include +#include #include #include -#include #include #include "clock.h" diff --git a/arch/arm/mach-davinci/devices.c b/arch/arm/mach-davinci/devices.c index a7068a3aa9d3..90b83d00fe2b 100644 --- a/arch/arm/mach-davinci/devices.c +++ b/arch/arm/mach-davinci/devices.c @@ -19,9 +19,10 @@ #include #include #include -#include #include #include +#include + #include "davinci.h" #include "clock.h" @@ -34,6 +35,9 @@ #define DM365_MMCSD0_BASE 0x01D11000 #define DM365_MMCSD1_BASE 0x01D00000 +#define DAVINCI_DMA_MMCRXEVT 26 +#define DAVINCI_DMA_MMCTXEVT 27 + void __iomem *davinci_sysmod_base; void davinci_map_sysmod(void) diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index a11034a358f1..526cf7d06d0e 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -28,6 +27,7 @@ #include #include #include +#include #include "davinci.h" #include "clock.h" diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 40fa4fee9331..c4b741173c06 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -18,11 +18,11 @@ #include #include #include +#include #include #include -#include #include #include #include diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 4d37d3e2a193..dd156d58fe64 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -12,11 +12,11 @@ #include #include #include +#include #include #include -#include #include #include #include diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index ac7b431c4c8e..6d52a321a8cf 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -13,11 +13,11 @@ #include #include #include +#include #include #include -#include #include #include #include diff --git a/arch/arm/mach-davinci/dma.c b/arch/arm/mach-davinci/dma.c deleted file mode 100644 index 45b7c71d9cc1..000000000000 --- a/arch/arm/mach-davinci/dma.c +++ /dev/null @@ -1,1591 +0,0 @@ -/* - * EDMA3 support for DaVinci - * - * Copyright (C) 2006-2009 Texas Instruments. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -#include -#include -#include -#include -#include -#include -#include - -#include - -/* Offsets matching "struct edmacc_param" */ -#define PARM_OPT 0x00 -#define PARM_SRC 0x04 -#define PARM_A_B_CNT 0x08 -#define PARM_DST 0x0c -#define PARM_SRC_DST_BIDX 0x10 -#define PARM_LINK_BCNTRLD 0x14 -#define PARM_SRC_DST_CIDX 0x18 -#define PARM_CCNT 0x1c - -#define PARM_SIZE 0x20 - -/* Offsets for EDMA CC global channel registers and their shadows */ -#define SH_ER 0x00 /* 64 bits */ -#define SH_ECR 0x08 /* 64 bits */ -#define SH_ESR 0x10 /* 64 bits */ -#define SH_CER 0x18 /* 64 bits */ -#define SH_EER 0x20 /* 64 bits */ -#define SH_EECR 0x28 /* 64 bits */ -#define SH_EESR 0x30 /* 64 bits */ -#define SH_SER 0x38 /* 64 bits */ -#define SH_SECR 0x40 /* 64 bits */ -#define SH_IER 0x50 /* 64 bits */ -#define SH_IECR 0x58 /* 64 bits */ -#define SH_IESR 0x60 /* 64 bits */ -#define SH_IPR 0x68 /* 64 bits */ -#define SH_ICR 0x70 /* 64 bits */ -#define SH_IEVAL 0x78 -#define SH_QER 0x80 -#define SH_QEER 0x84 -#define SH_QEECR 0x88 -#define SH_QEESR 0x8c -#define SH_QSER 0x90 -#define SH_QSECR 0x94 -#define SH_SIZE 0x200 - -/* Offsets for EDMA CC global registers */ -#define EDMA_REV 0x0000 -#define EDMA_CCCFG 0x0004 -#define EDMA_QCHMAP 0x0200 /* 8 registers */ -#define EDMA_DMAQNUM 0x0240 /* 8 registers (4 on OMAP-L1xx) */ -#define EDMA_QDMAQNUM 0x0260 -#define EDMA_QUETCMAP 0x0280 -#define EDMA_QUEPRI 0x0284 -#define EDMA_EMR 0x0300 /* 64 bits */ -#define EDMA_EMCR 0x0308 /* 64 bits */ -#define EDMA_QEMR 0x0310 -#define EDMA_QEMCR 0x0314 -#define EDMA_CCERR 0x0318 -#define EDMA_CCERRCLR 0x031c -#define EDMA_EEVAL 0x0320 -#define EDMA_DRAE 0x0340 /* 4 x 64 bits*/ -#define EDMA_QRAE 0x0380 /* 4 registers */ -#define EDMA_QUEEVTENTRY 0x0400 /* 2 x 16 registers */ -#define EDMA_QSTAT 0x0600 /* 2 registers */ -#define EDMA_QWMTHRA 0x0620 -#define EDMA_QWMTHRB 0x0624 -#define EDMA_CCSTAT 0x0640 - -#define EDMA_M 0x1000 /* global channel registers */ -#define EDMA_ECR 0x1008 -#define EDMA_ECRH 0x100C -#define EDMA_SHADOW0 0x2000 /* 4 regions shadowing global channels */ -#define EDMA_PARM 0x4000 /* 128 param entries */ - -#define PARM_OFFSET(param_no) (EDMA_PARM + ((param_no) << 5)) - -#define EDMA_DCHMAP 0x0100 /* 64 registers */ -#define CHMAP_EXIST BIT(24) - -#define EDMA_MAX_DMACH 64 -#define EDMA_MAX_PARAMENTRY 512 - -/*****************************************************************************/ - -static void __iomem *edmacc_regs_base[EDMA_MAX_CC]; - -static inline unsigned int edma_read(unsigned ctlr, int offset) -{ - return (unsigned int)__raw_readl(edmacc_regs_base[ctlr] + offset); -} - -static inline void edma_write(unsigned ctlr, int offset, int val) -{ - __raw_writel(val, edmacc_regs_base[ctlr] + offset); -} -static inline void edma_modify(unsigned ctlr, int offset, unsigned and, - unsigned or) -{ - unsigned val = edma_read(ctlr, offset); - val &= and; - val |= or; - edma_write(ctlr, offset, val); -} -static inline void edma_and(unsigned ctlr, int offset, unsigned and) -{ - unsigned val = edma_read(ctlr, offset); - val &= and; - edma_write(ctlr, offset, val); -} -static inline void edma_or(unsigned ctlr, int offset, unsigned or) -{ - unsigned val = edma_read(ctlr, offset); - val |= or; - edma_write(ctlr, offset, val); -} -static inline unsigned int edma_read_array(unsigned ctlr, int offset, int i) -{ - return edma_read(ctlr, offset + (i << 2)); -} -static inline void edma_write_array(unsigned ctlr, int offset, int i, - unsigned val) -{ - edma_write(ctlr, offset + (i << 2), val); -} -static inline void edma_modify_array(unsigned ctlr, int offset, int i, - unsigned and, unsigned or) -{ - edma_modify(ctlr, offset + (i << 2), and, or); -} -static inline void edma_or_array(unsigned ctlr, int offset, int i, unsigned or) -{ - edma_or(ctlr, offset + (i << 2), or); -} -static inline void edma_or_array2(unsigned ctlr, int offset, int i, int j, - unsigned or) -{ - edma_or(ctlr, offset + ((i*2 + j) << 2), or); -} -static inline void edma_write_array2(unsigned ctlr, int offset, int i, int j, - unsigned val) -{ - edma_write(ctlr, offset + ((i*2 + j) << 2), val); -} -static inline unsigned int edma_shadow0_read(unsigned ctlr, int offset) -{ - return edma_read(ctlr, EDMA_SHADOW0 + offset); -} -static inline unsigned int edma_shadow0_read_array(unsigned ctlr, int offset, - int i) -{ - return edma_read(ctlr, EDMA_SHADOW0 + offset + (i << 2)); -} -static inline void edma_shadow0_write(unsigned ctlr, int offset, unsigned val) -{ - edma_write(ctlr, EDMA_SHADOW0 + offset, val); -} -static inline void edma_shadow0_write_array(unsigned ctlr, int offset, int i, - unsigned val) -{ - edma_write(ctlr, EDMA_SHADOW0 + offset + (i << 2), val); -} -static inline unsigned int edma_parm_read(unsigned ctlr, int offset, - int param_no) -{ - return edma_read(ctlr, EDMA_PARM + offset + (param_no << 5)); -} -static inline void edma_parm_write(unsigned ctlr, int offset, int param_no, - unsigned val) -{ - edma_write(ctlr, EDMA_PARM + offset + (param_no << 5), val); -} -static inline void edma_parm_modify(unsigned ctlr, int offset, int param_no, - unsigned and, unsigned or) -{ - edma_modify(ctlr, EDMA_PARM + offset + (param_no << 5), and, or); -} -static inline void edma_parm_and(unsigned ctlr, int offset, int param_no, - unsigned and) -{ - edma_and(ctlr, EDMA_PARM + offset + (param_no << 5), and); -} -static inline void edma_parm_or(unsigned ctlr, int offset, int param_no, - unsigned or) -{ - edma_or(ctlr, EDMA_PARM + offset + (param_no << 5), or); -} - -static inline void set_bits(int offset, int len, unsigned long *p) -{ - for (; len > 0; len--) - set_bit(offset + (len - 1), p); -} - -static inline void clear_bits(int offset, int len, unsigned long *p) -{ - for (; len > 0; len--) - clear_bit(offset + (len - 1), p); -} - -/*****************************************************************************/ - -/* actual number of DMA channels and slots on this silicon */ -struct edma { - /* how many dma resources of each type */ - unsigned num_channels; - unsigned num_region; - unsigned num_slots; - unsigned num_tc; - unsigned num_cc; - enum dma_event_q default_queue; - - /* list of channels with no even trigger; terminated by "-1" */ - const s8 *noevent; - - /* The edma_inuse bit for each PaRAM slot is clear unless the - * channel is in use ... by ARM or DSP, for QDMA, or whatever. - */ - DECLARE_BITMAP(edma_inuse, EDMA_MAX_PARAMENTRY); - - /* The edma_unused bit for each channel is clear unless - * it is not being used on this platform. It uses a bit - * of SOC-specific initialization code. - */ - DECLARE_BITMAP(edma_unused, EDMA_MAX_DMACH); - - unsigned irq_res_start; - unsigned irq_res_end; - - struct dma_interrupt_data { - void (*callback)(unsigned channel, unsigned short ch_status, - void *data); - void *data; - } intr_data[EDMA_MAX_DMACH]; -}; - -static struct edma *edma_cc[EDMA_MAX_CC]; -static int arch_num_cc; - -/* dummy param set used to (re)initialize parameter RAM slots */ -static const struct edmacc_param dummy_paramset = { - .link_bcntrld = 0xffff, - .ccnt = 1, -}; - -/*****************************************************************************/ - -static void map_dmach_queue(unsigned ctlr, unsigned ch_no, - enum dma_event_q queue_no) -{ - int bit = (ch_no & 0x7) * 4; - - /* default to low priority queue */ - if (queue_no == EVENTQ_DEFAULT) - queue_no = edma_cc[ctlr]->default_queue; - - queue_no &= 7; - edma_modify_array(ctlr, EDMA_DMAQNUM, (ch_no >> 3), - ~(0x7 << bit), queue_no << bit); -} - -static void __init map_queue_tc(unsigned ctlr, int queue_no, int tc_no) -{ - int bit = queue_no * 4; - edma_modify(ctlr, EDMA_QUETCMAP, ~(0x7 << bit), ((tc_no & 0x7) << bit)); -} - -static void __init assign_priority_to_queue(unsigned ctlr, int queue_no, - int priority) -{ - int bit = queue_no * 4; - edma_modify(ctlr, EDMA_QUEPRI, ~(0x7 << bit), - ((priority & 0x7) << bit)); -} - -/** - * map_dmach_param - Maps channel number to param entry number - * - * This maps the dma channel number to param entry numberter. In - * other words using the DMA channel mapping registers a param entry - * can be mapped to any channel - * - * Callers are responsible for ensuring the channel mapping logic is - * included in that particular EDMA variant (Eg : dm646x) - * - */ -static void __init map_dmach_param(unsigned ctlr) -{ - int i; - for (i = 0; i < EDMA_MAX_DMACH; i++) - edma_write_array(ctlr, EDMA_DCHMAP , i , (i << 5)); -} - -static inline void -setup_dma_interrupt(unsigned lch, - void (*callback)(unsigned channel, u16 ch_status, void *data), - void *data) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(lch); - lch = EDMA_CHAN_SLOT(lch); - - if (!callback) - edma_shadow0_write_array(ctlr, SH_IECR, lch >> 5, - BIT(lch & 0x1f)); - - edma_cc[ctlr]->intr_data[lch].callback = callback; - edma_cc[ctlr]->intr_data[lch].data = data; - - if (callback) { - edma_shadow0_write_array(ctlr, SH_ICR, lch >> 5, - BIT(lch & 0x1f)); - edma_shadow0_write_array(ctlr, SH_IESR, lch >> 5, - BIT(lch & 0x1f)); - } -} - -static int irq2ctlr(int irq) -{ - if (irq >= edma_cc[0]->irq_res_start && irq <= edma_cc[0]->irq_res_end) - return 0; - else if (irq >= edma_cc[1]->irq_res_start && - irq <= edma_cc[1]->irq_res_end) - return 1; - - return -1; -} - -/****************************************************************************** - * - * DMA interrupt handler - * - *****************************************************************************/ -static irqreturn_t dma_irq_handler(int irq, void *data) -{ - int ctlr; - u32 sh_ier; - u32 sh_ipr; - u32 bank; - - ctlr = irq2ctlr(irq); - if (ctlr < 0) - return IRQ_NONE; - - dev_dbg(data, "dma_irq_handler\n"); - - sh_ipr = edma_shadow0_read_array(ctlr, SH_IPR, 0); - if (!sh_ipr) { - sh_ipr = edma_shadow0_read_array(ctlr, SH_IPR, 1); - if (!sh_ipr) - return IRQ_NONE; - sh_ier = edma_shadow0_read_array(ctlr, SH_IER, 1); - bank = 1; - } else { - sh_ier = edma_shadow0_read_array(ctlr, SH_IER, 0); - bank = 0; - } - - do { - u32 slot; - u32 channel; - - dev_dbg(data, "IPR%d %08x\n", bank, sh_ipr); - - slot = __ffs(sh_ipr); - sh_ipr &= ~(BIT(slot)); - - if (sh_ier & BIT(slot)) { - channel = (bank << 5) | slot; - /* Clear the corresponding IPR bits */ - edma_shadow0_write_array(ctlr, SH_ICR, bank, - BIT(slot)); - if (edma_cc[ctlr]->intr_data[channel].callback) - edma_cc[ctlr]->intr_data[channel].callback( - channel, DMA_COMPLETE, - edma_cc[ctlr]->intr_data[channel].data); - } - } while (sh_ipr); - - edma_shadow0_write(ctlr, SH_IEVAL, 1); - return IRQ_HANDLED; -} - -/****************************************************************************** - * - * DMA error interrupt handler - * - *****************************************************************************/ -static irqreturn_t dma_ccerr_handler(int irq, void *data) -{ - int i; - int ctlr; - unsigned int cnt = 0; - - ctlr = irq2ctlr(irq); - if (ctlr < 0) - return IRQ_NONE; - - dev_dbg(data, "dma_ccerr_handler\n"); - - if ((edma_read_array(ctlr, EDMA_EMR, 0) == 0) && - (edma_read_array(ctlr, EDMA_EMR, 1) == 0) && - (edma_read(ctlr, EDMA_QEMR) == 0) && - (edma_read(ctlr, EDMA_CCERR) == 0)) - return IRQ_NONE; - - while (1) { - int j = -1; - if (edma_read_array(ctlr, EDMA_EMR, 0)) - j = 0; - else if (edma_read_array(ctlr, EDMA_EMR, 1)) - j = 1; - if (j >= 0) { - dev_dbg(data, "EMR%d %08x\n", j, - edma_read_array(ctlr, EDMA_EMR, j)); - for (i = 0; i < 32; i++) { - int k = (j << 5) + i; - if (edma_read_array(ctlr, EDMA_EMR, j) & - BIT(i)) { - /* Clear the corresponding EMR bits */ - edma_write_array(ctlr, EDMA_EMCR, j, - BIT(i)); - /* Clear any SER */ - edma_shadow0_write_array(ctlr, SH_SECR, - j, BIT(i)); - if (edma_cc[ctlr]->intr_data[k]. - callback) { - edma_cc[ctlr]->intr_data[k]. - callback(k, - DMA_CC_ERROR, - edma_cc[ctlr]->intr_data - [k].data); - } - } - } - } else if (edma_read(ctlr, EDMA_QEMR)) { - dev_dbg(data, "QEMR %02x\n", - edma_read(ctlr, EDMA_QEMR)); - for (i = 0; i < 8; i++) { - if (edma_read(ctlr, EDMA_QEMR) & BIT(i)) { - /* Clear the corresponding IPR bits */ - edma_write(ctlr, EDMA_QEMCR, BIT(i)); - edma_shadow0_write(ctlr, SH_QSECR, - BIT(i)); - - /* NOTE: not reported!! */ - } - } - } else if (edma_read(ctlr, EDMA_CCERR)) { - dev_dbg(data, "CCERR %08x\n", - edma_read(ctlr, EDMA_CCERR)); - /* FIXME: CCERR.BIT(16) ignored! much better - * to just write CCERRCLR with CCERR value... - */ - for (i = 0; i < 8; i++) { - if (edma_read(ctlr, EDMA_CCERR) & BIT(i)) { - /* Clear the corresponding IPR bits */ - edma_write(ctlr, EDMA_CCERRCLR, BIT(i)); - - /* NOTE: not reported!! */ - } - } - } - if ((edma_read_array(ctlr, EDMA_EMR, 0) == 0) && - (edma_read_array(ctlr, EDMA_EMR, 1) == 0) && - (edma_read(ctlr, EDMA_QEMR) == 0) && - (edma_read(ctlr, EDMA_CCERR) == 0)) - break; - cnt++; - if (cnt > 10) - break; - } - edma_write(ctlr, EDMA_EEVAL, 1); - return IRQ_HANDLED; -} - -/****************************************************************************** - * - * Transfer controller error interrupt handlers - * - *****************************************************************************/ - -#define tc_errs_handled false /* disabled as long as they're NOPs */ - -static irqreturn_t dma_tc0err_handler(int irq, void *data) -{ - dev_dbg(data, "dma_tc0err_handler\n"); - return IRQ_HANDLED; -} - -static irqreturn_t dma_tc1err_handler(int irq, void *data) -{ - dev_dbg(data, "dma_tc1err_handler\n"); - return IRQ_HANDLED; -} - -static int reserve_contiguous_slots(int ctlr, unsigned int id, - unsigned int num_slots, - unsigned int start_slot) -{ - int i, j; - unsigned int count = num_slots; - int stop_slot = start_slot; - DECLARE_BITMAP(tmp_inuse, EDMA_MAX_PARAMENTRY); - - for (i = start_slot; i < edma_cc[ctlr]->num_slots; ++i) { - j = EDMA_CHAN_SLOT(i); - if (!test_and_set_bit(j, edma_cc[ctlr]->edma_inuse)) { - /* Record our current beginning slot */ - if (count == num_slots) - stop_slot = i; - - count--; - set_bit(j, tmp_inuse); - - if (count == 0) - break; - } else { - clear_bit(j, tmp_inuse); - - if (id == EDMA_CONT_PARAMS_FIXED_EXACT) { - stop_slot = i; - break; - } else { - count = num_slots; - } - } - } - - /* - * We have to clear any bits that we set - * if we run out parameter RAM slots, i.e we do find a set - * of contiguous parameter RAM slots but do not find the exact number - * requested as we may reach the total number of parameter RAM slots - */ - if (i == edma_cc[ctlr]->num_slots) - stop_slot = i; - - j = start_slot; - for_each_set_bit_from(j, tmp_inuse, stop_slot) - clear_bit(j, edma_cc[ctlr]->edma_inuse); - - if (count) - return -EBUSY; - - for (j = i - num_slots + 1; j <= i; ++j) - memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(j), - &dummy_paramset, PARM_SIZE); - - return EDMA_CTLR_CHAN(ctlr, i - num_slots + 1); -} - -static int prepare_unused_channel_list(struct device *dev, void *data) -{ - struct platform_device *pdev = to_platform_device(dev); - int i, ctlr; - - for (i = 0; i < pdev->num_resources; i++) { - if ((pdev->resource[i].flags & IORESOURCE_DMA) && - (int)pdev->resource[i].start >= 0) { - ctlr = EDMA_CTLR(pdev->resource[i].start); - clear_bit(EDMA_CHAN_SLOT(pdev->resource[i].start), - edma_cc[ctlr]->edma_unused); - } - } - - return 0; -} - -/*-----------------------------------------------------------------------*/ - -static bool unused_chan_list_done; - -/* Resource alloc/free: dma channels, parameter RAM slots */ - -/** - * edma_alloc_channel - allocate DMA channel and paired parameter RAM - * @channel: specific channel to allocate; negative for "any unmapped channel" - * @callback: optional; to be issued on DMA completion or errors - * @data: passed to callback - * @eventq_no: an EVENTQ_* constant, used to choose which Transfer - * Controller (TC) executes requests using this channel. Use - * EVENTQ_DEFAULT unless you really need a high priority queue. - * - * This allocates a DMA channel and its associated parameter RAM slot. - * The parameter RAM is initialized to hold a dummy transfer. - * - * Normal use is to pass a specific channel number as @channel, to make - * use of hardware events mapped to that channel. When the channel will - * be used only for software triggering or event chaining, channels not - * mapped to hardware events (or mapped to unused events) are preferable. - * - * DMA transfers start from a channel using edma_start(), or by - * chaining. When the transfer described in that channel's parameter RAM - * slot completes, that slot's data may be reloaded through a link. - * - * DMA errors are only reported to the @callback associated with the - * channel driving that transfer, but transfer completion callbacks can - * be sent to another channel under control of the TCC field in - * the option word of the transfer's parameter RAM set. Drivers must not - * use DMA transfer completion callbacks for channels they did not allocate. - * (The same applies to TCC codes used in transfer chaining.) - * - * Returns the number of the channel, else negative errno. - */ -int edma_alloc_channel(int channel, - void (*callback)(unsigned channel, u16 ch_status, void *data), - void *data, - enum dma_event_q eventq_no) -{ - unsigned i, done = 0, ctlr = 0; - int ret = 0; - - if (!unused_chan_list_done) { - /* - * Scan all the platform devices to find out the EDMA channels - * used and clear them in the unused list, making the rest - * available for ARM usage. - */ - ret = bus_for_each_dev(&platform_bus_type, NULL, NULL, - prepare_unused_channel_list); - if (ret < 0) - return ret; - - unused_chan_list_done = true; - } - - if (channel >= 0) { - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - } - - if (channel < 0) { - for (i = 0; i < arch_num_cc; i++) { - channel = 0; - for (;;) { - channel = find_next_bit(edma_cc[i]->edma_unused, - edma_cc[i]->num_channels, - channel); - if (channel == edma_cc[i]->num_channels) - break; - if (!test_and_set_bit(channel, - edma_cc[i]->edma_inuse)) { - done = 1; - ctlr = i; - break; - } - channel++; - } - if (done) - break; - } - if (!done) - return -ENOMEM; - } else if (channel >= edma_cc[ctlr]->num_channels) { - return -EINVAL; - } else if (test_and_set_bit(channel, edma_cc[ctlr]->edma_inuse)) { - return -EBUSY; - } - - /* ensure access through shadow region 0 */ - edma_or_array2(ctlr, EDMA_DRAE, 0, channel >> 5, BIT(channel & 0x1f)); - - /* ensure no events are pending */ - edma_stop(EDMA_CTLR_CHAN(ctlr, channel)); - memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(channel), - &dummy_paramset, PARM_SIZE); - - if (callback) - setup_dma_interrupt(EDMA_CTLR_CHAN(ctlr, channel), - callback, data); - - map_dmach_queue(ctlr, channel, eventq_no); - - return EDMA_CTLR_CHAN(ctlr, channel); -} -EXPORT_SYMBOL(edma_alloc_channel); - - -/** - * edma_free_channel - deallocate DMA channel - * @channel: dma channel returned from edma_alloc_channel() - * - * This deallocates the DMA channel and associated parameter RAM slot - * allocated by edma_alloc_channel(). - * - * Callers are responsible for ensuring the channel is inactive, and - * will not be reactivated by linking, chaining, or software calls to - * edma_start(). - */ -void edma_free_channel(unsigned channel) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - - if (channel >= edma_cc[ctlr]->num_channels) - return; - - setup_dma_interrupt(channel, NULL, NULL); - /* REVISIT should probably take out of shadow region 0 */ - - memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(channel), - &dummy_paramset, PARM_SIZE); - clear_bit(channel, edma_cc[ctlr]->edma_inuse); -} -EXPORT_SYMBOL(edma_free_channel); - -/** - * edma_alloc_slot - allocate DMA parameter RAM - * @slot: specific slot to allocate; negative for "any unused slot" - * - * This allocates a parameter RAM slot, initializing it to hold a - * dummy transfer. Slots allocated using this routine have not been - * mapped to a hardware DMA channel, and will normally be used by - * linking to them from a slot associated with a DMA channel. - * - * Normal use is to pass EDMA_SLOT_ANY as the @slot, but specific - * slots may be allocated on behalf of DSP firmware. - * - * Returns the number of the slot, else negative errno. - */ -int edma_alloc_slot(unsigned ctlr, int slot) -{ - if (!edma_cc[ctlr]) - return -EINVAL; - - if (slot >= 0) - slot = EDMA_CHAN_SLOT(slot); - - if (slot < 0) { - slot = edma_cc[ctlr]->num_channels; - for (;;) { - slot = find_next_zero_bit(edma_cc[ctlr]->edma_inuse, - edma_cc[ctlr]->num_slots, slot); - if (slot == edma_cc[ctlr]->num_slots) - return -ENOMEM; - if (!test_and_set_bit(slot, edma_cc[ctlr]->edma_inuse)) - break; - } - } else if (slot < edma_cc[ctlr]->num_channels || - slot >= edma_cc[ctlr]->num_slots) { - return -EINVAL; - } else if (test_and_set_bit(slot, edma_cc[ctlr]->edma_inuse)) { - return -EBUSY; - } - - memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot), - &dummy_paramset, PARM_SIZE); - - return EDMA_CTLR_CHAN(ctlr, slot); -} -EXPORT_SYMBOL(edma_alloc_slot); - -/** - * edma_free_slot - deallocate DMA parameter RAM - * @slot: parameter RAM slot returned from edma_alloc_slot() - * - * This deallocates the parameter RAM slot allocated by edma_alloc_slot(). - * Callers are responsible for ensuring the slot is inactive, and will - * not be activated. - */ -void edma_free_slot(unsigned slot) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot < edma_cc[ctlr]->num_channels || - slot >= edma_cc[ctlr]->num_slots) - return; - - memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot), - &dummy_paramset, PARM_SIZE); - clear_bit(slot, edma_cc[ctlr]->edma_inuse); -} -EXPORT_SYMBOL(edma_free_slot); - - -/** - * edma_alloc_cont_slots- alloc contiguous parameter RAM slots - * The API will return the starting point of a set of - * contiguous parameter RAM slots that have been requested - * - * @id: can only be EDMA_CONT_PARAMS_ANY or EDMA_CONT_PARAMS_FIXED_EXACT - * or EDMA_CONT_PARAMS_FIXED_NOT_EXACT - * @count: number of contiguous Paramter RAM slots - * @slot - the start value of Parameter RAM slot that should be passed if id - * is EDMA_CONT_PARAMS_FIXED_EXACT or EDMA_CONT_PARAMS_FIXED_NOT_EXACT - * - * If id is EDMA_CONT_PARAMS_ANY then the API starts looking for a set of - * contiguous Parameter RAM slots from parameter RAM 64 in the case of - * DaVinci SOCs and 32 in the case of DA8xx SOCs. - * - * If id is EDMA_CONT_PARAMS_FIXED_EXACT then the API starts looking for a - * set of contiguous parameter RAM slots from the "slot" that is passed as an - * argument to the API. - * - * If id is EDMA_CONT_PARAMS_FIXED_NOT_EXACT then the API initially tries - * starts looking for a set of contiguous parameter RAMs from the "slot" - * that is passed as an argument to the API. On failure the API will try to - * find a set of contiguous Parameter RAM slots from the remaining Parameter - * RAM slots - */ -int edma_alloc_cont_slots(unsigned ctlr, unsigned int id, int slot, int count) -{ - /* - * The start slot requested should be greater than - * the number of channels and lesser than the total number - * of slots - */ - if ((id != EDMA_CONT_PARAMS_ANY) && - (slot < edma_cc[ctlr]->num_channels || - slot >= edma_cc[ctlr]->num_slots)) - return -EINVAL; - - /* - * The number of parameter RAM slots requested cannot be less than 1 - * and cannot be more than the number of slots minus the number of - * channels - */ - if (count < 1 || count > - (edma_cc[ctlr]->num_slots - edma_cc[ctlr]->num_channels)) - return -EINVAL; - - switch (id) { - case EDMA_CONT_PARAMS_ANY: - return reserve_contiguous_slots(ctlr, id, count, - edma_cc[ctlr]->num_channels); - case EDMA_CONT_PARAMS_FIXED_EXACT: - case EDMA_CONT_PARAMS_FIXED_NOT_EXACT: - return reserve_contiguous_slots(ctlr, id, count, slot); - default: - return -EINVAL; - } - -} -EXPORT_SYMBOL(edma_alloc_cont_slots); - -/** - * edma_free_cont_slots - deallocate DMA parameter RAM slots - * @slot: first parameter RAM of a set of parameter RAM slots to be freed - * @count: the number of contiguous parameter RAM slots to be freed - * - * This deallocates the parameter RAM slots allocated by - * edma_alloc_cont_slots. - * Callers/applications need to keep track of sets of contiguous - * parameter RAM slots that have been allocated using the edma_alloc_cont_slots - * API. - * Callers are responsible for ensuring the slots are inactive, and will - * not be activated. - */ -int edma_free_cont_slots(unsigned slot, int count) -{ - unsigned ctlr, slot_to_free; - int i; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot < edma_cc[ctlr]->num_channels || - slot >= edma_cc[ctlr]->num_slots || - count < 1) - return -EINVAL; - - for (i = slot; i < slot + count; ++i) { - ctlr = EDMA_CTLR(i); - slot_to_free = EDMA_CHAN_SLOT(i); - - memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot_to_free), - &dummy_paramset, PARM_SIZE); - clear_bit(slot_to_free, edma_cc[ctlr]->edma_inuse); - } - - return 0; -} -EXPORT_SYMBOL(edma_free_cont_slots); - -/*-----------------------------------------------------------------------*/ - -/* Parameter RAM operations (i) -- read/write partial slots */ - -/** - * edma_set_src - set initial DMA source address in parameter RAM slot - * @slot: parameter RAM slot being configured - * @src_port: physical address of source (memory, controller FIFO, etc) - * @addressMode: INCR, except in very rare cases - * @fifoWidth: ignored unless @addressMode is FIFO, else specifies the - * width to use when addressing the fifo (e.g. W8BIT, W32BIT) - * - * Note that the source address is modified during the DMA transfer - * according to edma_set_src_index(). - */ -void edma_set_src(unsigned slot, dma_addr_t src_port, - enum address_mode mode, enum fifo_width width) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot < edma_cc[ctlr]->num_slots) { - unsigned int i = edma_parm_read(ctlr, PARM_OPT, slot); - - if (mode) { - /* set SAM and program FWID */ - i = (i & ~(EDMA_FWID)) | (SAM | ((width & 0x7) << 8)); - } else { - /* clear SAM */ - i &= ~SAM; - } - edma_parm_write(ctlr, PARM_OPT, slot, i); - - /* set the source port address - in source register of param structure */ - edma_parm_write(ctlr, PARM_SRC, slot, src_port); - } -} -EXPORT_SYMBOL(edma_set_src); - -/** - * edma_set_dest - set initial DMA destination address in parameter RAM slot - * @slot: parameter RAM slot being configured - * @dest_port: physical address of destination (memory, controller FIFO, etc) - * @addressMode: INCR, except in very rare cases - * @fifoWidth: ignored unless @addressMode is FIFO, else specifies the - * width to use when addressing the fifo (e.g. W8BIT, W32BIT) - * - * Note that the destination address is modified during the DMA transfer - * according to edma_set_dest_index(). - */ -void edma_set_dest(unsigned slot, dma_addr_t dest_port, - enum address_mode mode, enum fifo_width width) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot < edma_cc[ctlr]->num_slots) { - unsigned int i = edma_parm_read(ctlr, PARM_OPT, slot); - - if (mode) { - /* set DAM and program FWID */ - i = (i & ~(EDMA_FWID)) | (DAM | ((width & 0x7) << 8)); - } else { - /* clear DAM */ - i &= ~DAM; - } - edma_parm_write(ctlr, PARM_OPT, slot, i); - /* set the destination port address - in dest register of param structure */ - edma_parm_write(ctlr, PARM_DST, slot, dest_port); - } -} -EXPORT_SYMBOL(edma_set_dest); - -/** - * edma_get_position - returns the current transfer points - * @slot: parameter RAM slot being examined - * @src: pointer to source port position - * @dst: pointer to destination port position - * - * Returns current source and destination addresses for a particular - * parameter RAM slot. Its channel should not be active when this is called. - */ -void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst) -{ - struct edmacc_param temp; - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - edma_read_slot(EDMA_CTLR_CHAN(ctlr, slot), &temp); - if (src != NULL) - *src = temp.src; - if (dst != NULL) - *dst = temp.dst; -} -EXPORT_SYMBOL(edma_get_position); - -/** - * edma_set_src_index - configure DMA source address indexing - * @slot: parameter RAM slot being configured - * @src_bidx: byte offset between source arrays in a frame - * @src_cidx: byte offset between source frames in a block - * - * Offsets are specified to support either contiguous or discontiguous - * memory transfers, or repeated access to a hardware register, as needed. - * When accessing hardware registers, both offsets are normally zero. - */ -void edma_set_src_index(unsigned slot, s16 src_bidx, s16 src_cidx) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot < edma_cc[ctlr]->num_slots) { - edma_parm_modify(ctlr, PARM_SRC_DST_BIDX, slot, - 0xffff0000, src_bidx); - edma_parm_modify(ctlr, PARM_SRC_DST_CIDX, slot, - 0xffff0000, src_cidx); - } -} -EXPORT_SYMBOL(edma_set_src_index); - -/** - * edma_set_dest_index - configure DMA destination address indexing - * @slot: parameter RAM slot being configured - * @dest_bidx: byte offset between destination arrays in a frame - * @dest_cidx: byte offset between destination frames in a block - * - * Offsets are specified to support either contiguous or discontiguous - * memory transfers, or repeated access to a hardware register, as needed. - * When accessing hardware registers, both offsets are normally zero. - */ -void edma_set_dest_index(unsigned slot, s16 dest_bidx, s16 dest_cidx) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot < edma_cc[ctlr]->num_slots) { - edma_parm_modify(ctlr, PARM_SRC_DST_BIDX, slot, - 0x0000ffff, dest_bidx << 16); - edma_parm_modify(ctlr, PARM_SRC_DST_CIDX, slot, - 0x0000ffff, dest_cidx << 16); - } -} -EXPORT_SYMBOL(edma_set_dest_index); - -/** - * edma_set_transfer_params - configure DMA transfer parameters - * @slot: parameter RAM slot being configured - * @acnt: how many bytes per array (at least one) - * @bcnt: how many arrays per frame (at least one) - * @ccnt: how many frames per block (at least one) - * @bcnt_rld: used only for A-Synchronized transfers; this specifies - * the value to reload into bcnt when it decrements to zero - * @sync_mode: ASYNC or ABSYNC - * - * See the EDMA3 documentation to understand how to configure and link - * transfers using the fields in PaRAM slots. If you are not doing it - * all at once with edma_write_slot(), you will use this routine - * plus two calls each for source and destination, setting the initial - * address and saying how to index that address. - * - * An example of an A-Synchronized transfer is a serial link using a - * single word shift register. In that case, @acnt would be equal to - * that word size; the serial controller issues a DMA synchronization - * event to transfer each word, and memory access by the DMA transfer - * controller will be word-at-a-time. - * - * An example of an AB-Synchronized transfer is a device using a FIFO. - * In that case, @acnt equals the FIFO width and @bcnt equals its depth. - * The controller with the FIFO issues DMA synchronization events when - * the FIFO threshold is reached, and the DMA transfer controller will - * transfer one frame to (or from) the FIFO. It will probably use - * efficient burst modes to access memory. - */ -void edma_set_transfer_params(unsigned slot, - u16 acnt, u16 bcnt, u16 ccnt, - u16 bcnt_rld, enum sync_dimension sync_mode) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot < edma_cc[ctlr]->num_slots) { - edma_parm_modify(ctlr, PARM_LINK_BCNTRLD, slot, - 0x0000ffff, bcnt_rld << 16); - if (sync_mode == ASYNC) - edma_parm_and(ctlr, PARM_OPT, slot, ~SYNCDIM); - else - edma_parm_or(ctlr, PARM_OPT, slot, SYNCDIM); - /* Set the acount, bcount, ccount registers */ - edma_parm_write(ctlr, PARM_A_B_CNT, slot, (bcnt << 16) | acnt); - edma_parm_write(ctlr, PARM_CCNT, slot, ccnt); - } -} -EXPORT_SYMBOL(edma_set_transfer_params); - -/** - * edma_link - link one parameter RAM slot to another - * @from: parameter RAM slot originating the link - * @to: parameter RAM slot which is the link target - * - * The originating slot should not be part of any active DMA transfer. - */ -void edma_link(unsigned from, unsigned to) -{ - unsigned ctlr_from, ctlr_to; - - ctlr_from = EDMA_CTLR(from); - from = EDMA_CHAN_SLOT(from); - ctlr_to = EDMA_CTLR(to); - to = EDMA_CHAN_SLOT(to); - - if (from >= edma_cc[ctlr_from]->num_slots) - return; - if (to >= edma_cc[ctlr_to]->num_slots) - return; - edma_parm_modify(ctlr_from, PARM_LINK_BCNTRLD, from, 0xffff0000, - PARM_OFFSET(to)); -} -EXPORT_SYMBOL(edma_link); - -/** - * edma_unlink - cut link from one parameter RAM slot - * @from: parameter RAM slot originating the link - * - * The originating slot should not be part of any active DMA transfer. - * Its link is set to 0xffff. - */ -void edma_unlink(unsigned from) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(from); - from = EDMA_CHAN_SLOT(from); - - if (from >= edma_cc[ctlr]->num_slots) - return; - edma_parm_or(ctlr, PARM_LINK_BCNTRLD, from, 0xffff); -} -EXPORT_SYMBOL(edma_unlink); - -/*-----------------------------------------------------------------------*/ - -/* Parameter RAM operations (ii) -- read/write whole parameter sets */ - -/** - * edma_write_slot - write parameter RAM data for slot - * @slot: number of parameter RAM slot being modified - * @param: data to be written into parameter RAM slot - * - * Use this to assign all parameters of a transfer at once. This - * allows more efficient setup of transfers than issuing multiple - * calls to set up those parameters in small pieces, and provides - * complete control over all transfer options. - */ -void edma_write_slot(unsigned slot, const struct edmacc_param *param) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot >= edma_cc[ctlr]->num_slots) - return; - memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot), param, - PARM_SIZE); -} -EXPORT_SYMBOL(edma_write_slot); - -/** - * edma_read_slot - read parameter RAM data from slot - * @slot: number of parameter RAM slot being copied - * @param: where to store copy of parameter RAM data - * - * Use this to read data from a parameter RAM slot, perhaps to - * save them as a template for later reuse. - */ -void edma_read_slot(unsigned slot, struct edmacc_param *param) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(slot); - slot = EDMA_CHAN_SLOT(slot); - - if (slot >= edma_cc[ctlr]->num_slots) - return; - memcpy_fromio(param, edmacc_regs_base[ctlr] + PARM_OFFSET(slot), - PARM_SIZE); -} -EXPORT_SYMBOL(edma_read_slot); - -/*-----------------------------------------------------------------------*/ - -/* Various EDMA channel control operations */ - -/** - * edma_pause - pause dma on a channel - * @channel: on which edma_start() has been called - * - * This temporarily disables EDMA hardware events on the specified channel, - * preventing them from triggering new transfers on its behalf - */ -void edma_pause(unsigned channel) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - - if (channel < edma_cc[ctlr]->num_channels) { - unsigned int mask = BIT(channel & 0x1f); - - edma_shadow0_write_array(ctlr, SH_EECR, channel >> 5, mask); - } -} -EXPORT_SYMBOL(edma_pause); - -/** - * edma_resume - resumes dma on a paused channel - * @channel: on which edma_pause() has been called - * - * This re-enables EDMA hardware events on the specified channel. - */ -void edma_resume(unsigned channel) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - - if (channel < edma_cc[ctlr]->num_channels) { - unsigned int mask = BIT(channel & 0x1f); - - edma_shadow0_write_array(ctlr, SH_EESR, channel >> 5, mask); - } -} -EXPORT_SYMBOL(edma_resume); - -/** - * edma_start - start dma on a channel - * @channel: channel being activated - * - * Channels with event associations will be triggered by their hardware - * events, and channels without such associations will be triggered by - * software. (At this writing there is no interface for using software - * triggers except with channels that don't support hardware triggers.) - * - * Returns zero on success, else negative errno. - */ -int edma_start(unsigned channel) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - - if (channel < edma_cc[ctlr]->num_channels) { - int j = channel >> 5; - unsigned int mask = BIT(channel & 0x1f); - - /* EDMA channels without event association */ - if (test_bit(channel, edma_cc[ctlr]->edma_unused)) { - pr_debug("EDMA: ESR%d %08x\n", j, - edma_shadow0_read_array(ctlr, SH_ESR, j)); - edma_shadow0_write_array(ctlr, SH_ESR, j, mask); - return 0; - } - - /* EDMA channel with event association */ - pr_debug("EDMA: ER%d %08x\n", j, - edma_shadow0_read_array(ctlr, SH_ER, j)); - /* Clear any pending event or error */ - edma_write_array(ctlr, EDMA_ECR, j, mask); - edma_write_array(ctlr, EDMA_EMCR, j, mask); - /* Clear any SER */ - edma_shadow0_write_array(ctlr, SH_SECR, j, mask); - edma_shadow0_write_array(ctlr, SH_EESR, j, mask); - pr_debug("EDMA: EER%d %08x\n", j, - edma_shadow0_read_array(ctlr, SH_EER, j)); - return 0; - } - - return -EINVAL; -} -EXPORT_SYMBOL(edma_start); - -/** - * edma_stop - stops dma on the channel passed - * @channel: channel being deactivated - * - * When @lch is a channel, any active transfer is paused and - * all pending hardware events are cleared. The current transfer - * may not be resumed, and the channel's Parameter RAM should be - * reinitialized before being reused. - */ -void edma_stop(unsigned channel) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - - if (channel < edma_cc[ctlr]->num_channels) { - int j = channel >> 5; - unsigned int mask = BIT(channel & 0x1f); - - edma_shadow0_write_array(ctlr, SH_EECR, j, mask); - edma_shadow0_write_array(ctlr, SH_ECR, j, mask); - edma_shadow0_write_array(ctlr, SH_SECR, j, mask); - edma_write_array(ctlr, EDMA_EMCR, j, mask); - - pr_debug("EDMA: EER%d %08x\n", j, - edma_shadow0_read_array(ctlr, SH_EER, j)); - - /* REVISIT: consider guarding against inappropriate event - * chaining by overwriting with dummy_paramset. - */ - } -} -EXPORT_SYMBOL(edma_stop); - -/****************************************************************************** - * - * It cleans ParamEntry qand bring back EDMA to initial state if media has - * been removed before EDMA has finished.It is usedful for removable media. - * Arguments: - * ch_no - channel no - * - * Return: zero on success, or corresponding error no on failure - * - * FIXME this should not be needed ... edma_stop() should suffice. - * - *****************************************************************************/ - -void edma_clean_channel(unsigned channel) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - - if (channel < edma_cc[ctlr]->num_channels) { - int j = (channel >> 5); - unsigned int mask = BIT(channel & 0x1f); - - pr_debug("EDMA: EMR%d %08x\n", j, - edma_read_array(ctlr, EDMA_EMR, j)); - edma_shadow0_write_array(ctlr, SH_ECR, j, mask); - /* Clear the corresponding EMR bits */ - edma_write_array(ctlr, EDMA_EMCR, j, mask); - /* Clear any SER */ - edma_shadow0_write_array(ctlr, SH_SECR, j, mask); - edma_write(ctlr, EDMA_CCERRCLR, BIT(16) | BIT(1) | BIT(0)); - } -} -EXPORT_SYMBOL(edma_clean_channel); - -/* - * edma_clear_event - clear an outstanding event on the DMA channel - * Arguments: - * channel - channel number - */ -void edma_clear_event(unsigned channel) -{ - unsigned ctlr; - - ctlr = EDMA_CTLR(channel); - channel = EDMA_CHAN_SLOT(channel); - - if (channel >= edma_cc[ctlr]->num_channels) - return; - if (channel < 32) - edma_write(ctlr, EDMA_ECR, BIT(channel)); - else - edma_write(ctlr, EDMA_ECRH, BIT(channel - 32)); -} -EXPORT_SYMBOL(edma_clear_event); - -/*-----------------------------------------------------------------------*/ - -static int __init edma_probe(struct platform_device *pdev) -{ - struct edma_soc_info **info = pdev->dev.platform_data; - const s8 (*queue_priority_mapping)[2]; - const s8 (*queue_tc_mapping)[2]; - int i, j, off, ln, found = 0; - int status = -1; - const s16 (*rsv_chans)[2]; - const s16 (*rsv_slots)[2]; - int irq[EDMA_MAX_CC] = {0, 0}; - int err_irq[EDMA_MAX_CC] = {0, 0}; - struct resource *r[EDMA_MAX_CC] = {NULL}; - resource_size_t len[EDMA_MAX_CC]; - char res_name[10]; - char irq_name[10]; - - if (!info) - return -ENODEV; - - for (j = 0; j < EDMA_MAX_CC; j++) { - sprintf(res_name, "edma_cc%d", j); - r[j] = platform_get_resource_byname(pdev, IORESOURCE_MEM, - res_name); - if (!r[j] || !info[j]) { - if (found) - break; - else - return -ENODEV; - } else { - found = 1; - } - - len[j] = resource_size(r[j]); - - r[j] = request_mem_region(r[j]->start, len[j], - dev_name(&pdev->dev)); - if (!r[j]) { - status = -EBUSY; - goto fail1; - } - - edmacc_regs_base[j] = ioremap(r[j]->start, len[j]); - if (!edmacc_regs_base[j]) { - status = -EBUSY; - goto fail1; - } - - edma_cc[j] = kzalloc(sizeof(struct edma), GFP_KERNEL); - if (!edma_cc[j]) { - status = -ENOMEM; - goto fail1; - } - - edma_cc[j]->num_channels = min_t(unsigned, info[j]->n_channel, - EDMA_MAX_DMACH); - edma_cc[j]->num_slots = min_t(unsigned, info[j]->n_slot, - EDMA_MAX_PARAMENTRY); - edma_cc[j]->num_cc = min_t(unsigned, info[j]->n_cc, - EDMA_MAX_CC); - - edma_cc[j]->default_queue = info[j]->default_queue; - - dev_dbg(&pdev->dev, "DMA REG BASE ADDR=%p\n", - edmacc_regs_base[j]); - - for (i = 0; i < edma_cc[j]->num_slots; i++) - memcpy_toio(edmacc_regs_base[j] + PARM_OFFSET(i), - &dummy_paramset, PARM_SIZE); - - /* Mark all channels as unused */ - memset(edma_cc[j]->edma_unused, 0xff, - sizeof(edma_cc[j]->edma_unused)); - - if (info[j]->rsv) { - - /* Clear the reserved channels in unused list */ - rsv_chans = info[j]->rsv->rsv_chans; - if (rsv_chans) { - for (i = 0; rsv_chans[i][0] != -1; i++) { - off = rsv_chans[i][0]; - ln = rsv_chans[i][1]; - clear_bits(off, ln, - edma_cc[j]->edma_unused); - } - } - - /* Set the reserved slots in inuse list */ - rsv_slots = info[j]->rsv->rsv_slots; - if (rsv_slots) { - for (i = 0; rsv_slots[i][0] != -1; i++) { - off = rsv_slots[i][0]; - ln = rsv_slots[i][1]; - set_bits(off, ln, - edma_cc[j]->edma_inuse); - } - } - } - - sprintf(irq_name, "edma%d", j); - irq[j] = platform_get_irq_byname(pdev, irq_name); - edma_cc[j]->irq_res_start = irq[j]; - status = request_irq(irq[j], dma_irq_handler, 0, "edma", - &pdev->dev); - if (status < 0) { - dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n", - irq[j], status); - goto fail; - } - - sprintf(irq_name, "edma%d_err", j); - err_irq[j] = platform_get_irq_byname(pdev, irq_name); - edma_cc[j]->irq_res_end = err_irq[j]; - status = request_irq(err_irq[j], dma_ccerr_handler, 0, - "edma_error", &pdev->dev); - if (status < 0) { - dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n", - err_irq[j], status); - goto fail; - } - - for (i = 0; i < edma_cc[j]->num_channels; i++) - map_dmach_queue(j, i, info[j]->default_queue); - - queue_tc_mapping = info[j]->queue_tc_mapping; - queue_priority_mapping = info[j]->queue_priority_mapping; - - /* Event queue to TC mapping */ - for (i = 0; queue_tc_mapping[i][0] != -1; i++) - map_queue_tc(j, queue_tc_mapping[i][0], - queue_tc_mapping[i][1]); - - /* Event queue priority mapping */ - for (i = 0; queue_priority_mapping[i][0] != -1; i++) - assign_priority_to_queue(j, - queue_priority_mapping[i][0], - queue_priority_mapping[i][1]); - - /* Map the channel to param entry if channel mapping logic - * exist - */ - if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST) - map_dmach_param(j); - - for (i = 0; i < info[j]->n_region; i++) { - edma_write_array2(j, EDMA_DRAE, i, 0, 0x0); - edma_write_array2(j, EDMA_DRAE, i, 1, 0x0); - edma_write_array(j, EDMA_QRAE, i, 0x0); - } - arch_num_cc++; - } - - if (tc_errs_handled) { - status = request_irq(IRQ_TCERRINT0, dma_tc0err_handler, 0, - "edma_tc0", &pdev->dev); - if (status < 0) { - dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n", - IRQ_TCERRINT0, status); - return status; - } - status = request_irq(IRQ_TCERRINT, dma_tc1err_handler, 0, - "edma_tc1", &pdev->dev); - if (status < 0) { - dev_dbg(&pdev->dev, "request_irq %d --> %d\n", - IRQ_TCERRINT, status); - return status; - } - } - - return 0; - -fail: - for (i = 0; i < EDMA_MAX_CC; i++) { - if (err_irq[i]) - free_irq(err_irq[i], &pdev->dev); - if (irq[i]) - free_irq(irq[i], &pdev->dev); - } -fail1: - for (i = 0; i < EDMA_MAX_CC; i++) { - if (r[i]) - release_mem_region(r[i]->start, len[i]); - if (edmacc_regs_base[i]) - iounmap(edmacc_regs_base[i]); - kfree(edma_cc[i]); - } - return status; -} - - -static struct platform_driver edma_driver = { - .driver.name = "edma", -}; - -static int __init edma_init(void) -{ - return platform_driver_probe(&edma_driver, edma_probe); -} -arch_initcall(edma_init); - diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index faec783b0dc8..3c797e2272f8 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -20,8 +20,8 @@ #include #include -#include #include +#include #include #include #include diff --git a/arch/arm/mach-davinci/include/mach/edma.h b/arch/arm/mach-davinci/include/mach/edma.h deleted file mode 100644 index 7e84c906ceff..000000000000 --- a/arch/arm/mach-davinci/include/mach/edma.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * TI DAVINCI dma definitions - * - * Copyright (C) 2006-2009 Texas Instruments. - * - * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * 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., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -/* - * This EDMA3 programming framework exposes two basic kinds of resource: - * - * Channel Triggers transfers, usually from a hardware event but - * also manually or by "chaining" from DMA completions. - * Each channel is coupled to a Parameter RAM (PaRAM) slot. - * - * Slot Each PaRAM slot holds a DMA transfer descriptor (PaRAM - * "set"), source and destination addresses, a link to a - * next PaRAM slot (if any), options for the transfer, and - * instructions for updating those addresses. There are - * more than twice as many slots as event channels. - * - * Each PaRAM set describes a sequence of transfers, either for one large - * buffer or for several discontiguous smaller buffers. An EDMA transfer - * is driven only from a channel, which performs the transfers specified - * in its PaRAM slot until there are no more transfers. When that last - * transfer completes, the "link" field may be used to reload the channel's - * PaRAM slot with a new transfer descriptor. - * - * The EDMA Channel Controller (CC) maps requests from channels into physical - * Transfer Controller (TC) requests when the channel triggers (by hardware - * or software events, or by chaining). The two physical DMA channels provided - * by the TCs are thus shared by many logical channels. - * - * DaVinci hardware also has a "QDMA" mechanism which is not currently - * supported through this interface. (DSP firmware uses it though.) - */ - -#ifndef EDMA_H_ -#define EDMA_H_ - -/* PaRAM slots are laid out like this */ -struct edmacc_param { - unsigned int opt; - unsigned int src; - unsigned int a_b_cnt; - unsigned int dst; - unsigned int src_dst_bidx; - unsigned int link_bcntrld; - unsigned int src_dst_cidx; - unsigned int ccnt; -}; - -#define CCINT0_INTERRUPT 16 -#define CCERRINT_INTERRUPT 17 -#define TCERRINT0_INTERRUPT 18 -#define TCERRINT1_INTERRUPT 19 - -/* fields in edmacc_param.opt */ -#define SAM BIT(0) -#define DAM BIT(1) -#define SYNCDIM BIT(2) -#define STATIC BIT(3) -#define EDMA_FWID (0x07 << 8) -#define TCCMODE BIT(11) -#define EDMA_TCC(t) ((t) << 12) -#define TCINTEN BIT(20) -#define ITCINTEN BIT(21) -#define TCCHEN BIT(22) -#define ITCCHEN BIT(23) - -#define TRWORD (0x7<<2) -#define PAENTRY (0x1ff<<5) - -/* Drivers should avoid using these symbolic names for dm644x - * channels, and use platform_device IORESOURCE_DMA resources - * instead. (Other DaVinci chips have different peripherals - * and thus have different DMA channel mappings.) - */ -#define DAVINCI_DMA_MCBSP_TX 2 -#define DAVINCI_DMA_MCBSP_RX 3 -#define DAVINCI_DMA_VPSS_HIST 4 -#define DAVINCI_DMA_VPSS_H3A 5 -#define DAVINCI_DMA_VPSS_PRVU 6 -#define DAVINCI_DMA_VPSS_RSZ 7 -#define DAVINCI_DMA_IMCOP_IMXINT 8 -#define DAVINCI_DMA_IMCOP_VLCDINT 9 -#define DAVINCI_DMA_IMCO_PASQINT 10 -#define DAVINCI_DMA_IMCOP_DSQINT 11 -#define DAVINCI_DMA_SPI_SPIX 16 -#define DAVINCI_DMA_SPI_SPIR 17 -#define DAVINCI_DMA_UART0_URXEVT0 18 -#define DAVINCI_DMA_UART0_UTXEVT0 19 -#define DAVINCI_DMA_UART1_URXEVT1 20 -#define DAVINCI_DMA_UART1_UTXEVT1 21 -#define DAVINCI_DMA_UART2_URXEVT2 22 -#define DAVINCI_DMA_UART2_UTXEVT2 23 -#define DAVINCI_DMA_MEMSTK_MSEVT 24 -#define DAVINCI_DMA_MMCRXEVT 26 -#define DAVINCI_DMA_MMCTXEVT 27 -#define DAVINCI_DMA_I2C_ICREVT 28 -#define DAVINCI_DMA_I2C_ICXEVT 29 -#define DAVINCI_DMA_GPIO_GPINT0 32 -#define DAVINCI_DMA_GPIO_GPINT1 33 -#define DAVINCI_DMA_GPIO_GPINT2 34 -#define DAVINCI_DMA_GPIO_GPINT3 35 -#define DAVINCI_DMA_GPIO_GPINT4 36 -#define DAVINCI_DMA_GPIO_GPINT5 37 -#define DAVINCI_DMA_GPIO_GPINT6 38 -#define DAVINCI_DMA_GPIO_GPINT7 39 -#define DAVINCI_DMA_GPIO_GPBNKINT0 40 -#define DAVINCI_DMA_GPIO_GPBNKINT1 41 -#define DAVINCI_DMA_GPIO_GPBNKINT2 42 -#define DAVINCI_DMA_GPIO_GPBNKINT3 43 -#define DAVINCI_DMA_GPIO_GPBNKINT4 44 -#define DAVINCI_DMA_TIMER0_TINT0 48 -#define DAVINCI_DMA_TIMER1_TINT1 49 -#define DAVINCI_DMA_TIMER2_TINT2 50 -#define DAVINCI_DMA_TIMER3_TINT3 51 -#define DAVINCI_DMA_PWM0 52 -#define DAVINCI_DMA_PWM1 53 -#define DAVINCI_DMA_PWM2 54 - -/* DA830 specific EDMA3 information */ -#define EDMA_DA830_NUM_DMACH 32 -#define EDMA_DA830_NUM_TCC 32 -#define EDMA_DA830_NUM_PARAMENTRY 128 -#define EDMA_DA830_NUM_EVQUE 2 -#define EDMA_DA830_NUM_TC 2 -#define EDMA_DA830_CHMAP_EXIST 0 -#define EDMA_DA830_NUM_REGIONS 4 -#define DA830_DMACH2EVENT_MAP0 0x000FC03Fu -#define DA830_DMACH2EVENT_MAP1 0x00000000u -#define DA830_EDMA_ARM_OWN 0x30FFCCFFu - -/*ch_status paramater of callback function possible values*/ -#define DMA_COMPLETE 1 -#define DMA_CC_ERROR 2 -#define DMA_TC1_ERROR 3 -#define DMA_TC2_ERROR 4 - -enum address_mode { - INCR = 0, - FIFO = 1 -}; - -enum fifo_width { - W8BIT = 0, - W16BIT = 1, - W32BIT = 2, - W64BIT = 3, - W128BIT = 4, - W256BIT = 5 -}; - -enum dma_event_q { - EVENTQ_0 = 0, - EVENTQ_1 = 1, - EVENTQ_2 = 2, - EVENTQ_3 = 3, - EVENTQ_DEFAULT = -1 -}; - -enum sync_dimension { - ASYNC = 0, - ABSYNC = 1 -}; - -#define EDMA_CTLR_CHAN(ctlr, chan) (((ctlr) << 16) | (chan)) -#define EDMA_CTLR(i) ((i) >> 16) -#define EDMA_CHAN_SLOT(i) ((i) & 0xffff) - -#define EDMA_CHANNEL_ANY -1 /* for edma_alloc_channel() */ -#define EDMA_SLOT_ANY -1 /* for edma_alloc_slot() */ -#define EDMA_CONT_PARAMS_ANY 1001 -#define EDMA_CONT_PARAMS_FIXED_EXACT 1002 -#define EDMA_CONT_PARAMS_FIXED_NOT_EXACT 1003 - -#define EDMA_MAX_CC 2 - -/* alloc/free DMA channels and their dedicated parameter RAM slots */ -int edma_alloc_channel(int channel, - void (*callback)(unsigned channel, u16 ch_status, void *data), - void *data, enum dma_event_q); -void edma_free_channel(unsigned channel); - -/* alloc/free parameter RAM slots */ -int edma_alloc_slot(unsigned ctlr, int slot); -void edma_free_slot(unsigned slot); - -/* alloc/free a set of contiguous parameter RAM slots */ -int edma_alloc_cont_slots(unsigned ctlr, unsigned int id, int slot, int count); -int edma_free_cont_slots(unsigned slot, int count); - -/* calls that operate on part of a parameter RAM slot */ -void edma_set_src(unsigned slot, dma_addr_t src_port, - enum address_mode mode, enum fifo_width); -void edma_set_dest(unsigned slot, dma_addr_t dest_port, - enum address_mode mode, enum fifo_width); -void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst); -void edma_set_src_index(unsigned slot, s16 src_bidx, s16 src_cidx); -void edma_set_dest_index(unsigned slot, s16 dest_bidx, s16 dest_cidx); -void edma_set_transfer_params(unsigned slot, u16 acnt, u16 bcnt, u16 ccnt, - u16 bcnt_rld, enum sync_dimension sync_mode); -void edma_link(unsigned from, unsigned to); -void edma_unlink(unsigned from); - -/* calls that operate on an entire parameter RAM slot */ -void edma_write_slot(unsigned slot, const struct edmacc_param *params); -void edma_read_slot(unsigned slot, struct edmacc_param *params); - -/* channel control operations */ -int edma_start(unsigned channel); -void edma_stop(unsigned channel); -void edma_clean_channel(unsigned channel); -void edma_clear_event(unsigned channel); -void edma_pause(unsigned channel); -void edma_resume(unsigned channel); - -struct edma_rsv_info { - - const s16 (*rsv_chans)[2]; - const s16 (*rsv_slots)[2]; -}; - -/* platform_data for EDMA driver */ -struct edma_soc_info { - - /* how many dma resources of each type */ - unsigned n_channel; - unsigned n_region; - unsigned n_slot; - unsigned n_tc; - unsigned n_cc; - /* - * Default queue is expected to be a low-priority queue. - * This way, long transfers on the default queue started - * by the codec engine will not cause audio defects. - */ - enum dma_event_q default_queue; - - /* Resource reservation for other cores */ - struct edma_rsv_info *rsv; - - const s8 (*queue_tc_mapping)[2]; - const s8 (*queue_priority_mapping)[2]; -}; - -#endif diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index cd7e3280fadd..5f3e532436ee 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -24,7 +24,7 @@ #include #include -#include +#include #include "dmaengine.h" #include "virt-dma.h" diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 3946a0eb3a03..5dfb70c669dc 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -37,6 +37,7 @@ #include #include +#include #include /* diff --git a/include/linux/mfd/davinci_voicecodec.h b/include/linux/mfd/davinci_voicecodec.h index 0ab61320ffa8..7dd6524d2aac 100644 --- a/include/linux/mfd/davinci_voicecodec.h +++ b/include/linux/mfd/davinci_voicecodec.h @@ -26,8 +26,7 @@ #include #include #include - -#include +#include /* * Register values. diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h new file mode 100644 index 000000000000..2344ea2675ad --- /dev/null +++ b/include/linux/platform_data/edma.h @@ -0,0 +1,182 @@ +/* + * TI EDMA definitions + * + * Copyright (C) 2006-2013 Texas Instruments. + * + * 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 EDMA3 programming framework exposes two basic kinds of resource: + * + * Channel Triggers transfers, usually from a hardware event but + * also manually or by "chaining" from DMA completions. + * Each channel is coupled to a Parameter RAM (PaRAM) slot. + * + * Slot Each PaRAM slot holds a DMA transfer descriptor (PaRAM + * "set"), source and destination addresses, a link to a + * next PaRAM slot (if any), options for the transfer, and + * instructions for updating those addresses. There are + * more than twice as many slots as event channels. + * + * Each PaRAM set describes a sequence of transfers, either for one large + * buffer or for several discontiguous smaller buffers. An EDMA transfer + * is driven only from a channel, which performs the transfers specified + * in its PaRAM slot until there are no more transfers. When that last + * transfer completes, the "link" field may be used to reload the channel's + * PaRAM slot with a new transfer descriptor. + * + * The EDMA Channel Controller (CC) maps requests from channels into physical + * Transfer Controller (TC) requests when the channel triggers (by hardware + * or software events, or by chaining). The two physical DMA channels provided + * by the TCs are thus shared by many logical channels. + * + * DaVinci hardware also has a "QDMA" mechanism which is not currently + * supported through this interface. (DSP firmware uses it though.) + */ + +#ifndef EDMA_H_ +#define EDMA_H_ + +/* PaRAM slots are laid out like this */ +struct edmacc_param { + unsigned int opt; + unsigned int src; + unsigned int a_b_cnt; + unsigned int dst; + unsigned int src_dst_bidx; + unsigned int link_bcntrld; + unsigned int src_dst_cidx; + unsigned int ccnt; +}; + +/* fields in edmacc_param.opt */ +#define SAM BIT(0) +#define DAM BIT(1) +#define SYNCDIM BIT(2) +#define STATIC BIT(3) +#define EDMA_FWID (0x07 << 8) +#define TCCMODE BIT(11) +#define EDMA_TCC(t) ((t) << 12) +#define TCINTEN BIT(20) +#define ITCINTEN BIT(21) +#define TCCHEN BIT(22) +#define ITCCHEN BIT(23) + +/*ch_status paramater of callback function possible values*/ +#define DMA_COMPLETE 1 +#define DMA_CC_ERROR 2 +#define DMA_TC1_ERROR 3 +#define DMA_TC2_ERROR 4 + +enum address_mode { + INCR = 0, + FIFO = 1 +}; + +enum fifo_width { + W8BIT = 0, + W16BIT = 1, + W32BIT = 2, + W64BIT = 3, + W128BIT = 4, + W256BIT = 5 +}; + +enum dma_event_q { + EVENTQ_0 = 0, + EVENTQ_1 = 1, + EVENTQ_2 = 2, + EVENTQ_3 = 3, + EVENTQ_DEFAULT = -1 +}; + +enum sync_dimension { + ASYNC = 0, + ABSYNC = 1 +}; + +#define EDMA_CTLR_CHAN(ctlr, chan) (((ctlr) << 16) | (chan)) +#define EDMA_CTLR(i) ((i) >> 16) +#define EDMA_CHAN_SLOT(i) ((i) & 0xffff) + +#define EDMA_CHANNEL_ANY -1 /* for edma_alloc_channel() */ +#define EDMA_SLOT_ANY -1 /* for edma_alloc_slot() */ +#define EDMA_CONT_PARAMS_ANY 1001 +#define EDMA_CONT_PARAMS_FIXED_EXACT 1002 +#define EDMA_CONT_PARAMS_FIXED_NOT_EXACT 1003 + +#define EDMA_MAX_CC 2 + +/* alloc/free DMA channels and their dedicated parameter RAM slots */ +int edma_alloc_channel(int channel, + void (*callback)(unsigned channel, u16 ch_status, void *data), + void *data, enum dma_event_q); +void edma_free_channel(unsigned channel); + +/* alloc/free parameter RAM slots */ +int edma_alloc_slot(unsigned ctlr, int slot); +void edma_free_slot(unsigned slot); + +/* alloc/free a set of contiguous parameter RAM slots */ +int edma_alloc_cont_slots(unsigned ctlr, unsigned int id, int slot, int count); +int edma_free_cont_slots(unsigned slot, int count); + +/* calls that operate on part of a parameter RAM slot */ +void edma_set_src(unsigned slot, dma_addr_t src_port, + enum address_mode mode, enum fifo_width); +void edma_set_dest(unsigned slot, dma_addr_t dest_port, + enum address_mode mode, enum fifo_width); +void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst); +void edma_set_src_index(unsigned slot, s16 src_bidx, s16 src_cidx); +void edma_set_dest_index(unsigned slot, s16 dest_bidx, s16 dest_cidx); +void edma_set_transfer_params(unsigned slot, u16 acnt, u16 bcnt, u16 ccnt, + u16 bcnt_rld, enum sync_dimension sync_mode); +void edma_link(unsigned from, unsigned to); +void edma_unlink(unsigned from); + +/* calls that operate on an entire parameter RAM slot */ +void edma_write_slot(unsigned slot, const struct edmacc_param *params); +void edma_read_slot(unsigned slot, struct edmacc_param *params); + +/* channel control operations */ +int edma_start(unsigned channel); +void edma_stop(unsigned channel); +void edma_clean_channel(unsigned channel); +void edma_clear_event(unsigned channel); +void edma_pause(unsigned channel); +void edma_resume(unsigned channel); + +struct edma_rsv_info { + + const s16 (*rsv_chans)[2]; + const s16 (*rsv_slots)[2]; +}; + +/* platform_data for EDMA driver */ +struct edma_soc_info { + + /* how many dma resources of each type */ + unsigned n_channel; + unsigned n_region; + unsigned n_slot; + unsigned n_tc; + unsigned n_cc; + /* + * Default queue is expected to be a low-priority queue. + * This way, long transfers on the default queue started + * by the codec engine will not cause audio defects. + */ + enum dma_event_q default_queue; + + /* Resource reservation for other cores */ + struct edma_rsv_info *rsv; + + const s8 (*queue_tc_mapping)[2]; + const s8 (*queue_priority_mapping)[2]; +}; + +#endif diff --git a/include/linux/platform_data/spi-davinci.h b/include/linux/platform_data/spi-davinci.h index 7af305b37868..8dc2fa47a2aa 100644 --- a/include/linux/platform_data/spi-davinci.h +++ b/include/linux/platform_data/spi-davinci.h @@ -19,7 +19,7 @@ #ifndef __ARCH_ARM_DAVINCI_SPI_H #define __ARCH_ARM_DAVINCI_SPI_H -#include +#include #define SPI_INTERN_CS 0xFF diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 484b22c5df5d..fd7c45b9ed5a 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index b2f27c2e5fdc..8460edce1c3b 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h index b6ef7039dd09..fbb710c76c08 100644 --- a/sound/soc/davinci/davinci-pcm.h +++ b/sound/soc/davinci/davinci-pcm.h @@ -14,7 +14,7 @@ #include #include -#include +#include struct davinci_pcm_dma_params { int channel; /* sync dma channel ID */ -- cgit v1.2.3 From 6cba4355066bda19f14d4da66b8abbca0ffdfd59 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 20 Jun 2013 16:06:38 -0500 Subject: ARM: edma: Add DT and runtime PM support to the private EDMA API Adds support for parsing the TI EDMA DT data into the required EDMA private API platform data. Enables runtime PM support to initialize the EDMA hwmod. Enables build on OMAP. Changes by Joel: * Setup default one-to-one mapping for queue_priority and queue_tc mapping as discussed in [1]. * Split out xbar stuff to separate patch. [1] * Dropped unused DT helper to convert to array * Fixed dangling pointer issue with Sekhar's changes [1] https://patchwork.kernel.org/patch/2226761/ Signed-off-by: Matt Porter [nsekhar@ti.com: fix checkpatch errors, build breakages. Introduce edma_setup_info_from_dt() as part of that effort] Signed-off-by: Joel A Fernandes Acked-by: Arnd Bergmann Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 186 +++++++++++++++++++++++++++--- arch/arm/mach-davinci/devices-da8xx.c | 8 +- arch/arm/mach-davinci/devices-tnetv107x.c | 4 +- arch/arm/mach-davinci/dm355.c | 4 +- arch/arm/mach-davinci/dm365.c | 4 +- arch/arm/mach-davinci/dm644x.c | 4 +- arch/arm/mach-davinci/dm646x.c | 4 +- include/linux/platform_data/edma.h | 4 +- 8 files changed, 189 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 7658874cc3d5..5183a310657c 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -25,6 +25,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include @@ -1369,13 +1376,110 @@ void edma_clear_event(unsigned channel) } EXPORT_SYMBOL(edma_clear_event); -/*-----------------------------------------------------------------------*/ +#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DMADEVICES) + +static int edma_of_parse_dt(struct device *dev, + struct device_node *node, + struct edma_soc_info *pdata) +{ + int ret = 0, i; + u32 value; + struct edma_rsv_info *rsv_info; + s8 (*queue_tc_map)[2], (*queue_priority_map)[2]; + + memset(pdata, 0, sizeof(struct edma_soc_info)); + + ret = of_property_read_u32(node, "dma-channels", &value); + if (ret < 0) + return ret; + pdata->n_channel = value; + + ret = of_property_read_u32(node, "ti,edma-regions", &value); + if (ret < 0) + return ret; + pdata->n_region = value; + + ret = of_property_read_u32(node, "ti,edma-slots", &value); + if (ret < 0) + return ret; + pdata->n_slot = value; + + pdata->n_cc = 1; + + rsv_info = devm_kzalloc(dev, sizeof(struct edma_rsv_info), GFP_KERNEL); + if (!rsv_info) + return -ENOMEM; + pdata->rsv = rsv_info; + + queue_tc_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL); + if (!queue_tc_map) + return -ENOMEM; + + for (i = 0; i < 3; i++) { + queue_tc_map[i][0] = i; + queue_tc_map[i][1] = i; + } + queue_tc_map[i][0] = -1; + queue_tc_map[i][1] = -1; + + pdata->queue_tc_mapping = queue_tc_map; + + queue_priority_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL); + if (!queue_priority_map) + return -ENOMEM; + + for (i = 0; i < 3; i++) { + queue_priority_map[i][0] = i; + queue_priority_map[i][1] = i; + } + queue_priority_map[i][0] = -1; + queue_priority_map[i][1] = -1; + + pdata->queue_priority_mapping = queue_priority_map; + + pdata->default_queue = 0; -static int __init edma_probe(struct platform_device *pdev) + return ret; +} + +static struct of_dma_filter_info edma_filter_info = { + .filter_fn = edma_filter_fn, +}; + +static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev, + struct device_node *node) +{ + struct edma_soc_info *info; + int ret; + + info = devm_kzalloc(dev, sizeof(struct edma_soc_info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + ret = edma_of_parse_dt(dev, node, info); + if (ret) + return ERR_PTR(ret); + + dma_cap_set(DMA_SLAVE, edma_filter_info.dma_cap); + of_dma_controller_register(dev->of_node, of_dma_simple_xlate, + &edma_filter_info); + + return info; +} +#else +static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev, + struct device_node *node) +{ + return ERR_PTR(-ENOSYS); +} +#endif + +static int edma_probe(struct platform_device *pdev) { struct edma_soc_info **info = pdev->dev.platform_data; - const s8 (*queue_priority_mapping)[2]; - const s8 (*queue_tc_mapping)[2]; + struct edma_soc_info *ninfo[EDMA_MAX_CC] = {NULL}; + s8 (*queue_priority_mapping)[2]; + s8 (*queue_tc_mapping)[2]; int i, j, off, ln, found = 0; int status = -1; const s16 (*rsv_chans)[2]; @@ -1383,17 +1487,56 @@ static int __init edma_probe(struct platform_device *pdev) int irq[EDMA_MAX_CC] = {0, 0}; int err_irq[EDMA_MAX_CC] = {0, 0}; struct resource *r[EDMA_MAX_CC] = {NULL}; + struct resource res[EDMA_MAX_CC]; char res_name[10]; char irq_name[10]; + struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + int ret; + + if (node) { + /* Check if this is a second instance registered */ + if (arch_num_cc) { + dev_err(dev, "only one EDMA instance is supported via DT\n"); + return -ENODEV; + } + + ninfo[0] = edma_setup_info_from_dt(dev, node); + if (IS_ERR(ninfo[0])) { + dev_err(dev, "failed to get DT data\n"); + return PTR_ERR(ninfo[0]); + } + + info = ninfo; + } if (!info) return -ENODEV; + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync() failed\n"); + return ret; + } + for (j = 0; j < EDMA_MAX_CC; j++) { - sprintf(res_name, "edma_cc%d", j); - r[j] = platform_get_resource_byname(pdev, IORESOURCE_MEM, + if (!info[j]) { + if (!found) + return -ENODEV; + break; + } + if (node) { + ret = of_address_to_resource(node, j, &res[j]); + if (!ret) + r[j] = &res[j]; + } else { + sprintf(res_name, "edma_cc%d", j); + r[j] = platform_get_resource_byname(pdev, + IORESOURCE_MEM, res_name); - if (!r[j] || !info[j]) { + } + if (!r[j]) { if (found) break; else @@ -1440,7 +1583,7 @@ static int __init edma_probe(struct platform_device *pdev) off = rsv_chans[i][0]; ln = rsv_chans[i][1]; clear_bits(off, ln, - edma_cc[j]->edma_unused); + edma_cc[j]->edma_unused); } } @@ -1456,8 +1599,13 @@ static int __init edma_probe(struct platform_device *pdev) } } - sprintf(irq_name, "edma%d", j); - irq[j] = platform_get_irq_byname(pdev, irq_name); + + if (node) { + irq[j] = irq_of_parse_and_map(node, 0); + } else { + sprintf(irq_name, "edma%d", j); + irq[j] = platform_get_irq_byname(pdev, irq_name); + } edma_cc[j]->irq_res_start = irq[j]; status = devm_request_irq(&pdev->dev, irq[j], dma_irq_handler, 0, "edma", @@ -1469,8 +1617,12 @@ static int __init edma_probe(struct platform_device *pdev) return status; } - sprintf(irq_name, "edma%d_err", j); - err_irq[j] = platform_get_irq_byname(pdev, irq_name); + if (node) { + err_irq[j] = irq_of_parse_and_map(node, 2); + } else { + sprintf(irq_name, "edma%d_err", j); + err_irq[j] = platform_get_irq_byname(pdev, irq_name); + } edma_cc[j]->irq_res_end = err_irq[j]; status = devm_request_irq(&pdev->dev, err_irq[j], dma_ccerr_handler, 0, @@ -1516,9 +1668,17 @@ static int __init edma_probe(struct platform_device *pdev) return 0; } +static const struct of_device_id edma_of_ids[] = { + { .compatible = "ti,edma3", }, + {} +}; static struct platform_driver edma_driver = { - .driver.name = "edma", + .driver = { + .name = "edma", + .of_match_table = edma_of_ids, + }, + .probe = edma_probe, }; static int __init edma_init(void) diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index bf572525175d..eb254fe861ac 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -105,27 +105,27 @@ struct platform_device da8xx_serial_device = { }, }; -static const s8 da8xx_queue_tc_mapping[][2] = { +static s8 da8xx_queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, {1, 1}, {-1, -1} }; -static const s8 da8xx_queue_priority_mapping[][2] = { +static s8 da8xx_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, {1, 7}, {-1, -1} }; -static const s8 da850_queue_tc_mapping[][2] = { +static s8 da850_queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, {-1, -1} }; -static const s8 da850_queue_priority_mapping[][2] = { +static s8 da850_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, {-1, -1} diff --git a/arch/arm/mach-davinci/devices-tnetv107x.c b/arch/arm/mach-davinci/devices-tnetv107x.c index 612a0856e9c5..128cb9ae80f4 100644 --- a/arch/arm/mach-davinci/devices-tnetv107x.c +++ b/arch/arm/mach-davinci/devices-tnetv107x.c @@ -58,14 +58,14 @@ #define TNETV107X_DMACH_SDIO1_RX 28 #define TNETV107X_DMACH_SDIO1_TX 29 -static const s8 edma_tc_mapping[][2] = { +static s8 edma_tc_mapping[][2] = { /* event queue no TC no */ { 0, 0 }, { 1, 1 }, { -1, -1 } }; -static const s8 edma_priority_mapping[][2] = { +static s8 edma_priority_mapping[][2] = { /* event queue no Prio */ { 0, 3 }, { 1, 7 }, diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 526cf7d06d0e..42ef53f62c6c 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -569,7 +569,7 @@ static u8 dm355_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ -static const s8 +static s8 queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, @@ -577,7 +577,7 @@ queue_tc_mapping[][2] = { {-1, -1}, }; -static const s8 +static s8 queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index c4b741173c06..fa7af5eda52d 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -826,7 +826,7 @@ static u8 dm365_default_priorities[DAVINCI_N_AINTC_IRQ] = { }; /* Four Transfer Controllers on DM365 */ -static const s8 +static s8 dm365_queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, @@ -836,7 +836,7 @@ dm365_queue_tc_mapping[][2] = { {-1, -1}, }; -static const s8 +static s8 dm365_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 7}, diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index dd156d58fe64..a49d18246fe9 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -497,7 +497,7 @@ static u8 dm644x_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ -static const s8 +static s8 queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, @@ -505,7 +505,7 @@ queue_tc_mapping[][2] = { {-1, -1}, }; -static const s8 +static s8 queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 6d52a321a8cf..d1259e80141b 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -531,7 +531,7 @@ static u8 dm646x_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ /* Four Transfer Controllers on DM646x */ -static const s8 +static s8 dm646x_queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, @@ -541,7 +541,7 @@ dm646x_queue_tc_mapping[][2] = { {-1, -1}, }; -static const s8 +static s8 dm646x_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 4}, diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index 2344ea2675ad..317f2beeaece 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -175,8 +175,8 @@ struct edma_soc_info { /* Resource reservation for other cores */ struct edma_rsv_info *rsv; - const s8 (*queue_tc_mapping)[2]; - const s8 (*queue_priority_mapping)[2]; + s8 (*queue_tc_mapping)[2]; + s8 (*queue_priority_mapping)[2]; }; #endif -- cgit v1.2.3 From 2646a0e52b65a5ea3d108794611f95df1a6cb409 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 20 Jun 2013 16:06:39 -0500 Subject: ARM: edma: Add EDMA crossbar event mux support EDMA supports a cross bar which provides ability to mux additional events into physical channels present in the channel controller. This is required when the number of events present in the system are more than number of available physical channels. Changes by Joel: * Split EDMA xbar support out of original EDMA DT parsing patch to keep it easier for review. * Rewrite shift and offset calculation. Suggested-by: Sekhar Nori Suggested by: Andy Shevchenko Signed-off-by: Joel A Fernandes Acked-by: Arnd Bergmann [nsekhar@ti.com: fix checkpatch errors and a minor coding improvement] Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 78 ++++++++++++++++++++++++++++++++++++++ include/linux/platform_data/edma.h | 1 + 2 files changed, 79 insertions(+) (limited to 'include') diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 5183a310657c..a432e6c1dac1 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1378,12 +1378,76 @@ EXPORT_SYMBOL(edma_clear_event); #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DMADEVICES) +static int edma_of_read_u32_to_s16_array(const struct device_node *np, + const char *propname, s16 *out_values, + size_t sz) +{ + int ret; + + ret = of_property_read_u16_array(np, propname, out_values, sz); + if (ret) + return ret; + + /* Terminate it */ + *out_values++ = -1; + *out_values++ = -1; + + return 0; +} + +static int edma_xbar_event_map(struct device *dev, + struct device_node *node, + struct edma_soc_info *pdata, int len) +{ + int ret, i; + struct resource res; + void __iomem *xbar; + const s16 (*xbar_chans)[2]; + u32 shift, offset, mux; + + xbar_chans = devm_kzalloc(dev, + len/sizeof(s16) + 2*sizeof(s16), + GFP_KERNEL); + if (!xbar_chans) + return -ENOMEM; + + ret = of_address_to_resource(node, 1, &res); + if (ret) + return -EIO; + + xbar = devm_ioremap(dev, res.start, resource_size(&res)); + if (!xbar) + return -ENOMEM; + + ret = edma_of_read_u32_to_s16_array(node, + "ti,edma-xbar-event-map", + (s16 *)xbar_chans, + len/sizeof(u32)); + if (ret) + return -EIO; + + for (i = 0; xbar_chans[i][0] != -1; i++) { + shift = (xbar_chans[i][1] & 0x03) << 3; + offset = xbar_chans[i][1] & 0xfffffffc; + mux = readl(xbar + offset); + mux &= ~(0xff << shift); + mux |= xbar_chans[i][0] << shift; + writel(mux, (xbar + offset)); + } + + pdata->xbar_chans = xbar_chans; + + return 0; +} + static int edma_of_parse_dt(struct device *dev, struct device_node *node, struct edma_soc_info *pdata) { int ret = 0, i; u32 value; + struct property *prop; + size_t sz; struct edma_rsv_info *rsv_info; s8 (*queue_tc_map)[2], (*queue_priority_map)[2]; @@ -1439,6 +1503,10 @@ static int edma_of_parse_dt(struct device *dev, pdata->default_queue = 0; + prop = of_find_property(node, "ti,edma-xbar-event-map", &sz); + if (prop) + ret = edma_xbar_event_map(dev, node, pdata, sz); + return ret; } @@ -1484,6 +1552,7 @@ static int edma_probe(struct platform_device *pdev) int status = -1; const s16 (*rsv_chans)[2]; const s16 (*rsv_slots)[2]; + const s16 (*xbar_chans)[2]; int irq[EDMA_MAX_CC] = {0, 0}; int err_irq[EDMA_MAX_CC] = {0, 0}; struct resource *r[EDMA_MAX_CC] = {NULL}; @@ -1599,6 +1668,15 @@ static int edma_probe(struct platform_device *pdev) } } + /* Clear the xbar mapped channels in unused list */ + xbar_chans = info[j]->xbar_chans; + if (xbar_chans) { + for (i = 0; xbar_chans[i][1] != -1; i++) { + off = xbar_chans[i][1]; + clear_bits(off, 1, + edma_cc[j]->edma_unused); + } + } if (node) { irq[j] = irq_of_parse_and_map(node, 0); diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index 317f2beeaece..57300fd7cc03 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -177,6 +177,7 @@ struct edma_soc_info { s8 (*queue_tc_mapping)[2]; s8 (*queue_priority_mapping)[2]; + const s16 (*xbar_chans)[2]; }; #endif -- cgit v1.2.3