diff options
author | Zhao Chenhui <chenhui.zhao@freescale.com> | 2012-07-20 08:42:36 -0400 |
---|---|---|
committer | Kumar Gala <galak@kernel.crashing.org> | 2012-09-12 15:57:08 -0400 |
commit | d0832a75075b1119635e0f48549e378040cf5e67 (patch) | |
tree | 9d835dba5b7663517ea08cbde0b9b694614ba704 /arch/powerpc | |
parent | bf34526374a334ddfafaed73b0d8bf7eb4dea833 (diff) |
powerpc/85xx: add HOTPLUG_CPU support
Add support to disable and re-enable individual cores at runtime on
MPC85xx/QorIQ SMP machines. Currently support e500v1/e500v2 core.
MPC85xx machines use ePAPR spin-table in boot page for CPU kick-off. This
patch uses the boot page from bootloader to boot core at runtime. It
supports 32-bit and 36-bit physical address.
Signed-off-by: Li Yang <leoli@freescale.com>
Signed-off-by: Jin Qing <b24347@freescale.com>
Signed-off-by: Zhao Chenhui <chenhui.zhao@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/Kconfig | 6 | ||||
-rw-r--r-- | arch/powerpc/include/asm/cacheflush.h | 2 | ||||
-rw-r--r-- | arch/powerpc/include/asm/smp.h | 1 | ||||
-rw-r--r-- | arch/powerpc/kernel/head_fsl_booke.S | 28 | ||||
-rw-r--r-- | arch/powerpc/platforms/85xx/smp.c | 90 |
5 files changed, 112 insertions, 15 deletions
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 98e513b62709..b8bab10bd0f1 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig | |||
@@ -215,7 +215,8 @@ config ARCH_HIBERNATION_POSSIBLE | |||
215 | config ARCH_SUSPEND_POSSIBLE | 215 | config ARCH_SUSPEND_POSSIBLE |
216 | def_bool y | 216 | def_bool y |
217 | depends on ADB_PMU || PPC_EFIKA || PPC_LITE5200 || PPC_83xx || \ | 217 | depends on ADB_PMU || PPC_EFIKA || PPC_LITE5200 || PPC_83xx || \ |
218 | (PPC_85xx && !SMP) || PPC_86xx || PPC_PSERIES || 44x || 40x | 218 | (PPC_85xx && !PPC_E500MC) || PPC_86xx || PPC_PSERIES \ |
219 | || 44x || 40x | ||
219 | 220 | ||
220 | config PPC_DCR_NATIVE | 221 | config PPC_DCR_NATIVE |
221 | bool | 222 | bool |
@@ -328,7 +329,8 @@ config SWIOTLB | |||
328 | 329 | ||
329 | config HOTPLUG_CPU | 330 | config HOTPLUG_CPU |
330 | bool "Support for enabling/disabling CPUs" | 331 | bool "Support for enabling/disabling CPUs" |
331 | depends on SMP && HOTPLUG && EXPERIMENTAL && (PPC_PSERIES || PPC_PMAC || PPC_POWERNV) | 332 | depends on SMP && HOTPLUG && EXPERIMENTAL && (PPC_PSERIES || \ |
333 | PPC_PMAC || PPC_POWERNV || (PPC_85xx && !PPC_E500MC)) | ||
332 | ---help--- | 334 | ---help--- |
333 | Say Y here to be able to disable and re-enable individual | 335 | Say Y here to be able to disable and re-enable individual |
334 | CPUs at runtime on SMP machines. | 336 | CPUs at runtime on SMP machines. |
diff --git a/arch/powerpc/include/asm/cacheflush.h b/arch/powerpc/include/asm/cacheflush.h index ab9e402518e8..b843e35122e8 100644 --- a/arch/powerpc/include/asm/cacheflush.h +++ b/arch/powerpc/include/asm/cacheflush.h | |||
@@ -30,6 +30,8 @@ extern void flush_dcache_page(struct page *page); | |||
30 | #define flush_dcache_mmap_lock(mapping) do { } while (0) | 30 | #define flush_dcache_mmap_lock(mapping) do { } while (0) |
31 | #define flush_dcache_mmap_unlock(mapping) do { } while (0) | 31 | #define flush_dcache_mmap_unlock(mapping) do { } while (0) |
32 | 32 | ||
33 | extern void __flush_disable_L1(void); | ||
34 | |||
33 | extern void __flush_icache_range(unsigned long, unsigned long); | 35 | extern void __flush_icache_range(unsigned long, unsigned long); |
34 | static inline void flush_icache_range(unsigned long start, unsigned long stop) | 36 | static inline void flush_icache_range(unsigned long start, unsigned long stop) |
35 | { | 37 | { |
diff --git a/arch/powerpc/include/asm/smp.h b/arch/powerpc/include/asm/smp.h index ce8e2bdf84ed..e807e9d8e3f7 100644 --- a/arch/powerpc/include/asm/smp.h +++ b/arch/powerpc/include/asm/smp.h | |||
@@ -191,6 +191,7 @@ extern unsigned long __secondary_hold_spinloop; | |||
191 | extern unsigned long __secondary_hold_acknowledge; | 191 | extern unsigned long __secondary_hold_acknowledge; |
192 | extern char __secondary_hold; | 192 | extern char __secondary_hold; |
193 | 193 | ||
194 | extern void __early_start(void); | ||
194 | #endif /* __ASSEMBLY__ */ | 195 | #endif /* __ASSEMBLY__ */ |
195 | 196 | ||
196 | #endif /* __KERNEL__ */ | 197 | #endif /* __KERNEL__ */ |
diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index 0f59863c3ade..b221541d9861 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S | |||
@@ -1043,6 +1043,34 @@ _GLOBAL(flush_dcache_L1) | |||
1043 | 1043 | ||
1044 | blr | 1044 | blr |
1045 | 1045 | ||
1046 | /* Flush L1 d-cache, invalidate and disable d-cache and i-cache */ | ||
1047 | _GLOBAL(__flush_disable_L1) | ||
1048 | mflr r10 | ||
1049 | bl flush_dcache_L1 /* Flush L1 d-cache */ | ||
1050 | mtlr r10 | ||
1051 | |||
1052 | mfspr r4, SPRN_L1CSR0 /* Invalidate and disable d-cache */ | ||
1053 | li r5, 2 | ||
1054 | rlwimi r4, r5, 0, 3 | ||
1055 | |||
1056 | msync | ||
1057 | isync | ||
1058 | mtspr SPRN_L1CSR0, r4 | ||
1059 | isync | ||
1060 | |||
1061 | 1: mfspr r4, SPRN_L1CSR0 /* Wait for the invalidate to finish */ | ||
1062 | andi. r4, r4, 2 | ||
1063 | bne 1b | ||
1064 | |||
1065 | mfspr r4, SPRN_L1CSR1 /* Invalidate and disable i-cache */ | ||
1066 | li r5, 2 | ||
1067 | rlwimi r4, r5, 0, 3 | ||
1068 | |||
1069 | mtspr SPRN_L1CSR1, r4 | ||
1070 | isync | ||
1071 | |||
1072 | blr | ||
1073 | |||
1046 | #ifdef CONFIG_SMP | 1074 | #ifdef CONFIG_SMP |
1047 | /* When we get here, r24 needs to hold the CPU # */ | 1075 | /* When we get here, r24 needs to hold the CPU # */ |
1048 | .globl __secondary_start | 1076 | .globl __secondary_start |
diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c index 7ed52a604a13..6fcfa12e5c56 100644 --- a/arch/powerpc/platforms/85xx/smp.c +++ b/arch/powerpc/platforms/85xx/smp.c | |||
@@ -31,8 +31,6 @@ | |||
31 | #include <sysdev/mpic.h> | 31 | #include <sysdev/mpic.h> |
32 | #include "smp.h" | 32 | #include "smp.h" |
33 | 33 | ||
34 | extern void __early_start(void); | ||
35 | |||
36 | struct epapr_spin_table { | 34 | struct epapr_spin_table { |
37 | u32 addr_h; | 35 | u32 addr_h; |
38 | u32 addr_l; | 36 | u32 addr_l; |
@@ -100,15 +98,45 @@ static void mpc85xx_take_timebase(void) | |||
100 | local_irq_restore(flags); | 98 | local_irq_restore(flags); |
101 | } | 99 | } |
102 | 100 | ||
103 | static int __init | 101 | #ifdef CONFIG_HOTPLUG_CPU |
104 | smp_85xx_kick_cpu(int nr) | 102 | static void __cpuinit smp_85xx_mach_cpu_die(void) |
103 | { | ||
104 | unsigned int cpu = smp_processor_id(); | ||
105 | u32 tmp; | ||
106 | |||
107 | local_irq_disable(); | ||
108 | idle_task_exit(); | ||
109 | generic_set_cpu_dead(cpu); | ||
110 | mb(); | ||
111 | |||
112 | mtspr(SPRN_TCR, 0); | ||
113 | |||
114 | __flush_disable_L1(); | ||
115 | tmp = (mfspr(SPRN_HID0) & ~(HID0_DOZE|HID0_SLEEP)) | HID0_NAP; | ||
116 | mtspr(SPRN_HID0, tmp); | ||
117 | isync(); | ||
118 | |||
119 | /* Enter NAP mode. */ | ||
120 | tmp = mfmsr(); | ||
121 | tmp |= MSR_WE; | ||
122 | mb(); | ||
123 | mtmsr(tmp); | ||
124 | isync(); | ||
125 | |||
126 | while (1) | ||
127 | ; | ||
128 | } | ||
129 | #endif | ||
130 | |||
131 | static int __cpuinit smp_85xx_kick_cpu(int nr) | ||
105 | { | 132 | { |
106 | unsigned long flags; | 133 | unsigned long flags; |
107 | const u64 *cpu_rel_addr; | 134 | const u64 *cpu_rel_addr; |
108 | __iomem struct epapr_spin_table *spin_table; | 135 | __iomem struct epapr_spin_table *spin_table; |
109 | struct device_node *np; | 136 | struct device_node *np; |
110 | int n = 0, hw_cpu = get_hard_smp_processor_id(nr); | 137 | int hw_cpu = get_hard_smp_processor_id(nr); |
111 | int ioremappable; | 138 | int ioremappable; |
139 | int ret = 0; | ||
112 | 140 | ||
113 | WARN_ON(nr < 0 || nr >= NR_CPUS); | 141 | WARN_ON(nr < 0 || nr >= NR_CPUS); |
114 | WARN_ON(hw_cpu < 0 || hw_cpu >= NR_CPUS); | 142 | WARN_ON(hw_cpu < 0 || hw_cpu >= NR_CPUS); |
@@ -139,9 +167,34 @@ smp_85xx_kick_cpu(int nr) | |||
139 | spin_table = phys_to_virt(*cpu_rel_addr); | 167 | spin_table = phys_to_virt(*cpu_rel_addr); |
140 | 168 | ||
141 | local_irq_save(flags); | 169 | local_irq_save(flags); |
170 | #ifdef CONFIG_PPC32 | ||
171 | #ifdef CONFIG_HOTPLUG_CPU | ||
172 | /* Corresponding to generic_set_cpu_dead() */ | ||
173 | generic_set_cpu_up(nr); | ||
174 | |||
175 | if (system_state == SYSTEM_RUNNING) { | ||
176 | out_be32(&spin_table->addr_l, 0); | ||
142 | 177 | ||
178 | /* | ||
179 | * We don't set the BPTR register here since it already points | ||
180 | * to the boot page properly. | ||
181 | */ | ||
182 | mpic_reset_core(hw_cpu); | ||
183 | |||
184 | /* wait until core is ready... */ | ||
185 | if (!spin_event_timeout(in_be32(&spin_table->addr_l) == 1, | ||
186 | 10000, 100)) { | ||
187 | pr_err("%s: timeout waiting for core %d to reset\n", | ||
188 | __func__, hw_cpu); | ||
189 | ret = -ENOENT; | ||
190 | goto out; | ||
191 | } | ||
192 | |||
193 | /* clear the acknowledge status */ | ||
194 | __secondary_hold_acknowledge = -1; | ||
195 | } | ||
196 | #endif | ||
143 | out_be32(&spin_table->pir, hw_cpu); | 197 | out_be32(&spin_table->pir, hw_cpu); |
144 | #ifdef CONFIG_PPC32 | ||
145 | out_be32(&spin_table->addr_l, __pa(__early_start)); | 198 | out_be32(&spin_table->addr_l, __pa(__early_start)); |
146 | 199 | ||
147 | if (!ioremappable) | 200 | if (!ioremappable) |
@@ -149,11 +202,18 @@ smp_85xx_kick_cpu(int nr) | |||
149 | (ulong)spin_table + sizeof(struct epapr_spin_table)); | 202 | (ulong)spin_table + sizeof(struct epapr_spin_table)); |
150 | 203 | ||
151 | /* Wait a bit for the CPU to ack. */ | 204 | /* Wait a bit for the CPU to ack. */ |
152 | while ((__secondary_hold_acknowledge != hw_cpu) && (++n < 1000)) | 205 | if (!spin_event_timeout(__secondary_hold_acknowledge == hw_cpu, |
153 | mdelay(1); | 206 | 10000, 100)) { |
207 | pr_err("%s: timeout waiting for core %d to ack\n", | ||
208 | __func__, hw_cpu); | ||
209 | ret = -ENOENT; | ||
210 | goto out; | ||
211 | } | ||
212 | out: | ||
154 | #else | 213 | #else |
155 | smp_generic_kick_cpu(nr); | 214 | smp_generic_kick_cpu(nr); |
156 | 215 | ||
216 | out_be32(&spin_table->pir, hw_cpu); | ||
157 | out_be64((u64 *)(&spin_table->addr_h), | 217 | out_be64((u64 *)(&spin_table->addr_h), |
158 | __pa((u64)*((unsigned long long *)generic_secondary_smp_init))); | 218 | __pa((u64)*((unsigned long long *)generic_secondary_smp_init))); |
159 | 219 | ||
@@ -167,13 +227,15 @@ smp_85xx_kick_cpu(int nr) | |||
167 | if (ioremappable) | 227 | if (ioremappable) |
168 | iounmap(spin_table); | 228 | iounmap(spin_table); |
169 | 229 | ||
170 | pr_debug("waited %d msecs for CPU #%d.\n", n, nr); | 230 | return ret; |
171 | |||
172 | return 0; | ||
173 | } | 231 | } |
174 | 232 | ||
175 | struct smp_ops_t smp_85xx_ops = { | 233 | struct smp_ops_t smp_85xx_ops = { |
176 | .kick_cpu = smp_85xx_kick_cpu, | 234 | .kick_cpu = smp_85xx_kick_cpu, |
235 | #ifdef CONFIG_HOTPLUG_CPU | ||
236 | .cpu_disable = generic_cpu_disable, | ||
237 | .cpu_die = generic_cpu_die, | ||
238 | #endif | ||
177 | #ifdef CONFIG_KEXEC | 239 | #ifdef CONFIG_KEXEC |
178 | .give_timebase = smp_generic_give_timebase, | 240 | .give_timebase = smp_generic_give_timebase, |
179 | .take_timebase = smp_generic_take_timebase, | 241 | .take_timebase = smp_generic_take_timebase, |
@@ -277,8 +339,7 @@ static void mpc85xx_smp_machine_kexec(struct kimage *image) | |||
277 | } | 339 | } |
278 | #endif /* CONFIG_KEXEC */ | 340 | #endif /* CONFIG_KEXEC */ |
279 | 341 | ||
280 | static void __init | 342 | static void __cpuinit smp_85xx_setup_cpu(int cpu_nr) |
281 | smp_85xx_setup_cpu(int cpu_nr) | ||
282 | { | 343 | { |
283 | if (smp_85xx_ops.probe == smp_mpic_probe) | 344 | if (smp_85xx_ops.probe == smp_mpic_probe) |
284 | mpic_setup_this_cpu(); | 345 | mpic_setup_this_cpu(); |
@@ -329,6 +390,9 @@ void __init mpc85xx_smp_init(void) | |||
329 | } | 390 | } |
330 | smp_85xx_ops.give_timebase = mpc85xx_give_timebase; | 391 | smp_85xx_ops.give_timebase = mpc85xx_give_timebase; |
331 | smp_85xx_ops.take_timebase = mpc85xx_take_timebase; | 392 | smp_85xx_ops.take_timebase = mpc85xx_take_timebase; |
393 | #ifdef CONFIG_HOTPLUG_CPU | ||
394 | ppc_md.cpu_die = smp_85xx_mach_cpu_die; | ||
395 | #endif | ||
332 | } | 396 | } |
333 | 397 | ||
334 | smp_ops = &smp_85xx_ops; | 398 | smp_ops = &smp_85xx_ops; |