diff options
Diffstat (limited to 'arch/mips/kernel/irq.c')
-rw-r--r-- | arch/mips/kernel/irq.c | 344 |
1 files changed, 169 insertions, 175 deletions
diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index 608a0b431..a6f257c88 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -8,125 +8,120 @@ * instead of just grabbing them. Thus setups with different IRQ numbers * shouldn't result in any weird surprises, and installing new handlers * should be easier. - */ - -/* - * IRQ's are in fact implemented a bit like signal handlers for the kernel. - * Naturally it's not a 1:1 relation, but there are similarities. - */ - -/* - * Mips support by Ralf Baechle and Andreas Busse * - * The Deskstation Tyne is almost completely like an IBM compatible PC with - * another type of microprocessor. Therefore this code is almost completely - * the same. More work needs to be done to support Acer PICA and other - * machines. + * Mips support by Ralf Baechle and Andreas Busse */ - -#include <linux/ptrace.h> #include <linux/errno.h> #include <linux/kernel_stat.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/types.h> #include <linux/interrupt.h> +#include <linux/ioport.h> #include <linux/timex.h> +#include <linux/malloc.h> +#include <linux/random.h> #include <asm/bitops.h> #include <asm/bootinfo.h> #include <asm/io.h> #include <asm/irq.h> -#include <asm/mipsregs.h> #include <asm/jazz.h> +#include <asm/mipsregs.h> #include <asm/system.h> +#include <asm/vector.h> unsigned char cache_21 = 0xff; unsigned char cache_A1 = 0xff; unsigned long spurious_count = 0; -void disable_irq(unsigned int irq_nr) +/* + * (un)mask_irq, disable_irq() and enable_irq() only handle (E)ISA and + * PCI devices. Other onboard hardware needs specific routines. + */ +static inline void mask_irq(unsigned int irq_nr) { - unsigned long flags; unsigned char mask; mask = 1 << (irq_nr & 7); - save_flags(flags); if (irq_nr < 8) { - cli(); cache_21 |= mask; outb(cache_21,0x21); - restore_flags(flags); - return; + } else { + cache_A1 |= mask; + outb(cache_A1,0xA1); } - cli(); - cache_A1 |= mask; - outb(cache_A1,0xA1); - restore_flags(flags); } -void enable_irq(unsigned int irq_nr) +static inline void unmask_irq(unsigned int irq_nr) { - unsigned long flags; unsigned char mask; mask = ~(1 << (irq_nr & 7)); - save_flags(flags); if (irq_nr < 8) { - cli(); cache_21 &= mask; outb(cache_21,0x21); - restore_flags(flags); - return; + } else { + cache_A1 &= mask; + outb(cache_A1,0xA1); } +} + +void disable_irq(unsigned int irq_nr) +{ + unsigned long flags; + + save_flags(flags); cli(); - cache_A1 &= mask; - outb(cache_A1,0xA1); + mask_irq(irq_nr); + restore_flags(flags); +} + +void enable_irq(unsigned int irq_nr) +{ + unsigned long flags; + save_flags(flags); + cli(); + unmask_irq(irq_nr); restore_flags(flags); } /* - * Pointers to the low-level handlers: first the general ones, then the - * fast ones, then the bad ones. + * Low-level interrupt handlers: first the timer interrupt, then the + * general, then the fast and finally the bad interrupt handler. */ +extern void timer_interrupt(void); extern void interrupt(void); extern void fast_interrupt(void); extern void bad_interrupt(void); -/* - * Initial irq handlers. - */ -struct irqaction { - void (*handler)(int, struct pt_regs *); - unsigned long flags; - unsigned long mask; - const char *name; -}; - -static struct irqaction irq_action[16] = { - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL } +static struct irqaction *irq_action[16] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL }; int get_irq_list(char *buf) { int i, len = 0; - struct irqaction * action = irq_action; + struct irqaction * action; - for (i = 0 ; i < 16 ; i++, action++) { - if (!action->handler) + for (i = 0 ; i < 16 ; i++) { + action = irq_action[i]; + if (!action) continue; - len += sprintf(buf+len, "%2d: %8d %c %s\n", + len += sprintf(buf+len, "%2d: %8u %c %s", i, kstat.interrupts[i], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); + for (action=action->next; action; action = action->next) { + len += sprintf(buf+len, ",%s %s", + (action->flags & SA_INTERRUPT) ? " +" : "", + action->name); + } + len += sprintf(buf+len, "\n"); } return len; } @@ -140,14 +135,17 @@ int get_irq_list(char *buf) */ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) { - struct irqaction * action = irq + irq_action; -#if 0 -if (irq > 0) { - printk("in do_IRQ with irq=%d\n",irq); -} -#endif + struct irqaction * action = *(irq + irq_action); + int do_random = 0; + kstat.interrupts[irq]++; - action->handler(irq, regs); + while (action) { + do_random |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } + if (do_random & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); } /* @@ -157,129 +155,159 @@ if (irq > 0) { */ asmlinkage void do_fast_IRQ(int irq) { - struct irqaction * action = irq + irq_action; + struct irqaction * action = *(irq + irq_action); + int do_random = 0; kstat.interrupts[irq]++; - action->handler(irq, NULL); + while (action) { + do_random |= action->flags; + action->handler(irq, action->dev_id, NULL); + action = action->next; + } + if (do_random & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); } -#define SA_PROBE SA_ONESHOT +/* + * Used only for setup of PC style interrupts and therefore still + * called setup_x86_irq. Later on I'll provide a machine specific + * function with similar purpose. Idea is to put all interrupts + * in a single table and differenciate them just by number. + */ +int setup_x86_irq(int irq, struct irqaction * new) +{ + int shared = 0; + struct irqaction *old, **p; + unsigned long flags; + + p = irq_action + irq; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) + return -EBUSY; + + /* Can't share interrupts unless both are same type */ + if ((old->flags ^ new->flags) & SA_INTERRUPT) + return -EBUSY; + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + if (new->flags & SA_SAMPLE_RANDOM) + rand_initialize_irq(irq); -int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *), - unsigned long irqflags, const char * devname) + save_flags(flags); + cli(); + *p = new; + + if (!shared) { + if (new->flags & SA_INTERRUPT) + set_int_vector(irq,fast_interrupt); + else + if (irq == 0) + set_int_vector(irq,timer_interrupt); + else + set_int_vector(irq,interrupt); + unmask_irq(irq); + } + restore_flags(flags); + return 0; +} + +int request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char * devname, + void *dev_id) { + int retval; struct irqaction * action; - unsigned long flags; if (irq > 15) return -EINVAL; - action = irq + irq_action; - if (action->handler) - return -EBUSY; if (!handler) return -EINVAL; - save_flags(flags); - cli(); + + action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + action->handler = handler; action->flags = irqflags; action->mask = 0; action->name = devname; - if (!(action->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */ - /* - * FIXME: Does the SA_INTERRUPT flag make any sense on MIPS??? - */ - if (action->flags & SA_INTERRUPT) - set_int_vector(irq,fast_interrupt); - else - set_int_vector(irq,interrupt); - } - if (irq < 8) { - cache_21 &= ~(1<<irq); - outb(cache_21,0x21); - } else { - cache_21 &= ~(1<<2); - cache_A1 &= ~(1<<(irq-8)); - outb(cache_21,0x21); - outb(cache_A1,0xA1); - } - restore_flags(flags); - return 0; -} + action->next = NULL; + action->dev_id = dev_id; + + retval = setup_x86_irq(irq, action); -void free_irq(unsigned int irq) + if (retval) + kfree(action); + return retval; +} + +void free_irq(unsigned int irq, void *dev_id) { - struct irqaction * action = irq + irq_action; + struct irqaction * action, **p; unsigned long flags; if (irq > 15) { printk("Trying to free IRQ%d\n",irq); return; } - if (!action->handler) { - printk("Trying to free free IRQ%d\n",irq); + for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) { + if (action->dev_id != dev_id) + continue; + + /* Found it - now free it */ + save_flags(flags); + cli(); + *p = action->next; + if (!irq[irq_action]) { + mask_irq(irq); + set_int_vector(irq, bad_interrupt); + } + restore_flags(flags); + kfree(action); return; } - save_flags(flags); - cli(); - if (irq < 8) { - cache_21 |= 1 << irq; - outb(cache_21,0x21); - } else { - cache_A1 |= 1 << (irq-8); - outb(cache_A1,0xA1); - } - set_int_vector(irq,bad_interrupt); - action->handler = NULL; - action->flags = 0; - action->mask = 0; - action->name = NULL; - restore_flags(flags); + printk("Trying to free free IRQ%d\n",irq); } -static void no_action(int cpl, struct pt_regs * regs) { } - -unsigned int probe_irq_on (void) +unsigned long probe_irq_on (void) { unsigned int i, irqs = 0, irqmask; unsigned long delay; - /* first, snaffle up any unassigned irqs */ + /* first, enable any unassigned irqs */ for (i = 15; i > 0; i--) { - if (!request_irq(i, no_action, SA_PROBE, "probe")) { + if (!irq_action[i]) { enable_irq(i); irqs |= (1 << i); } } /* wait for spurious interrupts to mask themselves out again */ - for (delay = jiffies + 2; delay > jiffies; ); /* min 10ms delay */ + for (delay = jiffies + HZ/10; delay > jiffies; ) + /* about 100ms delay */; /* now filter out any obviously spurious interrupts */ irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; - for (i = 15; i > 0; i--) { - if (irqs & (1 << i) & irqmask) { - irqs ^= (1 << i); - free_irq(i); - } - } -#ifdef DEBUG - printk("probe_irq_on: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); -#endif - return irqs; + return irqs & ~irqmask; } -int probe_irq_off (unsigned int irqs) +int probe_irq_off (unsigned long irqs) { unsigned int i, irqmask; irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; - for (i = 15; i > 0; i--) { - if (irqs & (1 << i)) { - free_irq(i); - } - } #ifdef DEBUG - printk("probe_irq_off: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); + printk("probe_irq_off: irqs=0x%04lx irqmask=0x%04x\n", irqs, irqmask); #endif irqs &= irqmask; if (!irqs) @@ -294,41 +322,7 @@ void init_IRQ(void) { int i; - switch (boot_info.machtype) { - case MACH_MIPS_MAGNUM_4000: - case MACH_ACER_PICA_61: - r4030_write_reg16(JAZZ_IO_IRQ_ENABLE, - JAZZ_IE_ETHERNET | - JAZZ_IE_SERIAL1 | - JAZZ_IE_SERIAL2 | - JAZZ_IE_PARALLEL | - JAZZ_IE_FLOPPY); - r4030_read_reg16(JAZZ_IO_IRQ_SOURCE); /* clear pending IRQs */ - set_cp0_status(ST0_IM, IE_IRQ4 | IE_IRQ1); - /* set the clock to 100 Hz */ - r4030_write_reg32(JAZZ_TIMER_INTERVAL, 9); - break; - case MACH_DESKSTATION_TYNE: - /* set the clock to 100 Hz */ - outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ - outb_p(LATCH & 0xff , 0x40); /* LSB */ - outb(LATCH >> 8 , 0x40); /* MSB */ - - if (request_irq(2, no_action, SA_INTERRUPT, "cascade")) - printk("Unable to get IRQ2 for cascade\n"); - break; - default: - panic("Unknown machtype in init_IRQ"); - } - for (i = 0; i < 16 ; i++) set_int_vector(i, bad_interrupt); - - /* initialize the bottom half routines. */ - for (i = 0; i < 32; i++) { - bh_base[i].routine = NULL; - bh_base[i].data = NULL; - } - bh_active = 0; - intr_count = 0; + irq_setup(); } |