Re: [RFC][PATCH 4/4] PCI/ACPI PM: Propagate wake-up enable for devices w/o ACPI support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 2009-09-04 at 06:07 +0800, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@xxxxxxx>
> 
> 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@xxxxxxx>
> ---
>  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@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux