1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * The sun50i-cpufreq-nvmem driver reads the efuse value from the SoC to
4 * provide the OPP framework with required information.
5 *
6 * Copyright (C) 2020 frank@allwinnertech.com
7 */
8
9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11 #include <linux/module.h>
12 #include <linux/nvmem-consumer.h>
13 #include <linux/of_device.h>
14 #include <linux/platform_device.h>
15 #include <linux/pm_opp.h>
16 #include <linux/slab.h>
17 #include <sunxi-sid.h>
18
19 #define MAX_NAME_LEN 3
20
21 #define SUN50IW9_ICPU_MASK GENMASK(9, 0)
22
23 static struct platform_device *cpufreq_dt_pdev, *sun50i_cpufreq_pdev;
24
25 struct cpufreq_nvmem_data {
26 u32 nv_speed;
27 u32 nv_Icpu;
28 u32 nv_bin;
29 u32 nv_bin_ext;
30 u32 version;
31 char name[MAX_NAME_LEN];
32 };
33
34 static struct cpufreq_nvmem_data ver_data;
35
36 struct cpufreq_soc_data {
37 void (*nvmem_xlate)(u32 *versions, char *name);
38 bool has_nvmem_Icpu;
39 bool has_nvmem_bin;
40 bool has_nvmem_extend_bin;
41 };
42
sun50i_nvmem_get_data(char * cell_name,u32 * data)43 static int sun50i_nvmem_get_data(char *cell_name, u32 *data)
44 {
45 struct nvmem_cell *cell_nvmem;
46 size_t len;
47 u8 *cell_value;
48 u32 tmp_data = 0;
49 u32 i;
50 struct device_node *np;
51 struct device *cpu_dev;
52 int ret = 0;
53
54 cpu_dev = get_cpu_device(0);
55 if (!cpu_dev)
56 return -ENODEV;
57
58 np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
59 if (!np)
60 return -ENOENT;
61
62 ret = of_device_is_compatible(np,
63 "allwinner,sun50i-operating-points");
64 if (!ret) {
65 of_node_put(np);
66 return -ENOENT;
67 }
68
69 cell_nvmem = of_nvmem_cell_get(np, cell_name);
70 of_node_put(np);
71 if (IS_ERR(cell_nvmem)) {
72 if (PTR_ERR(cell_nvmem) != -EPROBE_DEFER)
73 pr_err("Could not get nvmem cell: %ld\n",
74 PTR_ERR(cell_nvmem));
75 return PTR_ERR(cell_nvmem);
76 }
77
78 cell_value = nvmem_cell_read(cell_nvmem, &len);
79 nvmem_cell_put(cell_nvmem);
80 if (IS_ERR(cell_value))
81 return PTR_ERR(cell_value);
82
83 if (len > 4) {
84 pr_err("Invalid nvmem cell length\n");
85 ret = -EINVAL;
86 } else {
87 for (i = 0; i < len; i++)
88 tmp_data |= ((u32)cell_value[i] << (i * 8));
89 *data = tmp_data;
90 }
91
92 kfree(cell_value);
93
94 return 0;
95 }
96
sun50iw9_icpu_xlate(char * prop_name,char * name,u32 i_data)97 static void sun50iw9_icpu_xlate(char *prop_name, char *name, u32 i_data)
98 {
99 int value = 0;
100
101 i_data &= SUN50IW9_ICPU_MASK;
102
103 if ((i_data >= 0) && (i_data < 93))
104 value = 0;
105 /* 150 is temp munber */
106 else if ((i_data >= 93) && (i_data < 150))
107 value = 1;
108 else if ((i_data >= 150) && (i_data < 271))
109 value = 2;
110
111 snprintf(name, MAX_NAME_LEN, "%s%d", prop_name, value);
112 }
113
sun50iw9_nvmem_xlate(u32 * versions,char * name)114 static void sun50iw9_nvmem_xlate(u32 *versions, char *name)
115 {
116 switch (ver_data.nv_speed) {
117 case 0x2400:
118 case 0x7400:
119 sun50iw9_icpu_xlate("a", name, ver_data.nv_Icpu);
120 break;
121 case 0x2c00:
122 case 0x7c00:
123 sun50iw9_icpu_xlate("b", name, ver_data.nv_Icpu);
124 break;
125 case 0x5000:
126 case 0x5400:
127 case 0x6c00:
128 *versions = 0b0001;
129 break;
130 case 0x5c00:
131 default:
132 *versions = 0b0010;
133 }
134 }
135
136 static struct cpufreq_soc_data sun50iw9_soc_data = {
137 .nvmem_xlate = sun50iw9_nvmem_xlate,
138 .has_nvmem_Icpu = true,
139 };
140
sun50iw10_bin_xlate(bool high_speed,char * name,u32 bin)141 static void sun50iw10_bin_xlate(bool high_speed, char *name, u32 bin)
142 {
143 int value = 0;
144 bool version_before_f;
145 unsigned int ver_bits = sunxi_get_soc_ver() & 0x7;
146 u32 bin_ext = ver_data.nv_bin_ext;
147
148 bin >>= 12;
149 bin_ext >>= 31;
150
151 if (ver_bits == 0 || ver_bits == 3 || ver_bits == 4)
152 version_before_f = true;
153 else
154 version_before_f = false;
155
156 if (high_speed) {
157 switch (bin) {
158 case 0b100:
159 if (version_before_f) {
160 /* ic version A-E */
161 value = 1;
162 } else {
163 /* ic version F and later version */
164 value = 3;
165 }
166 break;
167 default:
168 if (version_before_f) {
169 /* ic version A-E */
170 value = 0;
171 } else {
172 /* ic version F and later version */
173 value = 2;
174 }
175 }
176
177 snprintf(name, MAX_NAME_LEN, "b%d", value);
178 } else {
179 if (bin_ext && (!version_before_f)) {
180 value = 6;
181 } else {
182 switch (bin) {
183 case 0b100:
184 if (version_before_f) {
185 /* ic version A-E */
186 value = 2;
187 } else {
188 /* ic version F and later version */
189 value = 5;
190 }
191 break;
192 case 0b010:
193 if (version_before_f) {
194 /* ic version A-E */
195 value = 1;
196 } else {
197 /* ic version F and later version */
198 value = 4;
199 }
200 break;
201 default:
202 if (version_before_f) {
203 /* ic version A-E */
204 value = 0;
205 } else {
206 /* ic version F and later version */
207 value = 3;
208 }
209 }
210 }
211 snprintf(name, MAX_NAME_LEN, "a%d", value);
212 }
213 }
214
sun50iw10_nvmem_xlate(u32 * versions,char * name)215 static void sun50iw10_nvmem_xlate(u32 *versions, char *name)
216 {
217 unsigned int ver_bits = sunxi_get_soc_ver() & 0x7;
218
219 switch (ver_data.nv_speed) {
220 case 0x0200:
221 case 0x0600:
222 case 0x0620:
223 case 0x0640:
224 case 0x0800:
225 case 0x1000:
226 case 0x1400:
227 case 0x2000:
228 case 0x4000:
229 if (ver_bits == 0 || ver_bits == 3 || ver_bits == 4) {
230 /* ic version A-E */
231 *versions = 0b0100;
232 } else {
233 /* ic version F and later version */
234 *versions = 0b0010;
235 }
236 sun50iw10_bin_xlate(true, name, ver_data.nv_bin);
237 break;
238 case 0x0400:
239 default:
240 *versions = 0b0001;
241 sun50iw10_bin_xlate(false, name, ver_data.nv_bin);
242 }
243 }
244
245 static struct cpufreq_soc_data sun50iw10_soc_data = {
246 .nvmem_xlate = sun50iw10_nvmem_xlate,
247 .has_nvmem_bin = true,
248 .has_nvmem_extend_bin = true,
249 };
250
sun8iw20_bin_xlate(bool bin_select,char * name,u32 nv_bin)251 static void sun8iw20_bin_xlate(bool bin_select, char *name, u32 nv_bin)
252 {
253 int value = 0;
254 u32 bin = (nv_bin >> 12) & 0xf;
255
256 if (bin_select) {
257 if (bin <= 1)
258 value = 1;
259 else
260 value = 0;
261 /*BGA use axx in VF table*/
262 snprintf(name, MAX_NAME_LEN, "a%d", value);
263 } else {
264 value = 0;
265 /*QFN use bxx in VF table*/
266 snprintf(name, MAX_NAME_LEN, "b%d", value);
267 }
268 }
269
sun8iw20_nvmem_xlate(u32 * versions,char * name)270 static void sun8iw20_nvmem_xlate(u32 *versions, char *name)
271 {
272 switch (ver_data.nv_speed) {
273 case 0x6000:
274 sun8iw20_bin_xlate(false, name, ver_data.nv_bin);
275 *versions = 0b0010;
276 break;
277 case 0x6400:
278 case 0x7000:
279 case 0x7c00:
280 default:
281 sun8iw20_bin_xlate(true, name, ver_data.nv_bin);
282 *versions = 0b0001;
283 break;
284 }
285 pr_debug("sun8iw20 match vf:%s, mark:0x%x\n", name, ver_data.nv_speed);
286 }
287
288 static struct cpufreq_soc_data sun8iw20_soc_data = {
289 .nvmem_xlate = sun8iw20_nvmem_xlate,
290 .has_nvmem_bin = true,
291 };
292
sun20iw1_bin_xlate(bool bin_select,bool high_speed,char * name,u32 nv_bin)293 static void sun20iw1_bin_xlate(bool bin_select, bool high_speed, char *name, u32 nv_bin)
294 {
295 int value = 0;
296 u32 bin = (nv_bin >> 12) & 0xf;
297
298 if (bin_select) {
299 if (bin <= 1)
300 value = 1;
301 else
302 value = 0;
303 /*BGA use axx in VF table*/
304 snprintf(name, MAX_NAME_LEN, "a%d", value);
305 } else {
306 if (high_speed)
307 value = 1;
308 else
309 value = 0;
310 /*QFN use bxx in VF table*/
311 snprintf(name, MAX_NAME_LEN, "b%d", value);
312 }
313 }
314
sun20iw1_nvmem_xlate(u32 * versions,char * name)315 static void sun20iw1_nvmem_xlate(u32 *versions, char *name)
316 {
317 switch (ver_data.nv_speed) {
318 case 0x5e00:
319 sun20iw1_bin_xlate(false, false, name, ver_data.nv_bin);
320 *versions = 0b0010;
321 break;
322 case 0x5c00:
323 case 0x7400:
324 sun20iw1_bin_xlate(false, true, name, ver_data.nv_bin);
325 *versions = 0b0001;
326 break;
327 case 0x5000:
328 default:
329 sun20iw1_bin_xlate(true, true, name, ver_data.nv_bin);
330 *versions = 0b0001;
331 }
332 pr_debug("sun20iw1 match vf:%s, mark:0x%x\n", name, ver_data.nv_speed);
333 }
334
335 static struct cpufreq_soc_data sun20iw1_soc_data = {
336 .nvmem_xlate = sun20iw1_nvmem_xlate,
337 .has_nvmem_bin = true,
338 };
339
340 /**
341 * sun50i_cpufreq_get_efuse() - Determine speed grade from efuse value
342 * @versions: Set to the value parsed from efuse
343 *
344 * Returns 0 if success.
345 */
sun50i_cpufreq_get_efuse(const struct cpufreq_soc_data * soc_data,u32 * versions,char * name)346 static int sun50i_cpufreq_get_efuse(const struct cpufreq_soc_data *soc_data,
347 u32 *versions, char *name)
348 {
349 int ret;
350
351 ret = sun50i_nvmem_get_data("speed", &ver_data.nv_speed);
352 if (ret)
353 return ret;
354
355 if (soc_data->has_nvmem_Icpu) {
356 ret = sun50i_nvmem_get_data("Icpu", &ver_data.nv_Icpu);
357 if (ret)
358 return ret;
359 }
360
361 if (soc_data->has_nvmem_bin) {
362 ret = sun50i_nvmem_get_data("bin", &ver_data.nv_bin);
363 if (ret)
364 return ret;
365 }
366
367 if (soc_data->has_nvmem_extend_bin) {
368 ret = sun50i_nvmem_get_data("bin_ext", &ver_data.nv_bin_ext);
369 if (ret)
370 return ret;
371 }
372
373 soc_data->nvmem_xlate(versions, name);
374
375 return 0;
376 };
377
sun50i_cpufreq_nvmem_probe(struct platform_device * pdev)378 static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev)
379 {
380 const struct of_device_id *match;
381 struct opp_table **opp_tables;
382 unsigned int cpu;
383 int ret;
384
385 opp_tables = kcalloc(num_possible_cpus(), sizeof(*opp_tables),
386 GFP_KERNEL);
387 if (!opp_tables)
388 return -ENOMEM;
389
390 match = dev_get_platdata(&pdev->dev);
391 if (!match)
392 return -EINVAL;
393
394 ret = sun50i_cpufreq_get_efuse(match->data,
395 &ver_data.version, ver_data.name);
396 if (ret)
397 return ret;
398
399 for_each_possible_cpu(cpu) {
400 struct device *cpu_dev = get_cpu_device(cpu);
401
402 if (!cpu_dev) {
403 ret = -ENODEV;
404 goto free_opp;
405 }
406
407 if (strlen(ver_data.name)) {
408 opp_tables[cpu] = dev_pm_opp_set_prop_name(cpu_dev,
409 ver_data.name);
410 if (IS_ERR(opp_tables[cpu])) {
411 ret = PTR_ERR(opp_tables[cpu]);
412 pr_err("Failed to set prop name\n");
413 goto free_opp;
414 }
415 }
416
417 if (ver_data.version) {
418 opp_tables[cpu] = dev_pm_opp_set_supported_hw(cpu_dev,
419 &ver_data.version, 1);
420 if (IS_ERR(opp_tables[cpu])) {
421 ret = PTR_ERR(opp_tables[cpu]);
422 pr_err("Failed to set hw\n");
423 goto free_opp;
424 }
425 }
426 }
427
428 cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
429 NULL, 0);
430 if (!IS_ERR(cpufreq_dt_pdev)) {
431 platform_set_drvdata(pdev, opp_tables);
432 return 0;
433 }
434
435 ret = PTR_ERR(cpufreq_dt_pdev);
436 pr_err("Failed to register platform device\n");
437
438 free_opp:
439 for_each_possible_cpu(cpu) {
440 if (IS_ERR_OR_NULL(opp_tables[cpu]))
441 break;
442
443 if (strlen(ver_data.name))
444 dev_pm_opp_put_prop_name(opp_tables[cpu]);
445
446 if (ver_data.version)
447 dev_pm_opp_put_supported_hw(opp_tables[cpu]);
448 }
449 kfree(opp_tables);
450
451 return ret;
452 }
453
sun50i_cpufreq_nvmem_remove(struct platform_device * pdev)454 static int sun50i_cpufreq_nvmem_remove(struct platform_device *pdev)
455 {
456 struct opp_table **opp_tables = platform_get_drvdata(pdev);
457 unsigned int cpu;
458
459 platform_device_unregister(cpufreq_dt_pdev);
460
461 for_each_possible_cpu(cpu) {
462 if (IS_ERR_OR_NULL(opp_tables[cpu]))
463 break;
464
465 if (strlen(ver_data.name))
466 dev_pm_opp_put_prop_name(opp_tables[cpu]);
467
468 if (ver_data.version)
469 dev_pm_opp_put_supported_hw(opp_tables[cpu]);
470 }
471 kfree(opp_tables);
472
473 return 0;
474 }
475
476 static struct platform_driver sun50i_cpufreq_driver = {
477 .probe = sun50i_cpufreq_nvmem_probe,
478 .remove = sun50i_cpufreq_nvmem_remove,
479 .driver = {
480 .name = "sun50i-cpufreq-nvmem",
481 },
482 };
483
484 static const struct of_device_id sun50i_cpufreq_match_list[] = {
485 { .compatible = "arm,sun50iw9p1", .data = &sun50iw9_soc_data, },
486 { .compatible = "arm,sun50iw10p1", .data = &sun50iw10_soc_data, },
487 { .compatible = "arm,sun8iw20p1", .data = &sun8iw20_soc_data, },
488 { .compatible = "arm,sun20iw1p1", .data = &sun20iw1_soc_data, },
489 {}
490 };
491
sun50i_cpufreq_match_node(void)492 static const struct of_device_id *sun50i_cpufreq_match_node(void)
493 {
494 const struct of_device_id *match;
495 struct device_node *np;
496
497 np = of_find_node_by_path("/");
498 match = of_match_node(sun50i_cpufreq_match_list, np);
499 of_node_put(np);
500
501 return match;
502 }
503
504 /*
505 * Since the driver depends on nvmem drivers, which may return EPROBE_DEFER,
506 * all the real activity is done in the probe, which may be defered as well.
507 * The init here is only registering the driver and the platform device.
508 */
sun50i_cpufreq_init(void)509 static int __init sun50i_cpufreq_init(void)
510 {
511 const struct of_device_id *match;
512 int ret;
513
514 match = sun50i_cpufreq_match_node();
515 if (!match)
516 return -ENODEV;
517
518 ret = platform_driver_register(&sun50i_cpufreq_driver);
519 if (unlikely(ret < 0))
520 return ret;
521
522 sun50i_cpufreq_pdev = platform_device_register_data(NULL,
523 "sun50i-cpufreq-nvmem",
524 -1, match,
525 sizeof(*match));
526 ret = PTR_ERR_OR_ZERO(sun50i_cpufreq_pdev);
527 if (ret == 0)
528 return 0;
529
530 platform_driver_unregister(&sun50i_cpufreq_driver);
531 return ret;
532 }
533 module_init(sun50i_cpufreq_init);
534
sun50i_cpufreq_exit(void)535 static void __exit sun50i_cpufreq_exit(void)
536 {
537 platform_device_unregister(sun50i_cpufreq_pdev);
538 platform_driver_unregister(&sun50i_cpufreq_driver);
539 }
540 module_exit(sun50i_cpufreq_exit);
541
542 MODULE_DESCRIPTION("Sun50i cpufreq driver");
543 MODULE_LICENSE("GPL v2");
544