aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjorn Helgaas <bjorn.helgaas@hp.com>2008-03-11 16:45:15 -0400
committerLen Brown <len.brown@intel.com>2008-03-11 23:41:08 -0400
commit391df5dce30a5aab477b9e55ea65a3e83bae96b1 (patch)
tree94033ad7239a8bf5f3947072dcf64f65c0939476
parentbaadac8b10c5ac15ce3d26b68fa266c8889b163f (diff)
ACPI: add _PRT quirks to work around broken firmware
This patch works around incorrect _PRT (PCI interrupt routing) information from firmware. This does not fix any regressions and can wait for the next kernel release. On the Medion MD9580-F laptop, the BIOS says the builtin RTL8139 NIC interrupt at 00:09.0[A] is connected to \_SB.PCI0.ISA.LNKA, but it's really connected to \_SB.PCI0.ISA.LNKB. Before this patch, the workaround was to use "pci=routeirq". More details at http://bugzilla.kernel.org/show_bug.cgi?id=4773. On the Dell OptiPlex GX1, the BIOS says the PCI slot interrupt 00:0d[A] is connected to LNKB, but it's really connected to LNKA. Before this patch, the workaround was to use "pci=routeirq". Pierre Ossman tested a previous version of this patch and confirmed that it fixed the problem. More details at http://bugzilla.kernel.org/show_bug.cgi?id=5044. On the HP t5710 thin client, the BIOS says the builtin Radeon video interrupt at 01:00[A] is connected to LNK1, but it's really connected to LNK3. The previous workaround was to use a custom DSDT. I tested this patch and verified that it fixes the problem. More details at http://bugzilla.kernel.org/show_bug.cgi?id=10138. Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com> Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r--drivers/acpi/pci_irq.c98
1 files changed, 98 insertions, 0 deletions
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
index 7f19859580c7..7af414a3c63e 100644
--- a/drivers/acpi/pci_irq.c
+++ b/drivers/acpi/pci_irq.c
@@ -25,6 +25,7 @@
25 */ 25 */
26 26
27 27
28#include <linux/dmi.h>
28#include <linux/kernel.h> 29#include <linux/kernel.h>
29#include <linux/module.h> 30#include <linux/module.h>
30#include <linux/init.h> 31#include <linux/init.h>
@@ -76,6 +77,101 @@ static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(int segment,
76 return NULL; 77 return NULL;
77} 78}
78 79
80/* http://bugzilla.kernel.org/show_bug.cgi?id=4773 */
81static struct dmi_system_id medion_md9580[] = {
82 {
83 .ident = "Medion MD9580-F laptop",
84 .matches = {
85 DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
86 DMI_MATCH(DMI_PRODUCT_NAME, "A555"),
87 },
88 },
89 { }
90};
91
92/* http://bugzilla.kernel.org/show_bug.cgi?id=5044 */
93static struct dmi_system_id dell_optiplex[] = {
94 {
95 .ident = "Dell Optiplex GX1",
96 .matches = {
97 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
98 DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex GX1 600S+"),
99 },
100 },
101 { }
102};
103
104/* http://bugzilla.kernel.org/show_bug.cgi?id=10138 */
105static struct dmi_system_id hp_t5710[] = {
106 {
107 .ident = "HP t5710",
108 .matches = {
109 DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
110 DMI_MATCH(DMI_PRODUCT_NAME, "hp t5000 series"),
111 DMI_MATCH(DMI_BOARD_NAME, "098Ch"),
112 },
113 },
114 { }
115};
116
117struct prt_quirk {
118 struct dmi_system_id *system;
119 unsigned int segment;
120 unsigned int bus;
121 unsigned int device;
122 unsigned char pin;
123 char *source; /* according to BIOS */
124 char *actual_source;
125};
126
127/*
128 * These systems have incorrect _PRT entries. The BIOS claims the PCI
129 * interrupt at the listed segment/bus/device/pin is connected to the first
130 * link device, but it is actually connected to the second.
131 */
132static struct prt_quirk prt_quirks[] = {
133 { medion_md9580, 0, 0, 9, 'A',
134 "\\_SB_.PCI0.ISA.LNKA",
135 "\\_SB_.PCI0.ISA.LNKB"},
136 { dell_optiplex, 0, 0, 0xd, 'A',
137 "\\_SB_.LNKB",
138 "\\_SB_.LNKA"},
139 { hp_t5710, 0, 0, 1, 'A',
140 "\\_SB_.PCI0.LNK1",
141 "\\_SB_.PCI0.LNK3"},
142};
143
144static void
145do_prt_fixups(struct acpi_prt_entry *entry, struct acpi_pci_routing_table *prt)
146{
147 int i;
148 struct prt_quirk *quirk;
149
150 for (i = 0; i < ARRAY_SIZE(prt_quirks); i++) {
151 quirk = &prt_quirks[i];
152
153 /* All current quirks involve link devices, not GSIs */
154 if (!prt->source)
155 continue;
156
157 if (dmi_check_system(quirk->system) &&
158 entry->id.segment == quirk->segment &&
159 entry->id.bus == quirk->bus &&
160 entry->id.device == quirk->device &&
161 entry->pin + 'A' == quirk->pin &&
162 !strcmp(prt->source, quirk->source) &&
163 strlen(prt->source) >= strlen(quirk->actual_source)) {
164 printk(KERN_WARNING PREFIX "firmware reports "
165 "%04x:%02x:%02x[%c] connected to %s; "
166 "changing to %s\n",
167 entry->id.segment, entry->id.bus,
168 entry->id.device, 'A' + entry->pin,
169 prt->source, quirk->actual_source);
170 strcpy(prt->source, quirk->actual_source);
171 }
172 }
173}
174
79static int 175static int
80acpi_pci_irq_add_entry(acpi_handle handle, 176acpi_pci_irq_add_entry(acpi_handle handle,
81 int segment, int bus, struct acpi_pci_routing_table *prt) 177 int segment, int bus, struct acpi_pci_routing_table *prt)
@@ -96,6 +192,8 @@ acpi_pci_irq_add_entry(acpi_handle handle,
96 entry->id.function = prt->address & 0xFFFF; 192 entry->id.function = prt->address & 0xFFFF;
97 entry->pin = prt->pin; 193 entry->pin = prt->pin;
98 194
195 do_prt_fixups(entry, prt);
196
99 /* 197 /*
100 * Type 1: Dynamic 198 * Type 1: Dynamic
101 * --------------- 199 * ---------------