Blackfin: SMP: add PM/CPU hotplug support
authorGraf Yang <graf.yang@analog.com>
Mon, 28 Dec 2009 11:13:51 +0000 (11:13 +0000)
committerMike Frysinger <vapier@gentoo.org>
Tue, 9 Mar 2010 05:30:48 +0000 (00:30 -0500)
Signed-off-by: Graf Yang <graf.yang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
arch/blackfin/Kconfig
arch/blackfin/include/asm/smp.h
arch/blackfin/mach-bf561/Makefile
arch/blackfin/mach-bf561/hotplug.c [new file with mode: 0644]
arch/blackfin/mach-bf561/secondary.S
arch/blackfin/mach-bf561/smp.c
arch/blackfin/mach-common/smp.c

index 0bd26dbca09fa0d86e4b4bde94b410f3ebacfcaf..c0d6d966adece8cb19ab071cd7a254b18297334c 100644 (file)
@@ -250,6 +250,11 @@ config NR_CPUS
        depends on SMP
        default 2 if BF561
 
+config HOTPLUG_CPU
+       bool "Support for hot-pluggable CPUs"
+       depends on SMP && HOTPLUG
+       default y
+
 config IRQ_PER_CPU
        bool
        depends on SMP
@@ -1130,7 +1135,6 @@ source "fs/Kconfig.binfmt"
 endmenu
 
 menu "Power management options"
-       depends on !SMP
 
 source "kernel/power/Kconfig"
 
index 6a0fe94b84a62a5aaa7ee22ec810812c3b8f170d..29fb88219470e3946bf3bd69b6c611457020e216 100644 (file)
@@ -25,5 +25,12 @@ struct corelock_slot {
 
 void smp_icache_flush_range_others(unsigned long start,
                                   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 */
index 59e18afe28c616b4d448b0e9ac441b79542138c5..b3402971831841af413cbcdd0fd5a9405e4986b2 100644 (file)
@@ -6,3 +6,4 @@ obj-y := ints-priority.o dma.o
 
 obj-$(CONFIG_BF561_COREB) += coreb.o
 obj-$(CONFIG_SMP)  += smp.o secondary.o atomic.o
+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 (file)
index 0000000..c95169b
--- /dev/null
@@ -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);
+}
index 8e6050369c060b6d83e960492975e98ec0ca2be4..4624eebbf9c4c92b5e95220282b3f676dd6dbf76 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/init.h>
 #include <asm/blackfin.h>
 #include <asm/asm-offsets.h>
+#include <asm/trace.h>
 
 __INIT
 
@@ -62,6 +63,8 @@ ENTRY(_coreb_trampoline_start)
        M2 = r0;
        M3 = r0;
 
+       trace_buffer_init(p0,r0);
+
        /* Turn off the icache */
        p0.l = LO(IMEM_CONTROL);
        p0.h = HI(IMEM_CONTROL);
@@ -159,6 +162,41 @@ ENTRY(_coreb_trampoline_start)
 ENDPROC(_coreb_trampoline_start)
 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)
        [--sp] = reti;
 
@@ -176,12 +214,20 @@ ENTRY(_coreb_start)
        sp = [p0];
        usp = 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;
        call _init_pda
        sp += 12;
+#ifdef CONFIG_HOTPLUG_CPU
+3:
+#endif
        call _secondary_start_kernel;
 .L_exit:
        jump.s  .L_exit;
 ENDPROC(_coreb_start)
-
-__FINIT
index 90369429ee664d05c3a18bbffe48b689e194ae4f..3b9a4bf7daccf96b1314ae45e53deff5b09ed76a 100644 (file)
@@ -65,6 +65,8 @@ void __cpuinit platform_secondary_init(unsigned int cpu)
        bfin_write_SICB_IAR5(bfin_read_SICA_IAR5());
        bfin_write_SICB_IAR6(bfin_read_SICA_IAR6());
        bfin_write_SICB_IAR7(bfin_read_SICA_IAR7());
+       bfin_write_SICB_IWR0(IWR_DISABLE_ALL);
+       bfin_write_SICB_IWR1(IWR_DISABLE_ALL);
        SSYNC();
 
        /* 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;
 
-       /* CoreB already running?! */
-       BUG_ON((bfin_read_SICA_SYSCR() & COREB_SRAM_INIT) == 0);
-
        printk(KERN_INFO "Booting Core B.\n");
 
        spin_lock(&boot_lock);
 
-       /* Kick CoreB, which should start execution from CORE_SRAM_BASE. */
-       SSYNC();
-       bfin_write_SICA_SYSCR(bfin_read_SICA_SYSCR() & ~COREB_SRAM_INIT);
-       SSYNC();
+       if ((bfin_read_SICA_SYSCR() & COREB_SRAM_INIT) == 0) {
+               /* CoreB already running, sending ipi to wakeup it */
+               platform_send_ipi_cpu(cpu, IRQ_SUPPLE_0);
+       } 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;
        while (time_before(jiffies, timeout)) {
index b343ab3764a1c9c9f5c9566a344a28f54d923be4..efc47ffd066d6cdcc1c7d430065824991870acba 100644 (file)
@@ -344,8 +344,11 @@ void smp_send_stop(void)
 
 int __cpuinit __cpu_up(unsigned int cpu)
 {
-       struct task_struct *idle;
        int ret;
+       static struct task_struct *idle;
+
+       if (idle)
+               free_task(idle);
 
        idle = fork_idle(cpu);
        if (IS_ERR(idle)) {
@@ -354,7 +357,6 @@ int __cpuinit __cpu_up(unsigned int cpu)
        }
 
        secondary_stack = task_stack_page(idle) + THREAD_SIZE;
-       smp_wmb();
 
        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_count);
        current->active_mm = mm;
-       BUG_ON(current->mm);    /* Can't be, but better be safe than sorry. */
 
        preempt_disable();
 
@@ -495,3 +496,34 @@ void resync_core_dcache(void)
 }
 EXPORT_SYMBOL(resync_core_dcache);
 #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