1 /* 2 * ip22-int.c: Routines for generic manipulation of the INT[23] ASIC 3 * found on INDY and Indigo2 workstations. 4 * 5 * Copyright (C) 1996 David S. Miller (davem@davemloft.net) 6 * Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org) 7 * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) 8 * - Indigo2 changes 9 * - Interrupt handling fixes 10 * Copyright (C) 2001, 2003 Ladislav Michl (ladis@linux-mips.org) 11 */ 12 #include <linux/types.h> 13 #include <linux/init.h> 14 #include <linux/kernel_stat.h> 15 #include <linux/interrupt.h> 16 #include <linux/ftrace.h> 17 18 #include <asm/irq_cpu.h> 19 #include <asm/sgi/hpc3.h> 20 #include <asm/sgi/ip22.h> 21 22 /* So far nothing hangs here */ 23 #undef USE_LIO3_IRQ 24 25 struct sgint_regs *sgint; 26 27 static char lc0msk_to_irqnr[256]; 28 static char lc1msk_to_irqnr[256]; 29 static char lc2msk_to_irqnr[256]; 30 static char lc3msk_to_irqnr[256]; 31 32 extern int ip22_eisa_init(void); 33 34 static void enable_local0_irq(struct irq_data *d) 35 { 36 /* don't allow mappable interrupt to be enabled from setup_irq, 37 * we have our own way to do so */ 38 if (d->irq != SGI_MAP_0_IRQ) 39 sgint->imask0 |= (1 << (d->irq - SGINT_LOCAL0)); 40 } 41 42 static void disable_local0_irq(struct irq_data *d) 43 { 44 sgint->imask0 &= ~(1 << (d->irq - SGINT_LOCAL0)); 45 } 46 47 static struct irq_chip ip22_local0_irq_type = { 48 .name = "IP22 local 0", 49 .irq_mask = disable_local0_irq, 50 .irq_unmask = enable_local0_irq, 51 }; 52 53 static void enable_local1_irq(struct irq_data *d) 54 { 55 /* don't allow mappable interrupt to be enabled from setup_irq, 56 * we have our own way to do so */ 57 if (d->irq != SGI_MAP_1_IRQ) 58 sgint->imask1 |= (1 << (d->irq - SGINT_LOCAL1)); 59 } 60 61 static void disable_local1_irq(struct irq_data *d) 62 { 63 sgint->imask1 &= ~(1 << (d->irq - SGINT_LOCAL1)); 64 } 65 66 static struct irq_chip ip22_local1_irq_type = { 67 .name = "IP22 local 1", 68 .irq_mask = disable_local1_irq, 69 .irq_unmask = enable_local1_irq, 70 }; 71 72 static void enable_local2_irq(struct irq_data *d) 73 { 74 sgint->imask0 |= (1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0)); 75 sgint->cmeimask0 |= (1 << (d->irq - SGINT_LOCAL2)); 76 } 77 78 static void disable_local2_irq(struct irq_data *d) 79 { 80 sgint->cmeimask0 &= ~(1 << (d->irq - SGINT_LOCAL2)); 81 if (!sgint->cmeimask0) 82 sgint->imask0 &= ~(1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0)); 83 } 84 85 static struct irq_chip ip22_local2_irq_type = { 86 .name = "IP22 local 2", 87 .irq_mask = disable_local2_irq, 88 .irq_unmask = enable_local2_irq, 89 }; 90 91 static void enable_local3_irq(struct irq_data *d) 92 { 93 sgint->imask1 |= (1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1)); 94 sgint->cmeimask1 |= (1 << (d->irq - SGINT_LOCAL3)); 95 } 96 97 static void disable_local3_irq(struct irq_data *d) 98 { 99 sgint->cmeimask1 &= ~(1 << (d->irq - SGINT_LOCAL3)); 100 if (!sgint->cmeimask1) 101 sgint->imask1 &= ~(1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1)); 102 } 103 104 static struct irq_chip ip22_local3_irq_type = { 105 .name = "IP22 local 3", 106 .irq_mask = disable_local3_irq, 107 .irq_unmask = enable_local3_irq, 108 }; 109 110 static void indy_local0_irqdispatch(void) 111 { 112 u8 mask = sgint->istat0 & sgint->imask0; 113 u8 mask2; 114 int irq; 115 116 if (mask & SGINT_ISTAT0_LIO2) { 117 mask2 = sgint->vmeistat & sgint->cmeimask0; 118 irq = lc2msk_to_irqnr[mask2]; 119 } else 120 irq = lc0msk_to_irqnr[mask]; 121 122 /* if irq == 0, then the interrupt has already been cleared */ 123 if (irq) 124 do_IRQ(irq); 125 } 126 127 static void indy_local1_irqdispatch(void) 128 { 129 u8 mask = sgint->istat1 & sgint->imask1; 130 u8 mask2; 131 int irq; 132 133 if (mask & SGINT_ISTAT1_LIO3) { 134 mask2 = sgint->vmeistat & sgint->cmeimask1; 135 irq = lc3msk_to_irqnr[mask2]; 136 } else 137 irq = lc1msk_to_irqnr[mask]; 138 139 /* if irq == 0, then the interrupt has already been cleared */ 140 if (irq) 141 do_IRQ(irq); 142 } 143 144 extern void ip22_be_interrupt(int irq); 145 146 static void __irq_entry indy_buserror_irq(void) 147 { 148 int irq = SGI_BUSERR_IRQ; 149 150 irq_enter(); 151 kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq)); 152 ip22_be_interrupt(irq); 153 irq_exit(); 154 } 155 156 static struct irqaction local0_cascade = { 157 .handler = no_action, 158 .flags = IRQF_NO_THREAD, 159 .name = "local0 cascade", 160 }; 161 162 static struct irqaction local1_cascade = { 163 .handler = no_action, 164 .flags = IRQF_NO_THREAD, 165 .name = "local1 cascade", 166 }; 167 168 static struct irqaction buserr = { 169 .handler = no_action, 170 .flags = IRQF_NO_THREAD, 171 .name = "Bus Error", 172 }; 173 174 static struct irqaction map0_cascade = { 175 .handler = no_action, 176 .flags = IRQF_NO_THREAD, 177 .name = "mapable0 cascade", 178 }; 179 180 #ifdef USE_LIO3_IRQ 181 static struct irqaction map1_cascade = { 182 .handler = no_action, 183 .flags = IRQF_NO_THREAD, 184 .name = "mapable1 cascade", 185 }; 186 #define SGI_INTERRUPTS SGINT_END 187 #else 188 #define SGI_INTERRUPTS SGINT_LOCAL3 189 #endif 190 191 extern void indy_8254timer_irq(void); 192 193 /* 194 * IRQs on the INDY look basically (barring software IRQs which we don't use 195 * at all) like: 196 * 197 * MIPS IRQ Source 198 * -------- ------ 199 * 0 Software (ignored) 200 * 1 Software (ignored) 201 * 2 Local IRQ level zero 202 * 3 Local IRQ level one 203 * 4 8254 Timer zero 204 * 5 8254 Timer one 205 * 6 Bus Error 206 * 7 R4k timer (what we use) 207 * 208 * We handle the IRQ according to _our_ priority which is: 209 * 210 * Highest ---- R4k Timer 211 * Local IRQ zero 212 * Local IRQ one 213 * Bus Error 214 * 8254 Timer zero 215 * Lowest ---- 8254 Timer one 216 * 217 * then we just return, if multiple IRQs are pending then we will just take 218 * another exception, big deal. 219 */ 220 221 asmlinkage void plat_irq_dispatch(void) 222 { 223 unsigned int pending = read_c0_status() & read_c0_cause(); 224 225 /* 226 * First we check for r4k counter/timer IRQ. 227 */ 228 if (pending & CAUSEF_IP7) 229 do_IRQ(SGI_TIMER_IRQ); 230 else if (pending & CAUSEF_IP2) 231 indy_local0_irqdispatch(); 232 else if (pending & CAUSEF_IP3) 233 indy_local1_irqdispatch(); 234 else if (pending & CAUSEF_IP6) 235 indy_buserror_irq(); 236 else if (pending & (CAUSEF_IP4 | CAUSEF_IP5)) 237 indy_8254timer_irq(); 238 } 239 240 void __init arch_init_irq(void) 241 { 242 int i; 243 244 /* Init local mask --> irq tables. */ 245 for (i = 0; i < 256; i++) { 246 if (i & 0x80) { 247 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 7; 248 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 7; 249 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 7; 250 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 7; 251 } else if (i & 0x40) { 252 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 6; 253 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 6; 254 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 6; 255 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 6; 256 } else if (i & 0x20) { 257 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 5; 258 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 5; 259 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 5; 260 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 5; 261 } else if (i & 0x10) { 262 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 4; 263 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 4; 264 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 4; 265 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 4; 266 } else if (i & 0x08) { 267 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 3; 268 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 3; 269 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 3; 270 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 3; 271 } else if (i & 0x04) { 272 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 2; 273 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 2; 274 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 2; 275 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 2; 276 } else if (i & 0x02) { 277 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 1; 278 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 1; 279 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 1; 280 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 1; 281 } else if (i & 0x01) { 282 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 0; 283 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 0; 284 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 0; 285 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 0; 286 } else { 287 lc0msk_to_irqnr[i] = 0; 288 lc1msk_to_irqnr[i] = 0; 289 lc2msk_to_irqnr[i] = 0; 290 lc3msk_to_irqnr[i] = 0; 291 } 292 } 293 294 /* Mask out all interrupts. */ 295 sgint->imask0 = 0; 296 sgint->imask1 = 0; 297 sgint->cmeimask0 = 0; 298 sgint->cmeimask1 = 0; 299 300 /* init CPU irqs */ 301 mips_cpu_irq_init(); 302 303 for (i = SGINT_LOCAL0; i < SGI_INTERRUPTS; i++) { 304 struct irq_chip *handler; 305 306 if (i < SGINT_LOCAL1) 307 handler = &ip22_local0_irq_type; 308 else if (i < SGINT_LOCAL2) 309 handler = &ip22_local1_irq_type; 310 else if (i < SGINT_LOCAL3) 311 handler = &ip22_local2_irq_type; 312 else 313 handler = &ip22_local3_irq_type; 314 315 irq_set_chip_and_handler(i, handler, handle_level_irq); 316 } 317 318 /* vector handler. this register the IRQ as non-sharable */ 319 setup_irq(SGI_LOCAL_0_IRQ, &local0_cascade); 320 setup_irq(SGI_LOCAL_1_IRQ, &local1_cascade); 321 setup_irq(SGI_BUSERR_IRQ, &buserr); 322 323 /* cascade in cascade. i love Indy ;-) */ 324 setup_irq(SGI_MAP_0_IRQ, &map0_cascade); 325 #ifdef USE_LIO3_IRQ 326 setup_irq(SGI_MAP_1_IRQ, &map1_cascade); 327 #endif 328 329 #ifdef CONFIG_EISA 330 if (ip22_is_fullhouse()) /* Only Indigo-2 has EISA stuff */ 331 ip22_eisa_init(); 332 #endif 333 } 334