summaryrefslogtreecommitdiffstats
path: root/linux-2.6-enable-more-pci-autosuspend.patch
diff options
context:
space:
mode:
authorMatthew Garrett <mjg@redhat.com>2010-10-11 14:43:19 -0400
committerMatthew Garrett <mjg@redhat.com>2010-10-11 14:43:19 -0400
commit037ab21c45d98ac399fe4e92d1312213be0c24e3 (patch)
tree05e0a53493a203a3b3650f36b047e25700a6200e /linux-2.6-enable-more-pci-autosuspend.patch
parent72c4be84a676bbaab4d705986894ede0096300a3 (diff)
downloadkernel-037ab21c45d98ac399fe4e92d1312213be0c24e3.tar.gz
kernel-037ab21c45d98ac399fe4e92d1312213be0c24e3.tar.xz
kernel-037ab21c45d98ac399fe4e92d1312213be0c24e3.zip
- linux-2.6-enable-more-pci-autosuspend.patch: Enable more PCI autosuspend
Diffstat (limited to 'linux-2.6-enable-more-pci-autosuspend.patch')
-rw-r--r--linux-2.6-enable-more-pci-autosuspend.patch560
1 files changed, 560 insertions, 0 deletions
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 <linux/pci.h>
+ #include <linux/pci-acpi.h>
+ #include <linux/acpi.h>
++#include <linux/list.h>
+ #include <linux/pm_runtime.h>
+ #include <acpi/acpi_bus.h>
+ #include <acpi/acpi_drivers.h>
+@@ -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)