aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/loongson/loongson-3/smp.c
diff options
context:
space:
mode:
authorHuacai Chen <chenhc@lemote.com>2014-03-21 06:44:09 -0400
committerRalf Baechle <ralf@linux-mips.org>2014-03-31 12:17:12 -0400
commitc4a987db1b3cd89207cece4b8121c09cbfbc978a (patch)
treed9dc9db58b7259ac9e986b948e8b9c0abbebff39 /arch/mips/loongson/loongson-3/smp.c
parent300459d558725cdada5ddebbe52c24ef6e1853d3 (diff)
MIPS: Loongson 3: Add CPU hotplug support
Tips of Loongson's CPU hotplug: 1, To fully shutdown a core in Loongson 3, the target core should go to CKSEG1 and flush all L1 cache entries at first. Then, another core (usually Core 0) can safely disable the clock of the target core. So play_dead() call loongson3_play_dead() via CKSEG1 (both uncached and unmmaped). 2, The default clocksource of Loongson is MIPS. Since clock source is a global device, timekeeping need the CP0' Count registers of each core be synchronous. Thus, when a core is up, we use a SMP_ASK_C0COUNT IPI to ask Core-0's Count. Signed-off-by: Huacai Chen <chenhc@lemote.com> Signed-off-by: Hongliang Tao <taohl@lemote.com> Signed-off-by: Hua Yan <yanh@lemote.com> Tested-by: Alex Smith <alex.smith@imgtec.com> Reviewed-by: Alex Smith <alex.smith@imgtec.com> Cc: John Crispin <john@phrozen.org> Cc: Steven J. Hill <Steven.Hill@imgtec.com> Cc: Aurelien Jarno <aurelien@aurel32.net> Cc: linux-mips@linux-mips.org Cc: Fuxin Zhang <zhangfx@lemote.com> Cc: Zhangjin Wu <wuzhangjin@gmail.com> Patchwork: https://patchwork.linux-mips.org/patch/6639 Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/loongson/loongson-3/smp.c')
-rw-r--r--arch/mips/loongson/loongson-3/smp.c180
1 files changed, 178 insertions, 2 deletions
diff --git a/arch/mips/loongson/loongson-3/smp.c b/arch/mips/loongson/loongson-3/smp.c
index 93483c25b4b9..c665fe16d4c9 100644
--- a/arch/mips/loongson/loongson-3/smp.c
+++ b/arch/mips/loongson/loongson-3/smp.c
@@ -23,10 +23,14 @@
23#include <asm/time.h> 23#include <asm/time.h>
24#include <asm/clock.h> 24#include <asm/clock.h>
25#include <asm/tlbflush.h> 25#include <asm/tlbflush.h>
26#include <asm/cacheflush.h>
26#include <loongson.h> 27#include <loongson.h>
27 28
28#include "smp.h" 29#include "smp.h"
29 30
31DEFINE_PER_CPU(int, cpu_state);
32DEFINE_PER_CPU(uint32_t, core0_c0count);
33
30/* read a 32bit value from ipi register */ 34/* read a 32bit value from ipi register */
31#define loongson3_ipi_read32(addr) readl(addr) 35#define loongson3_ipi_read32(addr) readl(addr)
32/* read a 64bit value from ipi register */ 36/* read a 64bit value from ipi register */
@@ -158,8 +162,8 @@ loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action)
158 162
159void loongson3_ipi_interrupt(struct pt_regs *regs) 163void loongson3_ipi_interrupt(struct pt_regs *regs)
160{ 164{
161 int cpu = smp_processor_id(); 165 int i, cpu = smp_processor_id();
162 unsigned int action; 166 unsigned int action, c0count;
163 167
164 /* Load the ipi register to figure out what we're supposed to do */ 168 /* Load the ipi register to figure out what we're supposed to do */
165 action = loongson3_ipi_read32(ipi_status0_regs[cpu]); 169 action = loongson3_ipi_read32(ipi_status0_regs[cpu]);
@@ -172,14 +176,24 @@ void loongson3_ipi_interrupt(struct pt_regs *regs)
172 176
173 if (action & SMP_CALL_FUNCTION) 177 if (action & SMP_CALL_FUNCTION)
174 smp_call_function_interrupt(); 178 smp_call_function_interrupt();
179
180 if (action & SMP_ASK_C0COUNT) {
181 BUG_ON(cpu != 0);
182 c0count = read_c0_count();
183 for (i = 1; i < loongson_sysconf.nr_cpus; i++)
184 per_cpu(core0_c0count, i) = c0count;
185 }
175} 186}
176 187
188#define MAX_LOOPS 1111
177/* 189/*
178 * SMP init and finish on secondary CPUs 190 * SMP init and finish on secondary CPUs
179 */ 191 */
180static void loongson3_init_secondary(void) 192static void loongson3_init_secondary(void)
181{ 193{
182 int i; 194 int i;
195 uint32_t initcount;
196 unsigned int cpu = smp_processor_id();
183 unsigned int imask = STATUSF_IP7 | STATUSF_IP6 | 197 unsigned int imask = STATUSF_IP7 | STATUSF_IP6 |
184 STATUSF_IP3 | STATUSF_IP2; 198 STATUSF_IP3 | STATUSF_IP2;
185 199
@@ -188,6 +202,21 @@ static void loongson3_init_secondary(void)
188 202
189 for (i = 0; i < loongson_sysconf.nr_cpus; i++) 203 for (i = 0; i < loongson_sysconf.nr_cpus; i++)
190 loongson3_ipi_write32(0xffffffff, ipi_en0_regs[i]); 204 loongson3_ipi_write32(0xffffffff, ipi_en0_regs[i]);
205
206 per_cpu(cpu_state, cpu) = CPU_ONLINE;
207
208 i = 0;
209 __get_cpu_var(core0_c0count) = 0;
210 loongson3_send_ipi_single(0, SMP_ASK_C0COUNT);
211 while (!__get_cpu_var(core0_c0count)) {
212 i++;
213 cpu_relax();
214 }
215
216 if (i > MAX_LOOPS)
217 i = MAX_LOOPS;
218 initcount = __get_cpu_var(core0_c0count) + i;
219 write_c0_count(initcount);
191} 220}
192 221
193static void loongson3_smp_finish(void) 222static void loongson3_smp_finish(void)
@@ -222,6 +251,8 @@ static void __init loongson3_smp_setup(void)
222 251
223static void __init loongson3_prepare_cpus(unsigned int max_cpus) 252static void __init loongson3_prepare_cpus(unsigned int max_cpus)
224{ 253{
254 init_cpu_present(cpu_possible_mask);
255 per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
225} 256}
226 257
227/* 258/*
@@ -255,6 +286,147 @@ static void __init loongson3_cpus_done(void)
255{ 286{
256} 287}
257 288
289#ifdef CONFIG_HOTPLUG_CPU
290
291static int loongson3_cpu_disable(void)
292{
293 unsigned long flags;
294 unsigned int cpu = smp_processor_id();
295
296 if (cpu == 0)
297 return -EBUSY;
298
299 set_cpu_online(cpu, false);
300 cpu_clear(cpu, cpu_callin_map);
301 local_irq_save(flags);
302 fixup_irqs();
303 local_irq_restore(flags);
304 flush_cache_all();
305 local_flush_tlb_all();
306
307 return 0;
308}
309
310
311static void loongson3_cpu_die(unsigned int cpu)
312{
313 while (per_cpu(cpu_state, cpu) != CPU_DEAD)
314 cpu_relax();
315
316 mb();
317}
318
319/* To shutdown a core in Loongson 3, the target core should go to CKSEG1 and
320 * flush all L1 entries at first. Then, another core (usually Core 0) can
321 * safely disable the clock of the target core. loongson3_play_dead() is
322 * called via CKSEG1 (uncached and unmmaped) */
323static void loongson3_play_dead(int *state_addr)
324{
325 register int val;
326 register long cpuid, core, node, count;
327 register void *addr, *base, *initfunc;
328
329 __asm__ __volatile__(
330 " .set push \n"
331 " .set noreorder \n"
332 " li %[addr], 0x80000000 \n" /* KSEG0 */
333 "1: cache 0, 0(%[addr]) \n" /* flush L1 ICache */
334 " cache 0, 1(%[addr]) \n"
335 " cache 0, 2(%[addr]) \n"
336 " cache 0, 3(%[addr]) \n"
337 " cache 1, 0(%[addr]) \n" /* flush L1 DCache */
338 " cache 1, 1(%[addr]) \n"
339 " cache 1, 2(%[addr]) \n"
340 " cache 1, 3(%[addr]) \n"
341 " addiu %[sets], %[sets], -1 \n"
342 " bnez %[sets], 1b \n"
343 " addiu %[addr], %[addr], 0x20 \n"
344 " li %[val], 0x7 \n" /* *state_addr = CPU_DEAD; */
345 " sw %[val], (%[state_addr]) \n"
346 " sync \n"
347 " cache 21, (%[state_addr]) \n" /* flush entry of *state_addr */
348 " .set pop \n"
349 : [addr] "=&r" (addr), [val] "=&r" (val)
350 : [state_addr] "r" (state_addr),
351 [sets] "r" (cpu_data[smp_processor_id()].dcache.sets));
352
353 __asm__ __volatile__(
354 " .set push \n"
355 " .set noreorder \n"
356 " .set mips64 \n"
357 " mfc0 %[cpuid], $15, 1 \n"
358 " andi %[cpuid], 0x3ff \n"
359 " dli %[base], 0x900000003ff01000 \n"
360 " andi %[core], %[cpuid], 0x3 \n"
361 " sll %[core], 8 \n" /* get core id */
362 " or %[base], %[base], %[core] \n"
363 " andi %[node], %[cpuid], 0xc \n"
364 " dsll %[node], 42 \n" /* get node id */
365 " or %[base], %[base], %[node] \n"
366 "1: li %[count], 0x100 \n" /* wait for init loop */
367 "2: bnez %[count], 2b \n" /* limit mailbox access */
368 " addiu %[count], -1 \n"
369 " ld %[initfunc], 0x20(%[base]) \n" /* get PC via mailbox */
370 " beqz %[initfunc], 1b \n"
371 " nop \n"
372 " ld $sp, 0x28(%[base]) \n" /* get SP via mailbox */
373 " ld $gp, 0x30(%[base]) \n" /* get GP via mailbox */
374 " ld $a1, 0x38(%[base]) \n"
375 " jr %[initfunc] \n" /* jump to initial PC */
376 " nop \n"
377 " .set pop \n"
378 : [core] "=&r" (core), [node] "=&r" (node),
379 [base] "=&r" (base), [cpuid] "=&r" (cpuid),
380 [count] "=&r" (count), [initfunc] "=&r" (initfunc)
381 : /* No Input */
382 : "a1");
383}
384
385void play_dead(void)
386{
387 int *state_addr;
388 unsigned int cpu = smp_processor_id();
389 void (*play_dead_at_ckseg1)(int *);
390
391 idle_task_exit();
392 play_dead_at_ckseg1 =
393 (void *)CKSEG1ADDR((unsigned long)loongson3_play_dead);
394 state_addr = &per_cpu(cpu_state, cpu);
395 mb();
396 play_dead_at_ckseg1(state_addr);
397}
398
399#define CPU_POST_DEAD_FROZEN (CPU_POST_DEAD | CPU_TASKS_FROZEN)
400static int loongson3_cpu_callback(struct notifier_block *nfb,
401 unsigned long action, void *hcpu)
402{
403 unsigned int cpu = (unsigned long)hcpu;
404
405 switch (action) {
406 case CPU_POST_DEAD:
407 case CPU_POST_DEAD_FROZEN:
408 pr_info("Disable clock for CPU#%d\n", cpu);
409 LOONGSON_CHIPCFG0 &= ~(1 << (12 + cpu));
410 break;
411 case CPU_UP_PREPARE:
412 case CPU_UP_PREPARE_FROZEN:
413 pr_info("Enable clock for CPU#%d\n", cpu);
414 LOONGSON_CHIPCFG0 |= 1 << (12 + cpu);
415 break;
416 }
417
418 return NOTIFY_OK;
419}
420
421static int register_loongson3_notifier(void)
422{
423 hotcpu_notifier(loongson3_cpu_callback, 0);
424 return 0;
425}
426early_initcall(register_loongson3_notifier);
427
428#endif
429
258struct plat_smp_ops loongson3_smp_ops = { 430struct plat_smp_ops loongson3_smp_ops = {
259 .send_ipi_single = loongson3_send_ipi_single, 431 .send_ipi_single = loongson3_send_ipi_single,
260 .send_ipi_mask = loongson3_send_ipi_mask, 432 .send_ipi_mask = loongson3_send_ipi_mask,
@@ -264,4 +436,8 @@ struct plat_smp_ops loongson3_smp_ops = {
264 .boot_secondary = loongson3_boot_secondary, 436 .boot_secondary = loongson3_boot_secondary,
265 .smp_setup = loongson3_smp_setup, 437 .smp_setup = loongson3_smp_setup,
266 .prepare_cpus = loongson3_prepare_cpus, 438 .prepare_cpus = loongson3_prepare_cpus,
439#ifdef CONFIG_HOTPLUG_CPU
440 .cpu_disable = loongson3_cpu_disable,
441 .cpu_die = loongson3_cpu_die,
442#endif
267}; 443};