|
|
1.1 ! root 1: /* ! 2: * Linux IRQ management. ! 3: * Copyright (C) 1995 Shantanu Goel. ! 4: * ! 5: * This program is free software; you can redistribute it and/or modify ! 6: * it under the terms of the GNU General Public License as published by ! 7: * the Free Software Foundation; either version 2, or (at your option) ! 8: * any later version. ! 9: * ! 10: * This program is distributed in the hope that it will be useful, ! 11: * but WITHOUT ANY WARRANTY; without even the implied warranty of ! 12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 13: * GNU General Public License for more details. ! 14: * ! 15: * You should have received a copy of the GNU General Public License ! 16: * along with this program; if not, write to the Free Software ! 17: * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ! 18: */ ! 19: ! 20: /* ! 21: * linux/arch/i386/kernel/irq.c ! 22: * ! 23: * Copyright (C) 1992 Linus Torvalds ! 24: */ ! 25: ! 26: #include <sys/types.h> ! 27: #include <mach/mach_types.h> ! 28: #include <mach/vm_param.h> ! 29: #include <kern/assert.h> ! 30: ! 31: #include <i386/spl.h> ! 32: #include <i386/pic.h> ! 33: #include <i386/pit.h> ! 34: ! 35: #define MACH_INCLUDE ! 36: #include <linux/mm.h> ! 37: #include <linux/interrupt.h> ! 38: #include <linux/ptrace.h> ! 39: #include <linux/delay.h> ! 40: #include <linux/kernel_stat.h> ! 41: #include <linux/malloc.h> ! 42: #include <linux/ioport.h> ! 43: ! 44: #include <asm/system.h> ! 45: #include <asm/bitops.h> ! 46: #include <asm/irq.h> ! 47: #include <asm/io.h> ! 48: ! 49: extern int linux_timer_intr (void); ! 50: extern spl_t splhigh (void); ! 51: extern spl_t spl0 (void); ! 52: extern void form_pic_mask (void); ! 53: ! 54: /* ! 55: * XXX Move this into more suitable place... ! 56: * Set if the machine has an EISA bus. ! 57: */ ! 58: int EISA_bus = 0; ! 59: ! 60: /* ! 61: * Priority at which a Linux handler should be called. ! 62: * This is used at the time of an IRQ allocation. It is ! 63: * set by emulation routines for each class of device. ! 64: */ ! 65: spl_t linux_intr_pri; ! 66: ! 67: /* ! 68: * Flag indicating an interrupt is being handled. ! 69: */ ! 70: unsigned long intr_count = 0; ! 71: ! 72: /* ! 73: * List of Linux interrupt handlers. ! 74: */ ! 75: struct linux_action ! 76: { ! 77: void (*handler) (int, void *, struct pt_regs *); ! 78: void *dev_id; ! 79: struct linux_action *next; ! 80: unsigned long flags; ! 81: }; ! 82: ! 83: static struct linux_action *irq_action[16] = ! 84: { ! 85: NULL, NULL, NULL, NULL, ! 86: NULL, NULL, NULL, NULL, ! 87: NULL, NULL, NULL, NULL, ! 88: NULL, NULL, NULL, NULL ! 89: }; ! 90: ! 91: extern spl_t curr_ipl; ! 92: extern int curr_pic_mask; ! 93: extern int pic_mask[]; ! 94: ! 95: extern int intnull (), prtnull (); ! 96: ! 97: /* ! 98: * Generic interrupt handler for Linux devices. ! 99: * Set up a fake `struct pt_regs' then call the real handler. ! 100: */ ! 101: static int ! 102: linux_intr (int irq) ! 103: { ! 104: struct pt_regs regs; ! 105: struct linux_action *action = *(irq_action + irq); ! 106: ! 107: kstat.interrupts[irq]++; ! 108: intr_count++; ! 109: ! 110: while (action) ! 111: { ! 112: action->handler (irq, action->dev_id, ®s); ! 113: action = action->next; ! 114: } ! 115: ! 116: intr_count--; ! 117: ! 118: /* Not used. by OKUJI Yoshinori. */ ! 119: return 0; ! 120: } ! 121: ! 122: /* ! 123: * Mask an IRQ. ! 124: */ ! 125: static inline void ! 126: mask_irq (unsigned int irq_nr) ! 127: { ! 128: int i; ! 129: ! 130: for (i = 0; i < intpri[irq_nr]; i++) ! 131: pic_mask[i] |= 1 << irq_nr; ! 132: ! 133: if (curr_pic_mask != pic_mask[curr_ipl]) ! 134: { ! 135: curr_pic_mask = pic_mask[curr_ipl]; ! 136: if (irq_nr < 8) ! 137: outb (curr_pic_mask & 0xff, PIC_MASTER_OCW); ! 138: else ! 139: outb (curr_pic_mask >> 8, PIC_SLAVE_OCW); ! 140: } ! 141: } ! 142: ! 143: /* ! 144: * Unmask an IRQ. ! 145: */ ! 146: static inline void ! 147: unmask_irq (unsigned int irq_nr) ! 148: { ! 149: int mask, i; ! 150: ! 151: mask = 1 << irq_nr; ! 152: if (irq_nr >= 8) ! 153: mask |= 1 << 2; ! 154: ! 155: for (i = 0; i < intpri[irq_nr]; i++) ! 156: pic_mask[i] &= ~mask; ! 157: ! 158: if (curr_pic_mask != pic_mask[curr_ipl]) ! 159: { ! 160: curr_pic_mask = pic_mask[curr_ipl]; ! 161: if (irq_nr < 8) ! 162: outb (curr_pic_mask & 0xff, PIC_MASTER_OCW); ! 163: else ! 164: outb (curr_pic_mask >> 8, PIC_SLAVE_OCW); ! 165: } ! 166: } ! 167: ! 168: void ! 169: disable_irq (unsigned int irq_nr) ! 170: { ! 171: unsigned long flags; ! 172: ! 173: assert (irq_nr < NR_IRQS); ! 174: ! 175: save_flags (flags); ! 176: cli (); ! 177: mask_irq (irq_nr); ! 178: restore_flags (flags); ! 179: } ! 180: ! 181: void ! 182: enable_irq (unsigned int irq_nr) ! 183: { ! 184: unsigned long flags; ! 185: ! 186: assert (irq_nr < NR_IRQS); ! 187: ! 188: save_flags (flags); ! 189: cli (); ! 190: unmask_irq (irq_nr); ! 191: restore_flags (flags); ! 192: } ! 193: ! 194: /* ! 195: * Default interrupt handler for Linux. ! 196: */ ! 197: int ! 198: linux_bad_intr (int irq) ! 199: { ! 200: mask_irq (irq); ! 201: return 0; ! 202: } ! 203: ! 204: static int ! 205: setup_x86_irq (int irq, struct linux_action *new) ! 206: { ! 207: int shared = 0; ! 208: struct linux_action *old, **p; ! 209: unsigned long flags; ! 210: ! 211: p = irq_action + irq; ! 212: if ((old = *p) != NULL) ! 213: { ! 214: /* Can't share interrupts unless both agree to */ ! 215: if (!(old->flags & new->flags & SA_SHIRQ)) ! 216: return (-LINUX_EBUSY); ! 217: ! 218: /* Can't share interrupts unless both are same type */ ! 219: if ((old->flags ^ new->flags) & SA_INTERRUPT) ! 220: return (-LINUX_EBUSY); ! 221: ! 222: /* add new interrupt at end of irq queue */ ! 223: do ! 224: { ! 225: p = &old->next; ! 226: old = *p; ! 227: } ! 228: while (old); ! 229: shared = 1; ! 230: } ! 231: ! 232: save_flags (flags); ! 233: cli (); ! 234: *p = new; ! 235: ! 236: if (!shared) ! 237: { ! 238: ivect[irq] = linux_intr; ! 239: iunit[irq] = irq; ! 240: intpri[irq] = linux_intr_pri; ! 241: unmask_irq (irq); ! 242: } ! 243: restore_flags (flags); ! 244: return 0; ! 245: } ! 246: ! 247: /* ! 248: * Attach a handler to an IRQ. ! 249: */ ! 250: int ! 251: request_irq (unsigned int irq, void (*handler) (int, void *, struct pt_regs *), ! 252: unsigned long flags, const char *device, void *dev_id) ! 253: { ! 254: struct linux_action *action; ! 255: int retval; ! 256: ! 257: assert (irq < 16); ! 258: ! 259: if (!handler) ! 260: return -LINUX_EINVAL; ! 261: ! 262: /* ! 263: * Hmm... Should I use `kalloc()' ? ! 264: * By OKUJI Yoshinori. ! 265: */ ! 266: action = (struct linux_action *) ! 267: linux_kmalloc (sizeof (struct linux_action), GFP_KERNEL); ! 268: if (action == NULL) ! 269: return -LINUX_ENOMEM; ! 270: ! 271: action->handler = handler; ! 272: action->next = NULL; ! 273: action->dev_id = dev_id; ! 274: action->flags = flags; ! 275: ! 276: retval = setup_x86_irq (irq, action); ! 277: if (retval) ! 278: linux_kfree (action); ! 279: ! 280: return retval; ! 281: } ! 282: ! 283: /* ! 284: * Deallocate an irq. ! 285: */ ! 286: void ! 287: free_irq (unsigned int irq, void *dev_id) ! 288: { ! 289: struct linux_action *action, **p; ! 290: unsigned long flags; ! 291: ! 292: if (irq > 15) ! 293: panic ("free_irq: bad irq number"); ! 294: ! 295: for (p = irq_action + irq; (action = *p) != NULL; p = &action->next) ! 296: { ! 297: if (action->dev_id != dev_id) ! 298: continue; ! 299: ! 300: save_flags (flags); ! 301: cli (); ! 302: *p = action->next; ! 303: if (!irq_action[irq]) ! 304: { ! 305: mask_irq (irq); ! 306: ivect[irq] = linux_bad_intr; ! 307: iunit[irq] = irq; ! 308: intpri[irq] = SPL0; ! 309: } ! 310: restore_flags (flags); ! 311: linux_kfree (action); ! 312: return; ! 313: } ! 314: ! 315: panic ("free_irq: bad irq number"); ! 316: } ! 317: ! 318: /* ! 319: * Set for an irq probe. ! 320: */ ! 321: unsigned long ! 322: probe_irq_on (void) ! 323: { ! 324: unsigned i, irqs = 0; ! 325: unsigned long delay; ! 326: ! 327: assert (curr_ipl == 0); ! 328: ! 329: /* ! 330: * Allocate all available IRQs. ! 331: */ ! 332: for (i = 15; i > 0; i--) ! 333: { ! 334: if (!irq_action[i] && ivect[i] == linux_bad_intr) ! 335: { ! 336: intpri[i] = linux_intr_pri; ! 337: enable_irq (i); ! 338: irqs |= 1 << i; ! 339: } ! 340: } ! 341: ! 342: /* ! 343: * Wait for spurious interrupts to mask themselves out. ! 344: */ ! 345: for (delay = jiffies + HZ / 10; delay > jiffies;) ! 346: ; ! 347: ! 348: return (irqs & ~curr_pic_mask); ! 349: } ! 350: ! 351: /* ! 352: * Return the result of an irq probe. ! 353: */ ! 354: int ! 355: probe_irq_off (unsigned long irqs) ! 356: { ! 357: unsigned int i; ! 358: ! 359: assert (curr_ipl == 0); ! 360: ! 361: irqs &= curr_pic_mask; ! 362: ! 363: /* ! 364: * Disable unnecessary IRQs. ! 365: */ ! 366: for (i = 15; i > 0; i--) ! 367: { ! 368: if (!irq_action[i] && ivect[i] == linux_bad_intr) ! 369: { ! 370: disable_irq (i); ! 371: intpri[i] = SPL0; ! 372: } ! 373: } ! 374: ! 375: /* ! 376: * Return IRQ number. ! 377: */ ! 378: if (!irqs) ! 379: return 0; ! 380: i = ffz (~irqs); ! 381: if (irqs != (irqs & (1 << i))) ! 382: i = -i; ! 383: return i; ! 384: } ! 385: ! 386: /* ! 387: * Reserve IRQs used by Mach drivers. ! 388: * Must be called before Linux IRQ detection, after Mach IRQ detection. ! 389: */ ! 390: static void ! 391: reserve_mach_irqs (void) ! 392: { ! 393: unsigned int i; ! 394: ! 395: for (i = 0; i < 16; i++) ! 396: { ! 397: if (ivect[i] != prtnull && ivect[i] != intnull) ! 398: /* Set non-NULL value. */ ! 399: irq_action[i] = (struct linux_action *) -1; ! 400: } ! 401: } ! 402: ! 403: static int (*old_clock_handler) (); ! 404: static int old_clock_pri; ! 405: ! 406: void ! 407: init_IRQ (void) ! 408: { ! 409: int i; ! 410: char *p; ! 411: int latch = (CLKNUM + hz / 2) / hz; ! 412: ! 413: /* ! 414: * Ensure interrupts are disabled. ! 415: */ ! 416: (void) splhigh (); ! 417: ! 418: /* ! 419: * Program counter 0 of 8253 to interrupt hz times per second. ! 420: */ ! 421: outb_p (PIT_C0 | PIT_SQUAREMODE | PIT_READMODE, PITCTL_PORT); ! 422: outb_p (latch && 0xff, PITCTR0_PORT); ! 423: outb (latch >> 8, PITCTR0_PORT); ! 424: ! 425: /* ! 426: * Install our clock interrupt handler. ! 427: */ ! 428: old_clock_handler = ivect[0]; ! 429: old_clock_pri = intpri[0]; ! 430: ivect[0] = linux_timer_intr; ! 431: intpri[0] = SPLHI; ! 432: ! 433: reserve_mach_irqs (); ! 434: ! 435: for (i = 1; i < 16; i++) ! 436: { ! 437: /* ! 438: * irq2 and irq13 should be igonored. ! 439: */ ! 440: if (i == 2 || i == 13) ! 441: continue; ! 442: if (ivect[i] == prtnull || ivect[i] == intnull) ! 443: { ! 444: ivect[i] = linux_bad_intr; ! 445: iunit[i] = i; ! 446: intpri[i] = SPL0; ! 447: } ! 448: } ! 449: ! 450: form_pic_mask (); ! 451: ! 452: /* ! 453: * Enable interrupts. ! 454: */ ! 455: (void) spl0 (); ! 456: ! 457: /* ! 458: * Check if the machine has an EISA bus. ! 459: */ ! 460: p = (char *) 0x0FFFD9; ! 461: if (*p++ == 'E' && *p++ == 'I' && *p++ == 'S' && *p == 'A') ! 462: EISA_bus = 1; ! 463: ! 464: /* ! 465: * Permanently allocate standard device ports. ! 466: */ ! 467: request_region (0x00, 0x20, "dma1"); ! 468: request_region (0x20, 0x20, "pic1"); ! 469: request_region (0x40, 0x20, "timer"); ! 470: request_region (0x70, 0x10, "rtc"); ! 471: request_region (0x80, 0x20, "dma page reg"); ! 472: request_region (0xa0, 0x20, "pic2"); ! 473: request_region (0xc0, 0x20, "dma2"); ! 474: request_region (0xf0, 0x10, "npu"); ! 475: } ! 476: ! 477: void ! 478: restore_IRQ (void) ! 479: { ! 480: /* ! 481: * Disable interrupts. ! 482: */ ! 483: (void) splhigh (); ! 484: ! 485: /* ! 486: * Restore clock interrupt handler. ! 487: */ ! 488: ivect[0] = old_clock_handler; ! 489: intpri[0] = old_clock_pri; ! 490: form_pic_mask (); ! 491: } ! 492:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.