diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-01-17 23:32:45 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-01-17 23:32:45 +0000 |
commit | bb382d1cd598a97151a0edf5ac26e36afb0a64ce (patch) | |
tree | fd9f0ca1b31e5af2faa0d7165e6fd0fbb5524ebf /arch/mips64/sgi-ip27/ip27-irq.c | |
parent | af38bda129551834653720f277a920d7d284bd3d (diff) |
- IOC3 driver now will panic when encountering a RX/TX PCI DMA error.
- IOC3 driver does no longer use GFP_DMA which given the _very_ small
number of available GFP_DMA pages might have deadlocked the system.
- First cut of Origin support. Last minute change: Do no longer use
ARC memory / MD hub memory configuration information but klconfig.h
stuff. Simpler, faster, shorter.
- Zillions of MIPS64 fixes.
Diffstat (limited to 'arch/mips64/sgi-ip27/ip27-irq.c')
-rw-r--r-- | arch/mips64/sgi-ip27/ip27-irq.c | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/arch/mips64/sgi-ip27/ip27-irq.c b/arch/mips64/sgi-ip27/ip27-irq.c new file mode 100644 index 000000000..8749652b6 --- /dev/null +++ b/arch/mips64/sgi-ip27/ip27-irq.c @@ -0,0 +1,321 @@ +/* $Id$ + * + * ip27-irq.c: Highlevel interrupt handling for IP27 architecture. + * + * Copyright (C) 1999 Ralf Baechle (ralf@gnu.org) + * Copyright (C) 1999 Silicon Graphics, Inc. + */ +#include <linux/config.h> +#include <linux/init.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 <linux/smp.h> +#include <linux/smp_lock.h> + +#include <asm/bitops.h> +#include <asm/bootinfo.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mipsregs.h> +#include <asm/system.h> + +#include <asm/ptrace.h> +#include <asm/processor.h> +#include <asm/pci/bridge.h> +#include <asm/sn/sn0/hub.h> +#include <asm/sn/sn0/ip27.h> +#include <asm/sn/arch.h> + +extern asmlinkage void ip27_irq(void); +int (*irq_cannonicalize)(int irq); + +unsigned int local_bh_count[NR_CPUS]; +unsigned int local_irq_count[NR_CPUS]; +unsigned long spurious_count = 0; + +void disable_irq(unsigned int irq_nr) +{ + panic("disable_irq() called ..."); +} + +void enable_irq(unsigned int irq_nr) +{ + panic("enable_irq() called ..."); +} + +/* This is stupid for an Origin which can have thousands of IRQs ... */ +static struct irqaction *irq_action[NR_IRQS]; + +int get_irq_list(char *buf) +{ + int i, len = 0; + struct irqaction * action; + + for (i = 0 ; i < 32 ; i++) { + action = irq_action[i]; + if (!action) + continue; + len += sprintf(buf+len, "%2d: %8d %c %s", i, kstat.irqs[0][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; +} + +/* + * do_IRQ handles all normal device IRQ's (the special SMP cross-CPU interrupts + * have their own specific handlers). + */ +asmlinkage void do_IRQ(int irq, struct pt_regs * regs) +{ + struct irqaction *action; + int do_random, cpu; + + cpu = smp_processor_id(); + hardirq_enter(cpu); + kstat.irqs[cpu][irq]++; + + action = *(irq + irq_action); + if (action) { + if (!(action->flags & SA_INTERRUPT)) + __sti(); + action = *(irq + irq_action); + do_random = 0; + do { + do_random |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (do_random & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); + } + hardirq_exit(cpu); + + /* unmasking and bottom half handling is done magically for us. */ +} + +/* For now ... */ +void ip27_do_irq(struct pt_regs *regs) +{ + hubreg_t pend0, mask0; + + pend0 = LOCAL_HUB_L(PI_INT_PEND0); + mask0 = LOCAL_HUB_L(PI_INT_MASK0_A); + + if (pend0 & mask0 & (1 << 9)) { + LOCAL_HUB_S(PI_INT_MASK0_A, mask0 & ~(1 << 9)); + LOCAL_HUB_S(PI_INT_PEND_MOD, 9); + LOCAL_HUB_L(PI_INT_MASK0_A); /* Flush */ + do_IRQ(9, regs); + LOCAL_HUB_S(PI_INT_MASK0_A, mask0); + } +} + + +/* Startup one of the (PCI ...) IRQs routes over a bridge. */ +static unsigned int bridge_startup(unsigned int irq) +{ + bridge_t *bridge = (bridge_t *) 0x9200000008000000; + bridgereg_t br; + int pin; + + /* FIIIIIXME ... Temporary kludge. This knows how interrupts are + setup in _my_ Origin. */ + switch (irq) { + case IOC3_SERIAL_INT: pin = 3; break; + case IOC3_ETH_INT: pin = 2; break; + default: panic("bridge_startup: whoops?"); + } + + br = LOCAL_HUB_L(PI_INT_MASK0_A); + LOCAL_HUB_S(PI_INT_MASK0_A, br | (1 << irq)); + LOCAL_HUB_L(PI_INT_MASK0_A); /* Flush */ + + bridge->b_int_addr[pin].addr = 0x20000 | irq; + bridge->b_int_enable |= (1 << pin); + bridge->b_widget.w_tflush; /* Flush */ + + return 0; /* Never anything pending. */ +} + +/* Startup one of the (PCI ...) IRQs routes over a bridge. */ +static unsigned int bridge_shutdown(unsigned int irq) +{ + bridge_t *bridge = (bridge_t *) 0x9200000008000000; + bridgereg_t br; + int pin; + + /* FIIIIIXME ... Temporary kludge. This knows how interrupts are + setup in _my_ Origin. */ + switch (irq) { + case IOC3_SERIAL_INT: pin = 3; break; + case IOC3_ETH_INT: pin = 2; break; + default: panic("bridge_startup: whoops?"); + } + + br = LOCAL_HUB_L(PI_INT_MASK0_A); + LOCAL_HUB_S(PI_INT_MASK0_A, br & ~(1 << irq)); + LOCAL_HUB_L(PI_INT_MASK0_A); /* Flush */ + + bridge->b_int_enable &= ~(1 << pin); + bridge->b_widget.w_tflush; /* Flush */ + + return 0; /* Never anything pending. */ +} + +static void bridge_init(void) +{ + bridge_t *bridge = (bridge_t *) 0x9200000008000000; + + /* Hmm... IRIX sets additional bits in the address which are + documented as reserved in the bridge docs ... */ + bridge->b_int_mode = 0x0; /* Don't clear ints */ + bridge->b_wid_int_upper = 0x000a8000; /* Ints to node 0 */ + bridge->b_wid_int_lower = 0x01000090; + bridge->b_dir_map = 0xa00000; /* DMA */ + bridge->b_int_enable = 0; + bridge->b_widget.w_tflush; /* Flush */ + set_cp0_status(SRB_DEV0 | SRB_DEV1, SRB_DEV0 | SRB_DEV1); +} + +void irq_debug(void) +{ + bridge_t *bridge = (bridge_t *) 0x9200000008000000; + + printk("bridge->b_int_status = 0x%x\n", bridge->b_int_status); + printk("bridge->b_int_enable = 0x%x\n", bridge->b_int_enable); + printk("PI_INT_PEND0 = 0x%x\n", LOCAL_HUB_L(PI_INT_PEND0)); + printk("PI_INT_MASK0_A = 0x%x\n", LOCAL_HUB_L(PI_INT_MASK0_A)); +} + +int setup_irq(int irq, struct irqaction *new) +{ + int shared = 0; + struct irqaction *old, **p; + unsigned long flags; + + if (new->flags & SA_SAMPLE_RANDOM) + rand_initialize_irq(irq); + + save_and_cli(flags); + p = irq_action + irq; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) { + restore_flags(flags); + return -EBUSY; + } + + /* Add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + *p = new; + + if (!shared) { + bridge_startup(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; + + if (irq < 8 > irq > 9) + return -EINVAL; + if (!handler) + return -EINVAL; + + action = (struct irqaction *)kmalloc(sizeof(*action), GFP_KERNEL); + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + retval = setup_irq(irq, action); + if (retval) + kfree(action); + + return retval; +} + +void free_irq(unsigned int irq, void *dev_id) +{ + struct irqaction * action, **p; + unsigned long flags; + + if (irq < 8 > irq > 9) { + printk("Trying to free IRQ%d\n", irq); + return; + } + for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) { + if (action->dev_id != dev_id) + continue; + + /* Found it - now free it */ + save_and_cli(flags); + *p = action->next; + if (!irq[irq_action]) + bridge_shutdown(irq); + restore_flags(flags); + kfree(action); + return; + } + printk("Trying to free free IRQ%d\n",irq); +} + +/* Useless ISA nonsense. */ +unsigned long probe_irq_on (void) +{ + return 0; +} + +int probe_irq_off (unsigned long irqs) +{ + return 0; +} + +static int indy_irq_cannonicalize(int irq) +{ + return irq; /* Sane hardware, sane code ... */ +} + +void __init init_IRQ(void) +{ + irq_cannonicalize = indy_irq_cannonicalize; + + bridge_init(); + set_except_vector(0, ip27_irq); +} |