diff mbox

[RFC,5] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support

Message ID 200909050006.15506.rjw@sisk.pl (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Rafael Wysocki Sept. 4, 2009, 10:06 p.m. UTC
From: Rafael J. Wysocki <rjw@sisk.pl>

Some PCI devices (not PCI Express), like PCI add-on cards, can
generate PME#, but they don't have any special platform wake-up
support.  For this reason, even if they generate PME# to wake up the
system from a sleep state, wake-up events are not generated by the
platform.

It turns out that, at least on some systems, PCI bridges and the PCI
host bridge have ACPI GPEs associated with them that, if enabled to
generate wake-up events, allow the system to wake up if one of the
add-on devices asserts PME# while the system is in a sleep state.
Following this observation, if a PCI device without direct ACPI
wake-up support is prepared to wake up the system during a transition
into a sleep state (eg. suspend to RAM), try to configure the bridges
on the path from the device to the root bridge to wake-up the system.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/sleep.c   |    2 +-
 drivers/pci/pci-acpi.c |   26 ++++++++++++++++++++++++--
 2 files changed, 25 insertions(+), 3 deletions(-)

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

Index: linux-2.6/drivers/pci/pci-acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pci-acpi.c
+++ linux-2.6/drivers/pci/pci-acpi.c
@@ -109,10 +109,32 @@  static bool acpi_pci_can_wakeup(struct p
 	return handle ? acpi_bus_can_wakeup(handle) : false;
 }
 
+static void acpi_pci_propagate_wakeup_enable(struct pci_bus *bus, bool enable)
+{
+	while (bus->parent) {
+		struct pci_dev *bridge = bus->self;
+		int ret;
+
+		ret = acpi_pm_device_sleep_wake(&bridge->dev, enable);
+		if (!ret || bridge->is_pcie)
+			return;
+		bus = bus->parent;
+	}
+
+	/* We have reached the root bus. */
+	if (bus->bridge)
+		acpi_pm_device_sleep_wake(bus->bridge, enable);
+}
+
 static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
 {
-	return acpi_pci_can_wakeup(dev) ?
-		acpi_pm_device_sleep_wake(&dev->dev, enable) : 0;
+	if (acpi_pci_can_wakeup(dev))
+		return acpi_pm_device_sleep_wake(&dev->dev, enable);
+
+	if (!dev->is_pcie)
+		acpi_pci_propagate_wakeup_enable(dev->bus, enable);
+
+	return 0;
 }
 
 static struct pci_platform_pm_ops acpi_pci_platform_pm = {
Index: linux-2.6/drivers/acpi/sleep.c
===================================================================
--- linux-2.6.orig/drivers/acpi/sleep.c
+++ linux-2.6/drivers/acpi/sleep.c
@@ -691,7 +691,7 @@  int acpi_pm_device_sleep_wake(struct dev
 	struct acpi_device *adev;
 	int error;
 
-	if (!device_may_wakeup(dev))
+	if (!device_can_wakeup(dev))
 		return -EINVAL;
 
 	handle = DEVICE_ACPI_HANDLE(dev);