From 0f81250fc653b3f177796eb7241f16e89ddc09d5 Mon Sep 17 00:00:00 2001 From: Nick Hu Date: Wed, 11 Jun 2025 11:10:20 +0800 Subject: [PATCH 1/3] dt-bindings: power: Add SiFive Domain Management controllers SiFive Domain Management controller includes the following components - SiFive Tile Management Controller - SiFive Cluster Management Controller - SiFive Core Complex Management Controller These controllers control the clock and power domain of the corresponding domain. However, Since we don't have a SoC specific compatible string yet, so add '- {}' for the first entry [1][2]. Links: - [1] https://lore.kernel.org/lkml/20250311195953.GA14239-robh@kernel.org/ - [2] https://lore.kernel.org/lkml/CAKddAkAzDGL-7MbroRqQnZzPXOquUMKNuGGppqB-d_XZXbcvBA@mail.gmail.com/T/#t Signed-off-by: Nick Hu Reviewed-by: Samuel Holland Signed-off-by: Linux RISC-V bot --- .../devicetree/bindings/power/sifive,tmc.yaml | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/sifive,tmc.yaml diff --git a/Documentation/devicetree/bindings/power/sifive,tmc.yaml b/Documentation/devicetree/bindings/power/sifive,tmc.yaml new file mode 100644 index 00000000000000..4567d51148e1c1 --- /dev/null +++ b/Documentation/devicetree/bindings/power/sifive,tmc.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/sifive,tmc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SiFive Domain Management Controller + +maintainers: + - Cyan Yang + - Nick Hu + - Samuel Holland + +description: | + SiFive Domain Management Controllers includes the following components + - Tile Management Controller (TMC) + - Cluster Management Controller (CMC) + - Subsystem Management Controller (SMC) + These controllers manage both the clock and power domains of the + associated components. They support the SiFive Quiet Interface Protocol + (SQIP) starting from Version 1. The control method differs from Version + 0, making them incompatible. + +allOf: + - $ref: power-domain.yaml# + +properties: + compatible: + oneOf: + - items: + - {} # Leave a empty for future SoC specific compatible string + - const: sifive,cmc3 + - const: sifive,cmc2 + - items: + - {} # Leave a empty for future SoC specific compatible string + - const: sifive,cmc2 + - items: + - {} # Leave a empty for future SoC specific compatible string + - const: sifive,smc0 + - items: + - {} # Leave a empty for future SoC specific compatible string + - const: sifive,smc1 + - items: + - {} # Leave a empty for future SoC specific compatible string + - const: sifive,smc3 + - const: sifive,smc1 + - items: + - {} # Leave a empty for future SoC specific compatible string + - const: sifive,tmc0 + - items: + - {} # Leave a empty for future SoC specific compatible string + - const: sifive,tmc1 + - items: + - {} # Leave a empty for future SoC specific compatible string + - const: sifive,tmc3 + - const: sifive,tmc1 + + reg: + maxItems: 1 + + "#power-domain-cells": + const: 0 + +required: + - compatible + - reg + +additionalProperties: false + +# The example will be added once the SoC specific compatible string is available From 3e4a303bdd762f2b57c16c20622647c89f127301 Mon Sep 17 00:00:00 2001 From: Nick Hu Date: Wed, 11 Jun 2025 11:10:21 +0800 Subject: [PATCH 2/3] cpuidle: riscv-sbi: Work with the external pmdomain driver To work with the external pmdomain driver, exposing the `sbi_cpuidle_pd_power_off` and `sbi_dt_parse_state_node` so the external pmdomain driver can parse the riscv idle state data and set the domain idle state where powering off. In addition, defer the driver without error message when the parent domain is not ready. Co-developed-by: Samuel Holland Signed-off-by: Samuel Holland Signed-off-by: Nick Hu Signed-off-by: Linux RISC-V bot --- drivers/cpuidle/cpuidle-riscv-sbi.c | 17 +++++++++++------ drivers/cpuidle/cpuidle-riscv-sbi.h | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 drivers/cpuidle/cpuidle-riscv-sbi.h diff --git a/drivers/cpuidle/cpuidle-riscv-sbi.c b/drivers/cpuidle/cpuidle-riscv-sbi.c index 0fe1ece9fbdca4..d53254601522a2 100644 --- a/drivers/cpuidle/cpuidle-riscv-sbi.c +++ b/drivers/cpuidle/cpuidle-riscv-sbi.c @@ -27,6 +27,7 @@ #include #include "cpuidle.h" +#include "cpuidle-riscv-sbi.h" #include "dt_idle_states.h" #include "dt_idle_genpd.h" @@ -188,7 +189,7 @@ static const struct of_device_id sbi_cpuidle_state_match[] = { { }, }; -static int sbi_dt_parse_state_node(struct device_node *np, u32 *state) +int sbi_dt_parse_state_node(struct device_node *np, u32 *state) { int err = of_property_read_u32(np, "riscv,sbi-suspend-param", state); @@ -325,8 +326,9 @@ static int sbi_cpuidle_init_cpu(struct device *dev, int cpu) /* Initialize idle states from DT. */ ret = sbi_cpuidle_dt_init_states(dev, drv, cpu, state_count); if (ret) { - pr_err("HART%ld: failed to init idle states\n", - cpuid_to_hartid_map(cpu)); + if (ret != -EPROBE_DEFER) + pr_err("HART%ld: failed to init idle states\n", + cpuid_to_hartid_map(cpu)); return ret; } @@ -356,7 +358,7 @@ static void sbi_cpuidle_domain_sync_state(struct device *dev) #ifdef CONFIG_DT_IDLE_GENPD -static int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd) +int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd) { struct genpd_power_state *state = &pd->states[pd->state_idx]; u32 *pd_state; @@ -533,8 +535,11 @@ static int sbi_cpuidle_probe(struct platform_device *pdev) for_each_present_cpu(cpu) { ret = sbi_cpuidle_init_cpu(&pdev->dev, cpu); if (ret) { - pr_debug("HART%ld: idle driver init failed\n", - cpuid_to_hartid_map(cpu)); + if (ret == -EPROBE_DEFER) + pr_debug("idle driver probe deferred\n"); + else + pr_debug("HART%ld: idle driver init failed\n", + cpuid_to_hartid_map(cpu)); goto out_fail; } } diff --git a/drivers/cpuidle/cpuidle-riscv-sbi.h b/drivers/cpuidle/cpuidle-riscv-sbi.h new file mode 100644 index 00000000000000..f39186b65a1098 --- /dev/null +++ b/drivers/cpuidle/cpuidle-riscv-sbi.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __CPUIDLE_RISCV_SBI +#define __CPUIDLE_RISCV_SBI + +#ifdef CONFIG_DT_IDLE_GENPD + +int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd); + +#else + +static inline int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd) +{ + return 0; +} + +#endif + +int sbi_dt_parse_state_node(struct device_node *np, u32 *state); + +#endif From 4d9dedfbab51e819881e1e06be71dbed15780b55 Mon Sep 17 00:00:00 2001 From: Nick Hu Date: Wed, 11 Jun 2025 11:10:22 +0800 Subject: [PATCH 3/3] cpuidle: Add SiFive power provider The SiFive DMC is the power provider of the devices that inside the SiFive CPU power domains, which include Tile, Cluster and Core Complex power domains. Before the cpu entering the firmware-based idle state, each devices that inside the corresponding domain should be suspended properly. So this driver will create the power provider and set the correct idle state. Signed-off-by: Nick Hu Signed-off-by: Linux RISC-V bot --- drivers/cpuidle/Kconfig.riscv | 11 +++ drivers/cpuidle/Makefile | 1 + drivers/cpuidle/cpuidle-sifive-dmc-pd.c | 102 ++++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 drivers/cpuidle/cpuidle-sifive-dmc-pd.c diff --git a/drivers/cpuidle/Kconfig.riscv b/drivers/cpuidle/Kconfig.riscv index 78518c26af74ca..af802afefa2105 100644 --- a/drivers/cpuidle/Kconfig.riscv +++ b/drivers/cpuidle/Kconfig.riscv @@ -13,3 +13,14 @@ config RISCV_SBI_CPUIDLE Select this option to enable RISC-V SBI firmware based CPU idle driver for RISC-V systems. This drivers also supports hierarchical DT based layout of the idle state. + +config SIFIVE_DMC_PD_CPUIDLE + bool "SiFive DMC SBI PD Provider Driver" + depends on ARCH_SIFIVE + select PM_GENERIC_DOMAINS_OF + select RISCV_SBI_CPUIDLE + default y + help + Select this option to enable SiFive DMC SBI PD Provider driver. + This driver will create the genpd provider and work with the + RISC-V SBI firmware based CPU idle driver. diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 1de9e92c5b0fc9..1f8e01b415e82c 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -42,3 +42,4 @@ obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o ############################################################################### # RISC-V drivers obj-$(CONFIG_RISCV_SBI_CPUIDLE) += cpuidle-riscv-sbi.o +obj-$(CONFIG_SIFIVE_DMC_PD_CPUIDLE) += cpuidle-sifive-dmc-pd.o diff --git a/drivers/cpuidle/cpuidle-sifive-dmc-pd.c b/drivers/cpuidle/cpuidle-sifive-dmc-pd.c new file mode 100644 index 00000000000000..1c6b2131e5736a --- /dev/null +++ b/drivers/cpuidle/cpuidle-sifive-dmc-pd.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SiFive CPUIDLE SBI PD driver + */ + +#define pr_fmt(fmt) "sifive_cpuidle_sbi_pd: " fmt + +#include +#include +#include +#include + +#include "cpuidle-riscv-sbi.h" +#include "dt_idle_genpd.h" + +static void sifive_dmc_remove(struct platform_device *pdev) +{ + struct generic_pm_domain *pd = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + pm_runtime_disable(dev); + of_genpd_del_provider(dev->of_node); + pm_genpd_remove(pd); + dt_idle_pd_free(pd); +} + +static int sifive_dmc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct generic_pm_domain *pd; + struct of_phandle_args child, parent; + int ret = -ENOMEM; + + pd = dt_idle_pd_alloc(np, sbi_dt_parse_state_node); + if (!pd) + goto fail; + + pd->flags |= GENPD_FLAG_IRQ_SAFE | GENPD_FLAG_CPU_DOMAIN; + pd->power_off = sbi_cpuidle_pd_power_off; + + ret = pm_genpd_init(pd, &pm_domain_cpu_gov, false); + if (ret) + goto free_pd; + + ret = of_genpd_add_provider_simple(np, pd); + if (ret) + goto remove_pd; + + if (of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", 0, + &parent) == 0) { + child.np = np; + child.args_count = 0; + + if (of_genpd_add_subdomain(&parent, &child)) + pr_warn("%pOF failed to add subdomain: %pOF\n", + parent.np, child.np); + else + pr_debug("%pOF has a child subdomain: %pOF.\n", + parent.np, child.np); + } + + platform_set_drvdata(pdev, pd); + pm_runtime_enable(dev); + pr_info("%s create success\n", pd->name); + return 0; + +remove_pd: + pm_genpd_remove(pd); +free_pd: + dt_idle_pd_free(pd); +fail: + pr_info("%s create fail\n", pd->name); + + return ret; +} + +static const struct of_device_id sifive_dmc_of_match[] = { + { .compatible = "sifive,tmc1", }, + { .compatible = "sifive,tmc0", }, + { .compatible = "sifive,smc1", }, + { .compatible = "sifive,smc0", }, + { .compatible = "sifive,cmc2", }, + {} +}; + +static struct platform_driver sifive_dmc_driver = { + .probe = sifive_dmc_probe, + .remove = sifive_dmc_remove, + .driver = { + .name = "sifive_dmc", + .of_match_table = sifive_dmc_of_match, + .suppress_bind_attrs = true, + }, +}; + +static int __init sifive_dmc_init(void) +{ + return platform_driver_register(&sifive_dmc_driver); +} +arch_initcall(sifive_dmc_init);