On Wed, 24 Jun 2020 at 12:33, Daniel Baluta <daniel.baluta@oss.nxp.com> wrote:
From: Daniel Baluta <daniel.baluta@nxp.com>
This patch introduces helpers support for multi PM domains.
API consists of:
1) dev_multi_pm_attach - powers up all PM domains associated with a given device. Because we can attach one PM domain per device, we create virtual devices (children of initial device) and associate PM domains one per virtual device.
2) dev_multi_pm_detach - detaches all virtual devices from PM domains attached with.
Nit pick: I suggest to rename the helpers into dev_pm_domain_attach|detach_multi(), to be more consistent with existing function names. It's a bit long I admit that, but I prefer the consistency.
Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com> --- drivers/base/power/common.c | 93 +++++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 19 ++++++++ 2 files changed, 112 insertions(+)
diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c index bbddb267c2e6..b0a4d0109810 100644 --- a/drivers/base/power/common.c +++ b/drivers/base/power/common.c @@ -228,3 +228,96 @@ void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd) device_pm_check_callbacks(dev); } EXPORT_SYMBOL_GPL(dev_pm_domain_set); + +/** + * dev_multi_pm_attach - power up device associated power domains + * @dev: The device used to lookup the PM domains + * + * Parse device's OF node to find all PM domains specifiers. For each power + * domain found, create a virtual device and associate it with the + * current power domain. + * + * This function should typically be invoked by a driver during the + * probe phase, in the case its device requires power management through + * multiple PM domains. + * + * Returns a pointer to @dev_multi_pm_domain_data if successfully attached PM + * domains, NULL when the device doesn't need a PM domain or when single + * power-domains exists for it, else an ERR_PTR() in case of + * failures. + */ +struct dev_multi_pm_domain_data *dev_multi_pm_attach(struct device *dev) +{ + struct dev_multi_pm_domain_data *mpd, *retp; + int num_domains; + int i; + + num_domains = of_count_phandle_with_args(dev->of_node, "power-domains", + "#power-domain-cells"); + if (num_domains < 2) + return NULL;
dev_pm_domain_attach_* is typically wrapper functions, allowing different types of PM domains to be supported. For example, dev_pm_domain_attach() calls acpi_dev_pm_attach() and genpd_dev_pm_attach(). While dev_pm_domain_attach_by_id() only calls genpd_dev_pm_attach_by_id(), as there's no corresponding interface for the acpi PM domain. The above said, I don't think another layer should be needed here, but there is something missing that makes this consistent with the behaviour of the above mentioned functions. How about adding a genpd OF helper ("of_genpd_num_domains(struct device_node *)"), that deals with the above parsing and returns the number of domains for the device? In this way, if of_genpd_num_domains() returns an error code or zero, it's easier to continue to try with other PM domain providers (if/when that is supported).
+ + mpd = devm_kzalloc(dev, sizeof(*mpd), GFP_KERNEL); + if (!mpd) + return ERR_PTR(-ENOMEM); + + mpd->dev = dev; + mpd->num_domains = num_domains; + + mpd->virt_devs = devm_kmalloc_array(dev, mpd->num_domains, + sizeof(*mpd->virt_devs), + GFP_KERNEL); + if (!mpd->virt_devs) + return ERR_PTR(-ENOMEM); + + mpd->links = devm_kmalloc_array(dev, mpd->num_domains, + sizeof(*mpd->links), GFP_KERNEL); + if (!mpd->links) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < mpd->num_domains; i++) { + mpd->virt_devs[i] = dev_pm_domain_attach_by_id(dev, i); + if (IS_ERR(mpd->virt_devs[i])) { + retp = (struct dev_multi_pm_domain_data *) + mpd->virt_devs[i]; + goto exit_unroll_pm; + } + mpd->links[i] = device_link_add(dev, mpd->virt_devs[i], + DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE);
As a suggestion to be a little bit more flexible, perhaps these bits should be given as an in-parameter instead. Potentially we could then also treat the in-parameter being zero, as that no device link should be added. Although, it's kind of hard to know as the users of this interface aren't really widely known yet.
+ if (!mpd->links[i]) { + retp = ERR_PTR(-ENOMEM); + dev_pm_domain_detach(mpd->virt_devs[i], false); + goto exit_unroll_pm; + } + } + return mpd; + +exit_unroll_pm: + while (--i >= 0) { + device_link_del(mpd->links[i]); + dev_pm_domain_detach(mpd->virt_devs[i], false); + } + + return retp; +} +EXPORT_SYMBOL(dev_multi_pm_attach); +
[...] Kind regards Uffe