* [PATCH v3 1/3] dt-bindings: power: Add power-domains-child-ids property
2026-04-20 23:51 [PATCH v3 0/3] pmdomain: core: add support for domain hierarchies in DT Kevin Hilman (TI)
@ 2026-04-20 23:51 ` Kevin Hilman (TI)
2026-04-21 13:49 ` Rob Herring
2026-04-20 23:51 ` [PATCH v3 2/3] pmdomain: core: add support for power-domains-child-ids Kevin Hilman (TI)
2026-04-20 23:51 ` [PATCH v3 3/3] pmdomain: arm_scmi: add support for domain hierarchies Kevin Hilman (TI)
2 siblings, 1 reply; 6+ messages in thread
From: Kevin Hilman (TI) @ 2026-04-20 23:51 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 | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/Documentation/devicetree/bindings/power/power-domain.yaml b/Documentation/devicetree/bindings/power/power-domain.yaml
index b1147dbf2e73..163b0af158fd 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,22 @@ 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: power-controller-scmi {
+ compatible = "foo,power-controller";
+ #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] 6+ messages in thread* [PATCH v3 2/3] pmdomain: core: add support for power-domains-child-ids
2026-04-20 23:51 [PATCH v3 0/3] pmdomain: core: add support for domain hierarchies in DT Kevin Hilman (TI)
2026-04-20 23:51 ` [PATCH v3 1/3] dt-bindings: power: Add power-domains-child-ids property Kevin Hilman (TI)
@ 2026-04-20 23:51 ` Kevin Hilman (TI)
2026-04-20 23:51 ` [PATCH v3 3/3] pmdomain: arm_scmi: add support for domain hierarchies Kevin Hilman (TI)
2 siblings, 0 replies; 6+ messages in thread
From: Kevin Hilman (TI) @ 2026-04-20 23:51 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.
The add function is "all or nothing". If it cannot add all of the
child domains in the list, it will unwind any additions already made
and report a failure.
Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
---
drivers/pmdomain/core.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/pm_domain.h | 16 ++++++++++++++++
2 files changed, 183 insertions(+)
diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
index 61c2277c9ce3..97498a37336d 100644
--- a/drivers/pmdomain/core.c
+++ b/drivers/pmdomain/core.c
@@ -2909,6 +2909,173 @@ 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.
+ *
+ * Uses "all or nothing" semantics: either all relationships are established
+ * successfully, or none are (any partially-added relationships are unwound
+ * on error).
+ *
+ * Returns the number of parent-child relationships established on success,
+ * 0 if the properties don't exist, or a negative error code on failure.
+ */
+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 generic_pm_domain **pairs; /* pairs[2*i]=parent, pairs[2*i+1]=child */
+ u32 child_id;
+ int i, ret, count, child_count, added = 0;
+
+ /* Check if both properties exist */
+ count = of_count_phandle_with_args(np, "power-domains", "#power-domain-cells");
+ if (count <= 0)
+ return 0;
+
+ child_count = of_property_count_u32_elems(np, "power-domains-child-ids");
+ if (child_count < 0)
+ return 0;
+ if (child_count != count)
+ return -EINVAL;
+
+ /* Allocate tracking array for error unwind (parent/child pairs) */
+ pairs = kmalloc_array(count * 2, sizeof(*pairs), GFP_KERNEL);
+ if (!pairs)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ ret = of_property_read_u32_index(np, "power-domains-child-ids",
+ i, &child_id);
+ if (ret)
+ goto err_unwind;
+
+ /* 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_unwind;
+ }
+
+ /* 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_unwind;
+ }
+
+ ret = of_parse_phandle_with_args(np, "power-domains",
+ "#power-domain-cells", i,
+ &parent_args);
+ if (ret)
+ goto err_unwind;
+
+ /* Get the parent domain */
+ parent_genpd = genpd_get_from_provider(&parent_args);
+ of_node_put(parent_args.np);
+ 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_unwind;
+ }
+
+ /* Establish parent-child relationship */
+ ret = pm_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_unwind;
+ }
+
+ /* Track for potential unwind */
+ pairs[2 * added] = parent_genpd;
+ pairs[2 * added + 1] = child_genpd;
+ added++;
+
+ pr_debug("Added child domain %u (%s) to parent %s for %pOF\n",
+ child_id, child_genpd->name, parent_genpd->name, np);
+ }
+
+ kfree(pairs);
+ return count;
+
+err_unwind:
+ /* Reverse all previously established relationships */
+ while (added-- > 0)
+ pm_genpd_remove_subdomain(pairs[2 * added], pairs[2 * added + 1]);
+ kfree(pairs);
+ 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;
+ u32 child_id;
+ int i, ret, count, child_count;
+
+ /* Check if both properties exist */
+ count = of_count_phandle_with_args(np, "power-domains", "#power-domain-cells");
+ if (count <= 0)
+ return -ENOENT;
+
+ child_count = of_property_count_u32_elems(np, "power-domains-child-ids");
+ if (child_count < 0)
+ return -ENOENT;
+ if (child_count != count)
+ return -EINVAL;
+
+ for (i = 0; i < count; i++) {
+ if (of_property_read_u32_index(np, "power-domains-child-ids",
+ i, &child_id))
+ continue;
+
+ if (child_id >= data->num_domains || !data->domains[child_id])
+ continue;
+
+ ret = of_parse_phandle_with_args(np, "power-domains",
+ "#power-domain-cells", i,
+ &parent_args);
+ if (ret)
+ continue;
+
+ parent_genpd = genpd_get_from_provider(&parent_args);
+ of_node_put(parent_args.np);
+ if (IS_ERR(parent_genpd))
+ continue;
+
+ child_genpd = data->domains[child_id];
+ pm_genpd_remove_subdomain(parent_genpd, child_genpd);
+ }
+
+ return 0;
+}
+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] 6+ messages in thread* [PATCH v3 3/3] pmdomain: arm_scmi: add support for domain hierarchies
2026-04-20 23:51 [PATCH v3 0/3] pmdomain: core: add support for domain hierarchies in DT Kevin Hilman (TI)
2026-04-20 23:51 ` [PATCH v3 1/3] dt-bindings: power: Add power-domains-child-ids property Kevin Hilman (TI)
2026-04-20 23:51 ` [PATCH v3 2/3] pmdomain: core: add support for power-domains-child-ids Kevin Hilman (TI)
@ 2026-04-20 23:51 ` Kevin Hilman (TI)
2 siblings, 0 replies; 6+ messages in thread
From: Kevin Hilman (TI) @ 2026-04-20 23:51 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..66dcf2e687a8 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)
+ dev_err(dev, "Failed to add child domain hierarchy: %d\n", 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] 6+ messages in thread