Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / arch / i386 / pci / irq.c
index e29b67b..06dab00 100644 (file)
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
-#include <linux/irq.h>
+#include <linux/dmi.h>
 #include <asm/io.h>
 #include <asm/smp.h>
 #include <asm/io_apic.h>
-#include <asm/hw_irq.h>
+#include <linux/irq.h>
+#include <linux/acpi.h>
 
 #include "pci.h"
 
 #define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24))
 #define PIRQ_VERSION 0x0100
 
-int broken_hp_bios_irq9;
+static int broken_hp_bios_irq9;
+static int acer_tm360_irqrouting;
 
 static struct irq_routing_table *pirq_table;
 
+static int pirq_enable_irq(struct pci_dev *dev);
+
 /*
  * Never use: 0, 1, 2 (timer, keyboard, and cascade)
  * Avoid using: 13, 14 and 15 (FP error and IDE).
@@ -51,6 +55,36 @@ struct irq_router_handler {
 };
 
 int (*pcibios_enable_irq)(struct pci_dev *dev) = NULL;
+void (*pcibios_disable_irq)(struct pci_dev *dev) = NULL;
+
+/*
+ *  Check passed address for the PCI IRQ Routing Table signature
+ *  and perform checksum verification.
+ */
+
+static inline struct irq_routing_table * pirq_check_routing_table(u8 *addr)
+{
+       struct irq_routing_table *rt;
+       int i;
+       u8 sum;
+
+       rt = (struct irq_routing_table *) addr;
+       if (rt->signature != PIRQ_SIGNATURE ||
+           rt->version != PIRQ_VERSION ||
+           rt->size % 16 ||
+           rt->size < sizeof(struct irq_routing_table))
+               return NULL;
+       sum = 0;
+       for (i=0; i < rt->size; i++)
+               sum += addr[i];
+       if (!sum) {
+               DBG(KERN_DEBUG "PCI: Interrupt Routing Table found at 0x%p\n", rt);
+               return rt;
+       }
+       return NULL;
+}
+
+
 
 /*
  *  Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table.
@@ -60,23 +94,17 @@ static struct irq_routing_table * __init pirq_find_routing_table(void)
 {
        u8 *addr;
        struct irq_routing_table *rt;
-       int i;
-       u8 sum;
 
+       if (pirq_table_addr) {
+               rt = pirq_check_routing_table((u8 *) __va(pirq_table_addr));
+               if (rt)
+                       return rt;
+               printk(KERN_WARNING "PCI: PIRQ table NOT found at pirqaddr\n");
+       }
        for(addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) {
-               rt = (struct irq_routing_table *) addr;
-               if (rt->signature != PIRQ_SIGNATURE ||
-                   rt->version != PIRQ_VERSION ||
-                   rt->size % 16 ||
-                   rt->size < sizeof(struct irq_routing_table))
-                       continue;
-               sum = 0;
-               for(i=0; i<rt->size; i++)
-                       sum += addr[i];
-               if (!sum) {
-                       DBG("PCI: Interrupt Routing Table found at 0x%p\n", rt);
+               rt = pirq_check_routing_table(addr);
+               if (rt)
                        return rt;
-               }
        }
        return NULL;
 }
@@ -100,7 +128,7 @@ static void __init pirq_peer_trick(void)
 #ifdef DEBUG
                {
                        int j;
-                       DBG("%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot);
+                       DBG(KERN_DEBUG "%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot);
                        for(j=0; j<4; j++)
                                DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap);
                        DBG("\n");
@@ -125,10 +153,17 @@ void eisa_set_level_irq(unsigned int irq)
 {
        unsigned char mask = 1 << (irq & 7);
        unsigned int port = 0x4d0 + (irq >> 3);
-       unsigned char val = inb(port);
+       unsigned char val;
+       static u16 eisa_irq_mask;
 
+       if (irq >= 16 || (1 << irq) & eisa_irq_mask)
+               return;
+
+       eisa_irq_mask |= (1 << irq);
+       printk(KERN_DEBUG "PCI: setting IRQ %u as level-triggered\n", irq);
+       val = inb(port);
        if (!(val & mask)) {
-               DBG(" -> edge");
+               DBG(KERN_DEBUG " -> edge");
                outb(val | mask, port);
        }
 }
@@ -214,6 +249,24 @@ static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, i
        return 1;
 }
 
+/*
+ * The VIA pirq rules are nibble-based, like ALI,
+ * but without the ugly irq number munging.
+ * However, for 82C586, nibble map is different .
+ */
+static int pirq_via586_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
+{
+       static unsigned int pirqmap[4] = { 3, 2, 5, 1 };
+       return read_config_nybble(router, 0x55, pirqmap[pirq-1]);
+}
+
+static int pirq_via586_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+{
+       static unsigned int pirqmap[4] = { 3, 2, 5, 1 };
+       write_config_nybble(router, 0x55, pirqmap[pirq-1], irq);
+       return 1;
+}
+
 /*
  * ITE 8330G pirq rules are nibble-based
  * FIXME: pirqmap may be { 1, 0, 3, 2 },
@@ -450,14 +503,16 @@ static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq,
 
 #endif
 
-
 static __init int intel_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
 {
+       static struct pci_device_id pirq_440gx[] = {
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443GX_0) },
+               { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443GX_2) },
+               { },
+       };
+
        /* 440GX has a proprietary PIRQ router -- don't use it */
-       if (    pci_find_device(PCI_VENDOR_ID_INTEL,
-                               PCI_DEVICE_ID_INTEL_82443GX_0, NULL) ||
-               pci_find_device(PCI_VENDOR_ID_INTEL,
-                               PCI_DEVICE_ID_INTEL_82443GX_2, NULL))
+       if (pci_dev_present(pirq_440gx))
                return 0;
 
        switch(device)
@@ -479,6 +534,16 @@ static __init int intel_router_probe(struct irq_router *r, struct pci_dev *route
                case PCI_DEVICE_ID_INTEL_ESB_1:
                case PCI_DEVICE_ID_INTEL_ICH6_0:
                case PCI_DEVICE_ID_INTEL_ICH6_1:
+               case PCI_DEVICE_ID_INTEL_ICH7_0:
+               case PCI_DEVICE_ID_INTEL_ICH7_1:
+               case PCI_DEVICE_ID_INTEL_ICH7_30:
+               case PCI_DEVICE_ID_INTEL_ICH7_31:
+               case PCI_DEVICE_ID_INTEL_ESB2_0:
+               case PCI_DEVICE_ID_INTEL_ICH8_0:
+               case PCI_DEVICE_ID_INTEL_ICH8_1:
+               case PCI_DEVICE_ID_INTEL_ICH8_2:
+               case PCI_DEVICE_ID_INTEL_ICH8_3:
+               case PCI_DEVICE_ID_INTEL_ICH8_4:
                        r->name = "PIIX/ICH";
                        r->get = pirq_piix_get;
                        r->set = pirq_piix_set;
@@ -487,20 +552,50 @@ static __init int intel_router_probe(struct irq_router *r, struct pci_dev *route
        return 0;
 }
 
-static __init int via_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
+static __init int via_router_probe(struct irq_router *r,
+                               struct pci_dev *router, u16 device)
 {
        /* FIXME: We should move some of the quirk fixup stuff here */
-       switch(device)
-       {
-               case PCI_DEVICE_ID_VIA_82C586_0:
-               case PCI_DEVICE_ID_VIA_82C596:
+
+       /*
+        * work arounds for some buggy BIOSes
+        */
+       if (device == PCI_DEVICE_ID_VIA_82C586_0) {
+               switch(router->device) {
                case PCI_DEVICE_ID_VIA_82C686:
-               case PCI_DEVICE_ID_VIA_8231:
+                       /*
+                        * Asus k7m bios wrongly reports 82C686A
+                        * as 586-compatible
+                        */
+                       device = PCI_DEVICE_ID_VIA_82C686;
+                       break;
+               case PCI_DEVICE_ID_VIA_8235:
+                       /**
+                        * Asus a7v-x bios wrongly reports 8235
+                        * as 586-compatible
+                        */
+                       device = PCI_DEVICE_ID_VIA_8235;
+                       break;
+               }
+       }
+
+       switch(device) {
+       case PCI_DEVICE_ID_VIA_82C586_0:
+               r->name = "VIA";
+               r->get = pirq_via586_get;
+               r->set = pirq_via586_set;
+               return 1;
+       case PCI_DEVICE_ID_VIA_82C596:
+       case PCI_DEVICE_ID_VIA_82C686:
+       case PCI_DEVICE_ID_VIA_8231:
+       case PCI_DEVICE_ID_VIA_8233A:
+       case PCI_DEVICE_ID_VIA_8235:
+       case PCI_DEVICE_ID_VIA_8237:
                /* FIXME: add new ones for 8233/5 */
-                       r->name = "VIA";
-                       r->get = pirq_via_get;
-                       r->set = pirq_via_set;
-                       return 1;
+               r->name = "VIA";
+               r->get = pirq_via_get;
+               r->set = pirq_via_set;
+               return 1;
        }
        return 0;
 }
@@ -589,11 +684,11 @@ static __init int ali_router_probe(struct irq_router *r, struct pci_dev *router,
        {
        case PCI_DEVICE_ID_AL_M1533:
        case PCI_DEVICE_ID_AL_M1563:
-               printk("PCI: Using ALI IRQ Router\n");
-                       r->name = "ALI";
-                       r->get = pirq_ali_get;
-                       r->set = pirq_ali_set;
-                       return 1;
+               printk(KERN_DEBUG "PCI: Using ALI IRQ Router\n");
+               r->name = "ALI";
+               r->get = pirq_ali_get;
+               r->set = pirq_ali_set;
+               return 1;
        }
        return 0;
 }
@@ -661,12 +756,13 @@ static void __init pirq_find_router(struct irq_router *r)
        r->get = NULL;
        r->set = NULL;
        
-       DBG("PCI: Attempting to find IRQ router for %04x:%04x\n",
+       DBG(KERN_DEBUG "PCI: Attempting to find IRQ router for %04x:%04x\n",
            rt->rtr_vendor, rt->rtr_device);
 
        pirq_router_dev = pci_find_slot(rt->rtr_bus, rt->rtr_devfn);
        if (!pirq_router_dev) {
-               DBG("PCI: Interrupt router not found at %02x:%02x\n", rt->rtr_bus, rt->rtr_devfn);
+               DBG(KERN_DEBUG "PCI: Interrupt router not found at "
+                       "%02x:%02x\n", rt->rtr_bus, rt->rtr_devfn);
                return;
        }
 
@@ -711,7 +807,7 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
        /* Find IRQ pin */
        pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
        if (!pin) {
-               DBG(" -> no interrupt pin\n");
+               DBG(KERN_DEBUG " -> no interrupt pin\n");
                return 0;
        }
        pin = pin - 1;
@@ -721,16 +817,16 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
        if (!pirq_table)
                return 0;
        
-       DBG("IRQ for %s:%d", pci_name(dev), pin);
+       DBG(KERN_DEBUG "IRQ for %s[%c]", pci_name(dev), 'A' + pin);
        info = pirq_get_info(dev);
        if (!info) {
-               DBG(" -> not found in routing table\n");
+               DBG(" -> not found in routing table\n" KERN_DEBUG);
                return 0;
        }
        pirq = info->irq[pin].link;
        mask = info->irq[pin].bitmap;
        if (!pirq) {
-               DBG(" -> not routed\n");
+               DBG(" -> not routed\n" KERN_DEBUG);
                return 0;
        }
        DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs);
@@ -745,14 +841,25 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
                r->set(pirq_router_dev, dev, pirq, 11);
        }
 
+       /* same for Acer Travelmate 360, but with CB and irq 11 -> 10 */
+       if (acer_tm360_irqrouting && dev->irq == 11 && dev->vendor == PCI_VENDOR_ID_O2) {
+               pirq = 0x68;
+               mask = 0x400;
+               dev->irq = r->get(pirq_router_dev, dev, pirq);
+               pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+       }
+
        /*
         * Find the best IRQ to assign: use the one
         * reported by the device if possible.
         */
        newirq = dev->irq;
-       if (!((1 << newirq) & mask)) {
+       if (newirq && !((1 << newirq) & mask)) {
                if ( pci_probe & PCI_USE_PIRQ_MASK) newirq = 0;
-               else printk(KERN_WARNING "PCI: IRQ %i for device %s doesn't match PIRQ mask - try pci=usepirqmask\n", newirq, pci_name(dev));
+               else printk("\n" KERN_WARNING
+                       "PCI: IRQ %i for device %s doesn't match PIRQ mask "
+                       "- try pci=usepirqmask\n" KERN_DEBUG, newirq,
+                       pci_name(dev));
        }
        if (!newirq && assign) {
                for (i = 0; i < 16; i++) {
@@ -794,7 +901,7 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
        printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq, pci_name(dev));
 
        /* Update IRQ for all devices with the same pirq value */
-       while ((dev2 = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev2)) != NULL) {
+       while ((dev2 = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev2)) != NULL) {
                pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin);
                if (!pin)
                        continue;
@@ -807,7 +914,7 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
                        if ( dev2->irq && dev2->irq != irq && \
                        (!(pci_probe & PCI_USE_PIRQ_MASK) || \
                        ((1 << dev2->irq) & mask)) ) {
-#ifndef CONFIG_PCI_USE_VECTOR
+#ifndef CONFIG_PCI_MSI
                                printk(KERN_INFO "IRQ routing conflict for %s, have irq %d, want irq %d\n",
                                       pci_name(dev2), dev2->irq, irq);
 #endif
@@ -827,14 +934,14 @@ static void __init pcibios_fixup_irqs(void)
        struct pci_dev *dev = NULL;
        u8 pin;
 
-       DBG("PCI: IRQ fixup\n");
-       while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+       DBG(KERN_DEBUG "PCI: IRQ fixup\n");
+       while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
                /*
                 * If the BIOS has set an out of range IRQ number, just ignore it.
                 * Also keep track of which IRQ's are already in use.
                 */
                if (dev->irq >= 16) {
-                       DBG("%s: ignoring bogus IRQ %d\n", pci_name(dev), dev->irq);
+                       DBG(KERN_DEBUG "%s: ignoring bogus IRQ %d\n", pci_name(dev), dev->irq);
                        dev->irq = 0;
                }
                /* If the IRQ is already assigned to a PCI device, ignore its ISA use penalty */
@@ -844,7 +951,7 @@ static void __init pcibios_fixup_irqs(void)
        }
 
        dev = NULL;
-       while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+       while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
                pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
 #ifdef CONFIG_X86_IO_APIC
                /*
@@ -870,16 +977,16 @@ static void __init pcibios_fixup_irqs(void)
                                        irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, 
                                                        PCI_SLOT(bridge->devfn), pin);
                                        if (irq >= 0)
-                                               printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n", 
-                                                       bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq);
+                                               printk(KERN_WARNING "PCI: using PPB %s[%c] to get irq %d\n",
+                                                       pci_name(bridge), 'A' + pin, irq);
                                }
                                if (irq >= 0) {
                                        if (use_pci_vector() &&
                                                !platform_legacy_irq(irq))
                                                irq = IO_APIC_VECTOR(irq);
 
-                                       printk(KERN_INFO "PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n",
-                                               dev->bus->number, PCI_SLOT(dev->devfn), pin, irq);
+                                       printk(KERN_INFO "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n",
+                                               pci_name(dev), 'A' + pin, irq);
                                        dev->irq = irq;
                                }
                        }
@@ -893,13 +1000,63 @@ static void __init pcibios_fixup_irqs(void)
        }
 }
 
+/*
+ * Work around broken HP Pavilion Notebooks which assign USB to
+ * IRQ 9 even though it is actually wired to IRQ 11
+ */
+static int __init fix_broken_hp_bios_irq9(struct dmi_system_id *d)
+{
+       if (!broken_hp_bios_irq9) {
+               broken_hp_bios_irq9 = 1;
+               printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", d->ident);
+       }
+       return 0;
+}
+
+/*
+ * Work around broken Acer TravelMate 360 Notebooks which assign
+ * Cardbus to IRQ 11 even though it is actually wired to IRQ 10
+ */
+static int __init fix_acer_tm360_irqrouting(struct dmi_system_id *d)
+{
+       if (!acer_tm360_irqrouting) {
+               acer_tm360_irqrouting = 1;
+               printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", d->ident);
+       }
+       return 0;
+}
+
+static struct dmi_system_id __initdata pciirq_dmi_table[] = {
+       {
+               .callback = fix_broken_hp_bios_irq9,
+               .ident = "HP Pavilion N5400 Series Laptop",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+                       DMI_MATCH(DMI_BIOS_VERSION, "GE.M1.03"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook Model GE"),
+                       DMI_MATCH(DMI_BOARD_VERSION, "OmniBook N32N-736"),
+               },
+       },
+       {
+               .callback = fix_acer_tm360_irqrouting,
+               .ident = "Acer TravelMate 36x Laptop",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"),
+               },
+       },
+       { }
+};
+
 static int __init pcibios_irq_init(void)
 {
-       DBG("PCI: IRQ init\n");
+       DBG(KERN_DEBUG "PCI: IRQ init\n");
 
        if (pcibios_enable_irq || raw_pci_ops == NULL)
                return 0;
 
+       dmi_check_system(pciirq_dmi_table);
+
        pirq_table = pirq_find_routing_table();
 
 #ifdef CONFIG_PCI_BIOS
@@ -929,78 +1086,87 @@ static int __init pcibios_irq_init(void)
 subsys_initcall(pcibios_irq_init);
 
 
-void pcibios_penalize_isa_irq(int irq)
+static void pirq_penalize_isa_irq(int irq, int active)
 {
        /*
         *  If any ISAPnP device reports an IRQ in its list of possible
         *  IRQ's, we try to avoid assigning it to PCI devices.
         */
-       pirq_penalty[irq] += 100;
+       if (irq < 16) {
+               if (active)
+                       pirq_penalty[irq] += 1000;
+               else
+                       pirq_penalty[irq] += 100;
+       }
 }
 
-int pirq_enable_irq(struct pci_dev *dev)
+void pcibios_penalize_isa_irq(int irq, int active)
+{
+#ifdef CONFIG_ACPI
+       if (!acpi_noirq)
+               acpi_penalize_isa_irq(irq, active);
+       else
+#endif
+               pirq_penalize_isa_irq(irq, active);
+}
+
+static int pirq_enable_irq(struct pci_dev *dev)
 {
        u8 pin;
-       extern int interrupt_line_quirk;
        struct pci_dev *temp_dev;
 
        pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
        if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) {
-               char *msg;
-               msg = "";
+               char *msg = "";
+
+               pin--;          /* interrupt pins are numbered starting from 1 */
+
                if (io_apic_assign_pci_irqs) {
                        int irq;
 
-                       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);
-                               /*
-                                * Busses behind bridges are typically not listed in the MP-table.
-                                * In this case we have to look up the IRQ based on the parent bus,
-                                * parent slot, and pin number. The SMP code detects such bridged
-                                * busses itself so we should get into this branch reliably.
-                                */
-                               temp_dev = dev;
-                               while (irq < 0 && dev->bus->parent) { /* go back to the bridge */
-                                       struct pci_dev * bridge = dev->bus->self;
-
-                                       pin = (pin + PCI_SLOT(dev->devfn)) % 4;
-                                       irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, 
-                                                       PCI_SLOT(bridge->devfn), pin);
-                                       if (irq >= 0)
-                                               printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n", 
-                                                       bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq);
-                                       dev = bridge;
-                               }
-                               dev = temp_dev;
-                               if (irq >= 0) {
-#ifdef CONFIG_PCI_USE_VECTOR
-                                       if (!platform_legacy_irq(irq))
-                                               irq = IO_APIC_VECTOR(irq);
-#endif
-                                       printk(KERN_INFO "PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n",
-                                               dev->bus->number, PCI_SLOT(dev->devfn), pin, irq);
-                                       dev->irq = irq;
-                                       return 0;
-                               } else
-                                       msg = " Probably buggy MP table.";
+                       irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);
+                       /*
+                        * Busses behind bridges are typically not listed in the MP-table.
+                        * In this case we have to look up the IRQ based on the parent bus,
+                        * parent slot, and pin number. The SMP code detects such bridged
+                        * busses itself so we should get into this branch reliably.
+                        */
+                       temp_dev = dev;
+                       while (irq < 0 && dev->bus->parent) { /* go back to the bridge */
+                               struct pci_dev * bridge = dev->bus->self;
+
+                               pin = (pin + PCI_SLOT(dev->devfn)) % 4;
+                               irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, 
+                                               PCI_SLOT(bridge->devfn), pin);
+                               if (irq >= 0)
+                                       printk(KERN_WARNING "PCI: using PPB %s[%c] to get irq %d\n",
+                                               pci_name(bridge), 'A' + pin, irq);
+                               dev = bridge;
                        }
+                       dev = temp_dev;
+                       if (irq >= 0) {
+#ifdef CONFIG_PCI_MSI
+                               if (!platform_legacy_irq(irq))
+                                       irq = IO_APIC_VECTOR(irq);
+#endif
+                               printk(KERN_INFO "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n",
+                                       pci_name(dev), 'A' + pin, irq);
+                               dev->irq = irq;
+                               return 0;
+                       } else
+                               msg = " Probably buggy MP table.";
                } else if (pci_probe & PCI_BIOS_IRQ_SCAN)
                        msg = "";
                else
                        msg = " Please try using pci=biosirq.";
-                       
+
                /* With IDE legacy devices the IRQ lookup failure is not a problem.. */
                if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && !(dev->class & 0x5))
                        return 0;
-                       
+
                printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n",
-                      'A' + pin - 1, pci_name(dev), msg);
+                      'A' + pin, pci_name(dev), msg);
        }
-       /* VIA bridges use interrupt line for apic/pci steering across
-          the V-Link */
-       else if (interrupt_line_quirk)
-               pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
        return 0;
 }