diff options
Diffstat (limited to 'arch/arm64')
-rw-r--r-- | arch/arm64/include/asm/smp.h | 10 | ||||
-rw-r--r-- | arch/arm64/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/arm64/kernel/smp.c | 68 | ||||
-rw-r--r-- | arch/arm64/kernel/smp_spin_table.c | 66 |
4 files changed, 123 insertions, 23 deletions
diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h index 7e34295f78e3..b1f68c19d170 100644 --- a/arch/arm64/include/asm/smp.h +++ b/arch/arm64/include/asm/smp.h | |||
@@ -66,4 +66,14 @@ extern volatile unsigned long secondary_holding_pen_release; | |||
66 | extern void arch_send_call_function_single_ipi(int cpu); | 66 | extern void arch_send_call_function_single_ipi(int cpu); |
67 | extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); | 67 | extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); |
68 | 68 | ||
69 | struct device_node; | ||
70 | |||
71 | struct smp_enable_ops { | ||
72 | const char *name; | ||
73 | int (*init_cpu)(struct device_node *, int); | ||
74 | int (*prepare_cpu)(int); | ||
75 | }; | ||
76 | |||
77 | extern const struct smp_enable_ops smp_spin_table_ops; | ||
78 | |||
69 | #endif /* ifndef __ASM_SMP_H */ | 79 | #endif /* ifndef __ASM_SMP_H */ |
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index a1cace45e970..ac1b6823334c 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile | |||
@@ -14,7 +14,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ | |||
14 | arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ | 14 | arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ |
15 | sys_compat.o | 15 | sys_compat.o |
16 | arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o | 16 | arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o |
17 | arm64-obj-$(CONFIG_SMP) += smp.o | 17 | arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o |
18 | arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o | 18 | arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o |
19 | arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o | 19 | arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o |
20 | arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o | 20 | arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o |
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 538300f2273d..7776922945af 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c | |||
@@ -233,7 +233,27 @@ void __init smp_prepare_boot_cpu(void) | |||
233 | } | 233 | } |
234 | 234 | ||
235 | static void (*smp_cross_call)(const struct cpumask *, unsigned int); | 235 | static void (*smp_cross_call)(const struct cpumask *, unsigned int); |
236 | static phys_addr_t cpu_release_addr[NR_CPUS]; | 236 | |
237 | static const struct smp_enable_ops *enable_ops[] __initconst = { | ||
238 | &smp_spin_table_ops, | ||
239 | NULL, | ||
240 | }; | ||
241 | |||
242 | static const struct smp_enable_ops *smp_enable_ops[NR_CPUS]; | ||
243 | |||
244 | static const struct smp_enable_ops * __init smp_get_enable_ops(const char *name) | ||
245 | { | ||
246 | const struct smp_enable_ops *ops = enable_ops[0]; | ||
247 | |||
248 | while (ops) { | ||
249 | if (!strcmp(name, ops->name)) | ||
250 | return ops; | ||
251 | |||
252 | ops++; | ||
253 | } | ||
254 | |||
255 | return NULL; | ||
256 | } | ||
237 | 257 | ||
238 | /* | 258 | /* |
239 | * Enumerate the possible CPU set from the device tree. | 259 | * Enumerate the possible CPU set from the device tree. |
@@ -252,22 +272,22 @@ void __init smp_init_cpus(void) | |||
252 | * We currently support only the "spin-table" enable-method. | 272 | * We currently support only the "spin-table" enable-method. |
253 | */ | 273 | */ |
254 | enable_method = of_get_property(dn, "enable-method", NULL); | 274 | enable_method = of_get_property(dn, "enable-method", NULL); |
255 | if (!enable_method || strcmp(enable_method, "spin-table")) { | 275 | if (!enable_method) { |
256 | pr_err("CPU %d: missing or invalid enable-method property: %s\n", | 276 | pr_err("CPU %d: missing enable-method property\n", cpu); |
257 | cpu, enable_method); | ||
258 | goto next; | 277 | goto next; |
259 | } | 278 | } |
260 | 279 | ||
261 | /* | 280 | smp_enable_ops[cpu] = smp_get_enable_ops(enable_method); |
262 | * Determine the address from which the CPU is polling. | 281 | |
263 | */ | 282 | if (!smp_enable_ops[cpu]) { |
264 | if (of_property_read_u64(dn, "cpu-release-addr", | 283 | pr_err("CPU %d: invalid enable-method property: %s\n", |
265 | &cpu_release_addr[cpu])) { | 284 | cpu, enable_method); |
266 | pr_err("CPU %d: missing or invalid cpu-release-addr property\n", | ||
267 | cpu); | ||
268 | goto next; | 285 | goto next; |
269 | } | 286 | } |
270 | 287 | ||
288 | if (smp_enable_ops[cpu]->init_cpu(dn, cpu)) | ||
289 | goto next; | ||
290 | |||
271 | set_cpu_possible(cpu, true); | 291 | set_cpu_possible(cpu, true); |
272 | next: | 292 | next: |
273 | cpu++; | 293 | cpu++; |
@@ -281,8 +301,7 @@ next: | |||
281 | 301 | ||
282 | void __init smp_prepare_cpus(unsigned int max_cpus) | 302 | void __init smp_prepare_cpus(unsigned int max_cpus) |
283 | { | 303 | { |
284 | int cpu; | 304 | int cpu, err; |
285 | void **release_addr; | ||
286 | unsigned int ncores = num_possible_cpus(); | 305 | unsigned int ncores = num_possible_cpus(); |
287 | 306 | ||
288 | /* | 307 | /* |
@@ -291,30 +310,35 @@ void __init smp_prepare_cpus(unsigned int max_cpus) | |||
291 | if (max_cpus > ncores) | 310 | if (max_cpus > ncores) |
292 | max_cpus = ncores; | 311 | max_cpus = ncores; |
293 | 312 | ||
313 | /* Don't bother if we're effectively UP */ | ||
314 | if (max_cpus <= 1) | ||
315 | return; | ||
316 | |||
294 | /* | 317 | /* |
295 | * Initialise the present map (which describes the set of CPUs | 318 | * Initialise the present map (which describes the set of CPUs |
296 | * actually populated at the present time) and release the | 319 | * actually populated at the present time) and release the |
297 | * secondaries from the bootloader. | 320 | * secondaries from the bootloader. |
321 | * | ||
322 | * Make sure we online at most (max_cpus - 1) additional CPUs. | ||
298 | */ | 323 | */ |
324 | max_cpus--; | ||
299 | for_each_possible_cpu(cpu) { | 325 | for_each_possible_cpu(cpu) { |
300 | if (max_cpus == 0) | 326 | if (max_cpus == 0) |
301 | break; | 327 | break; |
302 | 328 | ||
303 | if (!cpu_release_addr[cpu]) | 329 | if (cpu == smp_processor_id()) |
330 | continue; | ||
331 | |||
332 | if (!smp_enable_ops[cpu]) | ||
304 | continue; | 333 | continue; |
305 | 334 | ||
306 | release_addr = __va(cpu_release_addr[cpu]); | 335 | err = smp_enable_ops[cpu]->prepare_cpu(cpu); |
307 | release_addr[0] = (void *)__pa(secondary_holding_pen); | 336 | if (err) |
308 | __flush_dcache_area(release_addr, sizeof(release_addr[0])); | 337 | continue; |
309 | 338 | ||
310 | set_cpu_present(cpu, true); | 339 | set_cpu_present(cpu, true); |
311 | max_cpus--; | 340 | max_cpus--; |
312 | } | 341 | } |
313 | |||
314 | /* | ||
315 | * Send an event to wake up the secondaries. | ||
316 | */ | ||
317 | sev(); | ||
318 | } | 342 | } |
319 | 343 | ||
320 | 344 | ||
diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c new file mode 100644 index 000000000000..7c35fa682f76 --- /dev/null +++ b/arch/arm64/kernel/smp_spin_table.c | |||
@@ -0,0 +1,66 @@ | |||
1 | /* | ||
2 | * Spin Table SMP initialisation | ||
3 | * | ||
4 | * Copyright (C) 2013 ARM Ltd. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #include <linux/init.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <linux/smp.h> | ||
22 | |||
23 | #include <asm/cacheflush.h> | ||
24 | |||
25 | static phys_addr_t cpu_release_addr[NR_CPUS]; | ||
26 | |||
27 | static int __init smp_spin_table_init_cpu(struct device_node *dn, int cpu) | ||
28 | { | ||
29 | /* | ||
30 | * Determine the address from which the CPU is polling. | ||
31 | */ | ||
32 | if (of_property_read_u64(dn, "cpu-release-addr", | ||
33 | &cpu_release_addr[cpu])) { | ||
34 | pr_err("CPU %d: missing or invalid cpu-release-addr property\n", | ||
35 | cpu); | ||
36 | |||
37 | return -1; | ||
38 | } | ||
39 | |||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static int __init smp_spin_table_prepare_cpu(int cpu) | ||
44 | { | ||
45 | void **release_addr; | ||
46 | |||
47 | if (!cpu_release_addr[cpu]) | ||
48 | return -ENODEV; | ||
49 | |||
50 | release_addr = __va(cpu_release_addr[cpu]); | ||
51 | release_addr[0] = (void *)__pa(secondary_holding_pen); | ||
52 | __flush_dcache_area(release_addr, sizeof(release_addr[0])); | ||
53 | |||
54 | /* | ||
55 | * Send an event to wake up the secondary CPU. | ||
56 | */ | ||
57 | sev(); | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | const struct smp_enable_ops smp_spin_table_ops __initconst = { | ||
63 | .name = "spin-table", | ||
64 | .init_cpu = smp_spin_table_init_cpu, | ||
65 | .prepare_cpu = smp_spin_table_prepare_cpu, | ||
66 | }; | ||