[PATCH 1/2] soc/tegra: pmc: Support nested PMC power domains
From: Mikko Perttunen
Date: Thu Oct 15 2026 - 06:21:39 EST
The PMC power domain provider implementation currently supports having
parent domains for its domains, but only if the parent domain is
registered before any of the PMC power partitions. In practice, this is
the core power domain.
On Tegra210, the TRM documents that part of the NVDEC hardware unit is
in the NVJPG power domain, and that the NVJPG power domain must be kept
powered while the NVDEC domain is powered on. Hence, we should model the
NVDEC power domain as a subdomain of NVJPG.
To enable this, support one level of self-referential subdomains in the
driver, by registering all power domains first before adding subdomain
links, and correspondingly removing all subdomain links before removing
power domains. Without this, a non-topological ordering of power domains
in the device tree would cause a probe deferral that would never be
resolved.
Cc: stable@xxxxxxxxxxxxxxx # 6.19.x
Signed-off-by: Mikko Perttunen <mperttunen@xxxxxxxxxx>
---
drivers/soc/tegra/pmc.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 2c70eedf514e..3d820c462829 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -1525,8 +1525,11 @@ static int tegra_powergate_init(struct tegra_pmc *pmc,
for_each_child_of_node_scoped(np, child) {
err = tegra_powergate_add(pmc, child);
if (err < 0)
- break;
+ goto put;
+ }
+ /* A parent powergate may come after its subdomains in DT order. */
+ for_each_child_of_node_scoped(np, child) {
if (of_parse_phandle_with_args(child, "power-domains",
"#power-domain-cells",
0, &parent_args))
@@ -1541,6 +1544,7 @@ static int tegra_powergate_init(struct tegra_pmc *pmc,
break;
}
+put:
of_node_put(np);
return err;
@@ -1564,6 +1568,7 @@ static void tegra_powergate_remove(struct generic_pm_domain *genpd)
static void tegra_powergate_remove_all(struct device_node *parent)
{
+ struct of_phandle_args child_args, parent_args;
struct generic_pm_domain *genpd;
struct device_node *np, *child;
@@ -1571,6 +1576,20 @@ static void tegra_powergate_remove_all(struct device_node *parent)
if (!np)
return;
+ /* A parent powergate cannot be removed while it has subdomains. */
+ for_each_child_of_node(np, child) {
+ if (of_parse_phandle_with_args(child, "power-domains",
+ "#power-domain-cells",
+ 0, &parent_args))
+ continue;
+
+ child_args.np = child;
+ child_args.args_count = 0;
+
+ of_genpd_remove_subdomain(&parent_args, &child_args);
+ of_node_put(parent_args.np);
+ }
+
for_each_child_of_node(np, child) {
of_genpd_del_provider(child);
--
2.55.0