[PATCH 2/3] pmdomain: core: add support for power-domains-child-ids

Dhruva Gole d-gole at ti.com
Fri Mar 13 04:55:24 PDT 2026


Hi Kevin,

On Mar 10, 2026 at 17:19:24 -0700, Kevin Hilman (TI) wrote:
> Currently, PM domains can only support hierarchy for simple
> providers (e.g. ones with #power-domain-cells = 0).
> 
> Add support for oncell providers as well by adding a new property
> `power-domains-child-ids` to describe the parent/child relationship.
> 
> For example, an SCMI PM domain provider has multiple domains, each of
> which might be a child of diffeent parent domains. In this example,

s/diffeent/different

> the parent domains are MAIN_PD and WKUP_PD:
> 
>     scmi_pds: protocol at 11 {
>         reg = <0x11>;
>         #power-domain-cells = <1>;
>         power-domains = <&MAIN_PD>, <&WKUP_PD>;
>         power-domains-child-ids = <15>, <19>;
>     };
> 
> With this example using the new property, SCMI PM domain 15 becomes a
> child domain of MAIN_PD, and SCMI domain 19 becomes a child domain of
> WKUP_PD.
> 
> To support this feature, add two new core functions
> 
> - of_genpd_add_child_ids()
> - of_genpd_remove_child_ids()
> 
> which can be called by pmdomain providers to add/remove child domains
> if they support the new property power-domains-child-ids.
> 
> Signed-off-by: Kevin Hilman (TI) <khilman at baylibre.com>
> ---

I've tested multiple possibilities with this series on the TI AM62L,
Tested-by: Dhruva Gole <d-gole at ti.com>

I tried having more parents than child nodes. That failed.
Tried less parents, more child nodes, failed as well.
< All as expected >

Tried the proper thing , worked :) (attached a short log of working case)

8<-----------------------
root at am62lxx-evm:~# cat /sys/kernel/debug/pm_genpd/power-controller-cluster/sub_domains
GPMC0
ELM0
root at am62lxx-evm:~# cat /sys/kernel/debug/pm_genpd/power-controller-main/sub_domains
power-controller-cluster
WKUP_GTC0
MSRAM_96K0
MCSPI0
root at am62lxx-evm:~# uname -a
Linux am62lxx-evm 7.0.0-rc3-next-20260312-00003-g9556feac2532-dirty #2 SMP PREEMPT Fri Mar 13 16:13:51 IST 2026 aarch64 GNU/Linux
---------------------->8

changes in DT:

8<------------------------------------------
+&scmi_pds {
+       power-domains = <&MAIN_PD>, <&MAIN_PD>, <&CLUSTER_PD>,<&CLUSTER_PD>, <&CLUSTER_PD>;
+       power-domains-child-ids = <58>, <63>, <72>, <37>, <25>;
+};
+
+&psci {
+       CLUSTER_PD: power-controller-cluster {
+               #power-domain-cells = <0>;
+               power-domains = <&MAIN_PD>;
+       };
+
+       MAIN_PD: power-controller-main {
+               #power-domain-cells = <0>;
+       };
 };
--------------------------------------------->8

>  drivers/pmdomain/core.c   | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/pm_domain.h |  16 ++++++++++++++++
>  2 files changed, 185 insertions(+)
> 
> diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
> index 61c2277c9ce3..acb45dd540b7 100644
> --- a/drivers/pmdomain/core.c
> +++ b/drivers/pmdomain/core.c
> @@ -2909,6 +2909,175 @@ static struct generic_pm_domain *genpd_get_from_provider(
>  	return genpd;
>  }
>  
> +/**
> + * of_genpd_add_child_ids() - Parse power-domains-child-ids property
> + * @np: Device node pointer associated with the PM domain provider.
> + * @data: Pointer to the onecell data associated with the PM domain provider.
> + *
> + * Parse the power-domains and power-domains-child-ids properties to establish
> + * parent-child relationships for PM domains. The power-domains property lists
> + * parent domains, and power-domains-child-ids lists which child domain IDs
> + * should be associated with each parent.
> + *
> + * Returns 0 on success, -ENOENT if properties don't exist, or negative error code.
> + */
> +int of_genpd_add_child_ids(struct device_node *np,
> +			   struct genpd_onecell_data *data)
> +{
> +	struct of_phandle_args parent_args;
> +	struct generic_pm_domain *parent_genpd, *child_genpd;
> +	struct of_phandle_iterator it;
> +	const struct property *prop;
> +	const __be32 *item;
> +	u32 child_id;
> +	int ret;
> +
> +	/* Check if both properties exist */
> +	if (of_count_phandle_with_args(np, "power-domains", "#power-domain-cells") <= 0)
> +		return -ENOENT;
> +
> +	prop = of_find_property(np, "power-domains-child-ids", NULL);
> +	if (!prop)
> +		return -ENOENT;
> +
> +	item = of_prop_next_u32(prop, NULL, &child_id);
> +
> +	/* Iterate over power-domains phandles and power-domains-child-ids in lockstep */
> +	of_for_each_phandle(&it, ret, np, "power-domains", "#power-domain-cells", 0) {
> +		if (!item) {
> +			pr_err("power-domains-child-ids shorter than power-domains for %pOF\n", np);
> +			ret = -EINVAL;
> +			goto err_put_node;
> +		}
> +
> +		/*
> +		 * Fill parent_args from the iterator. it.node is released by
> +		 * the next of_phandle_iterator_next() call at the top of the
> +		 * loop, or by the of_node_put() on the error path below.
> +		 */
> +		parent_args.np = it.node;
> +		parent_args.args_count = of_phandle_iterator_args(&it, parent_args.args,
> +								  MAX_PHANDLE_ARGS);
> +
> +		/* Get the parent domain */
> +		parent_genpd = genpd_get_from_provider(&parent_args);
> +		if (IS_ERR(parent_genpd)) {
> +			pr_err("Failed to get parent domain for %pOF: %ld\n",
> +			       np, PTR_ERR(parent_genpd));
> +			ret = PTR_ERR(parent_genpd);
> +			goto err_put_node;
> +		}
> +
> +		/* Validate child ID is within bounds */
> +		if (child_id >= data->num_domains) {
> +			pr_err("Child ID %u out of bounds (max %u) for %pOF\n",
> +			       child_id, data->num_domains - 1, np);
> +			ret = -EINVAL;
> +			goto err_put_node;
> +		}
> +
> +		/* Get the child domain */
> +		child_genpd = data->domains[child_id];
> +		if (!child_genpd) {
> +			pr_err("Child domain %u is NULL for %pOF\n", child_id, np);
> +			ret = -EINVAL;
> +			goto err_put_node;
> +		}
> +
> +		/* Establish parent-child relationship */
> +		ret = genpd_add_subdomain(parent_genpd, child_genpd);
> +		if (ret) {
> +			pr_err("Failed to add child domain %u to parent in %pOF: %d\n",
> +			       child_id, np, ret);
> +			goto err_put_node;
> +		}
> +
> +		pr_debug("Added child domain %u (%s) to parent %s for %pOF\n",
> +			 child_id, child_genpd->name, parent_genpd->name, np);
> +
> +		item = of_prop_next_u32(prop, item, &child_id);
> +	}
> +
> +	/* of_for_each_phandle returns -ENOENT at natural end-of-list */
> +	if (ret && ret != -ENOENT)
> +		return ret;
> +
> +	/* All power-domains phandles were consumed; check for trailing child IDs */
> +	if (item) {
> +		pr_err("power-domains-child-ids longer than power-domains for %pOF\n", np);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +
> +err_put_node:
> +	of_node_put(it.node);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(of_genpd_add_child_ids);
> +
> +/**
> + * of_genpd_remove_child_ids() - Remove parent-child PM domain relationships
> + * @np: Device node pointer associated with the PM domain provider.
> + * @data: Pointer to the onecell data associated with the PM domain provider.
> + *
> + * Reverses the effect of of_genpd_add_child_ids() by parsing the same
> + * power-domains and power-domains-child-ids properties and calling
> + * pm_genpd_remove_subdomain() for each established relationship.
> + *
> + * Returns 0 on success, -ENOENT if properties don't exist, or negative error
> + * code on failure.
> + */
> +int of_genpd_remove_child_ids(struct device_node *np,
> +			   struct genpd_onecell_data *data)
> +{
> +	struct of_phandle_args parent_args;
> +	struct generic_pm_domain *parent_genpd, *child_genpd;
> +	struct of_phandle_iterator it;
> +	const struct property *prop;
> +	const __be32 *item;
> +	u32 child_id;
> +	int ret;
> +
> +	/* Check if both properties exist */
> +	if (of_count_phandle_with_args(np, "power-domains", "#power-domain-cells") <= 0)
> +		return -ENOENT;
> +
> +	prop = of_find_property(np, "power-domains-child-ids", NULL);
> +	if (!prop)
> +		return -ENOENT;
> +
> +	item = of_prop_next_u32(prop, NULL, &child_id);
> +
> +	of_for_each_phandle(&it, ret, np, "power-domains", "#power-domain-cells", 0) {
> +		if (!item)
> +			break;
> +
> +		parent_args.np = it.node;
> +		parent_args.args_count = of_phandle_iterator_args(&it, parent_args.args,
> +								  MAX_PHANDLE_ARGS);
> +
> +		if (child_id >= data->num_domains || !data->domains[child_id]) {
> +			item = of_prop_next_u32(prop, item, &child_id);
> +			continue;
> +		}
> +
> +		parent_genpd = genpd_get_from_provider(&parent_args);
> +		if (IS_ERR(parent_genpd)) {
> +			item = of_prop_next_u32(prop, item, &child_id);
> +			continue;
> +		}
> +
> +		child_genpd = data->domains[child_id];
> +		pm_genpd_remove_subdomain(parent_genpd, child_genpd);
> +
> +		item = of_prop_next_u32(prop, item, &child_id);
> +	}
> +
> +	return (ret == -ENOENT) ? 0 : ret;
> +}
> +EXPORT_SYMBOL_GPL(of_genpd_remove_child_ids);
> +
>  /**
>   * of_genpd_add_device() - Add a device to an I/O PM domain
>   * @genpdspec: OF phandle args to use for look-up PM domain
> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
> index f67a2cb7d781..b44615d79af6 100644
> --- a/include/linux/pm_domain.h
> +++ b/include/linux/pm_domain.h
> @@ -465,6 +465,10 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np);
>  int of_genpd_parse_idle_states(struct device_node *dn,
>  			       struct genpd_power_state **states, int *n);
>  void of_genpd_sync_state(struct device_node *np);
> +int of_genpd_add_child_ids(struct device_node *np,
> +			   struct genpd_onecell_data *data);
> +int of_genpd_remove_child_ids(struct device_node *np,
> +			      struct genpd_onecell_data *data);
>  
>  int genpd_dev_pm_attach(struct device *dev);
>  struct device *genpd_dev_pm_attach_by_id(struct device *dev,
> @@ -534,6 +538,18 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
>  {
>  	return ERR_PTR(-EOPNOTSUPP);
>  }
> +
> +static inline int of_genpd_add_child_ids(struct device_node *np,
> +					 struct genpd_onecell_data *data)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline int of_genpd_remove_child_ids(struct device_node *np,
> +					    struct genpd_onecell_data *data)
> +{
> +	return -EOPNOTSUPP;
> +}
>  #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
>  
>  #ifdef CONFIG_PM
> 
> -- 
> 2.51.0
> 
> 

-- 
Best regards,
Dhruva Gole
Texas Instruments Incorporated



More information about the linux-arm-kernel mailing list