Re: [PATCHv2 1/3] software node: Power management operations for software nodes

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

 



Moi Heikki,

On Thu, Oct 29, 2020 at 01:59:39PM +0300, Heikki Krogerus wrote:
> The software node specific PM operations make it possible to
> handle most PM related quirks separately in their own
> functions instead of conditionally in the device driver's
> generic PM functions (and in some cases all over the
> driver). The software node specific PM operations will also
> reduce the need to pass platform data in some cases, for
> example from a core MFD driver to the child device drivers,
> as from now on the core MFD driver will be able to implement
> the PM quirks directly for the child devices without the
> need to touch the drivers of those child devices.
> 
> If a software node includes the PM operations, those PM
> operations are always executed separately on top of the
> other PM operations of the device, so the software node will
> never replace any of the "normal" PM operations of the
> device (including the PM domain's operations, class's or
> bus's PM operations, the device drivers own operations, or
> any other).
> 
> Signed-off-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>
> ---
>  drivers/base/power/common.c |   8 +-
>  drivers/base/swnode.c       | 693 +++++++++++++++++++++++++++++++++++-
>  include/linux/property.h    |  10 +
>  3 files changed, 701 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
> index bbddb267c2e69..b64cd4690ac63 100644
> --- a/drivers/base/power/common.c
> +++ b/drivers/base/power/common.c
> @@ -109,8 +109,14 @@ int dev_pm_domain_attach(struct device *dev, bool power_on)
>  	ret = acpi_dev_pm_attach(dev, power_on);
>  	if (!ret)
>  		ret = genpd_dev_pm_attach(dev);
> +	if (ret < 0)
> +		return ret;
>  
> -	return ret < 0 ? ret : 0;
> +	ret = software_node_dev_pm_attach(dev, power_on);
> +	if (ret)
> +		dev_pm_domain_detach(dev, power_on);
> +
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(dev_pm_domain_attach);
>  
> diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
> index 010828fc785bc..595a9c240fede 100644
> --- a/drivers/base/swnode.c
> +++ b/drivers/base/swnode.c
> @@ -8,6 +8,8 @@
>  
>  #include <linux/device.h>
>  #include <linux/kernel.h>
> +#include <linux/pm_domain.h>
> +#include <linux/pm_runtime.h>
>  #include <linux/property.h>
>  #include <linux/slab.h>
>  
> @@ -48,6 +50,19 @@ EXPORT_SYMBOL_GPL(is_software_node);
>  				     struct swnode, fwnode) : NULL;	\
>  	})
>  
> +static inline struct swnode *dev_to_swnode(struct device *dev)
> +{
> +	struct fwnode_handle *fwnode = dev_fwnode(dev);
> +
> +	if (!fwnode)
> +		return NULL;
> +
> +	if (!is_software_node(fwnode))
> +		fwnode = fwnode->secondary;
> +
> +	return to_swnode(fwnode);
> +}
> +
>  static struct swnode *
>  software_node_to_swnode(const struct software_node *node)
>  {
> @@ -344,6 +359,673 @@ void property_entries_free(const struct property_entry *properties)
>  }
>  EXPORT_SYMBOL_GPL(property_entries_free);
>  
> +/* -------------------------------------------------------------------------- */
> +/* Power management operations */
> +
> +/*
> + * The power management operations in software nodes are handled with a power
> + * management domain - a "wrapper" PM domain:
> + *
> + *   When PM operations are supplied as part of the software node, the primary
> + *   PM domain of the device is stored and replaced with a device specific
> + *   software node PM domain. The software node PM domain's PM operations, which
> + *   are implemented below, will then always call the matching PM operation of
> + *   the primary PM domain (which was stored) on top of the software node's own
> + *   operation.
> + *
> + * If the device does not have primary PM domain, the software node PM wrapper
> + * operations below will also call the classes, buses and device type's PM
> + * operations, and of course the device driver's own PM operations if they are
> + * implemented. The priority of those calls follows drivers/base/power/domain.c:
> + *
> + * 1) device type
> + * 2) class
> + * 3) bus
> + * 4) driver
> + *
> + * NOTE. The software node PM operation is always called before the primary
> + * PM domain with resume/on callbacks, and after the primary PM domain with
> + * suspend/off callbacks. This order is used because the software node PM
> + * operations are primarily meant to be used to implement quirks, quirks that
> + * may be needed to power on the device to a point where it is even possible to
> + * execute the primary PM domain's resume/on routines.
> + */
> +
> +#ifdef CONFIG_PM
> +struct swnode_pm_domain {
> +	struct dev_pm_domain pm_domain;
> +	struct dev_pm_domain *primary;
> +};
> +
> +#define to_swnode_pm_domain(d) \
> +		container_of(d, struct swnode_pm_domain, pm_domain)
> +
> +static int software_node_runtime_suspend(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (domain->primary && domain->primary->ops.runtime_suspend)
> +		ret = domain->primary->ops.runtime_suspend(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->runtime_suspend)
> +		ret = dev->type->pm->runtime_suspend(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->runtime_suspend)
> +		ret = dev->class->pm->runtime_suspend(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend)
> +		ret = dev->bus->pm->runtime_suspend(dev);
> +	else
> +		ret = pm_generic_runtime_suspend(dev);
> +
> +	if (ret || !swnode->node->pm->runtime_suspend)
> +		return ret;
> +
> +	return swnode->node->pm->runtime_suspend(dev);
> +}
> +
> +static int software_node_runtime_resume(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (swnode->node->pm->runtime_resume) {
> +		ret = swnode->node->pm->runtime_resume(dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (domain->primary && domain->primary->ops.runtime_resume)
> +		ret = domain->primary->ops.runtime_resume(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->runtime_resume)
> +		ret = dev->type->pm->runtime_resume(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->runtime_resume)
> +		ret = dev->class->pm->runtime_resume(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume)
> +		ret = dev->bus->pm->runtime_resume(dev);
> +	else
> +		ret = pm_generic_runtime_resume(dev);
> +
> +	return ret;
> +}
> +
> +static int software_node_runtime_idle(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret = 0;
> +
> +	if (domain->primary && domain->primary->ops.runtime_idle)
> +		ret = domain->primary->ops.runtime_idle(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->runtime_idle)
> +		ret = dev->type->pm->runtime_idle(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->runtime_idle)
> +		ret = dev->class->pm->runtime_idle(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle)
> +		ret = dev->bus->pm->runtime_idle(dev);
> +	else if (dev->driver && dev->driver->pm && dev->driver->pm->runtime_idle)
> +		ret = dev->driver->pm->runtime_idle(dev);
> +
> +	if (ret || !swnode->node->pm->runtime_idle)
> +		return ret;
> +
> +	return swnode->node->pm->runtime_idle(dev);
> +}

These functions are doing pretty much the same thing but with different
parameters. How about implementing a macro or a few, which would take all
the parameters as arguments and return the function to call? A few variants
may be needed. Individual functions performing different tasks would become
very simple.

> +
> +#ifdef CONFIG_PM_SLEEP
> +static int software_node_prepare(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (domain->primary && domain->primary->ops.prepare)
> +		ret = domain->primary->ops.prepare(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->prepare)
> +		ret = dev->type->pm->prepare(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->prepare)
> +		ret = dev->class->pm->prepare(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->prepare)
> +		ret = dev->bus->pm->prepare(dev);
> +	else
> +		ret = pm_generic_prepare(dev);
> +
> +	if (ret || !swnode->node->pm->prepare)
> +		return ret;
> +
> +	return swnode->node->pm->prepare(dev);
> +}
> +
> +static void software_node_complete(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +
> +	if (swnode->node->pm->complete)
> +		swnode->node->pm->complete(dev);
> +
> +	if (domain->primary && domain->primary->ops.complete)
> +		domain->primary->ops.complete(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->complete)
> +		dev->type->pm->complete(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->complete)
> +		dev->class->pm->complete(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->complete)
> +		dev->bus->pm->complete(dev);
> +	else
> +		pm_generic_complete(dev);
> +}
> +
> +static int software_node_suspend(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (domain->primary && domain->primary->ops.suspend)
> +		ret = domain->primary->ops.suspend(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->suspend)
> +		ret = dev->type->pm->suspend(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->suspend)
> +		ret = dev->class->pm->suspend(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->suspend)
> +		ret = dev->bus->pm->suspend(dev);
> +	else
> +		ret = pm_generic_suspend(dev);
> +
> +	if (ret || !swnode->node->pm->suspend)
> +		return ret;
> +
> +	return swnode->node->pm->suspend(dev);
> +}
> +
> +static int software_node_resume(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (swnode->node->pm->resume) {
> +		ret = swnode->node->pm->resume(dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (domain->primary && domain->primary->ops.resume)
> +		ret = domain->primary->ops.resume(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->resume)
> +		ret = dev->type->pm->resume(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->resume)
> +		ret = dev->class->pm->resume(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->resume)
> +		ret = dev->bus->pm->resume(dev);
> +	else
> +		ret = pm_generic_resume(dev);
> +
> +	return ret;
> +}
> +
> +static int software_node_freeze(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (domain->primary && domain->primary->ops.freeze)
> +		ret = domain->primary->ops.freeze(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->freeze)
> +		ret = dev->type->pm->freeze(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->freeze)
> +		ret = dev->class->pm->freeze(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->freeze)
> +		ret = dev->bus->pm->freeze(dev);
> +	else
> +		ret = pm_generic_freeze(dev);
> +
> +	if (ret || !swnode->node->pm->freeze)
> +		return ret;
> +
> +	return swnode->node->pm->freeze(dev);
> +}
> +
> +static int software_node_thaw(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (swnode->node->pm->thaw) {
> +		ret = swnode->node->pm->thaw(dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (domain->primary && domain->primary->ops.thaw)
> +		ret = domain->primary->ops.thaw(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->thaw)
> +		ret = dev->type->pm->thaw(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->thaw)
> +		ret = dev->class->pm->thaw(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->thaw)
> +		ret = dev->bus->pm->thaw(dev);
> +	else
> +		ret = pm_generic_thaw(dev);
> +
> +	return ret;
> +}
> +
> +static int software_node_poweroff(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (domain->primary && domain->primary->ops.poweroff)
> +		ret = domain->primary->ops.poweroff(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->poweroff)
> +		ret = dev->type->pm->poweroff(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->poweroff)
> +		ret = dev->class->pm->poweroff(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->poweroff)
> +		ret = dev->bus->pm->poweroff(dev);
> +	else
> +		ret = pm_generic_poweroff(dev);
> +
> +	if (ret || !swnode->node->pm->poweroff)
> +		return ret;
> +
> +	return swnode->node->pm->poweroff(dev);
> +}
> +
> +static int software_node_restore(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (swnode->node->pm->restore) {
> +		ret = swnode->node->pm->restore(dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (domain->primary && domain->primary->ops.restore)
> +		ret = domain->primary->ops.restore(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->restore)
> +		ret = dev->type->pm->restore(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->restore)
> +		ret = dev->class->pm->restore(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->restore)
> +		ret = dev->bus->pm->restore(dev);
> +	else
> +		ret = pm_generic_restore(dev);
> +
> +	return ret;
> +}
> +
> +static int software_node_suspend_late(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (domain->primary && domain->primary->ops.suspend_late)
> +		ret = domain->primary->ops.suspend_late(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->suspend_late)
> +		ret = dev->type->pm->suspend_late(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->suspend_late)
> +		ret = dev->class->pm->suspend_late(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->suspend_late)
> +		ret = dev->bus->pm->suspend_late(dev);
> +	else
> +		ret = pm_generic_suspend_late(dev);
> +
> +	if (ret || !swnode->node->pm->suspend_late)
> +		return ret;
> +
> +	return swnode->node->pm->suspend_late(dev);
> +}
> +
> +static int software_node_resume_early(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (swnode->node->pm->resume_early) {
> +		ret = swnode->node->pm->resume_early(dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (domain->primary && domain->primary->ops.resume_early)
> +		ret = domain->primary->ops.resume_early(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->resume_early)
> +		ret = dev->type->pm->resume_early(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->resume_early)
> +		ret = dev->class->pm->resume_early(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->resume_early)
> +		ret = dev->bus->pm->resume_early(dev);
> +	else
> +		pm_generic_resume_early(dev);
> +
> +	return 0;
> +}
> +
> +static int software_node_freeze_late(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (domain->primary && domain->primary->ops.freeze_late)
> +		ret = domain->primary->ops.freeze_late(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->freeze_late)
> +		ret = dev->type->pm->freeze_late(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->freeze_late)
> +		ret = dev->class->pm->freeze_late(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->freeze_late)
> +		ret = dev->bus->pm->freeze_late(dev);
> +	else
> +		ret = pm_generic_freeze_late(dev);
> +
> +	if (ret || !swnode->node->pm->freeze_late)
> +		return ret;
> +
> +	return swnode->node->pm->freeze_late(dev);
> +}
> +
> +static int software_node_thaw_early(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (swnode->node->pm->thaw_early) {
> +		ret = swnode->node->pm->thaw_early(dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (domain->primary && domain->primary->ops.thaw_early)
> +		ret = domain->primary->ops.thaw_early(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->thaw_early)
> +		ret = dev->type->pm->thaw_early(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->thaw_early)
> +		ret = dev->class->pm->thaw_early(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->thaw_early)
> +		ret = dev->bus->pm->thaw_early(dev);
> +	else
> +		ret = pm_generic_thaw_early(dev);
> +
> +	return ret;
> +}
> +
> +static int software_node_poweroff_late(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (domain->primary && domain->primary->ops.poweroff_late)
> +		ret = domain->primary->ops.poweroff_late(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->poweroff_late)
> +		ret = dev->type->pm->poweroff_late(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->poweroff_late)
> +		ret = dev->class->pm->poweroff_late(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->poweroff_late)
> +		ret = dev->bus->pm->poweroff_late(dev);
> +	else
> +		ret = pm_generic_poweroff_late(dev);
> +
> +	if (ret || !swnode->node->pm->poweroff_late)
> +		return ret;
> +
> +	return swnode->node->pm->poweroff(dev);
> +}
> +
> +static int software_node_restore_early(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (swnode->node->pm->restore_early) {
> +		ret = swnode->node->pm->restore_early(dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (domain->primary && domain->primary->ops.restore_early)
> +		ret = domain->primary->ops.restore_early(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->restore_early)
> +		ret = dev->type->pm->restore_early(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->restore_early)
> +		ret = dev->class->pm->restore_early(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->restore_early)
> +		ret = dev->bus->pm->restore_early(dev);
> +	else
> +		ret = pm_generic_restore_early(dev);
> +
> +	return ret;
> +}
> +
> +static int software_node_suspend_noirq(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (domain->primary && domain->primary->ops.suspend_noirq)
> +		ret = domain->primary->ops.suspend_noirq(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->suspend_noirq)
> +		ret = dev->type->pm->suspend_noirq(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->suspend_noirq)
> +		ret = dev->class->pm->suspend_noirq(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->suspend_noirq)
> +		ret = dev->bus->pm->suspend_noirq(dev);
> +	else
> +		ret = pm_generic_suspend_noirq(dev);
> +
> +	if (ret || !swnode->node->pm->suspend_noirq)
> +		return ret;
> +
> +	return swnode->node->pm->suspend_noirq(dev);
> +}
> +
> +static int software_node_resume_noirq(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (swnode->node->pm->resume_noirq) {
> +		ret = swnode->node->pm->resume_noirq(dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (domain->primary && domain->primary->ops.resume_noirq)
> +		ret = domain->primary->ops.resume_noirq(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->resume_noirq)
> +		ret = dev->type->pm->resume_noirq(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->resume_noirq)
> +		ret = dev->class->pm->resume_noirq(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->resume_noirq)
> +		ret = dev->bus->pm->resume_noirq(dev);
> +	else
> +		ret = pm_generic_resume_noirq(dev);
> +
> +	return ret;
> +}
> +
> +static int software_node_freeze_noirq(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (domain->primary && domain->primary->ops.freeze_noirq)
> +		ret = domain->primary->ops.freeze_noirq(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->freeze_noirq)
> +		ret = dev->type->pm->freeze_noirq(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->freeze_noirq)
> +		ret = dev->class->pm->freeze_noirq(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->freeze_noirq)
> +		ret = dev->bus->pm->freeze_noirq(dev);
> +	else
> +		ret = pm_generic_freeze_noirq(dev);
> +
> +	if (ret || !swnode->node->pm->freeze_noirq)
> +		return ret;
> +
> +	return swnode->node->pm->freeze_noirq(dev);
> +}
> +
> +static int software_node_thaw_noirq(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (swnode->node->pm->thaw_noirq) {
> +		ret = swnode->node->pm->thaw_noirq(dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (domain->primary && domain->primary->ops.thaw_noirq)
> +		ret = domain->primary->ops.thaw_noirq(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->thaw_noirq)
> +		ret = dev->type->pm->thaw_noirq(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->thaw_noirq)
> +		ret = dev->class->pm->thaw_noirq(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->thaw_noirq)
> +		ret = dev->bus->pm->thaw_noirq(dev);
> +	else
> +		ret = pm_generic_thaw_noirq(dev);
> +
> +	return ret;
> +}
> +
> +static int software_node_poweroff_noirq(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (domain->primary && domain->primary->ops.poweroff_noirq)
> +		ret = domain->primary->ops.poweroff_noirq(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->poweroff_noirq)
> +		ret = dev->type->pm->poweroff_noirq(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->poweroff_noirq)
> +		ret = dev->class->pm->poweroff_noirq(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->poweroff_noirq)
> +		ret = dev->bus->pm->poweroff_noirq(dev);
> +	else
> +		ret = pm_generic_poweroff_noirq(dev);
> +
> +	if (ret || !swnode->node->pm->poweroff)
> +		return ret;
> +
> +	return swnode->node->pm->poweroff_noirq(dev);
> +}
> +
> +static int software_node_restore_noirq(struct device *dev)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	int ret;
> +
> +	if (swnode->node->pm->restore_noirq) {
> +		ret = swnode->node->pm->restore_noirq(dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (domain->primary && domain->primary->ops.restore_noirq)
> +		ret = domain->primary->ops.restore_noirq(dev);
> +	else if (dev->type && dev->type->pm && dev->type->pm->restore_noirq)
> +		ret = dev->type->pm->restore_noirq(dev);
> +	else if (dev->class && dev->class->pm && dev->class->pm->restore_noirq)
> +		ret = dev->class->pm->restore_noirq(dev);
> +	else if (dev->bus && dev->bus->pm && dev->bus->pm->restore_noirq)
> +		ret = dev->bus->pm->restore_noirq(dev);
> +	else
> +		ret = pm_generic_restore_noirq(dev);
> +
> +	return ret;
> +}
> +#endif /* CONFIG_PM_SLEEP */
> +
> +static const struct dev_pm_ops software_node_pm_ops = {
> +	.runtime_suspend = software_node_runtime_suspend,
> +	.runtime_resume = software_node_runtime_resume,
> +	.runtime_idle = software_node_runtime_idle,
> +#ifdef CONFIG_PM_SLEEP
> +	.prepare = software_node_prepare,
> +	.complete = software_node_complete,
> +	.suspend = software_node_suspend,
> +	.resume = software_node_resume,
> +	.freeze = software_node_freeze,
> +	.thaw = software_node_thaw,
> +	.poweroff = software_node_poweroff,
> +	.restore = software_node_restore,
> +	.suspend_late = software_node_suspend_late,
> +	.resume_early = software_node_resume_early,
> +	.freeze_late = software_node_freeze_late,
> +	.thaw_early = software_node_thaw_early,
> +	.poweroff_late = software_node_poweroff_late,
> +	.restore_early = software_node_restore_early,
> +	.suspend_noirq = software_node_suspend_noirq,
> +	.resume_noirq = software_node_resume_noirq,
> +	.freeze_noirq = software_node_freeze_noirq,
> +	.thaw_noirq = software_node_thaw_noirq,
> +	.poweroff_noirq = software_node_poweroff_noirq,
> +	.restore_noirq = software_node_restore_noirq,
> +#endif /* CONFIG_PM_SLEEP */
> +};
> +
> +static void software_node_dev_pm_detach(struct device *dev, bool power_off)
> +{
> +	struct swnode_pm_domain *domain = to_swnode_pm_domain(dev->pm_domain);
> +
> +	if (domain->primary && domain->primary->detach) {
> +		dev->pm_domain = domain->primary;
> +		domain->primary->detach(dev, power_off);
> +	} else {
> +		dev_pm_domain_set(dev, NULL);
> +	}
> +
> +	kfree(domain);
> +}
> +
> +int software_node_dev_pm_attach(struct device *dev, bool power_on)
> +{
> +	struct swnode *swnode = dev_to_swnode(dev);
> +	struct swnode_pm_domain *domain;
> +
> +	if (!swnode || !swnode->node->pm)
> +		return 0;
> +
> +	domain = kzalloc(sizeof(*domain), GFP_KERNEL);
> +	if (!domain)
> +		return -ENOMEM;
> +
> +	if (dev->pm_domain)
> +		domain->pm_domain = *dev->pm_domain;
> +
> +	domain->primary = dev->pm_domain;
> +	domain->pm_domain.ops = software_node_pm_ops;
> +	domain->pm_domain.detach = software_node_dev_pm_detach;
> +
> +	dev_pm_domain_set(dev, &domain->pm_domain);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(software_node_dev_pm_attach);
> +#endif /* CONFIG_PM */
> +
>  /* -------------------------------------------------------------------------- */
>  /* fwnode operations */
>  
> @@ -845,20 +1527,13 @@ EXPORT_SYMBOL_GPL(fwnode_remove_software_node);
>  
>  int software_node_notify(struct device *dev, unsigned long action)
>  {
> -	struct fwnode_handle *fwnode = dev_fwnode(dev);
>  	struct swnode *swnode;
>  	int ret;
>  
> -	if (!fwnode)
> -		return 0;
> -
> -	if (!is_software_node(fwnode))
> -		fwnode = fwnode->secondary;
> -	if (!is_software_node(fwnode))
> +	swnode = dev_to_swnode(dev);
> +	if (!swnode)
>  		return 0;
>  
> -	swnode = to_swnode(fwnode);
> -
>  	switch (action) {
>  	case KOBJ_ADD:
>  		ret = sysfs_create_link(&dev->kobj, &swnode->kobj,
> diff --git a/include/linux/property.h b/include/linux/property.h
> index 2d4542629d80b..33b25c8bd4052 100644
> --- a/include/linux/property.h
> +++ b/include/linux/property.h
> @@ -453,11 +453,13 @@ static inline void *device_connection_find_match(struct device *dev,
>   * @name: Name of the software node
>   * @parent: Parent of the software node
>   * @properties: Array of device properties
> + * @pm: Power management operations
>   */
>  struct software_node {
>  	const char *name;
>  	const struct software_node *parent;
>  	const struct property_entry *properties;
> +	const struct dev_pm_ops *pm;
>  };
>  
>  bool is_software_node(const struct fwnode_handle *fwnode);
> @@ -479,6 +481,14 @@ int software_node_register(const struct software_node *node);
>  void software_node_unregister(const struct software_node *node);
>  
>  int software_node_notify(struct device *dev, unsigned long action);
> +#ifdef CONFIG_PM
> +int software_node_dev_pm_attach(struct device *dev, bool power_on);
> +#else
> +static inline int software_node_dev_pm_attach(struct device *dev, bool power_on)
> +{
> +	return 0;
> +}
> +#endif /* CONFIG_PM */
>  
>  struct fwnode_handle *
>  fwnode_create_software_node(const struct property_entry *properties,

-- 
Terveisin,

Sakari Ailus



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux