diff --git a/kernel.spec b/kernel.spec index 515ec5673..737a0628d 100644 --- a/kernel.spec +++ b/kernel.spec @@ -715,6 +715,7 @@ Patch12200: linux-2.6-bluetooth-autosuspend.patch Patch12201: linux-2.6-uvc-autosuspend.patch Patch12202: linux-2.6-qcserial-autosuspend.patch Patch12203: linux-2.6-usb-pci-autosuspend.patch +Patch12204: linux-2.6-enable-more-pci-autosuspend.patch # PCI patches to fix problems with _CRS Patch12221: pci-v2-1-4-resources-ensure-alignment-callback-doesn-t-allocate-below-available-start.patch @@ -1322,6 +1323,7 @@ ApplyPatch linux-2.6-bluetooth-autosuspend.patch ApplyPatch linux-2.6-uvc-autosuspend.patch ApplyPatch linux-2.6-qcserial-autosuspend.patch ApplyPatch linux-2.6-usb-pci-autosuspend.patch +ApplyPatch linux-2.6-enable-more-pci-autosuspend.patch # PCI patches to fix problems with _CRS # ( from https://bugzilla.kernel.org/show_bug.cgi?id=16228#c49 ) @@ -1940,6 +1942,9 @@ fi # || || %changelog +* Mon Oct 11 2010 Matthew Garrett 2.6.36-0.35.rc7.git1 +- linux-2.6-enable-more-pci-autosuspend.patch: Enable more PCI autosuspend + * Wed Oct 06 2010 Kyle McMartin 2.6.36-0.35.rc7.git0 - Linux 2.6.36-rc7 upstream. diff --git a/linux-2.6-enable-more-pci-autosuspend.patch b/linux-2.6-enable-more-pci-autosuspend.patch new file mode 100644 index 000000000..b1f92b134 --- /dev/null +++ b/linux-2.6-enable-more-pci-autosuspend.patch @@ -0,0 +1,560 @@ +diff -up linux-2.6.35.x86_64/drivers/acpi/acpica/aclocal.h.mjg linux-2.6.35.x86_64/drivers/acpi/acpica/aclocal.h +--- linux-2.6.35.x86_64/drivers/acpi/acpica/aclocal.h.mjg 2010-10-04 13:52:05.086789354 -0400 ++++ linux-2.6.35.x86_64/drivers/acpi/acpica/aclocal.h 2010-10-04 13:52:50.948801001 -0400 +@@ -406,16 +406,15 @@ struct acpi_predefined_data { + * + ****************************************************************************/ + +-/* Dispatch info for each GPE -- either a method or handler, cannot be both */ ++/* Dispatch info for each GPE */ + + struct acpi_handler_info { + acpi_event_handler address; /* Address of handler, if any */ + void *context; /* Context to be passed to handler */ +- struct acpi_namespace_node *method_node; /* Method node for this GPE level (saved) */ + u8 orig_flags; /* Original misc info about this GPE */ + }; + +-union acpi_gpe_dispatch_info { ++struct acpi_gpe_dispatch_info { + struct acpi_namespace_node *method_node; /* Method node for this GPE level */ + struct acpi_handler_info *handler; + }; +@@ -425,7 +424,7 @@ union acpi_gpe_dispatch_info { + * NOTE: Important to keep this struct as small as possible. + */ + struct acpi_gpe_event_info { +- union acpi_gpe_dispatch_info dispatch; /* Either Method or Handler */ ++ struct acpi_gpe_dispatch_info dispatch; + struct acpi_gpe_register_info *register_info; /* Backpointer to register info */ + u8 flags; /* Misc info about this GPE */ + u8 gpe_number; /* This GPE */ +diff -up linux-2.6.35.x86_64/drivers/acpi/acpica/evgpe.c.mjg linux-2.6.35.x86_64/drivers/acpi/acpica/evgpe.c +--- linux-2.6.35.x86_64/drivers/acpi/acpica/evgpe.c.mjg 2010-10-04 13:52:05.088789399 -0400 ++++ linux-2.6.35.x86_64/drivers/acpi/acpica/evgpe.c 2010-10-04 13:52:50.950801045 -0400 +@@ -474,9 +474,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_as + * Must check for control method type dispatch one more time to avoid a + * race with ev_gpe_install_handler + */ +- if ((local_gpe_event_info.flags & ACPI_GPE_DISPATCH_MASK) == +- ACPI_GPE_DISPATCH_METHOD) { +- ++ if (local_gpe_event_info.flags & ACPI_GPE_DISPATCH_METHOD) { + /* Allocate the evaluation information block */ + + info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); +@@ -575,41 +573,15 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_eve + } + + /* +- * Dispatch the GPE to either an installed handler, or the control method +- * associated with this GPE (_Lxx or _Exx). If a handler exists, we invoke +- * it and do not attempt to run the method. If there is neither a handler +- * nor a method, we disable this GPE to prevent further such pointless +- * events from firing. ++ * Dispatch the GPE to either any installed handler or control ++ * method associated with this GPE (_Lxx or _Exx). We invoke ++ * the method first in case it has side effects that would be ++ * interfered with if the handler has already altered hardware ++ * state. If there is neither a handler nor a method, we ++ * disable this GPE to prevent further such pointless events ++ * from firing. + */ +- switch (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) { +- case ACPI_GPE_DISPATCH_HANDLER: +- +- /* +- * Invoke the installed handler (at interrupt level) +- * Ignore return status for now. +- * TBD: leave GPE disabled on error? +- */ +- (void)gpe_event_info->dispatch.handler->address(gpe_event_info-> +- dispatch. +- handler-> +- context); +- +- /* It is now safe to clear level-triggered events. */ +- +- if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) == +- ACPI_GPE_LEVEL_TRIGGERED) { +- status = acpi_hw_clear_gpe(gpe_event_info); +- if (ACPI_FAILURE(status)) { +- ACPI_EXCEPTION((AE_INFO, status, +- "Unable to clear GPE[0x%2X]", +- gpe_number)); +- return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); +- } +- } +- break; +- +- case ACPI_GPE_DISPATCH_METHOD: +- ++ if (gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD) { + /* + * Disable the GPE, so it doesn't keep firing before the method has a + * chance to run (it runs asynchronously with interrupts enabled). +@@ -634,10 +606,34 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_eve + "Unable to queue handler for GPE[0x%2X] - event disabled", + gpe_number)); + } +- break; ++ } + +- default: ++ if (gpe_event_info->flags & ACPI_GPE_DISPATCH_HANDLER) { ++ /* ++ * Invoke the installed handler (at interrupt level) ++ * Ignore return status for now. ++ * TBD: leave GPE disabled on error? ++ */ ++ (void)gpe_event_info->dispatch.handler->address(gpe_event_info-> ++ dispatch. ++ handler-> ++ context); ++ ++ /* It is now safe to clear level-triggered events. */ ++ ++ if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) == ++ ACPI_GPE_LEVEL_TRIGGERED) { ++ status = acpi_hw_clear_gpe(gpe_event_info); ++ if (ACPI_FAILURE(status)) { ++ ACPI_EXCEPTION((AE_INFO, status, ++ "Unable to clear GPE[0x%2X]", ++ gpe_number)); ++ return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); ++ } ++ } ++ } + ++ if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK)) { + /* + * No handler or method to run! + * 03/2010: This case should no longer be possible. We will not allow +@@ -658,7 +654,6 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_eve + gpe_number)); + return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); + } +- break; + } + + return_UINT32(ACPI_INTERRUPT_HANDLED); +diff -up linux-2.6.35.x86_64/drivers/acpi/acpica/evgpeinit.c.mjg linux-2.6.35.x86_64/drivers/acpi/acpica/evgpeinit.c +--- linux-2.6.35.x86_64/drivers/acpi/acpica/evgpeinit.c.mjg 2010-10-04 13:52:05.089789421 -0400 ++++ linux-2.6.35.x86_64/drivers/acpi/acpica/evgpeinit.c 2010-10-04 13:52:50.951801067 -0400 +@@ -392,16 +392,7 @@ acpi_ev_match_gpe_method(acpi_handle obj + return_ACPI_STATUS(AE_OK); + } + +- if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == +- ACPI_GPE_DISPATCH_HANDLER) { +- +- /* If there is already a handler, ignore this GPE method */ +- +- return_ACPI_STATUS(AE_OK); +- } +- +- if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == +- ACPI_GPE_DISPATCH_METHOD) { ++ if (gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD) { + /* + * If there is already a method, ignore this method. But check + * for a type mismatch (if both the _Lxx AND _Exx exist) +diff -up linux-2.6.35.x86_64/drivers/acpi/acpica/evgpeutil.c.mjg linux-2.6.35.x86_64/drivers/acpi/acpica/evgpeutil.c +--- linux-2.6.35.x86_64/drivers/acpi/acpica/evgpeutil.c.mjg 2010-10-04 13:52:05.090789443 -0400 ++++ linux-2.6.35.x86_64/drivers/acpi/acpica/evgpeutil.c 2010-10-04 13:52:50.952801089 -0400 +@@ -323,12 +323,11 @@ acpi_ev_delete_gpe_handlers(struct acpi_ + ACPI_GPE_REGISTER_WIDTH) + + j]; + +- if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == +- ACPI_GPE_DISPATCH_HANDLER) { ++ if (gpe_event_info->flags & ACPI_GPE_DISPATCH_HANDLER) { + ACPI_FREE(gpe_event_info->dispatch.handler); + gpe_event_info->dispatch.handler = NULL; + gpe_event_info->flags &= +- ~ACPI_GPE_DISPATCH_MASK; ++ ~ACPI_GPE_DISPATCH_HANDLER; + } + } + } +diff -up linux-2.6.35.x86_64/drivers/acpi/acpica/evxface.c.mjg linux-2.6.35.x86_64/drivers/acpi/acpica/evxface.c +--- linux-2.6.35.x86_64/drivers/acpi/acpica/evxface.c.mjg 2010-10-04 13:52:05.092789487 -0400 ++++ linux-2.6.35.x86_64/drivers/acpi/acpica/evxface.c 2010-10-04 13:52:50.954801133 -0400 +@@ -662,6 +662,8 @@ ACPI_EXPORT_SYMBOL(acpi_remove_notify_ha + * edge- or level-triggered interrupt. + * Address - Address of the handler + * Context - Value passed to the handler on each GPE ++ * keep_method - Whether the existing method should be ++ * displaced or kept + * + * RETURN: Status + * +@@ -671,7 +673,8 @@ ACPI_EXPORT_SYMBOL(acpi_remove_notify_ha + acpi_status + acpi_install_gpe_handler(acpi_handle gpe_device, + u32 gpe_number, +- u32 type, acpi_event_handler address, void *context) ++ u32 type, acpi_event_handler address, void *context, ++ bool keep_method) + { + struct acpi_gpe_event_info *gpe_event_info; + struct acpi_handler_info *handler; +@@ -711,8 +714,7 @@ acpi_install_gpe_handler(acpi_handle gpe + + /* Make sure that there isn't a handler there already */ + +- if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == +- ACPI_GPE_DISPATCH_HANDLER) { ++ if (gpe_event_info->flags & ACPI_GPE_DISPATCH_HANDLER) { + status = AE_ALREADY_EXISTS; + goto free_and_exit; + } +@@ -721,7 +723,6 @@ acpi_install_gpe_handler(acpi_handle gpe + + handler->address = address; + handler->context = context; +- handler->method_node = gpe_event_info->dispatch.method_node; + handler->orig_flags = gpe_event_info->flags & + (ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); + +@@ -733,17 +734,17 @@ acpi_install_gpe_handler(acpi_handle gpe + */ + + if ((handler->orig_flags & ACPI_GPE_DISPATCH_METHOD) +- && !(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) ++ && !(gpe_event_info->flags & ACPI_GPE_CAN_WAKE) && !keep_method) + (void)acpi_raw_disable_gpe(gpe_event_info); + + /* Install the handler */ + + gpe_event_info->dispatch.handler = handler; + +- /* Setup up dispatch flags to indicate handler (vs. method) */ ++ if (!keep_method) ++ gpe_event_info->flags &= ++ ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); + +- gpe_event_info->flags &= +- ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); + gpe_event_info->flags |= (u8) (type | ACPI_GPE_DISPATCH_HANDLER); + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); +@@ -812,8 +813,7 @@ acpi_remove_gpe_handler(acpi_handle gpe_ + + /* Make sure that a handler is indeed installed */ + +- if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) != +- ACPI_GPE_DISPATCH_HANDLER) { ++ if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_HANDLER)) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } +@@ -829,9 +829,8 @@ acpi_remove_gpe_handler(acpi_handle gpe_ + + handler = gpe_event_info->dispatch.handler; + +- /* Restore Method node (if any), set dispatch flags */ ++ /* Set dispatch flags */ + +- gpe_event_info->dispatch.method_node = handler->method_node; + gpe_event_info->flags &= + ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); + gpe_event_info->flags |= handler->orig_flags; +diff -up linux-2.6.35.x86_64/drivers/acpi/ec.c.mjg linux-2.6.35.x86_64/drivers/acpi/ec.c +--- linux-2.6.35.x86_64/drivers/acpi/ec.c.mjg 2010-10-04 13:52:05.094789531 -0400 ++++ linux-2.6.35.x86_64/drivers/acpi/ec.c 2010-10-04 13:52:50.955801156 -0400 +@@ -746,7 +746,7 @@ static int ec_install_handlers(struct ac + return 0; + status = acpi_install_gpe_handler(NULL, ec->gpe, + ACPI_GPE_EDGE_TRIGGERED, +- &acpi_ec_gpe_handler, ec); ++ &acpi_ec_gpe_handler, ec, false); + if (ACPI_FAILURE(status)) + return -ENODEV; + +diff -up linux-2.6.35.x86_64/drivers/acpi/pci_bind.c.mjg linux-2.6.35.x86_64/drivers/acpi/pci_bind.c +--- linux-2.6.35.x86_64/drivers/acpi/pci_bind.c.mjg 2010-10-04 13:52:05.102789707 -0400 ++++ linux-2.6.35.x86_64/drivers/acpi/pci_bind.c 2010-10-04 13:52:50.962801311 -0400 +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -35,6 +36,43 @@ + #define _COMPONENT ACPI_PCI_COMPONENT + ACPI_MODULE_NAME("pci_bind"); + ++static LIST_HEAD(acpi_pci_gpe_devs); ++ ++struct pci_gpe_dev { ++ struct list_head node; ++ struct pci_dev *dev; ++ acpi_handle gpe_device; ++ int gpe_number; ++ struct work_struct work; ++}; ++ ++static void acpi_pci_wake_handler_work(struct work_struct *work) ++{ ++ struct pci_gpe_dev *gpe_dev = container_of(work, struct pci_gpe_dev, ++ work); ++ ++ pci_check_pme_status(gpe_dev->dev); ++ pm_runtime_resume(&gpe_dev->dev->dev); ++ pci_wakeup_event(gpe_dev->dev); ++ if (gpe_dev->dev->subordinate) ++ pci_pme_wakeup_bus(gpe_dev->dev->subordinate); ++} ++ ++static u32 acpi_pci_wake_handler(void *data) ++{ ++ long gpe_number = (long) data; ++ struct pci_gpe_dev *gpe_dev; ++ ++ list_for_each_entry(gpe_dev, &acpi_pci_gpe_devs, node) { ++ if (gpe_number != gpe_dev->gpe_number) ++ continue; ++ ++ schedule_work(&gpe_dev->work); ++ } ++ ++ return ACPI_INTERRUPT_HANDLED; ++} ++ + static int acpi_pci_unbind(struct acpi_device *device) + { + struct pci_dev *dev; +@@ -43,6 +81,30 @@ static int acpi_pci_unbind(struct acpi_d + if (!dev) + goto out; + ++ if (device->wakeup.flags.valid) { ++ struct pci_gpe_dev *gpe_dev; ++ struct pci_gpe_dev *tmp; ++ int gpe_count = 0; ++ int gpe_number = device->wakeup.gpe_number; ++ acpi_handle gpe_device = device->wakeup.gpe_device; ++ ++ list_for_each_entry_safe(gpe_dev, tmp, &acpi_pci_gpe_devs, node) { ++ if (gpe_dev->dev == dev) { ++ flush_work(&gpe_dev->work); ++ list_del(&gpe_dev->node); ++ kfree(gpe_dev); ++ } else if (gpe_dev->gpe_number == gpe_number && ++ gpe_dev->gpe_device == gpe_device) { ++ gpe_count++; ++ } ++ } ++ ++ if (gpe_count == 0) { ++ acpi_remove_gpe_handler(gpe_device, gpe_number, ++ &acpi_pci_wake_handler); ++ } ++ } ++ + device_set_run_wake(&dev->dev, false); + pci_acpi_remove_pm_notifier(device); + +@@ -71,6 +133,30 @@ static int acpi_pci_bind(struct acpi_dev + return 0; + + pci_acpi_add_pm_notifier(device, dev); ++ if (device->wakeup.flags.valid) { ++ struct pci_gpe_dev *gpe_dev; ++ acpi_handle gpe_device = device->wakeup.gpe_device; ++ long gpe_number = device->wakeup.gpe_number; ++ ++ gpe_dev = kmalloc(sizeof(struct pci_gpe_dev), GFP_KERNEL); ++ if (gpe_dev) { ++ gpe_dev->dev = dev; ++ gpe_dev->gpe_device = gpe_device; ++ gpe_dev->gpe_number = gpe_number; ++ INIT_WORK(&gpe_dev->work, acpi_pci_wake_handler_work); ++ ++ acpi_install_gpe_handler(gpe_device, gpe_number, ++ ACPI_GPE_LEVEL_TRIGGERED, ++ &acpi_pci_wake_handler, ++ (void *)gpe_number, ++ true); ++ acpi_gpe_can_wake(device->wakeup.gpe_device, ++ device->wakeup.gpe_number); ++ device->wakeup.flags.run_wake = 1; ++ list_add_tail(&gpe_dev->node, &acpi_pci_gpe_devs); ++ } ++ } ++ + if (device->wakeup.flags.run_wake) + device_set_run_wake(&dev->dev, true); + +diff -up linux-2.6.35.x86_64/drivers/acpi/sleep.c.mjg linux-2.6.35.x86_64/drivers/acpi/sleep.c +--- linux-2.6.35.x86_64/drivers/acpi/sleep.c.mjg 2010-10-04 13:52:05.103789729 -0400 ++++ linux-2.6.35.x86_64/drivers/acpi/sleep.c 2010-10-04 13:52:50.963801333 -0400 +@@ -631,9 +631,9 @@ int acpi_pm_device_sleep_state(struct de + acpi_method[3] = 'W'; + status = acpi_evaluate_integer(handle, acpi_method, NULL, + &d_max); +- if (ACPI_FAILURE(status)) { ++ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + d_max = d_min; +- } else if (d_max < d_min) { ++ } else if (ACPI_SUCCESS(status) && d_max < d_min) { + /* Warn the user of the broken DSDT */ + printk(KERN_WARNING "ACPI: Wrong value from %s\n", + acpi_method); +diff -up linux-2.6.35.x86_64/drivers/char/ipmi/ipmi_si_intf.c.mjg linux-2.6.35.x86_64/drivers/char/ipmi/ipmi_si_intf.c +--- linux-2.6.35.x86_64/drivers/char/ipmi/ipmi_si_intf.c.mjg 2010-10-04 13:52:05.097789597 -0400 ++++ linux-2.6.35.x86_64/drivers/char/ipmi/ipmi_si_intf.c 2010-10-04 13:52:50.958801223 -0400 +@@ -1959,7 +1959,7 @@ static int acpi_gpe_irq_setup(struct smi + info->irq, + ACPI_GPE_LEVEL_TRIGGERED, + &ipmi_acpi_gpe, +- info); ++ info, false); + if (status != AE_OK) { + dev_warn(info->dev, "%s unable to claim ACPI GPE %d," + " running polled\n", DEVICE_NAME, info->irq); +diff -up linux-2.6.35.x86_64/drivers/pci/pci.c.mjg linux-2.6.35.x86_64/drivers/pci/pci.c +--- linux-2.6.35.x86_64/drivers/pci/pci.c.mjg 2010-10-04 13:52:05.105789773 -0400 ++++ linux-2.6.35.x86_64/drivers/pci/pci.c 2010-10-04 13:52:50.965801377 -0400 +@@ -38,6 +38,19 @@ EXPORT_SYMBOL(pci_pci_problems); + + unsigned int pci_pm_d3_delay; + ++static void pci_pme_list_scan(struct work_struct *work); ++ ++static LIST_HEAD(pci_pme_list); ++static DEFINE_MUTEX(pci_pme_list_mutex); ++static DECLARE_DELAYED_WORK(pci_pme_work, pci_pme_list_scan); ++ ++struct pci_pme_device { ++ struct list_head list; ++ struct pci_dev *dev; ++}; ++ ++#define PME_TIMEOUT 1000 /* How long between PME checks */ ++ + static void pci_dev_d3_sleep(struct pci_dev *dev) + { + unsigned int delay = dev->d3_delay; +@@ -1331,6 +1344,32 @@ bool pci_pme_capable(struct pci_dev *dev + return !!(dev->pme_support & (1 << state)); + } + ++static void pci_pme_list_scan(struct work_struct *work) ++{ ++ struct pci_pme_device *pme_dev; ++ ++ mutex_lock(&pci_pme_list_mutex); ++ if (!list_empty(&pci_pme_list)) { ++ list_for_each_entry(pme_dev, &pci_pme_list, list) ++ pci_pme_wakeup(pme_dev->dev, NULL); ++ schedule_delayed_work(&pci_pme_work, msecs_to_jiffies(PME_TIMEOUT)); ++ } ++ mutex_unlock(&pci_pme_list_mutex); ++} ++ ++/** ++ * pci_external_pme - is a device an external PCI PME source? ++ * @dev: PCI device to check ++ * ++ */ ++ ++static bool pci_external_pme(struct pci_dev *dev) ++{ ++ if (pci_is_pcie(dev) || dev->bus->number == 0) ++ return false; ++ return true; ++} ++ + /** + * pci_pme_active - enable or disable PCI device's PME# function + * @dev: PCI device to handle. +@@ -1354,6 +1393,44 @@ void pci_pme_active(struct pci_dev *dev, + + pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); + ++ /* PCI (as opposed to PCIe) PME requires that the device have ++ its PME# line hooked up correctly. Not all hardware vendors ++ do this, so the PME never gets delivered and the device ++ remains asleep. The easiest way around this is to ++ periodically walk the list of suspended devices and check ++ whether any have their PME flag set. The assumption is that ++ we'll wake up often enough anyway that this won't be a huge ++ hit, and the power savings from the devices will still be a ++ win. */ ++ ++ if (pci_external_pme(dev)) { ++ struct pci_pme_device *pme_dev; ++ if (enable) { ++ pme_dev = kmalloc(sizeof(struct pci_pme_device), ++ GFP_KERNEL); ++ if (!pme_dev) ++ goto out; ++ pme_dev->dev = dev; ++ mutex_lock(&pci_pme_list_mutex); ++ list_add(&pme_dev->list, &pci_pme_list); ++ if (list_is_singular(&pci_pme_list)) ++ schedule_delayed_work(&pci_pme_work, ++ msecs_to_jiffies(PME_TIMEOUT)); ++ mutex_unlock(&pci_pme_list_mutex); ++ } else { ++ mutex_lock(&pci_pme_list_mutex); ++ list_for_each_entry(pme_dev, &pci_pme_list, list) { ++ if (pme_dev->dev == dev) { ++ list_del(&pme_dev->list); ++ kfree(pme_dev); ++ break; ++ } ++ } ++ mutex_unlock(&pci_pme_list_mutex); ++ } ++ } ++ ++out: + dev_printk(KERN_DEBUG, &dev->dev, "PME# %s\n", + enable ? "enabled" : "disabled"); + } +diff -up linux-2.6.35.x86_64/drivers/pci/pci.h.mjg linux-2.6.35.x86_64/drivers/pci/pci.h +--- linux-2.6.35.x86_64/drivers/pci/pci.h.mjg 2010-10-04 13:52:05.100789663 -0400 ++++ linux-2.6.35.x86_64/drivers/pci/pci.h 2010-10-04 13:52:50.960801267 -0400 +@@ -63,11 +63,8 @@ struct pci_platform_pm_ops { + extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); + extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); + extern void pci_disable_enabled_device(struct pci_dev *dev); +-extern bool pci_check_pme_status(struct pci_dev *dev); + extern int pci_finish_runtime_suspend(struct pci_dev *dev); +-extern void pci_wakeup_event(struct pci_dev *dev); + extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); +-extern void pci_pme_wakeup_bus(struct pci_bus *bus); + extern void pci_pm_init(struct pci_dev *dev); + extern void platform_pci_wakeup_init(struct pci_dev *dev); + extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); +diff -up linux-2.6.35.x86_64/include/acpi/acpixf.h.mjg linux-2.6.35.x86_64/include/acpi/acpixf.h +--- linux-2.6.35.x86_64/include/acpi/acpixf.h.mjg 2010-10-04 13:52:05.099789641 -0400 ++++ linux-2.6.35.x86_64/include/acpi/acpixf.h 2010-10-04 13:52:50.959801245 -0400 +@@ -253,7 +253,8 @@ acpi_remove_address_space_handler(acpi_h + acpi_status + acpi_install_gpe_handler(acpi_handle gpe_device, + u32 gpe_number, +- u32 type, acpi_event_handler address, void *context); ++ u32 type, acpi_event_handler address, void *context, ++ bool keep_method); + + acpi_status + acpi_remove_gpe_handler(acpi_handle gpe_device, +diff -up linux-2.6.35.x86_64/include/linux/pci.h.mjg linux-2.6.35.x86_64/include/linux/pci.h +--- linux-2.6.35.x86_64/include/linux/pci.h.mjg 2010-10-04 13:52:05.101789685 -0400 ++++ linux-2.6.35.x86_64/include/linux/pci.h 2010-10-04 13:52:50.962801311 -0400 +@@ -819,6 +819,9 @@ pci_power_t pci_target_state(struct pci_ + int pci_prepare_to_sleep(struct pci_dev *dev); + int pci_back_from_sleep(struct pci_dev *dev); + bool pci_dev_run_wake(struct pci_dev *dev); ++bool pci_check_pme_status(struct pci_dev *dev); ++void pci_wakeup_event(struct pci_dev *dev); ++void pci_pme_wakeup_bus(struct pci_bus *bus); + + static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state, + bool enable)