summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/kernel/idle_power4.S21
-rw-r--r--arch/powerpc/platforms/powermac/setup.c52
-rw-r--r--arch/powerpc/platforms/powermac/smp.c12
-rw-r--r--include/asm-powerpc/machdep.h1
4 files changed, 82 insertions, 4 deletions
diff --git a/arch/powerpc/kernel/idle_power4.S b/arch/powerpc/kernel/idle_power4.S
index ba319547860..5328709eeed 100644
--- a/arch/powerpc/kernel/idle_power4.S
+++ b/arch/powerpc/kernel/idle_power4.S
@@ -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
diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c
index 5ae57e17d2b..e365bff74d8 100644
--- a/arch/powerpc/platforms/powermac/setup.c
+++ b/arch/powerpc/platforms/powermac/setup.c
@@ -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
};
diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c
index 6f32c4eca6e..33035411318 100644
--- a/arch/powerpc/platforms/powermac/smp.c
+++ b/arch/powerpc/platforms/powermac/smp.c
@@ -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
};
diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h
index b204926ce91..bbd17d0e170 100644
--- a/include/asm-powerpc/machdep.h
+++ b/include/asm-powerpc/machdep.h
@@ -248,6 +248,7 @@ struct machdep_calls {
};
extern void power4_idle(void);
+extern void power4_cpu_offline_powersave(void);
extern void ppc6xx_idle(void);
/*