From 2bfac76fb01491cf4f272897e1cc91621fbe96d1 Mon Sep 17 00:00:00 2001 From: Nell Date: Wed, 3 Jan 2024 22:25:56 +0800 Subject: [PATCH] k1x:cpufreq: support adjust the ace/tcm's frequency accroding to cpu by the way, set the frequency of cci to 614M by default Change-Id: Ie375efff2c4432426a44d8a5a7406aa7904abd74 --- arch/riscv/boot/dts/spacemit/k1-x.dtsi | 23 +++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/spacemit-cpufreq.c | 185 +++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 drivers/cpufreq/spacemit-cpufreq.c diff --git a/arch/riscv/boot/dts/spacemit/k1-x.dtsi b/arch/riscv/boot/dts/spacemit/k1-x.dtsi index 41645a0e588c39..94e43259e0b710 100644 --- a/arch/riscv/boot/dts/spacemit/k1-x.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1-x.dtsi @@ -298,8 +298,14 @@ compatible = "operating-points-v2"; opp-shared; + clocks = <&ccu CLK_CPU_C0_ACE>, <&ccu CLK_CPU_C0_TCM>, <&ccu CLK_CCI550>; + clock-names = "ace","tcm", "cci"; + cci-hz = /bits/ 64 <614000000>; + opp614400000 { opp-hz = /bits/ 64 <614400000>; + tcm-hz = /bits/ 64 <307200000>; + ace-hz = /bits/ 64 <307200000>; opp-microvolt = <950000>; clock-latency-ns = <200000>; }; @@ -307,23 +313,31 @@ opp819000000 { opp-hz = /bits/ 64 <819000000>; opp-microvolt = <950000>; + tcm-hz = /bits/ 64 <409500000>; + ace-hz = /bits/ 64 <409500000>; clock-latency-ns = <200000>; }; opp1000000000 { opp-hz = /bits/ 64 <1000000000>; + tcm-hz = /bits/ 64 <500000000>; + ace-hz = /bits/ 64 <500000000>; opp-microvolt = <950000>; clock-latency-ns = <200000>; }; opp1228800000 { opp-hz = /bits/ 64 <1228800000>; + tcm-hz = /bits/ 64 <614400000>; + ace-hz = /bits/ 64 <614400000>; opp-microvolt = <950000>; clock-latency-ns = <200000>; }; opp1600000000 { opp-hz = /bits/ 64 <1600000000>; + tcm-hz = /bits/ 64 <800000000>; + ace-hz = /bits/ 64 <800000000>; opp-microvolt = <950000>; clock-latency-ns = <200000>; }; @@ -333,32 +347,41 @@ compatible = "operating-points-v2"; opp-shared; + clocks = <&ccu CLK_CPU_C1_ACE>, <&ccu CLK_CCI550>; + clock-names = "ace", "cci"; + cci-hz = /bits/ 64 <614000000>; + opp614400000 { opp-hz = /bits/ 64 <614400000>; + ace-hz = /bits/ 64 <307200000>; opp-microvolt = <950000>; clock-latency-ns = <200000>; }; opp819000000 { opp-hz = /bits/ 64 <819000000>; + ace-hz = /bits/ 64 <409500000>; opp-microvolt = <950000>; clock-latency-ns = <200000>; }; opp1000000000 { opp-hz = /bits/ 64 <1000000000>; + ace-hz = /bits/ 64 <500000000>; opp-microvolt = <950000>; clock-latency-ns = <200000>; }; opp1228800000 { opp-hz = /bits/ 64 <1228800000>; + ace-hz = /bits/ 64 <614400000>; opp-microvolt = <950000>; clock-latency-ns = <200000>; }; opp1600000000 { opp-hz = /bits/ 64 <1600000000>; + ace-hz = /bits/ 64 <800000000>; opp-microvolt = <950000>; clock-latency-ns = <200000>; }; diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index d35a28dd946307..9100a37277bbe9 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -107,3 +107,4 @@ obj-$(CONFIG_LOONGSON3_CPUFREQ) += loongson3_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/spacemit-cpufreq.c b/drivers/cpufreq/spacemit-cpufreq.c new file mode 100644 index 00000000000000..ee1109b99cf20d --- /dev/null +++ b/drivers/cpufreq/spacemit-cpufreq.c @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "../opp/opp.h" + +#if 0 +#define CCI_CLK_DEFAULT_RATE 245760 + +struct per_dev_qos { + struct clk *clk; + struct notifier_block notifier; + struct dev_pm_qos_request req; +}; + +static struct per_dev_qos *clk_dev_qos; + +static int spacemit_handle_clk_max_notifier_call(struct notifier_block *nb, unsigned long action, void *data) +{ + struct per_dev_qos *per_qos = container_of(nb, struct per_dev_qos, notifier); + + clk_set_rate(per_qos->clk, action * 1000); + + return 0; +} +#endif + +static int spacemit_policy_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + int cpu; + u64 rates; + static int cci_init; + struct clk *cci_clk; + struct device *cpu_dev; + struct cpufreq_policy *policy = data; + struct opp_table *opp_table; + + 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; + } + + 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; + + cpu = cpumask_first(policy->related_cpus); + cpu_dev = get_cpu_device(cpu); + opp_table = _find_opp_table(cpu_dev); + +#if 0 + if (clk_dev_qos == NULL) { + clk_dev_qos = (struct per_dev_qos *)devm_kzalloc(cpu_dev, sizeof(struct per_dev_qos), GFP_KERNEL); + if (!clk_dev_qos) { + pr_err(" allocate per device qos error\n"); + return -ENOMEM; + } + + clk_dev_qos->clk = of_clk_get_by_name(opp_table->np, "cci"); + clk_dev_qos->notifier.notifier_call = spacemit_handle_clk_max_notifier_call; + dev_pm_qos_add_notifier(get_cpu_device(0), &clk_dev_qos->notifier, DEV_PM_QOS_MAX_FREQUENCY); + dev_pm_qos_add_request(get_cpu_device(0), &clk_dev_qos->req, DEV_PM_QOS_MAX_FREQUENCY, CCI_CLK_DEFAULT_RATE); + } +#endif + 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) + break; + } +#if 0 + if (event == CPUFREQ_PRECHANGE) { + /* decrease the freqs */ + if (freqs->old > freqs->new) { + /* decrease the cci first */ + of_property_read_u64_array(np, "cci-hz", &rates, 1); + dev_pm_qos_update_request(&clk_dev_qos->req, rates / 1000); + } + } +#endif + + /* 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) { + /** + * 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 0 + /* increase the freqs */ + if (freqs->old < freqs->new) { + /* increase the cci after */ + of_property_read_u64_array(np, "cci-hz", &rates, 1); + dev_pm_qos_update_request(&clk_dev_qos->req, rates / 1000); + } +#endif + } + + 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; + } + + return 0; +} + +arch_initcall(spacemit_processor_driver_init);