| 
									
										
										
										
											2010-02-11 21:56:07 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  linux/arch/arm/mach-vexpress/platsmp.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Copyright (C) 2002 ARM Ltd. | 
					
						
							|  |  |  |  *  All Rights Reserved | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License version 2 as | 
					
						
							|  |  |  |  * published by the Free Software Foundation. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/errno.h>
 | 
					
						
							|  |  |  | #include <linux/smp.h>
 | 
					
						
							|  |  |  | #include <linux/io.h>
 | 
					
						
							| 
									
										
										
										
											2013-01-30 09:12:55 +00:00
										 |  |  | #include <linux/of.h>
 | 
					
						
							| 
									
										
										
										
											2012-02-24 09:18:14 +00:00
										 |  |  | #include <linux/of_fdt.h>
 | 
					
						
							| 
									
										
										
										
											2012-10-09 12:56:36 +01:00
										 |  |  | #include <linux/vexpress.h>
 | 
					
						
							| 
									
										
										
										
											2012-02-24 09:18:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-30 09:12:55 +00:00
										 |  |  | #include <asm/mcpm.h>
 | 
					
						
							| 
									
										
										
										
											2012-02-24 09:18:14 +00:00
										 |  |  | #include <asm/smp_scu.h>
 | 
					
						
							|  |  |  | #include <asm/mach/map.h>
 | 
					
						
							| 
									
										
										
										
											2010-02-11 21:56:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <mach/motherboard.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-08 13:15:22 +01:00
										 |  |  | #include <plat/platsmp.h>
 | 
					
						
							| 
									
										
										
										
											2010-02-11 21:56:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-08 13:15:22 +01:00
										 |  |  | #include "core.h"
 | 
					
						
							| 
									
										
											  
											
												ARM: Fix subtle race in CPU pen_release hotplug code
There is a subtle race in the CPU hotplug code, where a CPU which has
been offlined can online itself before being requested, which results
in things going astray on the next online/offline cycle.
What happens in the normal online/offline/online cycle is:
	CPU0			CPU3
	requests boot of CPU3
	pen_release = 3
	flush cache line
				checks pen_release, reads 3
				starts boot
				pen_release = -1
	... requests CPU3 offline ...
				... dies ...
				checks pen_release, reads -1
	requests boot of CPU3
	pen_release = 3
	flush cache line
				checks pen_release, reads 3
				starts boot
				pen_release = -1
However, as the write of -1 of pen_release is not fully flushed back to
memory, and the checking of pen_release is done with caches disabled,
this allows CPU3 the opportunity to read the old value of pen_release:
	CPU0			CPU3
	requests boot of CPU3
	pen_release = 3
	flush cache line
				checks pen_release, reads 3
				starts boot
				pen_release = -1
	... requests CPU3 offline ...
				... dies ...
				checks pen_release, reads 3
				starts boot
				pen_release = -1
	requests boot of CPU3
	pen_release = 3
	flush cache line
Fix this by grouping the write of pen_release along with its cache line
flushing code to ensure that any update to pen_release is always pushed
out to physical memory.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
											
										 
											2010-12-18 10:53:12 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-24 09:18:14 +00:00
										 |  |  | #if defined(CONFIG_OF)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static enum { | 
					
						
							|  |  |  | 	GENERIC_SCU, | 
					
						
							|  |  |  | 	CORTEX_A9_SCU, | 
					
						
							|  |  |  | } vexpress_dt_scu __initdata = GENERIC_SCU; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct map_desc vexpress_dt_cortex_a9_scu_map __initdata = { | 
					
						
							|  |  |  | 	.virtual	= V2T_PERIPH, | 
					
						
							|  |  |  | 	/* .pfn	set in vexpress_dt_init_cortex_a9_scu() */ | 
					
						
							|  |  |  | 	.length		= SZ_128, | 
					
						
							|  |  |  | 	.type		= MT_DEVICE, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *vexpress_dt_cortex_a9_scu_base __initdata; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const static char *vexpress_dt_cortex_a9_match[] __initconst = { | 
					
						
							|  |  |  | 	"arm,cortex-a5-scu", | 
					
						
							|  |  |  | 	"arm,cortex-a9-scu", | 
					
						
							|  |  |  | 	NULL | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init vexpress_dt_find_scu(unsigned long node, | 
					
						
							|  |  |  | 		const char *uname, int depth, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (of_flat_dt_match(node, vexpress_dt_cortex_a9_match)) { | 
					
						
							|  |  |  | 		phys_addr_t phys_addr; | 
					
						
							|  |  |  | 		__be32 *reg = of_get_flat_dt_prop(node, "reg", NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (WARN_ON(!reg)) | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		phys_addr = be32_to_cpup(reg); | 
					
						
							|  |  |  | 		vexpress_dt_scu = CORTEX_A9_SCU; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		vexpress_dt_cortex_a9_scu_map.pfn = __phys_to_pfn(phys_addr); | 
					
						
							|  |  |  | 		iotable_init(&vexpress_dt_cortex_a9_scu_map, 1); | 
					
						
							|  |  |  | 		vexpress_dt_cortex_a9_scu_base = ioremap(phys_addr, SZ_256); | 
					
						
							|  |  |  | 		if (WARN_ON(!vexpress_dt_cortex_a9_scu_base)) | 
					
						
							|  |  |  | 			return -EFAULT; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void __init vexpress_dt_smp_map_io(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (initial_boot_params) | 
					
						
							|  |  |  | 		WARN_ON(of_scan_flat_dt(vexpress_dt_find_scu, NULL)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init vexpress_dt_cpus_num(unsigned long node, const char *uname, | 
					
						
							|  |  |  | 		int depth, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	static int prev_depth = -1; | 
					
						
							|  |  |  | 	static int nr_cpus = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (prev_depth > depth && nr_cpus > 0) | 
					
						
							|  |  |  | 		return nr_cpus; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (nr_cpus < 0 && strcmp(uname, "cpus") == 0) | 
					
						
							|  |  |  | 		nr_cpus = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (nr_cpus >= 0) { | 
					
						
							|  |  |  | 		const char *device_type = of_get_flat_dt_prop(node, | 
					
						
							|  |  |  | 				"device_type", NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (device_type && strcmp(device_type, "cpu") == 0) | 
					
						
							|  |  |  | 			nr_cpus++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	prev_depth = depth; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __init vexpress_dt_smp_init_cpus(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ncores = 0, i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (vexpress_dt_scu) { | 
					
						
							|  |  |  | 	case GENERIC_SCU: | 
					
						
							|  |  |  | 		ncores = of_scan_flat_dt(vexpress_dt_cpus_num, NULL); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case CORTEX_A9_SCU: | 
					
						
							|  |  |  | 		ncores = scu_get_core_count(vexpress_dt_cortex_a9_scu_base); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		WARN_ON(1); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ncores < 2) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ncores > nr_cpu_ids) { | 
					
						
							|  |  |  | 		pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", | 
					
						
							|  |  |  | 				ncores, nr_cpu_ids); | 
					
						
							|  |  |  | 		ncores = nr_cpu_ids; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < ncores; ++i) | 
					
						
							|  |  |  | 		set_cpu_possible(i, true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __init vexpress_dt_smp_prepare_cpus(unsigned int max_cpus) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (vexpress_dt_scu) { | 
					
						
							|  |  |  | 	case GENERIC_SCU: | 
					
						
							|  |  |  | 		for (i = 0; i < max_cpus; i++) | 
					
						
							|  |  |  | 			set_cpu_present(i, true); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case CORTEX_A9_SCU: | 
					
						
							|  |  |  | 		scu_enable(vexpress_dt_cortex_a9_scu_base); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		WARN_ON(1); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __init vexpress_dt_smp_init_cpus(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	WARN_ON(1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void __init vexpress_dt_smp_prepare_cpus(unsigned int max_cpus) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	WARN_ON(1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-11 21:56:07 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Initialise the CPU possible map early - this describes the CPUs | 
					
						
							|  |  |  |  * which may be present or become present in the system. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2011-09-08 13:15:22 +01:00
										 |  |  | static void __init vexpress_smp_init_cpus(void) | 
					
						
							| 
									
										
										
										
											2010-02-11 21:56:07 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-02-24 09:18:14 +00:00
										 |  |  | 	if (ct_desc) | 
					
						
							|  |  |  | 		ct_desc->init_cpu_map(); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		vexpress_dt_smp_init_cpus(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-11 21:56:07 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-08 13:15:22 +01:00
										 |  |  | static void __init vexpress_smp_prepare_cpus(unsigned int max_cpus) | 
					
						
							| 
									
										
										
										
											2010-02-11 21:56:07 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Initialise the present map, which describes the set of CPUs | 
					
						
							|  |  |  | 	 * actually populated at the present time. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2012-02-24 09:18:14 +00:00
										 |  |  | 	if (ct_desc) | 
					
						
							|  |  |  | 		ct_desc->smp_enable(max_cpus); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		vexpress_dt_smp_prepare_cpus(max_cpus); | 
					
						
							| 
									
										
										
										
											2010-12-03 11:09:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-11 21:56:07 +00:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2010-12-03 11:09:48 +00:00
										 |  |  | 	 * Write the address of secondary startup into the | 
					
						
							|  |  |  | 	 * system-wide flags register. The boot monitor waits | 
					
						
							|  |  |  | 	 * until it receives a soft interrupt, and then the | 
					
						
							|  |  |  | 	 * secondary CPU branches to this address. | 
					
						
							| 
									
										
										
										
											2010-02-11 21:56:07 +00:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2012-10-09 12:56:36 +01:00
										 |  |  | 	vexpress_flags_set(virt_to_phys(versatile_secondary_startup)); | 
					
						
							| 
									
										
										
										
											2010-02-11 21:56:07 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2011-09-08 13:15:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | struct smp_operations __initdata vexpress_smp_ops = { | 
					
						
							|  |  |  | 	.smp_init_cpus		= vexpress_smp_init_cpus, | 
					
						
							|  |  |  | 	.smp_prepare_cpus	= vexpress_smp_prepare_cpus, | 
					
						
							|  |  |  | 	.smp_secondary_init	= versatile_secondary_init, | 
					
						
							|  |  |  | 	.smp_boot_secondary	= versatile_boot_secondary, | 
					
						
							|  |  |  | #ifdef CONFIG_HOTPLUG_CPU
 | 
					
						
							|  |  |  | 	.cpu_die		= vexpress_cpu_die, | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2013-01-30 09:12:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | bool __init vexpress_smp_init_ops(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | #ifdef CONFIG_MCPM
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * The best way to detect a multi-cluster configuration at the moment | 
					
						
							|  |  |  | 	 * is to look for the presence of a CCI in the system. | 
					
						
							|  |  |  | 	 * Override the default vexpress_smp_ops if so. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	struct device_node *node; | 
					
						
							|  |  |  | 	node = of_find_compatible_node(NULL, NULL, "arm,cci-400"); | 
					
						
							|  |  |  | 	if (node && of_device_is_available(node)) { | 
					
						
							|  |  |  | 		mcpm_smp_set_ops(); | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } |