1/* 2 * SMP support for iSeries machines. 3 * 4 * Dave Engebretsen, Peter Bergner, and 5 * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com 6 * 7 * Plus various changes from other IBM teams... 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * as published by the Free Software Foundation; either version 12 * 2 of the License, or (at your option) any later version. 13 */ 14 15#undef DEBUG 16 17#include <linux/kernel.h> 18#include <linux/module.h> 19#include <linux/sched.h> 20#include <linux/smp.h> 21#include <linux/interrupt.h> 22#include <linux/kernel_stat.h> 23#include <linux/delay.h> 24#include <linux/init.h> 25#include <linux/spinlock.h> 26#include <linux/cache.h> 27#include <linux/err.h> 28#include <linux/sysdev.h> 29#include <linux/cpu.h> 30 31#include <asm/ptrace.h> 32#include <asm/atomic.h> 33#include <asm/irq.h> 34#include <asm/page.h> 35#include <asm/pgtable.h> 36#include <asm/io.h> 37#include <asm/smp.h> 38#include <asm/paca.h> 39#include <asm/iseries/hv_call.h> 40#include <asm/time.h> 41#include <asm/machdep.h> 42#include <asm/cputable.h> 43#include <asm/system.h> 44 45#include "smp.h" 46 47static unsigned long iSeries_smp_message[NR_CPUS]; 48 49void iSeries_smp_message_recv(void) 50{ 51 int cpu = smp_processor_id(); 52 int msg; 53 54 if (num_online_cpus() < 2) 55 return; 56 57 for (msg = 0; msg < 4; msg++) 58 if (test_and_clear_bit(msg, &iSeries_smp_message[cpu])) 59 smp_message_recv(msg); 60} 61 62static inline void smp_iSeries_do_message(int cpu, int msg) 63{ 64 set_bit(msg, &iSeries_smp_message[cpu]); 65 HvCall_sendIPI(&(paca[cpu])); 66} 67 68static void smp_iSeries_message_pass(int target, int msg) 69{ 70 int i; 71 72 if (target < NR_CPUS) 73 smp_iSeries_do_message(target, msg); 74 else { 75 for_each_online_cpu(i) { 76 if ((target == MSG_ALL_BUT_SELF) && 77 (i == smp_processor_id())) 78 continue; 79 smp_iSeries_do_message(i, msg); 80 } 81 } 82} 83 84static int smp_iSeries_probe(void) 85{ 86 return cpus_weight(cpu_possible_map); 87} 88 89static void smp_iSeries_kick_cpu(int nr) 90{ 91 BUG_ON((nr < 0) || (nr >= NR_CPUS)); 92 93 /* Verify that our partition has a processor nr */ 94 if (lppaca[nr].dyn_proc_status >= 2) 95 return; 96 97 /* The processor is currently spinning, waiting 98 * for the cpu_start field to become non-zero 99 * After we set cpu_start, the processor will 100 * continue on to secondary_start in iSeries_head.S 101 */ 102 paca[nr].cpu_start = 1; 103} 104 105static void __devinit smp_iSeries_setup_cpu(int nr) 106{ 107} 108 109static struct smp_ops_t iSeries_smp_ops = { 110 .message_pass = smp_iSeries_message_pass, 111 .probe = smp_iSeries_probe, 112 .kick_cpu = smp_iSeries_kick_cpu, 113 .setup_cpu = smp_iSeries_setup_cpu, 114}; 115 116/* This is called very early. */ 117void __init smp_init_iSeries(void) 118{ 119 smp_ops = &iSeries_smp_ops; 120} 121