aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-vexpress
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-07-02 16:43:38 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-07-02 16:43:38 -0400
commit3883cbb6c1bda013a3ce2dbdab7dc97c52e4a232 (patch)
tree5b69f83b049d24ac81123ac954ca8c9128e48443 /arch/arm/mach-vexpress
parentd2033f2c1d1de2239ded15e478ddb4028f192a15 (diff)
parent1eb92b24e243085d242cf5ffd64829bba70972e1 (diff)
Merge tag 'soc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC specific changes from Arnd Bergmann: "These changes are all to SoC-specific code, a total of 33 branches on 17 platforms were pulled into this. Like last time, Renesas sh-mobile is now the platform with the most changes, followed by OMAP and EXYNOS. Two new platforms, TI Keystone and Rockchips RK3xxx are added in this branch, both containing almost no platform specific code at all, since they are using generic subsystem interfaces for clocks, pinctrl, interrupts etc. The device drivers are getting merged through the respective subsystem maintainer trees. One more SoC (u300) is now multiplatform capable and several others (shmobile, exynos, msm, integrator, kirkwood, clps711x) are moving towards that goal with this series but need more work. Also noteworthy is the work on PCI here, which is traditionally part of the SoC specific code. With the changes done by Thomas Petazzoni, we can now more easily have PCI host controller drivers as loadable modules and keep them separate from the platform code in drivers/pci/host. This has already led to the discovery that three platforms (exynos, spear and imx) are actually using an identical PCIe host controller and will be able to share a driver once support for spear and imx is added." * tag 'soc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (480 commits) ARM: integrator: let pciv3 use mem/premem from device tree ARM: integrator: set local side PCI addresses right ARM: dts: Add pcie controller node for exynos5440-ssdk5440 ARM: dts: Add pcie controller node for Samsung EXYNOS5440 SoC ARM: EXYNOS: Enable PCIe support for Exynos5440 pci: Add PCIe driver for Samsung Exynos ARM: OMAP5: voltagedomain data: remove temporary OMAP4 voltage data ARM: keystone: Move CPU bringup code to dedicated asm file ARM: multiplatform: always pick one CPU type ARM: imx: select syscon for IMX6SL ARM: keystone: select ARM_ERRATA_798181 only for SMP ARM: imx: Synertronixx scb9328 needs to select SOC_IMX1 ARM: OMAP2+: AM43x: resolve SMP related build error dmaengine: edma: enable build for AM33XX ARM: edma: Add EDMA crossbar event mux support ARM: edma: Add DT and runtime PM support to the private EDMA API dmaengine: edma: Add TI EDMA device tree binding arm: add basic support for Rockchip RK3066a boards arm: add debug uarts for rockchip rk29xx and rk3xxx series arm: Add basic clocks for Rockchip rk3066a SoCs ...
Diffstat (limited to 'arch/arm/mach-vexpress')
-rw-r--r--arch/arm/mach-vexpress/Kconfig9
-rw-r--r--arch/arm/mach-vexpress/Makefile1
-rw-r--r--arch/arm/mach-vexpress/core.h2
-rw-r--r--arch/arm/mach-vexpress/dcscb.c253
-rw-r--r--arch/arm/mach-vexpress/dcscb_setup.S38
-rw-r--r--arch/arm/mach-vexpress/platsmp.c20
-rw-r--r--arch/arm/mach-vexpress/v2m.c1
7 files changed, 324 insertions, 0 deletions
diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig
index 5907e10c37fd..b8bbabec6310 100644
--- a/arch/arm/mach-vexpress/Kconfig
+++ b/arch/arm/mach-vexpress/Kconfig
@@ -57,4 +57,13 @@ config ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA
57config ARCH_VEXPRESS_CA9X4 57config ARCH_VEXPRESS_CA9X4
58 bool "Versatile Express Cortex-A9x4 tile" 58 bool "Versatile Express Cortex-A9x4 tile"
59 59
60config ARCH_VEXPRESS_DCSCB
61 bool "Dual Cluster System Control Block (DCSCB) support"
62 depends on MCPM
63 select ARM_CCI
64 help
65 Support for the Dual Cluster System Configuration Block (DCSCB).
66 This is needed to provide CPU and cluster power management
67 on RTSM implementing big.LITTLE.
68
60endmenu 69endmenu
diff --git a/arch/arm/mach-vexpress/Makefile b/arch/arm/mach-vexpress/Makefile
index 42703e8b4d3b..48ba89a8149f 100644
--- a/arch/arm/mach-vexpress/Makefile
+++ b/arch/arm/mach-vexpress/Makefile
@@ -6,5 +6,6 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \
6 6
7obj-y := v2m.o 7obj-y := v2m.o
8obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o 8obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o
9obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.o
9obj-$(CONFIG_SMP) += platsmp.o 10obj-$(CONFIG_SMP) += platsmp.o
10obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o 11obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-vexpress/core.h b/arch/arm/mach-vexpress/core.h
index f134cd4a85f1..bde4374ab6d5 100644
--- a/arch/arm/mach-vexpress/core.h
+++ b/arch/arm/mach-vexpress/core.h
@@ -6,6 +6,8 @@
6 6
7void vexpress_dt_smp_map_io(void); 7void vexpress_dt_smp_map_io(void);
8 8
9bool vexpress_smp_init_ops(void);
10
9extern struct smp_operations vexpress_smp_ops; 11extern struct smp_operations vexpress_smp_ops;
10 12
11extern void vexpress_cpu_die(unsigned int cpu); 13extern void vexpress_cpu_die(unsigned int cpu);
diff --git a/arch/arm/mach-vexpress/dcscb.c b/arch/arm/mach-vexpress/dcscb.c
new file mode 100644
index 000000000000..16d57a8a9d5a
--- /dev/null
+++ b/arch/arm/mach-vexpress/dcscb.c
@@ -0,0 +1,253 @@
1/*
2 * arch/arm/mach-vexpress/dcscb.c - Dual Cluster System Configuration Block
3 *
4 * Created by: Nicolas Pitre, May 2012
5 * Copyright: (C) 2012-2013 Linaro Limited
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/init.h>
13#include <linux/kernel.h>
14#include <linux/io.h>
15#include <linux/spinlock.h>
16#include <linux/errno.h>
17#include <linux/of_address.h>
18#include <linux/vexpress.h>
19#include <linux/arm-cci.h>
20
21#include <asm/mcpm.h>
22#include <asm/proc-fns.h>
23#include <asm/cacheflush.h>
24#include <asm/cputype.h>
25#include <asm/cp15.h>
26
27
28#define RST_HOLD0 0x0
29#define RST_HOLD1 0x4
30#define SYS_SWRESET 0x8
31#define RST_STAT0 0xc
32#define RST_STAT1 0x10
33#define EAG_CFG_R 0x20
34#define EAG_CFG_W 0x24
35#define KFC_CFG_R 0x28
36#define KFC_CFG_W 0x2c
37#define DCS_CFG_R 0x30
38
39/*
40 * We can't use regular spinlocks. In the switcher case, it is possible
41 * for an outbound CPU to call power_down() while its inbound counterpart
42 * is already live using the same logical CPU number which trips lockdep
43 * debugging.
44 */
45static arch_spinlock_t dcscb_lock = __ARCH_SPIN_LOCK_UNLOCKED;
46
47static void __iomem *dcscb_base;
48static int dcscb_use_count[4][2];
49static int dcscb_allcpus_mask[2];
50
51static int dcscb_power_up(unsigned int cpu, unsigned int cluster)
52{
53 unsigned int rst_hold, cpumask = (1 << cpu);
54 unsigned int all_mask = dcscb_allcpus_mask[cluster];
55
56 pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
57 if (cpu >= 4 || cluster >= 2)
58 return -EINVAL;
59
60 /*
61 * Since this is called with IRQs enabled, and no arch_spin_lock_irq
62 * variant exists, we need to disable IRQs manually here.
63 */
64 local_irq_disable();
65 arch_spin_lock(&dcscb_lock);
66
67 dcscb_use_count[cpu][cluster]++;
68 if (dcscb_use_count[cpu][cluster] == 1) {
69 rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4);
70 if (rst_hold & (1 << 8)) {
71 /* remove cluster reset and add individual CPU's reset */
72 rst_hold &= ~(1 << 8);
73 rst_hold |= all_mask;
74 }
75 rst_hold &= ~(cpumask | (cpumask << 4));
76 writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4);
77 } else if (dcscb_use_count[cpu][cluster] != 2) {
78 /*
79 * The only possible values are:
80 * 0 = CPU down
81 * 1 = CPU (still) up
82 * 2 = CPU requested to be up before it had a chance
83 * to actually make itself down.
84 * Any other value is a bug.
85 */
86 BUG();
87 }
88
89 arch_spin_unlock(&dcscb_lock);
90 local_irq_enable();
91
92 return 0;
93}
94
95static void dcscb_power_down(void)
96{
97 unsigned int mpidr, cpu, cluster, rst_hold, cpumask, all_mask;
98 bool last_man = false, skip_wfi = false;
99
100 mpidr = read_cpuid_mpidr();
101 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
102 cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
103 cpumask = (1 << cpu);
104 all_mask = dcscb_allcpus_mask[cluster];
105
106 pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
107 BUG_ON(cpu >= 4 || cluster >= 2);
108
109 __mcpm_cpu_going_down(cpu, cluster);
110
111 arch_spin_lock(&dcscb_lock);
112 BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
113 dcscb_use_count[cpu][cluster]--;
114 if (dcscb_use_count[cpu][cluster] == 0) {
115 rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4);
116 rst_hold |= cpumask;
117 if (((rst_hold | (rst_hold >> 4)) & all_mask) == all_mask) {
118 rst_hold |= (1 << 8);
119 last_man = true;
120 }
121 writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4);
122 } else if (dcscb_use_count[cpu][cluster] == 1) {
123 /*
124 * A power_up request went ahead of us.
125 * Even if we do not want to shut this CPU down,
126 * the caller expects a certain state as if the WFI
127 * was aborted. So let's continue with cache cleaning.
128 */
129 skip_wfi = true;
130 } else
131 BUG();
132
133 if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
134 arch_spin_unlock(&dcscb_lock);
135
136 /*
137 * Flush all cache levels for this cluster.
138 *
139 * A15/A7 can hit in the cache with SCTLR.C=0, so we don't need
140 * a preliminary flush here for those CPUs. At least, that's
141 * the theory -- without the extra flush, Linux explodes on
142 * RTSM (to be investigated).
143 */
144 flush_cache_all();
145 set_cr(get_cr() & ~CR_C);
146 flush_cache_all();
147
148 /*
149 * This is a harmless no-op. On platforms with a real
150 * outer cache this might either be needed or not,
151 * depending on where the outer cache sits.
152 */
153 outer_flush_all();
154
155 /* Disable local coherency by clearing the ACTLR "SMP" bit: */
156 set_auxcr(get_auxcr() & ~(1 << 6));
157
158 /*
159 * Disable cluster-level coherency by masking
160 * incoming snoops and DVM messages:
161 */
162 cci_disable_port_by_cpu(mpidr);
163
164 __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
165 } else {
166 arch_spin_unlock(&dcscb_lock);
167
168 /*
169 * Flush the local CPU cache.
170 *
171 * A15/A7 can hit in the cache with SCTLR.C=0, so we don't need
172 * a preliminary flush here for those CPUs. At least, that's
173 * the theory -- without the extra flush, Linux explodes on
174 * RTSM (to be investigated).
175 */
176 flush_cache_louis();
177 set_cr(get_cr() & ~CR_C);
178 flush_cache_louis();
179
180 /* Disable local coherency by clearing the ACTLR "SMP" bit: */
181 set_auxcr(get_auxcr() & ~(1 << 6));
182 }
183
184 __mcpm_cpu_down(cpu, cluster);
185
186 /* Now we are prepared for power-down, do it: */
187 dsb();
188 if (!skip_wfi)
189 wfi();
190
191 /* Not dead at this point? Let our caller cope. */
192}
193
194static const struct mcpm_platform_ops dcscb_power_ops = {
195 .power_up = dcscb_power_up,
196 .power_down = dcscb_power_down,
197};
198
199static void __init dcscb_usage_count_init(void)
200{
201 unsigned int mpidr, cpu, cluster;
202
203 mpidr = read_cpuid_mpidr();
204 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
205 cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
206
207 pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
208 BUG_ON(cpu >= 4 || cluster >= 2);
209 dcscb_use_count[cpu][cluster] = 1;
210}
211
212extern void dcscb_power_up_setup(unsigned int affinity_level);
213
214static int __init dcscb_init(void)
215{
216 struct device_node *node;
217 unsigned int cfg;
218 int ret;
219
220 if (!cci_probed())
221 return -ENODEV;
222
223 node = of_find_compatible_node(NULL, NULL, "arm,rtsm,dcscb");
224 if (!node)
225 return -ENODEV;
226 dcscb_base = of_iomap(node, 0);
227 if (!dcscb_base)
228 return -EADDRNOTAVAIL;
229 cfg = readl_relaxed(dcscb_base + DCS_CFG_R);
230 dcscb_allcpus_mask[0] = (1 << (((cfg >> 16) >> (0 << 2)) & 0xf)) - 1;
231 dcscb_allcpus_mask[1] = (1 << (((cfg >> 16) >> (1 << 2)) & 0xf)) - 1;
232 dcscb_usage_count_init();
233
234 ret = mcpm_platform_register(&dcscb_power_ops);
235 if (!ret)
236 ret = mcpm_sync_init(dcscb_power_up_setup);
237 if (ret) {
238 iounmap(dcscb_base);
239 return ret;
240 }
241
242 pr_info("VExpress DCSCB support installed\n");
243
244 /*
245 * Future entries into the kernel can now go
246 * through the cluster entry vectors.
247 */
248 vexpress_flags_set(virt_to_phys(mcpm_entry_point));
249
250 return 0;
251}
252
253early_initcall(dcscb_init);
diff --git a/arch/arm/mach-vexpress/dcscb_setup.S b/arch/arm/mach-vexpress/dcscb_setup.S
new file mode 100644
index 000000000000..4bb7fbe0f621
--- /dev/null
+++ b/arch/arm/mach-vexpress/dcscb_setup.S
@@ -0,0 +1,38 @@
1/*
2 * arch/arm/include/asm/dcscb_setup.S
3 *
4 * Created by: Dave Martin, 2012-06-22
5 * Copyright: (C) 2012-2013 Linaro Limited
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/linkage.h>
13
14
15ENTRY(dcscb_power_up_setup)
16
17 cmp r0, #0 @ check affinity level
18 beq 2f
19
20/*
21 * Enable cluster-level coherency, in preparation for turning on the MMU.
22 * The ACTLR SMP bit does not need to be set here, because cpu_resume()
23 * already restores that.
24 *
25 * A15/A7 may not require explicit L2 invalidation on reset, dependent
26 * on hardware integration decisions.
27 * For now, this code assumes that L2 is either already invalidated,
28 * or invalidation is not required.
29 */
30
31 b cci_enable_port_for_self
32
332: @ Implementation-specific local CPU setup operations should go here,
34 @ if any. In this case, there is nothing to do.
35
36 bx lr
37
38ENDPROC(dcscb_power_up_setup)
diff --git a/arch/arm/mach-vexpress/platsmp.c b/arch/arm/mach-vexpress/platsmp.c
index dc1ace55d557..993c9ae5dc5e 100644
--- a/arch/arm/mach-vexpress/platsmp.c
+++ b/arch/arm/mach-vexpress/platsmp.c
@@ -12,9 +12,11 @@
12#include <linux/errno.h> 12#include <linux/errno.h>
13#include <linux/smp.h> 13#include <linux/smp.h>
14#include <linux/io.h> 14#include <linux/io.h>
15#include <linux/of.h>
15#include <linux/of_fdt.h> 16#include <linux/of_fdt.h>
16#include <linux/vexpress.h> 17#include <linux/vexpress.h>
17 18
19#include <asm/mcpm.h>
18#include <asm/smp_scu.h> 20#include <asm/smp_scu.h>
19#include <asm/mach/map.h> 21#include <asm/mach/map.h>
20 22
@@ -203,3 +205,21 @@ struct smp_operations __initdata vexpress_smp_ops = {
203 .cpu_die = vexpress_cpu_die, 205 .cpu_die = vexpress_cpu_die,
204#endif 206#endif
205}; 207};
208
209bool __init vexpress_smp_init_ops(void)
210{
211#ifdef CONFIG_MCPM
212 /*
213 * The best way to detect a multi-cluster configuration at the moment
214 * is to look for the presence of a CCI in the system.
215 * Override the default vexpress_smp_ops if so.
216 */
217 struct device_node *node;
218 node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
219 if (node && of_device_is_available(node)) {
220 mcpm_smp_set_ops();
221 return true;
222 }
223#endif
224 return false;
225}
diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c
index d6016970e2fe..95a469e23e37 100644
--- a/arch/arm/mach-vexpress/v2m.c
+++ b/arch/arm/mach-vexpress/v2m.c
@@ -455,6 +455,7 @@ static const char * const v2m_dt_match[] __initconst = {
455DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express") 455DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")
456 .dt_compat = v2m_dt_match, 456 .dt_compat = v2m_dt_match,
457 .smp = smp_ops(vexpress_smp_ops), 457 .smp = smp_ops(vexpress_smp_ops),
458 .smp_init = smp_init_ops(vexpress_smp_init_ops),
458 .map_io = v2m_dt_map_io, 459 .map_io = v2m_dt_map_io,
459 .init_early = v2m_dt_init_early, 460 .init_early = v2m_dt_init_early,
460 .init_time = v2m_dt_timer_init, 461 .init_time = v2m_dt_timer_init,