aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips
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
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')
-rw-r--r--arch/mips/include/asm/mach-loongson/irq.h1
-rw-r--r--arch/mips/include/asm/mach-loongson/loongson.h6
-rw-r--r--arch/mips/include/asm/smp.h1
-rw-r--r--arch/mips/loongson/Kconfig1
-rw-r--r--arch/mips/loongson/loongson-3/irq.c10
-rw-r--r--arch/mips/loongson/loongson-3/smp.c180
6 files changed, 194 insertions, 5 deletions
diff --git a/arch/mips/include/asm/mach-loongson/irq.h b/arch/mips/include/asm/mach-loongson/irq.h
index 0c77b22a6630..34560bda6626 100644
--- a/arch/mips/include/asm/mach-loongson/irq.h
+++ b/arch/mips/include/asm/mach-loongson/irq.h
@@ -37,6 +37,7 @@
37 37
38#endif 38#endif
39 39
40extern void fixup_irqs(void);
40extern void loongson3_ipi_interrupt(struct pt_regs *regs); 41extern void loongson3_ipi_interrupt(struct pt_regs *regs);
41 42
42#include_next <irq.h> 43#include_next <irq.h>
diff --git a/arch/mips/include/asm/mach-loongson/loongson.h b/arch/mips/include/asm/mach-loongson/loongson.h
index f185907e8ce3..f3fd1eb8e3dd 100644
--- a/arch/mips/include/asm/mach-loongson/loongson.h
+++ b/arch/mips/include/asm/mach-loongson/loongson.h
@@ -249,6 +249,9 @@ static inline void do_perfcnt_IRQ(void)
249#define LOONGSON_PXARB_CFG LOONGSON_REG(LOONGSON_REGBASE + 0x68) 249#define LOONGSON_PXARB_CFG LOONGSON_REG(LOONGSON_REGBASE + 0x68)
250#define LOONGSON_PXARB_STATUS LOONGSON_REG(LOONGSON_REGBASE + 0x6c) 250#define LOONGSON_PXARB_STATUS LOONGSON_REG(LOONGSON_REGBASE + 0x6c)
251 251
252/* Chip Config */
253#define LOONGSON_CHIPCFG0 LOONGSON_REG(LOONGSON_REGBASE + 0x80)
254
252/* pcimap */ 255/* pcimap */
253 256
254#define LOONGSON_PCIMAP_PCIMAP_LO0 0x0000003f 257#define LOONGSON_PCIMAP_PCIMAP_LO0 0x0000003f
@@ -264,9 +267,6 @@ static inline void do_perfcnt_IRQ(void)
264#ifdef CONFIG_CPU_SUPPORTS_CPUFREQ 267#ifdef CONFIG_CPU_SUPPORTS_CPUFREQ
265#include <linux/cpufreq.h> 268#include <linux/cpufreq.h>
266extern struct cpufreq_frequency_table loongson2_clockmod_table[]; 269extern struct cpufreq_frequency_table loongson2_clockmod_table[];
267
268/* Chip Config */
269#define LOONGSON_CHIPCFG0 LOONGSON_REG(LOONGSON_REGBASE + 0x80)
270#endif 270#endif
271 271
272/* 272/*
diff --git a/arch/mips/include/asm/smp.h b/arch/mips/include/asm/smp.h
index eb6008758484..efa02acd3dd5 100644
--- a/arch/mips/include/asm/smp.h
+++ b/arch/mips/include/asm/smp.h
@@ -42,6 +42,7 @@ extern int __cpu_logical_map[NR_CPUS];
42#define SMP_ICACHE_FLUSH 0x4 42#define SMP_ICACHE_FLUSH 0x4
43/* Used by kexec crashdump to save all cpu's state */ 43/* Used by kexec crashdump to save all cpu's state */
44#define SMP_DUMP 0x8 44#define SMP_DUMP 0x8
45#define SMP_ASK_C0COUNT 0x10
45 46
46extern volatile cpumask_t cpu_callin_map; 47extern volatile cpumask_t cpu_callin_map;
47 48
diff --git a/arch/mips/loongson/Kconfig b/arch/mips/loongson/Kconfig
index a5d46f5efe03..7397be226a06 100644
--- a/arch/mips/loongson/Kconfig
+++ b/arch/mips/loongson/Kconfig
@@ -79,6 +79,7 @@ config LEMOTE_MACH3A
79 select SYS_HAS_CPU_LOONGSON3 79 select SYS_HAS_CPU_LOONGSON3
80 select SYS_HAS_EARLY_PRINTK 80 select SYS_HAS_EARLY_PRINTK
81 select SYS_SUPPORTS_SMP 81 select SYS_SUPPORTS_SMP
82 select SYS_SUPPORTS_HOTPLUG_CPU
82 select SYS_SUPPORTS_64BIT_KERNEL 83 select SYS_SUPPORTS_64BIT_KERNEL
83 select SYS_SUPPORTS_HIGHMEM 84 select SYS_SUPPORTS_HIGHMEM
84 select SYS_SUPPORTS_LITTLE_ENDIAN 85 select SYS_SUPPORTS_LITTLE_ENDIAN
diff --git a/arch/mips/loongson/loongson-3/irq.c b/arch/mips/loongson/loongson-3/irq.c
index 088fd5e4fd64..f240828181ff 100644
--- a/arch/mips/loongson/loongson-3/irq.c
+++ b/arch/mips/loongson/loongson-3/irq.c
@@ -114,3 +114,13 @@ void __init mach_init_irq(void)
114 114
115 set_c0_status(STATUSF_IP2 | STATUSF_IP6); 115 set_c0_status(STATUSF_IP2 | STATUSF_IP6);
116} 116}
117
118#ifdef CONFIG_HOTPLUG_CPU
119
120void fixup_irqs(void)
121{
122 irq_cpu_offline();
123 clear_c0_status(ST0_IM);
124}
125
126#endif
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};