1 /* 2 * ip27-irq.c: Highlevel interrupt handling for IP27 architecture. 3 * 4 * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org) 5 * Copyright (C) 1999, 2000 Silicon Graphics, Inc. 6 * Copyright (C) 1999 - 2001 Kanoj Sarcar 7 */ 8 9 #undef DEBUG 10 11 #include <linux/init.h> 12 #include <linux/irq.h> 13 #include <linux/errno.h> 14 #include <linux/signal.h> 15 #include <linux/sched.h> 16 #include <linux/types.h> 17 #include <linux/interrupt.h> 18 #include <linux/ioport.h> 19 #include <linux/timex.h> 20 #include <linux/smp.h> 21 #include <linux/random.h> 22 #include <linux/kernel.h> 23 #include <linux/kernel_stat.h> 24 #include <linux/delay.h> 25 #include <linux/bitops.h> 26 27 #include <asm/bootinfo.h> 28 #include <asm/io.h> 29 #include <asm/mipsregs.h> 30 31 #include <asm/processor.h> 32 #include <asm/sn/addrs.h> 33 #include <asm/sn/agent.h> 34 #include <asm/sn/arch.h> 35 #include <asm/sn/hub.h> 36 #include <asm/sn/intr.h> 37 38 /* 39 * Linux has a controller-independent x86 interrupt architecture. 40 * every controller has a 'controller-template', that is used 41 * by the main code to do the right thing. Each driver-visible 42 * interrupt source is transparently wired to the appropriate 43 * controller. Thus drivers need not be aware of the 44 * interrupt-controller. 45 * 46 * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC, 47 * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC. 48 * (IO-APICs assumed to be messaging to Pentium local-APICs) 49 * 50 * the code is designed to be easily extended with new/different 51 * interrupt controllers, without having to do assembly magic. 52 */ 53 54 extern asmlinkage void ip27_irq(void); 55 56 /* 57 * Find first bit set 58 */ 59 static int ms1bit(unsigned long x) 60 { 61 int b = 0, s; 62 63 s = 16; if (x >> 16 == 0) s = 0; b += s; x >>= s; 64 s = 8; if (x >> 8 == 0) s = 0; b += s; x >>= s; 65 s = 4; if (x >> 4 == 0) s = 0; b += s; x >>= s; 66 s = 2; if (x >> 2 == 0) s = 0; b += s; x >>= s; 67 s = 1; if (x >> 1 == 0) s = 0; b += s; 68 69 return b; 70 } 71 72 /* 73 * This code is unnecessarily complex, because we do 74 * intr enabling. Basically, once we grab the set of intrs we need 75 * to service, we must mask _all_ these interrupts; firstly, to make 76 * sure the same intr does not intr again, causing recursion that 77 * can lead to stack overflow. Secondly, we can not just mask the 78 * one intr we are do_IRQing, because the non-masked intrs in the 79 * first set might intr again, causing multiple servicings of the 80 * same intr. This effect is mostly seen for intercpu intrs. 81 * Kanoj 05.13.00 82 */ 83 84 static void ip27_do_irq_mask0(void) 85 { 86 int irq, swlevel; 87 hubreg_t pend0, mask0; 88 cpuid_t cpu = smp_processor_id(); 89 int pi_int_mask0 = 90 (cputoslice(cpu) == 0) ? PI_INT_MASK0_A : PI_INT_MASK0_B; 91 92 /* copied from Irix intpend0() */ 93 pend0 = LOCAL_HUB_L(PI_INT_PEND0); 94 mask0 = LOCAL_HUB_L(pi_int_mask0); 95 96 pend0 &= mask0; /* Pick intrs we should look at */ 97 if (!pend0) 98 return; 99 100 swlevel = ms1bit(pend0); 101 #ifdef CONFIG_SMP 102 if (pend0 & (1UL << CPU_RESCHED_A_IRQ)) { 103 LOCAL_HUB_CLR_INTR(CPU_RESCHED_A_IRQ); 104 scheduler_ipi(); 105 } else if (pend0 & (1UL << CPU_RESCHED_B_IRQ)) { 106 LOCAL_HUB_CLR_INTR(CPU_RESCHED_B_IRQ); 107 scheduler_ipi(); 108 } else if (pend0 & (1UL << CPU_CALL_A_IRQ)) { 109 LOCAL_HUB_CLR_INTR(CPU_CALL_A_IRQ); 110 irq_enter(); 111 generic_smp_call_function_interrupt(); 112 irq_exit(); 113 } else if (pend0 & (1UL << CPU_CALL_B_IRQ)) { 114 LOCAL_HUB_CLR_INTR(CPU_CALL_B_IRQ); 115 irq_enter(); 116 generic_smp_call_function_interrupt(); 117 irq_exit(); 118 } else 119 #endif 120 { 121 /* "map" swlevel to irq */ 122 struct slice_data *si = cpu_data[cpu].data; 123 124 irq = si->level_to_irq[swlevel]; 125 do_IRQ(irq); 126 } 127 128 LOCAL_HUB_L(PI_INT_PEND0); 129 } 130 131 static void ip27_do_irq_mask1(void) 132 { 133 int irq, swlevel; 134 hubreg_t pend1, mask1; 135 cpuid_t cpu = smp_processor_id(); 136 int pi_int_mask1 = (cputoslice(cpu) == 0) ? PI_INT_MASK1_A : PI_INT_MASK1_B; 137 struct slice_data *si = cpu_data[cpu].data; 138 139 /* copied from Irix intpend0() */ 140 pend1 = LOCAL_HUB_L(PI_INT_PEND1); 141 mask1 = LOCAL_HUB_L(pi_int_mask1); 142 143 pend1 &= mask1; /* Pick intrs we should look at */ 144 if (!pend1) 145 return; 146 147 swlevel = ms1bit(pend1); 148 /* "map" swlevel to irq */ 149 irq = si->level_to_irq[swlevel]; 150 LOCAL_HUB_CLR_INTR(swlevel); 151 do_IRQ(irq); 152 153 LOCAL_HUB_L(PI_INT_PEND1); 154 } 155 156 static void ip27_prof_timer(void) 157 { 158 panic("CPU %d got a profiling interrupt", smp_processor_id()); 159 } 160 161 static void ip27_hub_error(void) 162 { 163 panic("CPU %d got a hub error interrupt", smp_processor_id()); 164 } 165 166 asmlinkage void plat_irq_dispatch(void) 167 { 168 unsigned long pending = read_c0_cause() & read_c0_status(); 169 extern unsigned int rt_timer_irq; 170 171 if (pending & CAUSEF_IP4) 172 do_IRQ(rt_timer_irq); 173 else if (pending & CAUSEF_IP2) /* PI_INT_PEND_0 or CC_PEND_{A|B} */ 174 ip27_do_irq_mask0(); 175 else if (pending & CAUSEF_IP3) /* PI_INT_PEND_1 */ 176 ip27_do_irq_mask1(); 177 else if (pending & CAUSEF_IP5) 178 ip27_prof_timer(); 179 else if (pending & CAUSEF_IP6) 180 ip27_hub_error(); 181 } 182 183 void __init arch_init_irq(void) 184 { 185 } 186 187 void install_ipi(void) 188 { 189 int slice = LOCAL_HUB_L(PI_CPU_NUM); 190 int cpu = smp_processor_id(); 191 struct slice_data *si = cpu_data[cpu].data; 192 struct hub_data *hub = hub_data(cpu_to_node(cpu)); 193 int resched, call; 194 195 resched = CPU_RESCHED_A_IRQ + slice; 196 __set_bit(resched, hub->irq_alloc_mask); 197 __set_bit(resched, si->irq_enable_mask); 198 LOCAL_HUB_CLR_INTR(resched); 199 200 call = CPU_CALL_A_IRQ + slice; 201 __set_bit(call, hub->irq_alloc_mask); 202 __set_bit(call, si->irq_enable_mask); 203 LOCAL_HUB_CLR_INTR(call); 204 205 if (slice == 0) { 206 LOCAL_HUB_S(PI_INT_MASK0_A, si->irq_enable_mask[0]); 207 LOCAL_HUB_S(PI_INT_MASK1_A, si->irq_enable_mask[1]); 208 } else { 209 LOCAL_HUB_S(PI_INT_MASK0_B, si->irq_enable_mask[0]); 210 LOCAL_HUB_S(PI_INT_MASK1_B, si->irq_enable_mask[1]); 211 } 212 } 213