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.