* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
-#include <linux/config.h>
+#include <linux/dmi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <acpi/acpi_drivers.h>
#define _COMPONENT ACPI_PCI_COMPONENT
-ACPI_MODULE_NAME("pci_irq")
+ACPI_MODULE_NAME("pci_irq");
static struct acpi_prt_list acpi_prt;
static DEFINE_SPINLOCK(acpi_prt_lock);
int bus,
int device, int pin)
{
- struct list_head *node = NULL;
struct acpi_prt_entry *entry = NULL;
- ACPI_FUNCTION_TRACE("acpi_pci_irq_find_prt_entry");
-
if (!acpi_prt.count)
- return_PTR(NULL);
+ return NULL;
/*
* Parse through all PRT entries looking for a match on the specified
*
*/
spin_lock(&acpi_prt_lock);
- list_for_each(node, &acpi_prt.entries) {
- entry = list_entry(node, struct acpi_prt_entry, node);
+ list_for_each_entry(entry, &acpi_prt.entries, node) {
if ((segment == entry->id.segment)
&& (bus == entry->id.bus)
&& (device == entry->id.device)
&& (pin == entry->pin)) {
spin_unlock(&acpi_prt_lock);
- return_PTR(entry);
+ return entry;
}
}
spin_unlock(&acpi_prt_lock);
- return_PTR(NULL);
+ 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
{
struct acpi_prt_entry *entry = NULL;
- ACPI_FUNCTION_TRACE("acpi_pci_irq_add_entry");
if (!prt)
- return_VALUE(-EINVAL);
+ return -EINVAL;
- entry = kmalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL);
+ entry = kzalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL);
if (!entry)
- return_VALUE(-ENOMEM);
- memset(entry, 0, sizeof(struct acpi_prt_entry));
+ return -ENOMEM;
entry->id.segment = segment;
entry->id.bus = bus;
entry->id.function = prt->address & 0xFFFF;
entry->pin = prt->pin;
+ do_prt_fixups(entry, prt);
+
/*
* Type 1: Dynamic
* ---------------
acpi_prt.count++;
spin_unlock(&acpi_prt_lock);
- return_VALUE(0);
+ return 0;
}
static void
struct acpi_pci_routing_table *entry = NULL;
static int first_time = 1;
- ACPI_FUNCTION_TRACE("acpi_pci_irq_add_prt");
- pathname = (char *)kmalloc(ACPI_PATHNAME_MAX, GFP_KERNEL);
+ pathname = kzalloc(ACPI_PATHNAME_MAX, GFP_KERNEL);
if (!pathname)
- return_VALUE(-ENOMEM);
- memset(pathname, 0, ACPI_PATHNAME_MAX);
+ return -ENOMEM;
if (first_time) {
acpi_prt.count = 0;
if (status != AE_BUFFER_OVERFLOW) {
ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRT [%s]",
acpi_format_exception(status)));
- return_VALUE(-ENODEV);
+ return -ENODEV;
}
- prt = kmalloc(buffer.length, GFP_KERNEL);
+ prt = kzalloc(buffer.length, GFP_KERNEL);
if (!prt) {
- return_VALUE(-ENOMEM);
+ return -ENOMEM;
}
- memset(prt, 0, buffer.length);
buffer.pointer = prt;
status = acpi_get_irq_routing_table(handle, &buffer);
ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRT [%s]",
acpi_format_exception(status)));
kfree(buffer.pointer);
- return_VALUE(-ENODEV);
+ return -ENODEV;
}
entry = prt;
kfree(prt);
- return_VALUE(0);
+ return 0;
}
void acpi_pci_irq_del_prt(int segment, int bus)
{
int irq;
- ACPI_FUNCTION_TRACE("acpi_pci_allocate_irq");
if (entry->link.handle) {
irq = acpi_pci_link_allocate_irq(entry->link.handle,
if (irq < 0) {
printk(KERN_WARNING PREFIX
"Invalid IRQ link routing entry\n");
- return_VALUE(-1);
+ return -1;
}
} else {
irq = entry->link.index;
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", irq));
- return_VALUE(irq);
+ return irq;
}
static int
{
int irq;
- ACPI_FUNCTION_TRACE("acpi_pci_free_irq");
if (entry->link.handle) {
irq = acpi_pci_link_free_irq(entry->link.handle);
} else {
irq = entry->link.index;
}
- return_VALUE(irq);
+ return irq;
}
/*
int bus_nr = bus->number;
int ret;
- ACPI_FUNCTION_TRACE("acpi_pci_irq_lookup");
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Searching for PRT entry for %02x:%02x:%02x[%c]\n",
entry = acpi_pci_irq_find_prt_entry(segment, bus_nr, device, pin);
if (!entry) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PRT entry not found\n"));
- return_VALUE(-1);
+ return -1;
}
ret = func(entry, triggering, polarity, link);
- return_VALUE(ret);
+ return ret;
}
/*
int irq = -1;
u8 bridge_pin = 0;
- ACPI_FUNCTION_TRACE("acpi_pci_irq_derive");
if (!dev)
- return_VALUE(-EINVAL);
+ return -EINVAL;
/*
* Attempt to derive an IRQ for this device from a parent bridge's
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"No interrupt pin configured for device %s\n",
pci_name(bridge)));
- return_VALUE(-1);
+ return -1;
}
/* Pin is from 0 to 3 */
bridge_pin--;
if (irq < 0) {
printk(KERN_WARNING PREFIX "Unable to derive IRQ for device %s\n",
pci_name(dev));
- return_VALUE(-1);
+ return -1;
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Derive IRQ %d for device %s from %s\n",
irq, pci_name(dev), pci_name(bridge)));
- return_VALUE(irq);
+ return irq;
}
/*
char *link = NULL;
int rc;
- ACPI_FUNCTION_TRACE("acpi_pci_irq_enable");
if (!dev)
- return_VALUE(-EINVAL);
+ return -EINVAL;
pin = dev->pin;
if (!pin) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"No interrupt pin configured for device %s\n",
pci_name(dev)));
- return_VALUE(0);
+ return 0;
}
pin--;
if (!dev->bus) {
- ACPI_ERROR((AE_INFO, "Invalid (NULL) 'bus' field"));
- return_VALUE(-ENODEV);
+ printk(KERN_ERR PREFIX "Invalid (NULL) 'bus' field\n");
+ return -ENODEV;
}
/*
&polarity, &link,
acpi_pci_allocate_irq);
+ if (irq < 0) {
+ /*
+ * IDE legacy mode controller IRQs are magic. Why do compat
+ * extensions always make such a nasty mess.
+ */
+ if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE &&
+ (dev->class & 0x05) == 0)
+ return 0;
+ }
/*
* No IRQ known to the ACPI subsystem - maybe the BIOS /
* driver reported one, then use it. Exit in any case.
printk(" - using IRQ %d\n", dev->irq);
acpi_register_gsi(dev->irq, ACPI_LEVEL_SENSITIVE,
ACPI_ACTIVE_LOW);
- return_VALUE(0);
+ return 0;
} else {
printk("\n");
- return_VALUE(0);
+ return 0;
}
}
if (rc < 0) {
printk(KERN_WARNING PREFIX "PCI Interrupt %s[%c]: failed "
"to register GSI\n", pci_name(dev), ('A' + pin));
- return_VALUE(rc);
+ return rc;
}
dev->irq = rc;
(triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge",
(polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq);
- return_VALUE(0);
+ return 0;
}
-EXPORT_SYMBOL(acpi_pci_irq_enable);
-
/* FIXME: implement x86/x86_64 version */
void __attribute__ ((weak)) acpi_unregister_gsi(u32 i)
{
int triggering = ACPI_LEVEL_SENSITIVE;
int polarity = ACPI_ACTIVE_LOW;
- ACPI_FUNCTION_TRACE("acpi_pci_irq_disable");
if (!dev || !dev->bus)
- return_VOID;
+ return;
pin = dev->pin;
if (!pin)
- return_VOID;
+ return;
pin--;
/*
&triggering, &polarity, NULL,
acpi_pci_free_irq);
if (gsi < 0)
- return_VOID;
+ return;
/*
* TBD: It might be worth clearing dev->irq by magic constant
acpi_unregister_gsi(gsi);
- return_VOID;
+ return;
}