aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2010-02-11 16:56:07 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2010-05-02 04:35:39 -0400
commit59ac59f6f1432aa9417d2592bdfd17c99804dd66 (patch)
treec4251d8827165d9eb926d77470a59fb196f998f8
parentfef88f10767cfd9f9b4eebb5d5490214c5e13ad5 (diff)
ARM: Add Versatile Express SMP support
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--arch/arm/Kconfig5
-rw-r--r--arch/arm/mach-vexpress/Makefile2
-rw-r--r--arch/arm/mach-vexpress/headsmp.S39
-rw-r--r--arch/arm/mach-vexpress/include/mach/smp.h21
-rw-r--r--arch/arm/mach-vexpress/localtimer.c26
-rw-r--r--arch/arm/mach-vexpress/platsmp.c190
6 files changed, 281 insertions, 2 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 58080e17c28b..4bb7730a2682 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1121,10 +1121,11 @@ source "kernel/time/Kconfig"
1121config SMP 1121config SMP
1122 bool "Symmetric Multi-Processing (EXPERIMENTAL)" 1122 bool "Symmetric Multi-Processing (EXPERIMENTAL)"
1123 depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || REALVIEW_EB_A9MP ||\ 1123 depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || REALVIEW_EB_A9MP ||\
1124 MACH_REALVIEW_PB11MP || MACH_REALVIEW_PBX || ARCH_OMAP4 || ARCH_U8500) 1124 MACH_REALVIEW_PB11MP || MACH_REALVIEW_PBX || ARCH_OMAP4 ||\
1125 ARCH_U8500 || ARCH_VEXPRESS_CA9X4)
1125 depends on GENERIC_CLOCKEVENTS 1126 depends on GENERIC_CLOCKEVENTS
1126 select USE_GENERIC_SMP_HELPERS 1127 select USE_GENERIC_SMP_HELPERS
1127 select HAVE_ARM_SCU if (ARCH_REALVIEW || ARCH_OMAP4 || ARCH_U8500) 1128 select HAVE_ARM_SCU if (ARCH_REALVIEW || ARCH_OMAP4 || ARCH_U8500 || ARCH_VEXPRESS_CA9X4)
1128 help 1129 help
1129 This enables support for systems with more than one CPU. If you have 1130 This enables support for systems with more than one CPU. If you have
1130 a system with only one CPU, like most personal computers, say N. If 1131 a system with only one CPU, like most personal computers, say N. If
diff --git a/arch/arm/mach-vexpress/Makefile b/arch/arm/mach-vexpress/Makefile
index 3c5e1609fc4f..1b71b77ade22 100644
--- a/arch/arm/mach-vexpress/Makefile
+++ b/arch/arm/mach-vexpress/Makefile
@@ -4,3 +4,5 @@
4 4
5obj-y := v2m.o 5obj-y := v2m.o
6obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o 6obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o
7obj-$(CONFIG_SMP) += platsmp.o headsmp.o
8obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
diff --git a/arch/arm/mach-vexpress/headsmp.S b/arch/arm/mach-vexpress/headsmp.S
new file mode 100644
index 000000000000..8a78ff68e1ee
--- /dev/null
+++ b/arch/arm/mach-vexpress/headsmp.S
@@ -0,0 +1,39 @@
1/*
2 * linux/arch/arm/mach-vexpress/headsmp.S
3 *
4 * Copyright (c) 2003 ARM Limited
5 * All Rights Reserved
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#include <linux/linkage.h>
12#include <linux/init.h>
13
14 __INIT
15
16/*
17 * Versatile Express specific entry point for secondary CPUs. This
18 * provides a "holding pen" into which all secondary cores are held
19 * until we're ready for them to initialise.
20 */
21ENTRY(vexpress_secondary_startup)
22 mrc p15, 0, r0, c0, c0, 5
23 and r0, r0, #15
24 adr r4, 1f
25 ldmia r4, {r5, r6}
26 sub r4, r4, r5
27 add r6, r6, r4
28pen: ldr r7, [r6]
29 cmp r7, r0
30 bne pen
31
32 /*
33 * we've been released from the holding pen: secondary_stack
34 * should now contain the SVC stack for this core
35 */
36 b secondary_startup
37
381: .long .
39 .long pen_release
diff --git a/arch/arm/mach-vexpress/include/mach/smp.h b/arch/arm/mach-vexpress/include/mach/smp.h
new file mode 100644
index 000000000000..72a9621ed087
--- /dev/null
+++ b/arch/arm/mach-vexpress/include/mach/smp.h
@@ -0,0 +1,21 @@
1#ifndef __MACH_SMP_H
2#define __MACH_SMP_H
3
4#include <asm/hardware/gic.h>
5
6#define hard_smp_processor_id() \
7 ({ \
8 unsigned int cpunum; \
9 __asm__("mrc p15, 0, %0, c0, c0, 5" \
10 : "=r" (cpunum)); \
11 cpunum &= 0x0F; \
12 })
13
14/*
15 * We use IRQ1 as the IPI
16 */
17static inline void smp_cross_call(const struct cpumask *mask)
18{
19 gic_raise_softirq(mask, 1);
20}
21#endif
diff --git a/arch/arm/mach-vexpress/localtimer.c b/arch/arm/mach-vexpress/localtimer.c
new file mode 100644
index 000000000000..c0e3a59a0bfc
--- /dev/null
+++ b/arch/arm/mach-vexpress/localtimer.c
@@ -0,0 +1,26 @@
1/*
2 * linux/arch/arm/mach-vexpress/localtimer.c
3 *
4 * Copyright (C) 2002 ARM Ltd.
5 * All Rights Reserved
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#include <linux/init.h>
12#include <linux/smp.h>
13#include <linux/clockchips.h>
14
15#include <asm/smp_twd.h>
16#include <asm/localtimer.h>
17#include <mach/irqs.h>
18
19/*
20 * Setup the local clock events for a CPU.
21 */
22void __cpuinit local_timer_setup(struct clock_event_device *evt)
23{
24 evt->irq = IRQ_LOCALTIMER;
25 twd_timer_setup(evt);
26}
diff --git a/arch/arm/mach-vexpress/platsmp.c b/arch/arm/mach-vexpress/platsmp.c
new file mode 100644
index 000000000000..670970699ba9
--- /dev/null
+++ b/arch/arm/mach-vexpress/platsmp.c
@@ -0,0 +1,190 @@
1/*
2 * linux/arch/arm/mach-vexpress/platsmp.c
3 *
4 * Copyright (C) 2002 ARM Ltd.
5 * All Rights Reserved
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#include <linux/init.h>
12#include <linux/errno.h>
13#include <linux/delay.h>
14#include <linux/device.h>
15#include <linux/jiffies.h>
16#include <linux/smp.h>
17#include <linux/io.h>
18
19#include <asm/cacheflush.h>
20#include <asm/localtimer.h>
21#include <asm/smp_scu.h>
22#include <asm/unified.h>
23
24#include <mach/ct-ca9x4.h>
25#include <mach/motherboard.h>
26#define V2M_PA_CS7 0x10000000
27
28#include "core.h"
29
30extern void vexpress_secondary_startup(void);
31
32/*
33 * control for which core is the next to come out of the secondary
34 * boot "holding pen"
35 */
36volatile int __cpuinitdata pen_release = -1;
37
38static void __iomem *scu_base_addr(void)
39{
40 return MMIO_P2V(A9_MPCORE_SCU);
41}
42
43static DEFINE_SPINLOCK(boot_lock);
44
45void __cpuinit platform_secondary_init(unsigned int cpu)
46{
47 trace_hardirqs_off();
48
49 /*
50 * if any interrupts are already enabled for the primary
51 * core (e.g. timer irq), then they will not have been enabled
52 * for us: do so
53 */
54 gic_cpu_init(0, gic_cpu_base_addr);
55
56 /*
57 * let the primary processor know we're out of the
58 * pen, then head off into the C entry point
59 */
60 pen_release = -1;
61 smp_wmb();
62
63 /*
64 * Synchronise with the boot thread.
65 */
66 spin_lock(&boot_lock);
67 spin_unlock(&boot_lock);
68}
69
70int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
71{
72 unsigned long timeout;
73
74 /*
75 * Set synchronisation state between this boot processor
76 * and the secondary one
77 */
78 spin_lock(&boot_lock);
79
80 /*
81 * This is really belt and braces; we hold unintended secondary
82 * CPUs in the holding pen until we're ready for them. However,
83 * since we haven't sent them a soft interrupt, they shouldn't
84 * be there.
85 */
86 pen_release = cpu;
87 __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
88 outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
89
90 /*
91 * Send the secondary CPU a soft interrupt, thereby causing
92 * the boot monitor to read the system wide flags register,
93 * and branch to the address found there.
94 */
95 smp_cross_call(cpumask_of(cpu));
96
97 timeout = jiffies + (1 * HZ);
98 while (time_before(jiffies, timeout)) {
99 smp_rmb();
100 if (pen_release == -1)
101 break;
102
103 udelay(10);
104 }
105
106 /*
107 * now the secondary core is starting up let it run its
108 * calibrations, then wait for it to finish
109 */
110 spin_unlock(&boot_lock);
111
112 return pen_release != -1 ? -ENOSYS : 0;
113}
114
115/*
116 * Initialise the CPU possible map early - this describes the CPUs
117 * which may be present or become present in the system.
118 */
119void __init smp_init_cpus(void)
120{
121 void __iomem *scu_base = scu_base_addr();
122 unsigned int i, ncores;
123
124 ncores = scu_base ? scu_get_core_count(scu_base) : 1;
125
126 /* sanity check */
127 if (ncores == 0) {
128 printk(KERN_ERR
129 "vexpress: strange CM count of 0? Default to 1\n");
130
131 ncores = 1;
132 }
133
134 if (ncores > NR_CPUS) {
135 printk(KERN_WARNING
136 "vexpress: no. of cores (%d) greater than configured "
137 "maximum of %d - clipping\n",
138 ncores, NR_CPUS);
139 ncores = NR_CPUS;
140 }
141
142 for (i = 0; i < ncores; i++)
143 set_cpu_possible(i, true);
144}
145
146void __init smp_prepare_cpus(unsigned int max_cpus)
147{
148 unsigned int ncores = num_possible_cpus();
149 unsigned int cpu = smp_processor_id();
150 int i;
151
152 smp_store_cpu_info(cpu);
153
154 /*
155 * are we trying to boot more cores than exist?
156 */
157 if (max_cpus > ncores)
158 max_cpus = ncores;
159
160 /*
161 * Initialise the present map, which describes the set of CPUs
162 * actually populated at the present time.
163 */
164 for (i = 0; i < max_cpus; i++)
165 set_cpu_present(i, true);
166
167 /*
168 * Initialise the SCU if there are more than one CPU and let
169 * them know where to start.
170 */
171 if (max_cpus > 1) {
172 /*
173 * Enable the local timer or broadcast device for the
174 * boot CPU, but only if we have more than one CPU.
175 */
176 percpu_timer_setup();
177
178 scu_enable(scu_base_addr());
179
180 /*
181 * Write the address of secondary startup into the
182 * system-wide flags register. The boot monitor waits
183 * until it receives a soft interrupt, and then the
184 * secondary CPU branches to this address.
185 */
186 writel(~0, MMIO_P2V(V2M_SYS_FLAGSCLR));
187 writel(BSYM(virt_to_phys(vexpress_secondary_startup)),
188 MMIO_P2V(V2M_SYS_FLAGSSET));
189 }
190}