X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Facpi%2Fpci_irq.c;h=65aee79b3971fc02f25b8faa53528ece37b0fd64;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=8e73079dfe9a574f7e2c5a77dfa58967615b0b32;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 8e73079df..65aee79b3 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -35,39 +35,25 @@ #include #include #include -#ifdef CONFIG_X86_IO_APIC -#include -#endif -#ifdef CONFIG_IOSAPIC -# include -#endif #include #include - #define _COMPONENT ACPI_PCI_COMPONENT -ACPI_MODULE_NAME ("pci_irq") - -struct acpi_prt_list acpi_prt; - -#ifdef CONFIG_X86 -extern void eisa_set_level_irq(unsigned int irq); -#endif +ACPI_MODULE_NAME("pci_irq") +static struct acpi_prt_list acpi_prt; +static DEFINE_SPINLOCK(acpi_prt_lock); /* -------------------------------------------------------------------------- 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) +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; + struct list_head *node = NULL; + struct acpi_prt_entry *entry = NULL; ACPI_FUNCTION_TRACE("acpi_pci_irq_find_prt_entry"); @@ -78,30 +64,28 @@ acpi_pci_irq_find_prt_entry ( * 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). * - * TBD: Acquire/release lock */ + 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)) { + if ((segment == entry->id.segment) + && (bus == entry->id.bus) + && (device == entry->id.device) + && (pin == entry->pin)) { + spin_unlock(&acpi_prt_lock); return_PTR(entry); } } + 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) +acpi_pci_irq_add_entry(acpi_handle handle, + int segment, int bus, struct acpi_pci_routing_table *prt) { - struct acpi_prt_entry *entry = NULL; + struct acpi_prt_entry *entry = NULL; ACPI_FUNCTION_TRACE("acpi_pci_irq_add_entry"); @@ -147,33 +131,45 @@ acpi_pci_irq_add_entry ( entry->link.index = prt->source_index; ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO, - " %02X:%02X:%02X[%c] -> %s[%d]\n", - entry->id.segment, entry->id.bus, entry->id.device, - ('A' + entry->pin), prt->source, entry->link.index)); + " %02X:%02X:%02X[%c] -> %s[%d]\n", + entry->id.segment, entry->id.bus, + entry->id.device, ('A' + entry->pin), prt->source, + entry->link.index)); - /* TBD: Acquire/release lock */ + spin_lock(&acpi_prt_lock); list_add_tail(&entry->node, &acpi_prt.entries); acpi_prt.count++; + spin_unlock(&acpi_prt_lock); return_VALUE(0); } +static void +acpi_pci_irq_del_entry(int segment, int bus, struct acpi_prt_entry *entry) +{ + if (segment == entry->id.segment && bus == entry->id.bus) { + acpi_prt.count--; + list_del(&entry->node); + kfree(entry); + } +} -int -acpi_pci_irq_add_prt ( - acpi_handle handle, - int segment, - int bus) +int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) { - acpi_status status = AE_OK; - char pathname[ACPI_PATHNAME_MAX] = {0}; - 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_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); @@ -185,12 +181,12 @@ acpi_pci_irq_add_prt ( * (either a PCI root bridge or PCI-PCI bridge). */ - buffer.length = sizeof(pathname); + 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); + pathname); /* * Evaluate this _PRT and add its entries to our global list (acpi_prt). @@ -198,23 +194,25 @@ acpi_pci_irq_add_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))); + acpi_format_exception(status))); return_VALUE(-ENODEV); } prt = kmalloc(buffer.length, GFP_KERNEL); - if (!prt) + if (!prt) { return_VALUE(-ENOMEM); + } memset(prt, 0, buffer.length); buffer.pointer = prt; 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))); + acpi_format_exception(status))); kfree(buffer.pointer); return_VALUE(-ENODEV); } @@ -224,7 +222,7 @@ acpi_pci_irq_add_prt ( while (entry && (entry->length > 0)) { acpi_pci_irq_add_entry(handle, segment, bus, entry); entry = (struct acpi_pci_routing_table *) - ((unsigned long) entry + entry->length); + ((unsigned long)entry + entry->length); } kfree(prt); @@ -232,55 +230,121 @@ acpi_pci_irq_add_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); +} /* -------------------------------------------------------------------------- PCI Interrupt Routing Support -------------------------------------------------------------------------- */ +typedef int (*irq_lookup_func) (struct acpi_prt_entry *, int *, int *, char **); + +static int +acpi_pci_allocate_irq(struct acpi_prt_entry *entry, + int *triggering, int *polarity, char **link) +{ + 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, triggering, + polarity, link); + if (irq < 0) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, + "Invalid IRQ link routing entry\n")); + return_VALUE(-1); + } + } else { + irq = entry->link.index; + *triggering = ACPI_LEVEL_SENSITIVE; + *polarity = ACPI_ACTIVE_LOW; + } -int -acpi_pci_irq_lookup (struct pci_bus *bus, int device, int pin) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", irq)); + return_VALUE(irq); +} + +static int +acpi_pci_free_irq(struct acpi_prt_entry *entry, + int *triggering, int *polarity, char **link) { - struct acpi_prt_entry *entry = NULL; + 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); +} + +/* + * 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 *triggering, + int *polarity, char **link, irq_lookup_func func) +{ + struct acpi_prt_entry *entry = NULL; int segment = pci_domain_nr(bus); 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", - segment, bus_nr, device, ('A' + pin))); + 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); + 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(0); + return_VALUE(-1); } - if (!entry->irq && entry->link.handle) { - entry->irq = acpi_pci_link_get_irq(entry->link.handle, entry->link.index, NULL, NULL); - if (!entry->irq) { - ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ link routing entry\n")); - return_VALUE(0); - } - } - else if (!entry->irq) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid static routing entry (IRQ 0)\n")); - return_VALUE(0); - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", entry->irq)); - - return_VALUE(entry->irq); + ret = func(entry, triggering, polarity, 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) +acpi_pci_irq_derive(struct pci_dev *dev, + int pin, + int *triggering, + int *polarity, char **link, irq_lookup_func func) { - struct pci_dev *bridge = dev; - int irq = 0; - u8 bridge_pin = 0; + struct pci_dev *bridge = dev; + int irq = -1; + u8 bridge_pin = 0; ACPI_FUNCTION_TRACE("acpi_pci_irq_derive"); @@ -291,60 +355,74 @@ acpi_pci_irq_derive ( * 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 && bridge->bus->self) { + while (irq < 0 && bridge->bus->self) { pin = (pin + PCI_SLOT(bridge->devfn)) % 4; bridge = bridge->bus->self; if ((bridge->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) { /* PC card has the same IRQ as its cardbridge */ - pci_read_config_byte(bridge, PCI_INTERRUPT_PIN, &bridge_pin); + bridge_pin = bridge->pin; if (!bridge_pin) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "No interrupt pin configured for device %s\n", pci_name(bridge))); - return_VALUE(0); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No interrupt pin configured for device %s\n", + pci_name(bridge))); + return_VALUE(-1); } /* Pin is from 0 to 3 */ - bridge_pin --; + bridge_pin--; pin = bridge_pin; } - irq = acpi_pci_irq_lookup(bridge->bus, - PCI_SLOT(bridge->devfn), pin); + irq = acpi_pci_irq_lookup(bridge->bus, PCI_SLOT(bridge->devfn), + pin, triggering, polarity, + link, func); } - if (!irq) { - ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Unable to derive IRQ for device %s\n", pci_name(dev))); - return_VALUE(0); + if (irq < 0) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, + "Unable to derive IRQ for device %s\n", + pci_name(dev))); + return_VALUE(-1); } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Derive IRQ %d for device %s from %s\n", - irq, pci_name(dev), pci_name(bridge))); + irq, pci_name(dev), pci_name(bridge))); return_VALUE(irq); } +/* + * acpi_pci_irq_enable + * success: return 0 + * failure: return < 0 + */ -int -acpi_pci_irq_enable ( - struct pci_dev *dev) +int acpi_pci_irq_enable(struct pci_dev *dev) { - int irq = 0; - u8 pin = 0; + int irq = 0; + u8 pin = 0; + int triggering = ACPI_LEVEL_SENSITIVE; + int polarity = ACPI_ACTIVE_LOW; + char *link = NULL; + int rc; ACPI_FUNCTION_TRACE("acpi_pci_irq_enable"); if (!dev) return_VALUE(-EINVAL); - - pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + + pin = dev->pin; if (!pin) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No interrupt pin configured for device %s\n", pci_name(dev))); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No interrupt pin configured for device %s\n", + pci_name(dev))); return_VALUE(0); } pin--; if (!dev->bus) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid (NULL) 'bus' field\n")); + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Invalid (NULL) 'bus' field\n")); return_VALUE(-ENODEV); } @@ -352,88 +430,109 @@ acpi_pci_irq_enable ( * 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); + irq = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, + &triggering, &polarity, &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) - irq = acpi_pci_irq_derive(dev, pin); - + if (irq < 0) + irq = acpi_pci_irq_derive(dev, pin, &triggering, + &polarity, &link, + acpi_pci_allocate_irq); + /* * No IRQ known to the ACPI subsystem - maybe the BIOS / * driver reported one, then use it. Exit in any case. */ - if (!irq) { - printk(KERN_WARNING PREFIX "No IRQ known for interrupt pin %c of device %s", ('A' + pin), pci_name(dev)); + 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 && (dev->irq <= 0xF)) { + if (dev->irq > 0 && (dev->irq <= 0xF)) { printk(" - using IRQ %d\n", dev->irq); - return_VALUE(dev->irq); - } - else { + acpi_register_gsi(dev->irq, ACPI_LEVEL_SENSITIVE, + ACPI_ACTIVE_LOW); + return_VALUE(0); + } else { printk("\n"); return_VALUE(0); } - } + } - dev->irq = irq; + 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)); + return_VALUE(rc); + } + dev->irq = rc; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device %s using IRQ %d\n", pci_name(dev), dev->irq)); + printk(KERN_INFO PREFIX "PCI Interrupt %s[%c] -> ", + pci_name(dev), 'A' + pin); - /* - * Make sure all (legacy) PCI IRQs are set as level-triggered. - */ -#ifdef CONFIG_X86 - { - static u16 irq_mask; - if ((dev->irq < 16) && !((1 << dev->irq) & irq_mask)) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Setting IRQ %d as level-triggered\n", dev->irq)); - irq_mask |= (1 << dev->irq); - eisa_set_level_irq(dev->irq); - } - } -#endif -#ifdef CONFIG_IOSAPIC - if (acpi_irq_model == ACPI_IRQ_MODEL_IOSAPIC) - iosapic_enable_intr(dev->irq); -#endif + if (link) + printk("Link [%s] -> ", link); - return_VALUE(dev->irq); + printk("GSI %u (%s, %s) -> IRQ %d\n", irq, + (triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge", + (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq); + + return_VALUE(0); } +EXPORT_SYMBOL(acpi_pci_irq_enable); -int __init -acpi_pci_irq_init (void) +/* FIXME: implement x86/x86_64 version */ +void __attribute__ ((weak)) acpi_unregister_gsi(u32 i) { - struct pci_dev *dev = NULL; +} - ACPI_FUNCTION_TRACE("acpi_pci_irq_init"); +void acpi_pci_irq_disable(struct pci_dev *dev) +{ + int gsi = 0; + u8 pin = 0; + int triggering = ACPI_LEVEL_SENSITIVE; + int polarity = ACPI_ACTIVE_LOW; - if (!acpi_prt.count) { - printk(KERN_WARNING PREFIX "ACPI tables contain no PCI IRQ " - "routing entries\n"); - return_VALUE(-ENODEV); - } + ACPI_FUNCTION_TRACE("acpi_pci_irq_disable"); - /* Make sure all link devices have a valid IRQ. */ - if (acpi_pci_link_check()) { - return_VALUE(-ENODEV); - } + if (!dev || !dev->bus) + return_VOID; + + pin = dev->pin; + if (!pin) + return_VOID; + pin--; + + /* + * First we check the PCI IRQ routing table (PRT) for an IRQ. + */ + gsi = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, + &triggering, &polarity, 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, + &triggering, &polarity, NULL, + acpi_pci_free_irq); + if (gsi < 0) + return_VOID; -#ifdef CONFIG_X86_IO_APIC - /* Program IOAPICs using data from PRT entries. */ - if (acpi_irq_model == ACPI_IRQ_MODEL_IOAPIC) - mp_parse_prt(); -#endif -#ifdef CONFIG_IOSAPIC - if (acpi_irq_model == ACPI_IRQ_MODEL_IOSAPIC) - iosapic_parse_prt(); -#endif + /* + * TBD: It might be worth clearing dev->irq by magic constant + * (e.g. PCI_UNDEFINED_IRQ). + */ - while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) - acpi_pci_irq_enable(dev); + printk(KERN_INFO PREFIX "PCI interrupt for device %s disabled\n", + pci_name(dev)); - return_VALUE(0); + acpi_unregister_gsi(gsi); + + return_VOID; }