V4L/DVB (8682): V4L: fix return value of register video func
[safe/jmp/linux-2.6] / drivers / acpi / pci_irq.c
index 7f19859..11acaee 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 PCI INT %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
         * ---------------
@@ -331,7 +429,7 @@ acpi_pci_irq_derive(struct pci_dev *dev,
 {
        struct pci_dev *bridge = dev;
        int irq = -1;
-       u8 bridge_pin = 0;
+       u8 bridge_pin = 0, orig_pin = pin;
 
 
        if (!dev)
@@ -365,8 +463,8 @@ acpi_pci_irq_derive(struct pci_dev *dev,
        }
 
        if (irq < 0) {
-               printk(KERN_WARNING PREFIX "Unable to derive IRQ for device %s\n",
-                             pci_name(dev));
+               dev_warn(&dev->dev, "can't derive routing for PCI INT %c\n",
+                        'A' + orig_pin);
                return -1;
        }
 
@@ -389,6 +487,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
        int triggering = ACPI_LEVEL_SENSITIVE;
        int polarity = ACPI_ACTIVE_LOW;
        char *link = NULL;
+       char link_desc[16];
        int rc;
 
 
@@ -405,7 +504,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
        pin--;
 
        if (!dev->bus) {
-               printk(KERN_ERR PREFIX "Invalid (NULL) 'bus' field\n");
+               dev_err(&dev->dev, "invalid (NULL) 'bus' field\n");
                return -ENODEV;
        }
 
@@ -440,8 +539,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
         * driver reported one, then use it. Exit in any case.
         */
        if (irq < 0) {
-               printk(KERN_WARNING PREFIX "PCI Interrupt %s[%c]: no GSI",
-                      pci_name(dev), ('A' + pin));
+               dev_warn(&dev->dev, "PCI INT %c: no GSI", 'A' + pin);
                /* Interrupt Line values above 0xF are forbidden */
                if (dev->irq > 0 && (dev->irq <= 0xF)) {
                        printk(" - using IRQ %d\n", dev->irq);
@@ -456,21 +554,21 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
 
        rc = acpi_register_gsi(irq, triggering, polarity);
        if (rc < 0) {
-               printk(KERN_WARNING PREFIX "PCI Interrupt %s[%c]: failed "
-                      "to register GSI\n", pci_name(dev), ('A' + pin));
+               dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n",
+                        'A' + pin);
                return rc;
        }
        dev->irq = rc;
 
-       printk(KERN_INFO PREFIX "PCI Interrupt %s[%c] -> ",
-              pci_name(dev), 'A' + pin);
-
        if (link)
-               printk("Link [%s] -> ", link);
+               snprintf(link_desc, sizeof(link_desc), " -> Link[%s]", link);
+       else
+               link_desc[0] = '\0';
 
-       printk("GSI %u (%s, %s) -> IRQ %d\n", irq,
-              (triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge",
-              (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq);
+       dev_info(&dev->dev, "PCI INT %c%s -> GSI %u (%s, %s) -> IRQ %d\n",
+                'A' + pin, link_desc, irq,
+                (triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge",
+                (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq);
 
        return 0;
 }
@@ -518,10 +616,6 @@ void acpi_pci_irq_disable(struct pci_dev *dev)
         * (e.g. PCI_UNDEFINED_IRQ).
         */
 
-       printk(KERN_INFO PREFIX "PCI interrupt for device %s disabled\n",
-              pci_name(dev));
-
+       dev_info(&dev->dev, "PCI INT %c disabled\n", 'A' + pin);
        acpi_unregister_gsi(gsi);
-
-       return;
 }