patch-2.2.0-pre9 linux/arch/i386/kernel/irq.c

Next file: linux/arch/i386/kernel/irq.h
Previous file: linux/arch/i386/kernel/i386_ksyms.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.0-pre8/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
@@ -15,6 +15,7 @@
  * Naturally it's not a 1:1 relation, but there are similarities.
  */
 
+#include <linux/config.h>
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/kernel_stat.h>
@@ -47,46 +48,28 @@
 atomic_t nmi_counter;
 
 /*
- * About the IO-APIC, the architecture is 'merged' into our
- * current irq architecture, seemlessly. (i hope). It is only
- * visible through a few more more hardware interrupt lines, but 
- * otherwise drivers are unaffected. The main code is believed
- * to be NR_IRQS-safe (nothing anymore thinks we have 16
- * irq lines only), but there might be some places left ...
+ * Linux has a controller-independent x86 interrupt architecture.
+ * every controller has a 'controller-template', that is used
+ * by the main code to do the right thing. Each driver-visible
+ * interrupt source is transparently wired to the apropriate
+ * controller. Thus drivers need not be aware of the
+ * interrupt-controller.
+ *
+ * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC,
+ * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC.
+ * (IO-APICs assumed to be messaging to Pentium local-APICs)
+ *
+ * the code is designed to be easily extended with new/different
+ * interrupt controllers, without having to do assembly magic.
  */
 
 /*
- * This contains the irq mask for both 8259A irq controllers,
+ * Micro-access to controllers is serialized over the whole
+ * system. We never hold this lock when we call the actual
+ * IRQ handler.
  */
-static unsigned int cached_irq_mask = 0xffff;
-
-#define __byte(x,y) (((unsigned char *)&(y))[x])
-#define __word(x,y) (((unsigned short *)&(y))[x])
-#define __long(x,y) (((unsigned int *)&(y))[x])
-
-#define cached_21	(__byte(0,cached_irq_mask))
-#define cached_A1	(__byte(1,cached_irq_mask))
-
 spinlock_t irq_controller_lock;
 
-/*
- * Not all IRQs can be routed through the IO-APIC, eg. on certain (older)
- * boards the timer interrupt is not connected to any IO-APIC pin, it's
- * fed to the CPU IRQ line directly.
- *
- * Any '1' bit in this mask means the IRQ is routed through the IO-APIC.
- * this 'mixed mode' IRQ handling costs us one more branch in do_IRQ,
- * but we have _much_ higher compatibility and robustness this way.
- */
-unsigned long long io_apic_irqs = 0;
-
-static void do_8259A_IRQ(unsigned int irq, struct pt_regs * regs);
-static void enable_8259A_irq(unsigned int irq);
-void disable_8259A_irq(unsigned int irq);
-
-/* startup is the same as "enable", shutdown is same as "disable" */
-#define startup_8259A_irq	enable_8259A_irq
-#define shutdown_8259A_irq	disable_8259A_irq
 
 /*
  * Dummy controller type for unused interrupts
@@ -108,6 +91,19 @@
 	disable_none
 };
 
+/*
+ * This is the 'legacy' 8259A Programmable Interrupt Controller,
+ * present in the majority of PC/AT boxes.
+ */
+
+static void do_8259A_IRQ(unsigned int irq, struct pt_regs * regs);
+static void enable_8259A_irq(unsigned int irq);
+void disable_8259A_irq(unsigned int irq);
+
+/* startup is the same as "enable", shutdown is same as "disable" */
+#define startup_8259A_irq	enable_8259A_irq
+#define shutdown_8259A_irq	disable_8259A_irq
+
 static struct hw_interrupt_type i8259A_irq_type = {
 	"XT-PIC",
 	startup_8259A_irq,
@@ -117,11 +113,38 @@
 	disable_8259A_irq
 };
 
-irq_desc_t irq_desc[NR_IRQS] = {
-	[0 ... 15] = { 0, &i8259A_irq_type, },		/* default to standard ISA IRQs */
-	[16 ... NR_IRQS-1] = { 0, &no_irq_type, },	/* 'high' PCI IRQs filled in on demand */
-};
+/*
+ * Controller mappings for all interrupt sources:
+ */
+irq_desc_t irq_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = { 0, &no_irq_type, }};
+
+
+/*
+ * 8259A PIC functions to handle ISA devices:
+ */
+
+/*
+ * This contains the irq mask for both 8259A irq controllers,
+ */
+static unsigned int cached_irq_mask = 0xffff;
+
+#define __byte(x,y) (((unsigned char *)&(y))[x])
+#define __word(x,y) (((unsigned short *)&(y))[x])
+#define __long(x,y) (((unsigned int *)&(y))[x])
+
+#define cached_21	(__byte(0,cached_irq_mask))
+#define cached_A1	(__byte(1,cached_irq_mask))
 
+/*
+ * Not all IRQs can be routed through the IO-APIC, eg. on certain (older)
+ * boards the timer interrupt is not connected to any IO-APIC pin, it's
+ * fed to the CPU IRQ line directly.
+ *
+ * Any '1' bit in this mask means the IRQ is routed through the IO-APIC.
+ * this 'mixed mode' IRQ handling costs us one more branch in do_IRQ,
+ * but we have _much_ higher compatibility and robustness this way.
+ */
+unsigned long long io_apic_irqs = 0;
 
 /*
  * These have to be protected by the irq controller spinlock
@@ -149,6 +172,77 @@
 	}
 }
 
+int i8259A_irq_pending(unsigned int irq)
+{
+	unsigned int mask = 1<<irq;
+
+	if (irq < 8)
+                return (inb(0x20) & mask);
+        return (inb(0xA0) & (mask >> 8));
+}
+
+void make_8259A_irq(unsigned int irq)
+{
+	disable_irq(irq);
+	__long(0,io_apic_irqs) &= ~(1<<irq);
+	irq_desc[irq].handler = &i8259A_irq_type;
+	enable_irq(irq);
+}
+
+/*
+ * Careful! The 8259A is a fragile beast, it pretty
+ * much _has_ to be done exactly like this (mask it
+ * first, _then_ send the EOI, and the order of EOI
+ * to the two 8259s is important!
+ */
+static inline void mask_and_ack_8259A(unsigned int irq)
+{
+	cached_irq_mask |= 1 << irq;
+	if (irq & 8) {
+		inb(0xA1);	/* DUMMY */
+		outb(cached_A1,0xA1);
+		outb(0x62,0x20);	/* Specific EOI to cascade */
+		outb(0x20,0xA0);
+	} else {
+		inb(0x21);	/* DUMMY */
+		outb(cached_21,0x21);
+		outb(0x20,0x20);
+	}
+}
+
+static void do_8259A_IRQ(unsigned int irq, struct pt_regs * regs)
+{
+	struct irqaction * action;
+	irq_desc_t *desc = irq_desc + irq;
+
+	spin_lock(&irq_controller_lock);
+	{
+		unsigned int status;
+		mask_and_ack_8259A(irq);
+		status = desc->status & ~IRQ_REPLAY;
+		action = NULL;
+		if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+			action = desc->action;
+		desc->status = status | IRQ_INPROGRESS;
+	}
+	spin_unlock(&irq_controller_lock);
+
+	/* Exit early if we had no action or it was disabled */
+	if (!action)
+		return;
+
+	handle_IRQ_event(irq, regs, action);
+
+	spin_lock(&irq_controller_lock);
+	{
+		unsigned int status = desc->status & ~IRQ_INPROGRESS;
+		desc->status = status;
+		if (!(status & IRQ_DISABLED))
+			enable_8259A_irq(irq);
+	}
+	spin_unlock(&irq_controller_lock);
+}
+
 /*
  * This builds up the IRQ handler stubs using some ugly macros in irq.h
  *
@@ -168,8 +262,7 @@
 BUILD_IRQ(8)  BUILD_IRQ(9)  BUILD_IRQ(10) BUILD_IRQ(11)
 BUILD_IRQ(12) BUILD_IRQ(13) BUILD_IRQ(14) BUILD_IRQ(15)
 
-#ifdef __SMP__
-
+#ifdef CONFIG_X86_IO_APIC
 /*
  * The IO-APIC gives us many more interrupt sources..
  */
@@ -185,7 +278,9 @@
 BUILD_IRQ(52) BUILD_IRQ(53) BUILD_IRQ(54) BUILD_IRQ(55)
 BUILD_IRQ(56) BUILD_IRQ(57) BUILD_IRQ(58) BUILD_IRQ(59)
 BUILD_IRQ(60) BUILD_IRQ(61) BUILD_IRQ(62) BUILD_IRQ(63)
+#endif
 
+#ifdef __SMP__
 /*
  * The following vectors are part of the Linux architecture, there
  * is no hardware IRQ pin equivalent for them, they are triggered
@@ -213,7 +308,7 @@
 	IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt,
 	IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt,
 	IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt
-#ifdef __SMP__
+#ifdef CONFIG_X86_IO_APIC
 	,IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt,
 	IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt,
 	IRQ24_interrupt, IRQ25_interrupt, IRQ26_interrupt, IRQ27_interrupt,
@@ -231,12 +326,16 @@
 #endif
 };
 
+
 /*
  * Initial irq handlers.
  */
 
-static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
+void no_action(int cpl, void *dev_id, struct pt_regs *regs)
+{
+}
 
+#ifndef CONFIG_VISWS
 /*
  * Note that on a 486, we don't want to do a SIGFPE on an irq13
  * as the irq is unreliable, and exception 16 works correctly
@@ -262,7 +361,13 @@
 /*
  * IRQ2 is cascade interrupt to second interrupt controller
  */
+
 static struct irqaction irq2  = { no_action, 0, 0, "cascade", NULL, NULL};
+#endif
+
+/*
+ * Generic, controller-independent functions:
+ */
 
 int get_irq_list(char *buf)
 {
@@ -351,7 +456,6 @@
 	}
 }
 	
-
 #define MAXCOUNT 100000000
 
 static inline void wait_on_bh(void)
@@ -608,79 +712,6 @@
 	return status;
 }
 
-int i8259A_irq_pending(unsigned int irq)
-{
-	unsigned int mask = 1<<irq;
-
-	if (irq < 8)
-                return (inb(0x20) & mask);
-        return (inb(0xA0) & (mask >> 8));
-}
-
-
-void make_8259A_irq(unsigned int irq)
-{
-	disable_irq(irq);
-	__long(0,io_apic_irqs) &= ~(1<<irq);
-	irq_desc[irq].handler = &i8259A_irq_type;
-	enable_irq(irq);
-}
-
-/*
- * Careful! The 8259A is a fragile beast, it pretty
- * much _has_ to be done exactly like this (mask it
- * first, _then_ send the EOI, and the order of EOI
- * to the two 8259s is important!
- */
-static inline void mask_and_ack_8259A(unsigned int irq)
-{
-	cached_irq_mask |= 1 << irq;
-	if (irq & 8) {
-		inb(0xA1);	/* DUMMY */
-		outb(cached_A1,0xA1);
-		outb(0x62,0x20);	/* Specific EOI to cascade */
-		outb(0x20,0xA0);
-	} else {
-		inb(0x21);	/* DUMMY */
-		outb(cached_21,0x21);
-		outb(0x20,0x20);
-	}
-}
-
-static void do_8259A_IRQ(unsigned int irq, struct pt_regs * regs)
-{
-	struct irqaction * action;
-	irq_desc_t *desc = irq_desc + irq;
-
-	spin_lock(&irq_controller_lock);
-	{
-		unsigned int status;
-		mask_and_ack_8259A(irq);
-		status = desc->status & ~IRQ_REPLAY;
-		action = NULL;
-		if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))
-			action = desc->action;
-		desc->status = status | IRQ_INPROGRESS;
-	}
-	spin_unlock(&irq_controller_lock);
-
-	/* Exit early if we had no action or it was disabled */
-	if (!action)
-		return;
-
-	handle_IRQ_event(irq, regs, action);
-
-	spin_lock(&irq_controller_lock);
-	{
-		unsigned int status = desc->status & ~IRQ_INPROGRESS;
-		desc->status = status;
-		if (!(status & IRQ_DISABLED))
-			enable_8259A_irq(irq);
-	}
-	spin_unlock(&irq_controller_lock);
-}
-
-
 /*
  * Generic enable/disable code: this just calls
  * down into the PIC-specific version for the actual
@@ -955,21 +986,75 @@
 	return irq_found;
 }
 
-__initfunc(void init_IRQ(void))
+/*
+ * Silly, horrible hack
+ */
+static char uglybuffer[10*256];
+
+__asm__("\n" __ALIGN_STR"\n"
+	"common_unexpected:\n\t"
+	SAVE_ALL
+	"pushl $ret_from_intr\n\t"
+	"jmp strange_interrupt");
+
+void strange_interrupt(int irqnum)
+{
+	printk("Unexpected interrupt %d\n", irqnum & 255);
+	for (;;);
+}
+
+extern int common_unexpected;
+__initfunc(void init_unexpected_irq(void))
 {
 	int i;
+	for (i = 0; i < 256; i++) {
+		char *code = uglybuffer + 10*i;
+		unsigned long jumpto = (unsigned long) &common_unexpected;
+
+		jumpto -= (unsigned long)(code+10);
+		code[0] = 0x68;		/* pushl */
+		*(int *)(code+1) = i - 512;
+		code[5] = 0xe9;		/* jmp */
+		*(int *)(code+6) = jumpto;
+
+		set_intr_gate(i,code);
+	}
+}
 
-	/* 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 */
 
-	for (i=0; i<NR_IRQS; i++)
+void init_ISA_irqs (void)
+{
+	int i;
+
+	for (i = 0; i < NR_IRQS; i++) {
 		irq_desc[i].status = IRQ_DISABLED;
+		irq_desc[i].action = 0;
+		irq_desc[i].depth = 0;
+
+		if (i < 16) {
+			/*
+			 * 16 old-style INTA-cycle interrupt gates:
+			 */
+			irq_desc[i].handler = &i8259A_irq_type;
+		} else {
+			/*
+			 * 'high' PCI IRQs filled in on demand
+			 */
+			irq_desc[i].handler = &no_irq_type;
+		}
+	}
+}
+
+__initfunc(void init_IRQ(void))
+{
+	int i;
+
+#ifndef CONFIG_X86_VISWS_APIC
+	init_ISA_irqs();
+#else
+	init_VISWS_APIC_irqs();
+#endif
 
-	/*
-	 * 16 old-style INTA-cycle interrupt gates:
-	 */
 	for (i = 0; i < 16; i++)
 		set_intr_gate(0x20+i,interrupt[i]);
 
@@ -1008,12 +1093,22 @@
 #endif	
 	request_region(0x20,0x20,"pic1");
 	request_region(0xa0,0x20,"pic2");
+
+	/*
+	 * Set the clock to 100 Hz, we already have a valid
+	 * vector now:
+	 */
+	outb_p(0x34,0x43);		/* binary, mode 2, LSB/MSB, ch 0 */
+	outb_p(LATCH & 0xff , 0x40);	/* LSB */
+	outb(LATCH >> 8 , 0x40);	/* MSB */
+
+#ifndef CONFIG_VISWS
 	setup_x86_irq(2, &irq2);
 	setup_x86_irq(13, &irq13);
+#endif
 }
 
-#ifdef __SMP__
-
+#ifdef CONFIG_X86_IO_APIC
 __initfunc(void init_IRQ_SMP(void))
 {
 	int i;
@@ -1021,5 +1116,5 @@
 		if (IO_APIC_VECTOR(i) > 0)
 			set_intr_gate(IO_APIC_VECTOR(i), interrupt[i]);
 }
-
 #endif
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov