diff options
Diffstat (limited to 'drivers/acpi/pci_irq.c')
| -rw-r--r-- | drivers/acpi/pci_irq.c | 672 |
1 files changed, 318 insertions, 354 deletions
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index e567c03b238..9c62340c236 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -4,6 +4,8 @@ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> * Copyright (C) 2002 Dominik Brodowski <devel@brodo.de> + * (c) Copyright 2008 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas <bjorn.helgaas@hp.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -24,84 +26,167 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -#include <linux/config.h> +#include <linux/dmi.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> -#include <linux/proc_fs.h> #include <linux/spinlock.h> #include <linux/pm.h> #include <linux/pci.h> #include <linux/acpi.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/slab.h> + +#define PREFIX "ACPI: " #define _COMPONENT ACPI_PCI_COMPONENT -ACPI_MODULE_NAME("pci_irq") +ACPI_MODULE_NAME("pci_irq"); + +struct acpi_prt_entry { + struct list_head list; + struct acpi_pci_id id; + u8 pin; + acpi_handle link; + u32 index; /* GSI, or link _CRS index */ +}; -static struct acpi_prt_list acpi_prt; -static DEFINE_SPINLOCK(acpi_prt_lock); +static inline char pin_name(int pin) +{ + return 'A' + pin - 1; +} /* -------------------------------------------------------------------------- PCI IRQ Routing Table (PRT) Support -------------------------------------------------------------------------- */ -static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(int segment, - 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); +/* http://bugzilla.kernel.org/show_bug.cgi?id=4773 */ +static const 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 const 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 const 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 { + const struct dmi_system_id *system; + unsigned int segment; + unsigned int bus; + unsigned int device; + unsigned char pin; + const char *source; /* according to BIOS */ + const char *actual_source; +}; + +#define PCI_INTX_PIN(c) (c - 'A' + 1) - /* - * Parse through all PRT entries looking for a match on the specified - * PCI device's segment, bus, device, and pin (don't care about func). - * - */ - spin_lock(&acpi_prt_lock); - list_for_each(node, &acpi_prt.entries) { - entry = list_entry(node, struct acpi_prt_entry, 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); +/* + * 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 const struct prt_quirk prt_quirks[] = { + { medion_md9580, 0, 0, 9, PCI_INTX_PIN('A'), + "\\_SB_.PCI0.ISA_.LNKA", + "\\_SB_.PCI0.ISA_.LNKB"}, + { dell_optiplex, 0, 0, 0xd, PCI_INTX_PIN('A'), + "\\_SB_.LNKB", + "\\_SB_.LNKA"}, + { hp_t5710, 0, 0, 1, PCI_INTX_PIN('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; + const 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 == 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, pin_name(entry->pin), + prt->source, quirk->actual_source); + strcpy(prt->source, quirk->actual_source); } } - - spin_unlock(&acpi_prt_lock); - return_PTR(NULL); } -static int -acpi_pci_irq_add_entry(acpi_handle handle, - int segment, int bus, struct acpi_pci_routing_table *prt) +static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev, + int pin, struct acpi_pci_routing_table *prt, + struct acpi_prt_entry **entry_ptr) { - struct acpi_prt_entry *entry = NULL; + int segment = pci_domain_nr(dev->bus); + int bus = dev->bus->number; + int device = PCI_SLOT(dev->devfn); + struct acpi_prt_entry *entry; - ACPI_FUNCTION_TRACE("acpi_pci_irq_add_entry"); + if (((prt->address >> 16) & 0xffff) != device || + prt->pin + 1 != pin) + return -ENODEV; - if (!prt) - return_VALUE(-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; + /* + * Note that the _PRT uses 0=INTA, 1=INTB, etc, while PCI uses + * 1=INTA, 2=INTB. We use the PCI encoding throughout, so convert + * it here. + */ entry->id.segment = segment; entry->id.bus = bus; entry->id.device = (prt->address >> 16) & 0xFFFF; - entry->id.function = prt->address & 0xFFFF; - entry->pin = prt->pin; + entry->pin = prt->pin + 1; + + do_prt_fixups(entry, prt); + + entry->index = prt->source_index; /* * Type 1: Dynamic @@ -116,10 +201,9 @@ acpi_pci_irq_add_entry(acpi_handle handle, * (e.g. exists somewhere 'below' this _PRT entry in the ACPI * namespace). */ - if (prt->source[0]) { - acpi_get_handle(handle, prt->source, &entry->link.handle); - entry->link.index = prt->source_index; - } + if (prt->source[0]) + acpi_get_handle(handle, prt->source, &entry->link); + /* * Type 2: Static * -------------- @@ -127,237 +211,134 @@ acpi_pci_irq_add_entry(acpi_handle handle, * the IRQ value, which is hardwired to specific interrupt inputs on * the interrupt controller. */ - else - entry->link.index = prt->source_index; ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO, - " %02X:%02X:%02X[%c] -> %s[%d]\n", + " %04x:%02x:%02x[%c] -> %s[%d]\n", entry->id.segment, entry->id.bus, - entry->id.device, ('A' + entry->pin), prt->source, - entry->link.index)); + entry->id.device, pin_name(entry->pin), + prt->source, entry->index)); - spin_lock(&acpi_prt_lock); - list_add_tail(&entry->node, &acpi_prt.entries); - acpi_prt.count++; - spin_unlock(&acpi_prt_lock); + *entry_ptr = entry; - return_VALUE(0); + return 0; } -static void -acpi_pci_irq_del_entry(int segment, int bus, struct acpi_prt_entry *entry) +static int acpi_pci_irq_find_prt_entry(struct pci_dev *dev, + int pin, struct acpi_prt_entry **entry_ptr) { - if (segment == entry->id.segment && bus == entry->id.bus) { - acpi_prt.count--; - list_del(&entry->node); - kfree(entry); - } -} + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_pci_routing_table *entry; + acpi_handle handle = NULL; -int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) -{ - acpi_status status = AE_OK; - char *pathname = NULL; - struct acpi_buffer buffer = { 0, NULL }; - struct acpi_pci_routing_table *prt = NULL; - 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); - if (!pathname) - return_VALUE(-ENOMEM); - memset(pathname, 0, ACPI_PATHNAME_MAX); - - if (first_time) { - acpi_prt.count = 0; - INIT_LIST_HEAD(&acpi_prt.entries); - first_time = 0; - } + if (dev->bus->bridge) + handle = ACPI_HANDLE(dev->bus->bridge); - /* - * NOTE: We're given a 'handle' to the _PRT object's parent device - * (either a PCI root bridge or PCI-PCI bridge). - */ - - buffer.length = ACPI_PATHNAME_MAX; - buffer.pointer = pathname; - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - - printk(KERN_DEBUG "ACPI: PCI Interrupt Routing Table [%s._PRT]\n", - pathname); - - /* - * Evaluate this _PRT and add its entries to our global list (acpi_prt). - */ - - buffer.length = 0; - buffer.pointer = NULL; - kfree(pathname); - status = acpi_get_irq_routing_table(handle, &buffer); - if (status != AE_BUFFER_OVERFLOW) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRT [%s]\n", - acpi_format_exception(status))); - return_VALUE(-ENODEV); - } - - prt = kmalloc(buffer.length, GFP_KERNEL); - if (!prt) { - return_VALUE(-ENOMEM); - } - memset(prt, 0, buffer.length); - buffer.pointer = prt; + if (!handle) + return -ENODEV; + /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */ status = acpi_get_irq_routing_table(handle, &buffer); if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRT [%s]\n", - acpi_format_exception(status))); kfree(buffer.pointer); - return_VALUE(-ENODEV); + return -ENODEV; } - entry = prt; - + entry = buffer.pointer; while (entry && (entry->length > 0)) { - acpi_pci_irq_add_entry(handle, segment, bus, entry); + if (!acpi_pci_irq_check_entry(handle, dev, pin, + entry, entry_ptr)) + break; entry = (struct acpi_pci_routing_table *) ((unsigned long)entry + entry->length); } - kfree(prt); - - return_VALUE(0); -} - -void acpi_pci_irq_del_prt(int segment, int bus) -{ - struct list_head *node = NULL, *n = NULL; - struct acpi_prt_entry *entry = NULL; - - if (!acpi_prt.count) { - return; - } - - printk(KERN_DEBUG - "ACPI: Delete PCI Interrupt Routing Table for %x:%x\n", segment, - bus); - spin_lock(&acpi_prt_lock); - list_for_each_safe(node, n, &acpi_prt.entries) { - entry = list_entry(node, struct acpi_prt_entry, node); - - acpi_pci_irq_del_entry(segment, bus, entry); - } - spin_unlock(&acpi_prt_lock); + kfree(buffer.pointer); + return 0; } /* -------------------------------------------------------------------------- PCI Interrupt Routing Support -------------------------------------------------------------------------- */ -typedef int (*irq_lookup_func) (struct acpi_prt_entry *, int *, int *, char **); +#ifdef CONFIG_X86_IO_APIC +extern int noioapicquirk; +extern int noioapicreroute; -static int -acpi_pci_allocate_irq(struct acpi_prt_entry *entry, - int *edge_level, int *active_high_low, char **link) +static int bridge_has_boot_interrupt_variant(struct pci_bus *bus) { - int irq; - - ACPI_FUNCTION_TRACE("acpi_pci_allocate_irq"); - - if (entry->link.handle) { - irq = acpi_pci_link_allocate_irq(entry->link.handle, - entry->link.index, edge_level, - active_high_low, link); - if (irq < 0) { - ACPI_DEBUG_PRINT((ACPI_DB_WARN, - "Invalid IRQ link routing entry\n")); - return_VALUE(-1); - } - } else { - irq = entry->link.index; - *edge_level = ACPI_LEVEL_SENSITIVE; - *active_high_low = ACPI_ACTIVE_LOW; - } + struct pci_bus *bus_it; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", irq)); - return_VALUE(irq); + for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) { + if (!bus_it->self) + return 0; + if (bus_it->self->irq_reroute_variant) + return bus_it->self->irq_reroute_variant; + } + return 0; } -static int -acpi_pci_free_irq(struct acpi_prt_entry *entry, - int *edge_level, int *active_high_low, char **link) +/* + * 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. + */ +static int acpi_reroute_boot_interrupt(struct pci_dev *dev, + struct acpi_prt_entry *entry) { - int irq; - - ACPI_FUNCTION_TRACE("acpi_pci_free_irq"); - if (entry->link.handle) { - irq = acpi_pci_link_free_irq(entry->link.handle); + if (noioapicquirk || noioapicreroute) { + return 0; } else { - irq = entry->link.index; + switch (bridge_has_boot_interrupt_variant(dev->bus)) { + case 0: + /* no rerouting necessary */ + return 0; + 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. + */ + dev_info(&dev->dev, "PCI IRQ %d -> rerouted to legacy " + "IRQ %d\n", entry->index, + (entry->index % 4) + 16); + entry->index = (entry->index % 4) + 16; + return 1; + default: + dev_warn(&dev->dev, "Cannot reroute IRQ %d to legacy " + "IRQ: unknown mapping\n", entry->index); + return -1; + } } - return_VALUE(irq); } +#endif /* CONFIG_X86_IO_APIC */ -/* - * acpi_pci_irq_lookup - * success: return IRQ >= 0 - * failure: return -1 - */ -static int -acpi_pci_irq_lookup(struct pci_bus *bus, - int device, - int pin, - int *edge_level, - int *active_high_low, char **link, irq_lookup_func func) +static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) { struct acpi_prt_entry *entry = NULL; - int segment = pci_domain_nr(bus); - int bus_nr = bus->number; + struct pci_dev *bridge; + u8 bridge_pin, orig_pin = pin; 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", - segment, bus_nr, device, ('A' + pin))); - - 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); + ret = acpi_pci_irq_find_prt_entry(dev, pin, &entry); + if (!ret && entry) { +#ifdef CONFIG_X86_IO_APIC + acpi_reroute_boot_interrupt(dev, entry); +#endif /* CONFIG_X86_IO_APIC */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %s[%c] _PRT entry\n", + pci_name(dev), pin_name(pin))); + return entry; } - ret = func(entry, edge_level, active_high_low, link); - return_VALUE(ret); -} - -/* - * acpi_pci_irq_derive - * success: return IRQ >= 0 - * failure: return < 0 - */ -static int -acpi_pci_irq_derive(struct pci_dev *dev, - int pin, - int *edge_level, - int *active_high_low, char **link, irq_lookup_func func) -{ - struct pci_dev *bridge = dev; - int irq = -1; - u8 bridge_pin = 0; - - ACPI_FUNCTION_TRACE("acpi_pci_irq_derive"); - - if (!dev) - return_VALUE(-EINVAL); - - /* + /* * Attempt to derive an IRQ for this device from a parent bridge's * PCI interrupt routing entry (eg. yenta bridge and add-in card bridge). */ - while (irq < 0 && bridge->bus->self) { - pin = (pin + PCI_SLOT(bridge->devfn)) % 4; - bridge = bridge->bus->self; + bridge = dev->bus->self; + while (bridge) { + pin = pci_swizzle_interrupt_pin(dev, pin); if ((bridge->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) { /* PC card has the same IRQ as its cardbridge */ @@ -366,173 +347,156 @@ acpi_pci_irq_derive(struct pci_dev *dev, ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No interrupt pin configured for device %s\n", pci_name(bridge))); - return_VALUE(-1); + return NULL; } - /* Pin is from 0 to 3 */ - bridge_pin--; pin = bridge_pin; } - irq = acpi_pci_irq_lookup(bridge->bus, PCI_SLOT(bridge->devfn), - pin, edge_level, active_high_low, - link, func); - } + ret = acpi_pci_irq_find_prt_entry(bridge, pin, &entry); + if (!ret && entry) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Derived GSI for %s INT %c from %s\n", + pci_name(dev), pin_name(orig_pin), + pci_name(bridge))); + return entry; + } - if (irq < 0) { - ACPI_DEBUG_PRINT((ACPI_DB_WARN, - "Unable to derive IRQ for device %s\n", - pci_name(dev))); - return_VALUE(-1); + dev = bridge; + bridge = dev->bus->self; } - 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); + dev_warn(&dev->dev, "can't derive routing for PCI INT %c\n", + pin_name(orig_pin)); + return NULL; } -/* - * acpi_pci_irq_enable - * success: return 0 - * failure: return < 0 - */ +#if IS_ENABLED(CONFIG_ISA) || IS_ENABLED(CONFIG_EISA) +static int acpi_isa_register_gsi(struct pci_dev *dev) +{ + u32 dev_gsi; + + /* Interrupt Line values above 0xF are forbidden */ + if (dev->irq > 0 && (dev->irq <= 0xF) && + (acpi_isa_irq_to_gsi(dev->irq, &dev_gsi) == 0)) { + dev_warn(&dev->dev, "PCI INT %c: no GSI - using ISA IRQ %d\n", + pin_name(dev->pin), dev->irq); + acpi_register_gsi(&dev->dev, dev_gsi, + ACPI_LEVEL_SENSITIVE, + ACPI_ACTIVE_LOW); + return 0; + } + return -EINVAL; +} +#else +static inline int acpi_isa_register_gsi(struct pci_dev *dev) +{ + return -ENODEV; +} +#endif int acpi_pci_irq_enable(struct pci_dev *dev) { - int irq = 0; - u8 pin = 0; - int edge_level = ACPI_LEVEL_SENSITIVE; - int active_high_low = ACPI_ACTIVE_LOW; + struct acpi_prt_entry *entry; + int gsi; + u8 pin; + int triggering = ACPI_LEVEL_SENSITIVE; + int polarity = ACPI_ACTIVE_LOW; char *link = NULL; + char link_desc[16]; int rc; - ACPI_FUNCTION_TRACE("acpi_pci_irq_enable"); - - if (!dev) - return_VALUE(-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_DEBUG_PRINT((ACPI_DB_ERROR, - "Invalid (NULL) 'bus' field\n")); - return_VALUE(-ENODEV); + entry = acpi_pci_irq_lookup(dev, pin); + if (!entry) { + /* + * 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; } - /* - * First we check the PCI IRQ routing table (PRT) for an IRQ. PRT - * values override any BIOS-assigned IRQs set during boot. - */ - irq = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, - &edge_level, &active_high_low, &link, - acpi_pci_allocate_irq); - - /* - * If no PRT entry was found, we'll try to derive an IRQ from the - * device's parent bridge. - */ - if (irq < 0) - irq = acpi_pci_irq_derive(dev, pin, &edge_level, - &active_high_low, &link, - acpi_pci_allocate_irq); + if (entry) { + if (entry->link) + gsi = acpi_pci_link_allocate_irq(entry->link, + entry->index, + &triggering, &polarity, + &link); + else + gsi = entry->index; + } else + gsi = -1; /* * No IRQ known to the ACPI subsystem - maybe the BIOS / * 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)); - /* Interrupt Line values above 0xF are forbidden */ - if (dev->irq > 0 && (dev->irq <= 0xF)) { - printk(" - using IRQ %d\n", dev->irq); - acpi_register_gsi(dev->irq, ACPI_LEVEL_SENSITIVE, - ACPI_ACTIVE_LOW); - return_VALUE(0); - } else { - printk("\n"); - return_VALUE(0); - } + if (gsi < 0) { + if (acpi_isa_register_gsi(dev)) + dev_warn(&dev->dev, "PCI INT %c: no GSI\n", + pin_name(pin)); + + kfree(entry); + return 0; } - rc = acpi_register_gsi(irq, edge_level, active_high_low); + rc = acpi_register_gsi(&dev->dev, gsi, triggering, polarity); if (rc < 0) { - printk(KERN_WARNING PREFIX "PCI Interrupt %s[%c]: failed " - "to register GSI\n", pci_name(dev), ('A' + pin)); - return_VALUE(rc); + dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n", + pin_name(pin)); + kfree(entry); + return rc; } dev->irq = rc; - printk(KERN_INFO PREFIX "PCI Interrupt %s[%c] -> ", - pci_name(dev), 'A' + pin); - if (link) - printk("Link [%s] -> ", link); - - printk("GSI %u (%s, %s) -> IRQ %d\n", irq, - (edge_level == ACPI_LEVEL_SENSITIVE) ? "level" : "edge", - (active_high_low == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq); - - return_VALUE(0); -} + snprintf(link_desc, sizeof(link_desc), " -> Link[%s]", link); + else + link_desc[0] = '\0'; -EXPORT_SYMBOL(acpi_pci_irq_enable); + dev_dbg(&dev->dev, "PCI INT %c%s -> GSI %u (%s, %s) -> IRQ %d\n", + pin_name(pin), link_desc, gsi, + (triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge", + (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq); -/* FIXME: implement x86/x86_64 version */ -void __attribute__ ((weak)) acpi_unregister_gsi(u32 i) -{ + kfree(entry); + return 0; } void acpi_pci_irq_disable(struct pci_dev *dev) { - int gsi = 0; - u8 pin = 0; - int edge_level = ACPI_LEVEL_SENSITIVE; - int active_high_low = ACPI_ACTIVE_LOW; - - ACPI_FUNCTION_TRACE("acpi_pci_irq_disable"); - - if (!dev || !dev->bus) - return_VOID; + struct acpi_prt_entry *entry; + int gsi; + u8 pin; pin = dev->pin; if (!pin) - return_VOID; - pin--; + return; - /* - * First we check the PCI IRQ routing table (PRT) for an IRQ. - */ - gsi = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, - &edge_level, &active_high_low, NULL, - acpi_pci_free_irq); - /* - * If no PRT entry was found, we'll try to derive an IRQ from the - * device's parent bridge. - */ - if (gsi < 0) - gsi = acpi_pci_irq_derive(dev, pin, - &edge_level, &active_high_low, NULL, - acpi_pci_free_irq); - if (gsi < 0) - return_VOID; + entry = acpi_pci_irq_lookup(dev, pin); + if (!entry) + return; + + if (entry->link) + gsi = acpi_pci_link_free_irq(entry->link); + else + gsi = entry->index; + + kfree(entry); /* * TBD: It might be worth clearing dev->irq by magic constant * (e.g. PCI_UNDEFINED_IRQ). */ - printk(KERN_INFO PREFIX "PCI interrupt for device %s disabled\n", - pci_name(dev)); - + dev_dbg(&dev->dev, "PCI INT %c disabled\n", pin_name(pin)); acpi_unregister_gsi(gsi); - - return_VOID; } |
