aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-vexpress/dcscb.c
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/dcscb.c
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/dcscb.c')
-rw-r--r--arch/arm/mach-vexpress/dcscb.c164
1 files changed, 164 insertions, 0 deletions
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);