• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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