tegra124_cpufreq.c revision 308324
1/*-
2 * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_cpufreq.c 308324 2016-11-05 04:17:32Z mmel $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/bus.h>
33#include <sys/cpu.h>
34#include <sys/kernel.h>
35#include <sys/lock.h>
36#include <sys/malloc.h>
37#include <sys/module.h>
38
39#include <machine/bus.h>
40#include <machine/cpu.h>
41
42#include <dev/extres/clk/clk.h>
43#include <dev/extres/regulator/regulator.h>
44#include <dev/ofw/ofw_bus_subr.h>
45
46#include <arm/nvidia/tegra_efuse.h>
47
48#include "cpufreq_if.h"
49
50#define	XXX
51
52/* CPU voltage table entry */
53struct speedo_entry {
54	uint64_t		freq; 	/* Frequency point */
55	int			c0; 	/* Coeeficient values for */
56	int			c1;	/* quadratic equation: */
57	int 			c2;	/* c2 * speedo^2 + c1 * speedo + c0 */
58};
59
60struct cpu_volt_def {
61	int			min_uvolt;	/* Min allowed CPU voltage */
62	int			max_uvolt;	/* Max allowed CPU voltage */
63	int 			step_uvolt; 	/* Step of CPU voltage */
64	int			speedo_scale;	/* Scaling factor for cvt */
65	int			speedo_nitems;	/* Size of speedo table */
66	struct speedo_entry	*speedo_tbl;	/* CPU voltage table */
67};
68
69struct cpu_speed_point {
70	uint64_t		freq;		/* Frequecy */
71	int			uvolt;		/* Requested voltage */
72};
73
74static struct speedo_entry tegra124_speedo_dpll_tbl[] =
75{
76	{ 204000000ULL,	1112619, -29295, 402},
77	{ 306000000ULL,	1150460, -30585, 402},
78	{ 408000000ULL,	1190122, -31865, 402},
79	{ 510000000ULL,	1231606, -33155, 402},
80	{ 612000000ULL,	1274912, -34435, 402},
81	{ 714000000ULL,	1320040, -35725, 402},
82	{ 816000000ULL,	1366990, -37005, 402},
83	{ 918000000ULL,	1415762, -38295, 402},
84	{1020000000ULL,	1466355, -39575, 402},
85	{1122000000ULL,	1518771, -40865, 402},
86	{1224000000ULL,	1573009, -42145, 402},
87	{1326000000ULL,	1629068, -43435, 402},
88	{1428000000ULL,	1686950, -44715, 402},
89	{1530000000ULL,	1746653, -46005, 402},
90	{1632000000ULL,	1808179, -47285, 402},
91	{1734000000ULL,	1871526, -48575, 402},
92	{1836000000ULL,	1936696, -49855, 402},
93	{1938000000ULL,	2003687, -51145, 402},
94	{2014500000ULL,	2054787, -52095, 402},
95	{2116500000ULL,	2124957, -53385, 402},
96	{2218500000ULL,	2196950, -54665, 402},
97	{2320500000ULL,	2270765, -55955, 402},
98	{2320500000ULL,	2270765, -55955, 402},
99	{2422500000ULL,	2346401, -57235, 402},
100	{2524500000ULL,	2437299, -58535, 402},
101};
102
103static struct cpu_volt_def tegra124_cpu_volt_dpll_def =
104{
105	.min_uvolt =  900000,		/* 0.9 V */
106	.max_uvolt = 1260000,		/* 1.26 */
107	.step_uvolt =  10000,		/* 10 mV */
108	.speedo_scale = 100,
109	.speedo_nitems = nitems(tegra124_speedo_dpll_tbl),
110	.speedo_tbl = tegra124_speedo_dpll_tbl,
111};
112
113static struct speedo_entry tegra124_speedo_pllx_tbl[] =
114{
115	{ 204000000ULL,	 800000, 0, 0},
116	{ 306000000ULL,	 800000, 0, 0},
117	{ 408000000ULL,	 800000, 0, 0},
118	{ 510000000ULL,	 800000, 0, 0},
119	{ 612000000ULL,	 800000, 0, 0},
120	{ 714000000ULL,	 800000, 0, 0},
121	{ 816000000ULL,	 820000, 0, 0},
122	{ 918000000ULL,	 840000, 0, 0},
123	{1020000000ULL,	 880000, 0, 0},
124	{1122000000ULL,	 900000, 0, 0},
125	{1224000000ULL,	 930000, 0, 0},
126	{1326000000ULL,	 960000, 0, 0},
127	{1428000000ULL,	 990000, 0, 0},
128	{1530000000ULL,	1020000, 0, 0},
129	{1632000000ULL,	1070000, 0, 0},
130	{1734000000ULL,	1100000, 0, 0},
131	{1836000000ULL,	1140000, 0, 0},
132	{1938000000ULL,	1180000, 0, 0},
133	{2014500000ULL,	1220000, 0, 0},
134	{2116500000ULL,	1260000, 0, 0},
135	{2218500000ULL,	1310000, 0, 0},
136	{2320500000ULL,	1360000, 0, 0},
137	{2397000000ULL,	1400000, 0, 0},
138	{2499000000ULL,	1400000, 0, 0},
139};
140
141
142static struct cpu_volt_def tegra124_cpu_volt_pllx_def =
143{
144	.min_uvolt =  900000,		/* 0.9 V */
145	.max_uvolt = 1260000,		/* 1.26 */
146	.step_uvolt =  10000,		/* 10 mV */
147	.speedo_scale = 100,
148	.speedo_nitems = nitems(tegra124_speedo_pllx_tbl),
149	.speedo_tbl = tegra124_speedo_pllx_tbl,
150};
151
152static uint64_t cpu_freq_tbl[] = {
153	 204000000ULL,
154	 306000000ULL,
155	 408000000ULL,
156	 510000000ULL,
157	 612000000ULL,
158	 714000000ULL,
159	 816000000ULL,
160	 918000000ULL,
161	1020000000ULL,
162	1122000000ULL,
163	1224000000ULL,
164	1326000000ULL,
165	1428000000ULL,
166	1530000000ULL,
167	1632000000ULL,
168	1734000000ULL,
169	1836000000ULL,
170	1938000000ULL,
171	2014000000ULL,
172	2116000000ULL,
173	2218000000ULL,
174	2320000000ULL,
175	2320000000ULL,
176	2422000000ULL,
177	2524000000ULL,
178};
179
180static uint64_t cpu_max_freq[] = {
181	2014500000ULL,
182	2320500000ULL,
183	2116500000ULL,
184	2524500000ULL,
185};
186
187struct tegra124_cpufreq_softc {
188	device_t		dev;
189	phandle_t		node;
190
191	regulator_t		supply_vdd_cpu;
192	clk_t			clk_cpu_g;
193	clk_t			clk_cpu_lp;
194	clk_t			clk_pll_x;
195	clk_t			clk_pll_p;
196	clk_t			clk_dfll;
197
198	int 			process_id;
199	int 			speedo_id;
200	int 			speedo_value;
201
202	uint64_t		cpu_max_freq;
203	struct cpu_volt_def	*cpu_def;
204	struct cpu_speed_point	*speed_points;
205	int			nspeed_points;
206
207	struct cpu_speed_point	*act_speed_point;
208
209	int			latency;
210};
211
212static int cpufreq_lowest_freq = 1;
213TUNABLE_INT("hw.tegra124.cpufreq.lowest_freq", &cpufreq_lowest_freq);
214
215#define	DIV_ROUND_CLOSEST(val, div)	(((val) + ((div) / 2)) / (div))
216
217#define	ROUND_UP(val, div)	roundup(val, div)
218#define	ROUND_DOWN(val, div)	rounddown(val, div)
219
220/*
221 * Compute requesetd voltage for given frequency and SoC process variations,
222 * - compute base voltage from speedo value using speedo table
223 * - round up voltage to next regulator step
224 * - clamp it to regulator limits
225 */
226static int
227freq_to_voltage(struct tegra124_cpufreq_softc *sc, uint64_t freq)
228{
229	int uv, scale, min_uvolt, max_uvolt, step_uvolt;
230	struct speedo_entry *ent;
231	int i;
232
233	/* Get speedo entry with higher frequency */
234	ent = NULL;
235	for (i = 0; i < sc->cpu_def->speedo_nitems; i++) {
236		if (sc->cpu_def->speedo_tbl[i].freq >= freq) {
237			ent = &sc->cpu_def->speedo_tbl[i];
238			break;
239		}
240	}
241	if (ent == NULL)
242		ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1];
243	scale = sc->cpu_def->speedo_scale;
244
245
246	/* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */
247	uv = DIV_ROUND_CLOSEST(ent->c2 * sc->speedo_value, scale);
248	uv = DIV_ROUND_CLOSEST((uv + ent->c1) * sc->speedo_value, scale) +
249	    ent->c0;
250	step_uvolt = sc->cpu_def->step_uvolt;
251	/* Round up it to next regulator step */
252	uv = ROUND_UP(uv, step_uvolt);
253
254	/* Clamp result */
255	min_uvolt = ROUND_UP(sc->cpu_def->min_uvolt, step_uvolt);
256	max_uvolt = ROUND_DOWN(sc->cpu_def->max_uvolt, step_uvolt);
257	if (uv < min_uvolt)
258		uv =  min_uvolt;
259	if (uv > max_uvolt)
260		uv =  max_uvolt;
261	return (uv);
262
263}
264
265static void
266build_speed_points(struct tegra124_cpufreq_softc *sc) {
267	int i;
268
269	sc->nspeed_points = nitems(cpu_freq_tbl);
270	sc->speed_points = malloc(sizeof(struct cpu_speed_point) *
271	    sc->nspeed_points, M_DEVBUF, M_NOWAIT);
272	for (i = 0; i < sc->nspeed_points; i++) {
273		sc->speed_points[i].freq = cpu_freq_tbl[i];
274		sc->speed_points[i].uvolt = freq_to_voltage(sc,
275		    cpu_freq_tbl[i]);
276	}
277}
278
279static struct cpu_speed_point *
280get_speed_point(struct tegra124_cpufreq_softc *sc, uint64_t freq)
281{
282	int i;
283
284	if (sc->speed_points[0].freq >= freq)
285		return (sc->speed_points + 0);
286
287	for (i = 0; i < sc->nspeed_points - 1; i++) {
288		if (sc->speed_points[i + 1].freq > freq)
289			return (sc->speed_points + i);
290	}
291
292	return (sc->speed_points + sc->nspeed_points - 1);
293}
294
295static int
296tegra124_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count)
297{
298	struct tegra124_cpufreq_softc *sc;
299	int i, j, max_cnt;
300
301	if (sets == NULL || count == NULL)
302		return (EINVAL);
303
304	sc = device_get_softc(dev);
305	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count));
306
307	max_cnt = min(sc->nspeed_points, *count);
308	for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) {
309		if (sc->cpu_max_freq < sc->speed_points[j].freq)
310			continue;
311		sets[i].freq = sc->speed_points[j].freq / 1000000;
312		sets[i].volts = sc->speed_points[j].uvolt / 1000;
313		sets[i].lat = sc->latency;
314		sets[i].dev = dev;
315		i++;
316	}
317	*count = i;
318
319	return (0);
320}
321
322static int
323set_cpu_freq(struct tegra124_cpufreq_softc *sc, uint64_t freq)
324{
325	struct cpu_speed_point *point;
326	int rv;
327
328	point = get_speed_point(sc, freq);
329
330	if (sc->act_speed_point->uvolt < point->uvolt) {
331		/* set cpu voltage */
332		rv = regulator_set_voltage(sc->supply_vdd_cpu,
333		    point->uvolt, point->uvolt);
334		DELAY(10000);
335		if (rv != 0)
336			return (rv);
337	}
338
339	/* Switch supermux to PLLP first */
340	rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_p);
341	if (rv != 0) {
342		device_printf(sc->dev, "Can't set parent to PLLP\n");
343		return (rv);
344	}
345
346	/* Set PLLX frequency */
347	rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN);
348	if (rv != 0) {
349		device_printf(sc->dev, "Can't set CPU clock frequency\n");
350		return (rv);
351	}
352
353	rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_x);
354	if (rv != 0) {
355		device_printf(sc->dev, "Can't set parent to PLLX\n");
356		return (rv);
357	}
358
359	if (sc->act_speed_point->uvolt > point->uvolt) {
360		/* set cpu voltage */
361		rv = regulator_set_voltage(sc->supply_vdd_cpu,
362		    point->uvolt, point->uvolt);
363		if (rv != 0)
364			return (rv);
365	}
366
367	sc->act_speed_point = point;
368
369	return (0);
370}
371
372static int
373tegra124_cpufreq_set(device_t dev, const struct cf_setting *cf)
374{
375	struct tegra124_cpufreq_softc *sc;
376	uint64_t freq;
377	int rv;
378
379	if (cf == NULL || cf->freq < 0)
380		return (EINVAL);
381
382	sc = device_get_softc(dev);
383
384	freq = cf->freq;
385	if (freq < cpufreq_lowest_freq)
386		freq = cpufreq_lowest_freq;
387	freq *= 1000000;
388	if (freq >= sc->cpu_max_freq)
389		freq = sc->cpu_max_freq;
390	rv = set_cpu_freq(sc, freq);
391
392	return (rv);
393}
394
395static int
396tegra124_cpufreq_get(device_t dev, struct cf_setting *cf)
397{
398	struct tegra124_cpufreq_softc *sc;
399
400	if (cf == NULL)
401		return (EINVAL);
402
403	sc = device_get_softc(dev);
404	memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
405	cf->dev = NULL;
406	cf->freq = sc->act_speed_point->freq / 1000000;
407	cf->volts = sc->act_speed_point->uvolt / 1000;
408	/* Transition latency in us. */
409	cf->lat = sc->latency;
410	/* Driver providing this setting. */
411	cf->dev = dev;
412
413	return (0);
414}
415
416
417static int
418tegra124_cpufreq_type(device_t dev, int *type)
419{
420
421	if (type == NULL)
422		return (EINVAL);
423	*type = CPUFREQ_TYPE_ABSOLUTE;
424
425	return (0);
426}
427
428static int
429get_fdt_resources(struct tegra124_cpufreq_softc *sc, phandle_t node)
430{
431	int rv;
432	device_t parent_dev;
433
434	parent_dev =  device_get_parent(sc->dev);
435	rv = regulator_get_by_ofw_property(parent_dev, 0, "vdd-cpu-supply",
436	    &sc->supply_vdd_cpu);
437	if (rv != 0) {
438		device_printf(sc->dev, "Cannot get 'vdd-cpu' regulator\n");
439		return (rv);
440	}
441
442	rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_g", &sc->clk_cpu_g);
443	if (rv != 0) {
444		device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv);
445		return (ENXIO);
446	}
447
448	rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_lp", &sc->clk_cpu_lp);
449	if (rv != 0) {
450		device_printf(sc->dev, "Cannot get 'cpu_lp' clock\n");
451		return (ENXIO);
452	}
453
454	rv = clk_get_by_ofw_name(parent_dev, 0, "pll_x", &sc->clk_pll_x);
455	if (rv != 0) {
456		device_printf(sc->dev, "Cannot get 'pll_x' clock\n");
457		return (ENXIO);
458	}
459	rv = clk_get_by_ofw_name(parent_dev, 0, "pll_p", &sc->clk_pll_p);
460	if (rv != 0) {
461		device_printf(parent_dev, "Cannot get 'pll_p' clock\n");
462		return (ENXIO);
463	}
464	rv = clk_get_by_ofw_name(parent_dev, 0, "dfll", &sc->clk_dfll);
465	if (rv != 0) {
466		/* XXX DPLL is not implemented yet */
467/*
468		device_printf(sc->dev, "Cannot get 'dfll' clock\n");
469		return (ENXIO);
470*/
471	}
472	return (0);
473}
474
475static void
476tegra124_cpufreq_identify(driver_t *driver, device_t parent)
477{
478
479	if (device_find_child(parent, "tegra124_cpufreq", -1) != NULL)
480		return;
481	if (BUS_ADD_CHILD(parent, 0, "tegra124_cpufreq", -1) == NULL)
482		device_printf(parent, "add child failed\n");
483}
484
485static int
486tegra124_cpufreq_probe(device_t dev)
487{
488
489	if (device_get_unit(dev) != 0)
490		return (ENXIO);
491	device_set_desc(dev, "CPU Frequency Control");
492
493	return (0);
494}
495
496static int
497tegra124_cpufreq_attach(device_t dev)
498{
499	struct tegra124_cpufreq_softc *sc;
500	uint64_t freq;
501	int rv;
502
503	sc = device_get_softc(dev);
504	sc->dev = dev;
505	sc->node = ofw_bus_get_node(device_get_parent(dev));
506
507	sc->process_id = tegra_sku_info.cpu_process_id;
508	sc->speedo_id = tegra_sku_info.cpu_speedo_id;
509	sc->speedo_value = tegra_sku_info.cpu_speedo_value;
510
511	/* Tegra 124 */
512	/* XXX DPLL is not implemented yet */
513	if (1)
514		sc->cpu_def = &tegra124_cpu_volt_pllx_def;
515	else
516		sc->cpu_def = &tegra124_cpu_volt_dpll_def;
517
518
519	rv = get_fdt_resources(sc, sc->node);
520	if (rv !=  0) {
521		return (rv);
522	}
523
524	build_speed_points(sc);
525
526	rv = clk_get_freq(sc->clk_cpu_g, &freq);
527	if (rv != 0) {
528		device_printf(dev, "Can't get CPU clock frequency\n");
529		return (rv);
530	}
531	if (sc->speedo_id < nitems(cpu_max_freq))
532		sc->cpu_max_freq = cpu_max_freq[sc->speedo_id];
533	else
534		sc->cpu_max_freq = cpu_max_freq[0];
535	sc->act_speed_point = get_speed_point(sc, freq);
536
537	/* Set safe startup CPU frequency. */
538	rv = set_cpu_freq(sc, 1632000000);
539	if (rv != 0) {
540		device_printf(dev, "Can't set initial CPU clock frequency\n");
541		return (rv);
542	}
543
544	/* This device is controlled by cpufreq(4). */
545	cpufreq_register(dev);
546
547	return (0);
548}
549
550static int
551tegra124_cpufreq_detach(device_t dev)
552{
553	struct tegra124_cpufreq_softc *sc;
554
555	sc = device_get_softc(dev);
556	cpufreq_unregister(dev);
557
558	if (sc->supply_vdd_cpu != NULL)
559		regulator_release(sc->supply_vdd_cpu);
560
561	if (sc->clk_cpu_g != NULL)
562		clk_release(sc->clk_cpu_g);
563	if (sc->clk_cpu_lp != NULL)
564		clk_release(sc->clk_cpu_lp);
565	if (sc->clk_pll_x != NULL)
566		clk_release(sc->clk_pll_x);
567	if (sc->clk_pll_p != NULL)
568		clk_release(sc->clk_pll_p);
569	if (sc->clk_dfll != NULL)
570		clk_release(sc->clk_dfll);
571	return (0);
572}
573
574static device_method_t tegra124_cpufreq_methods[] = {
575	/* Device interface */
576	DEVMETHOD(device_identify,	tegra124_cpufreq_identify),
577	DEVMETHOD(device_probe,		tegra124_cpufreq_probe),
578	DEVMETHOD(device_attach,	tegra124_cpufreq_attach),
579	DEVMETHOD(device_detach,	tegra124_cpufreq_detach),
580
581	/* cpufreq interface */
582	DEVMETHOD(cpufreq_drv_set,	tegra124_cpufreq_set),
583	DEVMETHOD(cpufreq_drv_get,	tegra124_cpufreq_get),
584	DEVMETHOD(cpufreq_drv_settings,	tegra124_cpufreq_settings),
585	DEVMETHOD(cpufreq_drv_type,	tegra124_cpufreq_type),
586
587	DEVMETHOD_END
588};
589
590static devclass_t tegra124_cpufreq_devclass;
591static driver_t tegra124_cpufreq_driver = {
592	"tegra124_cpufreq",
593	tegra124_cpufreq_methods,
594	sizeof(struct tegra124_cpufreq_softc),
595};
596
597DRIVER_MODULE(tegra124_cpufreq, cpu, tegra124_cpufreq_driver,
598    tegra124_cpufreq_devclass, 0, 0);
599