[POWERPC] powermac: Support G5 CPU hotplug
authorJohannes Berg <johannes@sipsolutions.net>
Wed, 2 May 2007 20:33:51 +0000 (06:33 +1000)
committerPaul Mackerras <paulus@samba.org>
Mon, 7 May 2007 10:31:13 +0000 (20:31 +1000)
This allows "hotplugging" of CPUs on G5 machines.  CPUs that are
disabled are put into an idle loop with the decrementer frequency set
to minimum.  To wake them up again we kick them just like when bringing
them up.  To stop those CPUs from messing with any global state we stop
them from entering the timer interrupt.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
arch/powerpc/kernel/idle_power4.S
arch/powerpc/platforms/powermac/setup.c
arch/powerpc/platforms/powermac/smp.c
include/asm-powerpc/machdep.h

index ba3195478600998a72bbdbabeaf7761963a435ed..5328709eeedcf952b3fa32445f8a6aea1c3e82cb 100644 (file)
@@ -53,3 +53,24 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
        isync
        b       1b
 
+_GLOBAL(power4_cpu_offline_powersave)
+       /* Go to NAP now */
+       mfmsr   r7
+       rldicl  r0,r7,48,1
+       rotldi  r0,r0,16
+       mtmsrd  r0,1                    /* hard-disable interrupts */
+       li      r0,1
+       li      r6,0
+       stb     r0,PACAHARDIRQEN(r13)   /* we'll hard-enable shortly */
+       stb     r6,PACASOFTIRQEN(r13)   /* soft-disable irqs */
+BEGIN_FTR_SECTION
+       DSSALL
+       sync
+END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
+       ori     r7,r7,MSR_EE
+       oris    r7,r7,MSR_POW@h
+       sync
+       isync
+       mtmsrd  r7
+       isync
+       blr
index 5ae57e17d2ba74105300708538d0f82f04d4055f..e365bff74d8391cbce7c5e524a6e36822599e7ed 100644 (file)
@@ -444,6 +444,9 @@ static int initializing = 1;
 static int pmac_late_init(void)
 {
        initializing = 0;
+       /* this is udbg (which is __init) and we can later use it during
+        * cpu hotplug (in smp_core99_kick_cpu) */
+       ppc_md.progress = NULL;
        return 0;
 }
 
@@ -661,7 +664,52 @@ static int pmac_pci_probe_mode(struct pci_bus *bus)
                return PCI_PROBE_NORMAL;
        return PCI_PROBE_DEVTREE;
 }
-#endif
+
+#ifdef CONFIG_HOTPLUG_CPU
+/* access per cpu vars from generic smp.c */
+DECLARE_PER_CPU(int, cpu_state);
+
+static void pmac_cpu_die(void)
+{
+       /*
+        * turn off as much as possible, we'll be
+        * kicked out as this will only be invoked
+        * on core99 platforms for now ...
+        */
+
+       printk(KERN_INFO "CPU#%d offline\n", smp_processor_id());
+       __get_cpu_var(cpu_state) = CPU_DEAD;
+       smp_wmb();
+
+       /*
+        * during the path that leads here preemption is disabled,
+        * reenable it now so that when coming up preempt count is
+        * zero correctly
+        */
+       preempt_enable();
+
+       /*
+        * hard-disable interrupts for the non-NAP case, the NAP code
+        * needs to re-enable interrupts (but soft-disables them)
+        */
+       hard_irq_disable();
+
+       while (1) {
+               /* let's not take timer interrupts too often ... */
+               set_dec(0x7fffffff);
+
+               /* should always be true at this point */
+               if (cpu_has_feature(CPU_FTR_CAN_NAP))
+                       power4_cpu_offline_powersave();
+               else {
+                       HMT_low();
+                       HMT_very_low();
+               }
+       }
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+#endif /* CONFIG_PPC64 */
 
 define_machine(powermac) {
        .name                   = "PowerMac",
@@ -698,6 +746,6 @@ define_machine(powermac) {
        .phys_mem_access_prot   = pci_phys_mem_access_prot,
 #endif
 #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PPC64)
-       .cpu_die                = generic_mach_cpu_die,
+       .cpu_die                = pmac_cpu_die,
 #endif
 };
index 6f32c4eca6e5f48c78eda6f00fd3c98087290537..330354113186a1b3c89cfd1dcdb430707fd24102 100644 (file)
@@ -900,7 +900,7 @@ void smp_core99_cpu_die(unsigned int cpu)
        cpu_dead[cpu] = 0;
 }
 
-#endif
+#endif /* CONFIG_HOTPLUG_CPU && CONFIG_PP32 */
 
 /* Core99 Macs (dual G4s and G5s) */
 struct smp_ops_t core99_smp_ops = {
@@ -910,8 +910,16 @@ struct smp_ops_t core99_smp_ops = {
        .setup_cpu      = smp_core99_setup_cpu,
        .give_timebase  = smp_core99_give_timebase,
        .take_timebase  = smp_core99_take_timebase,
-#if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PPC32)
+#if defined(CONFIG_HOTPLUG_CPU)
+# if defined(CONFIG_PPC32)
        .cpu_disable    = smp_core99_cpu_disable,
        .cpu_die        = smp_core99_cpu_die,
+# endif
+# if defined(CONFIG_PPC64)
+       .cpu_disable    = generic_cpu_disable,
+       .cpu_die        = generic_cpu_die,
+       /* intentionally do *NOT* assign cpu_enable,
+        * the generic code will use kick_cpu then! */
+# endif
 #endif
 };
index b204926ce91348a296618c8e350ce0f292136246..bbd17d0e170dec0077300fdf4320b6b83216083a 100644 (file)
@@ -248,6 +248,7 @@ struct machdep_calls {
 };
 
 extern void power4_idle(void);
+extern void power4_cpu_offline_powersave(void);
 extern void ppc6xx_idle(void);
 
 /*