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 (dm@engr.sgi.com) 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/config.h> 13 #include <linux/types.h> 14 #include <linux/init.h> 15 #include <linux/kernel_stat.h> 16 #include <linux/signal.h> 17 #include <linux/sched.h> 18 #include <linux/interrupt.h> 19 #include <linux/irq.h> 20 21 #include <asm/mipsregs.h> 22 #include <asm/addrspace.h> 23 24 #include <asm/sgi/ioc.h> 25 #include <asm/sgi/hpc3.h> 26 #include <asm/sgi/ip22.h> 27 28 /* #define DEBUG_SGINT */ 29 30 /* So far nothing hangs here */ 31 #undef USE_LIO3_IRQ 32 33 struct sgint_regs *sgint; 34 35 static char lc0msk_to_irqnr[256]; 36 static char lc1msk_to_irqnr[256]; 37 static char lc2msk_to_irqnr[256]; 38 static char lc3msk_to_irqnr[256]; 39 40 extern asmlinkage void indyIRQ(void); 41 extern int ip22_eisa_init(void); 42 43 static void enable_local0_irq(unsigned int irq) 44 { 45 unsigned long flags; 46 47 local_irq_save(flags); 48 /* don't allow mappable interrupt to be enabled from setup_irq, 49 * we have our own way to do so */ 50 if (irq != SGI_MAP_0_IRQ) 51 sgint->imask0 |= (1 << (irq - SGINT_LOCAL0)); 52 local_irq_restore(flags); 53 } 54 55 static unsigned int startup_local0_irq(unsigned int irq) 56 { 57 enable_local0_irq(irq); 58 return 0; /* Never anything pending */ 59 } 60 61 static void disable_local0_irq(unsigned int irq) 62 { 63 unsigned long flags; 64 65 local_irq_save(flags); 66 sgint->imask0 &= ~(1 << (irq - SGINT_LOCAL0)); 67 local_irq_restore(flags); 68 } 69 70 #define shutdown_local0_irq disable_local0_irq 71 #define mask_and_ack_local0_irq disable_local0_irq 72 73 static void end_local0_irq (unsigned int irq) 74 { 75 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) 76 enable_local0_irq(irq); 77 } 78 79 static struct hw_interrupt_type ip22_local0_irq_type = { 80 .typename = "IP22 local 0", 81 .startup = startup_local0_irq, 82 .shutdown = shutdown_local0_irq, 83 .enable = enable_local0_irq, 84 .disable = disable_local0_irq, 85 .ack = mask_and_ack_local0_irq, 86 .end = end_local0_irq, 87 }; 88 89 static void enable_local1_irq(unsigned int irq) 90 { 91 unsigned long flags; 92 93 local_irq_save(flags); 94 /* don't allow mappable interrupt to be enabled from setup_irq, 95 * we have our own way to do so */ 96 if (irq != SGI_MAP_1_IRQ) 97 sgint->imask1 |= (1 << (irq - SGINT_LOCAL1)); 98 local_irq_restore(flags); 99 } 100 101 static unsigned int startup_local1_irq(unsigned int irq) 102 { 103 enable_local1_irq(irq); 104 return 0; /* Never anything pending */ 105 } 106 107 void disable_local1_irq(unsigned int irq) 108 { 109 unsigned long flags; 110 111 local_irq_save(flags); 112 sgint->imask1 &= ~(1 << (irq - SGINT_LOCAL1)); 113 local_irq_restore(flags); 114 } 115 116 #define shutdown_local1_irq disable_local1_irq 117 #define mask_and_ack_local1_irq disable_local1_irq 118 119 static void end_local1_irq (unsigned int irq) 120 { 121 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) 122 enable_local1_irq(irq); 123 } 124 125 static struct hw_interrupt_type ip22_local1_irq_type = { 126 .typename = "IP22 local 1", 127 .startup = startup_local1_irq, 128 .shutdown = shutdown_local1_irq, 129 .enable = enable_local1_irq, 130 .disable = disable_local1_irq, 131 .ack = mask_and_ack_local1_irq, 132 .end = end_local1_irq, 133 }; 134 135 static void enable_local2_irq(unsigned int irq) 136 { 137 unsigned long flags; 138 139 local_irq_save(flags); 140 sgint->imask0 |= (1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0)); 141 sgint->cmeimask0 |= (1 << (irq - SGINT_LOCAL2)); 142 local_irq_restore(flags); 143 } 144 145 static unsigned int startup_local2_irq(unsigned int irq) 146 { 147 enable_local2_irq(irq); 148 return 0; /* Never anything pending */ 149 } 150 151 void disable_local2_irq(unsigned int irq) 152 { 153 unsigned long flags; 154 155 local_irq_save(flags); 156 sgint->cmeimask0 &= ~(1 << (irq - SGINT_LOCAL2)); 157 if (!sgint->cmeimask0) 158 sgint->imask0 &= ~(1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0)); 159 local_irq_restore(flags); 160 } 161 162 #define shutdown_local2_irq disable_local2_irq 163 #define mask_and_ack_local2_irq disable_local2_irq 164 165 static void end_local2_irq (unsigned int irq) 166 { 167 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) 168 enable_local2_irq(irq); 169 } 170 171 static struct hw_interrupt_type ip22_local2_irq_type = { 172 .typename = "IP22 local 2", 173 .startup = startup_local2_irq, 174 .shutdown = shutdown_local2_irq, 175 .enable = enable_local2_irq, 176 .disable = disable_local2_irq, 177 .ack = mask_and_ack_local2_irq, 178 .end = end_local2_irq, 179 }; 180 181 static void enable_local3_irq(unsigned int irq) 182 { 183 unsigned long flags; 184 185 local_irq_save(flags); 186 sgint->imask1 |= (1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1)); 187 sgint->cmeimask1 |= (1 << (irq - SGINT_LOCAL3)); 188 local_irq_restore(flags); 189 } 190 191 static unsigned int startup_local3_irq(unsigned int irq) 192 { 193 enable_local3_irq(irq); 194 return 0; /* Never anything pending */ 195 } 196 197 void disable_local3_irq(unsigned int irq) 198 { 199 unsigned long flags; 200 201 local_irq_save(flags); 202 sgint->cmeimask1 &= ~(1 << (irq - SGINT_LOCAL3)); 203 if (!sgint->cmeimask1) 204 sgint->imask1 &= ~(1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1)); 205 local_irq_restore(flags); 206 } 207 208 #define shutdown_local3_irq disable_local3_irq 209 #define mask_and_ack_local3_irq disable_local3_irq 210 211 static void end_local3_irq (unsigned int irq) 212 { 213 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) 214 enable_local3_irq(irq); 215 } 216 217 static struct hw_interrupt_type ip22_local3_irq_type = { 218 .typename = "IP22 local 3", 219 .startup = startup_local3_irq, 220 .shutdown = shutdown_local3_irq, 221 .enable = enable_local3_irq, 222 .disable = disable_local3_irq, 223 .ack = mask_and_ack_local3_irq, 224 .end = end_local3_irq, 225 }; 226 227 void indy_local0_irqdispatch(struct pt_regs *regs) 228 { 229 u8 mask = sgint->istat0 & sgint->imask0; 230 u8 mask2; 231 int irq; 232 233 if (mask & SGINT_ISTAT0_LIO2) { 234 mask2 = sgint->vmeistat & sgint->cmeimask0; 235 irq = lc2msk_to_irqnr[mask2]; 236 } else 237 irq = lc0msk_to_irqnr[mask]; 238 239 /* if irq == 0, then the interrupt has already been cleared */ 240 if (irq) 241 do_IRQ(irq, regs); 242 return; 243 } 244 245 void indy_local1_irqdispatch(struct pt_regs *regs) 246 { 247 u8 mask = sgint->istat1 & sgint->imask1; 248 u8 mask2; 249 int irq; 250 251 if (mask & SGINT_ISTAT1_LIO3) { 252 mask2 = sgint->vmeistat & sgint->cmeimask1; 253 irq = lc3msk_to_irqnr[mask2]; 254 } else 255 irq = lc1msk_to_irqnr[mask]; 256 257 /* if irq == 0, then the interrupt has already been cleared */ 258 if (irq) 259 do_IRQ(irq, regs); 260 return; 261 } 262 263 extern void ip22_be_interrupt(int irq, struct pt_regs *regs); 264 265 void indy_buserror_irq(struct pt_regs *regs) 266 { 267 int irq = SGI_BUSERR_IRQ; 268 269 irq_enter(); 270 kstat_this_cpu.irqs[irq]++; 271 ip22_be_interrupt(irq, regs); 272 irq_exit(); 273 } 274 275 static struct irqaction local0_cascade = { 276 .handler = no_action, 277 .flags = SA_INTERRUPT, 278 .name = "local0 cascade", 279 }; 280 281 static struct irqaction local1_cascade = { 282 .handler = no_action, 283 .flags = SA_INTERRUPT, 284 .name = "local1 cascade", 285 }; 286 287 static struct irqaction buserr = { 288 .handler = no_action, 289 .flags = SA_INTERRUPT, 290 .name = "Bus Error", 291 }; 292 293 static struct irqaction map0_cascade = { 294 .handler = no_action, 295 .flags = SA_INTERRUPT, 296 .name = "mapable0 cascade", 297 }; 298 299 #ifdef USE_LIO3_IRQ 300 static struct irqaction map1_cascade = { 301 .handler = no_action, 302 .flags = SA_INTERRUPT, 303 .name = "mapable1 cascade", 304 }; 305 #define SGI_INTERRUPTS SGINT_END 306 #else 307 #define SGI_INTERRUPTS SGINT_LOCAL3 308 #endif 309 310 extern void mips_cpu_irq_init(unsigned int irq_base); 311 312 void __init arch_init_irq(void) 313 { 314 int i; 315 316 /* Init local mask --> irq tables. */ 317 for (i = 0; i < 256; i++) { 318 if (i & 0x80) { 319 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 7; 320 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 7; 321 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 7; 322 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 7; 323 } else if (i & 0x40) { 324 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 6; 325 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 6; 326 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 6; 327 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 6; 328 } else if (i & 0x20) { 329 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 5; 330 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 5; 331 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 5; 332 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 5; 333 } else if (i & 0x10) { 334 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 4; 335 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 4; 336 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 4; 337 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 4; 338 } else if (i & 0x08) { 339 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 3; 340 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 3; 341 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 3; 342 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 3; 343 } else if (i & 0x04) { 344 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 2; 345 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 2; 346 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 2; 347 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 2; 348 } else if (i & 0x02) { 349 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 1; 350 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 1; 351 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 1; 352 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 1; 353 } else if (i & 0x01) { 354 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 0; 355 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 0; 356 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 0; 357 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 0; 358 } else { 359 lc0msk_to_irqnr[i] = 0; 360 lc1msk_to_irqnr[i] = 0; 361 lc2msk_to_irqnr[i] = 0; 362 lc3msk_to_irqnr[i] = 0; 363 } 364 } 365 366 /* Mask out all interrupts. */ 367 sgint->imask0 = 0; 368 sgint->imask1 = 0; 369 sgint->cmeimask0 = 0; 370 sgint->cmeimask1 = 0; 371 372 set_except_vector(0, indyIRQ); 373 374 /* init CPU irqs */ 375 mips_cpu_irq_init(SGINT_CPU); 376 377 for (i = SGINT_LOCAL0; i < SGI_INTERRUPTS; i++) { 378 hw_irq_controller *handler; 379 380 if (i < SGINT_LOCAL1) 381 handler = &ip22_local0_irq_type; 382 else if (i < SGINT_LOCAL2) 383 handler = &ip22_local1_irq_type; 384 else if (i < SGINT_LOCAL3) 385 handler = &ip22_local2_irq_type; 386 else 387 handler = &ip22_local3_irq_type; 388 389 irq_desc[i].status = IRQ_DISABLED; 390 irq_desc[i].action = 0; 391 irq_desc[i].depth = 1; 392 irq_desc[i].handler = handler; 393 } 394 395 /* vector handler. this register the IRQ as non-sharable */ 396 setup_irq(SGI_LOCAL_0_IRQ, &local0_cascade); 397 setup_irq(SGI_LOCAL_1_IRQ, &local1_cascade); 398 setup_irq(SGI_BUSERR_IRQ, &buserr); 399 400 /* cascade in cascade. i love Indy ;-) */ 401 setup_irq(SGI_MAP_0_IRQ, &map0_cascade); 402 #ifdef USE_LIO3_IRQ 403 setup_irq(SGI_MAP_1_IRQ, &map1_cascade); 404 #endif 405 406 #ifdef CONFIG_EISA 407 if (ip22_is_fullhouse()) /* Only Indigo-2 has EISA stuff */ 408 ip22_eisa_init (); 409 #endif 410 } 411