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