summaryrefslogtreecommitdiffstats
path: root/arch/i386/kernel/irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/i386/kernel/irq.c')
-rw-r--r--arch/i386/kernel/irq.c204
1 files changed, 153 insertions, 51 deletions
diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c
index 2b8b86cc7..fddc57c1f 100644
--- a/arch/i386/kernel/irq.c
+++ b/arch/i386/kernel/irq.c
@@ -70,9 +70,8 @@ spinlock_t irq_controller_lock;
/*
* Not all IRQs can be routed through the IO-APIC, eg. on certain (older)
- * boards the timer interrupt and sometimes the keyboard interrupt is
- * not connected to any IO-APIC pin, it's fed to the CPU ExtInt IRQ line
- * directly.
+ * 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,
@@ -82,11 +81,8 @@ spinlock_t irq_controller_lock;
/*
* Default to all normal IRQ's _not_ using the IO APIC.
*
- * To get IO-APIC interrupts you should either:
- * - turn some of them into IO-APIC interrupts at runtime
- * with some magic system call interface.
- * - explicitly use irq 16-19 depending on which PCI irq
- * line your PCI controller uses.
+ * To get IO-APIC interrupts we turn some of them into IO-APIC
+ * interrupts during boot.
*/
unsigned int io_apic_irqs = 0;
@@ -109,15 +105,34 @@ static struct hw_interrupt_type i8259A_irq_type = {
#ifdef __SMP__
-static void do_ioapic_IRQ (unsigned int irq, int cpu, struct pt_regs * regs);
-static void enable_ioapic_irq (unsigned int irq);
-static void disable_ioapic_irq (unsigned int irq);
-
-static struct hw_interrupt_type ioapic_irq_type = {
- do_ioapic_IRQ,
- enable_ioapic_irq,
- disable_ioapic_irq
+
+/*
+ * Level and edge triggered IO-APIC interrupts need different handling,
+ * so we use two separate irq descriptors:
+ */
+
+static void do_edge_ioapic_IRQ (unsigned int irq, int cpu,
+ struct pt_regs * regs);
+static void enable_edge_ioapic_irq (unsigned int irq);
+static void disable_edge_ioapic_irq (unsigned int irq);
+
+static struct hw_interrupt_type ioapic_edge_irq_type = {
+ do_edge_ioapic_IRQ,
+ enable_edge_ioapic_irq,
+ disable_edge_ioapic_irq
+};
+
+static void do_level_ioapic_IRQ (unsigned int irq, int cpu,
+ struct pt_regs * regs);
+static void enable_level_ioapic_irq (unsigned int irq);
+static void disable_level_ioapic_irq (unsigned int irq);
+
+static struct hw_interrupt_type ioapic_level_irq_type = {
+ do_level_ioapic_IRQ,
+ enable_level_ioapic_irq,
+ disable_level_ioapic_irq
};
+
#endif
/*
@@ -147,7 +162,7 @@ typedef struct {
irq_desc_t irq_desc[NR_IRQS] = {
[0 ... 15] = { 0, 0, 0, &i8259A_irq_type, }, /* standard ISA IRQs */
#ifdef __SMP__
- [16 ... 23] = { 0, 0, 0, &ioapic_irq_type, }, /* 'high' PCI IRQs */
+ [16 ... 23] = { 0, 0, 0, &ioapic_edge_irq_type, }, /* 'high' PCI IRQs */
#endif
};
@@ -341,11 +356,16 @@ int get_irq_list(char *buf)
p += sprintf(p, "%10u ",
kstat.irqs[cpu_logical_map(j)][i]);
#endif
-
- if (IO_APIC_IRQ(i))
- p += sprintf(p, " IO-APIC ");
- else
- p += sprintf(p, " XT-PIC ");
+ if (IO_APIC_IRQ(i)) {
+ p += sprintf(p, " IO-APIC");
+#ifdef __SMP__
+ if (irq_desc[i].handler == &ioapic_level_irq_type)
+ p += sprintf(p, "-level ");
+ else
+ p += sprintf(p, "-edge ");
+#endif
+ } else
+ p += sprintf(p, " XT-PIC ");
p += sprintf(p, " %s", action->name);
for (action=action->next; action; action = action->next) {
@@ -732,38 +752,53 @@ static void do_8259A_IRQ(unsigned int irq, int cpu, struct pt_regs * regs)
* better to do it this way as thus we dont have to be aware of
* 'pending' interrupts in the IRQ path, except at this point.
*/
-static void enable_ioapic_irq(unsigned int irq)
+static inline void self_IPI (unsigned int irq)
{
irq_desc_t *desc = irq_desc + irq;
+
if (desc->events && !desc->ipi) {
desc->ipi = 1;
send_IPI(APIC_DEST_SELF, IO_APIC_VECTOR(irq));
}
}
-/*
- * We do not actually disable IO-APIC irqs in hardware ...
- */
-static void disable_ioapic_irq(unsigned int irq)
+static void enable_edge_ioapic_irq(unsigned int irq)
{
+ self_IPI(irq);
}
-static void do_ioapic_IRQ(unsigned int irq, int cpu, struct pt_regs * regs)
+static void disable_edge_ioapic_irq(unsigned int irq)
{
- irq_desc_t *desc = irq_desc + irq;
+}
- spin_lock(&irq_controller_lock);
+/*
+ * if we enable this, why does it cause a hang in the BusLogic
+ * driver, when level triggered PCI IRQs are used?
+ */
+#define NOT_BROKEN 0
- /* Ack the irq inside the lock! */
- ack_APIC_irq();
- desc->ipi = 0;
+static void enable_level_ioapic_irq(unsigned int irq)
+{
+#if NOT_BROKEN
+ enable_IO_APIC_irq(irq);
+#endif
+ self_IPI(irq);
+}
- /* If the irq is disabled for whatever reason, just set a flag and return */
- if (desc->status & (IRQ_DISABLED | IRQ_INPROGRESS)) {
- desc->events = 1;
- spin_unlock(&irq_controller_lock);
- return;
- }
+static void disable_level_ioapic_irq(unsigned int irq)
+{
+#if NOT_BROKEN
+ disable_IO_APIC_irq(irq);
+#endif
+}
+
+/*
+ * Has to be called with the irq controller locked
+ */
+static void handle_ioapic_event (unsigned int irq, int cpu,
+ struct pt_regs * regs)
+{
+ irq_desc_t *desc = irq_desc + irq;
desc->status = IRQ_INPROGRESS;
desc->events = 0;
@@ -790,6 +825,65 @@ static void do_ioapic_IRQ(unsigned int irq, int cpu, struct pt_regs * regs)
spin_unlock(&irq_controller_lock);
no_handler:
+}
+
+static void do_edge_ioapic_IRQ(unsigned int irq, int cpu, struct pt_regs * regs)
+{
+ irq_desc_t *desc = irq_desc + irq;
+
+ /*
+ * Edge triggered IRQs can be acked immediately
+ */
+ ack_APIC_irq();
+
+ spin_lock(&irq_controller_lock);
+ desc->ipi = 0;
+
+ /*
+ * If the irq is disabled for whatever reason, just
+ * set a flag and return
+ */
+ if (desc->status & (IRQ_DISABLED | IRQ_INPROGRESS)) {
+ desc->events = 1;
+ spin_unlock(&irq_controller_lock);
+ return;
+ }
+
+ handle_ioapic_event(irq,cpu,regs);
+
+ hardirq_exit(cpu);
+ release_irqlock(cpu);
+}
+
+static void do_level_ioapic_IRQ (unsigned int irq, int cpu,
+ struct pt_regs * regs)
+{
+ irq_desc_t *desc = irq_desc + irq;
+
+ spin_lock(&irq_controller_lock);
+ /*
+ * in the level triggered case we first disable the IRQ
+ * in the IO-APIC, then we 'early ACK' the IRQ, then we
+ * handle it and enable the IRQ when finished.
+ */
+#if NOT_BROKEN
+ disable_IO_APIC_irq(irq);
+#endif
+ ack_APIC_irq();
+ desc->ipi = 0;
+
+ /*
+ * If the irq is disabled for whatever reason, just
+ * set a flag and return
+ */
+ if (desc->status & (IRQ_DISABLED | IRQ_INPROGRESS)) {
+ desc->events = 1;
+ spin_unlock(&irq_controller_lock);
+ return;
+ }
+
+ handle_ioapic_event(irq,cpu,regs);
+
hardirq_exit(cpu);
release_irqlock(cpu);
}
@@ -912,7 +1006,12 @@ int setup_x86_irq(unsigned int irq, struct irqaction * new)
spin_lock(&irq_controller_lock);
#ifdef __SMP__
if (IO_APIC_IRQ(irq)) {
- irq_desc[irq].handler = &ioapic_irq_type;
+ if (IO_APIC_VECTOR(irq) > 0xfe)
+ /*
+ * break visibly for now, FIXME
+ */
+ panic("ayiee, tell mingo");
+
/*
* First disable it in the 8259A:
*/
@@ -1066,18 +1165,21 @@ void init_IO_APIC_traps(void)
* also, we've got to be careful not to trash gate
* 0x80, because int 0x80 is hm, kindof importantish ;)
*/
- for (i = 0; i < NR_IRQS ; i++)
- if (IO_APIC_VECTOR(i) <= 0xfe) /* HACK */ {
- if (IO_APIC_IRQ(i)) {
- irq_desc[i].handler = &ioapic_irq_type;
- /*
- * First disable it in the 8259A:
- */
- cached_irq_mask |= 1 << i;
- if (i < 16)
- set_8259A_irq_mask(i);
- }
+ for (i = 0; i < NR_IRQS ; i++) {
+ if ((IO_APIC_VECTOR(i) <= 0xfe) /* HACK */ &&
+ (IO_APIC_IRQ(i))) {
+ if (IO_APIC_irq_trigger(i))
+ irq_desc[i].handler = &ioapic_level_irq_type;
+ else
+ irq_desc[i].handler = &ioapic_edge_irq_type;
+ /*
+ * disable it in the 8259A:
+ */
+ cached_irq_mask |= 1 << i;
+ if (i < 16)
+ set_8259A_irq_mask(i);
}
+ }
}
#endif