From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Patrick Yavitz Date: Fri, 21 Jun 2024 11:54:06 -0400 Subject: add spacemit patch set source: https://gitee.com/bianbu-linux/linux-6.1 Signed-off-by: Patrick Yavitz --- drivers/cpufreq/Kconfig | 11 +- drivers/cpufreq/Makefile | 1 + drivers/cpufreq/cpufreq-dt-platdev.c | 4 + drivers/cpufreq/spacemit-cpufreq.c | 215 ++++++++++ drivers/cpuidle/cpuidle-riscv-sbi.c | 4 + 5 files changed, 234 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 111111111111..222222222222 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -231,7 +231,7 @@ if X86 source "drivers/cpufreq/Kconfig.x86" endif -if ARM || ARM64 +if ARM || ARM64 || RISCV source "drivers/cpufreq/Kconfig.arm" endif @@ -321,5 +321,14 @@ config QORIQ_CPUFREQ This adds the CPUFreq driver support for Freescale QorIQ SoCs which are capable of changing the CPU's frequency dynamically. +config SPACEMIT_K1X_CPUFREQ + tristate "CPU frequency scaling driver for Spacemit K1X" + depends on OF && COMMON_CLK + select CPUFREQ_DT + select CPUFREQ_DT_PLATDEV + help + This adds the CPUFreq driver support for Freescale QorIQ SoCs + which are capable of changing the CPU's frequency dynamically. + endif endmenu diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 111111111111..222222222222 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -114,3 +114,4 @@ obj-$(CONFIG_LOONGSON1_CPUFREQ) += loongson1-cpufreq.o obj-$(CONFIG_SH_CPU_FREQ) += sh-cpufreq.o obj-$(CONFIG_SPARC_US2E_CPUFREQ) += sparc-us2e-cpufreq.o obj-$(CONFIG_SPARC_US3_CPUFREQ) += sparc-us3-cpufreq.o +obj-$(CONFIG_SPACEMIT_K1X_CPUFREQ) += spacemit-cpufreq.o diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index 111111111111..222222222222 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -93,6 +93,8 @@ static const struct of_device_id allowlist[] __initconst = { { .compatible = "xlnx,zynq-7000", }, { .compatible = "xlnx,zynqmp", }, + { .compatible = "spacemit,k1-x", }, + { } }; @@ -168,6 +170,8 @@ static const struct of_device_id blocklist[] __initconst = { { .compatible = "qcom,msm8974", }, { .compatible = "qcom,msm8960", }, + { .compatible = "spacemit,k1-x", }, + { } }; diff --git a/drivers/cpufreq/spacemit-cpufreq.c b/drivers/cpufreq/spacemit-cpufreq.c new file mode 100644 index 000000000000..111111111111 --- /dev/null +++ b/drivers/cpufreq/spacemit-cpufreq.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../opp/opp.h" + +struct per_device_qos { + struct regulator *regulator; + struct freq_qos_request qos; +}; + +static DEFINE_MUTEX(regulator_mutex); +static struct notifier_block vol_constraints_notifier; +static struct freq_constraints vol_constraints; +static struct per_device_qos *vol_qos[CONFIG_NR_CPUS]; + +#ifdef CONFIG_CPU_HOTPLUG_THERMAL +struct thermal_cooling_device **ghotplug_cooling; +extern struct thermal_cooling_device ** +of_hotplug_cooling_register(struct cpufreq_policy *policy); +#endif + +static int spacemit_vol_qos_notifier_call(struct notifier_block *nb, unsigned long action, void *data) +{ + regulator_set_voltage(vol_qos[0]->regulator, action * 1000, action * 1000); + + return 0; +} + +static int spacemit_policy_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + int cpu, err; + u64 rates; + static int cci_init; + struct clk *cci_clk; + struct device *cpu_dev; + struct cpufreq_policy *policy = data; + struct opp_table *opp_table; + const char *strings; + + cpu = cpumask_first(policy->related_cpus); + cpu_dev = get_cpu_device(cpu); + opp_table = _find_opp_table(cpu_dev); + + if (cci_init == 0) { + cci_clk = of_clk_get_by_name(opp_table->np, "cci"); + of_property_read_u64_array(opp_table->np, "cci-hz", &rates, 1); + clk_set_rate(cci_clk, rates); + clk_put(cci_clk); + cci_init = 1; + } + + vol_qos[cpu] = devm_kzalloc(cpu_dev, sizeof(struct per_device_qos), GFP_KERNEL); + if (!vol_qos[cpu]) + return -ENOMEM; + + err = of_property_read_string_array(cpu_dev->of_node, "vin-supply-names", + &strings, 1); + if (err >= 0) { + vol_qos[cpu]->regulator = devm_regulator_get(cpu_dev, strings); + if (IS_ERR(vol_qos[cpu]->regulator)) { + pr_err("regulator supply %s, get failed\n", strings); + return PTR_ERR(vol_qos[cpu]->regulator); + } + + err = regulator_enable(vol_qos[cpu]->regulator); + + } else { + /* using the same regulator */ + vol_qos[cpu]->regulator = vol_qos[0]->regulator; + } + + if (vol_qos[cpu]->regulator) + freq_qos_add_request(&vol_constraints, &vol_qos[cpu]->qos, FREQ_QOS_MIN, + regulator_get_voltage(vol_qos[cpu]->regulator) / 1000); + +#ifdef CONFIG_CPU_HOTPLUG_THERMAL + ghotplug_cooling = of_hotplug_cooling_register(policy); + if (!ghotplug_cooling) { + pr_err("register hotplug cpu cooling failed\n"); + return -EINVAL; + } +#endif + return 0; +} + +static int spacemit_processor_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + int cpu; + struct device *cpu_dev; + struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)data; + struct cpufreq_policy *policy = ( struct cpufreq_policy *)freqs->policy; + struct opp_table *opp_table; + struct device_node *np; + struct clk *tcm_clk, *ace_clk; + u64 rates; + u32 microvol; + + cpu = cpumask_first(policy->related_cpus); + cpu_dev = get_cpu_device(cpu); + opp_table = _find_opp_table(cpu_dev); + + for_each_available_child_of_node(opp_table->np, np) { + of_property_read_u64_array(np, "opp-hz", &rates, 1); + if (rates == freqs->new * 1000) { + of_property_read_u32(np, "opp-microvolt", µvol); + break; + } + } + + /* get the tcm/ace clk handler */ + tcm_clk = of_clk_get_by_name(opp_table->np, "tcm"); + ace_clk = of_clk_get_by_name(opp_table->np, "ace"); + + if (event == CPUFREQ_PRECHANGE) { + + mutex_lock(®ulator_mutex); + + if (freqs->new > freqs->old) { + /* increase voltage first */ + if (vol_qos[cpu]->regulator) + freq_qos_update_request(&vol_qos[cpu]->qos, microvol / 1000); + } + + /** + * change the tcm/ace's frequency first. + * binary division is safe + */ + if (!IS_ERR(ace_clk)) { + clk_set_rate(ace_clk, clk_get_rate(clk_get_parent(ace_clk)) / 2); + clk_put(ace_clk); + } + + if (!IS_ERR(tcm_clk)) { + clk_set_rate(tcm_clk, clk_get_rate(clk_get_parent(tcm_clk)) / 2); + clk_put(tcm_clk); + } + } + + if (event == CPUFREQ_POSTCHANGE) { + + if (!IS_ERR(tcm_clk)) { + clk_get_rate(clk_get_parent(tcm_clk)); + /* get the tcm-hz */ + of_property_read_u64_array(np, "tcm-hz", &rates, 1); + /* then set rate */ + clk_set_rate(tcm_clk, rates); + clk_put(tcm_clk); + } + + if (!IS_ERR(ace_clk)) { + clk_get_rate(clk_get_parent(ace_clk)); + /* get the ace-hz */ + of_property_read_u64_array(np, "ace-hz", &rates, 1); + /* then set rate */ + clk_set_rate(ace_clk, rates); + clk_put(ace_clk); + } + + if (freqs->new < freqs->old) { + /* decrease the voltage last */ + if (vol_qos[cpu]->regulator) + freq_qos_update_request(&vol_qos[cpu]->qos, microvol / 1000); + } + + mutex_unlock(®ulator_mutex); + } + + dev_pm_opp_put_opp_table(opp_table); + + return 0; +} + +static struct notifier_block spacemit_processor_notifier_block = { + .notifier_call = spacemit_processor_notifier, +}; + +static struct notifier_block spacemit_policy_notifier_block = { + .notifier_call = spacemit_policy_notifier, +}; + +static int __init spacemit_processor_driver_init(void) +{ + int ret; + + ret = cpufreq_register_notifier(&spacemit_processor_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); + if (ret) { + pr_err("register cpufreq notifier failed\n"); + return -EINVAL; + } + + ret = cpufreq_register_notifier(&spacemit_policy_notifier_block, CPUFREQ_POLICY_NOTIFIER); + if (ret) { + pr_err("register cpufreq notifier failed\n"); + return -EINVAL; + } + + vol_constraints_notifier.notifier_call = spacemit_vol_qos_notifier_call; + freq_constraints_init(&vol_constraints); + freq_qos_add_notifier(&vol_constraints, FREQ_QOS_MIN, &vol_constraints_notifier); + + return 0; +} + +arch_initcall(spacemit_processor_driver_init); diff --git a/drivers/cpuidle/cpuidle-riscv-sbi.c b/drivers/cpuidle/cpuidle-riscv-sbi.c index 111111111111..222222222222 100644 --- a/drivers/cpuidle/cpuidle-riscv-sbi.c +++ b/drivers/cpuidle/cpuidle-riscv-sbi.c @@ -79,6 +79,10 @@ static int sbi_suspend_finisher(unsigned long suspend_type, { struct sbiret ret; +#if defined(CONFIG_SOC_SPACEMIT_K1PRO) || defined(CONFIG_SOC_SPACEMIT_K1X) + /* flush the local cache */ + sbi_flush_local_dcache_all(); +#endif ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_SUSPEND, suspend_type, resume_addr, opaque, 0, 0, 0); -- Armbian