[PATCH/RFC 7/9] pmdomain: arm: scmi: Add clock domain support

Geert Uytterhoeven geert+renesas at glider.be
Thu Jun 11 06:02:11 PDT 2026


PM domain consumer devices may also part of a clock domain.

Add support for managing power management clocks in a clock domain
through Runtime PM.

Signed-off-by: Geert Uytterhoeven <geert+renesas at glider.be>
---
 drivers/pmdomain/arm/Kconfig          |  1 +
 drivers/pmdomain/arm/scmi_pm_domain.c | 81 ++++++++++++++++++++++++++-
 2 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/drivers/pmdomain/arm/Kconfig b/drivers/pmdomain/arm/Kconfig
index afed10d382ad7f66..11c4db47c1eadab0 100644
--- a/drivers/pmdomain/arm/Kconfig
+++ b/drivers/pmdomain/arm/Kconfig
@@ -14,6 +14,7 @@ config ARM_SCMI_PERF_DOMAIN
 config ARM_SCMI_POWER_DOMAIN
 	tristate "SCMI power domain driver"
 	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
+	depends on COMMON_CLK_SCMI || !COMMON_CLK_SCMI
 	default ARM_SCMI_PROTOCOL
 	select PM_GENERIC_DOMAINS if PM
 	help
diff --git a/drivers/pmdomain/arm/scmi_pm_domain.c b/drivers/pmdomain/arm/scmi_pm_domain.c
index 8e67f971c707e121..838917d3b236e3aa 100644
--- a/drivers/pmdomain/arm/scmi_pm_domain.c
+++ b/drivers/pmdomain/arm/scmi_pm_domain.c
@@ -5,9 +5,12 @@
  * Copyright (C) 2018-2021 ARM Ltd.
  */
 
+#include <linux/clk.h>
+#include <linux/clk/scmi.h>
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/module.h>
+#include <linux/pm_clock.h>
 #include <linux/pm_domain.h>
 #include <linux/scmi_protocol.h>
 
@@ -16,6 +19,7 @@ static const struct scmi_power_proto_ops *power_ops;
 struct scmi_pm_domain {
 	struct generic_pm_domain genpd;
 	const struct scmi_protocol_handle *ph;
+	struct device_node *clock_domain;
 	const char *name;
 	u32 domain;
 };
@@ -39,6 +43,67 @@ static int scmi_pd_power_off(struct generic_pm_domain *domain)
 	return scmi_pd_power(domain, SCMI_POWER_STATE_GENERIC_OFF);
 }
 
+static int scmi_pd_attach_dev(struct generic_pm_domain *domain,
+			      struct device *dev)
+{
+	struct scmi_pm_domain *pd = to_scmi_pd(domain);
+	struct device_node *np = dev->of_node;
+	struct of_phandle_args clkspec;
+	bool once = true;
+	struct clk *clk;
+	int ret;
+
+	for (int i = 0;
+	     !of_parse_phandle_with_args(np, "clocks", "#clock-cells", i, &clkspec);
+	     i++) {
+		if (clkspec.np != pd->clock_domain || clkspec.args_count != 1) {
+			of_node_put(clkspec.np);
+			continue;
+		}
+
+		clk = of_clk_get_from_provider(&clkspec);
+		of_node_put(clkspec.np);
+		if (!clk)
+			continue;
+
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			clk = NULL;
+			goto fail;
+		}
+
+		if (!scmi_clk_is_pm_clk(clk)) {
+			clk_put(clk);
+			continue;
+		}
+
+		if (once) {
+			once = false;
+			ret = pm_clk_create(dev);
+			if (ret)
+				goto fail;
+		}
+
+		ret = pm_clk_add_clk(dev, clk);
+		if (ret)
+			goto fail;
+	}
+
+	return 0;
+
+fail:
+	pm_clk_destroy(dev);
+	clk_put(clk);
+	return ret;
+}
+
+static void scmi_pd_detach_dev(struct generic_pm_domain *domain,
+			       struct device *dev)
+{
+	if (!pm_clk_no_clocks(dev))
+		pm_clk_destroy(dev);
+}
+
 static int scmi_pm_domain_probe(struct scmi_device *sdev)
 {
 	int num_domains, i, ret;
@@ -48,6 +113,7 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
 	struct genpd_onecell_data *scmi_pd_data;
 	struct generic_pm_domain **domains;
 	const struct scmi_handle *handle = sdev->handle;
+	struct device_node *clock_domain;
 	struct scmi_protocol_handle *ph;
 
 	if (!handle)
@@ -75,6 +141,8 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
 	if (!domains)
 		return -ENOMEM;
 
+	clock_domain = of_parse_phandle(np, "arm,clock-domain", 0);
+
 	for (i = 0; i < num_domains; i++, scmi_pd++) {
 		const struct scmi_power_domain_info *info;
 		u32 state;
@@ -106,6 +174,12 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
 		scmi_pd->genpd.power_on = scmi_pd_power_on;
 		scmi_pd->genpd.flags = GENPD_FLAG_ACTIVE_WAKEUP |
 				       info->genpd_flags;
+		if (clock_domain) {
+			scmi_pd->clock_domain = of_node_get(clock_domain);
+			scmi_pd->genpd.attach_dev = scmi_pd_attach_dev;
+			scmi_pd->genpd.detach_dev = scmi_pd_detach_dev;
+			scmi_pd->genpd.flags |= GENPD_FLAG_PM_CLK;
+		}
 
 		pm_genpd_init(&scmi_pd->genpd, NULL,
 			      state == SCMI_POWER_STATE_GENERIC_OFF);
@@ -113,6 +187,8 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
 		domains[i] = &scmi_pd->genpd;
 	}
 
+	of_node_put(clock_domain);
+
 	scmi_pd_data->domains = domains;
 	scmi_pd_data->num_domains = num_domains;
 
@@ -134,8 +210,10 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
 
 	return 0;
 err_rm_genpds:
-	for (i = num_domains - 1; i >= 0; i--)
+	for (i = num_domains - 1; i >= 0; i--) {
 		pm_genpd_remove(domains[i]);
+		of_node_put(to_scmi_pd(domains[i])->clock_domain);
+	}
 
 	return ret;
 }
@@ -158,6 +236,7 @@ static void scmi_pm_domain_remove(struct scmi_device *sdev)
 		if (!scmi_pd_data->domains[i])
 			continue;
 		pm_genpd_remove(scmi_pd_data->domains[i]);
+		of_node_put(to_scmi_pd(scmi_pd_data->domains[i])->clock_domain);
 	}
 }
 
-- 
2.43.0




More information about the linux-arm-kernel mailing list