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 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-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 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);