1 /* 2 * linux/arch/m68k/amiga/amiints.c -- Amiga Linux interrupt handling code 3 * 4 * This file is subject to the terms and conditions of the GNU General Public 5 * License. See the file COPYING in the main directory of this archive 6 * for more details. 7 * 8 * 11/07/96: rewritten interrupt handling, irq lists are exists now only for 9 * this sources where it makes sense (VERTB/PORTS/EXTER) and you must 10 * be careful that dev_id for this sources is unique since this the 11 * only possibility to distinguish between different handlers for 12 * free_irq. irq lists also have different irq flags: 13 * - IRQ_FLG_FAST: handler is inserted at top of list (after other 14 * fast handlers) 15 * - IRQ_FLG_SLOW: handler is inserted at bottom of list and before 16 * they're executed irq level is set to the previous 17 * one, but handlers don't need to be reentrant, if 18 * reentrance occurred, slow handlers will be just 19 * called again. 20 * The whole interrupt handling for CIAs is moved to cia.c 21 * /Roman Zippel 22 * 23 * 07/08/99: rewamp of the interrupt handling - we now have two types of 24 * interrupts, normal and fast handlers, fast handlers being 25 * marked with SA_INTERRUPT and runs with all other interrupts 26 * disabled. Normal interrupts disable their own source but 27 * run with all other interrupt sources enabled. 28 * PORTS and EXTER interrupts are always shared even if the 29 * drivers do not explicitly mark this when calling 30 * request_irq which they really should do. 31 * This is similar to the way interrupts are handled on all 32 * other architectures and makes a ton of sense besides 33 * having the advantage of making it easier to share 34 * drivers. 35 * /Jes 36 */ 37 38 #include <linux/types.h> 39 #include <linux/kernel.h> 40 #include <linux/sched.h> 41 #include <linux/kernel_stat.h> 42 #include <linux/init.h> 43 #include <linux/errno.h> 44 #include <linux/seq_file.h> 45 46 #include <asm/system.h> 47 #include <asm/irq.h> 48 #include <asm/traps.h> 49 #include <asm/amigahw.h> 50 #include <asm/amigaints.h> 51 #include <asm/amipcmcia.h> 52 53 extern int cia_request_irq(struct ciabase *base,int irq, 54 irqreturn_t (*handler)(int, void *, struct pt_regs *), 55 unsigned long flags, const char *devname, void *dev_id); 56 extern void cia_free_irq(struct ciabase *base, unsigned int irq, void *dev_id); 57 extern void cia_init_IRQ(struct ciabase *base); 58 extern int cia_get_irq_list(struct ciabase *base, struct seq_file *p); 59 60 /* irq node variables for amiga interrupt sources */ 61 static irq_node_t *ami_irq_list[AMI_STD_IRQS]; 62 63 static unsigned short amiga_intena_vals[AMI_STD_IRQS] = { 64 [IRQ_AMIGA_VERTB] = IF_VERTB, 65 [IRQ_AMIGA_COPPER] = IF_COPER, 66 [IRQ_AMIGA_AUD0] = IF_AUD0, 67 [IRQ_AMIGA_AUD1] = IF_AUD1, 68 [IRQ_AMIGA_AUD2] = IF_AUD2, 69 [IRQ_AMIGA_AUD3] = IF_AUD3, 70 [IRQ_AMIGA_BLIT] = IF_BLIT, 71 [IRQ_AMIGA_DSKSYN] = IF_DSKSYN, 72 [IRQ_AMIGA_DSKBLK] = IF_DSKBLK, 73 [IRQ_AMIGA_RBF] = IF_RBF, 74 [IRQ_AMIGA_TBE] = IF_TBE, 75 [IRQ_AMIGA_SOFT] = IF_SOFT, 76 [IRQ_AMIGA_PORTS] = IF_PORTS, 77 [IRQ_AMIGA_EXTER] = IF_EXTER 78 }; 79 static const unsigned char ami_servers[AMI_STD_IRQS] = { 80 [IRQ_AMIGA_VERTB] = 1, 81 [IRQ_AMIGA_PORTS] = 1, 82 [IRQ_AMIGA_EXTER] = 1 83 }; 84 85 static short ami_ablecount[AMI_IRQS]; 86 87 static irqreturn_t ami_badint(int irq, void *dev_id, struct pt_regs *fp) 88 { 89 num_spurious += 1; 90 return IRQ_NONE; 91 } 92 93 /* 94 * void amiga_init_IRQ(void) 95 * 96 * Parameters: None 97 * 98 * Returns: Nothing 99 * 100 * This function should be called during kernel startup to initialize 101 * the amiga IRQ handling routines. 102 */ 103 104 void __init amiga_init_IRQ(void) 105 { 106 int i; 107 108 /* initialize handlers */ 109 for (i = 0; i < AMI_STD_IRQS; i++) { 110 if (ami_servers[i]) { 111 ami_irq_list[i] = NULL; 112 } else { 113 ami_irq_list[i] = new_irq_node(); 114 ami_irq_list[i]->handler = ami_badint; 115 ami_irq_list[i]->flags = 0; 116 ami_irq_list[i]->dev_id = NULL; 117 ami_irq_list[i]->devname = NULL; 118 ami_irq_list[i]->next = NULL; 119 } 120 } 121 for (i = 0; i < AMI_IRQS; i++) 122 ami_ablecount[i] = 0; 123 124 /* turn off PCMCIA interrupts */ 125 if (AMIGAHW_PRESENT(PCMCIA)) 126 gayle.inten = GAYLE_IRQ_IDE; 127 128 /* turn off all interrupts and enable the master interrupt bit */ 129 amiga_custom.intena = 0x7fff; 130 amiga_custom.intreq = 0x7fff; 131 amiga_custom.intena = IF_SETCLR | IF_INTEN; 132 133 cia_init_IRQ(&ciaa_base); 134 cia_init_IRQ(&ciab_base); 135 } 136 137 static inline int amiga_insert_irq(irq_node_t **list, irq_node_t *node) 138 { 139 unsigned long flags; 140 irq_node_t *cur; 141 142 if (!node->dev_id) 143 printk("%s: Warning: dev_id of %s is zero\n", 144 __FUNCTION__, node->devname); 145 146 local_irq_save(flags); 147 148 cur = *list; 149 150 if (node->flags & SA_INTERRUPT) { 151 if (node->flags & SA_SHIRQ) 152 return -EBUSY; 153 /* 154 * There should never be more than one 155 */ 156 while (cur && cur->flags & SA_INTERRUPT) { 157 list = &cur->next; 158 cur = cur->next; 159 } 160 } else { 161 while (cur) { 162 list = &cur->next; 163 cur = cur->next; 164 } 165 } 166 167 node->next = cur; 168 *list = node; 169 170 local_irq_restore(flags); 171 return 0; 172 } 173 174 static inline void amiga_delete_irq(irq_node_t **list, void *dev_id) 175 { 176 unsigned long flags; 177 irq_node_t *node; 178 179 local_irq_save(flags); 180 181 for (node = *list; node; list = &node->next, node = *list) { 182 if (node->dev_id == dev_id) { 183 *list = node->next; 184 /* Mark it as free. */ 185 node->handler = NULL; 186 local_irq_restore(flags); 187 return; 188 } 189 } 190 local_irq_restore(flags); 191 printk ("%s: tried to remove invalid irq\n", __FUNCTION__); 192 } 193 194 /* 195 * amiga_request_irq : add an interrupt service routine for a particular 196 * machine specific interrupt source. 197 * If the addition was successful, it returns 0. 198 */ 199 200 int amiga_request_irq(unsigned int irq, 201 irqreturn_t (*handler)(int, void *, struct pt_regs *), 202 unsigned long flags, const char *devname, void *dev_id) 203 { 204 irq_node_t *node; 205 int error = 0; 206 207 if (irq >= AMI_IRQS) { 208 printk ("%s: Unknown IRQ %d from %s\n", __FUNCTION__, 209 irq, devname); 210 return -ENXIO; 211 } 212 213 if (irq >= IRQ_AMIGA_AUTO) 214 return cpu_request_irq(irq - IRQ_AMIGA_AUTO, handler, 215 flags, devname, dev_id); 216 217 if (irq >= IRQ_AMIGA_CIAB) 218 return cia_request_irq(&ciab_base, irq - IRQ_AMIGA_CIAB, 219 handler, flags, devname, dev_id); 220 221 if (irq >= IRQ_AMIGA_CIAA) 222 return cia_request_irq(&ciaa_base, irq - IRQ_AMIGA_CIAA, 223 handler, flags, devname, dev_id); 224 225 /* 226 * IRQ_AMIGA_PORTS & IRQ_AMIGA_EXTER defaults to shared, 227 * we could add a check here for the SA_SHIRQ flag but all drivers 228 * should be aware of sharing anyway. 229 */ 230 if (ami_servers[irq]) { 231 if (!(node = new_irq_node())) 232 return -ENOMEM; 233 node->handler = handler; 234 node->flags = flags; 235 node->dev_id = dev_id; 236 node->devname = devname; 237 node->next = NULL; 238 error = amiga_insert_irq(&ami_irq_list[irq], node); 239 } else { 240 ami_irq_list[irq]->handler = handler; 241 ami_irq_list[irq]->flags = flags; 242 ami_irq_list[irq]->dev_id = dev_id; 243 ami_irq_list[irq]->devname = devname; 244 } 245 246 /* enable the interrupt */ 247 if (irq < IRQ_AMIGA_PORTS && !ami_ablecount[irq]) 248 amiga_custom.intena = IF_SETCLR | amiga_intena_vals[irq]; 249 250 return error; 251 } 252 253 void amiga_free_irq(unsigned int irq, void *dev_id) 254 { 255 if (irq >= AMI_IRQS) { 256 printk ("%s: Unknown IRQ %d\n", __FUNCTION__, irq); 257 return; 258 } 259 260 if (irq >= IRQ_AMIGA_AUTO) 261 cpu_free_irq(irq - IRQ_AMIGA_AUTO, dev_id); 262 263 if (irq >= IRQ_AMIGA_CIAB) { 264 cia_free_irq(&ciab_base, irq - IRQ_AMIGA_CIAB, dev_id); 265 return; 266 } 267 268 if (irq >= IRQ_AMIGA_CIAA) { 269 cia_free_irq(&ciaa_base, irq - IRQ_AMIGA_CIAA, dev_id); 270 return; 271 } 272 273 if (ami_servers[irq]) { 274 amiga_delete_irq(&ami_irq_list[irq], dev_id); 275 /* if server list empty, disable the interrupt */ 276 if (!ami_irq_list[irq] && irq < IRQ_AMIGA_PORTS) 277 amiga_custom.intena = amiga_intena_vals[irq]; 278 } else { 279 if (ami_irq_list[irq]->dev_id != dev_id) 280 printk("%s: removing probably wrong IRQ %d from %s\n", 281 __FUNCTION__, irq, ami_irq_list[irq]->devname); 282 ami_irq_list[irq]->handler = ami_badint; 283 ami_irq_list[irq]->flags = 0; 284 ami_irq_list[irq]->dev_id = NULL; 285 ami_irq_list[irq]->devname = NULL; 286 amiga_custom.intena = amiga_intena_vals[irq]; 287 } 288 } 289 290 /* 291 * Enable/disable a particular machine specific interrupt source. 292 * Note that this may affect other interrupts in case of a shared interrupt. 293 * This function should only be called for a _very_ short time to change some 294 * internal data, that may not be changed by the interrupt at the same time. 295 * ami_(enable|disable)_irq calls may also be nested. 296 */ 297 298 void amiga_enable_irq(unsigned int irq) 299 { 300 if (irq >= AMI_IRQS) { 301 printk("%s: Unknown IRQ %d\n", __FUNCTION__, irq); 302 return; 303 } 304 305 if (--ami_ablecount[irq]) 306 return; 307 308 /* No action for auto-vector interrupts */ 309 if (irq >= IRQ_AMIGA_AUTO){ 310 printk("%s: Trying to enable auto-vector IRQ %i\n", 311 __FUNCTION__, irq - IRQ_AMIGA_AUTO); 312 return; 313 } 314 315 if (irq >= IRQ_AMIGA_CIAB) { 316 cia_set_irq(&ciab_base, (1 << (irq - IRQ_AMIGA_CIAB))); 317 cia_able_irq(&ciab_base, CIA_ICR_SETCLR | 318 (1 << (irq - IRQ_AMIGA_CIAB))); 319 return; 320 } 321 322 if (irq >= IRQ_AMIGA_CIAA) { 323 cia_set_irq(&ciaa_base, (1 << (irq - IRQ_AMIGA_CIAA))); 324 cia_able_irq(&ciaa_base, CIA_ICR_SETCLR | 325 (1 << (irq - IRQ_AMIGA_CIAA))); 326 return; 327 } 328 329 /* enable the interrupt */ 330 amiga_custom.intena = IF_SETCLR | amiga_intena_vals[irq]; 331 } 332 333 void amiga_disable_irq(unsigned int irq) 334 { 335 if (irq >= AMI_IRQS) { 336 printk("%s: Unknown IRQ %d\n", __FUNCTION__, irq); 337 return; 338 } 339 340 if (ami_ablecount[irq]++) 341 return; 342 343 /* No action for auto-vector interrupts */ 344 if (irq >= IRQ_AMIGA_AUTO) { 345 printk("%s: Trying to disable auto-vector IRQ %i\n", 346 __FUNCTION__, irq - IRQ_AMIGA_AUTO); 347 return; 348 } 349 350 if (irq >= IRQ_AMIGA_CIAB) { 351 cia_able_irq(&ciab_base, 1 << (irq - IRQ_AMIGA_CIAB)); 352 return; 353 } 354 355 if (irq >= IRQ_AMIGA_CIAA) { 356 cia_able_irq(&ciaa_base, 1 << (irq - IRQ_AMIGA_CIAA)); 357 return; 358 } 359 360 /* disable the interrupt */ 361 amiga_custom.intena = amiga_intena_vals[irq]; 362 } 363 364 inline void amiga_do_irq(int irq, struct pt_regs *fp) 365 { 366 kstat_cpu(0).irqs[SYS_IRQS + irq]++; 367 ami_irq_list[irq]->handler(irq, ami_irq_list[irq]->dev_id, fp); 368 } 369 370 void amiga_do_irq_list(int irq, struct pt_regs *fp) 371 { 372 irq_node_t *node; 373 374 kstat_cpu(0).irqs[SYS_IRQS + irq]++; 375 376 amiga_custom.intreq = amiga_intena_vals[irq]; 377 378 for (node = ami_irq_list[irq]; node; node = node->next) 379 node->handler(irq, node->dev_id, fp); 380 } 381 382 /* 383 * The builtin Amiga hardware interrupt handlers. 384 */ 385 386 static irqreturn_t ami_int1(int irq, void *dev_id, struct pt_regs *fp) 387 { 388 unsigned short ints = amiga_custom.intreqr & amiga_custom.intenar; 389 390 /* if serial transmit buffer empty, interrupt */ 391 if (ints & IF_TBE) { 392 amiga_custom.intreq = IF_TBE; 393 amiga_do_irq(IRQ_AMIGA_TBE, fp); 394 } 395 396 /* if floppy disk transfer complete, interrupt */ 397 if (ints & IF_DSKBLK) { 398 amiga_custom.intreq = IF_DSKBLK; 399 amiga_do_irq(IRQ_AMIGA_DSKBLK, fp); 400 } 401 402 /* if software interrupt set, interrupt */ 403 if (ints & IF_SOFT) { 404 amiga_custom.intreq = IF_SOFT; 405 amiga_do_irq(IRQ_AMIGA_SOFT, fp); 406 } 407 return IRQ_HANDLED; 408 } 409 410 static irqreturn_t ami_int3(int irq, void *dev_id, struct pt_regs *fp) 411 { 412 unsigned short ints = amiga_custom.intreqr & amiga_custom.intenar; 413 414 /* if a blitter interrupt */ 415 if (ints & IF_BLIT) { 416 amiga_custom.intreq = IF_BLIT; 417 amiga_do_irq(IRQ_AMIGA_BLIT, fp); 418 } 419 420 /* if a copper interrupt */ 421 if (ints & IF_COPER) { 422 amiga_custom.intreq = IF_COPER; 423 amiga_do_irq(IRQ_AMIGA_COPPER, fp); 424 } 425 426 /* if a vertical blank interrupt */ 427 if (ints & IF_VERTB) 428 amiga_do_irq_list(IRQ_AMIGA_VERTB, fp); 429 return IRQ_HANDLED; 430 } 431 432 static irqreturn_t ami_int4(int irq, void *dev_id, struct pt_regs *fp) 433 { 434 unsigned short ints = amiga_custom.intreqr & amiga_custom.intenar; 435 436 /* if audio 0 interrupt */ 437 if (ints & IF_AUD0) { 438 amiga_custom.intreq = IF_AUD0; 439 amiga_do_irq(IRQ_AMIGA_AUD0, fp); 440 } 441 442 /* if audio 1 interrupt */ 443 if (ints & IF_AUD1) { 444 amiga_custom.intreq = IF_AUD1; 445 amiga_do_irq(IRQ_AMIGA_AUD1, fp); 446 } 447 448 /* if audio 2 interrupt */ 449 if (ints & IF_AUD2) { 450 amiga_custom.intreq = IF_AUD2; 451 amiga_do_irq(IRQ_AMIGA_AUD2, fp); 452 } 453 454 /* if audio 3 interrupt */ 455 if (ints & IF_AUD3) { 456 amiga_custom.intreq = IF_AUD3; 457 amiga_do_irq(IRQ_AMIGA_AUD3, fp); 458 } 459 return IRQ_HANDLED; 460 } 461 462 static irqreturn_t ami_int5(int irq, void *dev_id, struct pt_regs *fp) 463 { 464 unsigned short ints = amiga_custom.intreqr & amiga_custom.intenar; 465 466 /* if serial receive buffer full interrupt */ 467 if (ints & IF_RBF) { 468 /* acknowledge of IF_RBF must be done by the serial interrupt */ 469 amiga_do_irq(IRQ_AMIGA_RBF, fp); 470 } 471 472 /* if a disk sync interrupt */ 473 if (ints & IF_DSKSYN) { 474 amiga_custom.intreq = IF_DSKSYN; 475 amiga_do_irq(IRQ_AMIGA_DSKSYN, fp); 476 } 477 return IRQ_HANDLED; 478 } 479 480 static irqreturn_t ami_int7(int irq, void *dev_id, struct pt_regs *fp) 481 { 482 panic ("level 7 interrupt received\n"); 483 } 484 485 irqreturn_t (*amiga_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = { 486 [0] = ami_badint, 487 [1] = ami_int1, 488 [2] = ami_badint, 489 [3] = ami_int3, 490 [4] = ami_int4, 491 [5] = ami_int5, 492 [6] = ami_badint, 493 [7] = ami_int7 494 }; 495 496 int show_amiga_interrupts(struct seq_file *p, void *v) 497 { 498 int i; 499 irq_node_t *node; 500 501 for (i = 0; i < AMI_STD_IRQS; i++) { 502 if (!(node = ami_irq_list[i])) 503 continue; 504 seq_printf(p, "ami %2d: %10u ", i, 505 kstat_cpu(0).irqs[SYS_IRQS + i]); 506 do { 507 if (node->flags & SA_INTERRUPT) 508 seq_puts(p, "F "); 509 else 510 seq_puts(p, " "); 511 seq_printf(p, "%s\n", node->devname); 512 if ((node = node->next)) 513 seq_puts(p, " "); 514 } while (node); 515 } 516 517 cia_get_irq_list(&ciaa_base, p); 518 cia_get_irq_list(&ciab_base, p); 519 return 0; 520 } 521