| 
									
										
										
										
											2012-11-19 16:02:17 -05:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * linux/arch/arm/mach-vexpress/mcpm_platsmp.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Created by:  Nicolas Pitre, November 2012 | 
					
						
							|  |  |  |  * Copyright:   (C) 2012-2013  Linaro Limited | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Code to handle secondary CPU bringup and hotplug for the cluster power API. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/smp.h>
 | 
					
						
							|  |  |  | #include <linux/spinlock.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <asm/mcpm.h>
 | 
					
						
							|  |  |  | #include <asm/smp.h>
 | 
					
						
							|  |  |  | #include <asm/smp_plat.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-01 19:57:28 +01:00
										 |  |  | static void cpu_to_pcpu(unsigned int cpu, | 
					
						
							|  |  |  | 			unsigned int *pcpu, unsigned int *pcluster) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int mpidr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mpidr = cpu_logical_map(cpu); | 
					
						
							|  |  |  | 	*pcpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); | 
					
						
							|  |  |  | 	*pcluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-17 15:43:14 -04:00
										 |  |  | static int mcpm_boot_secondary(unsigned int cpu, struct task_struct *idle) | 
					
						
							| 
									
										
										
										
											2012-11-19 16:02:17 -05:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-10-01 19:57:28 +01:00
										 |  |  | 	unsigned int pcpu, pcluster, ret; | 
					
						
							| 
									
										
										
										
											2012-11-19 16:02:17 -05:00
										 |  |  | 	extern void secondary_startup(void); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-01 19:57:28 +01:00
										 |  |  | 	cpu_to_pcpu(cpu, &pcpu, &pcluster); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-19 16:02:17 -05:00
										 |  |  | 	pr_debug("%s: logical CPU %d is physical CPU %d cluster %d\n", | 
					
						
							|  |  |  | 		 __func__, cpu, pcpu, pcluster); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mcpm_set_entry_vector(pcpu, pcluster, NULL); | 
					
						
							|  |  |  | 	ret = mcpm_cpu_power_up(pcpu, pcluster); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	mcpm_set_entry_vector(pcpu, pcluster, secondary_startup); | 
					
						
							|  |  |  | 	arch_send_wakeup_ipi_mask(cpumask_of(cpu)); | 
					
						
							|  |  |  | 	dsb_sev(); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-17 15:43:14 -04:00
										 |  |  | static void mcpm_secondary_init(unsigned int cpu) | 
					
						
							| 
									
										
										
										
											2012-11-19 16:02:17 -05:00
										 |  |  | { | 
					
						
							|  |  |  | 	mcpm_cpu_powered_up(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_HOTPLUG_CPU
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-01 19:58:17 +01:00
										 |  |  | static int mcpm_cpu_kill(unsigned int cpu) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int pcpu, pcluster; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cpu_to_pcpu(cpu, &pcpu, &pcluster); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-04-17 16:58:39 +01:00
										 |  |  | 	return !mcpm_wait_for_cpu_powerdown(pcpu, pcluster); | 
					
						
							| 
									
										
										
										
											2013-10-01 19:58:17 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-19 16:02:17 -05:00
										 |  |  | static int mcpm_cpu_disable(unsigned int cpu) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * We assume all CPUs may be shut down. | 
					
						
							|  |  |  | 	 * This would be the hook to use for eventual Secure | 
					
						
							|  |  |  | 	 * OS migration requests as described in the PSCI spec. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void mcpm_cpu_die(unsigned int cpu) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int mpidr, pcpu, pcluster; | 
					
						
							|  |  |  | 	mpidr = read_cpuid_mpidr(); | 
					
						
							|  |  |  | 	pcpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); | 
					
						
							|  |  |  | 	pcluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); | 
					
						
							|  |  |  | 	mcpm_set_entry_vector(pcpu, pcluster, NULL); | 
					
						
							|  |  |  | 	mcpm_cpu_power_down(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-09 01:29:17 -04:00
										 |  |  | static struct smp_operations __initdata mcpm_smp_ops = { | 
					
						
							| 
									
										
										
										
											2012-11-19 16:02:17 -05:00
										 |  |  | 	.smp_boot_secondary	= mcpm_boot_secondary, | 
					
						
							|  |  |  | 	.smp_secondary_init	= mcpm_secondary_init, | 
					
						
							|  |  |  | #ifdef CONFIG_HOTPLUG_CPU
 | 
					
						
							| 
									
										
										
										
											2013-10-01 19:58:17 +01:00
										 |  |  | 	.cpu_kill		= mcpm_cpu_kill, | 
					
						
							| 
									
										
										
										
											2012-11-19 16:02:17 -05:00
										 |  |  | 	.cpu_disable		= mcpm_cpu_disable, | 
					
						
							|  |  |  | 	.cpu_die		= mcpm_cpu_die, | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2013-04-09 01:29:17 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | void __init mcpm_smp_set_ops(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	smp_set_ops(&mcpm_smp_ops); | 
					
						
							|  |  |  | } |