lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250528-pmdomain-hierarchy-onecell-v1-1-851780700c68@baylibre.com>
Date: Wed, 28 May 2025 13:03:43 -0700
From: Kevin Hilman <khilman@...libre.com>
To: Ulf Hansson <ulf.hansson@...aro.org>, Rob Herring <robh@...nel.org>, 
 Krzysztof Kozlowski <krzk+dt@...nel.org>, 
 Conor Dooley <conor+dt@...nel.org>, "Rafael J. Wysocki" <rjw@...ysocki.net>
Cc: linux-pm@...r.kernel.org, devicetree@...r.kernel.org, 
 linux-kernel@...r.kernel.org, arm-scmi@...r.kernel.org
Subject: [PATCH RFC] pmdomain: core: add hierarchy support for onecell
 providers

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 might be a subdomain of
multiple 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 the new property, child domain 15 (scmi_pds 15) becomes a
subdomain of MAIN_PD, and child domain 19 (scmi_pds 19) becomes a
subdomain of WKUP_PD.

Note: this idea was previously discussed on the arm-scmi mailing
list[1] where this approach was proposed by Ulf.  This is my initial
attempt at implementing it for discussion.  I'm definitely a noob at
adding support new DT properties, so I got some help from an AI friend
named Claude in writing this code, so feedback on the apprach is
welcomed.

[1] https://lore.kernel.org/arm-scmi/CAPDyKFo_P129sVirHHYjOQT+QUmpymcRJme9obzKJeRgO7B-1A@mail.gmail.com/

Signed-off-by: Kevin Hilman <khilman@...libre.com>
---
 Documentation/devicetree/bindings/power/power-domain.yaml |  39 ++++++++++++++++++++++++++++++++
 drivers/pmdomain/core.c                                   | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 150 insertions(+)

diff --git a/Documentation/devicetree/bindings/power/power-domain.yaml b/Documentation/devicetree/bindings/power/power-domain.yaml
index 8fdb529d560b..1db82013e407 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 subdomain 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,27 @@ examples:
             min-residency-us = <7000>;
         };
     };
+
+  - |
+    // Example of power-domains-child-ids usage
+    MAIN_PD: main-power-controller {
+        compatible = "foo,main-power-controller";
+        #power-domain-cells = <0>;
+    };
+
+    WKUP_PD: wkup-power-controller {
+        compatible = "foo,wkup-power-controller";
+        #power-domain-cells = <0>;
+    };
+
+    scmi_pds: protocol@11 {
+        reg = <0x11>;
+        #power-domain-cells = <1>;
+        power-domains = <&MAIN_PD>, <&WKUP_PD>;
+        power-domains-child-ids = <15>, <19>;
+    };
+
+    // In the above example:
+    // - Child domain 15 (scmi_pds 15) becomes a subdomain of MAIN_PD
+    // - Child domain 19 (scmi_pds 19) becomes a subdomain of WKUP_PD
+    // - Other child domains (0-14, 16-18, 20+) have no parent relationship
diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
index d6c1ddb807b2..d9ae4f1d35ca 100644
--- a/drivers/pmdomain/core.c
+++ b/drivers/pmdomain/core.c
@@ -2441,6 +2441,9 @@ static LIST_HEAD(of_genpd_providers);
 /* Mutex to protect the list above. */
 static DEFINE_MUTEX(of_genpd_mutex);
 
+static int of_genpd_parse_child_ids(struct device_node *np,
+				    struct genpd_onecell_data *data);
+
 /**
  * genpd_xlate_simple() - Xlate function for direct node-domain mapping
  * @genpdspec: OF phandle args to map into a PM domain
@@ -2635,6 +2638,14 @@ int of_genpd_add_provider_onecell(struct device_node *np,
 	if (ret < 0)
 		goto error;
 
+	/* Parse power-domains-child-ids property to establish parent-child relationships */
+	ret = of_genpd_parse_child_ids(np, data);
+	if (ret < 0 && ret != -ENOENT) {
+		pr_err("Failed to parse power-domains-child-ids for %pOF: %d\n", np, ret);
+		of_genpd_del_provider(np);
+		goto error;
+	}
+
 	return 0;
 
 error:
@@ -2734,6 +2745,106 @@ static struct generic_pm_domain *genpd_get_from_provider(
 	return genpd;
 }
 
+/**
+ * of_genpd_parse_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.
+ */
+static int of_genpd_parse_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_ids;
+	int num_parents, num_child_ids, i, ret;
+
+	/* Check if both properties exist */
+	num_parents = of_count_phandle_with_args(np, "power-domains", "#power-domain-cells");
+	if (num_parents <= 0)
+		return -ENOENT;
+
+	num_child_ids = of_property_count_u32_elems(np, "power-domains-child-ids");
+	if (num_child_ids <= 0)
+		return -ENOENT;
+
+	if (num_parents != num_child_ids) {
+		pr_err("power-domains (%d) and power-domains-child-ids (%d) count mismatch for %pOF\n",
+		       num_parents, num_child_ids, np);
+		return -EINVAL;
+	}
+
+	child_ids = kcalloc(num_child_ids, sizeof(*child_ids), GFP_KERNEL);
+	if (!child_ids)
+		return -ENOMEM;
+
+	ret = of_property_read_u32_array(np, "power-domains-child-ids", child_ids, num_child_ids);
+	if (ret) {
+		pr_err("Failed to read power-domains-child-ids for %pOF: %d\n", np, ret);
+		goto out_free;
+	}
+
+	/* For each parent domain, establish parent-child relationship */
+	for (i = 0; i < num_parents; i++) {
+		ret = of_parse_phandle_with_args(np, "power-domains",
+						 "#power-domain-cells", i, &parent_args);
+		if (ret) {
+			pr_err("Failed to parse parent domain %d for %pOF: %d\n", i, np, ret);
+			goto out_free;
+		}
+
+		/* 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 %d for %pOF: %ld\n",
+			       i, np, PTR_ERR(parent_genpd));
+			ret = PTR_ERR(parent_genpd);
+			goto out_free;
+		}
+
+		/* Validate child ID is within bounds */
+		if (child_ids[i] >= data->num_domains) {
+			pr_err("Child ID %u out of bounds (max %u) for parent %d in %pOF\n",
+			       child_ids[i], data->num_domains - 1, i, np);
+			ret = -EINVAL;
+			goto out_free;
+		}
+
+		/* Get the child domain */
+		child_genpd = data->domains[child_ids[i]];
+		if (!child_genpd) {
+			pr_err("Child domain %u is NULL for parent %d in %pOF\n",
+			       child_ids[i], i, np);
+			ret = -EINVAL;
+			goto out_free;
+		}
+
+		/* Establish parent-child relationship */
+		ret = genpd_add_subdomain(parent_genpd, child_genpd);
+		if (ret) {
+			pr_err("Failed to add child domain %u to parent %d in %pOF: %d\n",
+			       child_ids[i], i, np, ret);
+			goto out_free;
+		}
+
+		pr_debug("Added child domain %u (%s) to parent %s for %pOF\n",
+			 child_ids[i], child_genpd->name, parent_genpd->name, np);
+	}
+
+	ret = 0;
+
+out_free:
+	kfree(child_ids);
+	return ret;
+}
+
 /**
  * of_genpd_add_device() - Add a device to an I/O PM domain
  * @genpdspec: OF phandle args to use for look-up PM domain

---
base-commit: 0ff41df1cb268fc69e703a08a57ee14ae967d0ca
change-id: 20250528-pmdomain-hierarchy-onecell-a46fad47d855

Best regards,
-- 
Kevin Hilman <khilman@...libre.com>


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ