Re: [PATCH v3 1/2] PM / domains: Introduce multi PM domains helpers

2020-07-07 Thread Ulf Hansson
On Wed, 24 Jun 2020 at 12:33, Daniel Baluta  wrote:
>
> From: Daniel Baluta 
>
> 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 
> ---
>  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]) {
> +   

[PATCH v3 1/2] PM / domains: Introduce multi PM domains helpers

2020-06-24 Thread Daniel Baluta
From: Daniel Baluta 

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.

Signed-off-by: Daniel Baluta 
---
 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;
+
+   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);
+   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);
+
+/**
+ * dev_multi_pm_detach - Detach a device from its PM domains.
+ * Each multi power domain is attached to a virtual children device
+ *
+ * @mpd: multi power domains data, contains the association between
+ * virtul device and PM domain
+ */
+void dev_multi_pm_detach(struct dev_multi_pm_domain_data *mpd)
+{
+   int i;
+
+   for (i = 0; i < mpd->num_domains; i++) {
+   device_link_del(mpd->links[i]);
+   dev_pm_domain_detach(mpd->virt_devs[i], false);
+   }
+}
+EXPORT_SYMBOL(dev_multi_pm_detach);
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 9ec78ee53652..af1107731044 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -183,6 +183,13 @@ struct generic_pm_domain_data {
void *data;
 };
 
+struct dev_multi_pm_domain_data {
+   struct device *dev; /* parent device */
+   struct device **virt_devs; /* virtual children links */
+   struct device_link **links; /*  links parent <-> virtual children */
+   int num_domains;
+};
+
 #ifdef CONFIG_PM_GENERIC_DOMAINS
 static inline struct generic_pm_domain_data *to_gpd_data(struct pm_domain_data 
*pdd)
 {
@@