patch-1.3.45 linux/arch/ppc/kernel/irq.c
Next file: linux/arch/ppc/kernel/ld.script-user
Previous file: linux/arch/ppc/kernel/head.S
Back to the patch index
Back to the overall index
- Lines: 384
- Date:
Mon Oct 30 21:21:45 1995
- Orig file:
v1.3.44/linux/arch/ppc/kernel/irq.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v1.3.44/linux/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c
@@ -0,0 +1,383 @@
+/*
+ * linux/arch/ppc/kernel/irq.c
+ *
+ * Copyright (C) 1992 Linus Torvalds
+ * Adapted from arch/i386 by Gary Thomas
+ *
+ * This file contains the code used by various IRQ handling routines:
+ * asking for different IRQ's should be done through these routines
+ * 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.
+ */
+
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+
+#define CR0_NE 32
+
+static unsigned char cache_21 = 0xff;
+static unsigned char cache_A1 = 0xff;
+
+void disable_irq(unsigned int irq_nr)
+{
+ unsigned char mask;
+ int s = _disable_interrupts();
+
+ mask = 1 << (irq_nr & 7);
+ if (irq_nr < 8) {
+ cache_21 |= mask;
+ outb(cache_21,0x21);
+ _enable_interrupts(s);
+ return;
+ }
+ cache_A1 |= mask;
+ outb(cache_A1,0xA1);
+ _enable_interrupts(s);
+}
+
+void enable_irq(unsigned int irq_nr)
+{
+ unsigned char mask;
+ int s = _disable_interrupts();
+
+ mask = ~(1 << (irq_nr & 7));
+ if (irq_nr < 8) {
+ cache_21 &= mask;
+ outb(cache_21,0x21);
+ _enable_interrupts(s);
+ return;
+ }
+ cache_A1 &= mask;
+ outb(cache_A1,0xA1);
+ _enable_interrupts(s);
+}
+
+/*
+ * Irq handlers.
+ */
+struct irqaction {
+ void (*handler)(int, struct pt_regs *);
+ unsigned long flags;
+ unsigned long mask;
+ const char *name;
+ int notified;
+};
+
+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 }
+};
+
+int get_irq_list(char *buf)
+{
+ int i, len = 0;
+ struct irqaction * action = irq_action;
+
+ for (i = 0 ; i < 16 ; i++, action++) {
+ if (!action->handler)
+ continue;
+ len += sprintf(buf+len, "%2d: %8d %c %s\n",
+ i, kstat.interrupts[i],
+ (action->flags & SA_INTERRUPT) ? '+' : ' ',
+ action->name);
+ }
+ return len;
+}
+
+asmlinkage void handle_IRQ(struct pt_regs *regs)
+{
+ int irq, s;
+ struct irqaction *action;
+ intr_count++;
+ /* Figure out IRQ#, etc. */
+ outb(0x0C, 0x20); /* Poll interrupt controller */
+ irq = inb(0x20);
+ irq &= 0x07; /* Caution! */
+ if (irq == 2)
+ { /* Cascaded interrupt -> IRQ8..IRQ15 */
+ outb(0x0C, 0xA0);
+ irq = inb(0xA0) & 0x07;
+ irq += 8;
+ }
+ /* Mask interrupt & Issue EOI to interrupt controller */
+ if (irq > 7)
+ {
+ outb(cache_A1 | (1<<(irq-7)), 0xA1);
+ outb(0x20, 0xA0);
+ /* Need to ack cascade controller as well */
+ outb(0x20, 0x20);
+ } else
+ {
+ outb(cache_21 | (1<<irq), 0x21);
+ outb(0x20, 0x20);
+ }
+ action = irq + irq_action;
+ kstat.interrupts[irq]++;
+ if (action->handler)
+ {
+ action->handler(irq, regs);
+ } else
+ {
+ _printk("Bogus interrupt #%d\n", irq);
+ }
+ if (_disable_interrupts() && !action->notified)
+ {
+ action->notified = 1;
+ printk("*** WARNING! %s handler [IRQ %d] turned interrupts on!\n", action->name, irq);
+ }
+ /* Re-enable interrupt */
+ if (irq > 7)
+ {
+ outb(cache_A1, 0xA1);
+ } else
+ {
+ outb(cache_21, 0x21);
+ }
+ intr_count--;
+}
+
+/*
+ * This routine gets called when the SCSI times out on an operation.
+ * I don't know why this happens, but every so often it does and it
+ * seems to be a problem with the interrupt controller [state]. It
+ * happens a lot when there is also network activity (both devices
+ * are on the PCI bus with interrupts on the cascaded controller).
+ * Re-initializing the interrupt controller [which might lose some
+ * pending edge detected interrupts] seems to fix it.
+ */
+void check_irq(void )
+{
+ int s = _disable_interrupts();
+ unsigned char _a0, _a1, _20, _21;
+ _a1 = inb(0xA1);
+ _21 = inb(0x21);
+ outb(0x0C, 0x20); _20 = inb(0x20);
+ outb(0x0C, 0xA0); _a0 = inb(0xA0);
+#if 0
+ printk("IRQ 0x20 = %x, 0x21 = %x/%x, 0xA0 = %x, 0xA1 = %x/%x\n",
+ _20, _21, cache_21, _a0, _a1, cache_A1);
+#endif
+ /* Reset interrupt controller - see if this fixes it! */
+ /* Initialize interrupt controllers */
+ outb(0x11, 0x20); /* Start init sequence */
+ outb(0x40, 0x21); /* Vector base */
+ outb(0x04, 0x21); /* Cascade (slave) on IRQ2 */
+ outb(0x01, 0x21); /* Select 8086 mode */
+ outb(0xFF, 0x21); /* Mask all */
+ outb(0x00, 0x4D0); /* All edge triggered */
+ outb(0x11, 0xA0); /* Start init sequence */
+ outb(0x48, 0xA1); /* Vector base */
+ outb(0x02, 0xA1); /* Cascade (slave) on IRQ2 */
+ outb(0x01, 0xA1); /* Select 8086 mode */
+ outb(0xFF, 0xA1); /* Mask all */
+ outb(0xCF, 0x4D1); /* Trigger mode */
+ outb(cache_A1, 0xA1);
+ outb(cache_21, 0x21);
+ enable_irq(2); /* Enable cascade interrupt */
+ _enable_interrupts(s);
+}
+
+#define SA_PROBE SA_ONESHOT
+
+int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
+ unsigned long irqflags, const char * devname)
+{
+ struct irqaction * action;
+ unsigned long flags;
+
+#if 0
+_printk("Request IRQ #%d, Handler: %x\n", irq, handler);
+cnpause();
+#endif
+ if (irq > 15)
+ return -EINVAL;
+ action = irq + irq_action;
+ if (action->handler)
+ return -EBUSY;
+ if (!handler)
+ return -EINVAL;
+ save_flags(flags);
+ cli();
+ action->handler = handler;
+ action->flags = irqflags;
+ action->mask = 0;
+ action->name = devname;
+#if 0
+ if (!(action->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */
+ if (action->flags & SA_INTERRUPT)
+ set_intr_gate(0x20+irq,fast_interrupt[irq]);
+ else
+ set_intr_gate(0x20+irq,interrupt[irq]);
+ }
+#endif
+ 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;
+}
+
+void free_irq(unsigned int irq)
+{
+ struct irqaction * action = irq + irq_action;
+ 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);
+ 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);
+ }
+#if 0
+ set_intr_gate(0x20+irq,bad_interrupt[irq]);
+#endif
+ action->handler = NULL;
+ action->flags = 0;
+ action->mask = 0;
+ action->name = NULL;
+ restore_flags(flags);
+}
+
+static void no_action(int cpl, struct pt_regs * regs) { }
+
+unsigned /*int*/ long probe_irq_on (void)
+{
+ unsigned int i, irqs = 0, irqmask;
+ unsigned long delay;
+
+ /* first, snaffle up any unassigned irqs */
+ for (i = 15; i > 0; i--) {
+ if (!request_irq(i, no_action, SA_PROBE, "probe")) {
+ enable_irq(i);
+ irqs |= (1 << i);
+ }
+ }
+
+ /* wait for spurious interrupts to mask themselves out again */
+ for (delay = jiffies + 2; delay > jiffies; ); /* min 10ms 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;
+}
+
+int probe_irq_off (unsigned /*int*/ 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);
+#endif
+ irqs &= irqmask;
+ if (!irqs)
+ return 0;
+ i = ffz(~irqs);
+ if (irqs != (irqs & (1 << i)))
+ i = -i;
+ return i;
+}
+
+void init_IRQ(void)
+{
+ unsigned long *vme2_ie = (unsigned long *)0xFEFF006C;
+ unsigned long *vme2_ic = (unsigned long *)0xFEFF0074;
+ unsigned long *vme2_il2 = (unsigned long *)0xFEFF007C;
+ unsigned long *vme2_ioc = (unsigned long *)0xFEFF0088;
+ unsigned char *vme2pci_ic = (unsigned char *)0x80802050;
+ unsigned char *ibc_pirq = (unsigned char *)0x80800860;
+ unsigned char *ibc_pcicon = (unsigned char *)0x80800840;
+ int i;
+
+ /* 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");
+ request_region(0x20,0x20,"pic1");
+ request_region(0xa0,0x20,"pic2");
+#if 0
+ /* Enable SIG0 */
+ *vme2_ie = (*vme2_ie & 0xFFFBFFFF) | 0x00040000;
+ /* Clear any pending interrupts */
+ *vme2_ic = 0xFFFFFFFF;
+ /* SIG0 -> Level 5 */
+ *vme2_il2 = (*vme2_il2 & 0xFFFFF0FF) | 0x00000500;
+ /* Master interrupt enable */
+ *vme2_ioc |= 0x00800000;
+#endif
+ /* Enable interrupts from VMECHIP */
+ *vme2pci_ic |= 0x08;
+ /* Route PCI interrupts */
+ ibc_pirq[0] = 0x0A; /* PIRQ0 -> ISA10 */
+ ibc_pirq[1] = 0x0B; /* PIRQ1 -> ISA11 */
+ ibc_pirq[2] = 0x0E; /* PIRQ2 -> ISA14 */
+ ibc_pirq[3] = 0x0F; /* PIRQ3 -> ISA15 */
+ /* Enable PCI interrupts */
+ *ibc_pcicon |= 0x20;
+}
+
+PCI_irq(int irq)
+{
+ static short _irq[] = {10, 11, 14, 15};
+ int res = _irq[(irq-1)&0x03];
+#if 0
+ _printk("PCI IRQ #%d = %d\n", irq, res);
+#endif
+ return (res);
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this