]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/acpi/pci_irq.c
pci, acpi: reroute PCI interrupt to legacy boot interrupt equivalent
[linux-2.6-omap-h63xx.git] / drivers / acpi / pci_irq.c
index 7f19859580c7efcbdf9675d6be44436573d0e8d6..b37cb0a9826ee85699d5f5328b2815001f9063a5 100644 (file)
@@ -25,6 +25,7 @@
  */
 
 
+#include <linux/dmi.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -76,6 +77,101 @@ static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(int segment,
        return NULL;
 }
 
+/* http://bugzilla.kernel.org/show_bug.cgi?id=4773 */
+static struct dmi_system_id medion_md9580[] = {
+       {
+               .ident = "Medion MD9580-F laptop",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "A555"),
+               },
+       },
+       { }
+};
+
+/* http://bugzilla.kernel.org/show_bug.cgi?id=5044 */
+static struct dmi_system_id dell_optiplex[] = {
+       {
+               .ident = "Dell Optiplex GX1",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex GX1 600S+"),
+               },
+       },
+       { }
+};
+
+/* http://bugzilla.kernel.org/show_bug.cgi?id=10138 */
+static struct dmi_system_id hp_t5710[] = {
+       {
+               .ident = "HP t5710",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "hp t5000 series"),
+                       DMI_MATCH(DMI_BOARD_NAME, "098Ch"),
+               },
+       },
+       { }
+};
+
+struct prt_quirk {
+       struct dmi_system_id    *system;
+       unsigned int            segment;
+       unsigned int            bus;
+       unsigned int            device;
+       unsigned char           pin;
+       char                    *source;        /* according to BIOS */
+       char                    *actual_source;
+};
+
+/*
+ * These systems have incorrect _PRT entries.  The BIOS claims the PCI
+ * interrupt at the listed segment/bus/device/pin is connected to the first
+ * link device, but it is actually connected to the second.
+ */
+static struct prt_quirk prt_quirks[] = {
+       { medion_md9580, 0, 0, 9, 'A',
+               "\\_SB_.PCI0.ISA_.LNKA",
+               "\\_SB_.PCI0.ISA_.LNKB"},
+       { dell_optiplex, 0, 0, 0xd, 'A',
+               "\\_SB_.LNKB",
+               "\\_SB_.LNKA"},
+       { hp_t5710, 0, 0, 1, 'A',
+               "\\_SB_.PCI0.LNK1",
+               "\\_SB_.PCI0.LNK3"},
+};
+
+static void
+do_prt_fixups(struct acpi_prt_entry *entry, struct acpi_pci_routing_table *prt)
+{
+       int i;
+       struct prt_quirk *quirk;
+
+       for (i = 0; i < ARRAY_SIZE(prt_quirks); i++) {
+               quirk = &prt_quirks[i];
+
+               /* All current quirks involve link devices, not GSIs */
+               if (!prt->source)
+                       continue;
+
+               if (dmi_check_system(quirk->system) &&
+                   entry->id.segment == quirk->segment &&
+                   entry->id.bus == quirk->bus &&
+                   entry->id.device == quirk->device &&
+                   entry->pin + 'A' == quirk->pin &&
+                   !strcmp(prt->source, quirk->source) &&
+                   strlen(prt->source) >= strlen(quirk->actual_source)) {
+                       printk(KERN_WARNING PREFIX "firmware reports "
+                               "%04x:%02x:%02x[%c] connected to %s; "
+                               "changing to %s\n",
+                               entry->id.segment, entry->id.bus,
+                               entry->id.device, 'A' + entry->pin,
+                               prt->source, quirk->actual_source);
+                       strcpy(prt->source, quirk->actual_source);
+               }
+       }
+}
+
 static int
 acpi_pci_irq_add_entry(acpi_handle handle,
                       int segment, int bus, struct acpi_pci_routing_table *prt)
@@ -96,6 +192,8 @@ acpi_pci_irq_add_entry(acpi_handle handle,
        entry->id.function = prt->address & 0xFFFF;
        entry->pin = prt->pin;
 
+       do_prt_fixups(entry, prt);
+
        /*
         * Type 1: Dynamic
         * ---------------
@@ -286,6 +384,27 @@ acpi_pci_free_irq(struct acpi_prt_entry *entry,
        return irq;
 }
 
+#ifdef CONFIG_X86_IO_APIC
+extern int noioapicquirk;
+
+static int bridge_has_boot_interrupt_variant(struct pci_bus *bus)
+{
+       struct pci_bus *bus_it;
+
+       for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) {
+               if (!bus_it->self)
+                       return 0;
+
+               printk(KERN_INFO "vendor=%04x device=%04x\n", bus_it->self->vendor,
+                               bus_it->self->device);
+
+               if (bus_it->self->irq_reroute_variant)
+                       return bus_it->self->irq_reroute_variant;
+       }
+       return 0;
+}
+#endif /* CONFIG_X86_IO_APIC */
+
 /*
  * acpi_pci_irq_lookup
  * success: return IRQ >= 0
@@ -315,6 +434,41 @@ acpi_pci_irq_lookup(struct pci_bus *bus,
        }
 
        ret = func(entry, triggering, polarity, link);
+
+#ifdef CONFIG_X86_IO_APIC
+       /*
+        * Some chipsets (e.g. intel 6700PXH) generate a legacy INTx when the
+        * IRQ entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel
+        * does during interrupt handling). When this INTx generation cannot be
+        * disabled, we reroute these interrupts to their legacy equivalent to
+        * get rid of spurious interrupts.
+        */
+        if (!noioapicquirk) {
+               switch (bridge_has_boot_interrupt_variant(bus)) {
+               case 0:
+                       /* no rerouting necessary */
+                       break;
+
+               case INTEL_IRQ_REROUTE_VARIANT:
+                       /*
+                        * Remap according to INTx routing table in 6700PXH
+                        * specs, intel order number 302628-002, section
+                        * 2.15.2. Other chipsets (80332, ...) have the same
+                        * mapping and are handled here as well.
+                        */
+                       printk(KERN_INFO "pci irq %d -> rerouted to legacy "
+                                        "irq %d\n", ret, (ret % 4) + 16);
+                       ret = (ret % 4) + 16;
+                       break;
+
+               default:
+                       printk(KERN_INFO "not rerouting irq %d to legacy irq: "
+                                        "unknown mapping\n", ret);
+                       break;
+               }
+       }
+#endif /* CONFIG_X86_IO_APIC */
+
        return ret;
 }