From 31139971b3dc9fbb2e8a8572fb81e6e8470f363a Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 10 Sep 2005 21:13:13 +1000 Subject: [PATCH] ppc32: support hotplug cpu on powermacs This allows cpus to be off-lined on 32-bit SMP powermacs. When a cpu is off-lined, it is put into sleep mode with interrupts disabled. It can be on-lined again by asserting its soft-reset pin, which is connected to a GPIO pin. With this I can off-line the second cpu in my dual G4 powermac, which means that I can then suspend the machine (the suspend/resume code refuses to suspend if more than one cpu is online, and making it cope with multiple cpus is surprisingly messy). Signed-off-by: Paul Mackerras Signed-off-by: Linus Torvalds --- arch/ppc/platforms/pmac_sleep.S | 2 + arch/ppc/platforms/pmac_smp.c | 85 ++++++++++++++++++++++++++--------------- 2 files changed, 56 insertions(+), 31 deletions(-) (limited to 'arch/ppc/platforms') diff --git a/arch/ppc/platforms/pmac_sleep.S b/arch/ppc/platforms/pmac_sleep.S index 8d67adc7692..88419c77ac4 100644 --- a/arch/ppc/platforms/pmac_sleep.S +++ b/arch/ppc/platforms/pmac_sleep.S @@ -161,6 +161,8 @@ _GLOBAL(low_sleep_handler) addi r3,r3,sleep_storage@l stw r5,0(r3) + .globl low_cpu_die +low_cpu_die: /* Flush & disable all caches */ bl flush_disable_caches diff --git a/arch/ppc/platforms/pmac_smp.c b/arch/ppc/platforms/pmac_smp.c index 8e049dab4e6..794a23994b8 100644 --- a/arch/ppc/platforms/pmac_smp.c +++ b/arch/ppc/platforms/pmac_smp.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -55,9 +56,7 @@ * Powersurge (old powermac SMP) support. */ -extern void __secondary_start_psurge(void); -extern void __secondary_start_psurge2(void); /* Temporary horrible hack */ -extern void __secondary_start_psurge3(void); /* Temporary horrible hack */ +extern void __secondary_start_pmac_0(void); /* Addresses for powersurge registers */ #define HAMMERHEAD_BASE 0xf8000000 @@ -119,7 +118,7 @@ static volatile int sec_tb_reset = 0; static unsigned int pri_tb_hi, pri_tb_lo; static unsigned int pri_tb_stamp; -static void __init core99_init_caches(int cpu) +static void __devinit core99_init_caches(int cpu) { if (!cpu_has_feature(CPU_FTR_L2CR)) return; @@ -346,7 +345,7 @@ static int __init smp_psurge_probe(void) static void __init smp_psurge_kick_cpu(int nr) { - void (*start)(void) = __secondary_start_psurge; + unsigned long start = __pa(__secondary_start_pmac_0) + nr * 8; unsigned long a; /* may need to flush here if secondary bats aren't setup */ @@ -356,17 +355,7 @@ static void __init smp_psurge_kick_cpu(int nr) if (ppc_md.progress) ppc_md.progress("smp_psurge_kick_cpu", 0x353); - /* setup entry point of secondary processor */ - switch (nr) { - case 2: - start = __secondary_start_psurge2; - break; - case 3: - start = __secondary_start_psurge3; - break; - } - - out_be32(psurge_start, __pa(start)); + out_be32(psurge_start, start); mb(); psurge_set_ipi(nr); @@ -500,14 +489,14 @@ static int __init smp_core99_probe(void) return ncpus; } -static void __init smp_core99_kick_cpu(int nr) +static void __devinit smp_core99_kick_cpu(int nr) { unsigned long save_vector, new_vector; unsigned long flags; volatile unsigned long *vector = ((volatile unsigned long *)(KERNELBASE+0x100)); - if (nr < 1 || nr > 3) + if (nr < 0 || nr > 3) return; if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu", 0x346); @@ -518,19 +507,9 @@ static void __init smp_core99_kick_cpu(int nr) save_vector = *vector; /* Setup fake reset vector that does - * b __secondary_start_psurge - KERNELBASE + * b __secondary_start_pmac_0 + nr*8 - KERNELBASE */ - switch(nr) { - case 1: - new_vector = (unsigned long)__secondary_start_psurge; - break; - case 2: - new_vector = (unsigned long)__secondary_start_psurge2; - break; - case 3: - new_vector = (unsigned long)__secondary_start_psurge3; - break; - } + new_vector = (unsigned long) __secondary_start_pmac_0 + nr * 8; *vector = 0x48000002 + new_vector - KERNELBASE; /* flush data cache and inval instruction cache */ @@ -554,7 +533,7 @@ static void __init smp_core99_kick_cpu(int nr) if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu done", 0x347); } -static void __init smp_core99_setup_cpu(int cpu_nr) +static void __devinit smp_core99_setup_cpu(int cpu_nr) { /* Setup L2/L3 */ if (cpu_nr != 0) @@ -668,3 +647,47 @@ struct smp_ops_t core99_smp_ops __pmacdata = { .give_timebase = smp_core99_give_timebase, .take_timebase = smp_core99_take_timebase, }; + +#ifdef CONFIG_HOTPLUG_CPU + +int __cpu_disable(void) +{ + cpu_clear(smp_processor_id(), cpu_online_map); + + /* XXX reset cpu affinity here */ + openpic_set_priority(0xf); + asm volatile("mtdec %0" : : "r" (0x7fffffff)); + mb(); + udelay(20); + asm volatile("mtdec %0" : : "r" (0x7fffffff)); + return 0; +} + +extern void low_cpu_die(void) __attribute__((noreturn)); /* in pmac_sleep.S */ +static int cpu_dead[NR_CPUS]; + +void cpu_die(void) +{ + local_irq_disable(); + cpu_dead[smp_processor_id()] = 1; + mb(); + low_cpu_die(); +} + +void __cpu_die(unsigned int cpu) +{ + int timeout; + + timeout = 1000; + while (!cpu_dead[cpu]) { + if (--timeout == 0) { + printk("CPU %u refused to die!\n", cpu); + break; + } + msleep(1); + } + cpu_callin_map[cpu] = 0; + cpu_dead[cpu] = 0; +} + +#endif -- cgit v1.2.3