[PATCH/RFC 7/9] pmdomain: arm: scmi: Add clock domain support
Geert Uytterhoeven <[email protected]>
| Newsgroups | org.kernel.vger.arm-scmi,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-clk,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pm,org.kernel.vger.linux-renesas-soc |
|---|---|
| Message-ID | <e99b203025504306cd858563d438df32ea47605e.1781171706.git.geert+renesas@glider.be> |
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 <[email protected]> --- 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