public inbox for linux-arm-kernel@lists.infradead.org
 help / color / mirror / Atom feed
* [PATCH 0/3] pmdomain: core: add support for domain hierarchies in DT
@ 2026-03-11  0:19 Kevin Hilman (TI)
  2026-03-11  0:19 ` [PATCH 1/3] dt-bindings: power: Add power-domains-child-ids property Kevin Hilman (TI)
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Kevin Hilman (TI) @ 2026-03-11  0:19 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Geert Uytterhoeven, linux-pm, devicetree, linux-kernel, arm-scmi,
	linux-arm-kernel

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.

This idea was previously discussed on the arm-scmi mailing list[1]
where this approach was proposed by Ulf, and then an initial RFC[2]
implementation was made.  From there, it was suggested by Rob[3] to
use a nexus node map instead, which led to several more versions
attempting to implement that, culminating in v5[4], where Rob and
Geert then had second thoughts about the power-domain-map approach.

Therefore, I've gone back to the approach in the initial RFC[2] to use
the child-ids approach.

Changes compared to initial RFC[2]
- dropped RFC
- rewrote the parse/add function to use iterators/helpers from of.h
- add a remove function for cleanup
- use child domain language instead of subdomain

[1] https://lore.kernel.org/arm-scmi/CAPDyKFo_P129sVirHHYjOQT+QUmpymcRJme9obzKJeRgO7B-1A@mail.gmail.com/
[2] https://lore.kernel.org/all/20250528-pmdomain-hierarchy-onecell-v1-1-851780700c68@baylibre.com/
[3] https://lore.kernel.org/all/20250528203532.GA704342-robh@kernel.org/
[4] https://lore.kernel.org/r/20260122-pmdomain-hierarchy-onecell-v5-0-76855ec856bd@baylibre.com

Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
---
Kevin Hilman (TI) (3):
      dt-bindings: power: Add power-domains-child-ids property
      pmdomain: core: add support for power-domains-child-ids
      pmdomain: arm_scmi: add support for domain hierarchies

 Documentation/devicetree/bindings/power/power-domain.yaml |  35 ++++++++++++++++++++++++++++++++++
 drivers/pmdomain/arm/scmi_pm_domain.c                     |  14 +++++++++++++-
 drivers/pmdomain/core.c                                   | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm_domain.h                                 |  16 ++++++++++++++++
 4 files changed, 233 insertions(+), 1 deletion(-)
---
base-commit: f7b88edb52c8dd01b7e576390d658ae6eef0e134
change-id: 20260310-topic-lpm-pmdomain-child-ids-e3d57ae57040

Best regards,
--  
Kevin Hilman (TI) <khilman@baylibre.com>



^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/3] dt-bindings: power: Add power-domains-child-ids property
  2026-03-11  0:19 [PATCH 0/3] pmdomain: core: add support for domain hierarchies in DT Kevin Hilman (TI)
@ 2026-03-11  0:19 ` Kevin Hilman (TI)
  2026-03-11  1:38   ` Rob Herring (Arm)
  2026-03-24 23:25   ` Rob Herring
  2026-03-11  0:19 ` [PATCH 2/3] pmdomain: core: add support for power-domains-child-ids Kevin Hilman (TI)
  2026-03-11  0:19 ` [PATCH 3/3] pmdomain: arm_scmi: add support for domain hierarchies Kevin Hilman (TI)
  2 siblings, 2 replies; 10+ messages in thread
From: Kevin Hilman (TI) @ 2026-03-11  0:19 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Geert Uytterhoeven, linux-pm, devicetree, linux-kernel, arm-scmi,
	linux-arm-kernel

Add binding documentation for the new power-domains-child-ids property,
which works in conjunction with the existing power-domains property to
establish parent-child relationships between a multi-domain power domain
provider and external parent domains.

Each element in the uint32 array identifies the child domain
ID (index) within the provider that should be made a child domain of
the corresponding phandle entry in power-domains. The two arrays must
have the same number of elements.

Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
---
 Documentation/devicetree/bindings/power/power-domain.yaml | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/Documentation/devicetree/bindings/power/power-domain.yaml b/Documentation/devicetree/bindings/power/power-domain.yaml
index b1147dbf2e73..a3d2af124d37 100644
--- a/Documentation/devicetree/bindings/power/power-domain.yaml
+++ b/Documentation/devicetree/bindings/power/power-domain.yaml
@@ -68,6 +68,21 @@ properties:
       by the given provider should be subdomains of the domain specified
       by this binding.
 
+  power-domains-child-ids:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    description:
+      An array of child domain IDs that correspond to the power-domains
+      property. This property is only applicable to power domain providers
+      with "#power-domain-cells" > 0 (i.e., providers that supply multiple
+      power domains). It specifies which of the provider's child domains
+      should be associated with each parent domain listed in the power-domains
+      property. The number of elements in this array must match the number of
+      phandles in the power-domains property. Each element specifies the child
+      domain ID (index) that should be made a child domain of the corresponding
+      parent domain. This enables hierarchical power domain structures where
+      different child domains from the same provider can have different
+      parent domains.
+
 required:
   - "#power-domain-cells"
 
@@ -133,3 +148,23 @@ examples:
             min-residency-us = <7000>;
         };
     };
+
+  - |
+    // Example: SCMI domain 15 -> MAIN_PD, SCMI domain 19 -> WKUP_PD
+    MAIN_PD: power-controller-main {
+        compatible = "foo,power-controller";
+        #power-domain-cells = <0>;
+    };
+
+    WKUP_PD: power-controller-wkup {
+        compatible = "foo,power-controller";
+        #power-domain-cells = <0>;
+    };
+
+    scmi_pds: protocol@11 {
+        compatible = "foo,power-controller";
+        reg = <0x11>;
+        #power-domain-cells = <1>;
+        power-domains = <&MAIN_PD>, <&WKUP_PD>;
+        power-domains-child-ids = <15>, <19>;
+    };

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/3] pmdomain: core: add support for power-domains-child-ids
  2026-03-11  0:19 [PATCH 0/3] pmdomain: core: add support for domain hierarchies in DT Kevin Hilman (TI)
  2026-03-11  0:19 ` [PATCH 1/3] dt-bindings: power: Add power-domains-child-ids property Kevin Hilman (TI)
@ 2026-03-11  0:19 ` Kevin Hilman (TI)
  2026-03-13 11:55   ` Dhruva Gole
  2026-03-25 10:22   ` Ulf Hansson
  2026-03-11  0:19 ` [PATCH 3/3] pmdomain: arm_scmi: add support for domain hierarchies Kevin Hilman (TI)
  2 siblings, 2 replies; 10+ messages in thread
From: Kevin Hilman (TI) @ 2026-03-11  0:19 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Geert Uytterhoeven, linux-pm, devicetree, linux-kernel, arm-scmi,
	linux-arm-kernel

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,
the parent domains are MAIN_PD and WKUP_PD:

    scmi_pds: protocol@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@baylibre.com>
---
 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



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/3] pmdomain: arm_scmi: add support for domain hierarchies
  2026-03-11  0:19 [PATCH 0/3] pmdomain: core: add support for domain hierarchies in DT Kevin Hilman (TI)
  2026-03-11  0:19 ` [PATCH 1/3] dt-bindings: power: Add power-domains-child-ids property Kevin Hilman (TI)
  2026-03-11  0:19 ` [PATCH 2/3] pmdomain: core: add support for power-domains-child-ids Kevin Hilman (TI)
@ 2026-03-11  0:19 ` Kevin Hilman (TI)
  2026-03-13 12:07   ` Dhruva Gole
  2 siblings, 1 reply; 10+ messages in thread
From: Kevin Hilman (TI) @ 2026-03-11  0:19 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Geert Uytterhoeven, linux-pm, devicetree, linux-kernel, arm-scmi,
	linux-arm-kernel

After primary SCMI pmdomain is created, use new of_genpd helper which
checks for child domain mappings defined in power-domains-child-ids.

Also remove any child domain mappings when SCMI domain is removed.

Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
---
 drivers/pmdomain/arm/scmi_pm_domain.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/pmdomain/arm/scmi_pm_domain.c b/drivers/pmdomain/arm/scmi_pm_domain.c
index b5e2ffd5ea64..9d8faef44aa9 100644
--- a/drivers/pmdomain/arm/scmi_pm_domain.c
+++ b/drivers/pmdomain/arm/scmi_pm_domain.c
@@ -114,6 +114,14 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
 
 	dev_set_drvdata(dev, scmi_pd_data);
 
+	/*
+	 * Parse (optional) power-domains-child-ids property to
+	 * establish parent-child relationships
+	 */
+	ret = of_genpd_add_child_ids(np, scmi_pd_data);
+	if (ret < 0 && ret != -ENOENT)
+		pr_err("Failed to parse power-domains-child-ids for %pOF: %d\n", np, ret);
+
 	return 0;
 err_rm_genpds:
 	for (i = num_domains - 1; i >= 0; i--)
@@ -129,9 +137,13 @@ static void scmi_pm_domain_remove(struct scmi_device *sdev)
 	struct device *dev = &sdev->dev;
 	struct device_node *np = dev->of_node;
 
+	scmi_pd_data = dev_get_drvdata(dev);
+
+	/* Remove any parent-child relationships established at probe time */
+	of_genpd_remove_child_ids(np, scmi_pd_data);
+
 	of_genpd_del_provider(np);
 
-	scmi_pd_data = dev_get_drvdata(dev);
 	for (i = 0; i < scmi_pd_data->num_domains; i++) {
 		if (!scmi_pd_data->domains[i])
 			continue;

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/3] dt-bindings: power: Add power-domains-child-ids property
  2026-03-11  0:19 ` [PATCH 1/3] dt-bindings: power: Add power-domains-child-ids property Kevin Hilman (TI)
@ 2026-03-11  1:38   ` Rob Herring (Arm)
  2026-03-11 21:34     ` Kevin Hilman
  2026-03-24 23:25   ` Rob Herring
  1 sibling, 1 reply; 10+ messages in thread
From: Rob Herring (Arm) @ 2026-03-11  1:38 UTC (permalink / raw)
  To: Kevin Hilman (TI)
  Cc: linux-pm, Ulf Hansson, Geert Uytterhoeven, linux-kernel, arm-scmi,
	linux-arm-kernel, devicetree


On Tue, 10 Mar 2026 17:19:23 -0700, Kevin Hilman (TI) wrote:
> Add binding documentation for the new power-domains-child-ids property,
> which works in conjunction with the existing power-domains property to
> establish parent-child relationships between a multi-domain power domain
> provider and external parent domains.
> 
> Each element in the uint32 array identifies the child domain
> ID (index) within the provider that should be made a child domain of
> the corresponding phandle entry in power-domains. The two arrays must
> have the same number of elements.
> 
> Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
> ---
>  Documentation/devicetree/bindings/power/power-domain.yaml | 35 +++++++++++++++++++++++++++++++++++
>  1 file changed, 35 insertions(+)
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Documentation/devicetree/bindings/power/power-domain.example.dts:136.13-26: Warning (reg_format): /example-3/protocol@11:reg: property has invalid length (4 bytes) (#address-cells == 1, #size-cells == 1)
Documentation/devicetree/bindings/power/power-domain.example.dtb: Warning (pci_device_reg): Failed prerequisite 'reg_format'
Documentation/devicetree/bindings/power/power-domain.example.dtb: Warning (pci_device_bus_num): Failed prerequisite 'reg_format'
Documentation/devicetree/bindings/power/power-domain.example.dtb: Warning (simple_bus_reg): Failed prerequisite 'reg_format'
Documentation/devicetree/bindings/power/power-domain.example.dtb: Warning (i2c_bus_reg): Failed prerequisite 'reg_format'
Documentation/devicetree/bindings/power/power-domain.example.dtb: Warning (spi_bus_reg): Failed prerequisite 'reg_format'

doc reference errors (make refcheckdocs):

See https://patchwork.kernel.org/project/devicetree/patch/20260310-topic-lpm-pmdomain-child-ids-v1-1-5361687a18ff@baylibre.com

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/3] dt-bindings: power: Add power-domains-child-ids property
  2026-03-11  1:38   ` Rob Herring (Arm)
@ 2026-03-11 21:34     ` Kevin Hilman
  0 siblings, 0 replies; 10+ messages in thread
From: Kevin Hilman @ 2026-03-11 21:34 UTC (permalink / raw)
  To: Rob Herring (Arm)
  Cc: linux-pm, Ulf Hansson, Geert Uytterhoeven, linux-kernel, arm-scmi,
	linux-arm-kernel, devicetree

"Rob Herring (Arm)" <robh@kernel.org> writes:

> On Tue, 10 Mar 2026 17:19:23 -0700, Kevin Hilman (TI) wrote:
>> Add binding documentation for the new power-domains-child-ids property,
>> which works in conjunction with the existing power-domains property to
>> establish parent-child relationships between a multi-domain power domain
>> provider and external parent domains.
>> 
>> Each element in the uint32 array identifies the child domain
>> ID (index) within the provider that should be made a child domain of
>> the corresponding phandle entry in power-domains. The two arrays must
>> have the same number of elements.
>> 
>> Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
>> ---
>>  Documentation/devicetree/bindings/power/power-domain.yaml | 35 +++++++++++++++++++++++++++++++++++
>>  1 file changed, 35 insertions(+)
>> 
>
> My bot found errors running 'make dt_binding_check' on your patch:
>
> yamllint warnings/errors:
>
> dtschema/dtc warnings/errors:
> Documentation/devicetree/bindings/power/power-domain.example.dts:136.13-26: Warning (reg_format): /example-3/protocol@11:reg: property has invalid length (4 bytes) (#address-cells == 1, #size-cells == 1)
> Documentation/devicetree/bindings/power/power-domain.example.dtb: Warning (pci_device_reg): Failed prerequisite 'reg_format'
> Documentation/devicetree/bindings/power/power-domain.example.dtb: Warning (pci_device_bus_num): Failed prerequisite 'reg_format'
> Documentation/devicetree/bindings/power/power-domain.example.dtb: Warning (simple_bus_reg): Failed prerequisite 'reg_format'
> Documentation/devicetree/bindings/power/power-domain.example.dtb: Warning (i2c_bus_reg): Failed prerequisite 'reg_format'
> Documentation/devicetree/bindings/power/power-domain.example.dtb: Warning (spi_bus_reg): Failed prerequisite 'reg_format'

Noted, and fix will be included in next rev.

Kevin


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/3] pmdomain: core: add support for power-domains-child-ids
  2026-03-11  0:19 ` [PATCH 2/3] pmdomain: core: add support for power-domains-child-ids Kevin Hilman (TI)
@ 2026-03-13 11:55   ` Dhruva Gole
  2026-03-25 10:22   ` Ulf Hansson
  1 sibling, 0 replies; 10+ messages in thread
From: Dhruva Gole @ 2026-03-13 11:55 UTC (permalink / raw)
  To: Kevin Hilman (TI)
  Cc: Ulf Hansson, Rob Herring, Geert Uytterhoeven, linux-pm,
	devicetree, linux-kernel, arm-scmi, linux-arm-kernel

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@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@baylibre.com>
> ---

I've tested multiple possibilities with this series on the TI AM62L,
Tested-by: Dhruva Gole <d-gole@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@am62lxx-evm:~# cat /sys/kernel/debug/pm_genpd/power-controller-cluster/sub_domains
GPMC0
ELM0
root@am62lxx-evm:~# cat /sys/kernel/debug/pm_genpd/power-controller-main/sub_domains
power-controller-cluster
WKUP_GTC0
MSRAM_96K0
MCSPI0
root@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


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/3] pmdomain: arm_scmi: add support for domain hierarchies
  2026-03-11  0:19 ` [PATCH 3/3] pmdomain: arm_scmi: add support for domain hierarchies Kevin Hilman (TI)
@ 2026-03-13 12:07   ` Dhruva Gole
  0 siblings, 0 replies; 10+ messages in thread
From: Dhruva Gole @ 2026-03-13 12:07 UTC (permalink / raw)
  To: Kevin Hilman (TI)
  Cc: Ulf Hansson, Rob Herring, Geert Uytterhoeven, linux-pm,
	devicetree, linux-kernel, arm-scmi, linux-arm-kernel

On Mar 10, 2026 at 17:19:25 -0700, Kevin Hilman (TI) wrote:
> After primary SCMI pmdomain is created, use new of_genpd helper which
> checks for child domain mappings defined in power-domains-child-ids.
> 
> Also remove any child domain mappings when SCMI domain is removed.
> 
> Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
> ---

Again, since it worked fine on my AM62L,
Tested-by: Dhruva Gole <d-gole@ti.com>

But I had some thoughts further down...

>  drivers/pmdomain/arm/scmi_pm_domain.c | 14 +++++++++++++-
>  1 file changed, 13 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/pmdomain/arm/scmi_pm_domain.c b/drivers/pmdomain/arm/scmi_pm_domain.c
> index b5e2ffd5ea64..9d8faef44aa9 100644
> --- a/drivers/pmdomain/arm/scmi_pm_domain.c
> +++ b/drivers/pmdomain/arm/scmi_pm_domain.c
> @@ -114,6 +114,14 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
>  
>  	dev_set_drvdata(dev, scmi_pd_data);
>  
> +	/*
> +	 * Parse (optional) power-domains-child-ids property to
> +	 * establish parent-child relationships
> +	 */
> +	ret = of_genpd_add_child_ids(np, scmi_pd_data);
> +	if (ret < 0 && ret != -ENOENT)
> +		pr_err("Failed to parse power-domains-child-ids for %pOF: %d\n", np, ret);

Nit: I think the style of this driver is to use dev_err than pr_err

Also, maybe a dev_warn makes more sense since we're not even returning
the error or doing anything different if we get certain error path.

I am wondering if it makes sense to just abort the whole idea of
creating power-domain child ids if anything goes wrong?

Basically just of_genpd_remove_child_ids if we face a condition where we
have different number of parents/ children or id > num etc...

All are error cases where the system behaviour can go on to become very
unpredictable if we end up making a false/ incomplete parent-child ID
map.

Thoughts?

PS.
If we go on to do a of_genpd_remove_child_ids here incase of failure then it makes sense to
scream a dev_err here.


> +
>  	return 0;
>  err_rm_genpds:
>  	for (i = num_domains - 1; i >= 0; i--)
> @@ -129,9 +137,13 @@ static void scmi_pm_domain_remove(struct scmi_device *sdev)
>  	struct device *dev = &sdev->dev;
>  	struct device_node *np = dev->of_node;
>  
> +	scmi_pd_data = dev_get_drvdata(dev);
> +
> +	/* Remove any parent-child relationships established at probe time */
> +	of_genpd_remove_child_ids(np, scmi_pd_data);
> +
>  	of_genpd_del_provider(np);
>  
> -	scmi_pd_data = dev_get_drvdata(dev);
>  	for (i = 0; i < scmi_pd_data->num_domains; i++) {
>  		if (!scmi_pd_data->domains[i])
>  			continue;
> 
> -- 
> 2.51.0
> 
> 

-- 
Best regards,
Dhruva Gole
Texas Instruments Incorporated


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/3] dt-bindings: power: Add power-domains-child-ids property
  2026-03-11  0:19 ` [PATCH 1/3] dt-bindings: power: Add power-domains-child-ids property Kevin Hilman (TI)
  2026-03-11  1:38   ` Rob Herring (Arm)
@ 2026-03-24 23:25   ` Rob Herring
  1 sibling, 0 replies; 10+ messages in thread
From: Rob Herring @ 2026-03-24 23:25 UTC (permalink / raw)
  To: Kevin Hilman (TI)
  Cc: Ulf Hansson, Geert Uytterhoeven, linux-pm, devicetree,
	linux-kernel, arm-scmi, linux-arm-kernel

On Tue, Mar 10, 2026 at 05:19:23PM -0700, Kevin Hilman (TI) wrote:
> Add binding documentation for the new power-domains-child-ids property,
> which works in conjunction with the existing power-domains property to
> establish parent-child relationships between a multi-domain power domain
> provider and external parent domains.
> 
> Each element in the uint32 array identifies the child domain
> ID (index) within the provider that should be made a child domain of
> the corresponding phandle entry in power-domains. The two arrays must
> have the same number of elements.
> 
> Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
> ---
>  Documentation/devicetree/bindings/power/power-domain.yaml | 35 +++++++++++++++++++++++++++++++++++
>  1 file changed, 35 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/power/power-domain.yaml b/Documentation/devicetree/bindings/power/power-domain.yaml
> index b1147dbf2e73..a3d2af124d37 100644
> --- a/Documentation/devicetree/bindings/power/power-domain.yaml
> +++ b/Documentation/devicetree/bindings/power/power-domain.yaml
> @@ -68,6 +68,21 @@ properties:
>        by the given provider should be subdomains of the domain specified
>        by this binding.
>  
> +  power-domains-child-ids:
> +    $ref: /schemas/types.yaml#/definitions/uint32-array
> +    description:
> +      An array of child domain IDs that correspond to the power-domains
> +      property. This property is only applicable to power domain providers
> +      with "#power-domain-cells" > 0 (i.e., providers that supply multiple
> +      power domains). It specifies which of the provider's child domains
> +      should be associated with each parent domain listed in the power-domains
> +      property. The number of elements in this array must match the number of
> +      phandles in the power-domains property. Each element specifies the child
> +      domain ID (index) that should be made a child domain of the corresponding
> +      parent domain. This enables hierarchical power domain structures where
> +      different child domains from the same provider can have different
> +      parent domains.

Okay, I guess we stick with this. Sorry for the detour.

With the example fixed,

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>

Rob


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/3] pmdomain: core: add support for power-domains-child-ids
  2026-03-11  0:19 ` [PATCH 2/3] pmdomain: core: add support for power-domains-child-ids Kevin Hilman (TI)
  2026-03-13 11:55   ` Dhruva Gole
@ 2026-03-25 10:22   ` Ulf Hansson
  1 sibling, 0 replies; 10+ messages in thread
From: Ulf Hansson @ 2026-03-25 10:22 UTC (permalink / raw)
  To: Kevin Hilman (TI)
  Cc: Rob Herring, Geert Uytterhoeven, linux-pm, devicetree,
	linux-kernel, arm-scmi, linux-arm-kernel

On Wed, 11 Mar 2026 at 01:19, Kevin Hilman (TI) <khilman@baylibre.com> 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,
> the parent domains are MAIN_PD and WKUP_PD:
>
>     scmi_pds: protocol@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@baylibre.com>

Thanks for working on this! It certainly is a missing feature!

> ---
>  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.

I think we should avoid returning specific error codes for specific
errors, simply because it usually becomes messy.

If I understand correctly the intent here is to allow the caller to
check for -ENOENT and potentially avoid bailing out as it may not
really be an error, right?

Perhaps a better option is to return the number of children for whom
we successfully assigned parents. Hence 0 or a positive value allows
the caller to understand what happened. More importantly, a negative
error code then really becomes an error for the caller to consider.

> + */
> +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);

Perhaps it's easier to check if of_property_count_u32_elems() returns
the same number as of_count_phandle_with_args() above? If it doesn't,
something is wrong, and there is no need to continue.

This way you also know the number of loops upfront that must iterate
through all indexes. This should allow us to use a simpler for-loop
below, I think. In this case you can also use
of_property_read_u32_index() instead.

> +
> +       /* 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);

Before getting the parent_genpd like this, we need to take the
gpd_list_lock. The lock must be held when genpd_add_subdomain() is
being called.

> +               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:

This isn't a suffient error handling.

If we successfully added child domains using genpd_add_subdomain(), we
must remove them here, by calling pm_genpd_remove_subdomain() in the
reverse order as we just added them.

> +       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);

Similar comments as for of_genpd_add_child_ids().

Moreover, I think we should remove the children in the reverse order
of how we added them.

> +
> +       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
>

Kind regards
Uffe


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-03-25 10:23 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-11  0:19 [PATCH 0/3] pmdomain: core: add support for domain hierarchies in DT Kevin Hilman (TI)
2026-03-11  0:19 ` [PATCH 1/3] dt-bindings: power: Add power-domains-child-ids property Kevin Hilman (TI)
2026-03-11  1:38   ` Rob Herring (Arm)
2026-03-11 21:34     ` Kevin Hilman
2026-03-24 23:25   ` Rob Herring
2026-03-11  0:19 ` [PATCH 2/3] pmdomain: core: add support for power-domains-child-ids Kevin Hilman (TI)
2026-03-13 11:55   ` Dhruva Gole
2026-03-25 10:22   ` Ulf Hansson
2026-03-11  0:19 ` [PATCH 3/3] pmdomain: arm_scmi: add support for domain hierarchies Kevin Hilman (TI)
2026-03-13 12:07   ` Dhruva Gole

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox