diff options
author | Graf Yang <graf.yang@analog.com> | 2009-12-28 06:13:51 -0500 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2010-03-09 00:30:48 -0500 |
commit | 0b39db28b953945232719e7ff6fb802aa8a2be5f (patch) | |
tree | c35193b07e9413ed6b5436aa79e24b0f22627082 | |
parent | 0d152c27e336b5fd777da7dd3e814617e7305afd (diff) |
Blackfin: SMP: add PM/CPU hotplug support
Signed-off-by: Graf Yang <graf.yang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
-rw-r--r-- | arch/blackfin/Kconfig | 6 | ||||
-rw-r--r-- | arch/blackfin/include/asm/smp.h | 7 | ||||
-rw-r--r-- | arch/blackfin/mach-bf561/Makefile | 1 | ||||
-rw-r--r-- | arch/blackfin/mach-bf561/hotplug.c | 32 | ||||
-rw-r--r-- | arch/blackfin/mach-bf561/secondary.S | 50 | ||||
-rw-r--r-- | arch/blackfin/mach-bf561/smp.c | 17 | ||||
-rw-r--r-- | arch/blackfin/mach-common/smp.c | 38 |
7 files changed, 138 insertions, 13 deletions
diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index 0bd26dbca09f..c0d6d966adec 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig | |||
@@ -250,6 +250,11 @@ config NR_CPUS | |||
250 | depends on SMP | 250 | depends on SMP |
251 | default 2 if BF561 | 251 | default 2 if BF561 |
252 | 252 | ||
253 | config HOTPLUG_CPU | ||
254 | bool "Support for hot-pluggable CPUs" | ||
255 | depends on SMP && HOTPLUG | ||
256 | default y | ||
257 | |||
253 | config IRQ_PER_CPU | 258 | config IRQ_PER_CPU |
254 | bool | 259 | bool |
255 | depends on SMP | 260 | depends on SMP |
@@ -1130,7 +1135,6 @@ source "fs/Kconfig.binfmt" | |||
1130 | endmenu | 1135 | endmenu |
1131 | 1136 | ||
1132 | menu "Power management options" | 1137 | menu "Power management options" |
1133 | depends on !SMP | ||
1134 | 1138 | ||
1135 | source "kernel/power/Kconfig" | 1139 | source "kernel/power/Kconfig" |
1136 | 1140 | ||
diff --git a/arch/blackfin/include/asm/smp.h b/arch/blackfin/include/asm/smp.h index 6a0fe94b84a6..29fb88219470 100644 --- a/arch/blackfin/include/asm/smp.h +++ b/arch/blackfin/include/asm/smp.h | |||
@@ -25,5 +25,12 @@ struct corelock_slot { | |||
25 | 25 | ||
26 | void smp_icache_flush_range_others(unsigned long start, | 26 | void smp_icache_flush_range_others(unsigned long start, |
27 | unsigned long end); | 27 | unsigned long end); |
28 | #ifdef CONFIG_HOTPLUG_CPU | ||
29 | void coreb_sleep(u32 sic_iwr0, u32 sic_iwr1, u32 sic_iwr2); | ||
30 | void cpu_die(void); | ||
31 | void platform_cpu_die(void); | ||
32 | int __cpu_disable(void); | ||
33 | int __cpu_die(unsigned int cpu); | ||
34 | #endif | ||
28 | 35 | ||
29 | #endif /* !__ASM_BLACKFIN_SMP_H */ | 36 | #endif /* !__ASM_BLACKFIN_SMP_H */ |
diff --git a/arch/blackfin/mach-bf561/Makefile b/arch/blackfin/mach-bf561/Makefile index 59e18afe28c6..b34029718318 100644 --- a/arch/blackfin/mach-bf561/Makefile +++ b/arch/blackfin/mach-bf561/Makefile | |||
@@ -6,3 +6,4 @@ obj-y := ints-priority.o dma.o | |||
6 | 6 | ||
7 | obj-$(CONFIG_BF561_COREB) += coreb.o | 7 | obj-$(CONFIG_BF561_COREB) += coreb.o |
8 | obj-$(CONFIG_SMP) += smp.o secondary.o atomic.o | 8 | obj-$(CONFIG_SMP) += smp.o secondary.o atomic.o |
9 | obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o | ||
diff --git a/arch/blackfin/mach-bf561/hotplug.c b/arch/blackfin/mach-bf561/hotplug.c new file mode 100644 index 000000000000..c95169b612dc --- /dev/null +++ b/arch/blackfin/mach-bf561/hotplug.c | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * Copyright 2007-2009 Analog Devices Inc. | ||
3 | * Graff Yang <graf.yang@analog.com> | ||
4 | * | ||
5 | * Licensed under the GPL-2 or later. | ||
6 | */ | ||
7 | |||
8 | #include <asm/blackfin.h> | ||
9 | #include <asm/smp.h> | ||
10 | #define SIC_SYSIRQ(irq) (irq - (IRQ_CORETMR + 1)) | ||
11 | |||
12 | int hotplug_coreb; | ||
13 | |||
14 | void platform_cpu_die(void) | ||
15 | { | ||
16 | unsigned long iwr[2] = {0, 0}; | ||
17 | unsigned long bank = SIC_SYSIRQ(IRQ_SUPPLE_0) / 32; | ||
18 | unsigned long bit = 1 << (SIC_SYSIRQ(IRQ_SUPPLE_0) % 32); | ||
19 | |||
20 | hotplug_coreb = 1; | ||
21 | |||
22 | iwr[bank] = bit; | ||
23 | |||
24 | /* disable core timer */ | ||
25 | bfin_write_TCNTL(0); | ||
26 | |||
27 | /* clear ipi interrupt IRQ_SUPPLE_0 */ | ||
28 | bfin_write_SICB_SYSCR(bfin_read_SICB_SYSCR() | (1 << (10 + 1))); | ||
29 | SSYNC(); | ||
30 | |||
31 | coreb_sleep(iwr[0], iwr[1], 0); | ||
32 | } | ||
diff --git a/arch/blackfin/mach-bf561/secondary.S b/arch/blackfin/mach-bf561/secondary.S index 8e6050369c06..4624eebbf9c4 100644 --- a/arch/blackfin/mach-bf561/secondary.S +++ b/arch/blackfin/mach-bf561/secondary.S | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/init.h> | 11 | #include <linux/init.h> |
12 | #include <asm/blackfin.h> | 12 | #include <asm/blackfin.h> |
13 | #include <asm/asm-offsets.h> | 13 | #include <asm/asm-offsets.h> |
14 | #include <asm/trace.h> | ||
14 | 15 | ||
15 | __INIT | 16 | __INIT |
16 | 17 | ||
@@ -62,6 +63,8 @@ ENTRY(_coreb_trampoline_start) | |||
62 | M2 = r0; | 63 | M2 = r0; |
63 | M3 = r0; | 64 | M3 = r0; |
64 | 65 | ||
66 | trace_buffer_init(p0,r0); | ||
67 | |||
65 | /* Turn off the icache */ | 68 | /* Turn off the icache */ |
66 | p0.l = LO(IMEM_CONTROL); | 69 | p0.l = LO(IMEM_CONTROL); |
67 | p0.h = HI(IMEM_CONTROL); | 70 | p0.h = HI(IMEM_CONTROL); |
@@ -159,6 +162,41 @@ ENTRY(_coreb_trampoline_start) | |||
159 | ENDPROC(_coreb_trampoline_start) | 162 | ENDPROC(_coreb_trampoline_start) |
160 | ENTRY(_coreb_trampoline_end) | 163 | ENTRY(_coreb_trampoline_end) |
161 | 164 | ||
165 | .section ".text" | ||
166 | ENTRY(_set_sicb_iwr) | ||
167 | P0.H = hi(SICB_IWR0); | ||
168 | P0.L = lo(SICB_IWR0); | ||
169 | P1.H = hi(SICB_IWR1); | ||
170 | P1.L = lo(SICB_IWR1); | ||
171 | [P0] = R0; | ||
172 | [P1] = R1; | ||
173 | SSYNC; | ||
174 | RTS; | ||
175 | ENDPROC(_set_sicb_iwr) | ||
176 | |||
177 | ENTRY(_coreb_sleep) | ||
178 | sp.l = lo(INITIAL_STACK); | ||
179 | sp.h = hi(INITIAL_STACK); | ||
180 | fp = sp; | ||
181 | usp = sp; | ||
182 | |||
183 | call _set_sicb_iwr; | ||
184 | |||
185 | CLI R2; | ||
186 | SSYNC; | ||
187 | IDLE; | ||
188 | STI R2; | ||
189 | |||
190 | R0 = IWR_DISABLE_ALL; | ||
191 | R1 = IWR_DISABLE_ALL; | ||
192 | call _set_sicb_iwr; | ||
193 | |||
194 | p0.h = hi(COREB_L1_CODE_START); | ||
195 | p0.l = lo(COREB_L1_CODE_START); | ||
196 | jump (p0); | ||
197 | ENDPROC(_coreb_sleep) | ||
198 | |||
199 | __CPUINIT | ||
162 | ENTRY(_coreb_start) | 200 | ENTRY(_coreb_start) |
163 | [--sp] = reti; | 201 | [--sp] = reti; |
164 | 202 | ||
@@ -176,12 +214,20 @@ ENTRY(_coreb_start) | |||
176 | sp = [p0]; | 214 | sp = [p0]; |
177 | usp = sp; | 215 | usp = sp; |
178 | fp = sp; | 216 | fp = sp; |
217 | #ifdef CONFIG_HOTPLUG_CPU | ||
218 | p0.l = _hotplug_coreb; | ||
219 | p0.h = _hotplug_coreb; | ||
220 | r0 = [p0]; | ||
221 | cc = BITTST(r0, 0); | ||
222 | if cc jump 3f; | ||
223 | #endif | ||
179 | sp += -12; | 224 | sp += -12; |
180 | call _init_pda | 225 | call _init_pda |
181 | sp += 12; | 226 | sp += 12; |
227 | #ifdef CONFIG_HOTPLUG_CPU | ||
228 | 3: | ||
229 | #endif | ||
182 | call _secondary_start_kernel; | 230 | call _secondary_start_kernel; |
183 | .L_exit: | 231 | .L_exit: |
184 | jump.s .L_exit; | 232 | jump.s .L_exit; |
185 | ENDPROC(_coreb_start) | 233 | ENDPROC(_coreb_start) |
186 | |||
187 | __FINIT | ||
diff --git a/arch/blackfin/mach-bf561/smp.c b/arch/blackfin/mach-bf561/smp.c index 90369429ee66..3b9a4bf7dacc 100644 --- a/arch/blackfin/mach-bf561/smp.c +++ b/arch/blackfin/mach-bf561/smp.c | |||
@@ -65,6 +65,8 @@ void __cpuinit platform_secondary_init(unsigned int cpu) | |||
65 | bfin_write_SICB_IAR5(bfin_read_SICA_IAR5()); | 65 | bfin_write_SICB_IAR5(bfin_read_SICA_IAR5()); |
66 | bfin_write_SICB_IAR6(bfin_read_SICA_IAR6()); | 66 | bfin_write_SICB_IAR6(bfin_read_SICA_IAR6()); |
67 | bfin_write_SICB_IAR7(bfin_read_SICA_IAR7()); | 67 | bfin_write_SICB_IAR7(bfin_read_SICA_IAR7()); |
68 | bfin_write_SICB_IWR0(IWR_DISABLE_ALL); | ||
69 | bfin_write_SICB_IWR1(IWR_DISABLE_ALL); | ||
68 | SSYNC(); | 70 | SSYNC(); |
69 | 71 | ||
70 | /* Store CPU-private information to the cpu_data array. */ | 72 | /* Store CPU-private information to the cpu_data array. */ |
@@ -80,17 +82,18 @@ int __cpuinit platform_boot_secondary(unsigned int cpu, struct task_struct *idle | |||
80 | { | 82 | { |
81 | unsigned long timeout; | 83 | unsigned long timeout; |
82 | 84 | ||
83 | /* CoreB already running?! */ | ||
84 | BUG_ON((bfin_read_SICA_SYSCR() & COREB_SRAM_INIT) == 0); | ||
85 | |||
86 | printk(KERN_INFO "Booting Core B.\n"); | 85 | printk(KERN_INFO "Booting Core B.\n"); |
87 | 86 | ||
88 | spin_lock(&boot_lock); | 87 | spin_lock(&boot_lock); |
89 | 88 | ||
90 | /* Kick CoreB, which should start execution from CORE_SRAM_BASE. */ | 89 | if ((bfin_read_SICA_SYSCR() & COREB_SRAM_INIT) == 0) { |
91 | SSYNC(); | 90 | /* CoreB already running, sending ipi to wakeup it */ |
92 | bfin_write_SICA_SYSCR(bfin_read_SICA_SYSCR() & ~COREB_SRAM_INIT); | 91 | platform_send_ipi_cpu(cpu, IRQ_SUPPLE_0); |
93 | SSYNC(); | 92 | } else { |
93 | /* Kick CoreB, which should start execution from CORE_SRAM_BASE. */ | ||
94 | bfin_write_SICA_SYSCR(bfin_read_SICA_SYSCR() & ~COREB_SRAM_INIT); | ||
95 | SSYNC(); | ||
96 | } | ||
94 | 97 | ||
95 | timeout = jiffies + 1 * HZ; | 98 | timeout = jiffies + 1 * HZ; |
96 | while (time_before(jiffies, timeout)) { | 99 | while (time_before(jiffies, timeout)) { |
diff --git a/arch/blackfin/mach-common/smp.c b/arch/blackfin/mach-common/smp.c index b343ab3764a1..efc47ffd066d 100644 --- a/arch/blackfin/mach-common/smp.c +++ b/arch/blackfin/mach-common/smp.c | |||
@@ -344,8 +344,11 @@ void smp_send_stop(void) | |||
344 | 344 | ||
345 | int __cpuinit __cpu_up(unsigned int cpu) | 345 | int __cpuinit __cpu_up(unsigned int cpu) |
346 | { | 346 | { |
347 | struct task_struct *idle; | ||
348 | int ret; | 347 | int ret; |
348 | static struct task_struct *idle; | ||
349 | |||
350 | if (idle) | ||
351 | free_task(idle); | ||
349 | 352 | ||
350 | idle = fork_idle(cpu); | 353 | idle = fork_idle(cpu); |
351 | if (IS_ERR(idle)) { | 354 | if (IS_ERR(idle)) { |
@@ -354,7 +357,6 @@ int __cpuinit __cpu_up(unsigned int cpu) | |||
354 | } | 357 | } |
355 | 358 | ||
356 | secondary_stack = task_stack_page(idle) + THREAD_SIZE; | 359 | secondary_stack = task_stack_page(idle) + THREAD_SIZE; |
357 | smp_wmb(); | ||
358 | 360 | ||
359 | ret = platform_boot_secondary(cpu, idle); | 361 | ret = platform_boot_secondary(cpu, idle); |
360 | 362 | ||
@@ -413,7 +415,6 @@ void __cpuinit secondary_start_kernel(void) | |||
413 | atomic_inc(&mm->mm_users); | 415 | atomic_inc(&mm->mm_users); |
414 | atomic_inc(&mm->mm_count); | 416 | atomic_inc(&mm->mm_count); |
415 | current->active_mm = mm; | 417 | current->active_mm = mm; |
416 | BUG_ON(current->mm); /* Can't be, but better be safe than sorry. */ | ||
417 | 418 | ||
418 | preempt_disable(); | 419 | preempt_disable(); |
419 | 420 | ||
@@ -495,3 +496,34 @@ void resync_core_dcache(void) | |||
495 | } | 496 | } |
496 | EXPORT_SYMBOL(resync_core_dcache); | 497 | EXPORT_SYMBOL(resync_core_dcache); |
497 | #endif | 498 | #endif |
499 | |||
500 | #ifdef CONFIG_HOTPLUG_CPU | ||
501 | int __cpuexit __cpu_disable(void) | ||
502 | { | ||
503 | unsigned int cpu = smp_processor_id(); | ||
504 | |||
505 | if (cpu == 0) | ||
506 | return -EPERM; | ||
507 | |||
508 | set_cpu_online(cpu, false); | ||
509 | return 0; | ||
510 | } | ||
511 | |||
512 | static DECLARE_COMPLETION(cpu_killed); | ||
513 | |||
514 | int __cpuexit __cpu_die(unsigned int cpu) | ||
515 | { | ||
516 | return wait_for_completion_timeout(&cpu_killed, 5000); | ||
517 | } | ||
518 | |||
519 | void cpu_die(void) | ||
520 | { | ||
521 | complete(&cpu_killed); | ||
522 | |||
523 | atomic_dec(&init_mm.mm_users); | ||
524 | atomic_dec(&init_mm.mm_count); | ||
525 | |||
526 | local_irq_disable(); | ||
527 | platform_cpu_die(); | ||
528 | } | ||
529 | #endif | ||