patch-2.1.51 linux/arch/ppc/kernel/irq.c
Next file: linux/arch/ppc/kernel/ksyms.c
Previous file: linux/arch/ppc/kernel/idle.c
Back to the patch index
Back to the overall index
- Lines: 684
- Date:
Sat Aug 16 09:51:08 1997
- Orig file:
v2.1.50/linux/arch/ppc/kernel/irq.c
- Orig date:
Mon Aug 4 16:25:36 1997
diff -u --recursive --new-file v2.1.50/linux/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c
@@ -1,13 +1,13 @@
/*
* arch/ppc/kernel/irq.c
*
- * Power Macintosh version
- * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au)
- *
* Derived from arch/i386/kernel/irq.c
* Copyright (C) 1992 Linus Torvalds
* Adapted from arch/i386 by Gary Thomas
- * Modified by Cort Dougan (cort@cs.nmt.edu)
+ * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ * Updated and modified by Cort Dougan (cort@cs.nmt.edu)
+ * Adapted for Power Macintosh by Paul Mackerras
+ * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au)
*
* This file contains the code used by various IRQ handling routines:
* asking for different IRQ's should be done through these routines
@@ -15,11 +15,7 @@
* 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>
@@ -30,20 +26,15 @@
#include <linux/interrupt.h>
#include <linux/timex.h>
#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/bitops.h>
-#define IRQ_FLAG ((unsigned *)0xf3000020)
-#define IRQ_ENABLE ((unsigned *)0xf3000024)
-#define IRQ_ACK ((unsigned *)0xf3000028)
-#define IRQ_LEVEL ((unsigned *)0xf300002c)
-
-#define KEYBOARD_IRQ 20 /* irq number for command-power interrupt */
-
-#undef SHOW_IRQ 1
+#undef SHOW_IRQ
unsigned lost_interrupts = 0;
@@ -51,100 +42,114 @@
static struct irqaction irq_action[32];
/*
- * This contains the irq mask for both irq controllers
+ * These are set to the appropriate functions by init_IRQ()
*/
-static unsigned int cached_irq_mask = 0xffff;
-
-#define cached_21 (((char *)(&cached_irq_mask))[0])
-#define cached_A1 (((char *)(&cached_irq_mask))[1])
-
+void (*mask_and_ack_irq)(int irq_nr);
+void (*set_irq_mask)(int irq_nr);
+static unsigned int cached_irq_mask = 0xffffffff;
+#define cached_21 (((char *)(&cached_irq_mask))[3])
+#define cached_A1 (((char *)(&cached_irq_mask))[2])
+static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
int __ppc_bh_counter;
-void *null_handler(int,void *,struct pt_regs *);
+/* prep */
+#define PREP_IRQ_MASK (((unsigned int)cached_A1)<<8) | (unsigned int)cached_21
+extern unsigned long route_pci_interrupts(void);
-/*
- * disable and enable intrs in software. This is used
- * from the non-realtime parts of Linux to disable interrupts.
- * The realtime part disables/enables intrs in the hardware.
- * -- Cort
- */
-unsigned long soft_intr_enable = 1;
-void _soft_cli(void)
-{
- soft_intr_enable = 0;
-}
+/* pmac */
+#define IRQ_FLAG ((unsigned *)0xf3000020)
+#define IRQ_ENABLE ((unsigned *)0xf3000024)
+#define IRQ_ACK ((unsigned *)0xf3000028)
+#define IRQ_LEVEL ((unsigned *)0xf300002c)
+#define KEYBOARD_IRQ 20 /* irq number for command-power interrupt */
+#define PMAC_IRQ_MASK (~ld_le32(IRQ_ENABLE))
-void _soft_sti(void)
+void prep_mask_and_ack_irq(int irq_nr)
{
- soft_intr_enable = 1;
- if ( lost_interrupts )
- {
- printk("lost_interrupts from _soft_sti() %x\n",lost_interrupts);
- fake_interrupt();
+ spin_lock(&irq_controller_lock);
+ cached_irq_mask |= 1 << irq_nr;
+ if (irq_nr > 7) {
+ inb(0xA1); /* DUMMY */
+ outb(cached_A1,0xA1);
+ outb(0x62,0x20); /* Specific EOI to cascade */
+ /*outb(0x20,0xA0);*/
+ outb(0x60|(irq_nr-8), 0xA0); /* specific eoi */
+ } else {
+ inb(0x21); /* DUMMY */
+ outb(cached_21,0x21);
+ /*outb(0x20,0x20);*/
+ outb(0x60|irq_nr,0x20); /* specific eoi */
+
}
+ spin_unlock(&irq_controller_lock);
}
-void *
-null_handler(int a, void *b, struct pt_regs *regs)
+void pmac_mask_and_ack_irq(int irq_nr)
{
- /*printk("irq.c: null_handler() called. Should not have happened.\n");*/
+ spin_lock(&irq_controller_lock);
+ cached_irq_mask |= 1 << irq_nr;
+ out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) & ~(1 << irq_nr));
+ out_le32(IRQ_ACK, 1U << irq_nr);
+ spin_unlock(&irq_controller_lock);
}
-void
-disable_irq(unsigned int irq_nr)
+void prep_set_irq_mask(int irq_nr)
{
- int s = _disable_interrupts();
- unsigned char mask;
-
-#ifdef CONFIG_PMAC
- out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) & ~(1 << irq_nr));
-#else /* CONFIG_PMAC */
- mask = 1 << (irq_nr & 7);
- if (irq_nr < 8)
- {
- cached_21 |= mask;
- outb(cached_21,0x21);
- } else
- {
- cached_A1 |= mask;
+ if (irq_nr > 7) {
outb(cached_A1,0xA1);
- }
-#endif /* CONFIG_PMAC */
- _enable_interrupts(s);
+ } else {
+ outb(cached_21,0x21);
+ }
}
-void
-enable_irq(unsigned int irq_nr)
+void pmac_set_irq_mask(int irq_nr)
{
- int s = _disable_interrupts();
-#ifdef CONFIG_PMAC
- unsigned bit = 1U << irq_nr;
-
- out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) & ~(1 << irq_nr));
- out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) | bit);
-
+ /* this could be being enabled or disabled - so use cached_irq_mask */
+ out_le32(IRQ_ENABLE, ~cached_irq_mask /* enable all unmasked */ );
/*
* Unfortunately, setting the bit in the enable register
* when the device interrupt is already on *doesn't* set
* the bit in the flag register or request another interrupt.
*/
- if ((ld_le32(IRQ_LEVEL) & bit) && !(ld_le32(IRQ_FLAG) & bit))
- lost_interrupts |= bit;
-#else /* CONFIG_PMAC */
- if (irq_nr < 8) {
- cached_21 &= ~(1 << (irq_nr & 7));
- outb(cached_21,0x21);
- } else
- {
- cached_A1 &= ~(1 << (irq_nr-8 & 7));
- outb(cached_A1,0xA1);
- }
-#endif /* CONFIG_PMAC */
+ if ((ld_le32(IRQ_LEVEL) & (1UL<<irq_nr)) && !(ld_le32(IRQ_FLAG) & (1UL<<irq_nr)))
+ lost_interrupts |= (1UL<<irq_nr);
+}
- _enable_interrupts(s);
+/*
+ * These have to be protected by the spinlock
+ * before being called.
+ */
+static inline void mask_irq(unsigned int irq_nr)
+{
+ cached_irq_mask |= 1 << irq_nr;
+ set_irq_mask(irq_nr);
}
+static inline void unmask_irq(unsigned int irq_nr)
+{
+ cached_irq_mask &= ~(1 << irq_nr);
+ set_irq_mask(irq_nr);
+}
+
+void disable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&irq_controller_lock, flags);
+ mask_irq(irq_nr);
+ spin_unlock_irqrestore(&irq_controller_lock, flags);
+ synchronize_irq();
+}
+
+void enable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&irq_controller_lock, flags);
+ unmask_irq(irq_nr);
+ spin_unlock_irqrestore(&irq_controller_lock, flags);
+}
int get_irq_list(char *buf)
{
@@ -153,7 +158,7 @@
for (i = 0 ; i < NR_IRQS ; i++) {
action = irq_action + i;
- if (!action || !action->handler)
+ if (!action || !action->handler)
continue;
len += sprintf(buf+len, "%2d: %10u %s",
i, kstat.interrupts[i], action->name);
@@ -162,152 +167,99 @@
}
len += sprintf(buf+len, "\n");
}
-/*
- * Linus - should you add NMI counts here ?????
- */
#ifdef __SMP_PROF__
len+=sprintf(buf+len, "IPI: %8lu received\n",
ipi_count);
-#endif
+#endif
return len;
}
-asmlinkage void handle_IRQ(struct pt_regs *regs)
+asmlinkage void do_IRQ(struct pt_regs *regs)
{
int irq;
- unsigned bits;
+ unsigned long bits;
struct irqaction *action;
int cpu = smp_processor_id();
+ int status;
hardirq_enter(cpu);
-#ifdef CONFIG_PMAC
- bits = ld_le32(IRQ_FLAG) | lost_interrupts;
- lost_interrupts = 0;
-
- for (irq = NR_IRQS; irq >= 0; --irq)
- if (bits & (1U << irq))
- break;
-#else /* CONFIG_PMAC */
-#if 1
- if ( lost_interrupts )
+ if ( _machine == _MACH_Pmac )
{
- irq = ffz(~lost_interrupts);
- lost_interrupts &= ~irq;
- goto retry;
- }
- outb(0x0C, 0x20);
- irq = inb(0x20) & 7;
- if (irq == 2)
- {
-retry_cascade:
- outb(0x0C, 0xA0);
- irq = inb(0xA0);
- /* if no intr left */
- if ( !(irq & 128 ) )
- goto out;
- irq = (irq&7) + 8;
+ bits = ld_le32(IRQ_FLAG) | lost_interrupts;
+ lost_interrupts = 0;
}
-retry:
-#else
- /* get the irr from the intr controller */
- outb(0x0A, 0x20);
- bits = inb(0x20);
- /* handle cascade */
- if ( bits )
+ else /* prep */
{
- bits &= 4;
- outb(0x0A, 0xA0);
- bits = inb(0xA0)<<8;
- }
- /* get lost interrupts */
- bits |= lost_interrupts;
- /* save intrs that are masked out */
- lost_interrupts = bits & cached_irq_mask;
- /* get rid of intrs being masked */
- bits &= ~cached_irq_mask;
- /* non-specifc eoi */
- outb(0x20,0x20);
- if ( bits & 0xff00 )
- outb(0x20,0xA0);
-
- printk("bits %04X lost %04X mask %04x\n",
- bits, lost_interrupts,cached_irq_mask);
+#if 1
+ outb(0x0C, 0x20);
+ irq = inb(0x20) & 7;
+ if (irq == 2)
+ {
+retry_cascade:
+ outb(0x0C, 0xA0);
+ irq = inb(0xA0);
+ /* if no intr left */
+ if ( !(irq & 128 ) )
+ goto out;
+ irq = (irq&7) + 8;
+ }
+ bits = 1UL << irq;
+#else
+ /*
+ * get the isr from the intr controller since
+ * the bit in the irr has been cleared
+ */
+ outb(0x0a, 0x20);
+ bits = inb(0x20)&0xff;
+ /* handle cascade */
+ if ( bits & 4 )
+ {
+ bits &= ~4UL;
+ outb(0x0a, 0xA0);
+ bits |= inb(0xA0)<<8;
+ }
+ /* ignore masked irqs */
+ bits &= ~cached_irq_mask;
+#endif
+ }
- for (irq = NR_IRQS; irq >= 0; --irq)
+ for (irq = NR_IRQS - 1; irq >= 0; --irq)
if (bits & (1U << irq))
break;
-#endif
-#endif /* CONFIG_PMAC */
-
+
if (irq < 0) {
- printk("Bogus interrupt from PC = %lx, irq %d\n",regs->nip,irq);
+ printk("Bogus interrupt from PC = %lx\n", regs->nip);
goto out;
}
-#ifdef CONFIG_PMAC
- out_le32(IRQ_ACK, 1U << irq);
-#else /* CONFIG_PMAC */
- /* mask out the irq while handling it */
- disable_irq(irq);
- /*
- * send eoi to interrupt controller right away or lower
- * priority intrs would be ignored even if with intrs enabled
- */
- if (irq > 7)
- {
- outb(0xE0|(irq-8), 0xA0);
- outb(0xE2, 0x20);
- } else
- {
- outb(0xE0|irq, 0x20);
- }
-#endif /* !CONFIG_PMAC */
+ mask_and_ack_irq(irq);
- /*
- * now that we've acked the irq, if intrs are disabled in software
- * we're in the real-time system and non-rt linux has disabled them
- * so we just queue it up and return -- Cort
- */
- if ( ! soft_intr_enable )
- {
- lost_interrupts |= 1UL << irq;
- /* can't printk - kernel expects intrs off! */
- /*printk("irq %d while intrs soft disabled\n", irq);*/
- goto out;
- }
-
- action = irq + irq_action;
+ status = 0;
+ action = irq_action + irq;
kstat.interrupts[irq]++;
- if (action->handler) {
+ if ( action )
+ {
+ if (!(action->flags & SA_INTERRUPT))
+ __sti();
+ status |= action->flags;
action->handler(irq, action->dev_id, regs);
- _disable_interrupts(); /* in case the handler turned them on */
+ /*if (status & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);*/
+ __cli(); /* in case the handler turned them on */
+ spin_lock(&irq_controller_lock);
+ unmask_irq(irq);
+ spin_unlock(&irq_controller_lock);
} else {
disable_irq( irq );
}
-#ifdef CONFIG_PREP
- /* re-enable if the interrupt was good and isn't one-shot */
- if ( action->handler && !(action->flags & SA_ONESHOT) )
- enable_irq(irq);
+
/* make sure we don't miss any cascade intrs due to eoi-ing irq 2 */
- if ( irq > 7 )
+ if ( (_machine != _MACH_Pmac)/*prep*/ && (irq > 7) )
goto retry_cascade;
-#endif
-
- hardirq_exit(cpu);
- /*
- * This should be conditional: we should really get
- * a return code from the irq handler to tell us
- * whether the handler wants us to do software bottom
- * half handling or not..
- */
- if (1)
- if (bh_active & bh_mask)
- do_bottom_half();
- return;
+ /* do_bottom_half is called if necessary from int_return in head.S */
out:
hardirq_exit(cpu);
-
}
int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
@@ -315,13 +267,13 @@
{
struct irqaction * action;
unsigned long flags;
-
-#ifdef SHOW_IRQ
+
+#ifdef SHOW_IRQ
printk("request_irq(): irq %d handler %08x name %s dev_id %04x\n",
irq,handler,devname,dev_id);
#endif /* SHOW_IRQ */
-
- if (irq > NR_IRQS)
+
+ if (irq >= NR_IRQS)
return -EINVAL;
action = irq + irq_action;
if (action->handler)
@@ -339,17 +291,16 @@
restore_flags(flags);
return 0;
}
-
void free_irq(unsigned int irq, void *dev_id)
{
struct irqaction * action = irq + irq_action;
unsigned long flags;
-#ifdef SHOW_IRQ
+#ifdef SHOW_IRQ
printk("free_irq(): irq %d dev_id %04x\n", irq, dev_id);
#endif /* SHOW_IRQ */
-
- if (irq > NR_IRQS) {
+
+ if (irq >= NR_IRQS) {
printk("Trying to free IRQ%d\n",irq);
return;
}
@@ -370,114 +321,85 @@
unsigned 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, null_handler, SA_ONESHOT, "probe", NULL)) {
- 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)cached_A1)<<8) | (unsigned int)cached_21;
- for (i = 15; i > 0; i--) {
- if (irqs & (1 << i) & irqmask) {
- irqs ^= (1 << i);
- free_irq(i, NULL);
- }
- }
-#ifdef DEBUG
- printk("probe_irq_on: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask);
-#endif
- return irqs;
+ return 0;
}
int probe_irq_off (unsigned long irqs)
{
- unsigned int i, irqmask;
-
- irqmask = (((unsigned int)cached_A1)<<8) | (unsigned int)cached_21;
- for (i = 15; i > 0; i--) {
- if (irqs & (1 << i)) {
- free_irq(i, NULL);
- }
- }
-#ifdef SHOW_IRQ
- 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;
+ return 0;
}
-void init_IRQ(void)
+__initfunc(void init_IRQ(void))
{
-#ifdef CONFIG_PMAC
extern void xmon_irq(int, void *, struct pt_regs *);
- *IRQ_ENABLE = 0;
- request_irq(KEYBOARD_IRQ, xmon_irq, 0, "NMI", 0);
-#else /* CONFIG_PMAC */
- /* Initialize interrupt controllers */
- outb(0x11, 0x20); /* Start init sequence */
- outb(0x40, 0x21); /* Vector base */
-#if 1
- outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */
-#else
- outb(0x0C, 0x21); /* level triggered, Cascade (slave) on IRQ2 */
-#endif
-
- outb(0x01, 0x21); /* Select 8086 mode */
- outb(0xFF, 0x21); /* Mask all */
-
- outb(0x11, 0xA0); /* Start init sequence */
- outb(0x48, 0xA1); /* Vector base */
-#if 1
- outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */
-#else
- outb(0x0A, 0x21); /* level triggered, Cascade (slave) on IRQ2 */
-#endif
- outb(0x01, 0xA1); /* Select 8086 mode */
- outb(0xFF, 0xA1); /* Mask all */
+ if ( _machine == _MACH_Pmac )
+ {
+ mask_and_ack_irq = pmac_mask_and_ack_irq;
+ set_irq_mask = pmac_set_irq_mask;
+
+ *IRQ_ENABLE = 0;
+#ifdef CONFIG_XMON
+ request_irq(KEYBOARD_IRQ, xmon_irq, 0, "NMI", 0);
+#endif /* CONFIG_XMON */
+ }
+ else /* prep */
+ {
+ mask_and_ack_irq = prep_mask_and_ack_irq;
+ set_irq_mask = prep_set_irq_mask;
+
+ /* init master interrupt controller */
+ outb(0x11, 0x20); /* Start init sequence */
+ outb(0x40, 0x21); /* Vector base */
+ /*outb(0x04, 0x21);*/ /* edge tiggered, Cascade (slave) on IRQ2 */
+ outb(0x0C, 0x21); /* level triggered, Cascade (slave) on IRQ2 */
+ outb(0x01, 0x21); /* Select 8086 mode */
+ outb(0xFF, 0x21); /* Mask all */
+
+ /* init slave interrupt controller */
+ outb(0x11, 0xA0); /* Start init sequence */
+ outb(0x48, 0xA1); /* Vector base */
+ /*outb(0x02, 0xA1);*/ /* edge triggered, Cascade (slave) on IRQ2 */
+ outb(0x0A, 0x21); /* level triggered, Cascade (slave) on IRQ2 */
+ outb(0x01, 0xA1); /* Select 8086 mode */
+ outb(0xFF, 0xA1); /* Mask all */
+
+ /*
+ * According to the Carolina spec from ibm irq's 0,1,2, and 8
+ * must be edge triggered. Also, the pci intrs must be level
+ * triggered and _only_ isa intrs can be level sensitive
+ * which are 3-7,9-12,14-15. 13 is special - it can be level.
+ *
+ * power on default is 0's in both regs - all edge.
+ *
+ * These edge/level control regs allow edge/level status
+ * to be decided on a irq basis instead of on a PIC basis.
+ * It's still pretty ugly.
+ * - Cort
+ */
+ {
+ unsigned char irq_mode1 = 0, irq_mode2 = 0;
+ irq_mode1 = 0; /* to get rid of compiler warnings */
+ /*
+ * On Carolina, irq 15 and 13 must be level (scsi/ide/net).
+ */
+ if ( _machine == _MACH_IBM )
+ irq_mode2 |= 0xa0;
+ /*
+ * Sound on the Powerstack reportedly needs to be edge triggered
+ */
+ if ( _machine == _MACH_Motorola )
+ {}
- /*
- * Program level mode for irq's that 'can' be level triggered.
- * This does not effect irq's 0,1,2 and 8 since they must be
- * edge triggered. This is not the PIC controller. The default
- * here is for edge trigger mode. -- Cort
- */
-#if 0
- outb(0xf8, 0x4d0); /* level triggered */
- outb(0xff, 0x4d1);
-#endif
-#if 0
- outb(0x00, 0x4D0); /* All edge triggered */
- outb(0xCF, 0x4D1); /* Trigger mode */
-#endif
- outb(cached_A1, 0xA1);
- outb(cached_21, 0x21);
- if (request_irq(2, null_handler, SA_INTERRUPT, "cascade", NULL))
- printk("Unable to get IRQ2 for cascade\n");
- enable_irq(2); /* Enable cascade interrupt */
-
-#define TIMER0_COUNT 0x40
-#define TIMER_CONTROL 0x43
- /* set timer to periodic mode */
- outb_p(0x34,TIMER_CONTROL); /* binary, mode 2, LSB/MSB, ch 0 */
- /* set the clock to ~100 Hz */
- outb_p(LATCH & 0xff , TIMER0_COUNT); /* LSB */
- outb(LATCH >> 8 , TIMER0_COUNT); /* MSB */
+ /*outb( irq_mode1 , 0x4d0 );
+ outb( irq_mode2 , 0x4d1 );*/
+ }
+ outb(cached_A1, 0xA1);
+ outb(cached_21, 0x21);
+ if (request_irq(2, no_action, SA_INTERRUPT, "cascade", NULL) != 0)
+ panic("Could not allocate cascade IRQ!");
+ enable_irq(2); /* Enable cascade interrupt */
- route_pci_interrupts();
-#endif /* CONFIG_PMAC */
+ route_pci_interrupts();
+ }
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov