Re: [RFC PATCH v2 2/8] ACPI: Add interface to register/unregisterdevice to/from power resources

From: Lin Ming
Date: Sun Mar 18 2012 - 21:32:15 EST


On Thu, Mar 1, 2012 at 5:02 PM, Lin Ming <ming.m.lin@xxxxxxxxx> wrote:
> Devices may share same list of power resources in _PR0, for example
>
> Device(Dev0)
> {
>        Name (_PR0, Package (0x01)
>        {
>                P0PR,
>                P1PR
>        })
> }
>
> Device(Dev1)
> {
>        Name (_PR0, Package (0x01)
>        {
>                P0PR,
>                P1PR
>        }
> }
>
> Assume Dev0 and Dev1 were runtime suspended.
> Then Dev0 is resumed first and it goes into D0 state.
> But Dev1 is left in D0_Uninitialised state.
>
> This is wrong. In this case, Dev1 must be resumed too.
>
> In order to hand this case, each power resource maintains a list of
> devices which relies on it.
>
> When power resource is ON, it will check if the devices on its list
> can be resumed. The device can only be resumed when all the power
> resouces of its _PR0 are ON.
>
> Signed-off-by: Lin Ming <ming.m.lin@xxxxxxxxx>

Hi Rafael,

This version addressed your comment at:
https://lkml.org/lkml/2012/2/13/355

Now we support all combinations of device/power resource.

Do you like it?

Thanks,
Lin Ming

> ---
>  drivers/acpi/power.c    |  162 +++++++++++++++++++++++++++++++++++++++++++++++
>  include/acpi/acpi_bus.h |    2 +
>  2 files changed, 164 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
> index 0d681fb..7049a7d 100644
> --- a/drivers/acpi/power.c
> +++ b/drivers/acpi/power.c
> @@ -40,9 +40,11 @@
>  #include <linux/init.h>
>  #include <linux/types.h>
>  #include <linux/slab.h>
> +#include <linux/pm_runtime.h>
>  #include <acpi/acpi_bus.h>
>  #include <acpi/acpi_drivers.h>
>  #include "sleep.h"
> +#include "internal.h"
>
>  #define PREFIX "ACPI: "
>
> @@ -77,6 +79,20 @@ static struct acpi_driver acpi_power_driver = {
>                },
>  };
>
> +/*
> + * A power managed device
> + * A device may rely on multiple power resources.
> + * */
> +struct acpi_power_managed_device {
> +       struct device *dev; /* The physical device */
> +       acpi_handle *handle;
> +};
> +
> +struct acpi_power_resource_device {
> +       struct acpi_power_managed_device *device;
> +       struct acpi_power_resource_device *next;
> +};
> +
>  struct acpi_power_resource {
>        struct acpi_device * device;
>        acpi_bus_id name;
> @@ -84,6 +100,9 @@ struct acpi_power_resource {
>        u32 order;
>        unsigned int ref_count;
>        struct mutex resource_lock;
> +
> +       /* List of devices relying on this power resource */
> +       struct acpi_power_resource_device *devices;
>  };
>
>  static struct list_head acpi_power_resource_list;
> @@ -183,8 +202,26 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
>        return 0;
>  }
>
> +/* Resume the device when all power resources in _PR0 are on */
> +static void acpi_power_on_device(struct acpi_power_managed_device *device)
> +{
> +       struct acpi_device *acpi_dev;
> +       acpi_handle handle = device->handle;
> +       int state;
> +
> +       if (acpi_bus_get_device(handle, &acpi_dev))
> +               return;
> +
> +       if(acpi_power_get_inferred_state(acpi_dev, &state))
> +               return;
> +
> +       if (state == ACPI_STATE_D0 && pm_runtime_suspended(device->dev))
> +               pm_request_resume(device->dev);
> +}
> +
>  static int __acpi_power_on(struct acpi_power_resource *resource)
>  {
> +       struct acpi_power_resource_device *device_list = resource->devices;
>        acpi_status status = AE_OK;
>
>        status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
> @@ -197,6 +234,12 @@ static int __acpi_power_on(struct acpi_power_resource *resource)
>        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n",
>                          resource->name));
>
> +       while (device_list) {
> +               acpi_power_on_device(device_list->device);
> +
> +               device_list = device_list->next;
> +       }
> +
>        return 0;
>  }
>
> @@ -299,6 +342,125 @@ static int acpi_power_on_list(struct acpi_handle_list *list)
>        return result;
>  }
>
> +static void __acpi_power_resource_unregister_device(struct device *dev,
> +               acpi_handle res_handle)
> +{
> +       struct acpi_power_resource *resource = NULL;
> +       struct acpi_power_resource_device *prev, *curr;
> +
> +       if (acpi_power_get_context(res_handle, &resource))
> +               return;
> +
> +       mutex_lock(&resource->resource_lock);
> +       prev = NULL;
> +       curr = resource->devices;
> +       while (curr) {
> +               if (curr->device->dev == dev) {
> +                       if (!prev)
> +                               resource->devices = curr->next;
> +                       else
> +                               prev->next = curr->next;
> +
> +                       kfree(curr);
> +                       break;
> +               }
> +
> +               prev = curr;
> +               curr = curr->next;
> +       }
> +       mutex_unlock(&resource->resource_lock);
> +}
> +
> +/* Unlink dev from all power resources in _PR0 */
> +void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle)
> +{
> +       struct acpi_device *acpi_dev;
> +       struct acpi_handle_list *list;
> +       int i;
> +
> +       if (!dev || !handle)
> +               return;
> +
> +       if (acpi_bus_get_device(handle, &acpi_dev))
> +               return;
> +
> +       list = &acpi_dev->power.states[ACPI_STATE_D0].resources;
> +
> +       for (i = 0; i < list->count; i++)
> +               __acpi_power_resource_unregister_device(dev,
> +                       list->handles[i]);
> +}
> +
> +static int __acpi_power_resource_register_device(
> +       struct acpi_power_managed_device *powered_device, acpi_handle handle)
> +{
> +       struct acpi_power_resource *resource = NULL;
> +       struct acpi_power_resource_device *power_resource_device;
> +       int result;
> +
> +       result = acpi_power_get_context(handle, &resource);
> +       if (result)
> +               return result;
> +
> +       power_resource_device = kzalloc(
> +               sizeof(*power_resource_device), GFP_KERNEL);
> +       if (!power_resource_device)
> +               return -ENOMEM;
> +
> +       power_resource_device->device = powered_device;
> +
> +       mutex_lock(&resource->resource_lock);
> +       power_resource_device->next = resource->devices;
> +       resource->devices = power_resource_device;
> +       mutex_unlock(&resource->resource_lock);
> +
> +       return 0;
> +}
> +
> +/* Link dev to all power resources in _PR0 */
> +int acpi_power_resource_register_device(struct device *dev, acpi_handle handle)
> +{
> +       struct acpi_device *acpi_dev;
> +       struct acpi_handle_list *list;
> +       struct acpi_power_managed_device *powered_device;
> +       int i, ret;
> +
> +       if (!dev || !handle)
> +               return -ENODEV;
> +
> +       ret = acpi_bus_get_device(handle, &acpi_dev);
> +       if (ret)
> +               goto no_power_resource;
> +
> +       if (!acpi_dev->power.flags.power_resources)
> +               goto no_power_resource;
> +
> +       powered_device = kzalloc(sizeof(*powered_device), GFP_KERNEL);
> +       if (!powered_device)
> +               return -ENOMEM;
> +
> +       powered_device->dev = dev;
> +       powered_device->handle = handle;
> +
> +       list = &acpi_dev->power.states[ACPI_STATE_D0].resources;
> +
> +       for (i = 0; i < list->count; i++) {
> +               ret = __acpi_power_resource_register_device(powered_device,
> +                       list->handles[i]);
> +
> +               if (ret) {
> +                       acpi_power_resource_unregister_device(dev, handle);
> +                       break;
> +               }
> +       }
> +
> +       return ret;
> +
> +no_power_resource:
> +       printk(KERN_WARNING PREFIX "Invalid Power Resource to register!");
> +       return -ENODEV;
> +}
> +
>  /**
>  * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in
>  *                          ACPI 3.0) _PSW (Power State Wake)
> diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
> index 6cd5b64..e168fff 100644
> --- a/include/acpi/acpi_bus.h
> +++ b/include/acpi/acpi_bus.h
> @@ -323,6 +323,8 @@ int acpi_bus_set_power(acpi_handle handle, int state);
>  int acpi_bus_update_power(acpi_handle handle, int *state_p);
>  bool acpi_bus_power_manageable(acpi_handle handle);
>  bool acpi_bus_can_wakeup(acpi_handle handle);
> +int acpi_power_resource_register_device(struct device *dev, acpi_handle handle);
> +void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle);
>  #ifdef CONFIG_ACPI_PROC_EVENT
>  int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data);
>  int acpi_bus_generate_proc_event4(const char *class, const char *bid, u8 type, int data);
> --
> 1.7.2.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/