xref: /freebsd/sys/arm64/nvidia/tegra210/tegra210_cpufreq.c (revision 66fd12cf4896eb08ad8e7a2627537f84ead84dd3)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright 2020 Michal Meloun <mmel@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/bus.h>
34 #include <sys/cpu.h>
35 #include <sys/kernel.h>
36 #include <sys/lock.h>
37 #include <sys/malloc.h>
38 #include <sys/module.h>
39 
40 #include <machine/bus.h>
41 #include <machine/cpu.h>
42 
43 #include <dev/extres/clk/clk.h>
44 #include <dev/extres/regulator/regulator.h>
45 #include <dev/ofw/ofw_bus_subr.h>
46 
47 #include <arm/nvidia/tegra_efuse.h>
48 
49 #include "cpufreq_if.h"
50 
51 /* CPU voltage table entry */
52 struct speedo_entry {
53 	uint64_t		freq; 	/* Frequency point */
54 	int			c0; 	/* Coeeficient values for */
55 	int			c1;	/* quadratic equation: */
56 	int 			c2;	/* c2 * speedo^2 + c1 * speedo + c0 */
57 };
58 
59 struct cpu_volt_def {
60 	int			min_uvolt;	/* Min allowed CPU voltage */
61 	int			max_uvolt;	/* Max allowed CPU voltage */
62 	int 			step_uvolt; 	/* Step of CPU voltage */
63 	int			speedo_scale;	/* Scaling factor for cvt */
64 	int			speedo_nitems;	/* Size of speedo table */
65 	struct speedo_entry	*speedo_tbl;	/* CPU voltage table */
66 };
67 
68 struct cpu_speed_point {
69 	uint64_t		freq;		/* Frequecy */
70 	int			uvolt;		/* Requested voltage */
71 };
72 
73 static struct speedo_entry tegra210_speedo_tbl[] =
74 {
75 	{204000000UL,	1007452, -23865, 370},
76 	{306000000UL,	1052709, -24875, 370},
77 	{408000000UL,	1099069, -25895, 370},
78 	{510000000UL,	1146534, -26905, 370},
79 	{612000000UL,	1195102, -27915, 370},
80 	{714000000UL,	1244773, -28925, 370},
81 	{816000000UL,	1295549, -29935, 370},
82 	{918000000UL,	1347428, -30955, 370},
83 	{1020000000UL,	1400411, -31965, 370},
84 	{1122000000UL,	1454497, -32975, 370},
85 	{1224000000UL,	1509687, -33985, 370},
86 	{1326000000UL,	1565981, -35005, 370},
87 	{1428000000UL,	1623379, -36015, 370},
88 	{1530000000UL,	1681880, -37025, 370},
89 	{1632000000UL,	1741485, -38035, 370},
90 	{1734000000UL,	1802194, -39055, 370},
91 	{1836000000UL,	1864006, -40065, 370},
92 	{1912500000UL,	1910780, -40815, 370},
93 	{2014500000UL,	1227000,      0,   0},
94 	{2218500000UL,	1227000,      0,   0},
95 };
96 
97 static struct cpu_volt_def tegra210_cpu_volt_def =
98 {
99 	.min_uvolt = 900000,		/* 0.9 V */
100 	.max_uvolt = 1227000,		/* 1.227 */
101 	.step_uvolt =  10000,		/* 10 mV */
102 	.speedo_scale = 100,
103 	.speedo_nitems = nitems(tegra210_speedo_tbl),
104 	.speedo_tbl = tegra210_speedo_tbl,
105 };
106 
107 static uint64_t cpu_max_freq[] = {
108 	1912500000UL,
109 	1912500000UL,
110 	2218500000UL,
111 	1785000000UL,
112 	1632000000UL,
113 	1912500000UL,
114 	2014500000UL,
115 	1734000000UL,
116 	1683000000UL,
117 	1555500000UL,
118 	1504500000UL,
119 };
120 
121 static uint64_t cpu_freq_tbl[] = {
122 	 204000000UL,
123 	 306000000UL,
124 	 408000000UL,
125 	 510000000UL,
126 	 612000000UL,
127 	 714000000UL,
128 	 816000000UL,
129 	 918000000UL,
130 	1020000000UL,
131 	1122000000UL,
132 	1224000000UL,
133 	1326000000UL,
134 	1428000000UL,
135 	1530000000UL,
136 	1632000000UL,
137 	1734000000UL,
138 	1836000000UL,
139 	1912500000UL,
140 	2014500000UL,
141 	2218500000UL,
142 };
143 
144 struct tegra210_cpufreq_softc {
145 	device_t		dev;
146 	phandle_t		node;
147 
148 	clk_t			clk_cpu_g;
149 	clk_t			clk_pll_x;
150 	clk_t			clk_pll_p;
151 	clk_t			clk_dfll;
152 
153 	int 			process_id;
154 	int 			speedo_id;
155 	int 			speedo_value;
156 
157 	uint64_t		cpu_max_freq;
158 	struct cpu_volt_def	*cpu_def;
159 	struct cpu_speed_point	*speed_points;
160 	int			nspeed_points;
161 
162 	struct cpu_speed_point	*act_speed_point;
163 
164 	int			latency;
165 };
166 
167 static int cpufreq_lowest_freq = 1;
168 TUNABLE_INT("hw.tegra210.cpufreq.lowest_freq", &cpufreq_lowest_freq);
169 
170 #define	DIV_ROUND_CLOSEST(val, div)	(((val) + ((div) / 2)) / (div))
171 
172 #define	ROUND_UP(val, div)	roundup(val, div)
173 #define	ROUND_DOWN(val, div)	rounddown(val, div)
174 
175 /*
176  * Compute requesetd voltage for given frequency and SoC process variations,
177  * - compute base voltage from speedo value using speedo table
178  * - round up voltage to next regulator step
179  * - clamp it to regulator limits
180  */
181 static int
182 freq_to_voltage(struct tegra210_cpufreq_softc *sc, uint64_t freq)
183 {
184 	int uv, scale, min_uvolt, max_uvolt, step_uvolt;
185 	struct speedo_entry *ent;
186 	int i;
187 
188 	/* Get speedo entry with higher frequency */
189 	ent = NULL;
190 	for (i = 0; i < sc->cpu_def->speedo_nitems; i++) {
191 		if (sc->cpu_def->speedo_tbl[i].freq >= freq) {
192 			ent = &sc->cpu_def->speedo_tbl[i];
193 			break;
194 		}
195 	}
196 	if (ent == NULL)
197 		ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1];
198 	scale = sc->cpu_def->speedo_scale;
199 
200 
201 	/* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */
202 	uv = DIV_ROUND_CLOSEST(ent->c2 * sc->speedo_value, scale);
203 	uv = DIV_ROUND_CLOSEST((uv + ent->c1) * sc->speedo_value, scale) +
204 	    ent->c0;
205 	step_uvolt = sc->cpu_def->step_uvolt;
206 	/* Round up it to next regulator step */
207 	uv = ROUND_UP(uv, step_uvolt);
208 
209 	/* Clamp result */
210 	min_uvolt = ROUND_UP(sc->cpu_def->min_uvolt, step_uvolt);
211 	max_uvolt = ROUND_DOWN(sc->cpu_def->max_uvolt, step_uvolt);
212 	if (uv < min_uvolt)
213 		uv =  min_uvolt;
214 	if (uv > max_uvolt)
215 		uv =  max_uvolt;
216 	return (uv);
217 
218 }
219 
220 static void
221 build_speed_points(struct tegra210_cpufreq_softc *sc) {
222 	int i;
223 
224 	sc->nspeed_points = nitems(cpu_freq_tbl);
225 	sc->speed_points = malloc(sizeof(struct cpu_speed_point) *
226 	    sc->nspeed_points, M_DEVBUF, M_NOWAIT);
227 	for (i = 0; i < sc->nspeed_points; i++) {
228 		sc->speed_points[i].freq = cpu_freq_tbl[i];
229 		sc->speed_points[i].uvolt = freq_to_voltage(sc,
230 		    cpu_freq_tbl[i]);
231 	}
232 }
233 
234 static struct cpu_speed_point *
235 get_speed_point(struct tegra210_cpufreq_softc *sc, uint64_t freq)
236 {
237 	int i;
238 
239 	if (sc->speed_points[0].freq >= freq)
240 		return (sc->speed_points + 0);
241 
242 	for (i = 0; i < sc->nspeed_points - 1; i++) {
243 		if (sc->speed_points[i + 1].freq > freq)
244 			return (sc->speed_points + i);
245 	}
246 
247 	return (sc->speed_points + sc->nspeed_points - 1);
248 }
249 
250 static int
251 tegra210_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count)
252 {
253 	struct tegra210_cpufreq_softc *sc;
254 	int i, j;
255 
256 	if (sets == NULL || count == NULL)
257 		return (EINVAL);
258 
259 	sc = device_get_softc(dev);
260 	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count));
261 
262 	for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) {
263 		if (sc->cpu_max_freq < sc->speed_points[j].freq)
264 			continue;
265 		sets[i].freq = sc->speed_points[j].freq / 1000000;
266 		sets[i].volts = sc->speed_points[j].uvolt / 1000;
267 		sets[i].lat = sc->latency;
268 		sets[i].dev = dev;
269 		i++;
270 	}
271 	*count = i;
272 
273 	return (0);
274 }
275 
276 static int
277 set_cpu_freq(struct tegra210_cpufreq_softc *sc, uint64_t freq)
278 {
279 	struct cpu_speed_point *point;
280 	int rv;
281 
282 	point = get_speed_point(sc, freq);
283 
284 	/* Set PLLX frequency */
285 	rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN);
286 	if (rv != 0) {
287 		device_printf(sc->dev, "Can't set CPU clock frequency\n");
288 		return (rv);
289 	}
290 
291 	sc->act_speed_point = point;
292 
293 	return (0);
294 }
295 
296 static int
297 tegra210_cpufreq_set(device_t dev, const struct cf_setting *cf)
298 {
299 	struct tegra210_cpufreq_softc *sc;
300 	uint64_t freq;
301 	int rv;
302 
303 	if (cf == NULL || cf->freq < 0)
304 		return (EINVAL);
305 
306 	sc = device_get_softc(dev);
307 
308 	freq = cf->freq;
309 	if (freq < cpufreq_lowest_freq)
310 		freq = cpufreq_lowest_freq;
311 	freq *= 1000000;
312 	if (freq >= sc->cpu_max_freq)
313 		freq = sc->cpu_max_freq;
314 	rv = set_cpu_freq(sc, freq);
315 
316 	return (rv);
317 }
318 
319 static int
320 tegra210_cpufreq_get(device_t dev, struct cf_setting *cf)
321 {
322 	struct tegra210_cpufreq_softc *sc;
323 
324 	if (cf == NULL)
325 		return (EINVAL);
326 
327 	sc = device_get_softc(dev);
328 	memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
329 	cf->dev = NULL;
330 	cf->freq = sc->act_speed_point->freq / 1000000;
331 	cf->volts = sc->act_speed_point->uvolt / 1000;
332 	/* Transition latency in us. */
333 	cf->lat = sc->latency;
334 	/* Driver providing this setting. */
335 	cf->dev = dev;
336 
337 	return (0);
338 }
339 
340 
341 static int
342 tegra210_cpufreq_type(device_t dev, int *type)
343 {
344 
345 	if (type == NULL)
346 		return (EINVAL);
347 	*type = CPUFREQ_TYPE_ABSOLUTE;
348 
349 	return (0);
350 }
351 
352 static int
353 get_fdt_resources(struct tegra210_cpufreq_softc *sc, phandle_t node)
354 {
355 	int rv;
356 	device_t parent_dev;
357 
358 	parent_dev =  device_get_parent(sc->dev);
359 
360 	rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_g", &sc->clk_cpu_g);
361 	if (rv != 0) {
362 		device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv);
363 		return (ENXIO);
364 	}
365 
366 	rv = clk_get_by_ofw_name(parent_dev, 0, "pll_x", &sc->clk_pll_x);
367 	if (rv != 0) {
368 		device_printf(sc->dev, "Cannot get 'pll_x' clock\n");
369 		return (ENXIO);
370 	}
371 	rv = clk_get_by_ofw_name(parent_dev, 0, "pll_p", &sc->clk_pll_p);
372 	if (rv != 0) {
373 		device_printf(parent_dev, "Cannot get 'pll_p' clock\n");
374 		return (ENXIO);
375 	}
376 	rv = clk_get_by_ofw_name(parent_dev, 0, "dfll", &sc->clk_dfll);
377 
378 	/* XXX DPLL is not implemented yet */
379 #if 0
380 	if (rv != 0) {
381 		device_printf(sc->dev, "Cannot get 'dfll' clock\n");
382 		return (ENXIO);
383 	}
384 #endif
385 	return (0);
386 }
387 
388 static void
389 tegra210_cpufreq_identify(driver_t *driver, device_t parent)
390 {
391 	phandle_t root;
392 
393 	root = OF_finddevice("/");
394 	if (!ofw_bus_node_is_compatible(root, "nvidia,tegra210"))
395 		return;
396 
397 	if (device_get_unit(parent) != 0)
398 		return;
399 	if (device_find_child(parent, "tegra210_cpufreq", -1) != NULL)
400 		return;
401 	if (BUS_ADD_CHILD(parent, 0, "tegra210_cpufreq", -1) == NULL)
402 		device_printf(parent, "add child failed\n");
403 }
404 
405 static int
406 tegra210_cpufreq_probe(device_t dev)
407 {
408 
409 	device_set_desc(dev, "CPU Frequency Control");
410 
411 	return (0);
412 }
413 
414 static int
415 tegra210_cpufreq_attach(device_t dev)
416 {
417 	struct tegra210_cpufreq_softc *sc;
418 	uint64_t freq;
419 	int rv;
420 
421 	sc = device_get_softc(dev);
422 	sc->dev = dev;
423 	sc->node = ofw_bus_get_node(device_get_parent(dev));
424 
425 	sc->process_id = tegra_sku_info.cpu_process_id;
426 	sc->speedo_id = tegra_sku_info.cpu_speedo_id;
427 	sc->speedo_value = tegra_sku_info.cpu_speedo_value;
428 
429 	sc->cpu_def = &tegra210_cpu_volt_def;
430 
431 	rv = get_fdt_resources(sc, sc->node);
432 	if (rv !=  0) {
433 		return (rv);
434 	}
435 
436 	build_speed_points(sc);
437 
438 	rv = clk_get_freq(sc->clk_cpu_g, &freq);
439 	if (rv != 0) {
440 		device_printf(dev, "Can't get CPU clock frequency\n");
441 		return (rv);
442 	}
443 	if (sc->speedo_id < nitems(cpu_max_freq))
444 		sc->cpu_max_freq = cpu_max_freq[sc->speedo_id];
445 	else
446 		sc->cpu_max_freq = cpu_max_freq[0];
447 	sc->act_speed_point = get_speed_point(sc, freq);
448 
449 	/* Set safe startup CPU frequency. */
450 	rv = set_cpu_freq(sc, 1632000000);
451 	if (rv != 0) {
452 		device_printf(dev, "Can't set initial CPU clock frequency\n");
453 		return (rv);
454 	}
455 
456 	/* This device is controlled by cpufreq(4). */
457 	cpufreq_register(dev);
458 
459 	return (0);
460 }
461 
462 static int
463 tegra210_cpufreq_detach(device_t dev)
464 {
465 	struct tegra210_cpufreq_softc *sc;
466 
467 	sc = device_get_softc(dev);
468 	cpufreq_unregister(dev);
469 
470 	if (sc->clk_cpu_g != NULL)
471 		clk_release(sc->clk_cpu_g);
472 	if (sc->clk_pll_x != NULL)
473 		clk_release(sc->clk_pll_x);
474 	if (sc->clk_pll_p != NULL)
475 		clk_release(sc->clk_pll_p);
476 	if (sc->clk_dfll != NULL)
477 		clk_release(sc->clk_dfll);
478 	return (0);
479 }
480 
481 static device_method_t tegra210_cpufreq_methods[] = {
482 	/* Device interface */
483 	DEVMETHOD(device_identify,	tegra210_cpufreq_identify),
484 	DEVMETHOD(device_probe,		tegra210_cpufreq_probe),
485 	DEVMETHOD(device_attach,	tegra210_cpufreq_attach),
486 	DEVMETHOD(device_detach,	tegra210_cpufreq_detach),
487 
488 	/* cpufreq interface */
489 	DEVMETHOD(cpufreq_drv_set,	tegra210_cpufreq_set),
490 	DEVMETHOD(cpufreq_drv_get,	tegra210_cpufreq_get),
491 	DEVMETHOD(cpufreq_drv_settings,	tegra210_cpufreq_settings),
492 	DEVMETHOD(cpufreq_drv_type,	tegra210_cpufreq_type),
493 
494 	DEVMETHOD_END
495 };
496 
497 static DEFINE_CLASS_0(tegra210_cpufreq, tegra210_cpufreq_driver,
498     tegra210_cpufreq_methods, sizeof(struct tegra210_cpufreq_softc));
499 DRIVER_MODULE(tegra210_cpufreq, cpu, tegra210_cpufreq_driver, NULL, NULL);
500