--- include/asm-i386/io_apic.h | 0 linux-2.6.11-root/arch/i386/kernel/acpi/boot.c | 11 +++ linux-2.6.11-root/arch/i386/kernel/mpparse.c | 43 ++++++++++-- linux-2.6.11-root/arch/i386/pci/acpi.c | 1 linux-2.6.11-root/arch/i386/pci/common.c | 6 + linux-2.6.11-root/arch/i386/pci/irq.c | 1 linux-2.6.11-root/arch/i386/pci/pci.h | 1 linux-2.6.11-root/drivers/acpi/pci_irq.c | 82 ++++++++++++++++-------- linux-2.6.11-root/drivers/acpi/pci_link.c | 71 +++++++++++++++++--- linux-2.6.11-root/drivers/pcmcia/yenta_socket.c | 2 linux-2.6.11-root/include/acpi/acpi_drivers.h | 3 linux-2.6.11-root/include/asm-i386/mpspec.h | 1 linux-2.6.11-root/include/linux/acpi.h | 4 - 13 files changed, 175 insertions(+), 51 deletions(-) diff -puN drivers/acpi/pci_irq.c~suspend_resume_irq drivers/acpi/pci_irq.c --- linux-2.6.11/drivers/acpi/pci_irq.c~suspend_resume_irq 2005-04-30 13:10:03.000000000 +0800 +++ linux-2.6.11-root/drivers/acpi/pci_irq.c 2005-05-09 10:22:08.691469464 +0800 @@ -269,7 +269,51 @@ acpi_pci_irq_del_prt (int segment, int b /* -------------------------------------------------------------------------- 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 *edge_level, + int *active_high_low, + 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, 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; + } + + 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 *edge_level, + int *active_high_low, + char **link) +{ + 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 @@ -282,12 +326,13 @@ acpi_pci_irq_lookup ( int pin, int *edge_level, int *active_high_low, - char **link) + char **link, + irq_lookup_func func) { struct acpi_prt_entry *entry = NULL; int segment = pci_domain_nr(bus); int bus_nr = bus->number; - int irq; + int ret; ACPI_FUNCTION_TRACE("acpi_pci_irq_lookup"); @@ -301,22 +346,8 @@ acpi_pci_irq_lookup ( return_VALUE(-1); } - if (entry->link.handle) { - irq = acpi_pci_link_get_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; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", irq)); - - return_VALUE(irq); + ret = func(entry, edge_level, active_high_low, link); + return_VALUE(ret); } /* @@ -330,7 +361,8 @@ acpi_pci_irq_derive ( int pin, int *edge_level, int *active_high_low, - char **link) + char **link, + irq_lookup_func func) { struct pci_dev *bridge = dev; int irq = -1; @@ -363,7 +395,7 @@ acpi_pci_irq_derive ( } irq = acpi_pci_irq_lookup(bridge->bus, PCI_SLOT(bridge->devfn), - pin, edge_level, active_high_low, link); + pin, edge_level, active_high_low, link, func); } if (irq < 0) { @@ -415,7 +447,7 @@ acpi_pci_irq_enable ( * 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); + &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 @@ -423,7 +455,7 @@ acpi_pci_irq_enable ( */ if (irq < 0) irq = acpi_pci_irq_derive(dev, pin, &edge_level, - &active_high_low, &link); + &active_high_low, &link, acpi_pci_allocate_irq); /* * No IRQ known to the ACPI subsystem - maybe the BIOS / @@ -461,7 +493,6 @@ acpi_pci_irq_enable ( EXPORT_SYMBOL(acpi_pci_irq_enable); -#ifdef CONFIG_ACPI_DEALLOCATE_IRQ void acpi_pci_irq_disable ( struct pci_dev *dev) @@ -488,14 +519,14 @@ acpi_pci_irq_disable ( * 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); + &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); + &edge_level, &active_high_low, NULL, acpi_pci_free_irq); if (gsi < 0) return_VOID; @@ -511,4 +542,3 @@ acpi_pci_irq_disable ( return_VOID; } -#endif /* CONFIG_ACPI_DEALLOCATE_IRQ */ diff -puN include/acpi/acpi_drivers.h~suspend_resume_irq include/acpi/acpi_drivers.h --- linux-2.6.11/include/acpi/acpi_drivers.h~suspend_resume_irq 2005-04-30 13:10:03.000000000 +0800 +++ linux-2.6.11-root/include/acpi/acpi_drivers.h 2005-04-30 13:10:03.000000000 +0800 @@ -56,8 +56,9 @@ /* ACPI PCI Interrupt Link (pci_link.c) */ int acpi_irq_penalty_init (void); -int acpi_pci_link_get_irq (acpi_handle handle, int index, int *edge_level, +int acpi_pci_link_allocate_irq (acpi_handle handle, int index, int *edge_level, int *active_high_low, char **name); +int acpi_pci_link_free_irq(acpi_handle handle); /* ACPI PCI Interrupt Routing (pci_irq.c) */ diff -puN drivers/acpi/pci_link.c~suspend_resume_irq drivers/acpi/pci_link.c --- linux-2.6.11/drivers/acpi/pci_link.c~suspend_resume_irq 2005-04-30 13:10:03.000000000 +0800 +++ linux-2.6.11-root/drivers/acpi/pci_link.c 2005-05-09 10:29:11.569182280 +0800 @@ -68,6 +68,10 @@ static struct acpi_driver acpi_pci_link_ }, }; +/* + * If a link is initialized, we never change its active and the initialized + * later even the link is disable. Instead, we just repick the active irq + */ struct acpi_pci_link_irq { u8 active; /* Current IRQ */ u8 edge_level; /* All IRQs */ @@ -76,8 +80,7 @@ struct acpi_pci_link_irq { u8 possible_count; u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE]; u8 initialized:1; - u8 suspend_resume:1; - u8 reserved:6; + u8 reserved:7; }; struct acpi_pci_link { @@ -85,6 +88,7 @@ struct acpi_pci_link { struct acpi_device *device; acpi_handle handle; struct acpi_pci_link_irq irq; + int refcnt; }; static struct { @@ -532,12 +536,12 @@ static int acpi_pci_link_allocate( ACPI_FUNCTION_TRACE("acpi_pci_link_allocate"); - if (link->irq.suspend_resume) { - acpi_pci_link_set(link, link->irq.active); - link->irq.suspend_resume = 0; - } - if (link->irq.initialized) + if (link->irq.initialized) { + if (link->refcnt == 0) + /* This means the link is disabled but initialized */ + acpi_pci_link_set(link, link->irq.active); return_VALUE(0); + } /* * search for active IRQ in list of possible IRQs. @@ -596,13 +600,13 @@ static int acpi_pci_link_allocate( } /* - * acpi_pci_link_get_irq + * acpi_pci_link_allocate_irq * success: return IRQ >= 0 * failure: return -1 */ int -acpi_pci_link_get_irq ( +acpi_pci_link_allocate_irq ( acpi_handle handle, int index, int *edge_level, @@ -613,7 +617,7 @@ acpi_pci_link_get_irq ( struct acpi_device *device = NULL; struct acpi_pci_link *link = NULL; - ACPI_FUNCTION_TRACE("acpi_pci_link_get_irq"); + ACPI_FUNCTION_TRACE("acpi_pci_link_allocate_irq"); result = acpi_bus_get_device(handle, &device); if (result) { @@ -644,10 +648,49 @@ acpi_pci_link_get_irq ( if (edge_level) *edge_level = link->irq.edge_level; if (active_high_low) *active_high_low = link->irq.active_high_low; if (name) *name = acpi_device_bid(link->device); + link->refcnt ++; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Link %s is referenced\n", acpi_device_bid(link->device))); return_VALUE(link->irq.active); } +/* We don't change link's irq information here. After it is reenabled, we + * continue use the info + */ +int +acpi_pci_link_free_irq(acpi_handle handle) +{ + struct acpi_device *device = NULL; + struct acpi_pci_link *link = NULL; + acpi_status result; + + ACPI_FUNCTION_TRACE("acpi_pci_link_free_irq"); + result = acpi_bus_get_device(handle, &device); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link device\n")); + return_VALUE(-1); + } + + link = (struct acpi_pci_link *) acpi_driver_data(device); + if (!link) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n")); + return_VALUE(-1); + } + if (!link->irq.initialized) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link isn't initialized\n")); + return_VALUE(-1); + } + + link->refcnt --; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Link %s is dereferenced\n", acpi_device_bid(link->device))); + + if (link->refcnt == 0) { + acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL); + } + return_VALUE(link->irq.active); +} /* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ @@ -735,8 +778,12 @@ irqrouter_suspend( ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n")); continue; } - if (link->irq.active && link->irq.initialized) - link->irq.suspend_resume = 1; + if (link->irq.initialized && link->refcnt != 0) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Link device [%s] refrerence isn't 0\n", + acpi_device_bid(link->device))); + link->refcnt = 0; + } } return_VALUE(0); } diff -puN include/linux/acpi.h~suspend_resume_irq include/linux/acpi.h --- linux-2.6.11/include/linux/acpi.h~suspend_resume_irq 2005-04-30 13:10:03.000000000 +0800 +++ linux-2.6.11-root/include/linux/acpi.h 2005-04-30 13:10:03.000000000 +0800 @@ -428,9 +428,7 @@ int acpi_gsi_to_irq (u32 gsi, unsigned i * If this matches the last registration, any IRQ resources for gsi * are freed. */ -#ifdef CONFIG_ACPI_DEALLOCATE_IRQ void acpi_unregister_gsi (u32 gsi); -#endif #ifdef CONFIG_ACPI_PCI @@ -455,9 +453,7 @@ struct pci_dev; int acpi_pci_irq_enable (struct pci_dev *dev); void acpi_penalize_isa_irq(int irq, int active); -#ifdef CONFIG_ACPI_DEALLOCATE_IRQ void acpi_pci_irq_disable (struct pci_dev *dev); -#endif struct acpi_pci_driver { struct acpi_pci_driver *next; diff -puN arch/i386/kernel/acpi/boot.c~suspend_resume_irq arch/i386/kernel/acpi/boot.c --- linux-2.6.11/arch/i386/kernel/acpi/boot.c~suspend_resume_irq 2005-04-30 13:10:03.000000000 +0800 +++ linux-2.6.11-root/arch/i386/kernel/acpi/boot.c 2005-05-08 14:31:26.000000000 +0800 @@ -484,6 +484,17 @@ unsigned int acpi_register_gsi(u32 gsi, } EXPORT_SYMBOL(acpi_register_gsi); +void acpi_unregister_gsi(u32 gsi) +{ + /* TBD: pic mode */ +#ifdef CONFIG_X86_IO_APIC + if (acpi_irq_model == ACPI_IRQ_MODEL_IOAPIC) { + mp_unregister_gsi(gsi); + } +#endif +} +EXPORT_SYMBOL(acpi_unregister_gsi); + /* * ACPI based hotplug support for CPU */ diff -puN arch/i386/pci/common.c~suspend_resume_irq arch/i386/pci/common.c --- linux-2.6.11/arch/i386/pci/common.c~suspend_resume_irq 2005-04-30 13:10:03.000000000 +0800 +++ linux-2.6.11-root/arch/i386/pci/common.c 2005-04-30 13:10:03.000000000 +0800 @@ -253,3 +253,9 @@ int pcibios_enable_device(struct pci_dev return pcibios_enable_irq(dev); } + +void pcibios_disable_device (struct pci_dev *dev) +{ + if (pcibios_disable_irq) + pcibios_disable_irq(dev); +} diff -puN arch/i386/pci/pci.h~suspend_resume_irq arch/i386/pci/pci.h --- linux-2.6.11/arch/i386/pci/pci.h~suspend_resume_irq 2005-04-30 13:10:03.000000000 +0800 +++ linux-2.6.11-root/arch/i386/pci/pci.h 2005-04-30 13:10:03.000000000 +0800 @@ -73,3 +73,4 @@ extern int pcibios_scanned; extern spinlock_t pci_config_lock; extern int (*pcibios_enable_irq)(struct pci_dev *dev); +extern void (*pcibios_disable_irq)(struct pci_dev *dev); diff -puN arch/i386/pci/irq.c~suspend_resume_irq arch/i386/pci/irq.c --- linux-2.6.11/arch/i386/pci/irq.c~suspend_resume_irq 2005-04-30 13:10:03.000000000 +0800 +++ linux-2.6.11-root/arch/i386/pci/irq.c 2005-04-30 13:10:03.000000000 +0800 @@ -56,6 +56,7 @@ struct irq_router_handler { }; int (*pcibios_enable_irq)(struct pci_dev *dev) = NULL; +void (*pcibios_disable_irq)(struct pci_dev *dev) = NULL; /* * Check passed address for the PCI IRQ Routing Table signature diff -puN arch/i386/pci/acpi.c~suspend_resume_irq arch/i386/pci/acpi.c --- linux-2.6.11/arch/i386/pci/acpi.c~suspend_resume_irq 2005-04-30 13:10:03.000000000 +0800 +++ linux-2.6.11-root/arch/i386/pci/acpi.c 2005-04-30 13:10:03.000000000 +0800 @@ -30,6 +30,7 @@ static int __init pci_acpi_init(void) acpi_irq_penalty_init(); pcibios_scanned++; pcibios_enable_irq = acpi_pci_irq_enable; + pcibios_disable_irq = acpi_pci_irq_disable; if (pci_routeirq) { /* diff -puN include/asm-i386/mpspec.h~suspend_resume_irq include/asm-i386/mpspec.h --- linux-2.6.11/include/asm-i386/mpspec.h~suspend_resume_irq 2005-05-08 14:32:08.000000000 +0800 +++ linux-2.6.11-root/include/asm-i386/mpspec.h 2005-05-08 14:32:39.000000000 +0800 @@ -33,6 +33,7 @@ extern void mp_register_ioapic (u8 id, u extern void mp_override_legacy_irq (u8 bus_irq, u8 polarity, u8 trigger, u32 gsi); extern void mp_config_acpi_legacy_irqs (void); extern int mp_register_gsi (u32 gsi, int edge_level, int active_high_low); +extern void mp_unregister_gsi (u32 gsi); #endif /*CONFIG_ACPI_BOOT*/ #define PHYSID_ARRAY_SIZE BITS_TO_LONGS(MAX_APICS) diff -puN arch/i386/kernel/mpparse.c~suspend_resume_irq arch/i386/kernel/mpparse.c --- linux-2.6.11/arch/i386/kernel/mpparse.c~suspend_resume_irq 2005-05-08 14:32:56.000000000 +0800 +++ linux-2.6.11-root/arch/i386/kernel/mpparse.c 2005-05-09 09:34:01.366409592 +0800 @@ -867,7 +867,7 @@ static struct mp_ioapic_routing { int apic_id; int gsi_base; int gsi_end; - u32 pin_programmed[4]; + int refcnts[MP_MAX_IOAPIC_PIN]; } mp_ioapic_routing[MAX_IO_APICS]; @@ -1059,7 +1059,6 @@ int mp_register_gsi (u32 gsi, int edge_l { int ioapic = -1; int ioapic_pin = 0; - int idx, bit = 0; #ifdef CONFIG_ACPI_BUS /* Don't set up the ACPI SCI because it's already set up */ @@ -1083,27 +1082,55 @@ int mp_register_gsi (u32 gsi, int edge_l * with redundant pin->gsi mappings (but unique PCI devices); * we only program the IOAPIC on the first. */ - bit = ioapic_pin % 32; - idx = (ioapic_pin < 32) ? 0 : (ioapic_pin / 32); - if (idx > 3) { + if (ioapic_pin > MP_MAX_IOAPIC_PIN) { printk(KERN_ERR "Invalid reference to IOAPIC pin " "%d-%d\n", mp_ioapic_routing[ioapic].apic_id, ioapic_pin); return gsi; } - if ((1<saved_state[0]); pci_read_config_dword(dev, 17*4, &socket->saved_state[1]); + free_irq(socket->cb_irq, socket); pci_disable_device(dev); /* @@ -1073,6 +1074,7 @@ static int yenta_dev_resume (struct pci_ pci_write_config_dword(dev, 17*4, socket->saved_state[1]); pci_enable_device(dev); pci_set_master(dev); + request_irq(socket->cb_irq, yenta_interrupt, SA_SHIRQ, "yenta", socket); if (socket->type && socket->type->restore_state) socket->type->restore_state(socket); _