/* * Linux IRQ management. * Copyright (C) 1995 Shantanu Goel. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * linux/arch/i386/kernel/irq.c * * Copyright (C) 1992 Linus Torvalds */ #include #include #include #include #include #define MACH_INCLUDE #include #include #include #include #include #include #include #include #include /* * Priority at which a Linux handler should be called. * This is used at the time of an IRQ allocation. It is * set by emulation routines for each class of device. */ spl_t linux_intr_pri; /* * Flag indicating an interrupt is being handled. */ unsigned long intr_count = 0; /* * List of Linux interrupt handlers. */ static void (*linux_handlers[16])(int, struct pt_regs *); extern spl_t curr_ipl; extern int curr_pic_mask; extern int pic_mask[]; extern int intnull(), prtnull(); /* * Generic interrupt handler for Linux devices. * Set up a fake `struct pt_regs' then call the real handler. */ static int linux_intr(irq) int irq; { struct pt_regs regs; kstat.interrupts[irq]++; intr_count++; (*linux_handlers[irq])(irq, ®s); intr_count--; } /* * Mask an IRQ. */ void disable_irq(irq) unsigned int irq; { int i, flags; assert (irq < NR_IRQS); save_flags(flags); cli(); for (i = 0; i < intpri[irq]; i++) pic_mask[i] |= 1 << irq; if (curr_pic_mask != pic_mask[curr_ipl]) { curr_pic_mask = pic_mask[curr_ipl]; outb(PIC_MASTER_OCW, curr_pic_mask); outb(PIC_SLAVE_OCW, curr_pic_mask >> 8); } restore_flags(flags); } /* * Unmask an IRQ. */ void enable_irq(irq) unsigned int irq; { int mask, i, flags; assert (irq < NR_IRQS); mask = 1 << irq; if (irq >= 8) mask |= 1 << 2; save_flags(flags); cli(); for (i = 0; i < intpri[irq]; i++) pic_mask[i] &= ~mask; if (curr_pic_mask != pic_mask[curr_ipl]) { curr_pic_mask = pic_mask[curr_ipl]; outb(PIC_MASTER_OCW, curr_pic_mask); outb(PIC_SLAVE_OCW, curr_pic_mask >> 8); } restore_flags(flags); } /* * Attach a handler to an IRQ. */ int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *), unsigned long flags, const char *device) { assert(irq < 16); if (ivect[irq] == intnull || ivect[irq] == prtnull) { if (!handler) return (-LINUX_EINVAL); linux_handlers[irq] = handler; ivect[irq] = linux_intr; iunit[irq] = irq; intpri[irq] = linux_intr_pri; enable_irq(irq); return (0); } return (-LINUX_EBUSY); } /* * Deallocate an irq. */ void free_irq(unsigned int irq) { if (irq > 15) panic("free_irq: bad irq number"); disable_irq(irq); ivect[irq] = (irq == 7) ? prtnull : intnull; iunit[irq] = irq; intpri[irq] = SPL0; } /* * IRQ probe interrupt handler. */ void probe_intr(irq) int irq; { disable_irq(irq); } /* * Set for an irq probe. */ unsigned long probe_irq_on() { unsigned i, irqs = 0; unsigned long delay; assert (curr_ipl == 0); /* * Allocate all available IRQs. */ for (i = 15; i > 0; i--) if (request_irq(i, probe_intr, 0, "probe") == 0) irqs |= 1 << i; /* * Wait for spurious interrupts to mask themselves out. */ for (delay = jiffies + 2; delay > jiffies; ) ; /* * Free IRQs that caused spurious interrupts. */ for (i = 15; i > 0; i--) { if (irqs & (1 << i) & pic_mask[0]) { irqs ^= 1 << i; free_irq(i); } } return (irqs); } /* * Return the result of an irq probe. */ int probe_irq_off(unsigned long irqs) { unsigned i, irqs_save = irqs; assert (curr_ipl == 0); irqs &= pic_mask[0]; /* * Deallocate IRQs. */ for (i = 15; i > 0; i--) if (irqs_save & (1 << i)) free_irq(i); /* * Return IRQ number. */ if (!irqs) return (0); i = ffz(~irqs); if (irqs != (irqs & (1 << i))) i = -i; return (i); }