diff options
| -rw-r--r-- | arch/arm/common/mcpm_entry.c | 15 | ||||
| -rw-r--r-- | arch/arm/common/mcpm_platsmp.c | 10 | ||||
| -rw-r--r-- | arch/arm/include/asm/mcpm.h | 31 |
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 | ||
| 93 | int 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 | |||
| 93 | void mcpm_cpu_suspend(u64 expected_residency) | 108 | void 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 | ||
| 59 | static 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 | |||
| 59 | static int mcpm_cpu_disable(unsigned int cpu) | 68 | static 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 | */ |
| 85 | void mcpm_cpu_power_down(void); | 90 | void 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 | */ | ||
| 115 | int 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); | |||
| 126 | struct mcpm_platform_ops { | 156 | struct 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 | }; |
