Blackfin: SMP: add PM/CPU hotplug support
Signed-off-by: Graf Yang <graf.yang@analog.com> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
This commit is contained in:
committed by
Mike Frysinger
parent
0d152c27e3
commit
0b39db28b9
@@ -250,6 +250,11 @@ config NR_CPUS
|
|||||||
depends on SMP
|
depends on SMP
|
||||||
default 2 if BF561
|
default 2 if BF561
|
||||||
|
|
||||||
|
config HOTPLUG_CPU
|
||||||
|
bool "Support for hot-pluggable CPUs"
|
||||||
|
depends on SMP && HOTPLUG
|
||||||
|
default y
|
||||||
|
|
||||||
config IRQ_PER_CPU
|
config IRQ_PER_CPU
|
||||||
bool
|
bool
|
||||||
depends on SMP
|
depends on SMP
|
||||||
@@ -1130,7 +1135,6 @@ source "fs/Kconfig.binfmt"
|
|||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
menu "Power management options"
|
menu "Power management options"
|
||||||
depends on !SMP
|
|
||||||
|
|
||||||
source "kernel/power/Kconfig"
|
source "kernel/power/Kconfig"
|
||||||
|
|
||||||
|
@@ -25,5 +25,12 @@ struct corelock_slot {
|
|||||||
|
|
||||||
void smp_icache_flush_range_others(unsigned long start,
|
void smp_icache_flush_range_others(unsigned long start,
|
||||||
unsigned long end);
|
unsigned long end);
|
||||||
|
#ifdef CONFIG_HOTPLUG_CPU
|
||||||
|
void coreb_sleep(u32 sic_iwr0, u32 sic_iwr1, u32 sic_iwr2);
|
||||||
|
void cpu_die(void);
|
||||||
|
void platform_cpu_die(void);
|
||||||
|
int __cpu_disable(void);
|
||||||
|
int __cpu_die(unsigned int cpu);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* !__ASM_BLACKFIN_SMP_H */
|
#endif /* !__ASM_BLACKFIN_SMP_H */
|
||||||
|
@@ -6,3 +6,4 @@ obj-y := ints-priority.o dma.o
|
|||||||
|
|
||||||
obj-$(CONFIG_BF561_COREB) += coreb.o
|
obj-$(CONFIG_BF561_COREB) += coreb.o
|
||||||
obj-$(CONFIG_SMP) += smp.o secondary.o atomic.o
|
obj-$(CONFIG_SMP) += smp.o secondary.o atomic.o
|
||||||
|
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
|
||||||
|
32
arch/blackfin/mach-bf561/hotplug.c
Normal file
32
arch/blackfin/mach-bf561/hotplug.c
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007-2009 Analog Devices Inc.
|
||||||
|
* Graff Yang <graf.yang@analog.com>
|
||||||
|
*
|
||||||
|
* Licensed under the GPL-2 or later.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asm/blackfin.h>
|
||||||
|
#include <asm/smp.h>
|
||||||
|
#define SIC_SYSIRQ(irq) (irq - (IRQ_CORETMR + 1))
|
||||||
|
|
||||||
|
int hotplug_coreb;
|
||||||
|
|
||||||
|
void platform_cpu_die(void)
|
||||||
|
{
|
||||||
|
unsigned long iwr[2] = {0, 0};
|
||||||
|
unsigned long bank = SIC_SYSIRQ(IRQ_SUPPLE_0) / 32;
|
||||||
|
unsigned long bit = 1 << (SIC_SYSIRQ(IRQ_SUPPLE_0) % 32);
|
||||||
|
|
||||||
|
hotplug_coreb = 1;
|
||||||
|
|
||||||
|
iwr[bank] = bit;
|
||||||
|
|
||||||
|
/* disable core timer */
|
||||||
|
bfin_write_TCNTL(0);
|
||||||
|
|
||||||
|
/* clear ipi interrupt IRQ_SUPPLE_0 */
|
||||||
|
bfin_write_SICB_SYSCR(bfin_read_SICB_SYSCR() | (1 << (10 + 1)));
|
||||||
|
SSYNC();
|
||||||
|
|
||||||
|
coreb_sleep(iwr[0], iwr[1], 0);
|
||||||
|
}
|
@@ -11,6 +11,7 @@
|
|||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <asm/blackfin.h>
|
#include <asm/blackfin.h>
|
||||||
#include <asm/asm-offsets.h>
|
#include <asm/asm-offsets.h>
|
||||||
|
#include <asm/trace.h>
|
||||||
|
|
||||||
__INIT
|
__INIT
|
||||||
|
|
||||||
@@ -62,6 +63,8 @@ ENTRY(_coreb_trampoline_start)
|
|||||||
M2 = r0;
|
M2 = r0;
|
||||||
M3 = r0;
|
M3 = r0;
|
||||||
|
|
||||||
|
trace_buffer_init(p0,r0);
|
||||||
|
|
||||||
/* Turn off the icache */
|
/* Turn off the icache */
|
||||||
p0.l = LO(IMEM_CONTROL);
|
p0.l = LO(IMEM_CONTROL);
|
||||||
p0.h = HI(IMEM_CONTROL);
|
p0.h = HI(IMEM_CONTROL);
|
||||||
@@ -159,6 +162,41 @@ ENTRY(_coreb_trampoline_start)
|
|||||||
ENDPROC(_coreb_trampoline_start)
|
ENDPROC(_coreb_trampoline_start)
|
||||||
ENTRY(_coreb_trampoline_end)
|
ENTRY(_coreb_trampoline_end)
|
||||||
|
|
||||||
|
.section ".text"
|
||||||
|
ENTRY(_set_sicb_iwr)
|
||||||
|
P0.H = hi(SICB_IWR0);
|
||||||
|
P0.L = lo(SICB_IWR0);
|
||||||
|
P1.H = hi(SICB_IWR1);
|
||||||
|
P1.L = lo(SICB_IWR1);
|
||||||
|
[P0] = R0;
|
||||||
|
[P1] = R1;
|
||||||
|
SSYNC;
|
||||||
|
RTS;
|
||||||
|
ENDPROC(_set_sicb_iwr)
|
||||||
|
|
||||||
|
ENTRY(_coreb_sleep)
|
||||||
|
sp.l = lo(INITIAL_STACK);
|
||||||
|
sp.h = hi(INITIAL_STACK);
|
||||||
|
fp = sp;
|
||||||
|
usp = sp;
|
||||||
|
|
||||||
|
call _set_sicb_iwr;
|
||||||
|
|
||||||
|
CLI R2;
|
||||||
|
SSYNC;
|
||||||
|
IDLE;
|
||||||
|
STI R2;
|
||||||
|
|
||||||
|
R0 = IWR_DISABLE_ALL;
|
||||||
|
R1 = IWR_DISABLE_ALL;
|
||||||
|
call _set_sicb_iwr;
|
||||||
|
|
||||||
|
p0.h = hi(COREB_L1_CODE_START);
|
||||||
|
p0.l = lo(COREB_L1_CODE_START);
|
||||||
|
jump (p0);
|
||||||
|
ENDPROC(_coreb_sleep)
|
||||||
|
|
||||||
|
__CPUINIT
|
||||||
ENTRY(_coreb_start)
|
ENTRY(_coreb_start)
|
||||||
[--sp] = reti;
|
[--sp] = reti;
|
||||||
|
|
||||||
@@ -176,12 +214,20 @@ ENTRY(_coreb_start)
|
|||||||
sp = [p0];
|
sp = [p0];
|
||||||
usp = sp;
|
usp = sp;
|
||||||
fp = sp;
|
fp = sp;
|
||||||
|
#ifdef CONFIG_HOTPLUG_CPU
|
||||||
|
p0.l = _hotplug_coreb;
|
||||||
|
p0.h = _hotplug_coreb;
|
||||||
|
r0 = [p0];
|
||||||
|
cc = BITTST(r0, 0);
|
||||||
|
if cc jump 3f;
|
||||||
|
#endif
|
||||||
sp += -12;
|
sp += -12;
|
||||||
call _init_pda
|
call _init_pda
|
||||||
sp += 12;
|
sp += 12;
|
||||||
|
#ifdef CONFIG_HOTPLUG_CPU
|
||||||
|
3:
|
||||||
|
#endif
|
||||||
call _secondary_start_kernel;
|
call _secondary_start_kernel;
|
||||||
.L_exit:
|
.L_exit:
|
||||||
jump.s .L_exit;
|
jump.s .L_exit;
|
||||||
ENDPROC(_coreb_start)
|
ENDPROC(_coreb_start)
|
||||||
|
|
||||||
__FINIT
|
|
||||||
|
@@ -65,6 +65,8 @@ void __cpuinit platform_secondary_init(unsigned int cpu)
|
|||||||
bfin_write_SICB_IAR5(bfin_read_SICA_IAR5());
|
bfin_write_SICB_IAR5(bfin_read_SICA_IAR5());
|
||||||
bfin_write_SICB_IAR6(bfin_read_SICA_IAR6());
|
bfin_write_SICB_IAR6(bfin_read_SICA_IAR6());
|
||||||
bfin_write_SICB_IAR7(bfin_read_SICA_IAR7());
|
bfin_write_SICB_IAR7(bfin_read_SICA_IAR7());
|
||||||
|
bfin_write_SICB_IWR0(IWR_DISABLE_ALL);
|
||||||
|
bfin_write_SICB_IWR1(IWR_DISABLE_ALL);
|
||||||
SSYNC();
|
SSYNC();
|
||||||
|
|
||||||
/* Store CPU-private information to the cpu_data array. */
|
/* 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
|
|||||||
{
|
{
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
|
|
||||||
/* CoreB already running?! */
|
|
||||||
BUG_ON((bfin_read_SICA_SYSCR() & COREB_SRAM_INIT) == 0);
|
|
||||||
|
|
||||||
printk(KERN_INFO "Booting Core B.\n");
|
printk(KERN_INFO "Booting Core B.\n");
|
||||||
|
|
||||||
spin_lock(&boot_lock);
|
spin_lock(&boot_lock);
|
||||||
|
|
||||||
/* Kick CoreB, which should start execution from CORE_SRAM_BASE. */
|
if ((bfin_read_SICA_SYSCR() & COREB_SRAM_INIT) == 0) {
|
||||||
SSYNC();
|
/* CoreB already running, sending ipi to wakeup it */
|
||||||
bfin_write_SICA_SYSCR(bfin_read_SICA_SYSCR() & ~COREB_SRAM_INIT);
|
platform_send_ipi_cpu(cpu, IRQ_SUPPLE_0);
|
||||||
SSYNC();
|
} else {
|
||||||
|
/* Kick CoreB, which should start execution from CORE_SRAM_BASE. */
|
||||||
|
bfin_write_SICA_SYSCR(bfin_read_SICA_SYSCR() & ~COREB_SRAM_INIT);
|
||||||
|
SSYNC();
|
||||||
|
}
|
||||||
|
|
||||||
timeout = jiffies + 1 * HZ;
|
timeout = jiffies + 1 * HZ;
|
||||||
while (time_before(jiffies, timeout)) {
|
while (time_before(jiffies, timeout)) {
|
||||||
|
@@ -344,8 +344,11 @@ void smp_send_stop(void)
|
|||||||
|
|
||||||
int __cpuinit __cpu_up(unsigned int cpu)
|
int __cpuinit __cpu_up(unsigned int cpu)
|
||||||
{
|
{
|
||||||
struct task_struct *idle;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
static struct task_struct *idle;
|
||||||
|
|
||||||
|
if (idle)
|
||||||
|
free_task(idle);
|
||||||
|
|
||||||
idle = fork_idle(cpu);
|
idle = fork_idle(cpu);
|
||||||
if (IS_ERR(idle)) {
|
if (IS_ERR(idle)) {
|
||||||
@@ -354,7 +357,6 @@ int __cpuinit __cpu_up(unsigned int cpu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
secondary_stack = task_stack_page(idle) + THREAD_SIZE;
|
secondary_stack = task_stack_page(idle) + THREAD_SIZE;
|
||||||
smp_wmb();
|
|
||||||
|
|
||||||
ret = platform_boot_secondary(cpu, idle);
|
ret = platform_boot_secondary(cpu, idle);
|
||||||
|
|
||||||
@@ -413,7 +415,6 @@ void __cpuinit secondary_start_kernel(void)
|
|||||||
atomic_inc(&mm->mm_users);
|
atomic_inc(&mm->mm_users);
|
||||||
atomic_inc(&mm->mm_count);
|
atomic_inc(&mm->mm_count);
|
||||||
current->active_mm = mm;
|
current->active_mm = mm;
|
||||||
BUG_ON(current->mm); /* Can't be, but better be safe than sorry. */
|
|
||||||
|
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
|
|
||||||
@@ -495,3 +496,34 @@ void resync_core_dcache(void)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(resync_core_dcache);
|
EXPORT_SYMBOL(resync_core_dcache);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_HOTPLUG_CPU
|
||||||
|
int __cpuexit __cpu_disable(void)
|
||||||
|
{
|
||||||
|
unsigned int cpu = smp_processor_id();
|
||||||
|
|
||||||
|
if (cpu == 0)
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
|
set_cpu_online(cpu, false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DECLARE_COMPLETION(cpu_killed);
|
||||||
|
|
||||||
|
int __cpuexit __cpu_die(unsigned int cpu)
|
||||||
|
{
|
||||||
|
return wait_for_completion_timeout(&cpu_killed, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_die(void)
|
||||||
|
{
|
||||||
|
complete(&cpu_killed);
|
||||||
|
|
||||||
|
atomic_dec(&init_mm.mm_users);
|
||||||
|
atomic_dec(&init_mm.mm_count);
|
||||||
|
|
||||||
|
local_irq_disable();
|
||||||
|
platform_cpu_die();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Reference in New Issue
Block a user