1 From 01be227eff7e5fc01f7c8de8f6daddd5fb17ddd1 Mon Sep 17 00:00:00 2001
2 From: Rex-BC Chen <rex-bc.chen@mediatek.com>
3 Date: Thu, 5 May 2022 19:52:21 +0800
4 Subject: [PATCH 11/21] cpufreq: mediatek: Add opp notification support
6 From this opp notifier, cpufreq should listen to opp notification and do
7 proper actions when receiving events of disable and voltage adjustment.
9 One of the user for this opp notifier is MediaTek SVS.
10 The MediaTek Smart Voltage Scaling (SVS) is a hardware which calculates
11 suitable SVS bank voltages to OPP voltage table.
13 Signed-off-by: Andrew-sh.Cheng <andrew-sh.cheng@mediatek.com>
14 Signed-off-by: Jia-Wei Chang <jia-wei.chang@mediatek.com>
15 Signed-off-by: Rex-BC Chen <rex-bc.chen@mediatek.com>
16 Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
17 [ Viresh: Renamed opp_freq as current_freq and moved its initialization ]
18 Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
20 drivers/cpufreq/mediatek-cpufreq.c | 90 +++++++++++++++++++++++++++---
21 1 file changed, 82 insertions(+), 8 deletions(-)
23 --- a/drivers/cpufreq/mediatek-cpufreq.c
24 +++ b/drivers/cpufreq/mediatek-cpufreq.c
25 @@ -46,6 +46,11 @@ struct mtk_cpu_dvfs_info {
26 int intermediate_voltage;
27 bool need_voltage_tracking;
29 + /* Avoid race condition for regulators between notify and policy */
30 + struct mutex reg_lock;
31 + struct notifier_block opp_nb;
32 + unsigned int opp_cpu;
33 + unsigned long current_freq;
34 const struct mtk_cpufreq_platform_data *soc_data;
37 @@ -182,6 +187,8 @@ static int mtk_cpufreq_set_target(struct
39 pre_freq_hz = clk_get_rate(cpu_clk);
41 + mutex_lock(&info->reg_lock);
43 if (unlikely(info->pre_vproc <= 0))
44 pre_vproc = regulator_get_voltage(info->proc_reg);
46 @@ -214,7 +221,7 @@ static int mtk_cpufreq_set_target(struct
48 "cpu%d: failed to scale up voltage!\n", policy->cpu);
49 mtk_cpufreq_set_voltage(info, pre_vproc);
55 @@ -224,8 +231,7 @@ static int mtk_cpufreq_set_target(struct
57 "cpu%d: failed to re-parent cpu clock!\n", policy->cpu);
58 mtk_cpufreq_set_voltage(info, pre_vproc);
64 /* Set the original PLL to target rate. */
65 @@ -235,7 +241,7 @@ static int mtk_cpufreq_set_target(struct
66 "cpu%d: failed to scale cpu clock rate!\n", policy->cpu);
67 clk_set_parent(cpu_clk, armpll);
68 mtk_cpufreq_set_voltage(info, pre_vproc);
73 /* Set parent of CPU clock back to the original PLL. */
74 @@ -244,8 +250,7 @@ static int mtk_cpufreq_set_target(struct
76 "cpu%d: failed to re-parent cpu clock!\n", policy->cpu);
77 mtk_cpufreq_set_voltage(info, inter_vproc);
84 @@ -260,15 +265,72 @@ static int mtk_cpufreq_set_target(struct
85 clk_set_parent(cpu_clk, info->inter_clk);
86 clk_set_rate(armpll, pre_freq_hz);
87 clk_set_parent(cpu_clk, armpll);
94 + info->current_freq = freq_hz;
97 + mutex_unlock(&info->reg_lock);
102 #define DYNAMIC_POWER "dynamic-power-coefficient"
104 +static int mtk_cpufreq_opp_notifier(struct notifier_block *nb,
105 + unsigned long event, void *data)
107 + struct dev_pm_opp *opp = data;
108 + struct dev_pm_opp *new_opp;
109 + struct mtk_cpu_dvfs_info *info;
110 + unsigned long freq, volt;
111 + struct cpufreq_policy *policy;
114 + info = container_of(nb, struct mtk_cpu_dvfs_info, opp_nb);
116 + if (event == OPP_EVENT_ADJUST_VOLTAGE) {
117 + freq = dev_pm_opp_get_freq(opp);
119 + mutex_lock(&info->reg_lock);
120 + if (info->current_freq == freq) {
121 + volt = dev_pm_opp_get_voltage(opp);
122 + ret = mtk_cpufreq_set_voltage(info, volt);
124 + dev_err(info->cpu_dev,
125 + "failed to scale voltage: %d\n", ret);
127 + mutex_unlock(&info->reg_lock);
128 + } else if (event == OPP_EVENT_DISABLE) {
129 + freq = dev_pm_opp_get_freq(opp);
131 + /* case of current opp item is disabled */
132 + if (info->current_freq == freq) {
134 + new_opp = dev_pm_opp_find_freq_ceil(info->cpu_dev,
136 + if (IS_ERR(new_opp)) {
137 + dev_err(info->cpu_dev,
138 + "all opp items are disabled\n");
139 + ret = PTR_ERR(new_opp);
140 + return notifier_from_errno(ret);
143 + dev_pm_opp_put(new_opp);
144 + policy = cpufreq_cpu_get(info->opp_cpu);
146 + cpufreq_driver_target(policy, freq / 1000,
147 + CPUFREQ_RELATION_L);
148 + cpufreq_cpu_put(policy);
153 + return notifier_from_errno(ret);
156 static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
158 struct device *cpu_dev;
159 @@ -357,6 +419,17 @@ static int mtk_cpu_dvfs_info_init(struct
160 info->intermediate_voltage = dev_pm_opp_get_voltage(opp);
163 + mutex_init(&info->reg_lock);
164 + info->current_freq = clk_get_rate(info->cpu_clk);
166 + info->opp_cpu = cpu;
167 + info->opp_nb.notifier_call = mtk_cpufreq_opp_notifier;
168 + ret = dev_pm_opp_register_notifier(cpu_dev, &info->opp_nb);
170 + dev_err(cpu_dev, "cpu%d: failed to register opp notifier\n", cpu);
171 + goto out_disable_inter_clock;
175 * If SRAM regulator is present, software "voltage tracking" is needed
176 * for this CPU power domain.
177 @@ -421,6 +494,7 @@ static void mtk_cpu_dvfs_info_release(st
180 dev_pm_opp_of_cpumask_remove_table(&info->cpus);
181 + dev_pm_opp_unregister_notifier(info->cpu_dev, &info->opp_nb);
184 static int mtk_cpufreq_init(struct cpufreq_policy *policy)