aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorDave Martin <dave.martin@linaro.org>2013-10-01 14:58:17 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2013-10-29 07:07:15 -0400
commit0de0d64675259bf21d06b18985318ffb66a5218f (patch)
tree06f797f0632d681d537e867d1e6a1bc69a9fa135 /arch
parent1e5660999aa7703654ec345caaf06b83415dbdff (diff)
ARM: 7848/1: mcpm: Implement cpu_kill() to synchronise on powerdown
CPU hotplug and kexec rely on smp_ops.cpu_kill(), which is supposed to wait for the CPU to park or power down, and perform the last rites (such as disabling clocks etc., where the platform doesn't do this automatically). kexec in particular is unsafe without performing this synchronisation to park secondaries. Without it, the secondaries might not be parked when kexec trashes the kernel. There is no generic way to do this synchronisation, so a new mcpm platform_ops method power_down_finish() is added by this patch. The new method is mandatory. A platform which provides no way to detect when CPUs are parked is likely broken. Signed-off-by: Dave Martin <Dave.Martin@arm.com> Reviewed-by: Nicolas Pitre <nico@linaro.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/common/mcpm_entry.c15
-rw-r--r--arch/arm/common/mcpm_platsmp.c10
-rw-r--r--arch/arm/include/asm/mcpm.h31
3 files changed, 56 insertions, 0 deletions
diff --git a/arch/arm/common/mcpm_entry.c b/arch/arm/common/mcpm_entry.c
index 990250965f2c..6c03d0152e7f 100644
--- a/arch/arm/common/mcpm_entry.c
+++ b/arch/arm/common/mcpm_entry.c
@@ -90,6 +90,21 @@ void mcpm_cpu_power_down(void)
90 BUG(); 90 BUG();
91} 91}
92 92
93int mcpm_cpu_power_down_finish(unsigned int cpu, unsigned int cluster)
94{
95 int ret;
96
97 if (WARN_ON_ONCE(!platform_ops || !platform_ops->power_down_finish))
98 return -EUNATCH;
99
100 ret = platform_ops->power_down_finish(cpu, cluster);
101 if (ret)
102 pr_warn("%s: cpu %u, cluster %u failed to power down (%d)\n",
103 __func__, cpu, cluster, ret);
104
105 return ret;
106}
107
93void mcpm_cpu_suspend(u64 expected_residency) 108void mcpm_cpu_suspend(u64 expected_residency)
94{ 109{
95 phys_reset_t phys_reset; 110 phys_reset_t phys_reset;
diff --git a/arch/arm/common/mcpm_platsmp.c b/arch/arm/common/mcpm_platsmp.c
index c0c3cd799ccc..177251a4dd9a 100644
--- a/arch/arm/common/mcpm_platsmp.c
+++ b/arch/arm/common/mcpm_platsmp.c
@@ -56,6 +56,15 @@ static void mcpm_secondary_init(unsigned int cpu)
56 56
57#ifdef CONFIG_HOTPLUG_CPU 57#ifdef CONFIG_HOTPLUG_CPU
58 58
59static int mcpm_cpu_kill(unsigned int cpu)
60{
61 unsigned int pcpu, pcluster;
62
63 cpu_to_pcpu(cpu, &pcpu, &pcluster);
64
65 return !mcpm_cpu_power_down_finish(pcpu, pcluster);
66}
67
59static int mcpm_cpu_disable(unsigned int cpu) 68static int mcpm_cpu_disable(unsigned int cpu)
60{ 69{
61 /* 70 /*
@@ -82,6 +91,7 @@ static struct smp_operations __initdata mcpm_smp_ops = {
82 .smp_boot_secondary = mcpm_boot_secondary, 91 .smp_boot_secondary = mcpm_boot_secondary,
83 .smp_secondary_init = mcpm_secondary_init, 92 .smp_secondary_init = mcpm_secondary_init,
84#ifdef CONFIG_HOTPLUG_CPU 93#ifdef CONFIG_HOTPLUG_CPU
94 .cpu_kill = mcpm_cpu_kill,
85 .cpu_disable = mcpm_cpu_disable, 95 .cpu_disable = mcpm_cpu_disable,
86 .cpu_die = mcpm_cpu_die, 96 .cpu_die = mcpm_cpu_die,
87#endif 97#endif
diff --git a/arch/arm/include/asm/mcpm.h b/arch/arm/include/asm/mcpm.h
index fc82a88f5b69..1cf26010a6f3 100644
--- a/arch/arm/include/asm/mcpm.h
+++ b/arch/arm/include/asm/mcpm.h
@@ -81,10 +81,40 @@ int mcpm_cpu_power_up(unsigned int cpu, unsigned int cluster);
81 * 81 *
82 * This will return if mcpm_platform_register() has not been called 82 * This will return if mcpm_platform_register() has not been called
83 * previously in which case the caller should take appropriate action. 83 * previously in which case the caller should take appropriate action.
84 *
85 * On success, the CPU is not guaranteed to be truly halted until
86 * mcpm_cpu_power_down_finish() subsequently returns non-zero for the
87 * specified cpu. Until then, other CPUs should make sure they do not
88 * trash memory the target CPU might be executing/accessing.
84 */ 89 */
85void mcpm_cpu_power_down(void); 90void mcpm_cpu_power_down(void);
86 91
87/** 92/**
93 * mcpm_cpu_power_down_finish - wait for a specified CPU to halt, and
94 * make sure it is powered off
95 *
96 * @cpu: CPU number within given cluster
97 * @cluster: cluster number for the CPU
98 *
99 * Call this function to ensure that a pending powerdown has taken
100 * effect and the CPU is safely parked before performing non-mcpm
101 * operations that may affect the CPU (such as kexec trashing the
102 * kernel text).
103 *
104 * It is *not* necessary to call this function if you only need to
105 * serialise a pending powerdown with mcpm_cpu_power_up() or a wakeup
106 * event.
107 *
108 * Do not call this function unless the specified CPU has already
109 * called mcpm_cpu_power_down() or has committed to doing so.
110 *
111 * @return:
112 * - zero if the CPU is in a safely parked state
113 * - nonzero otherwise (e.g., timeout)
114 */
115int mcpm_cpu_power_down_finish(unsigned int cpu, unsigned int cluster);
116
117/**
88 * mcpm_cpu_suspend - bring the calling CPU in a suspended state 118 * mcpm_cpu_suspend - bring the calling CPU in a suspended state
89 * 119 *
90 * @expected_residency: duration in microseconds the CPU is expected 120 * @expected_residency: duration in microseconds the CPU is expected
@@ -126,6 +156,7 @@ int mcpm_cpu_powered_up(void);
126struct mcpm_platform_ops { 156struct mcpm_platform_ops {
127 int (*power_up)(unsigned int cpu, unsigned int cluster); 157 int (*power_up)(unsigned int cpu, unsigned int cluster);
128 void (*power_down)(void); 158 void (*power_down)(void);
159 int (*power_down_finish)(unsigned int cpu, unsigned int cluster);
129 void (*suspend)(u64); 160 void (*suspend)(u64);
130 void (*powered_up)(void); 161 void (*powered_up)(void);
131}; 162};