[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 <paulus@samba.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c
index e70b587..726fe7c 100644
--- a/arch/ppc/kernel/smp.c
+++ b/arch/ppc/kernel/smp.c
@@ -45,6 +45,7 @@
 cpumask_t cpu_possible_map;
 int smp_hw_index[NR_CPUS];
 struct thread_info *secondary_ti;
+static struct task_struct *idle_tasks[NR_CPUS];
 
 EXPORT_SYMBOL(cpu_online_map);
 EXPORT_SYMBOL(cpu_possible_map);
@@ -286,7 +287,8 @@
 
 void __init smp_prepare_cpus(unsigned int max_cpus)
 {
-	int num_cpus, i;
+	int num_cpus, i, cpu;
+	struct task_struct *p;
 
 	/* Fixup boot cpu */
         smp_store_cpu_info(smp_processor_id());
@@ -308,6 +310,17 @@
 
 	if (smp_ops->space_timers)
 		smp_ops->space_timers(num_cpus);
+
+	for_each_cpu(cpu) {
+		if (cpu == smp_processor_id())
+			continue;
+		/* create a process for the processor */
+		p = fork_idle(cpu);
+		if (IS_ERR(p))
+			panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p));
+		p->thread_info->cpu = cpu;
+		idle_tasks[cpu] = p;
+	}
 }
 
 void __devinit smp_prepare_boot_cpu(void)
@@ -334,12 +347,17 @@
 	set_dec(tb_ticks_per_jiffy);
 	cpu_callin_map[cpu] = 1;
 
-	printk("CPU %i done callin...\n", cpu);
+	printk("CPU %d done callin...\n", cpu);
 	smp_ops->setup_cpu(cpu);
-	printk("CPU %i done setup...\n", cpu);
-	local_irq_enable();
+	printk("CPU %d done setup...\n", cpu);
 	smp_ops->take_timebase();
-	printk("CPU %i done timebase take...\n", cpu);
+	printk("CPU %d done timebase take...\n", cpu);
+
+	spin_lock(&call_lock);
+	cpu_set(cpu, cpu_online_map);
+	spin_unlock(&call_lock);
+
+	local_irq_enable();
 
 	cpu_idle();
 	return 0;
@@ -347,17 +365,11 @@
 
 int __cpu_up(unsigned int cpu)
 {
-	struct task_struct *p;
 	char buf[32];
 	int c;
 
-	/* create a process for the processor */
-	/* only regs.msr is actually used, and 0 is OK for it */
-	p = fork_idle(cpu);
-	if (IS_ERR(p))
-		panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p));
-	secondary_ti = p->thread_info;
-	p->thread_info->cpu = cpu;
+	secondary_ti = idle_tasks[cpu]->thread_info;
+	mb();
 
 	/*
 	 * There was a cache flush loop here to flush the cache
@@ -389,7 +401,11 @@
 	printk("Processor %d found.\n", cpu);
 
 	smp_ops->give_timebase();
-	cpu_set(cpu, cpu_online_map);
+
+	/* Wait until cpu puts itself in the online map */
+	while (!cpu_online(cpu))
+		cpu_relax();
+
 	return 0;
 }