[patch] Re: A question concerning time outs and possible lost interrupts

MOLNAR Ingo (mingo@chiara.csoma.elte.hu)
Tue, 15 Sep 1998 23:05:48 +0200 (CEST)


> - we can read the PIRQ routing table. Pros: it's easy to do. Cons: this
> is PCI chipset dependent. (although it seems to be a very standard
> register even present in the Neptune chipset, but still, i dont know
> wether this can be considered an 'architectural feature'.) Also, an
> inactive but PIRQ-routed PCI device (say a PCI video card sitting on
> IRQ9) shuts up an IRQ line that might be perfectly usable for an ISA
> device.

ok, it looks like there is a workaround similar to the above which works.
The solution is this: if the mptable has nothing sane to say about a PCI
interrupt, we 'clone' the ELCR setting, instead of looking at the PIRQ
routing table. The ELCR will be correct since otherwise windows/DOS
wouldnt work ...

the attached patch has been tested by Hubert Tonneau too who has one of
those 'problem boards'. Until this patch his system was not stable at all
under 2.1 and high load. I have tested the patch under two mptable
settings, and everything is stable.

[the only weakness i can see regarding this solution is that the ELCR port
is queried 'blindly', but even in this case we are mostly safe, a
nonexistent port produces 0xff which will 'force' all PCI interrupt lines
to be set up as level-triggered, which is a sane default. We could query
for the motherboard chipsets, but there are a gazillion ones ... The
offending code is never supposed to be executed on newer boards.]

[the naive workaround of 'setting all PCI devices to level-triggered' does
not work on Hubert's board, since this method misidentifies the polarity
of the chipset-internal EIDE controller, which is edge-triggered. But the
ELCR is correctly set so EIDE stays edge-triggered with the attached patch
even though it's a PCI device.]

the workaround can be disabled with a oneliner change. Whenever the
workaround kicks in it prints a warning into the syslog too, so if this
patch breaks any working system, it will not happen silently. The diff is
against 2.1.121.

-- mingo

--- linux/arch/i386/kernel/irq.h.orig4 Tue Sep 15 14:28:06 1998
+++ linux/arch/i386/kernel/irq.h Tue Sep 15 14:28:12 1998
@@ -60,7 +60,7 @@
int i8259A_irq_pending(unsigned int irq);
void ack_APIC_irq(void);
void setup_IO_APIC(void);
-int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn);
+int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn, int oldirq);
void make_8259A_irq(unsigned int irq);
void send_IPI(int dest, int vector);
void init_pic_mode(void);
--- linux/arch/i386/kernel/bios32.c.orig4 Tue Sep 15 13:52:26 1998
+++ linux/arch/i386/kernel/bios32.c Tue Sep 15 14:04:05 1998
@@ -1052,7 +1052,7 @@
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
if (pin) {
pin--; /* interrupt pins are numbered starting from 1 */
- irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);
+ irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin, dev->irq);
if (irq >= 0) {
printk("PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n",
dev->bus->number, PCI_SLOT(dev->devfn), pin, irq);
--- linux/arch/i386/kernel/io_apic.c.orig4 Tue Sep 15 14:04:13 1998
+++ linux/arch/i386/kernel/io_apic.c Tue Sep 15 19:47:49 1998
@@ -268,29 +268,6 @@
}

/*
- * Find a specific PCI IRQ entry.
- * Not an initfunc, possibly needed by modules
- */
-int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pci_pin)
-{
- int i;
-
- for (i = 0; i < mp_irq_entries; i++) {
- int lbus = mp_irqs[i].mpc_srcbus;
-
- if (IO_APIC_IRQ(mp_irqs[i].mpc_dstirq) &&
- (mp_bus_id_to_type[lbus] == MP_BUS_PCI) &&
- !mp_irqs[i].mpc_irqtype &&
- (bus == mp_bus_id_to_pci_bus[mp_irqs[i].mpc_srcbus]) &&
- (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f)) &&
- (pci_pin == (mp_irqs[i].mpc_srcbusirq & 3)))
-
- return mp_irqs[i].mpc_dstirq;
- }
- return -1;
-}
-
-/*
* There are broken mptables which register ISA+high-active+level IRQs,
* these are illegal and are converted here to ISA+high-active+edge
* IRQ sources. Careful, ISA+low-active+level is another broken entry
@@ -499,7 +476,7 @@
return irq;
}

-static inline int IO_APIC_irq_trigger(int irq)
+static int IO_APIC_irq_trigger(int irq)
{
int idx, pin;

@@ -1077,7 +1054,80 @@
disable_level_ioapic_irq
};

-static inline void init_IO_APIC_traps(void)
+static void clone_ELCR (int irq)
+{
+ int pin;
+ unsigned int elcr;
+ int trigger, ioapic_trigger;
+ struct IO_APIC_route_entry entry;
+
+ elcr = inb(0x4D0) | (inb(0x4D1)<<8 );
+ trigger = elcr & (1<<irq) ? 1 : 0;
+ ioapic_trigger = irq_desc[irq].handler==&ioapic_level_irq_type ? 1 : 0;
+
+ if (ioapic_trigger == trigger)
+ return;
+
+ /*
+ * ok, from this point on we can be sure that:
+ * 1) it's a PCI interrupt queried first time by a PCI driver
+ * 2) it's not an entry in the MP-BIOS having PCI bus type
+ * 3) it's doesnt have the same trigger as in the ELCR => bad
+ */
+ pin = irq_2_pin[irq];
+
+ if (pin == -1)
+ return;
+
+ printk("buggy BIOS, changing IRQ%d to %s triggered ...\n",
+ irq, trigger ? "level" : "edge");
+
+ *(((int *)&entry) + 0) = io_apic_read(0x10 + pin * 2);
+ *(((int *)&entry) + 1) = io_apic_read(0x11 + pin * 2);
+
+ entry.trigger = trigger;
+ irq_desc[irq].handler = &ioapic_level_irq_type;
+
+ io_apic_write(0x10+2*pin, *(((int *)&entry)+0));
+ io_apic_write(0x11+2*pin, *(((int *)&entry)+1));
+
+ io_apic_sync();
+}
+
+/*
+ * Find a specific PCI IRQ entry.
+ * Not an initfunc, possibly needed by modules
+ */
+int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pci_pin, int oldirq)
+{
+ int i;
+
+ for (i = 0; i < mp_irq_entries; i++) {
+ int lbus = mp_irqs[i].mpc_srcbus;
+
+ if (IO_APIC_IRQ(mp_irqs[i].mpc_dstirq) &&
+ (mp_bus_id_to_type[lbus] == MP_BUS_PCI) &&
+ !mp_irqs[i].mpc_irqtype &&
+ (bus == mp_bus_id_to_pci_bus[mp_irqs[i].mpc_srcbus]) &&
+ (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f)) &&
+ (pci_pin == (mp_irqs[i].mpc_srcbusirq & 3)))
+
+ return mp_irqs[i].mpc_dstirq;
+ }
+ /*
+ * PCI interrupt 'embedded' into ISA space ... clone the ELCR bit
+ * for trigger because we cannot always trust the BIOS getting it
+ * right for us, sigh.
+ *
+ * The ELCR registers are only available on Intel chipsets, but
+ * this bug concerns only some older Intel chipset boards.
+ */
+ clone_ELCR(oldirq);
+
+ return -1;
+}
+
+static void init_IO_APIC_traps(void)
{
int i;
/*

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/