cpufreq: qcom-hw: Use of_device_get_match_data for offsets and row size
[linux-block.git] / drivers / cpufreq / qcom-cpufreq-hw.c
CommitLineData
2849dd8b
TD
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
4 */
5
6#include <linux/bitfield.h>
7#include <linux/cpufreq.h>
8#include <linux/init.h>
51c843cf 9#include <linux/interconnect.h>
2849dd8b
TD
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/of_address.h>
13#include <linux/of_platform.h>
55538fbc 14#include <linux/pm_opp.h>
2849dd8b
TD
15#include <linux/slab.h>
16
17#define LUT_MAX_ENTRIES 40U
18#define LUT_SRC GENMASK(31, 30)
19#define LUT_L_VAL GENMASK(7, 0)
20#define LUT_CORE_COUNT GENMASK(18, 16)
55538fbc 21#define LUT_VOLT GENMASK(11, 0)
2849dd8b 22#define CLK_HW_DIV 2
0eae1e37 23#define LUT_TURBO_IND 1
2849dd8b 24
dcd1fd72
MS
25struct qcom_cpufreq_soc_data {
26 u32 reg_enable;
27 u32 reg_freq_lut;
28 u32 reg_volt_lut;
29 u32 reg_perf_state;
30 u8 lut_row_size;
31};
32
33struct qcom_cpufreq_data {
34 void __iomem *base;
35 const struct qcom_cpufreq_soc_data *soc_data;
36};
2849dd8b
TD
37
38static unsigned long cpu_hw_rate, xo_rate;
51c843cf
SS
39static bool icc_scaling_enabled;
40
41static int qcom_cpufreq_set_bw(struct cpufreq_policy *policy,
42 unsigned long freq_khz)
43{
44 unsigned long freq_hz = freq_khz * 1000;
45 struct dev_pm_opp *opp;
46 struct device *dev;
47 int ret;
48
49 dev = get_cpu_device(policy->cpu);
50 if (!dev)
51 return -ENODEV;
52
53 opp = dev_pm_opp_find_freq_exact(dev, freq_hz, true);
54 if (IS_ERR(opp))
55 return PTR_ERR(opp);
56
57 ret = dev_pm_opp_set_bw(dev, opp);
58 dev_pm_opp_put(opp);
59 return ret;
60}
61
62static int qcom_cpufreq_update_opp(struct device *cpu_dev,
63 unsigned long freq_khz,
64 unsigned long volt)
65{
66 unsigned long freq_hz = freq_khz * 1000;
67 int ret;
68
69 /* Skip voltage update if the opp table is not available */
70 if (!icc_scaling_enabled)
71 return dev_pm_opp_add(cpu_dev, freq_hz, volt);
72
73 ret = dev_pm_opp_adjust_voltage(cpu_dev, freq_hz, volt, volt, volt);
74 if (ret) {
75 dev_err(cpu_dev, "Voltage update failed freq=%ld\n", freq_khz);
76 return ret;
77 }
78
79 return dev_pm_opp_enable(cpu_dev, freq_hz);
80}
2849dd8b
TD
81
82static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy,
83 unsigned int index)
84{
dcd1fd72
MS
85 struct qcom_cpufreq_data *data = policy->driver_data;
86 const struct qcom_cpufreq_soc_data *soc_data = data->soc_data;
ada54f35 87 unsigned long freq = policy->freq_table[index].frequency;
2849dd8b 88
dcd1fd72 89 writel_relaxed(index, data->base + soc_data->reg_perf_state);
2849dd8b 90
51c843cf
SS
91 if (icc_scaling_enabled)
92 qcom_cpufreq_set_bw(policy, freq);
93
ada54f35
DR
94 arch_set_freq_scale(policy->related_cpus, freq,
95 policy->cpuinfo.max_freq);
2849dd8b
TD
96 return 0;
97}
98
99static unsigned int qcom_cpufreq_hw_get(unsigned int cpu)
100{
dcd1fd72
MS
101 struct qcom_cpufreq_data *data;
102 const struct qcom_cpufreq_soc_data *soc_data;
2849dd8b
TD
103 struct cpufreq_policy *policy;
104 unsigned int index;
105
106 policy = cpufreq_cpu_get_raw(cpu);
107 if (!policy)
108 return 0;
109
dcd1fd72
MS
110 data = policy->driver_data;
111 soc_data = data->soc_data;
2849dd8b 112
dcd1fd72 113 index = readl_relaxed(data->base + soc_data->reg_perf_state);
2849dd8b
TD
114 index = min(index, LUT_MAX_ENTRIES - 1);
115
116 return policy->freq_table[index].frequency;
117}
118
119static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy,
120 unsigned int target_freq)
121{
dcd1fd72
MS
122 struct qcom_cpufreq_data *data = policy->driver_data;
123 const struct qcom_cpufreq_soc_data *soc_data = data->soc_data;
292072c3 124 unsigned int index;
ada54f35 125 unsigned long freq;
2849dd8b
TD
126
127 index = policy->cached_resolved_idx;
dcd1fd72 128 writel_relaxed(index, data->base + soc_data->reg_perf_state);
2849dd8b 129
ada54f35
DR
130 freq = policy->freq_table[index].frequency;
131 arch_set_freq_scale(policy->related_cpus, freq,
132 policy->cpuinfo.max_freq);
133
134 return freq;
2849dd8b
TD
135}
136
55538fbc 137static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
dcd1fd72 138 struct cpufreq_policy *policy)
2849dd8b 139{
0eae1e37 140 u32 data, src, lval, i, core_count, prev_freq = 0, freq;
55538fbc 141 u32 volt;
2849dd8b 142 struct cpufreq_frequency_table *table;
51c843cf
SS
143 struct dev_pm_opp *opp;
144 unsigned long rate;
145 int ret;
dcd1fd72
MS
146 struct qcom_cpufreq_data *drv_data = policy->driver_data;
147 const struct qcom_cpufreq_soc_data *soc_data = drv_data->soc_data;
2849dd8b
TD
148
149 table = kcalloc(LUT_MAX_ENTRIES + 1, sizeof(*table), GFP_KERNEL);
150 if (!table)
151 return -ENOMEM;
152
51c843cf
SS
153 ret = dev_pm_opp_of_add_table(cpu_dev);
154 if (!ret) {
155 /* Disable all opps and cross-validate against LUT later */
156 icc_scaling_enabled = true;
157 for (rate = 0; ; rate++) {
158 opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
159 if (IS_ERR(opp))
160 break;
161
162 dev_pm_opp_put(opp);
163 dev_pm_opp_disable(cpu_dev, rate);
164 }
165 } else if (ret != -ENODEV) {
166 dev_err(cpu_dev, "Invalid opp table in device tree\n");
167 return ret;
168 } else {
afdb219b 169 policy->fast_switch_possible = true;
51c843cf
SS
170 icc_scaling_enabled = false;
171 }
172
2849dd8b 173 for (i = 0; i < LUT_MAX_ENTRIES; i++) {
dcd1fd72
MS
174 data = readl_relaxed(drv_data->base + soc_data->reg_freq_lut +
175 i * soc_data->lut_row_size);
2849dd8b
TD
176 src = FIELD_GET(LUT_SRC, data);
177 lval = FIELD_GET(LUT_L_VAL, data);
178 core_count = FIELD_GET(LUT_CORE_COUNT, data);
179
dcd1fd72
MS
180 data = readl_relaxed(drv_data->base + soc_data->reg_volt_lut +
181 i * soc_data->lut_row_size);
55538fbc
TD
182 volt = FIELD_GET(LUT_VOLT, data) * 1000;
183
2849dd8b
TD
184 if (src)
185 freq = xo_rate * lval / 1000;
186 else
187 freq = cpu_hw_rate / 1000;
188
0eae1e37 189 if (freq != prev_freq && core_count != LUT_TURBO_IND) {
2849dd8b 190 table[i].frequency = freq;
51c843cf 191 qcom_cpufreq_update_opp(cpu_dev, freq, volt);
55538fbc 192 dev_dbg(cpu_dev, "index=%d freq=%d, core_count %d\n", i,
2849dd8b 193 freq, core_count);
0eae1e37 194 } else if (core_count == LUT_TURBO_IND) {
55538fbc 195 table[i].frequency = CPUFREQ_ENTRY_INVALID;
2849dd8b
TD
196 }
197
198 /*
199 * Two of the same frequencies with the same core counts means
200 * end of table
201 */
0eae1e37 202 if (i > 0 && prev_freq == freq) {
2849dd8b
TD
203 struct cpufreq_frequency_table *prev = &table[i - 1];
204
205 /*
206 * Only treat the last frequency that might be a boost
207 * as the boost frequency
208 */
0eae1e37 209 if (prev->frequency == CPUFREQ_ENTRY_INVALID) {
2849dd8b
TD
210 prev->frequency = prev_freq;
211 prev->flags = CPUFREQ_BOOST_FREQ;
51c843cf 212 qcom_cpufreq_update_opp(cpu_dev, prev_freq, volt);
2849dd8b
TD
213 }
214
215 break;
216 }
217
2849dd8b
TD
218 prev_freq = freq;
219 }
220
221 table[i].frequency = CPUFREQ_TABLE_END;
222 policy->freq_table = table;
55538fbc 223 dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
2849dd8b
TD
224
225 return 0;
226}
227
228static void qcom_get_related_cpus(int index, struct cpumask *m)
229{
230 struct device_node *cpu_np;
231 struct of_phandle_args args;
232 int cpu, ret;
233
234 for_each_possible_cpu(cpu) {
235 cpu_np = of_cpu_device_node_get(cpu);
236 if (!cpu_np)
237 continue;
238
239 ret = of_parse_phandle_with_args(cpu_np, "qcom,freq-domain",
240 "#freq-domain-cells", 0,
241 &args);
242 of_node_put(cpu_np);
243 if (ret < 0)
244 continue;
245
246 if (index == args.args[0])
247 cpumask_set_cpu(cpu, m);
248 }
249}
250
dcd1fd72
MS
251static const struct qcom_cpufreq_soc_data qcom_soc_data = {
252 .reg_enable = 0x0,
253 .reg_freq_lut = 0x110,
254 .reg_volt_lut = 0x114,
255 .reg_perf_state = 0x920,
256 .lut_row_size = 32,
257};
258
259static const struct of_device_id qcom_cpufreq_hw_match[] = {
260 { .compatible = "qcom,cpufreq-hw", .data = &qcom_soc_data },
261 {}
262};
263MODULE_DEVICE_TABLE(of, qcom_cpufreq_hw_match);
264
2849dd8b
TD
265static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
266{
bd74e286
MS
267 struct platform_device *pdev = cpufreq_get_driver_data();
268 struct device *dev = &pdev->dev;
2849dd8b
TD
269 struct of_phandle_args args;
270 struct device_node *cpu_np;
55538fbc 271 struct device *cpu_dev;
2849dd8b 272 void __iomem *base;
dcd1fd72 273 struct qcom_cpufreq_data *data;
2849dd8b
TD
274 int ret, index;
275
55538fbc
TD
276 cpu_dev = get_cpu_device(policy->cpu);
277 if (!cpu_dev) {
278 pr_err("%s: failed to get cpu%d device\n", __func__,
279 policy->cpu);
280 return -ENODEV;
281 }
282
2849dd8b
TD
283 cpu_np = of_cpu_device_node_get(policy->cpu);
284 if (!cpu_np)
285 return -EINVAL;
286
287 ret = of_parse_phandle_with_args(cpu_np, "qcom,freq-domain",
288 "#freq-domain-cells", 0, &args);
289 of_node_put(cpu_np);
290 if (ret)
291 return ret;
292
293 index = args.args[0];
294
f17b3e44
MS
295 base = devm_platform_ioremap_resource(pdev, index);
296 if (IS_ERR(base))
297 return PTR_ERR(base);
2849dd8b 298
dcd1fd72
MS
299 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
300 if (!data) {
301 ret = -ENOMEM;
302 goto error;
303 }
304
305 data->soc_data = of_device_get_match_data(&pdev->dev);
306 data->base = base;
307
2849dd8b 308 /* HW should be in enabled state to proceed */
dcd1fd72 309 if (!(readl_relaxed(base + data->soc_data->reg_enable) & 0x1)) {
2849dd8b
TD
310 dev_err(dev, "Domain-%d cpufreq hardware not enabled\n", index);
311 ret = -ENODEV;
312 goto error;
313 }
314
315 qcom_get_related_cpus(index, policy->cpus);
316 if (!cpumask_weight(policy->cpus)) {
317 dev_err(dev, "Domain-%d failed to get related CPUs\n", index);
318 ret = -ENOENT;
319 goto error;
320 }
321
dcd1fd72 322 policy->driver_data = data;
2849dd8b 323
dcd1fd72 324 ret = qcom_cpufreq_hw_read_lut(cpu_dev, policy);
2849dd8b
TD
325 if (ret) {
326 dev_err(dev, "Domain-%d failed to read LUT\n", index);
327 goto error;
328 }
329
55538fbc
TD
330 ret = dev_pm_opp_get_opp_count(cpu_dev);
331 if (ret <= 0) {
332 dev_err(cpu_dev, "Failed to add OPPs\n");
333 ret = -ENODEV;
334 goto error;
335 }
336
0e0ffa85 337 dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
dab53505 338
2849dd8b
TD
339 return 0;
340error:
341 devm_iounmap(dev, base);
342 return ret;
343}
344
345static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy)
346{
55538fbc 347 struct device *cpu_dev = get_cpu_device(policy->cpu);
dcd1fd72 348 struct qcom_cpufreq_data *data = policy->driver_data;
bd74e286 349 struct platform_device *pdev = cpufreq_get_driver_data();
2849dd8b 350
55538fbc 351 dev_pm_opp_remove_all_dynamic(cpu_dev);
51c843cf 352 dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
2849dd8b 353 kfree(policy->freq_table);
dcd1fd72 354 devm_iounmap(&pdev->dev, data->base);
2849dd8b
TD
355
356 return 0;
357}
358
359static struct freq_attr *qcom_cpufreq_hw_attr[] = {
360 &cpufreq_freq_attr_scaling_available_freqs,
361 &cpufreq_freq_attr_scaling_boost_freqs,
362 NULL
363};
364
365static struct cpufreq_driver cpufreq_qcom_hw_driver = {
366 .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK |
4c5ff1c8
AK
367 CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
368 CPUFREQ_IS_COOLING_DEV,
2849dd8b
TD
369 .verify = cpufreq_generic_frequency_table_verify,
370 .target_index = qcom_cpufreq_hw_target_index,
371 .get = qcom_cpufreq_hw_get,
372 .init = qcom_cpufreq_hw_cpu_init,
373 .exit = qcom_cpufreq_hw_cpu_exit,
374 .fast_switch = qcom_cpufreq_hw_fast_switch,
375 .name = "qcom-cpufreq-hw",
376 .attr = qcom_cpufreq_hw_attr,
377};
378
379static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
380{
51c843cf 381 struct device *cpu_dev;
2849dd8b
TD
382 struct clk *clk;
383 int ret;
384
385 clk = clk_get(&pdev->dev, "xo");
386 if (IS_ERR(clk))
387 return PTR_ERR(clk);
388
389 xo_rate = clk_get_rate(clk);
390 clk_put(clk);
391
392 clk = clk_get(&pdev->dev, "alternate");
393 if (IS_ERR(clk))
394 return PTR_ERR(clk);
395
396 cpu_hw_rate = clk_get_rate(clk) / CLK_HW_DIV;
397 clk_put(clk);
398
bd74e286 399 cpufreq_qcom_hw_driver.driver_data = pdev;
2849dd8b 400
51c843cf
SS
401 /* Check for optional interconnect paths on CPU0 */
402 cpu_dev = get_cpu_device(0);
403 if (!cpu_dev)
404 return -EPROBE_DEFER;
405
406 ret = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL);
407 if (ret)
408 return ret;
409
2849dd8b
TD
410 ret = cpufreq_register_driver(&cpufreq_qcom_hw_driver);
411 if (ret)
412 dev_err(&pdev->dev, "CPUFreq HW driver failed to register\n");
413 else
414 dev_dbg(&pdev->dev, "QCOM CPUFreq HW driver initialized\n");
415
416 return ret;
417}
418
419static int qcom_cpufreq_hw_driver_remove(struct platform_device *pdev)
420{
421 return cpufreq_unregister_driver(&cpufreq_qcom_hw_driver);
422}
423
2849dd8b
TD
424static struct platform_driver qcom_cpufreq_hw_driver = {
425 .probe = qcom_cpufreq_hw_driver_probe,
426 .remove = qcom_cpufreq_hw_driver_remove,
427 .driver = {
428 .name = "qcom-cpufreq-hw",
429 .of_match_table = qcom_cpufreq_hw_match,
430 },
431};
432
433static int __init qcom_cpufreq_hw_init(void)
434{
435 return platform_driver_register(&qcom_cpufreq_hw_driver);
436}
11ff4bdd 437postcore_initcall(qcom_cpufreq_hw_init);
2849dd8b
TD
438
439static void __exit qcom_cpufreq_hw_exit(void)
440{
441 platform_driver_unregister(&qcom_cpufreq_hw_driver);
442}
443module_exit(qcom_cpufreq_hw_exit);
444
445MODULE_DESCRIPTION("QCOM CPUFREQ HW Driver");
446MODULE_LICENSE("GPL v2");