aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2009-09-08 17:16:24 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-09-09 17:19:24 -0400
commit0baed8da1ed91b664759f6c7f955b3a804457389 (patch)
tree673cb494cb7e98df71c33e16ab304a5618e263fe /drivers
parent9b83ccd2f14f647936dcfbf4a9a20c501007dd69 (diff)
PCI / ACPI PM: Propagate wake-up enable for devices w/o ACPI support
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. Reviewed-by: Matthew Garrett <mjg59@srcf.ucam.org> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/sleep.c2
-rw-r--r--drivers/pci/pci-acpi.c26
2 files changed, 25 insertions, 3 deletions
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index ab889becd3f3..feece693d773 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -691,7 +691,7 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable)
691 struct acpi_device *adev; 691 struct acpi_device *adev;
692 int error; 692 int error;
693 693
694 if (!device_may_wakeup(dev)) 694 if (!device_can_wakeup(dev))
695 return -EINVAL; 695 return -EINVAL;
696 696
697 handle = DEVICE_ACPI_HANDLE(dev); 697 handle = DEVICE_ACPI_HANDLE(dev);
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 0bddd787490d..33317df47699 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -109,10 +109,32 @@ static bool acpi_pci_can_wakeup(struct pci_dev *dev)
109 return handle ? acpi_bus_can_wakeup(handle) : false; 109 return handle ? acpi_bus_can_wakeup(handle) : false;
110} 110}
111 111
112static void acpi_pci_propagate_wakeup_enable(struct pci_bus *bus, bool enable)
113{
114 while (bus->parent) {
115 struct pci_dev *bridge = bus->self;
116 int ret;
117
118 ret = acpi_pm_device_sleep_wake(&bridge->dev, enable);
119 if (!ret || bridge->is_pcie)
120 return;
121 bus = bus->parent;
122 }
123
124 /* We have reached the root bus. */
125 if (bus->bridge)
126 acpi_pm_device_sleep_wake(bus->bridge, enable);
127}
128
112static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) 129static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
113{ 130{
114 return acpi_pci_can_wakeup(dev) ? 131 if (acpi_pci_can_wakeup(dev))
115 acpi_pm_device_sleep_wake(&dev->dev, enable) : 0; 132 return acpi_pm_device_sleep_wake(&dev->dev, enable);
133
134 if (!dev->is_pcie)
135 acpi_pci_propagate_wakeup_enable(dev->bus, enable);
136
137 return 0;
116} 138}
117 139
118static struct pci_platform_pm_ops acpi_pci_platform_pm = { 140static struct pci_platform_pm_ops acpi_pci_platform_pm = {