blob: 9d5a7ec425b3b43cbbd0514f0d7c11be65fbea50 [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2019 MediaTek Inc.
4 */
5#include <linux/clk.h>
6#include <linux/cpu.h>
7#include <linux/cpufreq.h>
8#include <linux/cpumask.h>
9#include <linux/energy_model.h>
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/of.h>
13#include <linux/of_address.h>
14#include <linux/of_device.h>
15#include <linux/platform_device.h>
16#include <linux/pm_opp.h>
17#include <linux/regulator/consumer.h>
18#include "../misc/mediatek/sspm/sspm_ipi.h"
19
20#define OFFS_WFI_S 0x037c
21#define DVFS_D_LEN (4)
22
23struct mtk_cpu_dvfs_info {
24 struct cpumask cpus;
25 struct clk *cpu_clk;
26 struct device *cpu_dev;
27 struct list_head list_head;
28 struct mutex lock;
29 struct regulator *proc_reg;
30 struct regulator *sram_reg;
31 void __iomem *csram_base;
32};
33
34static LIST_HEAD(dvfs_info_list);
35
36enum cpu_dvfs_ipi_type {
37 IPI_DVFS_INIT,
38 IPI_SET_CLUSTER_ON_OFF,
39 NR_DVFS_IPI,
40};
41
42struct cdvfs_data {
43 unsigned int cmd;
44 union {
45 struct {
46 unsigned int arg[3];
47 } set_fv;
48 } u;
49};
50
51int dvfs_to_spm2_command(u32 cmd, struct cdvfs_data *cdvfs_d)
52{
53 unsigned int len = DVFS_D_LEN;
54 int ack_data;
55 unsigned int ret = 0;
56
57 switch (cmd) {
58 case IPI_DVFS_INIT:
59 cdvfs_d->cmd = cmd;
60 sspm_ipi_send_sync_new(IPI_ID_CPU_DVFS, IPI_OPT_POLLING,
61 cdvfs_d, len, &ack_data, 1);
62 if (ret) {
63 pr_debug("#@# %s(%d) sspm_ipi_send_sync ret %d\n",
64 __func__, __LINE__, ret);
65 } else if (ack_data < 0) {
66 ret = ack_data;
67 pr_debug("#@# %s(%d) cmd(%d) return %d\n",
68 __func__, __LINE__, cmd, ret);
69 }
70 break;
71
72 case IPI_SET_CLUSTER_ON_OFF:
73 cdvfs_d->cmd = cmd;
74 sspm_ipi_send_sync_new(IPI_ID_CPU_DVFS, IPI_OPT_POLLING,
75 cdvfs_d, len, &ack_data, 1);
76 if (ret) {
77 pr_debug("ret = %d, set cluster%d ON/OFF state to %d\n",
78 ret, cdvfs_d->u.set_fv.arg[0],
79 cdvfs_d->u.set_fv.arg[1]);
80 } else if (ack_data < 0) {
81 pr_debug("ret = %d, set cluster%d ON/OFF state to %d\n",
82 ret, cdvfs_d->u.set_fv.arg[0],
83 cdvfs_d->u.set_fv.arg[1]);
84 }
85 break;
86
87 default:
88 break;
89 }
90
91 return ret;
92}
93
94static struct mtk_cpu_dvfs_info *mtk_cpu_dvfs_info_lookup(int cpu)
95{
96 struct mtk_cpu_dvfs_info *info;
97 struct list_head *list;
98
99 list_for_each(list, &dvfs_info_list) {
100 info = list_entry(list, struct mtk_cpu_dvfs_info, list_head);
101 if (cpumask_test_cpu(cpu, &info->cpus))
102 return info;
103 }
104 return NULL;
105}
106
107static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
108 unsigned int index)
109{
110 struct mtk_cpu_dvfs_info *info = policy->driver_data;
111 unsigned int cluster_id = policy->cpu / 6;
112
113 writel_relaxed(index, info->csram_base + (OFFS_WFI_S + (cluster_id * 4))
114 );
115 arch_set_freq_scale(policy->related_cpus,
116 policy->freq_table[index].frequency,
117 policy->cpuinfo.max_freq);
118
119 return 0;
120}
121
122static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
123{
124 struct device *cpu_dev;
125 struct regulator *proc_reg = ERR_PTR(-ENODEV);
126 struct regulator *sram_reg = ERR_PTR(-ENODEV);
127 struct clk *cpu_clk = ERR_PTR(-ENODEV);
128 int ret;
129
130 cpu_dev = get_cpu_device(cpu);
131 if (!cpu_dev)
132 return -ENODEV;
133
134 cpu_clk = devm_clk_get(cpu_dev, "cpu");
135 if (IS_ERR(cpu_clk)) {
136 if (PTR_ERR(cpu_clk) == -EPROBE_DEFER)
137 pr_debug("cpu clk for cpu%d not ready, retry.\n", cpu);
138 else
139 pr_debug("failed to get cpu clk for cpu%d\n", cpu);
140 ret = PTR_ERR(cpu_clk);
141 return ret;
142 }
143
144 ret = clk_prepare_enable(cpu_clk);
145 if (ret)
146 pr_debug("cannot enable parent clock: %d\n", ret);
147
148 proc_reg = regulator_get_optional(cpu_dev, "proc");
149 if (IS_ERR(proc_reg)) {
150 if (PTR_ERR(proc_reg) == -EPROBE_DEFER)
151 pr_debug("proc regulator for cpu%d not ready, retry.\n",
152 cpu);
153 else
154 pr_debug("failed to get proc regulator for cpu%d\n",
155 cpu);
156
157 ret = PTR_ERR(proc_reg);
158 goto out_free_resources;
159 }
160
161 /* Both presence and absence of sram regulator are valid cases. */
162 sram_reg = regulator_get_optional(cpu_dev, "sram");
163
164 /* Get OPP-sharing information from "operating-points-v2" bindings */
165 ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, &info->cpus);
166 if (ret)
167 goto out_free_resources;
168
169 ret = dev_pm_opp_of_cpumask_add_table(&info->cpus);
170 if (ret)
171 goto out_free_resources;
172
173 info->cpu_dev = cpu_dev;
174 info->proc_reg = proc_reg;
175 info->sram_reg = IS_ERR(sram_reg) ? NULL : sram_reg;
176 info->cpu_clk = cpu_clk;
177 mutex_init(&info->lock);
178
179 return 0;
180
181out_free_resources:
182 if (!IS_ERR(proc_reg))
183 regulator_put(proc_reg);
184 if (!IS_ERR(sram_reg))
185 regulator_put(sram_reg);
186 if (!IS_ERR(cpu_clk))
187 clk_put(cpu_clk);
188
189 return ret;
190}
191
192static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info)
193{
194 if (!IS_ERR(info->proc_reg))
195 regulator_put(info->proc_reg);
196 if (!IS_ERR(info->sram_reg))
197 regulator_put(info->sram_reg);
198 if (!IS_ERR(info->cpu_clk))
199 clk_put(info->cpu_clk);
200
201 dev_pm_opp_of_cpumask_remove_table(&info->cpus);
202}
203
204static int mtk_cpufreq_init(struct cpufreq_policy *policy)
205{
206 struct mtk_cpu_dvfs_info *info;
207 struct cdvfs_data cdvfs_d;
208 struct cpufreq_frequency_table *freq_table;
209 struct em_data_callback em_cb = EM_DATA_CB(of_dev_pm_opp_get_cpu_power);
210 int ret;
211
212 info = mtk_cpu_dvfs_info_lookup(policy->cpu);
213 if (!info)
214 return -EINVAL;
215
216 ret = dev_pm_opp_init_cpufreq_table(info->cpu_dev, &freq_table);
217 if (ret)
218 return ret;
219
220 ret = cpufreq_frequency_table_verify(policy, freq_table);
221 if (ret)
222 goto out_free_cpufreq_table;
223
224 ret = dev_pm_opp_get_opp_count(info->cpu_dev);
225 if (ret <= 0) {
226 ret = -EINVAL;
227 goto out_free_opp;
228 }
229
230 cpumask_copy(policy->cpus, &info->cpus);
231 em_register_perf_domain(policy->cpus, ret, &em_cb);
232 policy->driver_data = info;
233 policy->clk = info->cpu_clk;
234 policy->freq_table = freq_table;
235 policy->transition_delay_us = 1000; /* us */
236 /* Cluster, ON:1/OFF:0 */
237 cdvfs_d.u.set_fv.arg[0] = policy->cpu / 6;
238 cdvfs_d.u.set_fv.arg[1] = 1;
239 dvfs_to_spm2_command(IPI_SET_CLUSTER_ON_OFF, &cdvfs_d);
240
241 return 0;
242
243out_free_opp:
244 dev_pm_opp_of_cpumask_remove_table(policy->cpus);
245out_free_cpufreq_table:
246 dev_pm_opp_free_cpufreq_table(info->cpu_dev, &freq_table);
247 return ret;
248}
249
250static int mtk_cpufreq_exit(struct cpufreq_policy *policy)
251{
252 struct cdvfs_data cdvfs_d;
253 struct mtk_cpu_dvfs_info *info = policy->driver_data;
254
255 /* Cluster, ON:1/OFF:0 */
256 cdvfs_d.u.set_fv.arg[0] = policy->cpu / 6;
257 cdvfs_d.u.set_fv.arg[1] = 0;
258 dvfs_to_spm2_command(IPI_SET_CLUSTER_ON_OFF, &cdvfs_d);
259 dev_pm_opp_free_cpufreq_table(info->cpu_dev, &policy->freq_table);
260
261 return 0;
262}
263
264static struct cpufreq_driver mtk_cpufreq_driver = {
265 .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK |
266 CPUFREQ_HAVE_GOVERNOR_PER_POLICY | CPUFREQ_IS_COOLING_DEV,
267 .verify = cpufreq_generic_frequency_table_verify,
268 .target_index = mtk_cpufreq_set_target,
269 .init = mtk_cpufreq_init,
270 .exit = mtk_cpufreq_exit,
271 .name = "mtk-cpufreq",
272 .attr = cpufreq_generic_attr,
273};
274
275static int mtk_cpufreq_probe(struct platform_device *pdev)
276{
277 struct mtk_cpu_dvfs_info *info;
278 struct list_head *list, *tmp;
279 int cpu, ret;
280 struct cdvfs_data cdvfs_d;
281
282 cdvfs_d.u.set_fv.arg[0] = 0;
283 dvfs_to_spm2_command(IPI_DVFS_INIT, &cdvfs_d);
284
285 for_each_possible_cpu(cpu) {
286 info = mtk_cpu_dvfs_info_lookup(cpu);
287 if (info)
288 continue;
289
290 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
291 if (!info) {
292 ret = -ENOMEM;
293 goto release_dvfs_info_list;
294 }
295
296 info->csram_base = of_iomap(pdev->dev.of_node, 0);
297 if (!info->csram_base) {
298 ret = -ENOMEM;
299 goto release_dvfs_info_list;
300 }
301
302 ret = mtk_cpu_dvfs_info_init(info, cpu);
303 if (ret)
304 goto release_dvfs_info_list;
305
306 list_add(&info->list_head, &dvfs_info_list);
307 }
308
309 ret = cpufreq_register_driver(&mtk_cpufreq_driver);
310 if (ret)
311 goto release_dvfs_info_list;
312
313 return 0;
314
315release_dvfs_info_list:
316 list_for_each_safe(list, tmp, &dvfs_info_list) {
317 info = list_entry(list, struct mtk_cpu_dvfs_info, list_head);
318 mtk_cpu_dvfs_info_release(info);
319 list_del(list);
320 }
321
322 return ret;
323}
324
325/* List of machines supported by this driver */
326static const struct of_device_id mtk_cpufreq_machines[] = {
327 { .compatible = "mediatek,sspm-dvfsp", },
328 { }
329};
330
331MODULE_DEVICE_TABLE(of, mtk_cpufreq_machines);
332
333static struct platform_driver mtk_cpufreq_platdrv = {
334 .probe = mtk_cpufreq_probe,
335 .driver = {
336 .name = "dvfsp",
337 .of_match_table = of_match_ptr(mtk_cpufreq_machines),
338 },
339};
340module_platform_driver(mtk_cpufreq_platdrv);
341
342MODULE_AUTHOR("Wei-Chia Su <Wei-Chia.Su@mediatek.com>");
343MODULE_DESCRIPTION("Medaitek SSPM CPUFreq Platform driver");
344MODULE_LICENSE("GPL v2");