From nobody Mon Feb 9 20:09:44 2026 Received: from mail-pf1-f171.google.com (mail-pf1-f171.google.com [209.85.210.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EDF8D3161A1 for ; Fri, 23 Jan 2026 01:14:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769130863; cv=none; b=npByVycXJYJpQZN34N8l7tJSOjEgoA8ppLDsZ5GzH1dB5yrqYkBYPfKI6FzQgzdVe+K4zYBtSSqsLliO9AU3mWqVLtLSCK8yRIJDcUGxud2WT8KqJp79VlXajyQIAtnDjdfU2u1oMQBbz8VVW0XpSJp5mkbBH0vWIUvPlZUILDw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769130863; c=relaxed/simple; bh=lzR3NEhB2g7iCRM+bJ588/XYQ1irfnPbijYHHqE2mb0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=fqRzjDiMSfp9/D0Cy7bCRTFDBa7/3J0aSFAncQaRiyOsG2GgFmP4UL1k6zYrm/crcpqu1o57os5f1tWj6VgeUEf7ui9t0CIqehWf7N93csOYwoNm/9HG68oWhMDQPdd3j1D1N6Ed5oI9eTfpw2OB2UfUBPgP57MFYAj3GFP6240= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com; spf=pass smtp.mailfrom=baylibre.com; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b=LUyKIaGR; arc=none smtp.client-ip=209.85.210.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=baylibre.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b="LUyKIaGR" Received: by mail-pf1-f171.google.com with SMTP id d2e1a72fcca58-81ed3e6b8e3so821871b3a.2 for ; Thu, 22 Jan 2026 17:14:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1769130848; x=1769735648; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=aHt+mmH4C5nxGUCfgNrBDFG/vUcO3dbURh+yi1mxNm8=; b=LUyKIaGRIpheIGdp0yKFGdzr0U1qdx5mYQcDms5odjnfkUx37cLKUx7hgJvUVbdU1F uuX0RWhLlNMcix2W6aSeG45R7ET9gGKuk3QtrBVR6YSb3epHMY3Nucwp0zp+XYAJPST2 DHL4rN3wyymwZX2yDZtKb+w0JrQkse4s8275juX658M0rD1nW99kJWm8NIp7MPcyN1hu HEisa7voX2PE4rUxF+o7hO72KIEVVPyjgusIqGGl8mxpiiSStTue1ifRJP0utekUOVc1 wDWaB1Xh2ISCublU0d+pS77/7JlLQ6a9KuNWrWwCDkGcUazVVgQmreje1yWj/nlBC2c/ +yiQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769130848; x=1769735648; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=aHt+mmH4C5nxGUCfgNrBDFG/vUcO3dbURh+yi1mxNm8=; b=GgLxqANuSCpliGiioqB8x5uVIjfHFeznHd1d0ppY0ty98Z6aML92w9XY0gRpCpBXg7 1JiIn8er8rXBnxUS3AuVhUWV0zSGsYYzqtKn6ya3WL2KlLJdJwOGZNwC8Bg6nWaUFTBj H11N243bcyXWDQxB2egsmcH176vbipaAuXoID0cY8gdA/t9nMOCibAsfRAj55r8S6ssp hIr634nZWMl/vTU0NUNjlaawyQcHUI8n0b5WXkABcGvxue8wauerSbKnZfbUAH/0TIzx 1Etx72c9tA8rB1vyARZJIj3qPWFc4jfKOFHU1gDphFdleEvlYk920KKSlBXNGKk5eTI7 S4dA== X-Forwarded-Encrypted: i=1; AJvYcCX+A5Ke3N74gXqRMRCX8y13bd+fBb60T5a/Uu4nFAsdvDCcvlhVpZ0zrNoOB0arEc3TdjoWLpd7WPvepnU=@vger.kernel.org X-Gm-Message-State: AOJu0Yzw+OLHNEl2dqL2DPjUynPUCZ1REyWewFWIK59C1jAHHv4DT+XO qzP0tOew1qJo+qRaKubkwbHHHkhaZAbLHcZsS428WWFk4t1doWj1aH57InadTOAH+q8= X-Gm-Gg: AZuq6aJXbtMLXH92uj//r07Il1TXdCprXdVTKkUjFSOIlnGenevmulrbsaLD0Ow7eWy wmMTEJDUrsu/5lk71MR8W/AUVGk315abCZ12+bW0iPvdARDZ/CftXdZU7s44+nNsUDtPyQhsCoN L1HnwW0QXMIsT+be7aNY4gMfme+L/E4oRXr0gcCJ3qeJGP1ytsE1yH2fnh6wzCywvGfm1IOb615 QwUe+pkwS6nheebKgUK5KW6lAuF3AxAdFXSV/cZERnwgDkKyy4xMI7r60opkTB5/2Ij7LZ4FZBh pzHsrORziCfMnsaeFD+ZFAjlpBDRzPtJTvytv9/vPVpo0mH9EmzPkLH/OR1YT2TQqNcDYQCLmri A9OpB26im2vM3YZRPI4YCLl02OlkST0d7WRE+jl8L5uU2QIGJzO45r8tK0aH8YnfFx1ENEkim/K WNyW/pCT+r X-Received: by 2002:a05:6a00:228a:b0:823:ad3:4ff4 with SMTP id d2e1a72fcca58-82317e1f2fbmr995725b3a.37.1769130848424; Thu, 22 Jan 2026 17:14:08 -0800 (PST) Received: from localhost ([71.212.200.220]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-8231876e5d0sm495195b3a.61.2026.01.22.17.14.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 22 Jan 2026 17:14:07 -0800 (PST) From: "Kevin Hilman (TI)" Date: Thu, 22 Jan 2026 17:14:00 -0800 Subject: [PATCH RFC v5 1/2] pmdomain: core: support domain hierarchy via power-domain-map Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260122-pmdomain-hierarchy-onecell-v5-1-76855ec856bd@baylibre.com> References: <20260122-pmdomain-hierarchy-onecell-v5-0-76855ec856bd@baylibre.com> In-Reply-To: <20260122-pmdomain-hierarchy-onecell-v5-0-76855ec856bd@baylibre.com> To: Ulf Hansson , "Rafael J. Wysocki" , Sudeep Holla , Cristian Marussi Cc: devicetree@vger.kernel.org, linux-pm@vger.kernel.org, arm-scmi@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org X-Mailer: b4 0.15-dev-47773 X-Developer-Signature: v=1; a=openpgp-sha256; l=8104; i=khilman@baylibre.com; h=from:subject:message-id; bh=lzR3NEhB2g7iCRM+bJ588/XYQ1irfnPbijYHHqE2mb0=; b=owEBbQKS/ZANAwAIAVk3GJrT+8ZlAcsmYgBpcstegivFzLujdyy/XYyCoOTFhTKHJRAJ8NSZt PKtjt8GNvKJAjMEAAEIAB0WIQR7h0YOFpJ/qfW/8QxZNxia0/vGZQUCaXLLXgAKCRBZNxia0/vG ZYTyEACh3s27sFpbfZ/ydsd8FMkRCxosg8QXT/a2xgDoeAuFNT+1jyG2LMV6Cjudi5xBIfK5o5+ K3rCkRFQItyaH0a2LcgHP1ysn+3J+w2O9jUPpq9M/bPVLnvG0wrPPggT5UyVG7/tuHVDuXbEUym E3YJRv9CholjJhRnXmP/Kr3ZBnbGrol0MS9vaP5sCLYbKRZR5dysNMLxbv4Tgq3N/AKfgBfw6b3 Q74xOX/xuxPeFQD2H2MRdkrxmo9B62WkZkuMRYVMVERQxnn2oYBHTtl0X4e8IHnR1TtQBrhFAaa T6YDp5ROfuR0IFTYO1JkOYl2SkaCB5FvyM+nJx6GbuWKGzcUo/QsyzdiWSSiCfe6nGkwcuvnGa3 2wje3dW4WqQfamM80nMSj1P6vznxc55kdLF/KxoRZRw/4pZwuoHt+Xuryy6vuV40VRntvMRE10D OxizOhFu6oqR26Yneomed1EZP0UaI66OIqdyQVj9Rqm8ZrTeVfJ35Y3hEjpHPr1HsRFZ/Ir4kCB LI59eoPpfLf+zMyBvm2ycc9gabfPz64DoKuvsnoGOgbxbSb0E8AIPP1XJ1mNJoelQmFGh8L1jhO /txN/VKIOHIm+hyiJleWlDYBJ2VOc8zeFSqePAamWNlzSi8bg91+VSFiBVONnrg409+suH1Gu2r kx4s+ReBZ01uOpg== X-Developer-Key: i=khilman@baylibre.com; a=openpgp; fpr=7B87460E16927FA9F5BFF10C5937189AD3FBC665 Add of_genpd_[add|remove]_subdomain_map() helper functions to support hierarchical PM domains defined by using power-domains-map property (c.f. nexus node maps in DT spec, section 2.5.1). This enables PM domain providers with #power-domain-cells > 0 to establish subdomain relationships via the power-domain-map property, which was not previously possible. These new helper functions: - uses an OF helper to iterate to over entries in power-domain-map - For each mapped entry: extracts child specifier, resolves parent phandle, extracts parent specifier args, and establishes subdomain relationship - Calls genpd_[add|remove]_subdomain() with proper gpd_list_lock mutex prot= ection Example from k3-am62l.dtsi: scmi_pds: protocol@11 { #power-domain-cells =3D <1>; power-domain-map =3D <15 &MAIN_PD>, /* TIMER0 */ <19 &WKUP_PD>; /* WKUP_TIMER0 */ }; MAIN_PD: power-controller-main { #power-domain-cells =3D <0>; }; WKUP_PD: power-controller-main { #power-domain-cells =3D <0>; }; This allows SCMI power domain 15 to become a subdomain of MAIN_PD, and domain 19 to become a subdomain of WKUP_PD. Signed-off-by: Kevin Hilman (TI) --- drivers/pmdomain/core.c | 160 ++++++++++++++++++++++++++++++++++++++++++= +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++= +++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 16 ++++++++++++++++ 2 files changed, 176 insertions(+) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index bf82775f6a67..cee7fbbda829 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -3556,6 +3556,166 @@ static struct device_driver genpd_provider_drv =3D { .suppress_bind_attrs =3D true, }; =20 +/** + * of_genpd_remove_subdomain_map - Remove subdomain relationships from map + * + * @np: pointer to parent node containing map property + * @data: pointer to PM domain onecell data + * + * Iterate over entries in a power-domain-map, and remove the subdomain + * relationships that were previously established by of_genpd_add_subdomai= n_map(). + * This allows cleanup during driver removal or error handling. + * + * Return: 0 on success, negative error code on failure + */ +int of_genpd_remove_subdomain_map(struct device_node *np, + struct genpd_onecell_data *data) +{ + struct generic_pm_domain *genpd, *parent_genpd; + struct of_phandle_args child_args, parent_args; + int index =3D 0; + int ret =3D 0; + u32 child_index; + + if (!np || !data) + return -EINVAL; + + /* Iterate through power-domain-map entries using the OF helper */ + while (!of_parse_map_iter(np, "power-domain", &index, + &child_args, &parent_args)) { + /* Extract the child domain index from the child specifier */ + if (child_args.args_count < 1) { + of_node_put(parent_args.np); + continue; + } + child_index =3D child_args.args[0]; + + /* Validate child domain index */ + if (child_index >=3D data->num_domains) { + of_node_put(parent_args.np); + continue; + } + + genpd =3D data->domains[child_index]; + if (!genpd) { + of_node_put(parent_args.np); + continue; + } + + /* Get parent power domain from provider */ + mutex_lock(&gpd_list_lock); + + parent_genpd =3D genpd_get_from_provider(&parent_args); + if (IS_ERR(parent_genpd)) { + mutex_unlock(&gpd_list_lock); + of_node_put(parent_args.np); + dev_warn(&genpd->dev, "failed to get parent domain for removal\n"); + continue; + } + + /* Remove subdomain relationship */ + ret =3D pm_genpd_remove_subdomain(parent_genpd, genpd); + mutex_unlock(&gpd_list_lock); + of_node_put(parent_args.np); + + if (ret) + dev_warn(&genpd->dev, "failed to remove as subdomain of %s: %d\n", + parent_genpd->name, ret); + else + dev_dbg(&genpd->dev, "removed as subdomain of %s\n", + parent_genpd->name); + } + + return 0; +} +EXPORT_SYMBOL_GPL(of_genpd_remove_subdomain_map); + +/** + * of_genpd_add_subdomain_map - Parse and map child PM domains + * + * @np: pointer to parent node containing map property + * @data: pointer to PM domain onecell data + * + * Iterate over entries in a power-domain-map, and add them as + * children of the parent domain. If any child fails to be added, + * all previously added children are removed to maintain atomicity. + * + * Return: 0 on success, negative error code on failure + */ +int of_genpd_add_subdomain_map(struct device_node *np, + struct genpd_onecell_data *data) +{ + struct generic_pm_domain *genpd, *parent_genpd; + struct of_phandle_args child_args, parent_args; + int index =3D 0; + int ret =3D 0; + u32 child_index; + + if (!np || !data) + return -EINVAL; + + /* Iterate through power-domain-map entries using the OF helper */ + while (!of_parse_map_iter(np, "power-domain", &index, + &child_args, &parent_args)) { + /* Extract the child domain index from the child specifier */ + if (child_args.args_count < 1) { + of_node_put(parent_args.np); + ret =3D -EINVAL; + goto cleanup; + } + child_index =3D child_args.args[0]; + + /* Validate child domain index */ + if (child_index >=3D data->num_domains) { + of_node_put(parent_args.np); + pr_debug("map's child index (%u) > number of domains (%u). Skipping.\n= ", + child_index, data->num_domains); + ret =3D -EINVAL; + goto cleanup; + } + + genpd =3D data->domains[child_index]; + if (!genpd) { + of_node_put(parent_args.np); + continue; + } + + /* Get parent power domain from provider and establish subdomain relatio= nship */ + mutex_lock(&gpd_list_lock); + + parent_genpd =3D genpd_get_from_provider(&parent_args); + if (IS_ERR(parent_genpd)) { + mutex_unlock(&gpd_list_lock); + of_node_put(parent_args.np); + ret =3D PTR_ERR(parent_genpd); + dev_err(&genpd->dev, "failed to get parent domain: %d\n", ret); + goto cleanup; + } + + ret =3D genpd_add_subdomain(parent_genpd, genpd); + mutex_unlock(&gpd_list_lock); + of_node_put(parent_args.np); + + if (ret) { + dev_err(&genpd->dev, "failed to add as subdomain of %s: %d\n", + parent_genpd->name, ret); + goto cleanup; + } + + dev_dbg(&genpd->dev, "added as subdomain of %s\n", + parent_genpd->name); + } + + return 0; + +cleanup: + /* Remove all successfully added subdomains using the removal function */ + pr_err("rolling back child map additions due to error: %d\n", ret); + of_genpd_remove_subdomain_map(np, data); + return ret; +} +EXPORT_SYMBOL_GPL(of_genpd_add_subdomain_map); + static int __init genpd_bus_init(void) { int ret; diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 93ba0143ca47..3baf224e4f24 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -463,6 +463,10 @@ int of_genpd_add_subdomain(const struct of_phandle_arg= s *parent_spec, int of_genpd_remove_subdomain(const struct of_phandle_args *parent_spec, const struct of_phandle_args *subdomain_spec); struct generic_pm_domain *of_genpd_remove_last(struct device_node *np); +int of_genpd_add_subdomain_map(struct device_node *np, + struct genpd_onecell_data *data); +int of_genpd_remove_subdomain_map(struct device_node *np, + struct genpd_onecell_data *data); 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); @@ -505,6 +509,18 @@ static inline int of_genpd_remove_subdomain(const stru= ct of_phandle_args *parent return -ENODEV; } =20 +static inline int of_genpd_add_subdomain_map(struct device_node *np, + struct genpd_onecell_data *data) +{ + return -ENODEV; +} + +static inline int of_genpd_remove_subdomain_map(struct device_node *np, + struct genpd_onecell_data *data) +{ + return -ENODEV; +} + static inline int of_genpd_parse_idle_states(struct device_node *dn, struct genpd_power_state **states, int *n) { --=20 2.51.0