| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * SMP support for iSeries machines. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Dave Engebretsen, Peter Bergner, and | 
					
						
							|  |  |  |  * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Plus various changes from other IBM teams... | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *      This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  *      modify it under the terms of the GNU General Public License | 
					
						
							|  |  |  |  *      as published by the Free Software Foundation; either version | 
					
						
							|  |  |  |  *      2 of the License, or (at your option) any later version. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #undef DEBUG
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/sched.h>
 | 
					
						
							|  |  |  | #include <linux/smp.h>
 | 
					
						
							|  |  |  | #include <linux/interrupt.h>
 | 
					
						
							|  |  |  | #include <linux/kernel_stat.h>
 | 
					
						
							|  |  |  | #include <linux/delay.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/spinlock.h>
 | 
					
						
							|  |  |  | #include <linux/cache.h>
 | 
					
						
							|  |  |  | #include <linux/err.h>
 | 
					
						
							|  |  |  | #include <linux/sysdev.h>
 | 
					
						
							|  |  |  | #include <linux/cpu.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <asm/ptrace.h>
 | 
					
						
							|  |  |  | #include <asm/atomic.h>
 | 
					
						
							|  |  |  | #include <asm/irq.h>
 | 
					
						
							|  |  |  | #include <asm/page.h>
 | 
					
						
							|  |  |  | #include <asm/pgtable.h>
 | 
					
						
							|  |  |  | #include <asm/io.h>
 | 
					
						
							|  |  |  | #include <asm/smp.h>
 | 
					
						
							|  |  |  | #include <asm/paca.h>
 | 
					
						
							| 
									
										
										
										
											2005-11-01 16:59:20 +11:00
										 |  |  | #include <asm/iseries/hv_call.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <asm/time.h>
 | 
					
						
							|  |  |  | #include <asm/machdep.h>
 | 
					
						
							|  |  |  | #include <asm/cputable.h>
 | 
					
						
							|  |  |  | #include <asm/system.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-10-06 13:55:26 +10:00
										 |  |  | #include "smp.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static unsigned long iSeries_smp_message[NR_CPUS]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-10-06 13:55:26 +10:00
										 |  |  | void iSeries_smp_message_recv(void) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int cpu = smp_processor_id(); | 
					
						
							|  |  |  | 	int msg; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-09-28 03:07:14 +10:00
										 |  |  | 	if (num_online_cpus() < 2) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-09-28 03:07:14 +10:00
										 |  |  | 	for (msg = 0; msg < 4; msg++) | 
					
						
							|  |  |  | 		if (test_and_clear_bit(msg, &iSeries_smp_message[cpu])) | 
					
						
							| 
									
										
										
										
											2006-10-06 13:55:26 +10:00
										 |  |  | 			smp_message_recv(msg); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void smp_iSeries_do_message(int cpu, int msg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	set_bit(msg, &iSeries_smp_message[cpu]); | 
					
						
							|  |  |  | 	HvCall_sendIPI(&(paca[cpu])); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void smp_iSeries_message_pass(int target, int msg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (target < NR_CPUS) | 
					
						
							|  |  |  | 		smp_iSeries_do_message(target, msg); | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		for_each_online_cpu(i) { | 
					
						
							| 
									
										
										
										
											2005-09-28 03:07:14 +10:00
										 |  |  | 			if ((target == MSG_ALL_BUT_SELF) && | 
					
						
							|  |  |  | 					(i == smp_processor_id())) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				continue; | 
					
						
							|  |  |  | 			smp_iSeries_do_message(i, msg); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int smp_iSeries_probe(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2005-09-23 15:03:10 +10:00
										 |  |  | 	return cpus_weight(cpu_possible_map); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void smp_iSeries_kick_cpu(int nr) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2005-09-28 03:07:14 +10:00
										 |  |  | 	BUG_ON((nr < 0) || (nr >= NR_CPUS)); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Verify that our partition has a processor nr */ | 
					
						
							| 
									
										
										
										
											2006-01-13 10:26:42 +11:00
										 |  |  | 	if (lppaca[nr].dyn_proc_status >= 2) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* The processor is currently spinning, waiting
 | 
					
						
							|  |  |  | 	 * for the cpu_start field to become non-zero | 
					
						
							|  |  |  | 	 * After we set cpu_start, the processor will | 
					
						
							|  |  |  | 	 * continue on to secondary_start in iSeries_head.S | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	paca[nr].cpu_start = 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __devinit smp_iSeries_setup_cpu(int nr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct smp_ops_t iSeries_smp_ops = { | 
					
						
							|  |  |  | 	.message_pass = smp_iSeries_message_pass, | 
					
						
							|  |  |  | 	.probe        = smp_iSeries_probe, | 
					
						
							|  |  |  | 	.kick_cpu     = smp_iSeries_kick_cpu, | 
					
						
							|  |  |  | 	.setup_cpu    = smp_iSeries_setup_cpu, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* This is called very early. */ | 
					
						
							|  |  |  | void __init smp_init_iSeries(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	smp_ops = &iSeries_smp_ops; | 
					
						
							|  |  |  | } |