Re: [PATCH v2 2/3] ACPI / PM: Introduce concept of a _PR0 dependent device

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

 



On Tue, Jun 18, 2019 at 6:19 PM Mika Westerberg
<mika.westerberg@xxxxxxxxxxxxxxx> wrote:
>
> If there are shared power resources between otherwise unrelated devices
> turning them on causes the other devices sharing them to be powered up
> as well. In case of PCI devices go into D0uninitialized state meaning
> that if they were configured to trigger wake that configuration is lost
> at this point.
>
> For this reason introduce a concept of "_PR0 dependent device" that can
> be added to any ACPI device that has power resources. The dependent
> device will be included in a list of dependent devices for all power
> resources returned by the ACPI device's _PR0 (assuming it has one).
> Whenever a power resource having dependent devices is turned physically
> on (its _ON method is called) we runtime resume all of them to allow
> their driver or in case of PCI the PCI core to re-initialize the device
> and its wake configuration.
>
> This adds two functions that can be used to add and remove these
> dependent devices. Note the dependent device does not necessary need
> share power resources so this functionality can be used to add "software
> dependencies" as well if needed.
>
> Signed-off-by: Mika Westerberg <mika.westerberg@xxxxxxxxxxxxxxx>
> ---
>  drivers/acpi/power.c    | 139 ++++++++++++++++++++++++++++++++++++++++
>  include/acpi/acpi_bus.h |   4 ++
>  2 files changed, 143 insertions(+)
>
> diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
> index a916417b9e70..76d298192940 100644
> --- a/drivers/acpi/power.c
> +++ b/drivers/acpi/power.c
> @@ -42,6 +42,11 @@ ACPI_MODULE_NAME("power");
>  #define ACPI_POWER_RESOURCE_STATE_ON   0x01
>  #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF
>
> +struct acpi_power_dependent_device {
> +       struct device *dev;
> +       struct list_head node;
> +};
> +
>  struct acpi_power_resource {
>         struct acpi_device device;
>         struct list_head list_node;
> @@ -51,6 +56,7 @@ struct acpi_power_resource {
>         unsigned int ref_count;
>         bool wakeup_enabled;
>         struct mutex resource_lock;
> +       struct list_head dependents;
>  };
>
>  struct acpi_power_resource_entry {
> @@ -232,8 +238,125 @@ static int acpi_power_get_list_state(struct list_head *list, int *state)
>         return 0;
>  }
>
> +static int
> +acpi_power_resource_add_dependent(struct acpi_power_resource *resource,
> +                                 struct device *dev)
> +{
> +       struct acpi_power_dependent_device *dep;
> +       int ret = 0;
> +
> +       mutex_lock(&resource->resource_lock);
> +       list_for_each_entry(dep, &resource->dependents, node) {
> +               /* Only add it once */
> +               if (dep->dev == dev)
> +                       goto unlock;
> +       }
> +
> +       dep = kzalloc(sizeof(*dep), GFP_KERNEL);
> +       if (!dep) {
> +               ret = -ENOMEM;
> +               goto unlock;
> +       }
> +
> +       dep->dev = dev;
> +       list_add_tail(&dep->node, &resource->dependents);
> +       dev_dbg(dev, "added power dependency to [%s]\n", resource->name);
> +
> +unlock:
> +       mutex_unlock(&resource->resource_lock);
> +       return ret;
> +}
> +
> +static void
> +acpi_power_resource_remove_dependent(struct acpi_power_resource *resource,
> +                                    struct device *dev)
> +{
> +       struct acpi_power_dependent_device *dep;
> +
> +       mutex_lock(&resource->resource_lock);
> +       list_for_each_entry(dep, &resource->dependents, node) {
> +               if (dep->dev == dev) {
> +                       list_del(&dep->node);
> +                       kfree(dep);
> +                       dev_dbg(dev, "removed power dependency to [%s]\n",
> +                               resource->name);
> +                       break;
> +               }
> +       }
> +       mutex_unlock(&resource->resource_lock);
> +}
> +
> +/**
> + * acpi_device_power_add_dependent - Add dependent device of this ACPI device
> + * @adev: ACPI device pointer
> + * @dev: Dependent device
> + *
> + * If @adev has non-empty _PR0 the @dev is added as dependent device to all
> + * power resources returned by it. This means that whenever these power
> + * resources are turned _ON the dependent devices get runtime resumed. This
> + * is needed for devices such as PCI to allow its driver to re-initialize
> + * it after it went to D0uninitialized.
> + *
> + * If @adev does not have _PR0 this does nothing.
> + *
> + * Returns %0 in case of success and negative errno otherwise.
> + */
> +int acpi_device_power_add_dependent(struct acpi_device *adev,
> +                                   struct device *dev)
> +{
> +       struct acpi_power_resource_entry *entry;
> +       struct list_head *resources;
> +       int ret;
> +
> +       if (!adev->power.flags.power_resources)
> +               return 0;
> +       if (!adev->power.states[ACPI_STATE_D0].flags.valid)
> +               return 0;

The two checks above can be replaced with an
adev->flags.power_manageable one AFAICS (the "valid" flag is always
set for D0 and the list below will be empty if there are no power
resources).

Same for acpi_device_power_remove_dependent(), of course.

Apart from this LGTM.



[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