* [PATCH] ACPI / PM: Allow PCI root bridges to wake up the system
@ 2009-08-29 22:41 Rafael J. Wysocki
2009-08-31 19:41 ` [linux-pm] " Rafael J. Wysocki
0 siblings, 1 reply; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-08-29 22:41 UTC (permalink / raw)
To: Len Brown
Cc: ACPI Devel Maling List, Linux PCI, Jesse Barnes, Matthew Garrett,
Henrique de Moraes Holschuh, pm list
From: Rafael J. Wysocki <rjw@sisk.pl>
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.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
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
===================================================================
--- linux-2.6.orig/drivers/acpi/glue.c
+++ linux-2.6/drivers/acpi/glue.c
@@ -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
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -138,26 +138,27 @@ acpi_handle acpi_get_pci_rootbridge_hand
EXPORT_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
===================================================================
--- linux-2.6.orig/include/acpi/acpi_bus.h
+++ linux-2.6/include/acpi/acpi_bus.h
@@ -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))
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [linux-pm] [PATCH] ACPI / PM: Allow PCI root bridges to wake up the system
2009-08-29 22:41 [PATCH] ACPI / PM: Allow PCI root bridges to wake up the system Rafael J. Wysocki
@ 2009-08-31 19:41 ` Rafael J. Wysocki
2009-08-31 21:24 ` [RFC][PATCH update] " Rafael J. Wysocki
0 siblings, 1 reply; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-08-31 19:41 UTC (permalink / raw)
To: linux-pm
Cc: Len Brown, Linux PCI, Jesse Barnes, ACPI Devel Maling List,
Henrique de Moraes Holschuh
On Sunday 30 August 2009, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> 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.
One piece is missing from the patch, I'll prepare an updated one shortly.
Thanks,
Rafael
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---
> 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
> ===================================================================
> --- linux-2.6.orig/drivers/acpi/glue.c
> +++ linux-2.6/drivers/acpi/glue.c
> @@ -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
> ===================================================================
> --- linux-2.6.orig/drivers/acpi/pci_root.c
> +++ linux-2.6/drivers/acpi/pci_root.c
> @@ -138,26 +138,27 @@ acpi_handle acpi_get_pci_rootbridge_hand
> EXPORT_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
> ===================================================================
> --- linux-2.6.orig/include/acpi/acpi_bus.h
> +++ linux-2.6/include/acpi/acpi_bus.h
> @@ -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))
^ permalink raw reply [flat|nested] 27+ messages in thread
* [RFC][PATCH update] ACPI / PM: Allow PCI root bridges to wake up the system
2009-08-31 19:41 ` [linux-pm] " Rafael J. Wysocki
@ 2009-08-31 21:24 ` Rafael J. Wysocki
2009-09-01 13:25 ` Matthew Garrett
0 siblings, 1 reply; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-08-31 21:24 UTC (permalink / raw)
To: linux-pm
Cc: Linux PCI, Henrique de Moraes Holschuh, ACPI Devel Maling List,
Jesse Barnes
On Monday 31 August 2009, Rafael J. Wysocki wrote:
> On Sunday 30 August 2009, Rafael J. Wysocki wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > 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.
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 <rjw@sisk.pl>
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.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
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(-)
Index: linux-2.6/drivers/acpi/glue.c
===================================================================
--- linux-2.6.orig/drivers/acpi/glue.c
+++ linux-2.6/drivers/acpi/glue.c
@@ -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);
}
Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -138,26 +138,27 @@ acpi_handle acpi_get_pci_rootbridge_hand
EXPORT_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
===================================================================
--- linux-2.6.orig/include/acpi/acpi_bus.h
+++ linux-2.6/include/acpi/acpi_bus.h
@@ -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))
Index: linux-2.6/drivers/acpi/proc.c
===================================================================
--- linux-2.6.orig/drivers/acpi/proc.c
+++ linux-2.6/drivers/acpi/proc.c
@@ -377,14 +377,6 @@ acpi_system_wakeup_device_seq_show(struc
return 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;
Index: linux-2.6/drivers/acpi/wakeup.c
===================================================================
--- linux-2.6.orig/drivers/acpi/wakeup.c
+++ linux-2.6/drivers/acpi/wakeup.c
@@ -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;
Index: linux-2.6/drivers/acpi/sleep.h
===================================================================
--- linux-2.6.orig/drivers/acpi/sleep.h
+++ linux-2.6/drivers/acpi/sleep.h
@@ -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;
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC][PATCH update] ACPI / PM: Allow PCI root bridges to wake up the system
2009-08-31 21:24 ` [RFC][PATCH update] " Rafael J. Wysocki
@ 2009-09-01 13:25 ` Matthew Garrett
2009-09-01 19:03 ` Rafael J. Wysocki
0 siblings, 1 reply; 27+ messages in thread
From: Matthew Garrett @ 2009-09-01 13:25 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: linux-pm, Linux PCI, Henrique de Moraes Holschuh,
ACPI Devel Maling List, Jesse Barnes
On Mon, Aug 31, 2009 at 11:24:20PM +0200, Rafael J. Wysocki wrote:
> 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.
--
Matthew Garrett | mjg59@srcf.ucam.org
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC][PATCH update] ACPI / PM: Allow PCI root bridges to wake up the system
2009-09-01 13:25 ` Matthew Garrett
@ 2009-09-01 19:03 ` Rafael J. Wysocki
2009-09-01 22:41 ` [linux-pm] " Rafael J. Wysocki
0 siblings, 1 reply; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-01 19:03 UTC (permalink / raw)
To: Matthew Garrett
Cc: linux-pm, Linux PCI, Henrique de Moraes Holschuh,
ACPI Devel Maling List, Jesse Barnes
On Tuesday 01 September 2009, Matthew Garrett wrote:
> On Mon, Aug 31, 2009 at 11:24:20PM +0200, Rafael J. Wysocki wrote:
>
> > 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?
> 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
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [linux-pm] [RFC][PATCH update] ACPI / PM: Allow PCI root bridges to wake up the system
2009-09-01 19:03 ` Rafael J. Wysocki
@ 2009-09-01 22:41 ` Rafael J. Wysocki
2009-09-03 22:02 ` [RFC][PATCH 0/4] PCI/ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
0 siblings, 1 reply; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-01 22:41 UTC (permalink / raw)
To: linux-pm
Cc: Matthew Garrett, Linux PCI, Jesse Barnes,
Henrique de Moraes Holschuh, ACPI Devel Maling List
On Tuesday 01 September 2009, Rafael J. Wysocki wrote:
> On Tuesday 01 September 2009, Matthew Garrett wrote:
> > On Mon, Aug 31, 2009 at 11:24:20PM +0200, Rafael J. Wysocki wrote:
> >
> > > 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.
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(-)
Index: linux-2.6/drivers/pci/pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pci.c
+++ linux-2.6/drivers/pci/pci.c
@@ -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);
Index: linux-2.6/include/linux/pci.h
===================================================================
--- linux-2.6.orig/include/linux/pci.h
+++ linux-2.6/include/linux/pci.h
@@ -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
^ permalink raw reply [flat|nested] 27+ messages in thread
* [RFC][PATCH 0/4] PCI/ACPI PM: Propagate wake-up enable upstream
2009-09-01 22:41 ` [linux-pm] " Rafael J. Wysocki
@ 2009-09-03 22:02 ` Rafael J. Wysocki
2009-09-03 22:03 ` [RFC][PATCH 1/4] PCI PM: Simplify PCI wake-up code Rafael J. Wysocki
` (4 more replies)
0 siblings, 5 replies; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-03 22:02 UTC (permalink / raw)
To: linux-pm
Cc: ACPI Devel Maling List, Henrique de Moraes Holschuh, Jesse Barnes,
Linux PCI
On Wednesday 02 September 2009, Rafael J. Wysocki wrote:
> On Tuesday 01 September 2009, Rafael J. Wysocki wrote:
> > On Tuesday 01 September 2009, Matthew Garrett wrote:
> > > On Mon, Aug 31, 2009 at 11:24:20PM +0200, Rafael J. Wysocki wrote:
> > >
> > > > 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.
>
> 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
^ permalink raw reply [flat|nested] 27+ messages in thread
* [RFC][PATCH 1/4] PCI PM: Simplify PCI wake-up code
2009-09-03 22:02 ` [RFC][PATCH 0/4] PCI/ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
@ 2009-09-03 22:03 ` Rafael J. Wysocki
2009-09-03 22:04 ` [RFC][PATCH 2/4] PCI/ACPI PM: Rework some debug messages Rafael J. Wysocki
` (3 subsequent siblings)
4 siblings, 0 replies; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-03 22:03 UTC (permalink / raw)
To: linux-pm
Cc: ACPI Devel Maling List, Henrique de Moraes Holschuh, Jesse Barnes,
Linux PCI
From: Rafael J. Wysocki <rjw@sisk.pl>
Rework the PCI wake-up code so that it's easier to read without
changing the functionality.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
drivers/pci/pci-acpi.c | 10 +++++++---
drivers/pci/pci.c | 25 ++++++++++++++-----------
2 files changed, 21 insertions(+), 14 deletions(-)
Index: linux-2.6/drivers/pci/pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pci.c
+++ linux-2.6/drivers/pci/pci.c
@@ -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;
}
/**
Index: linux-2.6/drivers/pci/pci-acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pci-acpi.c
+++ linux-2.6/drivers/pci/pci-acpi.c
@@ -111,12 +111,16 @@ static bool acpi_pci_can_wakeup(struct p
static 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;
}
^ permalink raw reply [flat|nested] 27+ messages in thread
* [RFC][PATCH 2/4] PCI/ACPI PM: Rework some debug messages
2009-09-03 22:02 ` [RFC][PATCH 0/4] PCI/ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
2009-09-03 22:03 ` [RFC][PATCH 1/4] PCI PM: Simplify PCI wake-up code Rafael J. Wysocki
@ 2009-09-03 22:04 ` Rafael J. Wysocki
2009-09-03 22:05 ` [RFC][PATCH 3/4] PCI PM: Introduce device flag wakeup_prepared Rafael J. Wysocki
` (2 subsequent siblings)
4 siblings, 0 replies; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-03 22:04 UTC (permalink / raw)
To: linux-pm
Cc: ACPI Devel Maling List, Henrique de Moraes Holschuh, Jesse Barnes,
Linux PCI
From: Rafael J. Wysocki <rjw@sisk.pl>
Move a debug message from acpi_pci_sleep_wake() to
acpi_pm_device_sleep_wake() and use the standard dev_*() macros
in there.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
drivers/acpi/sleep.c | 10 ++++++++--
drivers/pci/pci-acpi.c | 13 ++-----------
2 files changed, 10 insertions(+), 13 deletions(-)
Index: linux-2.6/drivers/acpi/sleep.c
===================================================================
--- linux-2.6.orig/drivers/acpi/sleep.c
+++ linux-2.6/drivers/acpi/sleep.c
@@ -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
Index: linux-2.6/drivers/pci/pci-acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pci-acpi.c
+++ linux-2.6/drivers/pci/pci-acpi.c
@@ -111,17 +111,8 @@ static bool acpi_pci_can_wakeup(struct p
static 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 = {
^ permalink raw reply [flat|nested] 27+ messages in thread
* [RFC][PATCH 3/4] PCI PM: Introduce device flag wakeup_prepared
2009-09-03 22:02 ` [RFC][PATCH 0/4] PCI/ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
2009-09-03 22:03 ` [RFC][PATCH 1/4] PCI PM: Simplify PCI wake-up code Rafael J. Wysocki
2009-09-03 22:04 ` [RFC][PATCH 2/4] PCI/ACPI PM: Rework some debug messages Rafael J. Wysocki
@ 2009-09-03 22:05 ` Rafael J. Wysocki
2009-09-03 22:07 ` [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support Rafael J. Wysocki
2009-09-08 21:11 ` [PATCH 0/5 update] PCI / ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
4 siblings, 0 replies; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-03 22:05 UTC (permalink / raw)
To: linux-pm
Cc: ACPI Devel Maling List, Henrique de Moraes Holschuh, Jesse Barnes,
Linux PCI
From: Rafael J. Wysocki <rjw@sisk.pl>
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.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
drivers/pci/pci.c | 8 ++++++++
include/linux/pci.h | 1 +
2 files changed, 9 insertions(+)
Index: linux-2.6/drivers/pci/pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pci.c
+++ linux-2.6/drivers/pci/pci.c
@@ -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 */
Index: linux-2.6/include/linux/pci.h
===================================================================
--- linux-2.6.orig/include/linux/pci.h
+++ linux-2.6/include/linux/pci.h
@@ -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. */
^ permalink raw reply [flat|nested] 27+ messages in thread
* [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
2009-09-03 22:02 ` [RFC][PATCH 0/4] PCI/ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
` (2 preceding siblings ...)
2009-09-03 22:05 ` [RFC][PATCH 3/4] PCI PM: Introduce device flag wakeup_prepared Rafael J. Wysocki
@ 2009-09-03 22:07 ` Rafael J. Wysocki
2009-09-04 2:13 ` ykzhao
2009-09-04 22:03 ` [linux-pm] " Rafael J. Wysocki
2009-09-08 21:11 ` [PATCH 0/5 update] PCI / ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
4 siblings, 2 replies; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-03 22:07 UTC (permalink / raw)
To: linux-pm
Cc: ACPI Devel Maling List, Henrique de Moraes Holschuh, Jesse Barnes,
Linux PCI
From: Rafael J. Wysocki <rjw@sisk.pl>
Some PCI devices (not PCI Express), like PCI add-on cards, can
generate PME#, but they don't have any special platform wake-up
support. For this reason, even if they generate PME# to wake up the
system from a sleep state, wake-up events are not generated by the
platform.
It turns out that, at least on some systems, PCI bridges and the PCI
host bridge have ACPI GPEs associated with them that, if enabled to
generate wake-up events, allow the system to wake up if one of the
add-on devices asserts PME# while the system is in a sleep state.
Following this observation, if a PCI device without direct ACPI
wake-up support is prepared to wake up the system during a transition
into a sleep state (eg. suspend to RAM), configure all the bridges on
the path from the device to the root bridge (including the root
bridge itself) to wake-up the system.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
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
===================================================================
--- linux-2.6.orig/drivers/pci/pci-acpi.c
+++ linux-2.6/drivers/pci/pci-acpi.c
@@ -109,10 +109,31 @@ static bool acpi_pci_can_wakeup(struct p
return handle ? acpi_bus_can_wakeup(handle) : false;
}
+static void acpi_pci_propagate_wakeup_enable(struct pci_bus *bus, bool enable)
+{
+ while (bus->parent) {
+ struct pci_dev *bridge = bus->self;
+
+ 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
===================================================================
--- linux-2.6.orig/drivers/acpi/sleep.c
+++ linux-2.6/drivers/acpi/sleep.c
@@ -691,7 +691,7 @@ int acpi_pm_device_sleep_wake(struct dev
struct acpi_device *adev;
int error;
- if (!device_may_wakeup(dev))
+ if (!device_can_wakeup(dev))
return -EINVAL;
handle = DEVICE_ACPI_HANDLE(dev);
Index: linux-2.6/drivers/acpi/scan.c
===================================================================
--- linux-2.6.orig/drivers/acpi/scan.c
+++ linux-2.6/drivers/acpi/scan.c
@@ -782,6 +782,7 @@ static int acpi_bus_get_wakeup_device_fl
kfree(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
===================================================================
--- linux-2.6.orig/include/acpi/acpi_bus.h
+++ linux-2.6/include/acpi/acpi_bus.h
@@ -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
===================================================================
--- linux-2.6.orig/drivers/acpi/power.c
+++ linux-2.6/drivers/acpi/power.c
@@ -44,6 +44,8 @@
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
+#include "sleep.h"
+
#define _COMPONENT ACPI_POWER_COMPONENT
ACPI_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(stru
if (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
===================================================================
--- linux-2.6.orig/drivers/acpi/wakeup.c
+++ linux-2.6/drivers/acpi/wakeup.c
@@ -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 sleep
if (!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,
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
2009-09-03 22:07 ` [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support Rafael J. Wysocki
@ 2009-09-04 2:13 ` ykzhao
2009-09-04 14:39 ` Rafael J. Wysocki
2009-09-04 22:03 ` [linux-pm] " Rafael J. Wysocki
1 sibling, 1 reply; 27+ messages in thread
From: ykzhao @ 2009-09-04 2:13 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: linux-pm@lists.linux-foundation.org, ACPI Devel Maling List,
Henrique de Moraes Holschuh, Jesse Barnes, Linux PCI
On Fri, 2009-09-04 at 06:07 +0800, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Some PCI devices (not PCI Express), like PCI add-on cards, can
> generate PME#, but they don't have any special platform wake-up
> support. For this reason, even if they generate PME# to wake up the
> system from a sleep state, wake-up events are not generated by the
> platform.
>
> It turns out that, at least on some systems, PCI bridges and the PCI
> host bridge have ACPI GPEs associated with them that, if enabled to
> generate wake-up events, allow the system to wake up if one of the
> add-on devices asserts PME# while the system is in a sleep state.
> Following this observation, if a PCI device without direct ACPI
> wake-up support is prepared to wake up the system during a transition
> into a sleep state (eg. suspend to RAM), 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.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---
> 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
> ===================================================================
> --- linux-2.6.orig/drivers/pci/pci-acpi.c
> +++ linux-2.6/drivers/pci/pci-acpi.c
> @@ -109,10 +109,31 @@ static bool acpi_pci_can_wakeup(struct p
> return handle ? acpi_bus_can_wakeup(handle) : false;
> }
>
> +static void acpi_pci_propagate_wakeup_enable(struct pci_bus *bus, bool enable)
> +{
> + while (bus->parent) {
> + struct pci_dev *bridge = bus->self;
> +
> + 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
> ===================================================================
> --- linux-2.6.orig/drivers/acpi/sleep.c
> +++ linux-2.6/drivers/acpi/sleep.c
> @@ -691,7 +691,7 @@ int acpi_pm_device_sleep_wake(struct dev
> struct acpi_device *adev;
> int error;
>
> - if (!device_may_wakeup(dev))
> + if (!device_can_wakeup(dev))
> return -EINVAL;
>
> handle = DEVICE_ACPI_HANDLE(dev);
> Index: linux-2.6/drivers/acpi/scan.c
> ===================================================================
> --- linux-2.6.orig/drivers/acpi/scan.c
> +++ linux-2.6/drivers/acpi/scan.c
> @@ -782,6 +782,7 @@ static int acpi_bus_get_wakeup_device_fl
> kfree(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
> ===================================================================
> --- linux-2.6.orig/include/acpi/acpi_bus.h
> +++ linux-2.6/include/acpi/acpi_bus.h
> @@ -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
> ===================================================================
> --- linux-2.6.orig/drivers/acpi/power.c
> +++ linux-2.6/drivers/acpi/power.c
> @@ -44,6 +44,8 @@
> #include <acpi/acpi_bus.h>
> #include <acpi/acpi_drivers.h>
>
> +#include "sleep.h"
> +
> #define _COMPONENT ACPI_POWER_COMPONENT
> ACPI_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(stru
> if (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
> ===================================================================
> --- linux-2.6.orig/drivers/acpi/wakeup.c
> +++ linux-2.6/drivers/acpi/wakeup.c
> @@ -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 sleep
> if (!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 majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
2009-09-04 2:13 ` ykzhao
@ 2009-09-04 14:39 ` Rafael J. Wysocki
2009-09-04 14:56 ` Matthew Garrett
0 siblings, 1 reply; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-04 14:39 UTC (permalink / raw)
To: ykzhao
Cc: linux-pm@lists.linux-foundation.org, ACPI Devel Maling List,
Henrique de Moraes Holschuh, Jesse Barnes, Linux PCI
On Friday 04 September 2009, ykzhao wrote:
> On Fri, 2009-09-04 at 06:07 +0800, Rafael J. Wysocki wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Some PCI devices (not PCI Express), like PCI add-on cards, can
> > generate PME#, but they don't have any special platform wake-up
> > support. For this reason, even if they generate PME# to wake up the
> > system from a sleep state, wake-up events are not generated by the
> > platform.
> >
> > It turns out that, at least on some systems, PCI bridges and the PCI
> > host bridge have ACPI GPEs associated with them that, if enabled to
> > generate wake-up events, allow the system to wake up if one of the
> > add-on devices asserts PME# while the system is in a sleep state.
> > Following this observation, if a PCI device without direct ACPI
> > wake-up support is prepared to wake up the system during a transition
> > into a sleep state (eg. suspend to RAM), 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?
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
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
2009-09-04 14:39 ` Rafael J. Wysocki
@ 2009-09-04 14:56 ` Matthew Garrett
2009-09-04 22:00 ` Rafael J. Wysocki
0 siblings, 1 reply; 27+ messages in thread
From: Matthew Garrett @ 2009-09-04 14:56 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: ykzhao, linux-pm@lists.linux-foundation.org,
ACPI Devel Maling List, Henrique de Moraes Holschuh, Jesse Barnes,
Linux PCI
On Fri, Sep 04, 2009 at 04:39:46PM +0200, Rafael J. Wysocki wrote:
> 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.
> 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.
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.
--
Matthew Garrett | mjg59@srcf.ucam.org
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
2009-09-04 14:56 ` Matthew Garrett
@ 2009-09-04 22:00 ` Rafael J. Wysocki
2009-09-04 22:06 ` Matthew Garrett
0 siblings, 1 reply; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-04 22:00 UTC (permalink / raw)
To: Matthew Garrett
Cc: ykzhao, linux-pm@lists.linux-foundation.org,
ACPI Devel Maling List, Henrique de Moraes Holschuh, Jesse Barnes,
Linux PCI
On Friday 04 September 2009, Matthew Garrett wrote:
> On Fri, Sep 04, 2009 at 04:39:46PM +0200, Rafael J. Wysocki wrote:
>
> > 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.
>
> > 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.
>
> 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
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [linux-pm] [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
2009-09-03 22:07 ` [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support Rafael J. Wysocki
2009-09-04 2:13 ` ykzhao
@ 2009-09-04 22:03 ` Rafael J. Wysocki
2009-09-04 22:05 ` [RFC][PATCH 4/4 replacement] ACPI PM: Replace wakeup.prepared with reference counter Rafael J. Wysocki
2009-09-04 22:06 ` [RFC][PATCH 5] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support Rafael J. Wysocki
1 sibling, 2 replies; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-04 22:03 UTC (permalink / raw)
To: linux-pm
Cc: ACPI Devel Maling List, Jesse Barnes, Henrique de Moraes Holschuh,
Linux PCI, ykzhao
On Friday 04 September 2009, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Some PCI devices (not PCI Express), like PCI add-on cards, can
> generate PME#, but they don't have any special platform wake-up
> support. For this reason, even if they generate PME# to wake up the
> system from a sleep state, wake-up events are not generated by the
> platform.
>
> It turns out that, at least on some systems, PCI bridges and the PCI
> host bridge have ACPI GPEs associated with them that, if enabled to
> generate wake-up events, allow the system to wake up if one of the
> add-on devices asserts PME# while the system is in a sleep state.
> Following this observation, if a PCI device without direct ACPI
> wake-up support is prepared to wake up the system during a transition
> into a sleep state (eg. suspend to RAM), 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
^ permalink raw reply [flat|nested] 27+ messages in thread
* [RFC][PATCH 4/4 replacement] ACPI PM: Replace wakeup.prepared with reference counter
2009-09-04 22:03 ` [linux-pm] " Rafael J. Wysocki
@ 2009-09-04 22:05 ` Rafael J. Wysocki
2009-09-04 22:06 ` [RFC][PATCH 5] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support Rafael J. Wysocki
1 sibling, 0 replies; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-04 22:05 UTC (permalink / raw)
To: linux-pm
Cc: ACPI Devel Maling List, Jesse Barnes, Henrique de Moraes Holschuh,
Linux PCI, ykzhao
From: Rafael J. Wysocki <rjw@sisk.pl>
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.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
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(-)
Index: linux-2.6/drivers/acpi/scan.c
===================================================================
--- linux-2.6.orig/drivers/acpi/scan.c
+++ linux-2.6/drivers/acpi/scan.c
@@ -782,6 +782,7 @@ static int acpi_bus_get_wakeup_device_fl
kfree(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
===================================================================
--- linux-2.6.orig/include/acpi/acpi_bus.h
+++ linux-2.6/include/acpi/acpi_bus.h
@@ -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
===================================================================
--- linux-2.6.orig/drivers/acpi/power.c
+++ linux-2.6/drivers/acpi/power.c
@@ -44,6 +44,8 @@
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
+#include "sleep.h"
+
#define _COMPONENT ACPI_POWER_COMPONENT
ACPI_MODULE_NAME("power");
#define ACPI_POWER_CLASS "power_resource"
@@ -361,17 +363,15 @@ 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;
- /*
- * 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;
+ mutex_lock(&acpi_device_lock);
+
+ if (dev->wakeup.prepare_count++)
+ goto out;
/* Open power resource */
for (i = 0; i < dev->wakeup.resources.count; i++) {
@@ -379,7 +379,8 @@ int acpi_enable_wakeup_device_power(stru
if (ret) {
printk(KERN_ERR PREFIX "Transition power state\n");
dev->wakeup.flags.valid = 0;
- return -ENODEV;
+ err = -ENODEV;
+ goto err_out;
}
}
@@ -388,9 +389,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 +407,42 @@ 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);
+
+ if (--dev->wakeup.prepare_count > 0)
+ goto out;
+
/*
- * Do not execute the code below twice in a row without calling
- * acpi_enable_wakeup_device_power() in between for the same device
+ * Executing the code below even if prepare_count is already zero when
+ * the function is called may be useful, for example for initialisation.
*/
- if (!dev->wakeup.flags.prepared)
- return 0;
+ if (dev->wakeup.prepare_count < 0)
+ dev->wakeup.prepare_count = 0;
- dev->wakeup.flags.prepared = 0;
-
- 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
===================================================================
--- linux-2.6.orig/drivers/acpi/wakeup.c
+++ linux-2.6/drivers/acpi/wakeup.c
@@ -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 sleep
if (!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,
^ permalink raw reply [flat|nested] 27+ messages in thread
* [RFC][PATCH 5] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
2009-09-04 22:03 ` [linux-pm] " Rafael J. Wysocki
2009-09-04 22:05 ` [RFC][PATCH 4/4 replacement] ACPI PM: Replace wakeup.prepared with reference counter Rafael J. Wysocki
@ 2009-09-04 22:06 ` Rafael J. Wysocki
1 sibling, 0 replies; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-04 22:06 UTC (permalink / raw)
To: linux-pm
Cc: ACPI Devel Maling List, Jesse Barnes, Henrique de Moraes Holschuh,
Linux PCI, ykzhao
From: Rafael J. Wysocki <rjw@sisk.pl>
Some PCI devices (not PCI Express), like PCI add-on cards, can
generate PME#, but they don't have any special platform wake-up
support. For this reason, even if they generate PME# to wake up the
system from a sleep state, wake-up events are not generated by the
platform.
It turns out that, at least on some systems, PCI bridges and the PCI
host bridge have ACPI GPEs associated with them that, if enabled to
generate wake-up events, allow the system to wake up if one of the
add-on devices asserts PME# while the system is in a sleep state.
Following this observation, if a PCI device without direct ACPI
wake-up support is prepared to wake up the system during a transition
into a sleep state (eg. suspend to RAM), try to configure the bridges
on the path from the device to the root bridge to wake-up the system.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
drivers/acpi/sleep.c | 2 +-
drivers/pci/pci-acpi.c | 26 ++++++++++++++++++++++++--
2 files changed, 25 insertions(+), 3 deletions(-)
Index: linux-2.6/drivers/pci/pci-acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pci-acpi.c
+++ linux-2.6/drivers/pci/pci-acpi.c
@@ -109,10 +109,32 @@ static bool acpi_pci_can_wakeup(struct p
return handle ? acpi_bus_can_wakeup(handle) : false;
}
+static void acpi_pci_propagate_wakeup_enable(struct pci_bus *bus, bool enable)
+{
+ while (bus->parent) {
+ struct pci_dev *bridge = bus->self;
+ int ret;
+
+ ret = acpi_pm_device_sleep_wake(&bridge->dev, enable);
+ if (!ret || bridge->is_pcie)
+ return;
+ bus = bus->parent;
+ }
+
+ /* We have reached the root bus. */
+ if (bus->bridge)
+ acpi_pm_device_sleep_wake(bus->bridge, enable);
+}
+
static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
{
- return acpi_pci_can_wakeup(dev) ?
- acpi_pm_device_sleep_wake(&dev->dev, enable) : 0;
+ if (acpi_pci_can_wakeup(dev))
+ return acpi_pm_device_sleep_wake(&dev->dev, enable);
+
+ if (!dev->is_pcie)
+ acpi_pci_propagate_wakeup_enable(dev->bus, enable);
+
+ return 0;
}
static struct pci_platform_pm_ops acpi_pci_platform_pm = {
Index: linux-2.6/drivers/acpi/sleep.c
===================================================================
--- linux-2.6.orig/drivers/acpi/sleep.c
+++ linux-2.6/drivers/acpi/sleep.c
@@ -691,7 +691,7 @@ int acpi_pm_device_sleep_wake(struct dev
struct acpi_device *adev;
int error;
- if (!device_may_wakeup(dev))
+ if (!device_can_wakeup(dev))
return -EINVAL;
handle = DEVICE_ACPI_HANDLE(dev);
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
2009-09-04 22:00 ` Rafael J. Wysocki
@ 2009-09-04 22:06 ` Matthew Garrett
2009-09-04 22:21 ` Rafael J. Wysocki
0 siblings, 1 reply; 27+ messages in thread
From: Matthew Garrett @ 2009-09-04 22:06 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: ykzhao, linux-pm@lists.linux-foundation.org,
ACPI Devel Maling List, Henrique de Moraes Holschuh, Jesse Barnes,
Linux PCI
On Sat, Sep 05, 2009 at 12:00:32AM +0200, Rafael J. Wysocki wrote:
> > 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.
--
Matthew Garrett | mjg59@srcf.ucam.org
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support
2009-09-04 22:06 ` Matthew Garrett
@ 2009-09-04 22:21 ` Rafael J. Wysocki
0 siblings, 0 replies; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-04 22:21 UTC (permalink / raw)
To: Matthew Garrett
Cc: ykzhao, linux-pm@lists.linux-foundation.org,
ACPI Devel Maling List, Henrique de Moraes Holschuh, Jesse Barnes,
Linux PCI
On Saturday 05 September 2009, Matthew Garrett wrote:
> On Sat, Sep 05, 2009 at 12:00:32AM +0200, Rafael J. Wysocki wrote:
> > > 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.
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
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 0/5 update] PCI / ACPI PM: Propagate wake-up enable upstream
2009-09-03 22:02 ` [RFC][PATCH 0/4] PCI/ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
` (3 preceding siblings ...)
2009-09-03 22:07 ` [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support Rafael J. Wysocki
@ 2009-09-08 21:11 ` Rafael J. Wysocki
2009-09-08 21:12 ` [PATCH 1/5] PCI PM: Simplify PCI wake-up code Rafael J. Wysocki
` (4 more replies)
4 siblings, 5 replies; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-08 21:11 UTC (permalink / raw)
To: linux-pm
Cc: ACPI Devel Maling List, Jesse Barnes, Henrique de Moraes Holschuh,
Linux PCI, Len Brown, Matthew Garrett, ykzhao
Hi,
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
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 1/5] PCI PM: Simplify PCI wake-up code
2009-09-08 21:11 ` [PATCH 0/5 update] PCI / ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
@ 2009-09-08 21:12 ` Rafael J. Wysocki
2009-09-09 21:20 ` Jesse Barnes
2009-09-08 21:13 ` [PATCH 2/5] PCI / ACPI PM: Rework some debug messages Rafael J. Wysocki
` (3 subsequent siblings)
4 siblings, 1 reply; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-08 21:12 UTC (permalink / raw)
To: linux-pm
Cc: ACPI Devel Maling List, Jesse Barnes, Henrique de Moraes Holschuh,
Linux PCI, Len Brown, Matthew Garrett, ykzhao
From: Rafael J. Wysocki <rjw@sisk.pl>
Rework the PCI wake-up code so that it's easier to read without
changing the functionality.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
drivers/pci/pci-acpi.c | 10 +++++++---
drivers/pci/pci.c | 25 ++++++++++++++-----------
2 files changed, 21 insertions(+), 14 deletions(-)
Index: linux-2.6/drivers/pci/pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pci.c
+++ linux-2.6/drivers/pci/pci.c
@@ -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;
}
/**
Index: linux-2.6/drivers/pci/pci-acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pci-acpi.c
+++ linux-2.6/drivers/pci/pci-acpi.c
@@ -111,12 +111,16 @@ static bool acpi_pci_can_wakeup(struct p
static 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;
}
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 2/5] PCI / ACPI PM: Rework some debug messages
2009-09-08 21:11 ` [PATCH 0/5 update] PCI / ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
2009-09-08 21:12 ` [PATCH 1/5] PCI PM: Simplify PCI wake-up code Rafael J. Wysocki
@ 2009-09-08 21:13 ` Rafael J. Wysocki
2009-09-08 21:14 ` [PATCH 3/5] PCI PM: Introduce device flag wakeup_prepared Rafael J. Wysocki
` (2 subsequent siblings)
4 siblings, 0 replies; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-08 21:13 UTC (permalink / raw)
To: linux-pm
Cc: ACPI Devel Maling List, Jesse Barnes, Henrique de Moraes Holschuh,
Linux PCI, Len Brown, Matthew Garrett, ykzhao
From: Rafael J. Wysocki <rjw@sisk.pl>
Move a debug message from acpi_pci_sleep_wake() to
acpi_pm_device_sleep_wake() and use the standard dev_*() macros
in there.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
drivers/acpi/sleep.c | 10 ++++++++--
drivers/pci/pci-acpi.c | 13 ++-----------
2 files changed, 10 insertions(+), 13 deletions(-)
Index: linux-2.6/drivers/acpi/sleep.c
===================================================================
--- linux-2.6.orig/drivers/acpi/sleep.c
+++ linux-2.6/drivers/acpi/sleep.c
@@ -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
Index: linux-2.6/drivers/pci/pci-acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pci-acpi.c
+++ linux-2.6/drivers/pci/pci-acpi.c
@@ -111,17 +111,8 @@ static bool acpi_pci_can_wakeup(struct p
static 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 = {
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 3/5] PCI PM: Introduce device flag wakeup_prepared
2009-09-08 21:11 ` [PATCH 0/5 update] PCI / ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
2009-09-08 21:12 ` [PATCH 1/5] PCI PM: Simplify PCI wake-up code Rafael J. Wysocki
2009-09-08 21:13 ` [PATCH 2/5] PCI / ACPI PM: Rework some debug messages Rafael J. Wysocki
@ 2009-09-08 21:14 ` Rafael J. Wysocki
2009-09-08 21:15 ` [PATCH 4/5] ACPI PM: Replace wakeup.prepared with reference counter Rafael J. Wysocki
2009-09-08 21:16 ` [PATCH 5/5] PCI / ACPI PM: Propagate wake-up enable for devices w/o ACPI support Rafael J. Wysocki
4 siblings, 0 replies; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-08 21:14 UTC (permalink / raw)
To: linux-pm
Cc: ACPI Devel Maling List, Jesse Barnes, Henrique de Moraes Holschuh,
Linux PCI, Len Brown, Matthew Garrett, ykzhao
From: Rafael J. Wysocki <rjw@sisk.pl>
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.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
drivers/pci/pci.c | 8 ++++++++
include/linux/pci.h | 1 +
2 files changed, 9 insertions(+)
Index: linux-2.6/drivers/pci/pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pci.c
+++ linux-2.6/drivers/pci/pci.c
@@ -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 */
Index: linux-2.6/include/linux/pci.h
===================================================================
--- linux-2.6.orig/include/linux/pci.h
+++ linux-2.6/include/linux/pci.h
@@ -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. */
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 4/5] ACPI PM: Replace wakeup.prepared with reference counter
2009-09-08 21:11 ` [PATCH 0/5 update] PCI / ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
` (2 preceding siblings ...)
2009-09-08 21:14 ` [PATCH 3/5] PCI PM: Introduce device flag wakeup_prepared Rafael J. Wysocki
@ 2009-09-08 21:15 ` Rafael J. Wysocki
2009-09-08 21:16 ` [PATCH 5/5] PCI / ACPI PM: Propagate wake-up enable for devices w/o ACPI support Rafael J. Wysocki
4 siblings, 0 replies; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-08 21:15 UTC (permalink / raw)
To: linux-pm
Cc: ACPI Devel Maling List, Jesse Barnes, Henrique de Moraes Holschuh,
Linux PCI, Len Brown, Matthew Garrett, ykzhao
From: Rafael J. Wysocki <rjw@sisk.pl>
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.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
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(-)
Index: linux-2.6/drivers/acpi/scan.c
===================================================================
--- linux-2.6.orig/drivers/acpi/scan.c
+++ linux-2.6/drivers/acpi/scan.c
@@ -781,6 +781,7 @@ static int acpi_bus_get_wakeup_device_fl
kfree(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
===================================================================
--- linux-2.6.orig/include/acpi/acpi_bus.h
+++ linux-2.6/include/acpi/acpi_bus.h
@@ -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
===================================================================
--- linux-2.6.orig/drivers/acpi/power.c
+++ linux-2.6/drivers/acpi/power.c
@@ -44,6 +44,8 @@
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
+#include "sleep.h"
+
#define _COMPONENT ACPI_POWER_COMPONENT
ACPI_MODULE_NAME("power");
#define ACPI_POWER_CLASS "power_resource"
@@ -361,17 +363,15 @@ 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;
- /*
- * 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;
+ mutex_lock(&acpi_device_lock);
+
+ if (dev->wakeup.prepare_count++)
+ goto out;
/* Open power resource */
for (i = 0; i < dev->wakeup.resources.count; i++) {
@@ -379,7 +379,8 @@ int acpi_enable_wakeup_device_power(stru
if (ret) {
printk(KERN_ERR PREFIX "Transition power state\n");
dev->wakeup.flags.valid = 0;
- return -ENODEV;
+ err = -ENODEV;
+ goto err_out;
}
}
@@ -388,9 +389,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 +407,42 @@ 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);
+
+ if (--dev->wakeup.prepare_count > 0)
+ goto out;
+
/*
- * Do not execute the code below twice in a row without calling
- * acpi_enable_wakeup_device_power() in between for the same device
+ * Executing the code below even if prepare_count is already zero when
+ * the function is called may be useful, for example for initialisation.
*/
- if (!dev->wakeup.flags.prepared)
- return 0;
+ if (dev->wakeup.prepare_count < 0)
+ dev->wakeup.prepare_count = 0;
- dev->wakeup.flags.prepared = 0;
-
- 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
===================================================================
--- linux-2.6.orig/drivers/acpi/wakeup.c
+++ linux-2.6/drivers/acpi/wakeup.c
@@ -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 sleep
if (!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,
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 5/5] PCI / ACPI PM: Propagate wake-up enable for devices w/o ACPI support
2009-09-08 21:11 ` [PATCH 0/5 update] PCI / ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
` (3 preceding siblings ...)
2009-09-08 21:15 ` [PATCH 4/5] ACPI PM: Replace wakeup.prepared with reference counter Rafael J. Wysocki
@ 2009-09-08 21:16 ` Rafael J. Wysocki
4 siblings, 0 replies; 27+ messages in thread
From: Rafael J. Wysocki @ 2009-09-08 21:16 UTC (permalink / raw)
To: linux-pm
Cc: ACPI Devel Maling List, Jesse Barnes, Henrique de Moraes Holschuh,
Linux PCI, Len Brown, Matthew Garrett, ykzhao
From: Rafael J. Wysocki <rjw@sisk.pl>
Some PCI devices (not PCI Express), like PCI add-on cards, can
generate PME#, but they don't have any special platform wake-up
support. For this reason, even if they generate PME# to wake up the
system from a sleep state, wake-up events are not generated by the
platform.
It turns out that, at least on some systems, PCI bridges and the PCI
host bridge have ACPI GPEs associated with them that, if enabled to
generate wake-up events, allow the system to wake up if one of the
add-on devices asserts PME# while the system is in a sleep state.
Following this observation, if a PCI device without direct ACPI
wake-up support is prepared to wake up the system during a transition
into a sleep state (eg. suspend to RAM), try to configure the bridges
on the path from the device to the root bridge to wake-up the system.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
drivers/acpi/sleep.c | 2 +-
drivers/pci/pci-acpi.c | 26 ++++++++++++++++++++++++--
2 files changed, 25 insertions(+), 3 deletions(-)
Index: linux-2.6/drivers/pci/pci-acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pci-acpi.c
+++ linux-2.6/drivers/pci/pci-acpi.c
@@ -109,10 +109,32 @@ static bool acpi_pci_can_wakeup(struct p
return handle ? acpi_bus_can_wakeup(handle) : false;
}
+static void acpi_pci_propagate_wakeup_enable(struct pci_bus *bus, bool enable)
+{
+ while (bus->parent) {
+ struct pci_dev *bridge = bus->self;
+ int ret;
+
+ ret = acpi_pm_device_sleep_wake(&bridge->dev, enable);
+ if (!ret || bridge->is_pcie)
+ return;
+ bus = bus->parent;
+ }
+
+ /* We have reached the root bus. */
+ if (bus->bridge)
+ acpi_pm_device_sleep_wake(bus->bridge, enable);
+}
+
static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
{
- return acpi_pci_can_wakeup(dev) ?
- acpi_pm_device_sleep_wake(&dev->dev, enable) : 0;
+ if (acpi_pci_can_wakeup(dev))
+ return acpi_pm_device_sleep_wake(&dev->dev, enable);
+
+ if (!dev->is_pcie)
+ acpi_pci_propagate_wakeup_enable(dev->bus, enable);
+
+ return 0;
}
static struct pci_platform_pm_ops acpi_pci_platform_pm = {
Index: linux-2.6/drivers/acpi/sleep.c
===================================================================
--- linux-2.6.orig/drivers/acpi/sleep.c
+++ linux-2.6/drivers/acpi/sleep.c
@@ -691,7 +691,7 @@ int acpi_pm_device_sleep_wake(struct dev
struct acpi_device *adev;
int error;
- if (!device_may_wakeup(dev))
+ if (!device_can_wakeup(dev))
return -EINVAL;
handle = DEVICE_ACPI_HANDLE(dev);
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 1/5] PCI PM: Simplify PCI wake-up code
2009-09-08 21:12 ` [PATCH 1/5] PCI PM: Simplify PCI wake-up code Rafael J. Wysocki
@ 2009-09-09 21:20 ` Jesse Barnes
0 siblings, 0 replies; 27+ messages in thread
From: Jesse Barnes @ 2009-09-09 21:20 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: linux-pm, ACPI Devel Maling List, Henrique de Moraes Holschuh,
Linux PCI, Len Brown, Matthew Garrett, ykzhao
On Tue, 8 Sep 2009 23:12:59 +0200
"Rafael J. Wysocki" <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Rework the PCI wake-up code so that it's easier to read without
> changing the functionality.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---
Applied this series to my linux-next branch, thanks. Len, that include
the ACPI portion; hope that's ok.
--
Jesse Barnes, Intel Open Source Technology Center
^ permalink raw reply [flat|nested] 27+ messages in thread
end of thread, other threads:[~2009-09-09 21:20 UTC | newest]
Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-08-29 22:41 [PATCH] ACPI / PM: Allow PCI root bridges to wake up the system Rafael J. Wysocki
2009-08-31 19:41 ` [linux-pm] " Rafael J. Wysocki
2009-08-31 21:24 ` [RFC][PATCH update] " Rafael J. Wysocki
2009-09-01 13:25 ` Matthew Garrett
2009-09-01 19:03 ` Rafael J. Wysocki
2009-09-01 22:41 ` [linux-pm] " Rafael J. Wysocki
2009-09-03 22:02 ` [RFC][PATCH 0/4] PCI/ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
2009-09-03 22:03 ` [RFC][PATCH 1/4] PCI PM: Simplify PCI wake-up code Rafael J. Wysocki
2009-09-03 22:04 ` [RFC][PATCH 2/4] PCI/ACPI PM: Rework some debug messages Rafael J. Wysocki
2009-09-03 22:05 ` [RFC][PATCH 3/4] PCI PM: Introduce device flag wakeup_prepared Rafael J. Wysocki
2009-09-03 22:07 ` [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support Rafael J. Wysocki
2009-09-04 2:13 ` ykzhao
2009-09-04 14:39 ` Rafael J. Wysocki
2009-09-04 14:56 ` Matthew Garrett
2009-09-04 22:00 ` Rafael J. Wysocki
2009-09-04 22:06 ` Matthew Garrett
2009-09-04 22:21 ` Rafael J. Wysocki
2009-09-04 22:03 ` [linux-pm] " Rafael J. Wysocki
2009-09-04 22:05 ` [RFC][PATCH 4/4 replacement] ACPI PM: Replace wakeup.prepared with reference counter Rafael J. Wysocki
2009-09-04 22:06 ` [RFC][PATCH 5] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support Rafael J. Wysocki
2009-09-08 21:11 ` [PATCH 0/5 update] PCI / ACPI PM: Propagate wake-up enable upstream Rafael J. Wysocki
2009-09-08 21:12 ` [PATCH 1/5] PCI PM: Simplify PCI wake-up code Rafael J. Wysocki
2009-09-09 21:20 ` Jesse Barnes
2009-09-08 21:13 ` [PATCH 2/5] PCI / ACPI PM: Rework some debug messages Rafael J. Wysocki
2009-09-08 21:14 ` [PATCH 3/5] PCI PM: Introduce device flag wakeup_prepared Rafael J. Wysocki
2009-09-08 21:15 ` [PATCH 4/5] ACPI PM: Replace wakeup.prepared with reference counter Rafael J. Wysocki
2009-09-08 21:16 ` [PATCH 5/5] PCI / ACPI PM: Propagate wake-up enable for devices w/o ACPI support Rafael J. Wysocki
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).