Received: by 2002:a05:6a10:9e8c:0:0:0:0 with SMTP id y12csp347200pxx; Thu, 29 Oct 2020 04:15:16 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwbS76Ueptut3Zym8/lITlmcsl7GRSRlr4B84imGkLuFUa0ZqYOgprNG1xg6lHWnE/81aeP X-Received: by 2002:aa7:c815:: with SMTP id a21mr3310620edt.166.1603970116125; Thu, 29 Oct 2020 04:15:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1603970116; cv=none; d=google.com; s=arc-20160816; b=yhPjs4W5B4kkpc1XxaDc6/AQbfA3AtF2dqDS/YNaSeb5LFCodx5FshChy2H29/u2o/ pKbJg0MWVwA9oLgihVrN+OV+T3nsGWM9jlscPoXPBB/OSKaB6iquvPoxttQPUq3kgV11 0lwXkESvX2C/AgJPDA9ksjuUXDUfsYmb7uszGOhYsD4SkdU3k4ftuCcEIKYptntAydRr GRVPwPFFs1++R7qe4nzQ7YWYUg/vYHoperjO7lKz0iyE6Vn0CJ5ktlEMBVNJ+y8T5iy7 12ebx+voSh4OSFD05+UdkOc13AlDjhbGEeX9pHLHm3MINqfKTbqcwOAYxQTsvVTGZCXq ATOg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:user-agent:in-reply-to:content-disposition :mime-version:references:message-id:subject:cc:to:from:date :ironport-sdr:ironport-sdr; bh=iBbxc74x0gvLvMWWi4kyxQCISDp76EZGMvQYVe6EfNQ=; b=NQ1YOA2XDzTSZmoUCRsSDyQjbXyxHxsHyKB2BQxxwV3zPyI8ZiQod5bV8JYgxtdpXR I3UUqClSftpUh3NczxQLiKgaNG9iepVMMo6x2JZLumel8//q2vSxdxfLkuqr/TUFS0GC NA/Zz7AkimflBfMO1d3eygLKP2mS0I53i2weF/JeQIZ15HzKUkiA31EW9wtiUtU0V+DN XlMBciDprdYsgFXwyeJCGO/4dvh28LkP7kGQRwHt3jf48eSrevEqh9Ju985J2JjdAxsQ eba65LAEepkA0qKgEGXlmuhOLtvjC/ddH8BvP8T84vfJWe9wH/77EfdUzff3NjweBzs6 c3EA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id p3si2286392edd.200.2020.10.29.04.14.52; Thu, 29 Oct 2020 04:15:16 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727452AbgJ2LNJ (ORCPT + 99 others); Thu, 29 Oct 2020 07:13:09 -0400 Received: from mga01.intel.com ([192.55.52.88]:57371 "EHLO mga01.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727218AbgJ2LNI (ORCPT ); Thu, 29 Oct 2020 07:13:08 -0400 IronPort-SDR: Qa66GwwtRSKfXXShMiB96xTBQB0EzoCbmrJOO8+aQCUy69CB/OsEQBHh9A+mb1yOF781pFHCDt CJ7QdmGmRMtA== X-IronPort-AV: E=McAfee;i="6000,8403,9788"; a="186199172" X-IronPort-AV: E=Sophos;i="5.77,429,1596524400"; d="scan'208";a="186199172" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga005.jf.intel.com ([10.7.209.41]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 29 Oct 2020 04:13:08 -0700 IronPort-SDR: P2RP8QlbpI7b6/BG83yrK0lDFNVFossBNWMfIv6vcqEdduJRxrnstAgGwUeTsAcc8qGj9KMV+I jMV8B7W3GBQg== X-IronPort-AV: E=Sophos;i="5.77,429,1596524400"; d="scan'208";a="536633371" Received: from paasikivi.fi.intel.com ([10.237.72.42]) by orsmga005-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 29 Oct 2020 04:13:06 -0700 Received: by paasikivi.fi.intel.com (Postfix, from userid 1000) id E3FC920736; Thu, 29 Oct 2020 13:13:03 +0200 (EET) Date: Thu, 29 Oct 2020 13:13:03 +0200 From: Sakari Ailus To: Heikki Krogerus Cc: "Rafael J. Wysocki" , Felipe Balbi , Andy Shevchenko , linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org, linux-acpi@vger.kernel.org Subject: Re: [PATCHv2 1/3] software node: Power management operations for software nodes Message-ID: <20201029111303.GL26150@paasikivi.fi.intel.com> References: <20201029105941.63410-1-heikki.krogerus@linux.intel.com> <20201029105941.63410-2-heikki.krogerus@linux.intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20201029105941.63410-2-heikki.krogerus@linux.intel.com> User-Agent: Mutt/1.10.1 (2018-07-13) Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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 > --- > 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 > #include > +#include > +#include > #include > #include > > @@ -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