aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-vexpress
diff options
context:
space:
mode:
authorNicolas Pitre <nicolas.pitre@linaro.org>2012-05-02 20:56:52 -0400
committerNicolas Pitre <nicolas.pitre@linaro.org>2013-05-29 15:50:34 -0400
commit1e904e1bf6f1285cc2dd5696c44b7cf78cda643f (patch)
tree960d3d6b28b2a6097da4806b1059f55d4fa263ad /arch/arm/mach-vexpress
parentbbc8d77db655be61a21d7623428c46c578a866d3 (diff)
ARM: vexpress: introduce DCSCB support
This adds basic CPU and cluster reset controls on RTSM for the A15x4-A7x4 model configuration using the Dual Cluster System Configuration Block (DCSCB). The cache coherency interconnect (CCI) is not handled yet. Signed-off-by: Nicolas Pitre <nico@linaro.org> Reviewed-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Acked-by: Pawel Moll <pawel.moll@arm.com>
Diffstat (limited to 'arch/arm/mach-vexpress')
-rw-r--r--arch/arm/mach-vexpress/Kconfig8
-rw-r--r--arch/arm/mach-vexpress/Makefile1
-rw-r--r--arch/arm/mach-vexpress/dcscb.c164
3 files changed, 173 insertions, 0 deletions
diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig
index 5907e10c37fd..2f46385c2819 100644
--- a/arch/arm/mach-vexpress/Kconfig
+++ b/arch/arm/mach-vexpress/Kconfig
@@ -57,4 +57,12 @@ 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 help
64 Support for the Dual Cluster System Configuration Block (DCSCB).
65 This is needed to provide CPU and cluster power management
66 on RTSM implementing big.LITTLE.
67
60endmenu 68endmenu
diff --git a/arch/arm/mach-vexpress/Makefile b/arch/arm/mach-vexpress/Makefile
index 42703e8b4d3b..518519f57a5e 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
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/dcscb.c b/arch/arm/mach-vexpress/dcscb.c
new file mode 100644
index 000000000000..2ca4bbce530c
--- /dev/null
+++ b/arch/arm/mach-vexpress/dcscb.c
@@ -0,0 +1,164 @@
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
20#include <asm/mcpm.h>
21#include <asm/proc-fns.h>
22#include <asm/cacheflush.h>
23#include <asm/cputype.h>
24#include <asm/cp15.h>
25
26
27#define RST_HOLD0 0x0
28#define RST_HOLD1 0x4
29#define SYS_SWRESET 0x8
30#define RST_STAT0 0xc
31#define RST_STAT1 0x10
32#define EAG_CFG_R 0x20
33#define EAG_CFG_W 0x24
34#define KFC_CFG_R 0x28
35#define KFC_CFG_W 0x2c
36#define DCS_CFG_R 0x30
37
38/*
39 * We can't use regular spinlocks. In the switcher case, it is possible
40 * for an outbound CPU to call power_down() while its inbound counterpart
41 * is already live using the same logical CPU number which trips lockdep
42 * debugging.
43 */
44static arch_spinlock_t dcscb_lock = __ARCH_SPIN_LOCK_UNLOCKED;
45
46static void __iomem *dcscb_base;
47
48static int dcscb_power_up(unsigned int cpu, unsigned int cluster)
49{
50 unsigned int rst_hold, cpumask = (1 << cpu);
51
52 pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
53 if (cpu >= 4 || cluster >= 2)
54 return -EINVAL;
55
56 /*
57 * Since this is called with IRQs enabled, and no arch_spin_lock_irq
58 * variant exists, we need to disable IRQs manually here.
59 */
60 local_irq_disable();
61 arch_spin_lock(&dcscb_lock);
62
63 rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4);
64 if (rst_hold & (1 << 8)) {
65 /* remove cluster reset and add individual CPU's reset */
66 rst_hold &= ~(1 << 8);
67 rst_hold |= 0xf;
68 }
69 rst_hold &= ~(cpumask | (cpumask << 4));
70 writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4);
71
72 arch_spin_unlock(&dcscb_lock);
73 local_irq_enable();
74
75 return 0;
76}
77
78static void dcscb_power_down(void)
79{
80 unsigned int mpidr, cpu, cluster, rst_hold, cpumask, last_man;
81
82 mpidr = read_cpuid_mpidr();
83 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
84 cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
85 cpumask = (1 << cpu);
86
87 pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
88 BUG_ON(cpu >= 4 || cluster >= 2);
89
90 arch_spin_lock(&dcscb_lock);
91 rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4);
92 rst_hold |= cpumask;
93 if (((rst_hold | (rst_hold >> 4)) & 0xf) == 0xf)
94 rst_hold |= (1 << 8);
95 writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4);
96 arch_spin_unlock(&dcscb_lock);
97 last_man = (rst_hold & (1 << 8));
98
99 /*
100 * Now let's clean our L1 cache and shut ourself down.
101 * If we're the last CPU in this cluster then clean L2 too.
102 */
103
104 /*
105 * A15/A7 can hit in the cache with SCTLR.C=0, so we don't need
106 * a preliminary flush here for those CPUs. At least, that's
107 * the theory -- without the extra flush, Linux explodes on
108 * RTSM (to be investigated)..
109 */
110 flush_cache_louis();
111 set_cr(get_cr() & ~CR_C);
112
113 if (!last_man) {
114 flush_cache_louis();
115 } else {
116 flush_cache_all();
117 outer_flush_all();
118 }
119
120 /* Disable local coherency by clearing the ACTLR "SMP" bit: */
121 set_auxcr(get_auxcr() & ~(1 << 6));
122
123 /* Now we are prepared for power-down, do it: */
124 dsb();
125 wfi();
126
127 /* Not dead at this point? Let our caller cope. */
128}
129
130static const struct mcpm_platform_ops dcscb_power_ops = {
131 .power_up = dcscb_power_up,
132 .power_down = dcscb_power_down,
133};
134
135static int __init dcscb_init(void)
136{
137 struct device_node *node;
138 int ret;
139
140 node = of_find_compatible_node(NULL, NULL, "arm,rtsm,dcscb");
141 if (!node)
142 return -ENODEV;
143 dcscb_base = of_iomap(node, 0);
144 if (!dcscb_base)
145 return -EADDRNOTAVAIL;
146
147 ret = mcpm_platform_register(&dcscb_power_ops);
148 if (ret) {
149 iounmap(dcscb_base);
150 return ret;
151 }
152
153 pr_info("VExpress DCSCB support installed\n");
154
155 /*
156 * Future entries into the kernel can now go
157 * through the cluster entry vectors.
158 */
159 vexpress_flags_set(virt_to_phys(mcpm_entry_point));
160
161 return 0;
162}
163
164early_initcall(dcscb_init);