ACPI / PM: Allow PCI root bridges to wake up the system
Subsystems (3)
PATCH 1/1 ACPI / PM: Allow PCI root bridges to wake up the system
From: Rafael J. Wysocki <[email protected]>
If PCI root bridge is declared in the ACPI tables as a wake-up device, allow it to wake up the system by default.
This allows add-on PCI devices to work as wake-up devices on some systems, where PME# asserted by an add-on device causes the root bridge GPE to generate a wake-up event, without using /proc/acpi/wakeup to change the root bridge wake-up setting.
--- drivers/acpi/glue.c | 3 +++ drivers/acpi/pci_root.c | 25 +++++++++++++------------ include/acpi/acpi_bus.h | 1 + 3 files changed, 17 insertions(+), 12 deletions(-)
drivers/acpi/glue.c +3 -0
@@ -168,6 +168,9 @@ static int acpi_bind_one(struct device * "physical_node");if (acpi_dev->wakeup.flags.valid) {device_set_wakeup_capable (dev, true); +/* Allow PCI root bridges to wake up the system. */ +if (acpi_dev_is_root_bridge (acpi_dev)) + acpi_dev->wakeup.state.enabled = true;device_set_wakeup_enable (dev, acpi_dev->wakeup.state.enabled); }
drivers/acpi/pci_root.c +13 -12
@@ -138,26 +138,27 @@ acpi_handle acpi_get_pci_rootbridge_handEXPORT_SYMBOL_GPL (acpi_get_pci_rootbridge_handle);/** +* acpi_dev_is_root_bridge - determine if an ACPI device is a PCI root bridge +* @device: ACPI device to check. +*/ +bool acpi_dev_is_root_bridge (struct acpi_device *device) +{ +return !acpi_match_device_ids (device, root_device_ids); +} + +/** * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge -* @handle - the ACPI CA node in question. -* -* Note: we could make this API take a struct acpi_device * instead, but -* for now, it's more convenient to operate on an acpi_handle. +* @handle: the ACPI CA node in question. */ int acpi_is_root_bridge (acpi_handle handle) {int ret;struct acpi_device *device; - ret =acpi_bus_get_device (handle, &device); + ret = !acpi_bus_get_device (handle, &device);if (ret) -return 0 ; - - ret =acpi_match_device_ids (device, root_device_ids); -if (ret) -return 0 ; -else -return 1 ; + ret =acpi_dev_is_root_bridge (device); +return ret; }EXPORT_SYMBOL_GPL (acpi_is_root_bridge);
include/acpi/acpi_bus.h +1 -0
@@ -371,6 +371,7 @@ struct device *acpi_get_physical_device(/* helper */ acpi_handle acpi_get_child (acpi_handle ,acpi_integer ); +bool acpi_dev_is_root_bridge (struct acpi_device *);int acpi_is_root_bridge (acpi_handle );acpi_handle acpi_get_pci_rootbridge_handle (unsigned int ,unsigned int );#define DEVICE_ACPI_HANDLE (dev) ((acpi_handle)((dev)->archdata.acpi_handle))
Quoted 9 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
If PCI root bridge is declared in the ACPI tables as a wake-up device, allow it to wake up the system by default.
This allows add-on PCI devices to work as wake-up devices on some systems, where PME# asserted by an add-on device causes the root bridge GPE to generate a wake-up event, without using
/proc/acpi/wakeupto change the root bridge wake-up setting.
One piece is missing from the patch, I'll prepare an updated one shortly.
Thanks,
Rafael
Quoted 77 lines
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/acpi/glue.c | 3 +++ drivers/acpi/pci_root.c | 25 +++++++++++++------------ include/acpi/acpi_bus.h | 1 + 3 files changed, 17 insertions(+), 12 deletions(-)Index:
linux-2.6/drivers/acpi/glue.c
===================================================================drivers/acpi/glue.c +3
-0@@ -168,6 +168,9 @@ static int acpi_bind_one(struct device * "physical_node");if (acpi_dev->wakeup.flags.valid) {device_set_wakeup_capable (dev, true); +/* Allow PCI root bridges to wake up the system. */ +if (acpi_dev_is_root_bridge (acpi_dev)) + acpi_dev->wakeup.state.enabled = true;device_set_wakeup_enable (dev, acpi_dev->wakeup.state.enabled); }Index:
linux-2.6/drivers/acpi/pci_root.c
===================================================================drivers/acpi/pci_root.c +13
-12@@ -138,26 +138,27 @@ acpi_handle acpi_get_pci_rootbridge_handEXPORT_SYMBOL_GPL (acpi_get_pci_rootbridge_handle);/** +* acpi_dev_is_root_bridge - determine if an ACPI device is a PCI root bridge +* @device: ACPI device to check. +*/ +bool acpi_dev_is_root_bridge (struct acpi_device *device) +{ +return !acpi_match_device_ids (device, root_device_ids); +} + +/** * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge -* @handle - the ACPI CA node in question. -* -* Note: we could make this API take a struct acpi_device * instead, but -* for now, it's more convenient to operate on an acpi_handle. +* @handle: the ACPI CA node in question. */ int acpi_is_root_bridge (acpi_handle handle) {int ret;struct acpi_device *device; - ret =acpi_bus_get_device (handle, &device); + ret = !acpi_bus_get_device (handle, &device);if (ret) -return 0 ; - - ret =acpi_match_device_ids (device, root_device_ids); -if (ret) -return 0 ; -else -return 1 ; + ret =acpi_dev_is_root_bridge (device); +return ret; }EXPORT_SYMBOL_GPL (acpi_is_root_bridge);Index:
linux-2.6/include/acpi/acpi_bus.h
===================================================================include/acpi/acpi_bus.h +1
-0@@ -371,6 +371,7 @@ struct device *acpi_get_physical_device(/* helper */ acpi_handle acpi_get_child (acpi_handle ,acpi_integer ); +bool acpi_dev_is_root_bridge (struct acpi_device *);int acpi_is_root_bridge (acpi_handle );acpi_handle acpi_get_pci_rootbridge_handle (unsigned int ,unsigned int );#define DEVICE_ACPI_HANDLE (dev) ((acpi_handle)((dev)->archdata.acpi_handle))
RFC PATCH 1/1 ACPI / PM: Allow PCI root bridges to wake up the system
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
If PCI root bridge is declared in the ACPI tables as a wake-up device, allow it to wake up the system by default.
This allows add-on PCI devices to work as wake-up devices on some systems, where PME# asserted by an add-on device causes the root bridge GPE to generate a wake-up event, without using
/proc/acpi/wakeupto change the root bridge wake-up setting.
Updated patch is appended, but there's a problem with it. Namely, when the root bridge GPE is shared with other devices, those devices are also enabled to wake up and they may be kind of "interesting" (on one of my test boxes they include the ethernet and audio adapters). I'm not sure what to do about that.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: ACPI / PM: Allow PCI root bridges to wake up the system
If PCI root bridge is declared in the ACPI tables as a wake-up device, allow it to wake up the system by default.
This allows add-on PCI devices to work as wake-up devices on some systems, where PME# asserted by an add-on device causes the root bridge GPE to generate a wake-up event, without using /proc/acpi/wakeup to change the root bridge wake-up setting.
--- drivers/acpi/glue.c | 9 +++++++++ drivers/acpi/pci_root.c | 25 +++++++++++++------------ drivers/acpi/proc.c | 29 +---------------------------- drivers/acpi/sleep.h | 2 ++ drivers/acpi/wakeup.c | 35 +++++++++++++++++++++++++++++++++++ include/acpi/acpi_bus.h | 1 + 6 files changed, 61 insertions(+), 40 deletions(-)
drivers/acpi/glue.c +9 -0
@@ -13,6 +13,8 @@#include <linux/acpi.h> #include <linux/pm_link.h> +#include "sleep.h" +#define ACPI_GLUE_DEBUG 0#if ACPI_GLUE_DEBUG #define DBG(x...) printk(PREFIX x)
@@ -168,6 +170,13 @@ static int acpi_bind_one(struct device * "physical_node");if (acpi_dev->wakeup.flags.valid) {device_set_wakeup_capable (dev, true); +/* Allow PCI root bridges to wake up the system. */ +if (acpi_dev_is_root_bridge (acpi_dev)) { +mutex_lock (&acpi_device_lock); + acpi_dev->wakeup.state.enabled = true; +propagate_enable_wakeup (acpi_dev); +mutex_unlock (&acpi_device_lock); + }device_set_wakeup_enable (dev, acpi_dev->wakeup.state.enabled); }
drivers/acpi/pci_root.c +13 -12
@@ -138,26 +138,27 @@ acpi_handle acpi_get_pci_rootbridge_handEXPORT_SYMBOL_GPL (acpi_get_pci_rootbridge_handle);/** +* acpi_dev_is_root_bridge - determine if an ACPI device is a PCI root bridge +* @device: ACPI device to check. +*/ +bool acpi_dev_is_root_bridge (struct acpi_device *device) +{ +return !acpi_match_device_ids (device, root_device_ids); +} + +/** * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge -* @handle - the ACPI CA node in question. -* -* Note: we could make this API take a struct acpi_device * instead, but -* for now, it's more convenient to operate on an acpi_handle. +* @handle: the ACPI CA node in question. */ int acpi_is_root_bridge (acpi_handle handle) {int ret;struct acpi_device *device; - ret =acpi_bus_get_device (handle, &device); + ret = !acpi_bus_get_device (handle, &device);if (ret) -return 0 ; - - ret =acpi_match_device_ids (device, root_device_ids); -if (ret) -return 0 ; -else -return 1 ; + ret =acpi_dev_is_root_bridge (device); +return ret; }EXPORT_SYMBOL_GPL (acpi_is_root_bridge);
include/acpi/acpi_bus.h +1 -0
@@ -371,6 +371,7 @@ struct device *acpi_get_physical_device(/* helper */ acpi_handle acpi_get_child (acpi_handle ,acpi_integer ); +bool acpi_dev_is_root_bridge (struct acpi_device *);int acpi_is_root_bridge (acpi_handle );acpi_handle acpi_get_pci_rootbridge_handle (unsigned int ,unsigned int );#define DEVICE_ACPI_HANDLE (dev) ((acpi_handle)((dev)->archdata.acpi_handle))
drivers/acpi/proc.c +1 -28
@@ -377,14 +377,6 @@ acpi_system_wakeup_device_seq_show(strucreturn 0 ; } -static void physical_device_enable_wakeup (struct acpi_device *adev) -{ -struct device *dev =acpi_get_physical_device (adev->handle); - -if (dev &&device_can_wakeup (dev)) -device_set_wakeup_enable (dev, adev->wakeup.state.enabled); -} - static ssize_t acpi_system_write_wakeup_device(struct file *file, const char __user * buffer,
@@ -420,26 +412,7 @@ acpi_system_write_wakeup_device(struct f }if (found_dev) {physical_device_enable_wakeup (found_dev); -list_for_each_safe (node, next, &acpi_wakeup_device_list) { -struct acpi_device *dev =container_of (node, - struct - acpi_device, - wakeup_list); - -if ((dev != found_dev) && - (dev->wakeup.gpe_number == - found_dev->wakeup.gpe_number) - && (dev->wakeup.gpe_device == - found_dev->wakeup.gpe_device)) { -printk (KERN_WARNING -"ACPI: '%s' and '%s' have the same GPE, " -"can't disable/enable one seperately\n" , - dev->pnp.bus_id, found_dev->pnp.bus_id); - dev->wakeup.state.enabled = - found_dev->wakeup.state.enabled; -physical_device_enable_wakeup (dev); - } - } +propagate_enable_wakeup (found_dev); }mutex_unlock (&acpi_device_lock);return count;
drivers/acpi/wakeup.c +35 -0
@@ -124,6 +124,41 @@ void acpi_disable_wakeup_device(u8 sleep } } +void physical_device_enable_wakeup (struct acpi_device *adev) +{ +struct device *dev =acpi_get_physical_device (adev->handle); + +if (dev &&device_can_wakeup (dev)) +device_set_wakeup_enable (dev, adev->wakeup.state.enabled); +} + +void propagate_enable_wakeup (struct acpi_device *wakeup_dev) +{ +struct list_head *node, *next; + +list_for_each_safe (node, next, &acpi_wakeup_device_list) { +struct acpi_device *dev = + container_of(node, struct acpi_device, wakeup_list); + +if (!dev->wakeup.flags.valid) +continue ; + +if (dev == wakeup_dev) +continue ; + +if (dev->wakeup.gpe_number != wakeup_dev->wakeup.gpe_number + || dev->wakeup.gpe_device != wakeup_dev->wakeup.gpe_device) +continue ; + +printk (KERN_WARNING "ACPI: '%s' and '%s' share a GPE, " +"unable to disable/enable one seperately\n" , + dev->pnp.bus_id, wakeup_dev->pnp.bus_id); + + dev->wakeup.state.enabled = wakeup_dev->wakeup.state.enabled; +physical_device_enable_wakeup (dev); + } +} +int __init acpi_wakeup_device_init (void ) {struct list_head *node, *next;
drivers/acpi/sleep.h +2 -0
@@ -5,6 +5,8 @@ extern int acpi_suspend (u32 state);extern void acpi_enable_wakeup_device_prep (u8 sleep_state);extern void acpi_enable_wakeup_device (u8 sleep_state);extern void acpi_disable_wakeup_device (u8 sleep_state); +extern void physical_device_enable_wakeup (struct acpi_device *adev); +extern void propagate_enable_wakeup (struct acpi_device *wakeup_dev);extern struct list_head acpi_wakeup_device_list;extern struct mutex acpi_device_lock;
Quoted 4 lines · Rafael J. Wysocki
Updated patch is appended, but there's a problem with it. Namely, when the root bridge GPE is shared with other devices, those devices are also enabled to wake up and they may be kind of "interesting" (on one of my test boxes they include the ethernet and audio adapters). I'm not sure what to do about that.
Perhaps only enable it if a child device is enabled? This is difficult with the current code - the refcounted GPE stuff I'm working on ought to make it easier.
Signature · 1 line
Matthew Garrett | [email protected]
Quoted 8 lines · Matthew Garrett
Quoted 4 lines · Rafael J. Wysocki
Updated patch is appended, but there's a problem with it. Namely, when the root bridge GPE is shared with other devices, those devices are also enabled to wake up and they may be kind of "interesting" (on one of my test boxes they include the ethernet and audio adapters). I'm not sure what to do about that.
Perhaps only enable it if a child device is enabled?
Well, the problem is that we have to enable the devices that share the GPE to wake up together, but the child device we _really_ want to wake-up need not be any of them.
Also, the child device may be enabled to wake up after we've run the "glue" code.
Well, perhaps it's better to rework pci_enable_wake() so that it, if the device doesn't have an ACPI handle and is not PCIe, it will go and enable the root bridge to wake up as well?
I wonder what about the bridges between the desired wake-up device and the root bridge. I guess they also should be enabled to wake-up, so that they can forward the PME# in case the system is designed this way.
Thoughts?
Quoted 2 lines
This is difficult with the current code - the refcounted GPE stuff I'm working on ought to make it easier.
Ah, I think that's going to be useful.
Thanks,
Rafael
Quoted 17 lines · Rafael J. Wysocki
Quoted 8 lines · Matthew Garrett
Quoted 4 lines · Rafael J. Wysocki
Updated patch is appended, but there's a problem with it. Namely, when the root bridge GPE is shared with other devices, those devices are also enabled to wake up and they may be kind of "interesting" (on one of my test boxes they include the ethernet and audio adapters). I'm not sure what to do about that.
Perhaps only enable it if a child device is enabled?
Well, the problem is that we have to enable the devices that share the GPE to wake up together, but the child device we _really_ want to wake-up need not be any of them.
Also, the child device may be enabled to wake up after we've run the "glue" code.
Quoted 7 lines
Well, perhaps it's better to rework
pci_enable_wake()so that it, if the device doesn't have an ACPI handle and is not PCIe, it will go and enable the root bridge to wake up as well?I wonder what about the bridges between the desired wake-up device and the root bridge. I guess they also should be enabled to wake-up, so that they can forward the PME# in case the system is designed this way.
Below is a prototype of what I was thinking of, although I'm not sure if that's enough.
Comments welcome.
Rafael
--- drivers/pci/pci.c | 134 +++++++++++++++++++++++++++++++--------------------- include/linux/pci.h | 7 ++ 2 files changed, 87 insertions(+), 54 deletions(-)
drivers/pci/pci.c +81 -53
@@ -1198,71 +1198,100 @@ void pci_pme_active(struct pci_dev *dev, }/** -* pci_enable_wake - enable PCI device as wakeup event source -* @dev: PCI device affected -* @state: PCI state from which device will issue wakeup events -* @enable: True to enable event generation; false to disable -* -* This enables the device as a wakeup event source, or disables it. -* When such events involves platform-specific hooks, those hooks are -* called automatically by this routine. +* pci_enable_wakeup - Enable given device to wake up the system. +* @dev: Device to become a wake-up event source. * -* Devices with legacy power management (no standard PCI PM capabilities) -* always require such platform hooks. +* If the device itself has no platform support for wake-up, we go to it's +* parent bridge and try to enable it to wake up the system and so on, +* recursively, up to the root bridge. * -* RETURN VALUE: -* 0 is returned on success -* -EINVAL is returned if device is not supposed to wake up the system -* Error code depending on the platform is returned if both the platform and -* the native mechanism fail to enable the generation of wake-up events +* According to "PCI System Architecture" 4th ed. by Tom Shanley & Don Anderson +* we should be doing PME# wake enable followed by ACPI wake enable. To disable +* wake-up we call the platform first, for symmetry. */ -int pci_enable_wake (struct pci_dev *dev,pci_power_t state,bool enable) +static int pci_enable_wakeup (struct pci_dev *dev) { -int error =0 ; -bool pme_done = false; - -if (enable && !device_may_wakeup (&dev->dev)) +if (!device_may_wakeup (&dev->dev))return -EINVAL ; -/* -* According to "PCI System Architecture" 4th ed. by Tom Shanley & Don -* Anderson we should be doing PME# wake enable followed by ACPI wake -* enable. To disable wake-up we call the platform first, for symmetry. -*/ +dev_info (&dev->dev,"PCI: enabling wake-up.\n" ); + +if (atomic_add_return (1 , &dev->wakeup_count) !=1 ) +return 0 ; + +pci_pme_active (dev, true); +if (platform_pci_can_wakeup (dev)) +platform_pci_sleep_wake (dev, true); +else if (!dev->is_pcie && dev->bus && dev->bus->self) +pci_enable_wakeup (dev->bus->self); + + +return 0 ; +} + +/** +* pci_disable_wakeup - Disable given device from waking up the system. +* @dev: Device to handle. +* +* If the device itself has no platform support for wake-up, we go to it's +* parent bridge and try to disable it from waking up the system and so on, +* recursively, up to the root bridge. +*/ +static int pci_disable_wakeup (struct pci_dev *dev) +{ +int count; + +if (!device_may_wakeup (&dev->dev)) +return -EINVAL ; -if (!enable &&platform_pci_can_wakeup (dev)) - error =platform_pci_sleep_wake (dev, false); +dev_info (&dev->dev,"PCI: disabling wake-up.\n" ); -if (!enable ||pci_pme_capable (dev, state)) { -pci_pme_active (dev, enable); - pme_done = true; + count =atomic_add_return (-1 , &dev->wakeup_count); +if (count <0 ) { +dev_err (&dev->dev,"unbalanced %s!\n" , __func__); +return -EALREADY ; + }else if (count >0 ) { +return 0 ; } -if (enable &&platform_pci_can_wakeup (dev)) - error =platform_pci_sleep_wake (dev, true); +if (platform_pci_can_wakeup (dev)) { +platform_pci_sleep_wake (dev, false); +pci_pme_active (dev, false); + }else { +pci_pme_active (dev, false); +if (!dev->is_pcie && dev->bus && dev->bus->self) +pci_disable_wakeup (dev->bus->self); + } -return pme_done ?0 : error; +return 0 ; }/** -* pci_wake_from_d3 - enable/disable device to wake up from D3_hot or D3_cold -* @dev: PCI device to prepare -* @enable: True to enable wake-up event generation; false to disable +* pci_enable_wake - Enable PCI device as wake-up event source. +* @dev: PCI device affected. +* @state: Ignored. +* @enable: True to enable wake-up event generation; false to disable. +* +* Enable the device as a wake-up event source or disable it, depending on the +* value of @enable. The generation of such events may require us to set up the +* platform appropriately, which involves executing some platform-specific +* hooks. If they are needed, they will be called automatically. * -* Many drivers want the device to wake up the system from D3_hot or D3_cold -* and this function allows them to set that up cleanly - pci_enable_wake() -* should not be called twice in a row to enable wake-up due to PCI PM vs ACPI -* ordering constraints. +* Devices with legacy power management (no standard PCI PM capabilities) always +* require such platform hooks. * -* This function only returns error code if the device is not capable of -* generating PME# from both D3_hot and D3_cold, and the platform is unable to -* enable wake-up power for it. +* RETURN VALUE: +* 0 is returned on success. +* -EINVAL is returned if the device is not supposed to wake up the system. +* -EALREADY is returned on an attempt to disable a device that is not enabled +* to wake-up. */ -int pci_wake_from_d3 (struct pci_dev *dev,bool enable) +int pci_enable_wake (struct pci_dev *dev,pci_power_t state,bool enable) { -return pci_pme_capable (dev, PCI_D3cold) ? -pci_enable_wake (dev, PCI_D3cold, enable) : -pci_enable_wake (dev, PCI_D3hot, enable); +if (!enable && !atomic_read (&dev->wakeup_count)) +return 0 ; + +return enable ?pci_enable_wakeup (dev) :pci_disable_wakeup (dev); }/**
@@ -1323,18 +1352,16 @@ pci_power_t pci_target_state(struct pci_*/ int pci_prepare_to_sleep (struct pci_dev *dev) { -pci_power_t target_state =pci_target_state (dev);int error; +pci_power_t target_state =pci_target_state (dev);if (target_state ==PCI_POWER_ERROR )return -EIO ; -pci_enable_wake (dev, target_state,device_may_wakeup (&dev->dev)); - +pci_enable_wakeup (dev); error =pci_set_power_state (dev, target_state); -if (error) -pci_enable_wake (dev, target_state, false); +pci_disable_wakeup (dev);return error; }
@@ -1347,7 +1374,7 @@ int pci_prepare_to_sleep(struct pci_dev*/ int pci_back_from_sleep (struct pci_dev *dev) { -pci_enable_wake (dev,PCI_D0 , false); +pci_disable_wakeup (dev);return pci_set_power_state (dev,PCI_D0 ); }
@@ -1362,6 +1389,7 @@ void pci_pm_init(struct pci_dev *dev)device_enable_async_suspend (&dev->dev, true); dev->pm_cap =0 ; +atomic_set (&dev->wakeup_count,0 );/* find PCI PM capability in list */ pm =pci_find_capability (dev,PCI_CAP_ID_PM );
include/linux/pci.h +6 -1
@@ -241,6 +241,7 @@ struct pci_dev { unsigned int d1_support:1; /* Low power state D1 is supported */ unsigned int d2_support:1; /* Low power state D2 is supported */ unsigned int no_d1d2:1; /* Only allow D0 and D3 */ +atomic_t wakeup_count;#ifdef CONFIG_PCIEASPM struct pcie_link_state *link_state;/* ASPM link state. */
@@ -734,11 +735,15 @@ pci_power_t pci_choose_state(struct pci_bool pci_pme_capable (struct pci_dev *dev,pci_power_t state);void pci_pme_active (struct pci_dev *dev,bool enable);int pci_enable_wake (struct pci_dev *dev,pci_power_t state,bool enable); -int pci_wake_from_d3 (struct pci_dev *dev,bool enable);pci_power_t pci_target_state (struct pci_dev *dev);int pci_prepare_to_sleep (struct pci_dev *dev);int pci_back_from_sleep (struct pci_dev *dev); +static inline int pci_wake_from_d3 (struct pci_dev *dev,bool enable) +{ +return pci_enable_wake (dev, PCI_D3hot, enable); +} +/* Functions for PCI Hotplug drivers to use */ int pci_bus_find_capability (struct pci_bus *bus,unsigned int devfn,int cap);#ifdef CONFIG_HOTPLUG
cover PCI/ACPI PM: Propagate wake-up enable upstream
Quoted 22 lines · Rafael J. Wysocki
Quoted 21 lines · Rafael J. Wysocki
Quoted 8 lines · Matthew Garrett
Quoted 4 lines · Rafael J. Wysocki
Updated patch is appended, but there's a problem with it. Namely, when the root bridge GPE is shared with other devices, those devices are also enabled to wake up and they may be kind of "interesting" (on one of my test boxes they include the ethernet and audio adapters). I'm not sure what to do about that.
Perhaps only enable it if a child device is enabled?
Well, the problem is that we have to enable the devices that share the GPE to wake up together, but the child device we _really_ want to wake-up need not be any of them.
Also, the child device may be enabled to wake up after we've run the "glue" code.
Well, perhaps it's better to rework
pci_enable_wake()so that it, if the device doesn't have an ACPI handle and is not PCIe, it will go and enable the root bridge to wake up as well?
Quoted 8 lines
Quoted 3 lines
I wonder what about the bridges between the desired wake-up device and the root bridge. I guess they also should be enabled to wake-up, so that they can forward the PME# in case the system is designed this way.
Below is a prototype of what I was thinking of, although I'm not sure if that's enough.
Comments welcome.
Hm, no comments. :-(
In the meantime I spent some time on this problem and prepared the following series of patches.
The last one has to be tested on the system it was invented for, but that's going to take some time.
Comments welcome.
Rafael
cover PCI/ACPI PM: Propagate wake-up enable upstream
In reply to Rafael J. WysockiQuoted 22 lines · Rafael J. Wysocki
Quoted 21 lines · Rafael J. Wysocki
Quoted 8 lines · Matthew Garrett
Quoted 4 lines · Rafael J. Wysocki
Updated patch is appended, but there's a problem with it. Namely, when the root bridge GPE is shared with other devices, those devices are also enabled to wake up and they may be kind of "interesting" (on one of my test boxes they include the ethernet and audio adapters). I'm not sure what to do about that.
Perhaps only enable it if a child device is enabled?
Well, the problem is that we have to enable the devices that share the GPE to wake up together, but the child device we _really_ want to wake-up need not be any of them.
Also, the child device may be enabled to wake up after we've run the "glue" code.
Well, perhaps it's better to rework
pci_enable_wake()so that it, if the device doesn't have an ACPI handle and is not PCIe, it will go and enable the root bridge to wake up as well?
Quoted 8 lines
Quoted 3 lines
I wonder what about the bridges between the desired wake-up device and the root bridge. I guess they also should be enabled to wake-up, so that they can forward the PME# in case the system is designed this way.
Below is a prototype of what I was thinking of, although I'm not sure if that's enough.
Comments welcome.
Hm, no comments. :-(
In the meantime I spent some time on this problem and prepared the following series of patches.
The last one has to be tested on the system it was invented for, but that's going to take some time.
Comments welcome.
Rafael
RFC PATCH 1/4 PCI PM: Simplify PCI wake-up code
From: Rafael J. Wysocki <[email protected]>
Rework the PCI wake-up code so that it's easier to read without changing the functionality.
--- drivers/pci/pci-acpi.c | 10 +++++++--- drivers/pci/pci.c | 25 ++++++++++++++----------- 2 files changed, 21 insertions(+), 14 deletions(-)
drivers/pci/pci.c +14 -11
@@ -1218,8 +1218,7 @@ void pci_pme_active(struct pci_dev *dev,*/ int pci_enable_wake (struct pci_dev *dev,pci_power_t state,bool enable) { -int error =0 ; -bool pme_done = false; +int ret =0 ;if (enable && !device_may_wakeup (&dev->dev))return -EINVAL ;
@@ -1230,18 +1229,22 @@ int pci_enable_wake(struct pci_dev *dev,* enable. To disable wake-up we call the platform first, for symmetry. */ -if (!enable &&platform_pci_can_wakeup (dev)) - error =platform_pci_sleep_wake (dev, false); - -if (!enable ||pci_pme_capable (dev, state)) { -pci_pme_active (dev, enable); - pme_done = true; - } +if (enable) { +int error; -if (enable &&platform_pci_can_wakeup (dev)) +if (pci_pme_capable (dev, state)) +pci_pme_active (dev, true); +else + ret =1 ; error =platform_pci_sleep_wake (dev, true); +if (ret) + ret = error; + }else { +platform_pci_sleep_wake (dev, false); +pci_pme_active (dev, false); + } -return pme_done ?0 : error; +return ret; }/**
drivers/pci/pci-acpi.c +7 -3
@@ -111,12 +111,16 @@ static bool acpi_pci_can_wakeup(struct pstatic int acpi_pci_sleep_wake (struct pci_dev *dev,bool enable) { -int error =acpi_pm_device_sleep_wake (&dev->dev, enable); +int error; +if (!acpi_pci_can_wakeup (dev)) +return 0 ; + + error =acpi_pm_device_sleep_wake (&dev->dev, enable);if (!error) -dev_printk (KERN_INFO , &dev->dev, -"wake-up capability %s by ACPI\n" , +dev_info (&dev->dev,"wake-up capability %s by ACPI\n" , enable ?"enabled" :"disabled" ); +return error; }
RFC PATCH 1/4 PCI PM: Simplify PCI wake-up code
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Rework the PCI wake-up code so that it's easier to read without changing the functionality.
--- drivers/pci/pci-acpi.c | 10 +++++++--- drivers/pci/pci.c | 25 ++++++++++++++----------- 2 files changed, 21 insertions(+), 14 deletions(-)
drivers/pci/pci.c +14 -11
@@ -1218,8 +1218,7 @@ void pci_pme_active(struct pci_dev *dev,*/ int pci_enable_wake (struct pci_dev *dev,pci_power_t state,bool enable) { -int error =0 ; -bool pme_done = false; +int ret =0 ;if (enable && !device_may_wakeup (&dev->dev))return -EINVAL ;
@@ -1230,18 +1229,22 @@ int pci_enable_wake(struct pci_dev *dev,* enable. To disable wake-up we call the platform first, for symmetry. */ -if (!enable &&platform_pci_can_wakeup (dev)) - error =platform_pci_sleep_wake (dev, false); - -if (!enable ||pci_pme_capable (dev, state)) { -pci_pme_active (dev, enable); - pme_done = true; - } +if (enable) { +int error; -if (enable &&platform_pci_can_wakeup (dev)) +if (pci_pme_capable (dev, state)) +pci_pme_active (dev, true); +else + ret =1 ; error =platform_pci_sleep_wake (dev, true); +if (ret) + ret = error; + }else { +platform_pci_sleep_wake (dev, false); +pci_pme_active (dev, false); + } -return pme_done ?0 : error; +return ret; }/**
drivers/pci/pci-acpi.c +7 -3
@@ -111,12 +111,16 @@ static bool acpi_pci_can_wakeup(struct pstatic int acpi_pci_sleep_wake (struct pci_dev *dev,bool enable) { -int error =acpi_pm_device_sleep_wake (&dev->dev, enable); +int error; +if (!acpi_pci_can_wakeup (dev)) +return 0 ; + + error =acpi_pm_device_sleep_wake (&dev->dev, enable);if (!error) -dev_printk (KERN_INFO , &dev->dev, -"wake-up capability %s by ACPI\n" , +dev_info (&dev->dev,"wake-up capability %s by ACPI\n" , enable ?"enabled" :"disabled" ); +return error; }
RFC PATCH 2/4 PCI/ACPI PM: Rework some debug messages
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Move a debug message from acpi_pci_sleep_wake() to acpi_pm_device_sleep_wake() and use the standard dev_*() macros in there.
--- drivers/acpi/sleep.c | 10 ++++++++-- drivers/pci/pci-acpi.c | 13 ++----------- 2 files changed, 10 insertions(+), 13 deletions(-)
drivers/acpi/sleep.c +8 -2
@@ -689,19 +689,25 @@ int acpi_pm_device_sleep_wake(struct dev {acpi_handle handle;struct acpi_device *adev; +int error;if (!device_may_wakeup (dev))return -EINVAL ; handle =DEVICE_ACPI_HANDLE (dev);if (!handle ||ACPI_FAILURE (acpi_bus_get_device (handle, &adev))) { -printk (KERN_DEBUG "ACPI handle has no context!\n" ); +dev_dbg (dev,"ACPI handle has no context in %s!\n" , __func__);return -ENODEV ; } -return enable ? + error = enable ?acpi_enable_wakeup_device_power (adev, acpi_target_sleep_state) :acpi_disable_wakeup_device_power (adev); +if (!error) +dev_info (dev,"wake-up capability %s by ACPI\n" , + enable ?"enabled" :"disabled" ); + +return error; }#endif
drivers/pci/pci-acpi.c +2 -11
@@ -111,17 +111,8 @@ static bool acpi_pci_can_wakeup(struct pstatic int acpi_pci_sleep_wake (struct pci_dev *dev,bool enable) { -int error; - -if (!acpi_pci_can_wakeup (dev)) -return 0 ; - - error =acpi_pm_device_sleep_wake (&dev->dev, enable); -if (!error) -dev_info (&dev->dev,"wake-up capability %s by ACPI\n" , - enable ?"enabled" :"disabled" ); - -return error; +return acpi_pci_can_wakeup (dev) ? +acpi_pm_device_sleep_wake (&dev->dev, enable) :0 ; }static struct pci_platform_pm_ops acpi_pci_platform_pm = {
RFC PATCH 2/4 PCI/ACPI PM: Rework some debug messages
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Move a debug message from acpi_pci_sleep_wake() to acpi_pm_device_sleep_wake() and use the standard dev_*() macros in there.
--- drivers/acpi/sleep.c | 10 ++++++++-- drivers/pci/pci-acpi.c | 13 ++----------- 2 files changed, 10 insertions(+), 13 deletions(-)
drivers/acpi/sleep.c +8 -2
@@ -689,19 +689,25 @@ int acpi_pm_device_sleep_wake(struct dev {acpi_handle handle;struct acpi_device *adev; +int error;if (!device_may_wakeup (dev))return -EINVAL ; handle =DEVICE_ACPI_HANDLE (dev);if (!handle ||ACPI_FAILURE (acpi_bus_get_device (handle, &adev))) { -printk (KERN_DEBUG "ACPI handle has no context!\n" ); +dev_dbg (dev,"ACPI handle has no context in %s!\n" , __func__);return -ENODEV ; } -return enable ? + error = enable ?acpi_enable_wakeup_device_power (adev, acpi_target_sleep_state) :acpi_disable_wakeup_device_power (adev); +if (!error) +dev_info (dev,"wake-up capability %s by ACPI\n" , + enable ?"enabled" :"disabled" ); + +return error; }#endif
drivers/pci/pci-acpi.c +2 -11
@@ -111,17 +111,8 @@ static bool acpi_pci_can_wakeup(struct pstatic int acpi_pci_sleep_wake (struct pci_dev *dev,bool enable) { -int error; - -if (!acpi_pci_can_wakeup (dev)) -return 0 ; - - error =acpi_pm_device_sleep_wake (&dev->dev, enable); -if (!error) -dev_info (&dev->dev,"wake-up capability %s by ACPI\n" , - enable ?"enabled" :"disabled" ); - -return error; +return acpi_pci_can_wakeup (dev) ? +acpi_pm_device_sleep_wake (&dev->dev, enable) :0 ; }static struct pci_platform_pm_ops acpi_pci_platform_pm = {
RFC PATCH 3/4 PCI PM: Introduce device flag wakeup_prepared
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Introduce a new PCI device flag, wakeup_prepared, to prevent PCI wake-up preparation code from being executed twice in a row for the same device and for the same purpose.
--- drivers/pci/pci.c | 8 ++++++++ include/linux/pci.h | 1 + 2 files changed, 9 insertions(+)
drivers/pci/pci.c +8 -0
@@ -1223,6 +1223,10 @@ int pci_enable_wake(struct pci_dev *dev,if (enable && !device_may_wakeup (&dev->dev))return -EINVAL ; +/* Don't do the same thing twice in a row for one device. */ +if (!!enable == !!dev->wakeup_prepared) +return 0 ; +/* * According to "PCI System Architecture" 4th ed. by Tom Shanley & Don * Anderson we should be doing PME# wake enable followed by ACPI wake
@@ -1239,9 +1243,12 @@ int pci_enable_wake(struct pci_dev *dev, error =platform_pci_sleep_wake (dev, true);if (ret) ret = error; +if (!ret) + dev->wakeup_prepared = true; }else {platform_pci_sleep_wake (dev, false);pci_pme_active (dev, false); + dev->wakeup_prepared = false; }return ret;
@@ -1364,6 +1371,7 @@ void pci_pm_init(struct pci_dev *dev)u16 pmc;device_enable_async_suspend (&dev->dev, true); + dev->wakeup_prepared = false; dev->pm_cap =0 ;/* find PCI PM capability in list */
include/linux/pci.h +1 -0
@@ -241,6 +241,7 @@ struct pci_dev { unsigned int d1_support:1; /* Low power state D1 is supported */ unsigned int d2_support:1; /* Low power state D2 is supported */ unsigned int no_d1d2:1; /* Only allow D0 and D3 */ + unsigned int wakeup_prepared:1;#ifdef CONFIG_PCIEASPM struct pcie_link_state *link_state;/* ASPM link state. */
RFC PATCH 3/4 PCI PM: Introduce device flag wakeup_prepared
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Introduce a new PCI device flag, wakeup_prepared, to prevent PCI wake-up preparation code from being executed twice in a row for the same device and for the same purpose.
--- drivers/pci/pci.c | 8 ++++++++ include/linux/pci.h | 1 + 2 files changed, 9 insertions(+)
drivers/pci/pci.c +8 -0
@@ -1223,6 +1223,10 @@ int pci_enable_wake(struct pci_dev *dev,if (enable && !device_may_wakeup (&dev->dev))return -EINVAL ; +/* Don't do the same thing twice in a row for one device. */ +if (!!enable == !!dev->wakeup_prepared) +return 0 ; +/* * According to "PCI System Architecture" 4th ed. by Tom Shanley & Don * Anderson we should be doing PME# wake enable followed by ACPI wake
@@ -1239,9 +1243,12 @@ int pci_enable_wake(struct pci_dev *dev, error =platform_pci_sleep_wake (dev, true);if (ret) ret = error; +if (!ret) + dev->wakeup_prepared = true; }else {platform_pci_sleep_wake (dev, false);pci_pme_active (dev, false); + dev->wakeup_prepared = false; }return ret;
@@ -1364,6 +1371,7 @@ void pci_pm_init(struct pci_dev *dev)u16 pmc;device_enable_async_suspend (&dev->dev, true); + dev->wakeup_prepared = false; dev->pm_cap =0 ;/* find PCI PM capability in list */
include/linux/pci.h +1 -0
@@ -241,6 +241,7 @@ struct pci_dev { unsigned int d1_support:1; /* Low power state D1 is supported */ unsigned int d2_support:1; /* Low power state D2 is supported */ unsigned int no_d1d2:1; /* Only allow D0 and D3 */ + unsigned int wakeup_prepared:1;#ifdef CONFIG_PCIEASPM struct pcie_link_state *link_state;/* ASPM link state. */
RFC PATCH 4/4 PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
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), configure all the bridges on the path from the device to the root bridge (including the root bridge itself) to wake-up the system.
--- drivers/acpi/power.c | 52 ++++++++++++++++++++++++++++++++---------------- drivers/acpi/scan.c | 1 drivers/acpi/sleep.c | 2 - drivers/acpi/wakeup.c | 4 +-- drivers/pci/pci-acpi.c | 25 +++++++++++++++++++++-- include/acpi/acpi_bus.h | 2 - 6 files changed, 63 insertions(+), 23 deletions(-)
drivers/pci/pci-acpi.c +23 -2
@@ -109,10 +109,31 @@ static bool acpi_pci_can_wakeup(struct preturn 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; + +acpi_pm_device_sleep_wake (&bridge->dev, enable); +if (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 = {
drivers/acpi/sleep.c +1 -1
@@ -691,7 +691,7 @@ int acpi_pm_device_sleep_wake(struct devstruct acpi_device *adev;int error; -if (!device_may_wakeup (dev)) +if (!device_can_wakeup (dev))return -EINVAL ; handle =DEVICE_ACPI_HANDLE (dev);
drivers/acpi/scan.c +1 -0
@@ -782,6 +782,7 @@ static int acpi_bus_get_wakeup_device_flkfree (buffer.pointer); device->wakeup.flags.valid =1 ; + device->wakeup.prepare_count =0 ;/* Call _PSW/_DSW object to disable its ability to wake the sleeping * system for the ACPI device with the _PRW object. * The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW.
include/acpi/acpi_bus.h +1 -1
@@ -248,7 +248,6 @@ struct acpi_device_perf {/* Wakeup Management */ struct acpi_device_wakeup_flags {u8 valid:1 ;/* Can successfully enable wakeup? */ -u8 prepared:1 ;/* Has the wake-up capability been enabled? */ u8 run_wake:1 ;/* Run-Wake GPE devices */ };
@@ -263,6 +262,7 @@ struct acpi_device_wakeup {struct acpi_handle_list resources;struct acpi_device_wakeup_state state;struct acpi_device_wakeup_flags flags; +int prepare_count; };/* Device */
drivers/acpi/power.c +35 -17
109 lines not on this page · raw message
drivers/acpi/wakeup.c +2 -2
@@ -68,7 +68,7 @@ void acpi_enable_wakeup_device(u8 sleep_/* If users want to disable run-wake GPE, * we only disable it for wake and leave it for runtime */ -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) {/* set_gpe_type will disable GPE, leave it like that */
@@ -100,7 +100,7 @@ void acpi_disable_wakeup_device(u8 sleepif (!dev->wakeup.flags.valid)continue ; -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) { acpi_set_gpe_type(dev->wakeup.gpe_device,
Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
Quoted 8 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
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.
Quoted 10 lines
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), configure all the bridges on the path from the device to the root bridge (including the root bridge itself) to wake-up the system.
From the description it seems that it will propagate the wake-up
function to its parent device.
Is it enough to stop the propagation when we find one device that can generate wake-up events in its parent device tree?
thanks.
Quoted 238 lines
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/acpi/power.c | 52 ++++++++++++++++++++++++++++++++---------------- drivers/acpi/scan.c | 1 drivers/acpi/sleep.c | 2 - drivers/acpi/wakeup.c | 4 +-- drivers/pci/pci-acpi.c | 25 +++++++++++++++++++++-- include/acpi/acpi_bus.h | 2 - 6 files changed, 63 insertions(+), 23 deletions(-)Index:
linux-2.6/drivers/pci/pci-acpi.c
===================================================================drivers/pci/pci-acpi.c +23
-2@@ -109,10 +109,31 @@ static bool acpi_pci_can_wakeup(struct preturn 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; + +acpi_pm_device_sleep_wake (&bridge->dev, enable); +if (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
===================================================================drivers/acpi/sleep.c +1
-1@@ -691,7 +691,7 @@ int acpi_pm_device_sleep_wake(struct devstruct acpi_device *adev;int error; -if (!device_may_wakeup (dev)) +if (!device_can_wakeup (dev))return -EINVAL ; handle =DEVICE_ACPI_HANDLE (dev);Index:
linux-2.6/drivers/acpi/scan.c
===================================================================drivers/acpi/scan.c +1
-0@@ -782,6 +782,7 @@ static int acpi_bus_get_wakeup_device_flkfree (buffer.pointer); device->wakeup.flags.valid =1 ; + device->wakeup.prepare_count =0 ;/* Call _PSW/_DSW object to disable its ability to wake the sleeping * system for the ACPI device with the _PRW object. * The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW. Index:
linux-2.6/include/acpi/acpi_bus.h
===================================================================include/acpi/acpi_bus.h +1
-1@@ -248,7 +248,6 @@ struct acpi_device_perf {/* Wakeup Management */ struct acpi_device_wakeup_flags {u8 valid:1 ;/* Can successfully enable wakeup? */ -u8 prepared:1 ;/* Has the wake-up capability been enabled? */ u8 run_wake:1 ;/* Run-Wake GPE devices */ };@@ -263,6 +262,7 @@ struct acpi_device_wakeup {struct acpi_handle_list resources;struct acpi_device_wakeup_state state;struct acpi_device_wakeup_flags flags; +int prepare_count; };/* Device */ Index:
linux-2.6/drivers/acpi/power.c
===================================================================drivers/acpi/power.c +35
-17@@ -44,6 +44,8 @@#include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#include "sleep.h" +#define _COMPONENT ACPI_POWER_COMPONENTACPI_MODULE_NAME ("power" );#define ACPI_POWER_CLASS "power_resource"@@ -361,17 +363,19 @@ int acpi_device_sleep_wake(struct acpi_d*/ int acpi_enable_wakeup_device_power (struct acpi_device *dev,int sleep_state) { -int i, err; +int i, err =0 ;if (!dev || !dev->wakeup.flags.valid)return -EINVAL ; +mutex_lock (&acpi_device_lock); +/* * Do not execute the code below twice in a row without calling * acpi_disable_wakeup_device_power() in between for the same device */ -if (dev->wakeup.flags.prepared) -return 0 ; +if (dev->wakeup.prepare_count++) + goto out;/* Open power resource */ for (i =0 ; i < dev->wakeup.resources.count; i++) {@@ -379,7 +383,8 @@ int acpi_enable_wakeup_device_power(struif (ret) { printk(KERN_ERR PREFIX "Transition power state\n"); dev->wakeup.flags.valid =0 ; -return -ENODEV ; + err = -ENODEV ; + goto err_out; } }@@ -388,9 +393,13 @@ int acpi_enable_wakeup_device_power(stru* in arbitrary power state afterwards. */ err =acpi_device_sleep_wake (dev,1 , sleep_state,3 ); -if (!err) - dev->wakeup.flags.prepared =1 ; + err_out: +if (err) + dev->wakeup.prepare_count =0 ; + + out: +mutex_unlock (&acpi_device_lock);return err; }@@ -402,35 +411,44 @@ int acpi_enable_wakeup_device_power(stru*/ int acpi_disable_wakeup_device_power (struct acpi_device *dev) { -int i, ret; +int i, err =0 ;if (!dev || !dev->wakeup.flags.valid)return -EINVAL ; +mutex_lock (&acpi_device_lock); +/* * Do not execute the code below twice in a row without calling * acpi_enable_wakeup_device_power() in between for the same device */ -if (!dev->wakeup.flags.prepared) -return 0 ; - - dev->wakeup.flags.prepared =0 ; +if (--dev->wakeup.prepare_count) { +if (dev->wakeup.prepare_count <0 ) { +dev_warn (&dev->dev,"unbalanced %s!\n" , __func__); + dev->wakeup.prepare_count =0 ; + } + goto out; + } - ret =acpi_device_sleep_wake (dev,0 ,0 ,0 ); -if (ret) -return ret; + err =acpi_device_sleep_wake (dev,0 ,0 ,0 ); +if (err) + goto out;/* Close power resource */ for (i =0 ; i < dev->wakeup.resources.count; i++) { - ret =acpi_power_off_device (dev->wakeup.resources.handles[i], dev); +int ret =acpi_power_off_device ( + dev->wakeup.resources.handles[i], dev);if (ret) { printk(KERN_ERR PREFIX "Transition power state\n"); dev->wakeup.flags.valid =0 ; -return -ENODEV ; + err = -ENODEV ; + goto out; } } -return ret; + out: +mutex_unlock (&acpi_device_lock); +return err; }/* -------------------------------------------------------------------------- Index:
linux-2.6/drivers/acpi/wakeup.c
===================================================================drivers/acpi/wakeup.c +2
-3@@ -68,7 +68,7 @@ void acpi_enable_wakeup_device(u8 sleep_/* If users want to disable run-wake GPE, * we only disable it for wake and leave it for runtime */ -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) {/* set_gpe_type will disable GPE, leave it like that */ @@ -100,7 +100,7 @@ void acpi_disable_wakeup_device(u8 sleepif (!dev->wakeup.flags.valid)continue ; -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) { acpi_set_gpe_type(dev->wakeup.gpe_device, --To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
Quoted 19 lines · ykzhao
Quoted 8 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
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.
Quoted 10 lines
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), configure all the bridges on the path from the device to the root bridge (including the root bridge itself) to wake-up the system.
From the description it seems that it will propagate the wake-up
Quoted 3 lines
function to its parent device.
Is it enough to stop the propagation when we find one device that can generate wake-up events in its parent device tree?
Quite frankly, I'm not sure of that. It really depends on how PME# is routed in given system. The spec permits routing it directly to the chipset as well as routing it through bridges and I don't know if we can assume that the first upstream device capable of generating wake-up events will handle it.
That said, I think we can try returning from acpi_pci_propagate_wakeup_enable() as soon as acpi_pm_device_sleep_wake() returns 0 for current device. On the test systems I have it won't make any difference, because the GPE is shared among the root bridge and the first upstream bridge of the device in question.
Thanks,
Rafael
Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
Quoted 4 lines · Rafael J. Wysocki
Quite frankly, I'm not sure of that. It really depends on how PME# is routed in given system. The spec permits routing it directly to the chipset as well as routing it through bridges and I don't know if we can assume that the first upstream device capable of generating wake-up events will handle it.
The GPE block is going to be in the chipset, so unless the vendors have explicitly hooked up a link between the PME line from a bridge and the EC, we're presumably always going to get it from the chipset if at all.
Quoted 5 lines
That said, I think we can try returning from
acpi_pci_propagate_wakeup_enable()as soon asacpi_pm_device_sleep_wake()returns 0 for current device. On the test systems I have it won't make any difference, because the GPE is shared among the root bridge and the first upstream bridge of the device in question.
Yes, it's presumably the case that the PME event in the bridge is just tied to the root bridge in the chipset. Do we know what chipset this hardware is? For Intel, at least, GPE behaviour is defined in the chipset docs.
Signature · 1 line
Matthew Garrett | [email protected]
Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
Quoted 17 lines · Matthew Garrett
Quoted 4 lines · Rafael J. Wysocki
Quite frankly, I'm not sure of that. It really depends on how PME# is routed in given system. The spec permits routing it directly to the chipset as well as routing it through bridges and I don't know if we can assume that the first upstream device capable of generating wake-up events will handle it.
The GPE block is going to be in the chipset, so unless the vendors have explicitly hooked up a link between the PME line from a bridge and the EC, we're presumably always going to get it from the chipset if at all.
Quoted 5 lines
That said, I think we can try returning from
acpi_pci_propagate_wakeup_enable()as soon asacpi_pm_device_sleep_wake()returns 0 for current device. On the test systems I have it won't make any difference, because the GPE is shared among the root bridge and the first upstream bridge of the device in question.
Quoted 4 lines
Yes, it's presumably the case that the PME event in the bridge is just tied to the root bridge in the chipset. Do we know what chipset this hardware is? For Intel, at least, GPE behaviour is defined in the chipset docs.
One box is Intel, the other one is based on an ATI (pre-AMD) chipset, but the design is similar in that respect.
Thanks,
Rafael
Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
Quoted 7 lines · Rafael J. Wysocki
Quoted 4 lines
Yes, it's presumably the case that the PME event in the bridge is just tied to the root bridge in the chipset. Do we know what chipset this hardware is? For Intel, at least, GPE behaviour is defined in the chipset docs.
One box is Intel, the other one is based on an ATI (pre-AMD) chipset, but the design is similar in that respect.
GPE 0xb will be the one generated by any Intel chipset whenever the external PCI PME# goes active. 0xd is the equivalent for chipset-level devices that don't have a GPE of their own. I can't see any way that a downstream bridge could reasonably generate a GPE, so I'd bet that it's using 0xb.
Signature · 1 line
Matthew Garrett | [email protected]
Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
Quoted 6 lines · Matthew Garrett
Quoted 5 lines · Rafael J. Wysocki
Quoted 4 lines
Yes, it's presumably the case that the PME event in the bridge is just tied to the root bridge in the chipset. Do we know what chipset this hardware is? For Intel, at least, GPE behaviour is defined in the chipset docs.
Quoted 8 lines
Quoted 2 lines
One box is Intel, the other one is based on an ATI (pre-AMD) chipset, but the design is similar in that respect.
GPE
0xbwill be the one generated by any Intel chipset whenever the external PCI PME# goes active.0xdis the equivalent for chipset-level devices that don't have a GPE of their own. I can't see any way that a downstream bridge could reasonably generate a GPE, so I'd bet that it's using0xb.
I agree. Which seems to me that at least for Intel chipsets the "propagation" loop can be stopped as soon as we find the first device with ACPI wakeup support, like in the patch I've just sent.
Thanks,
Rafael
Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
In reply to Matthew GarrettQuoted 6 lines · Matthew Garrett
Quoted 5 lines · Rafael J. Wysocki
Quoted 4 lines
Yes, it's presumably the case that the PME event in the bridge is just tied to the root bridge in the chipset. Do we know what chipset this hardware is? For Intel, at least, GPE behaviour is defined in the chipset docs.
Quoted 8 lines
Quoted 2 lines
One box is Intel, the other one is based on an ATI (pre-AMD) chipset, but the design is similar in that respect.
GPE
0xbwill be the one generated by any Intel chipset whenever the external PCI PME# goes active.0xdis the equivalent for chipset-level devices that don't have a GPE of their own. I can't see any way that a downstream bridge could reasonably generate a GPE, so I'd bet that it's using0xb.
I agree. Which seems to me that at least for Intel chipsets the "propagation" loop can be stopped as soon as we find the first device with ACPI wakeup support, like in the patch I've just sent.
Thanks,
Rafael
Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
In reply to Rafael J. WysockiQuoted 7 lines · Rafael J. Wysocki
Quoted 4 lines
Yes, it's presumably the case that the PME event in the bridge is just tied to the root bridge in the chipset. Do we know what chipset this hardware is? For Intel, at least, GPE behaviour is defined in the chipset docs.
One box is Intel, the other one is based on an ATI (pre-AMD) chipset, but the design is similar in that respect.
GPE 0xb will be the one generated by any Intel chipset whenever the external PCI PME# goes active. 0xd is the equivalent for chipset-level devices that don't have a GPE of their own. I can't see any way that a downstream bridge could reasonably generate a GPE, so I'd bet that it's using 0xb.
Signature · 1 line
Matthew Garrett | [email protected]
Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
In reply to Matthew GarrettQuoted 17 lines · Matthew Garrett
Quoted 4 lines · Rafael J. Wysocki
Quite frankly, I'm not sure of that. It really depends on how PME# is routed in given system. The spec permits routing it directly to the chipset as well as routing it through bridges and I don't know if we can assume that the first upstream device capable of generating wake-up events will handle it.
The GPE block is going to be in the chipset, so unless the vendors have explicitly hooked up a link between the PME line from a bridge and the EC, we're presumably always going to get it from the chipset if at all.
Quoted 5 lines
That said, I think we can try returning from
acpi_pci_propagate_wakeup_enable()as soon asacpi_pm_device_sleep_wake()returns 0 for current device. On the test systems I have it won't make any difference, because the GPE is shared among the root bridge and the first upstream bridge of the device in question.
Quoted 4 lines
Yes, it's presumably the case that the PME event in the bridge is just tied to the root bridge in the chipset. Do we know what chipset this hardware is? For Intel, at least, GPE behaviour is defined in the chipset docs.
One box is Intel, the other one is based on an ATI (pre-AMD) chipset, but the design is similar in that respect.
Thanks,
Rafael
Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
In reply to Rafael J. WysockiQuoted 4 lines · Rafael J. Wysocki
Quite frankly, I'm not sure of that. It really depends on how PME# is routed in given system. The spec permits routing it directly to the chipset as well as routing it through bridges and I don't know if we can assume that the first upstream device capable of generating wake-up events will handle it.
The GPE block is going to be in the chipset, so unless the vendors have explicitly hooked up a link between the PME line from a bridge and the EC, we're presumably always going to get it from the chipset if at all.
Quoted 5 lines
That said, I think we can try returning from
acpi_pci_propagate_wakeup_enable()as soon asacpi_pm_device_sleep_wake()returns 0 for current device. On the test systems I have it won't make any difference, because the GPE is shared among the root bridge and the first upstream bridge of the device in question.
Yes, it's presumably the case that the PME event in the bridge is just tied to the root bridge in the chipset. Do we know what chipset this hardware is? For Intel, at least, GPE behaviour is defined in the chipset docs.
Signature · 1 line
Matthew Garrett | [email protected]
Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
In reply to ykzhaoQuoted 19 lines · ykzhao
Quoted 8 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
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.
Quoted 10 lines
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), configure all the bridges on the path from the device to the root bridge (including the root bridge itself) to wake-up the system.
From the description it seems that it will propagate the wake-up
Quoted 3 lines
function to its parent device.
Is it enough to stop the propagation when we find one device that can generate wake-up events in its parent device tree?
Quite frankly, I'm not sure of that. It really depends on how PME# is routed in given system. The spec permits routing it directly to the chipset as well as routing it through bridges and I don't know if we can assume that the first upstream device capable of generating wake-up events will handle it.
That said, I think we can try returning from acpi_pci_propagate_wakeup_enable() as soon as acpi_pm_device_sleep_wake() returns 0 for current device. On the test systems I have it won't make any difference, because the GPE is shared among the root bridge and the first upstream bridge of the device in question.
Thanks,
Rafael
Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
In reply to Rafael J. WysockiQuoted 8 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
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.
Quoted 10 lines
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), configure all the bridges on the path from the device to the root bridge (including the root bridge itself) to wake-up the system.
From the description it seems that it will propagate the wake-up
function to its parent device.
Is it enough to stop the propagation when we find one device that can generate wake-up events in its parent device tree?
thanks.
Quoted 238 lines
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/acpi/power.c | 52 ++++++++++++++++++++++++++++++++---------------- drivers/acpi/scan.c | 1 drivers/acpi/sleep.c | 2 - drivers/acpi/wakeup.c | 4 +-- drivers/pci/pci-acpi.c | 25 +++++++++++++++++++++-- include/acpi/acpi_bus.h | 2 - 6 files changed, 63 insertions(+), 23 deletions(-)Index:
linux-2.6/drivers/pci/pci-acpi.c
===================================================================drivers/pci/pci-acpi.c +23
-2@@ -109,10 +109,31 @@ static bool acpi_pci_can_wakeup(struct preturn 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; + +acpi_pm_device_sleep_wake (&bridge->dev, enable); +if (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
===================================================================drivers/acpi/sleep.c +1
-1@@ -691,7 +691,7 @@ int acpi_pm_device_sleep_wake(struct devstruct acpi_device *adev;int error; -if (!device_may_wakeup (dev)) +if (!device_can_wakeup (dev))return -EINVAL ; handle =DEVICE_ACPI_HANDLE (dev);Index:
linux-2.6/drivers/acpi/scan.c
===================================================================drivers/acpi/scan.c +1
-0@@ -782,6 +782,7 @@ static int acpi_bus_get_wakeup_device_flkfree (buffer.pointer); device->wakeup.flags.valid =1 ; + device->wakeup.prepare_count =0 ;/* Call _PSW/_DSW object to disable its ability to wake the sleeping * system for the ACPI device with the _PRW object. * The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW. Index:
linux-2.6/include/acpi/acpi_bus.h
===================================================================include/acpi/acpi_bus.h +1
-1@@ -248,7 +248,6 @@ struct acpi_device_perf {/* Wakeup Management */ struct acpi_device_wakeup_flags {u8 valid:1 ;/* Can successfully enable wakeup? */ -u8 prepared:1 ;/* Has the wake-up capability been enabled? */ u8 run_wake:1 ;/* Run-Wake GPE devices */ };@@ -263,6 +262,7 @@ struct acpi_device_wakeup {struct acpi_handle_list resources;struct acpi_device_wakeup_state state;struct acpi_device_wakeup_flags flags; +int prepare_count; };/* Device */ Index:
linux-2.6/drivers/acpi/power.c
===================================================================drivers/acpi/power.c +35
-17@@ -44,6 +44,8 @@#include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#include "sleep.h" +#define _COMPONENT ACPI_POWER_COMPONENTACPI_MODULE_NAME ("power" );#define ACPI_POWER_CLASS "power_resource"@@ -361,17 +363,19 @@ int acpi_device_sleep_wake(struct acpi_d*/ int acpi_enable_wakeup_device_power (struct acpi_device *dev,int sleep_state) { -int i, err; +int i, err =0 ;if (!dev || !dev->wakeup.flags.valid)return -EINVAL ; +mutex_lock (&acpi_device_lock); +/* * Do not execute the code below twice in a row without calling * acpi_disable_wakeup_device_power() in between for the same device */ -if (dev->wakeup.flags.prepared) -return 0 ; +if (dev->wakeup.prepare_count++) + goto out;/* Open power resource */ for (i =0 ; i < dev->wakeup.resources.count; i++) {@@ -379,7 +383,8 @@ int acpi_enable_wakeup_device_power(struif (ret) { printk(KERN_ERR PREFIX "Transition power state\n"); dev->wakeup.flags.valid =0 ; -return -ENODEV ; + err = -ENODEV ; + goto err_out; } }@@ -388,9 +393,13 @@ int acpi_enable_wakeup_device_power(stru* in arbitrary power state afterwards. */ err =acpi_device_sleep_wake (dev,1 , sleep_state,3 ); -if (!err) - dev->wakeup.flags.prepared =1 ; + err_out: +if (err) + dev->wakeup.prepare_count =0 ; + + out: +mutex_unlock (&acpi_device_lock);return err; }@@ -402,35 +411,44 @@ int acpi_enable_wakeup_device_power(stru*/ int acpi_disable_wakeup_device_power (struct acpi_device *dev) { -int i, ret; +int i, err =0 ;if (!dev || !dev->wakeup.flags.valid)return -EINVAL ; +mutex_lock (&acpi_device_lock); +/* * Do not execute the code below twice in a row without calling * acpi_enable_wakeup_device_power() in between for the same device */ -if (!dev->wakeup.flags.prepared) -return 0 ; - - dev->wakeup.flags.prepared =0 ; +if (--dev->wakeup.prepare_count) { +if (dev->wakeup.prepare_count <0 ) { +dev_warn (&dev->dev,"unbalanced %s!\n" , __func__); + dev->wakeup.prepare_count =0 ; + } + goto out; + } - ret =acpi_device_sleep_wake (dev,0 ,0 ,0 ); -if (ret) -return ret; + err =acpi_device_sleep_wake (dev,0 ,0 ,0 ); +if (err) + goto out;/* Close power resource */ for (i =0 ; i < dev->wakeup.resources.count; i++) { - ret =acpi_power_off_device (dev->wakeup.resources.handles[i], dev); +int ret =acpi_power_off_device ( + dev->wakeup.resources.handles[i], dev);if (ret) { printk(KERN_ERR PREFIX "Transition power state\n"); dev->wakeup.flags.valid =0 ; -return -ENODEV ; + err = -ENODEV ; + goto out; } } -return ret; + out: +mutex_unlock (&acpi_device_lock); +return err; }/* -------------------------------------------------------------------------- Index:
linux-2.6/drivers/acpi/wakeup.c
===================================================================drivers/acpi/wakeup.c +2
-3@@ -68,7 +68,7 @@ void acpi_enable_wakeup_device(u8 sleep_/* If users want to disable run-wake GPE, * we only disable it for wake and leave it for runtime */ -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) {/* set_gpe_type will disable GPE, leave it like that */ @@ -100,7 +100,7 @@ void acpi_disable_wakeup_device(u8 sleepif (!dev->wakeup.flags.valid)continue ; -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) { acpi_set_gpe_type(dev->wakeup.gpe_device, --To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [linux-pm] [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
In reply to Rafael J. WysockiQuoted 8 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
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.
Quoted 9 lines
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), configure all the bridges on the path from the device to the root bridge (including the root bridge itself) to wake-up the system.
I thought it would be better to split this patch into two patches, one replacing the wakeup.prepared flag with a reference counter and the other reworking the PCI ACPI wakeup routine.
I the second patch I followed the Yakui's suggestion to propagate wake-up enable until we find the first device with ACPI support for wake-up.
The patches will follow in replies to this message.
Thanks,
Rafael
RFC PATCH 4/4 ACPI PM: Replace wakeup.prepared with reference counter
From: Rafael J. Wysocki <[email protected]>
The wakeup.prepared flag is used for marking devices that have the wake-up power already enabled, so that the wake-up power is not enabled twice in a row for the same device. This assumes, however, that device wake-up power will only be enabled once, while the device is being prepared for a system-wide sleep transition, and the second attempt is made by acpi_enable_wakeup_device_prep().
With the upcoming PCI wake-up rework this assumption will not hold any more for PCI bridges and the root bridge whose wake-up power may be enabled as a result of wake-up enable propagation from other devices (eg. add-on devices that are not associated with any GPEs). Thus, there may be many attempts to enable wake-up power on a PCI bridge or the root bridge during a system power state transition and it's better to replace wakeup.prepared with a reference counter.
--- drivers/acpi/power.c | 58 ++++++++++++++++++++++++++++-------------------- drivers/acpi/scan.c | 1 drivers/acpi/wakeup.c | 4 +-- include/acpi/acpi_bus.h | 2 - 4 files changed, 39 insertions(+), 26 deletions(-)
drivers/acpi/scan.c +1 -0
@@ -782,6 +782,7 @@ static int acpi_bus_get_wakeup_device_flkfree (buffer.pointer); device->wakeup.flags.valid =1 ; + device->wakeup.prepare_count =0 ;/* Call _PSW/_DSW object to disable its ability to wake the sleeping * system for the ACPI device with the _PRW object. * The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW.
include/acpi/acpi_bus.h +1 -1
@@ -248,7 +248,6 @@ struct acpi_device_perf {/* Wakeup Management */ struct acpi_device_wakeup_flags {u8 valid:1 ;/* Can successfully enable wakeup? */ -u8 prepared:1 ;/* Has the wake-up capability been enabled? */ u8 run_wake:1 ;/* Run-Wake GPE devices */ };
@@ -263,6 +262,7 @@ struct acpi_device_wakeup {struct acpi_handle_list resources;struct acpi_device_wakeup_state state;struct acpi_device_wakeup_flags flags; +int prepare_count; };/* Device */
drivers/acpi/power.c +35 -23
109 lines not on this page · raw message
drivers/acpi/wakeup.c +2 -2
@@ -68,7 +68,7 @@ void acpi_enable_wakeup_device(u8 sleep_/* If users want to disable run-wake GPE, * we only disable it for wake and leave it for runtime */ -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) {/* set_gpe_type will disable GPE, leave it like that */
@@ -100,7 +100,7 @@ void acpi_disable_wakeup_device(u8 sleepif (!dev->wakeup.flags.valid)continue ; -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) { acpi_set_gpe_type(dev->wakeup.gpe_device,
RFC PATCH 4/4 ACPI PM: Replace wakeup.prepared with reference counter
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
The wakeup.prepared flag is used for marking devices that have the wake-up power already enabled, so that the wake-up power is not enabled twice in a row for the same device. This assumes, however, that device wake-up power will only be enabled once, while the device is being prepared for a system-wide sleep transition, and the second attempt is made by acpi_enable_wakeup_device_prep().
With the upcoming PCI wake-up rework this assumption will not hold any more for PCI bridges and the root bridge whose wake-up power may be enabled as a result of wake-up enable propagation from other devices (eg. add-on devices that are not associated with any GPEs). Thus, there may be many attempts to enable wake-up power on a PCI bridge or the root bridge during a system power state transition and it's better to replace wakeup.prepared with a reference counter.
--- drivers/acpi/power.c | 58 ++++++++++++++++++++++++++++-------------------- drivers/acpi/scan.c | 1 drivers/acpi/wakeup.c | 4 +-- include/acpi/acpi_bus.h | 2 - 4 files changed, 39 insertions(+), 26 deletions(-)
drivers/acpi/scan.c +1 -0
@@ -782,6 +782,7 @@ static int acpi_bus_get_wakeup_device_flkfree (buffer.pointer); device->wakeup.flags.valid =1 ; + device->wakeup.prepare_count =0 ;/* Call _PSW/_DSW object to disable its ability to wake the sleeping * system for the ACPI device with the _PRW object. * The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW.
include/acpi/acpi_bus.h +1 -1
@@ -248,7 +248,6 @@ struct acpi_device_perf {/* Wakeup Management */ struct acpi_device_wakeup_flags {u8 valid:1 ;/* Can successfully enable wakeup? */ -u8 prepared:1 ;/* Has the wake-up capability been enabled? */ u8 run_wake:1 ;/* Run-Wake GPE devices */ };
@@ -263,6 +262,7 @@ struct acpi_device_wakeup {struct acpi_handle_list resources;struct acpi_device_wakeup_state state;struct acpi_device_wakeup_flags flags; +int prepare_count; };/* Device */
drivers/acpi/power.c +35 -23
109 lines not on this page · raw message
drivers/acpi/wakeup.c +2 -2
@@ -68,7 +68,7 @@ void acpi_enable_wakeup_device(u8 sleep_/* If users want to disable run-wake GPE, * we only disable it for wake and leave it for runtime */ -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) {/* set_gpe_type will disable GPE, leave it like that */
@@ -100,7 +100,7 @@ void acpi_disable_wakeup_device(u8 sleepif (!dev->wakeup.flags.valid)continue ; -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) { acpi_set_gpe_type(dev->wakeup.gpe_device,
RFC PATCH 1/1 PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
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.
--- drivers/acpi/sleep.c | 2 +- drivers/pci/pci-acpi.c | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-)
drivers/pci/pci-acpi.c +24 -2
@@ -109,10 +109,32 @@ static bool acpi_pci_can_wakeup(struct preturn 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 = {
drivers/acpi/sleep.c +1 -1
@@ -691,7 +691,7 @@ int acpi_pm_device_sleep_wake(struct devstruct acpi_device *adev;int error; -if (!device_may_wakeup (dev)) +if (!device_can_wakeup (dev))return -EINVAL ; handle =DEVICE_ACPI_HANDLE (dev);
RFC PATCH 1/1 PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
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.
--- drivers/acpi/sleep.c | 2 +- drivers/pci/pci-acpi.c | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-)
drivers/pci/pci-acpi.c +24 -2
@@ -109,10 +109,32 @@ static bool acpi_pci_can_wakeup(struct preturn 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 = {
drivers/acpi/sleep.c +1 -1
@@ -691,7 +691,7 @@ int acpi_pm_device_sleep_wake(struct devstruct acpi_device *adev;int error; -if (!device_may_wakeup (dev)) +if (!device_can_wakeup (dev))return -EINVAL ; handle =DEVICE_ACPI_HANDLE (dev);
Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
In reply to Rafael J. WysockiQuoted 8 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
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.
Quoted 9 lines
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), configure all the bridges on the path from the device to the root bridge (including the root bridge itself) to wake-up the system.
I thought it would be better to split this patch into two patches, one replacing the wakeup.prepared flag with a reference counter and the other reworking the PCI ACPI wakeup routine.
I the second patch I followed the Yakui's suggestion to propagate wake-up enable until we find the first device with ACPI support for wake-up.
The patches will follow in replies to this message.
Thanks,
Rafael
RFC PATCH 4/4 PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
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), configure all the bridges on the path from the device to the root bridge (including the root bridge itself) to wake-up the system.
--- drivers/acpi/power.c | 52 ++++++++++++++++++++++++++++++++---------------- drivers/acpi/scan.c | 1 drivers/acpi/sleep.c | 2 - drivers/acpi/wakeup.c | 4 +-- drivers/pci/pci-acpi.c | 25 +++++++++++++++++++++-- include/acpi/acpi_bus.h | 2 - 6 files changed, 63 insertions(+), 23 deletions(-)
drivers/pci/pci-acpi.c +23 -2
@@ -109,10 +109,31 @@ static bool acpi_pci_can_wakeup(struct preturn 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; + +acpi_pm_device_sleep_wake (&bridge->dev, enable); +if (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 = {
drivers/acpi/sleep.c +1 -1
@@ -691,7 +691,7 @@ int acpi_pm_device_sleep_wake(struct devstruct acpi_device *adev;int error; -if (!device_may_wakeup (dev)) +if (!device_can_wakeup (dev))return -EINVAL ; handle =DEVICE_ACPI_HANDLE (dev);
drivers/acpi/scan.c +1 -0
@@ -782,6 +782,7 @@ static int acpi_bus_get_wakeup_device_flkfree (buffer.pointer); device->wakeup.flags.valid =1 ; + device->wakeup.prepare_count =0 ;/* Call _PSW/_DSW object to disable its ability to wake the sleeping * system for the ACPI device with the _PRW object. * The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW.
include/acpi/acpi_bus.h +1 -1
@@ -248,7 +248,6 @@ struct acpi_device_perf {/* Wakeup Management */ struct acpi_device_wakeup_flags {u8 valid:1 ;/* Can successfully enable wakeup? */ -u8 prepared:1 ;/* Has the wake-up capability been enabled? */ u8 run_wake:1 ;/* Run-Wake GPE devices */ };
@@ -263,6 +262,7 @@ struct acpi_device_wakeup {struct acpi_handle_list resources;struct acpi_device_wakeup_state state;struct acpi_device_wakeup_flags flags; +int prepare_count; };/* Device */
drivers/acpi/power.c +35 -17
109 lines not on this page · raw message
drivers/acpi/wakeup.c +2 -2
@@ -68,7 +68,7 @@ void acpi_enable_wakeup_device(u8 sleep_/* If users want to disable run-wake GPE, * we only disable it for wake and leave it for runtime */ -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) {/* set_gpe_type will disable GPE, leave it like that */
@@ -100,7 +100,7 @@ void acpi_disable_wakeup_device(u8 sleepif (!dev->wakeup.flags.valid)continue ; -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) { acpi_set_gpe_type(dev->wakeup.gpe_device,
cover PCI / ACPI PM: Propagate wake-up enable upstream
In reply to Rafael J. WysockiHi,
The patchset has been confirmed to fix the issue it was designed for (ie. it allows an add-on PCI card to wake-up the system from S3 by enabling the GPE of the bridge upstream to wake up), so I'd like to get it in as soon as reasonably possible.
Please review.
Thanks,
Rafael
PATCH 1/5 PCI PM: Simplify PCI wake-up code
From: Rafael J. Wysocki <[email protected]>
Rework the PCI wake-up code so that it's easier to read without changing the functionality.
--- drivers/pci/pci-acpi.c | 10 +++++++--- drivers/pci/pci.c | 25 ++++++++++++++----------- 2 files changed, 21 insertions(+), 14 deletions(-)
drivers/pci/pci.c +14 -11
@@ -1218,8 +1218,7 @@ void pci_pme_active(struct pci_dev *dev,*/ int pci_enable_wake (struct pci_dev *dev,pci_power_t state,bool enable) { -int error =0 ; -bool pme_done = false; +int ret =0 ;if (enable && !device_may_wakeup (&dev->dev))return -EINVAL ;
@@ -1230,18 +1229,22 @@ int pci_enable_wake(struct pci_dev *dev,* enable. To disable wake-up we call the platform first, for symmetry. */ -if (!enable &&platform_pci_can_wakeup (dev)) - error =platform_pci_sleep_wake (dev, false); - -if (!enable ||pci_pme_capable (dev, state)) { -pci_pme_active (dev, enable); - pme_done = true; - } +if (enable) { +int error; -if (enable &&platform_pci_can_wakeup (dev)) +if (pci_pme_capable (dev, state)) +pci_pme_active (dev, true); +else + ret =1 ; error =platform_pci_sleep_wake (dev, true); +if (ret) + ret = error; + }else { +platform_pci_sleep_wake (dev, false); +pci_pme_active (dev, false); + } -return pme_done ?0 : error; +return ret; }/**
drivers/pci/pci-acpi.c +7 -3
@@ -111,12 +111,16 @@ static bool acpi_pci_can_wakeup(struct pstatic int acpi_pci_sleep_wake (struct pci_dev *dev,bool enable) { -int error =acpi_pm_device_sleep_wake (&dev->dev, enable); +int error; +if (!acpi_pci_can_wakeup (dev)) +return 0 ; + + error =acpi_pm_device_sleep_wake (&dev->dev, enable);if (!error) -dev_printk (KERN_INFO , &dev->dev, -"wake-up capability %s by ACPI\n" , +dev_info (&dev->dev,"wake-up capability %s by ACPI\n" , enable ?"enabled" :"disabled" ); +return error; }
PATCH 1/5 PCI PM: Simplify PCI wake-up code
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Rework the PCI wake-up code so that it's easier to read without changing the functionality.
--- drivers/pci/pci-acpi.c | 10 +++++++--- drivers/pci/pci.c | 25 ++++++++++++++----------- 2 files changed, 21 insertions(+), 14 deletions(-)
drivers/pci/pci.c +14 -11
@@ -1218,8 +1218,7 @@ void pci_pme_active(struct pci_dev *dev,*/ int pci_enable_wake (struct pci_dev *dev,pci_power_t state,bool enable) { -int error =0 ; -bool pme_done = false; +int ret =0 ;if (enable && !device_may_wakeup (&dev->dev))return -EINVAL ;
@@ -1230,18 +1229,22 @@ int pci_enable_wake(struct pci_dev *dev,* enable. To disable wake-up we call the platform first, for symmetry. */ -if (!enable &&platform_pci_can_wakeup (dev)) - error =platform_pci_sleep_wake (dev, false); - -if (!enable ||pci_pme_capable (dev, state)) { -pci_pme_active (dev, enable); - pme_done = true; - } +if (enable) { +int error; -if (enable &&platform_pci_can_wakeup (dev)) +if (pci_pme_capable (dev, state)) +pci_pme_active (dev, true); +else + ret =1 ; error =platform_pci_sleep_wake (dev, true); +if (ret) + ret = error; + }else { +platform_pci_sleep_wake (dev, false); +pci_pme_active (dev, false); + } -return pme_done ?0 : error; +return ret; }/**
drivers/pci/pci-acpi.c +7 -3
@@ -111,12 +111,16 @@ static bool acpi_pci_can_wakeup(struct pstatic int acpi_pci_sleep_wake (struct pci_dev *dev,bool enable) { -int error =acpi_pm_device_sleep_wake (&dev->dev, enable); +int error; +if (!acpi_pci_can_wakeup (dev)) +return 0 ; + + error =acpi_pm_device_sleep_wake (&dev->dev, enable);if (!error) -dev_printk (KERN_INFO , &dev->dev, -"wake-up capability %s by ACPI\n" , +dev_info (&dev->dev,"wake-up capability %s by ACPI\n" , enable ?"enabled" :"disabled" ); +return error; }
Jesse Barnes
AppliedRe: [PATCH 1/5] PCI PM: Simplify PCI wake-up code
Quoted 7 lines · On Tue, 8 Sep 2009 23:12:59 +0200 "Rafael J. Wysocki" <[email protected]> wrote:
From: Rafael J. Wysocki <[email protected]>
Rework the PCI wake-up code so that it's easier to read without changing the functionality.
Signed-off-by Rafael J. Wysocki <[email protected]>---
Applied this series to my linux-next branch, thanks. Len, that include the ACPI portion; hope that's ok.
Signature · 1 line
Jesse Barnes, Intel Open Source Technology Center
Jesse Barnes
AppliedRe: [PATCH 1/5] PCI PM: Simplify PCI wake-up code
In reply to Rafael J. WysockiQuoted 7 lines · On Tue, 8 Sep 2009 23:12:59 +0200 "Rafael J. Wysocki" <[email protected]> wrote:
From: Rafael J. Wysocki <[email protected]>
Rework the PCI wake-up code so that it's easier to read without changing the functionality.
Signed-off-by Rafael J. Wysocki <[email protected]>---
Applied this series to my linux-next branch, thanks. Len, that include the ACPI portion; hope that's ok.
Signature · 1 line
Jesse Barnes, Intel Open Source Technology Center
PATCH 2/5 PCI / ACPI PM: Rework some debug messages
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Move a debug message from acpi_pci_sleep_wake() to acpi_pm_device_sleep_wake() and use the standard dev_*() macros in there.
--- drivers/acpi/sleep.c | 10 ++++++++-- drivers/pci/pci-acpi.c | 13 ++----------- 2 files changed, 10 insertions(+), 13 deletions(-)
drivers/acpi/sleep.c +8 -2
@@ -689,19 +689,25 @@ int acpi_pm_device_sleep_wake(struct dev {acpi_handle handle;struct acpi_device *adev; +int error;if (!device_may_wakeup (dev))return -EINVAL ; handle =DEVICE_ACPI_HANDLE (dev);if (!handle ||ACPI_FAILURE (acpi_bus_get_device (handle, &adev))) { -printk (KERN_DEBUG "ACPI handle has no context!\n" ); +dev_dbg (dev,"ACPI handle has no context in %s!\n" , __func__);return -ENODEV ; } -return enable ? + error = enable ?acpi_enable_wakeup_device_power (adev, acpi_target_sleep_state) :acpi_disable_wakeup_device_power (adev); +if (!error) +dev_info (dev,"wake-up capability %s by ACPI\n" , + enable ?"enabled" :"disabled" ); + +return error; }#endif
drivers/pci/pci-acpi.c +2 -11
@@ -111,17 +111,8 @@ static bool acpi_pci_can_wakeup(struct pstatic int acpi_pci_sleep_wake (struct pci_dev *dev,bool enable) { -int error; - -if (!acpi_pci_can_wakeup (dev)) -return 0 ; - - error =acpi_pm_device_sleep_wake (&dev->dev, enable); -if (!error) -dev_info (&dev->dev,"wake-up capability %s by ACPI\n" , - enable ?"enabled" :"disabled" ); - -return error; +return acpi_pci_can_wakeup (dev) ? +acpi_pm_device_sleep_wake (&dev->dev, enable) :0 ; }static struct pci_platform_pm_ops acpi_pci_platform_pm = {
PATCH 2/5 PCI / ACPI PM: Rework some debug messages
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Move a debug message from acpi_pci_sleep_wake() to acpi_pm_device_sleep_wake() and use the standard dev_*() macros in there.
--- drivers/acpi/sleep.c | 10 ++++++++-- drivers/pci/pci-acpi.c | 13 ++----------- 2 files changed, 10 insertions(+), 13 deletions(-)
drivers/acpi/sleep.c +8 -2
@@ -689,19 +689,25 @@ int acpi_pm_device_sleep_wake(struct dev {acpi_handle handle;struct acpi_device *adev; +int error;if (!device_may_wakeup (dev))return -EINVAL ; handle =DEVICE_ACPI_HANDLE (dev);if (!handle ||ACPI_FAILURE (acpi_bus_get_device (handle, &adev))) { -printk (KERN_DEBUG "ACPI handle has no context!\n" ); +dev_dbg (dev,"ACPI handle has no context in %s!\n" , __func__);return -ENODEV ; } -return enable ? + error = enable ?acpi_enable_wakeup_device_power (adev, acpi_target_sleep_state) :acpi_disable_wakeup_device_power (adev); +if (!error) +dev_info (dev,"wake-up capability %s by ACPI\n" , + enable ?"enabled" :"disabled" ); + +return error; }#endif
drivers/pci/pci-acpi.c +2 -11
@@ -111,17 +111,8 @@ static bool acpi_pci_can_wakeup(struct pstatic int acpi_pci_sleep_wake (struct pci_dev *dev,bool enable) { -int error; - -if (!acpi_pci_can_wakeup (dev)) -return 0 ; - - error =acpi_pm_device_sleep_wake (&dev->dev, enable); -if (!error) -dev_info (&dev->dev,"wake-up capability %s by ACPI\n" , - enable ?"enabled" :"disabled" ); - -return error; +return acpi_pci_can_wakeup (dev) ? +acpi_pm_device_sleep_wake (&dev->dev, enable) :0 ; }static struct pci_platform_pm_ops acpi_pci_platform_pm = {
PATCH 3/5 PCI PM: Introduce device flag wakeup_prepared
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Introduce a new PCI device flag, wakeup_prepared, to prevent PCI wake-up preparation code from being executed twice in a row for the same device and for the same purpose.
--- drivers/pci/pci.c | 8 ++++++++ include/linux/pci.h | 1 + 2 files changed, 9 insertions(+)
drivers/pci/pci.c +8 -0
@@ -1223,6 +1223,10 @@ int pci_enable_wake(struct pci_dev *dev,if (enable && !device_may_wakeup (&dev->dev))return -EINVAL ; +/* Don't do the same thing twice in a row for one device. */ +if (!!enable == !!dev->wakeup_prepared) +return 0 ; +/* * According to "PCI System Architecture" 4th ed. by Tom Shanley & Don * Anderson we should be doing PME# wake enable followed by ACPI wake
@@ -1239,9 +1243,12 @@ int pci_enable_wake(struct pci_dev *dev, error =platform_pci_sleep_wake (dev, true);if (ret) ret = error; +if (!ret) + dev->wakeup_prepared = true; }else {platform_pci_sleep_wake (dev, false);pci_pme_active (dev, false); + dev->wakeup_prepared = false; }return ret;
@@ -1363,6 +1370,7 @@ void pci_pm_init(struct pci_dev *dev)int pm;u16 pmc; + dev->wakeup_prepared = false; dev->pm_cap =0 ;/* find PCI PM capability in list */
include/linux/pci.h +1 -0
@@ -241,6 +241,7 @@ struct pci_dev { unsigned int d1_support:1; /* Low power state D1 is supported */ unsigned int d2_support:1; /* Low power state D2 is supported */ unsigned int no_d1d2:1; /* Only allow D0 and D3 */ + unsigned int wakeup_prepared:1;#ifdef CONFIG_PCIEASPM struct pcie_link_state *link_state;/* ASPM link state. */
PATCH 3/5 PCI PM: Introduce device flag wakeup_prepared
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Introduce a new PCI device flag, wakeup_prepared, to prevent PCI wake-up preparation code from being executed twice in a row for the same device and for the same purpose.
--- drivers/pci/pci.c | 8 ++++++++ include/linux/pci.h | 1 + 2 files changed, 9 insertions(+)
drivers/pci/pci.c +8 -0
@@ -1223,6 +1223,10 @@ int pci_enable_wake(struct pci_dev *dev,if (enable && !device_may_wakeup (&dev->dev))return -EINVAL ; +/* Don't do the same thing twice in a row for one device. */ +if (!!enable == !!dev->wakeup_prepared) +return 0 ; +/* * According to "PCI System Architecture" 4th ed. by Tom Shanley & Don * Anderson we should be doing PME# wake enable followed by ACPI wake
@@ -1239,9 +1243,12 @@ int pci_enable_wake(struct pci_dev *dev, error =platform_pci_sleep_wake (dev, true);if (ret) ret = error; +if (!ret) + dev->wakeup_prepared = true; }else {platform_pci_sleep_wake (dev, false);pci_pme_active (dev, false); + dev->wakeup_prepared = false; }return ret;
@@ -1363,6 +1370,7 @@ void pci_pm_init(struct pci_dev *dev)int pm;u16 pmc; + dev->wakeup_prepared = false; dev->pm_cap =0 ;/* find PCI PM capability in list */
include/linux/pci.h +1 -0
@@ -241,6 +241,7 @@ struct pci_dev { unsigned int d1_support:1; /* Low power state D1 is supported */ unsigned int d2_support:1; /* Low power state D2 is supported */ unsigned int no_d1d2:1; /* Only allow D0 and D3 */ + unsigned int wakeup_prepared:1;#ifdef CONFIG_PCIEASPM struct pcie_link_state *link_state;/* ASPM link state. */
PATCH 4/5 ACPI PM: Replace wakeup.prepared with reference counter
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
The wakeup.prepared flag is used for marking devices that have the wake-up power already enabled, so that the wake-up power is not enabled twice in a row for the same device. This assumes, however, that device wake-up power will only be enabled once, while the device is being prepared for a system-wide sleep transition, and the second attempt is made by acpi_enable_wakeup_device_prep().
With the upcoming PCI wake-up rework this assumption will not hold any more for PCI bridges and the root bridge whose wake-up power may be enabled as a result of wake-up enable propagation from other devices (eg. add-on devices that are not associated with any GPEs). Thus, there may be many attempts to enable wake-up power on a PCI bridge or the root bridge during a system power state transition and it's better to replace wakeup.prepared with a reference counter.
--- drivers/acpi/power.c | 58 ++++++++++++++++++++++++++++-------------------- drivers/acpi/scan.c | 1 drivers/acpi/wakeup.c | 4 +-- include/acpi/acpi_bus.h | 2 - 4 files changed, 39 insertions(+), 26 deletions(-)
drivers/acpi/scan.c +1 -0
@@ -781,6 +781,7 @@ static int acpi_bus_get_wakeup_device_flkfree (buffer.pointer); device->wakeup.flags.valid =1 ; + device->wakeup.prepare_count =0 ;/* Call _PSW/_DSW object to disable its ability to wake the sleeping * system for the ACPI device with the _PRW object. * The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW.
include/acpi/acpi_bus.h +1 -1
@@ -248,7 +248,6 @@ struct acpi_device_perf {/* Wakeup Management */ struct acpi_device_wakeup_flags {u8 valid:1 ;/* Can successfully enable wakeup? */ -u8 prepared:1 ;/* Has the wake-up capability been enabled? */ u8 run_wake:1 ;/* Run-Wake GPE devices */ };
@@ -263,6 +262,7 @@ struct acpi_device_wakeup {struct acpi_handle_list resources;struct acpi_device_wakeup_state state;struct acpi_device_wakeup_flags flags; +int prepare_count; };/* Device */
drivers/acpi/power.c +35 -23
109 lines not on this page · raw message
drivers/acpi/wakeup.c +2 -2
@@ -68,7 +68,7 @@ void acpi_enable_wakeup_device(u8 sleep_/* If users want to disable run-wake GPE, * we only disable it for wake and leave it for runtime */ -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) {/* set_gpe_type will disable GPE, leave it like that */
@@ -100,7 +100,7 @@ void acpi_disable_wakeup_device(u8 sleepif (!dev->wakeup.flags.valid)continue ; -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) { acpi_set_gpe_type(dev->wakeup.gpe_device,
PATCH 4/5 ACPI PM: Replace wakeup.prepared with reference counter
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
The wakeup.prepared flag is used for marking devices that have the wake-up power already enabled, so that the wake-up power is not enabled twice in a row for the same device. This assumes, however, that device wake-up power will only be enabled once, while the device is being prepared for a system-wide sleep transition, and the second attempt is made by acpi_enable_wakeup_device_prep().
With the upcoming PCI wake-up rework this assumption will not hold any more for PCI bridges and the root bridge whose wake-up power may be enabled as a result of wake-up enable propagation from other devices (eg. add-on devices that are not associated with any GPEs). Thus, there may be many attempts to enable wake-up power on a PCI bridge or the root bridge during a system power state transition and it's better to replace wakeup.prepared with a reference counter.
--- drivers/acpi/power.c | 58 ++++++++++++++++++++++++++++-------------------- drivers/acpi/scan.c | 1 drivers/acpi/wakeup.c | 4 +-- include/acpi/acpi_bus.h | 2 - 4 files changed, 39 insertions(+), 26 deletions(-)
drivers/acpi/scan.c +1 -0
@@ -781,6 +781,7 @@ static int acpi_bus_get_wakeup_device_flkfree (buffer.pointer); device->wakeup.flags.valid =1 ; + device->wakeup.prepare_count =0 ;/* Call _PSW/_DSW object to disable its ability to wake the sleeping * system for the ACPI device with the _PRW object. * The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW.
include/acpi/acpi_bus.h +1 -1
@@ -248,7 +248,6 @@ struct acpi_device_perf {/* Wakeup Management */ struct acpi_device_wakeup_flags {u8 valid:1 ;/* Can successfully enable wakeup? */ -u8 prepared:1 ;/* Has the wake-up capability been enabled? */ u8 run_wake:1 ;/* Run-Wake GPE devices */ };
@@ -263,6 +262,7 @@ struct acpi_device_wakeup {struct acpi_handle_list resources;struct acpi_device_wakeup_state state;struct acpi_device_wakeup_flags flags; +int prepare_count; };/* Device */
drivers/acpi/power.c +35 -23
109 lines not on this page · raw message
drivers/acpi/wakeup.c +2 -2
@@ -68,7 +68,7 @@ void acpi_enable_wakeup_device(u8 sleep_/* If users want to disable run-wake GPE, * we only disable it for wake and leave it for runtime */ -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) {/* set_gpe_type will disable GPE, leave it like that */
@@ -100,7 +100,7 @@ void acpi_disable_wakeup_device(u8 sleepif (!dev->wakeup.flags.valid)continue ; -if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) +if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) || sleep_state > (u32 ) dev->wakeup.sleep_state) {if (dev->wakeup.flags.run_wake) { acpi_set_gpe_type(dev->wakeup.gpe_device,
PATCH 5/5 PCI / ACPI PM: Propagate wake-up enable for devices w/o ACPI support
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
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.
--- drivers/acpi/sleep.c | 2 +- drivers/pci/pci-acpi.c | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-)
drivers/pci/pci-acpi.c +24 -2
@@ -109,10 +109,32 @@ static bool acpi_pci_can_wakeup(struct preturn 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 = {
drivers/acpi/sleep.c +1 -1
@@ -691,7 +691,7 @@ int acpi_pm_device_sleep_wake(struct devstruct acpi_device *adev;int error; -if (!device_may_wakeup (dev)) +if (!device_can_wakeup (dev))return -EINVAL ; handle =DEVICE_ACPI_HANDLE (dev);
PATCH 5/5 PCI / ACPI PM: Propagate wake-up enable for devices w/o ACPI support
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
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.
--- drivers/acpi/sleep.c | 2 +- drivers/pci/pci-acpi.c | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-)
drivers/pci/pci-acpi.c +24 -2
@@ -109,10 +109,32 @@ static bool acpi_pci_can_wakeup(struct preturn 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 = {
drivers/acpi/sleep.c +1 -1
@@ -691,7 +691,7 @@ int acpi_pm_device_sleep_wake(struct devstruct acpi_device *adev;int error; -if (!device_may_wakeup (dev)) +if (!device_can_wakeup (dev))return -EINVAL ; handle =DEVICE_ACPI_HANDLE (dev);
cover PCI / ACPI PM: Propagate wake-up enable upstream
In reply to Rafael J. WysockiHi,
The patchset has been confirmed to fix the issue it was designed for (ie. it allows an add-on PCI card to wake-up the system from S3 by enabling the GPE of the bridge upstream to wake up), so I'd like to get it in as soon as reasonably possible.
Please review.
Thanks,
Rafael
Quoted 17 lines · Rafael J. Wysocki
Quoted 8 lines · Matthew Garrett
Quoted 4 lines · Rafael J. Wysocki
Updated patch is appended, but there's a problem with it. Namely, when the root bridge GPE is shared with other devices, those devices are also enabled to wake up and they may be kind of "interesting" (on one of my test boxes they include the ethernet and audio adapters). I'm not sure what to do about that.
Perhaps only enable it if a child device is enabled?
Well, the problem is that we have to enable the devices that share the GPE to wake up together, but the child device we _really_ want to wake-up need not be any of them.
Also, the child device may be enabled to wake up after we've run the "glue" code.
Quoted 7 lines
Well, perhaps it's better to rework
pci_enable_wake()so that it, if the device doesn't have an ACPI handle and is not PCIe, it will go and enable the root bridge to wake up as well?I wonder what about the bridges between the desired wake-up device and the root bridge. I guess they also should be enabled to wake-up, so that they can forward the PME# in case the system is designed this way.
Below is a prototype of what I was thinking of, although I'm not sure if that's enough.
Comments welcome.
Rafael
--- drivers/pci/pci.c | 134 +++++++++++++++++++++++++++++++--------------------- include/linux/pci.h | 7 ++ 2 files changed, 87 insertions(+), 54 deletions(-)
drivers/pci/pci.c +81 -53
@@ -1198,71 +1198,100 @@ void pci_pme_active(struct pci_dev *dev, }/** -* pci_enable_wake - enable PCI device as wakeup event source -* @dev: PCI device affected -* @state: PCI state from which device will issue wakeup events -* @enable: True to enable event generation; false to disable -* -* This enables the device as a wakeup event source, or disables it. -* When such events involves platform-specific hooks, those hooks are -* called automatically by this routine. +* pci_enable_wakeup - Enable given device to wake up the system. +* @dev: Device to become a wake-up event source. * -* Devices with legacy power management (no standard PCI PM capabilities) -* always require such platform hooks. +* If the device itself has no platform support for wake-up, we go to it's +* parent bridge and try to enable it to wake up the system and so on, +* recursively, up to the root bridge. * -* RETURN VALUE: -* 0 is returned on success -* -EINVAL is returned if device is not supposed to wake up the system -* Error code depending on the platform is returned if both the platform and -* the native mechanism fail to enable the generation of wake-up events +* According to "PCI System Architecture" 4th ed. by Tom Shanley & Don Anderson +* we should be doing PME# wake enable followed by ACPI wake enable. To disable +* wake-up we call the platform first, for symmetry. */ -int pci_enable_wake (struct pci_dev *dev,pci_power_t state,bool enable) +static int pci_enable_wakeup (struct pci_dev *dev) { -int error =0 ; -bool pme_done = false; - -if (enable && !device_may_wakeup (&dev->dev)) +if (!device_may_wakeup (&dev->dev))return -EINVAL ; -/* -* According to "PCI System Architecture" 4th ed. by Tom Shanley & Don -* Anderson we should be doing PME# wake enable followed by ACPI wake -* enable. To disable wake-up we call the platform first, for symmetry. -*/ +dev_info (&dev->dev,"PCI: enabling wake-up.\n" ); + +if (atomic_add_return (1 , &dev->wakeup_count) !=1 ) +return 0 ; + +pci_pme_active (dev, true); +if (platform_pci_can_wakeup (dev)) +platform_pci_sleep_wake (dev, true); +else if (!dev->is_pcie && dev->bus && dev->bus->self) +pci_enable_wakeup (dev->bus->self); + + +return 0 ; +} + +/** +* pci_disable_wakeup - Disable given device from waking up the system. +* @dev: Device to handle. +* +* If the device itself has no platform support for wake-up, we go to it's +* parent bridge and try to disable it from waking up the system and so on, +* recursively, up to the root bridge. +*/ +static int pci_disable_wakeup (struct pci_dev *dev) +{ +int count; + +if (!device_may_wakeup (&dev->dev)) +return -EINVAL ; -if (!enable &&platform_pci_can_wakeup (dev)) - error =platform_pci_sleep_wake (dev, false); +dev_info (&dev->dev,"PCI: disabling wake-up.\n" ); -if (!enable ||pci_pme_capable (dev, state)) { -pci_pme_active (dev, enable); - pme_done = true; + count =atomic_add_return (-1 , &dev->wakeup_count); +if (count <0 ) { +dev_err (&dev->dev,"unbalanced %s!\n" , __func__); +return -EALREADY ; + }else if (count >0 ) { +return 0 ; } -if (enable &&platform_pci_can_wakeup (dev)) - error =platform_pci_sleep_wake (dev, true); +if (platform_pci_can_wakeup (dev)) { +platform_pci_sleep_wake (dev, false); +pci_pme_active (dev, false); + }else { +pci_pme_active (dev, false); +if (!dev->is_pcie && dev->bus && dev->bus->self) +pci_disable_wakeup (dev->bus->self); + } -return pme_done ?0 : error; +return 0 ; }/** -* pci_wake_from_d3 - enable/disable device to wake up from D3_hot or D3_cold -* @dev: PCI device to prepare -* @enable: True to enable wake-up event generation; false to disable +* pci_enable_wake - Enable PCI device as wake-up event source. +* @dev: PCI device affected. +* @state: Ignored. +* @enable: True to enable wake-up event generation; false to disable. +* +* Enable the device as a wake-up event source or disable it, depending on the +* value of @enable. The generation of such events may require us to set up the +* platform appropriately, which involves executing some platform-specific +* hooks. If they are needed, they will be called automatically. * -* Many drivers want the device to wake up the system from D3_hot or D3_cold -* and this function allows them to set that up cleanly - pci_enable_wake() -* should not be called twice in a row to enable wake-up due to PCI PM vs ACPI -* ordering constraints. +* Devices with legacy power management (no standard PCI PM capabilities) always +* require such platform hooks. * -* This function only returns error code if the device is not capable of -* generating PME# from both D3_hot and D3_cold, and the platform is unable to -* enable wake-up power for it. +* RETURN VALUE: +* 0 is returned on success. +* -EINVAL is returned if the device is not supposed to wake up the system. +* -EALREADY is returned on an attempt to disable a device that is not enabled +* to wake-up. */ -int pci_wake_from_d3 (struct pci_dev *dev,bool enable) +int pci_enable_wake (struct pci_dev *dev,pci_power_t state,bool enable) { -return pci_pme_capable (dev, PCI_D3cold) ? -pci_enable_wake (dev, PCI_D3cold, enable) : -pci_enable_wake (dev, PCI_D3hot, enable); +if (!enable && !atomic_read (&dev->wakeup_count)) +return 0 ; + +return enable ?pci_enable_wakeup (dev) :pci_disable_wakeup (dev); }/**
@@ -1323,18 +1352,16 @@ pci_power_t pci_target_state(struct pci_*/ int pci_prepare_to_sleep (struct pci_dev *dev) { -pci_power_t target_state =pci_target_state (dev);int error; +pci_power_t target_state =pci_target_state (dev);if (target_state ==PCI_POWER_ERROR )return -EIO ; -pci_enable_wake (dev, target_state,device_may_wakeup (&dev->dev)); - +pci_enable_wakeup (dev); error =pci_set_power_state (dev, target_state); -if (error) -pci_enable_wake (dev, target_state, false); +pci_disable_wakeup (dev);return error; }
@@ -1347,7 +1374,7 @@ int pci_prepare_to_sleep(struct pci_dev*/ int pci_back_from_sleep (struct pci_dev *dev) { -pci_enable_wake (dev,PCI_D0 , false); +pci_disable_wakeup (dev);return pci_set_power_state (dev,PCI_D0 ); }
@@ -1362,6 +1389,7 @@ void pci_pm_init(struct pci_dev *dev)device_enable_async_suspend (&dev->dev, true); dev->pm_cap =0 ; +atomic_set (&dev->wakeup_count,0 );/* find PCI PM capability in list */ pm =pci_find_capability (dev,PCI_CAP_ID_PM );
include/linux/pci.h +6 -1
@@ -241,6 +241,7 @@ struct pci_dev { unsigned int d1_support:1; /* Low power state D1 is supported */ unsigned int d2_support:1; /* Low power state D2 is supported */ unsigned int no_d1d2:1; /* Only allow D0 and D3 */ +atomic_t wakeup_count;#ifdef CONFIG_PCIEASPM struct pcie_link_state *link_state;/* ASPM link state. */
@@ -734,11 +735,15 @@ pci_power_t pci_choose_state(struct pci_bool pci_pme_capable (struct pci_dev *dev,pci_power_t state);void pci_pme_active (struct pci_dev *dev,bool enable);int pci_enable_wake (struct pci_dev *dev,pci_power_t state,bool enable); -int pci_wake_from_d3 (struct pci_dev *dev,bool enable);pci_power_t pci_target_state (struct pci_dev *dev);int pci_prepare_to_sleep (struct pci_dev *dev);int pci_back_from_sleep (struct pci_dev *dev); +static inline int pci_wake_from_d3 (struct pci_dev *dev,bool enable) +{ +return pci_enable_wake (dev, PCI_D3hot, enable); +} +/* Functions for PCI Hotplug drivers to use */ int pci_bus_find_capability (struct pci_bus *bus,unsigned int devfn,int cap);#ifdef CONFIG_HOTPLUG
Quoted 8 lines · Matthew Garrett
Quoted 4 lines · Rafael J. Wysocki
Updated patch is appended, but there's a problem with it. Namely, when the root bridge GPE is shared with other devices, those devices are also enabled to wake up and they may be kind of "interesting" (on one of my test boxes they include the ethernet and audio adapters). I'm not sure what to do about that.
Perhaps only enable it if a child device is enabled?
Well, the problem is that we have to enable the devices that share the GPE to wake up together, but the child device we _really_ want to wake-up need not be any of them.
Also, the child device may be enabled to wake up after we've run the "glue" code.
Well, perhaps it's better to rework pci_enable_wake() so that it, if the device doesn't have an ACPI handle and is not PCIe, it will go and enable the root bridge to wake up as well?
I wonder what about the bridges between the desired wake-up device and the root bridge. I guess they also should be enabled to wake-up, so that they can forward the PME# in case the system is designed this way.
Thoughts?
Quoted 2 lines
This is difficult with the current code - the refcounted GPE stuff I'm working on ought to make it easier.
Ah, I think that's going to be useful.
Thanks,
Rafael
Quoted 4 lines · Rafael J. Wysocki
Updated patch is appended, but there's a problem with it. Namely, when the root bridge GPE is shared with other devices, those devices are also enabled to wake up and they may be kind of "interesting" (on one of my test boxes they include the ethernet and audio adapters). I'm not sure what to do about that.
Perhaps only enable it if a child device is enabled? This is difficult with the current code - the refcounted GPE stuff I'm working on ought to make it easier.
Signature · 1 line
Matthew Garrett | [email protected]
RFC PATCH 1/1 ACPI / PM: Allow PCI root bridges to wake up the system
In reply to Rafael J. WysockiQuoted 10 lines · Rafael J. Wysocki
Quoted 9 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
If PCI root bridge is declared in the ACPI tables as a wake-up device, allow it to wake up the system by default.
This allows add-on PCI devices to work as wake-up devices on some systems, where PME# asserted by an add-on device causes the root bridge GPE to generate a wake-up event, without using
/proc/acpi/wakeupto change the root bridge wake-up setting.
Updated patch is appended, but there's a problem with it. Namely, when the root bridge GPE is shared with other devices, those devices are also enabled to wake up and they may be kind of "interesting" (on one of my test boxes they include the ethernet and audio adapters). I'm not sure what to do about that.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: ACPI / PM: Allow PCI root bridges to wake up the system
If PCI root bridge is declared in the ACPI tables as a wake-up device, allow it to wake up the system by default.
This allows add-on PCI devices to work as wake-up devices on some systems, where PME# asserted by an add-on device causes the root bridge GPE to generate a wake-up event, without using /proc/acpi/wakeup to change the root bridge wake-up setting.
--- drivers/acpi/glue.c | 9 +++++++++ drivers/acpi/pci_root.c | 25 +++++++++++++------------ drivers/acpi/proc.c | 29 +---------------------------- drivers/acpi/sleep.h | 2 ++ drivers/acpi/wakeup.c | 35 +++++++++++++++++++++++++++++++++++ include/acpi/acpi_bus.h | 1 + 6 files changed, 61 insertions(+), 40 deletions(-)
drivers/acpi/glue.c +9 -0
@@ -13,6 +13,8 @@#include <linux/acpi.h> #include <linux/pm_link.h> +#include "sleep.h" +#define ACPI_GLUE_DEBUG 0#if ACPI_GLUE_DEBUG #define DBG(x...) printk(PREFIX x)
@@ -168,6 +170,13 @@ static int acpi_bind_one(struct device * "physical_node");if (acpi_dev->wakeup.flags.valid) {device_set_wakeup_capable (dev, true); +/* Allow PCI root bridges to wake up the system. */ +if (acpi_dev_is_root_bridge (acpi_dev)) { +mutex_lock (&acpi_device_lock); + acpi_dev->wakeup.state.enabled = true; +propagate_enable_wakeup (acpi_dev); +mutex_unlock (&acpi_device_lock); + }device_set_wakeup_enable (dev, acpi_dev->wakeup.state.enabled); }
drivers/acpi/pci_root.c +13 -12
@@ -138,26 +138,27 @@ acpi_handle acpi_get_pci_rootbridge_handEXPORT_SYMBOL_GPL (acpi_get_pci_rootbridge_handle);/** +* acpi_dev_is_root_bridge - determine if an ACPI device is a PCI root bridge +* @device: ACPI device to check. +*/ +bool acpi_dev_is_root_bridge (struct acpi_device *device) +{ +return !acpi_match_device_ids (device, root_device_ids); +} + +/** * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge -* @handle - the ACPI CA node in question. -* -* Note: we could make this API take a struct acpi_device * instead, but -* for now, it's more convenient to operate on an acpi_handle. +* @handle: the ACPI CA node in question. */ int acpi_is_root_bridge (acpi_handle handle) {int ret;struct acpi_device *device; - ret =acpi_bus_get_device (handle, &device); + ret = !acpi_bus_get_device (handle, &device);if (ret) -return 0 ; - - ret =acpi_match_device_ids (device, root_device_ids); -if (ret) -return 0 ; -else -return 1 ; + ret =acpi_dev_is_root_bridge (device); +return ret; }EXPORT_SYMBOL_GPL (acpi_is_root_bridge);
include/acpi/acpi_bus.h +1 -0
@@ -371,6 +371,7 @@ struct device *acpi_get_physical_device(/* helper */ acpi_handle acpi_get_child (acpi_handle ,acpi_integer ); +bool acpi_dev_is_root_bridge (struct acpi_device *);int acpi_is_root_bridge (acpi_handle );acpi_handle acpi_get_pci_rootbridge_handle (unsigned int ,unsigned int );#define DEVICE_ACPI_HANDLE (dev) ((acpi_handle)((dev)->archdata.acpi_handle))
drivers/acpi/proc.c +1 -28
@@ -377,14 +377,6 @@ acpi_system_wakeup_device_seq_show(strucreturn 0 ; } -static void physical_device_enable_wakeup (struct acpi_device *adev) -{ -struct device *dev =acpi_get_physical_device (adev->handle); - -if (dev &&device_can_wakeup (dev)) -device_set_wakeup_enable (dev, adev->wakeup.state.enabled); -} - static ssize_t acpi_system_write_wakeup_device(struct file *file, const char __user * buffer,
@@ -420,26 +412,7 @@ acpi_system_write_wakeup_device(struct f }if (found_dev) {physical_device_enable_wakeup (found_dev); -list_for_each_safe (node, next, &acpi_wakeup_device_list) { -struct acpi_device *dev =container_of (node, - struct - acpi_device, - wakeup_list); - -if ((dev != found_dev) && - (dev->wakeup.gpe_number == - found_dev->wakeup.gpe_number) - && (dev->wakeup.gpe_device == - found_dev->wakeup.gpe_device)) { -printk (KERN_WARNING -"ACPI: '%s' and '%s' have the same GPE, " -"can't disable/enable one seperately\n" , - dev->pnp.bus_id, found_dev->pnp.bus_id); - dev->wakeup.state.enabled = - found_dev->wakeup.state.enabled; -physical_device_enable_wakeup (dev); - } - } +propagate_enable_wakeup (found_dev); }mutex_unlock (&acpi_device_lock);return count;
drivers/acpi/wakeup.c +35 -0
@@ -124,6 +124,41 @@ void acpi_disable_wakeup_device(u8 sleep } } +void physical_device_enable_wakeup (struct acpi_device *adev) +{ +struct device *dev =acpi_get_physical_device (adev->handle); + +if (dev &&device_can_wakeup (dev)) +device_set_wakeup_enable (dev, adev->wakeup.state.enabled); +} + +void propagate_enable_wakeup (struct acpi_device *wakeup_dev) +{ +struct list_head *node, *next; + +list_for_each_safe (node, next, &acpi_wakeup_device_list) { +struct acpi_device *dev = + container_of(node, struct acpi_device, wakeup_list); + +if (!dev->wakeup.flags.valid) +continue ; + +if (dev == wakeup_dev) +continue ; + +if (dev->wakeup.gpe_number != wakeup_dev->wakeup.gpe_number + || dev->wakeup.gpe_device != wakeup_dev->wakeup.gpe_device) +continue ; + +printk (KERN_WARNING "ACPI: '%s' and '%s' share a GPE, " +"unable to disable/enable one seperately\n" , + dev->pnp.bus_id, wakeup_dev->pnp.bus_id); + + dev->wakeup.state.enabled = wakeup_dev->wakeup.state.enabled; +physical_device_enable_wakeup (dev); + } +} +int __init acpi_wakeup_device_init (void ) {struct list_head *node, *next;
drivers/acpi/sleep.h +2 -0
@@ -5,6 +5,8 @@ extern int acpi_suspend (u32 state);extern void acpi_enable_wakeup_device_prep (u8 sleep_state);extern void acpi_enable_wakeup_device (u8 sleep_state);extern void acpi_disable_wakeup_device (u8 sleep_state); +extern void physical_device_enable_wakeup (struct acpi_device *adev); +extern void propagate_enable_wakeup (struct acpi_device *wakeup_dev);extern struct list_head acpi_wakeup_device_list;extern struct mutex acpi_device_lock;
Quoted 9 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
If PCI root bridge is declared in the ACPI tables as a wake-up device, allow it to wake up the system by default.
This allows add-on PCI devices to work as wake-up devices on some systems, where PME# asserted by an add-on device causes the root bridge GPE to generate a wake-up event, without using
/proc/acpi/wakeupto change the root bridge wake-up setting.
One piece is missing from the patch, I'll prepare an updated one shortly.
Thanks,
Rafael
Quoted 77 lines
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/acpi/glue.c | 3 +++ drivers/acpi/pci_root.c | 25 +++++++++++++------------ include/acpi/acpi_bus.h | 1 + 3 files changed, 17 insertions(+), 12 deletions(-)Index:
linux-2.6/drivers/acpi/glue.c
===================================================================drivers/acpi/glue.c +3
-0@@ -168,6 +168,9 @@ static int acpi_bind_one(struct device * "physical_node");if (acpi_dev->wakeup.flags.valid) {device_set_wakeup_capable (dev, true); +/* Allow PCI root bridges to wake up the system. */ +if (acpi_dev_is_root_bridge (acpi_dev)) + acpi_dev->wakeup.state.enabled = true;device_set_wakeup_enable (dev, acpi_dev->wakeup.state.enabled); }Index:
linux-2.6/drivers/acpi/pci_root.c
===================================================================drivers/acpi/pci_root.c +13
-12@@ -138,26 +138,27 @@ acpi_handle acpi_get_pci_rootbridge_handEXPORT_SYMBOL_GPL (acpi_get_pci_rootbridge_handle);/** +* acpi_dev_is_root_bridge - determine if an ACPI device is a PCI root bridge +* @device: ACPI device to check. +*/ +bool acpi_dev_is_root_bridge (struct acpi_device *device) +{ +return !acpi_match_device_ids (device, root_device_ids); +} + +/** * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge -* @handle - the ACPI CA node in question. -* -* Note: we could make this API take a struct acpi_device * instead, but -* for now, it's more convenient to operate on an acpi_handle. +* @handle: the ACPI CA node in question. */ int acpi_is_root_bridge (acpi_handle handle) {int ret;struct acpi_device *device; - ret =acpi_bus_get_device (handle, &device); + ret = !acpi_bus_get_device (handle, &device);if (ret) -return 0 ; - - ret =acpi_match_device_ids (device, root_device_ids); -if (ret) -return 0 ; -else -return 1 ; + ret =acpi_dev_is_root_bridge (device); +return ret; }EXPORT_SYMBOL_GPL (acpi_is_root_bridge);Index:
linux-2.6/include/acpi/acpi_bus.h
===================================================================include/acpi/acpi_bus.h +1
-0@@ -371,6 +371,7 @@ struct device *acpi_get_physical_device(/* helper */ acpi_handle acpi_get_child (acpi_handle ,acpi_integer ); +bool acpi_dev_is_root_bridge (struct acpi_device *);int acpi_is_root_bridge (acpi_handle );acpi_handle acpi_get_pci_rootbridge_handle (unsigned int ,unsigned int );#define DEVICE_ACPI_HANDLE (dev) ((acpi_handle)((dev)->archdata.acpi_handle))