fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / arch / i386 / pci / irq.c
index 6aa9e5f..99c02d9 100644 (file)
@@ -4,19 +4,18 @@
  *     (c) 1999--2000 Martin Mares <mj@ucw.cz>
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #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"
 
@@ -28,6 +27,8 @@ 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).
@@ -53,6 +54,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.
@@ -62,23 +93,29 @@ static struct irq_routing_table * __init pirq_find_routing_table(void)
 {
        u8 *addr;
        struct irq_routing_table *rt;
-       int i;
-       u8 sum;
 
+#ifdef CONFIG_XEN
+       if (!is_initial_xendomain())
+               return NULL;
+#endif
+       if (pirq_table_addr) {
+#ifdef CONFIG_XEN
+               rt = pirq_check_routing_table((u8 *) isa_bus_to_virt(pirq_table_addr));
+#else
+               rt = pirq_check_routing_table((u8 *) __va(pirq_table_addr));
+#endif
+               if (rt)
+                       return rt;
+               printk(KERN_WARNING "PCI: PIRQ table NOT found at pirqaddr\n");
+       }
+#ifdef CONFIG_XEN
+       for(addr = (u8 *) isa_bus_to_virt(0xf0000); addr < (u8 *) isa_bus_to_virt(0x100000); addr += 16) {
+#else
        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);
+#endif
+               rt = pirq_check_routing_table(addr);
+               if (rt)
                        return rt;
-               }
        }
        return NULL;
 }
@@ -102,7 +139,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");
@@ -127,10 +164,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);
        }
 }
@@ -165,14 +209,14 @@ static void write_config_nybble(struct pci_dev *router, unsigned offset, unsigne
  */
 static int pirq_ali_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 {
-       static unsigned char irqmap[16] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
+       static const unsigned char irqmap[16] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
 
        return irqmap[read_config_nybble(router, 0x48, pirq-1)];
 }
 
 static int pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 {
-       static unsigned char irqmap[16] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
+       static const unsigned char irqmap[16] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
        unsigned int val = irqmap[irq];
                
        if (val) {
@@ -216,6 +260,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 const unsigned int pirqmap[5] = { 3, 2, 5, 1, 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 const unsigned int pirqmap[5] = { 3, 2, 5, 1, 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 },
@@ -223,13 +285,13 @@ static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, i
  */
 static int pirq_ite_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 {
-       static unsigned char pirqmap[4] = { 1, 0, 2, 3 };
+       static const unsigned char pirqmap[4] = { 1, 0, 2, 3 };
        return read_config_nybble(router,0x43, pirqmap[pirq-1]);
 }
 
 static int pirq_ite_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 {
-       static unsigned char pirqmap[4] = { 1, 0, 2, 3 };
+       static const unsigned char pirqmap[4] = { 1, 0, 2, 3 };
        write_config_nybble(router, 0x43, pirqmap[pirq-1], irq);
        return 1;
 }
@@ -452,14 +514,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 __initdata 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)
@@ -481,6 +545,22 @@ 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:
+               case PCI_DEVICE_ID_INTEL_ICH9_0:
+               case PCI_DEVICE_ID_INTEL_ICH9_1:
+               case PCI_DEVICE_ID_INTEL_ICH9_2:
+               case PCI_DEVICE_ID_INTEL_ICH9_3:
+               case PCI_DEVICE_ID_INTEL_ICH9_4:
+               case PCI_DEVICE_ID_INTEL_ICH9_5:
                        r->name = "PIIX/ICH";
                        r->get = pirq_piix_get;
                        r->set = pirq_piix_set;
@@ -489,20 +569,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;
 }
@@ -591,11 +701,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;
 }
@@ -663,12 +773,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);
+       pirq_router_dev = pci_get_bus_and_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;
        }
 
@@ -685,6 +796,8 @@ static void __init pirq_find_router(struct irq_router *r)
                pirq_router_dev->vendor,
                pirq_router_dev->device,
                pci_name(pirq_router_dev));
+
+       /* The device remains referenced for the kernel lifetime */
 }
 
 static struct irq_info *pirq_get_info(struct pci_dev *dev)
@@ -713,7 +826,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;
@@ -723,16 +836,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);
@@ -760,15 +873,18 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
         * 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++) {
                        if (!(mask & (1 << i)))
                                continue;
-                       if (pirq_penalty[i] < pirq_penalty[newirq] && can_request_irq(i, SA_SHIRQ))
+                       if (pirq_penalty[i] < pirq_penalty[newirq] && can_request_irq(i, IRQF_SHARED))
                                newirq = i;
                }
        }
@@ -783,6 +899,7 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
        ((!(pci_probe & PCI_USE_PIRQ_MASK)) || ((1 << irq) & mask)) ) {
                DBG(" -> got IRQ %d\n", irq);
                msg = "Found";
+               eisa_set_level_irq(irq);
        } else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
                DBG(" -> assigning IRQ %d", newirq);
                if (r->set(pirq_router_dev, dev, pirq, newirq)) {
@@ -804,7 +921,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;
@@ -837,14 +954,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 */
@@ -854,7 +971,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
                /*
@@ -880,16 +997,12 @@ 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;
                                }
                        }
@@ -953,7 +1066,7 @@ static struct dmi_system_id __initdata pciirq_dmi_table[] = {
 
 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;
@@ -989,107 +1102,82 @@ 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;
+       }
+}
+
+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);
 }
 
-int pirq_enable_irq(struct pci_dev *dev)
+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_MSI
-                                       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) {
+                               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 & 15);
        return 0;
 }
-
-int pci_vector_resources(int last, int nr_released)
-{
-       int count = nr_released;
-
-       int next = last;
-       int offset = (last % 8);
-
-       while (next < FIRST_SYSTEM_VECTOR) {
-               next += 8;
-#ifdef CONFIG_X86_64
-               if (next == IA32_SYSCALL_VECTOR)
-                       continue;
-#else
-               if (next == SYSCALL_VECTOR)
-                       continue;
-#endif
-               count++;
-               if (next >= FIRST_SYSTEM_VECTOR) {
-                       if (offset%8) {
-                               next = FIRST_DEVICE_VECTOR + offset;
-                               offset++;
-                               continue;
-                       }
-                       count--;
-               }
-       }
-
-       return count;
-}