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.kroge...@linux.intel.com>
> ---
>  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

Reply via email to