xref: /freebsd/sys/arm64/nvidia/tegra210/tegra210_cpufreq.c (revision f7c32ed617858bcd22f8d1b03199099d50125721)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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, max_cnt;
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 	max_cnt = min(sc->nspeed_points, *count);
263 	for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) {
264 		if (sc->cpu_max_freq < sc->speed_points[j].freq)
265 			continue;
266 		sets[i].freq = sc->speed_points[j].freq / 1000000;
267 		sets[i].volts = sc->speed_points[j].uvolt / 1000;
268 		sets[i].lat = sc->latency;
269 		sets[i].dev = dev;
270 		i++;
271 	}
272 	*count = i;
273 
274 	return (0);
275 }
276 
277 static int
278 set_cpu_freq(struct tegra210_cpufreq_softc *sc, uint64_t freq)
279 {
280 	struct cpu_speed_point *point;
281 	int rv;
282 
283 	point = get_speed_point(sc, freq);
284 
285 	/* Set PLLX frequency */
286 	rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN);
287 	if (rv != 0) {
288 		device_printf(sc->dev, "Can't set CPU clock frequency\n");
289 		return (rv);
290 	}
291 
292 	sc->act_speed_point = point;
293 
294 	return (0);
295 }
296 
297 static int
298 tegra210_cpufreq_set(device_t dev, const struct cf_setting *cf)
299 {
300 	struct tegra210_cpufreq_softc *sc;
301 	uint64_t freq;
302 	int rv;
303 
304 	if (cf == NULL || cf->freq < 0)
305 		return (EINVAL);
306 
307 	sc = device_get_softc(dev);
308 
309 	freq = cf->freq;
310 	if (freq < cpufreq_lowest_freq)
311 		freq = cpufreq_lowest_freq;
312 	freq *= 1000000;
313 	if (freq >= sc->cpu_max_freq)
314 		freq = sc->cpu_max_freq;
315 	rv = set_cpu_freq(sc, freq);
316 
317 	return (rv);
318 }
319 
320 static int
321 tegra210_cpufreq_get(device_t dev, struct cf_setting *cf)
322 {
323 	struct tegra210_cpufreq_softc *sc;
324 
325 	if (cf == NULL)
326 		return (EINVAL);
327 
328 	sc = device_get_softc(dev);
329 	memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
330 	cf->dev = NULL;
331 	cf->freq = sc->act_speed_point->freq / 1000000;
332 	cf->volts = sc->act_speed_point->uvolt / 1000;
333 	/* Transition latency in us. */
334 	cf->lat = sc->latency;
335 	/* Driver providing this setting. */
336 	cf->dev = dev;
337 
338 	return (0);
339 }
340 
341 
342 static int
343 tegra210_cpufreq_type(device_t dev, int *type)
344 {
345 
346 	if (type == NULL)
347 		return (EINVAL);
348 	*type = CPUFREQ_TYPE_ABSOLUTE;
349 
350 	return (0);
351 }
352 
353 static int
354 get_fdt_resources(struct tegra210_cpufreq_softc *sc, phandle_t node)
355 {
356 	int rv;
357 	device_t parent_dev;
358 
359 	parent_dev =  device_get_parent(sc->dev);
360 
361 	rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_g", &sc->clk_cpu_g);
362 	if (rv != 0) {
363 		device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv);
364 		return (ENXIO);
365 	}
366 
367 	rv = clk_get_by_ofw_name(parent_dev, 0, "pll_x", &sc->clk_pll_x);
368 	if (rv != 0) {
369 		device_printf(sc->dev, "Cannot get 'pll_x' clock\n");
370 		return (ENXIO);
371 	}
372 	rv = clk_get_by_ofw_name(parent_dev, 0, "pll_p", &sc->clk_pll_p);
373 	if (rv != 0) {
374 		device_printf(parent_dev, "Cannot get 'pll_p' clock\n");
375 		return (ENXIO);
376 	}
377 	rv = clk_get_by_ofw_name(parent_dev, 0, "dfll", &sc->clk_dfll);
378 
379 	/* XXX DPLL is not implemented yet */
380 #if 0
381 	if (rv != 0) {
382 		device_printf(sc->dev, "Cannot get 'dfll' clock\n");
383 		return (ENXIO);
384 	}
385 #endif
386 	return (0);
387 }
388 
389 static void
390 tegra210_cpufreq_identify(driver_t *driver, device_t parent)
391 {
392 	phandle_t root;
393 
394 	root = OF_finddevice("/");
395 	if (!ofw_bus_node_is_compatible(root, "nvidia,tegra210"))
396 		return;
397 
398 	if (device_get_unit(parent) != 0)
399 		return;
400 	if (device_find_child(parent, "tegra210_cpufreq", -1) != NULL)
401 		return;
402 	if (BUS_ADD_CHILD(parent, 0, "tegra210_cpufreq", -1) == NULL)
403 		device_printf(parent, "add child failed\n");
404 }
405 
406 static int
407 tegra210_cpufreq_probe(device_t dev)
408 {
409 
410 	device_set_desc(dev, "CPU Frequency Control");
411 
412 	return (0);
413 }
414 
415 static int
416 tegra210_cpufreq_attach(device_t dev)
417 {
418 	struct tegra210_cpufreq_softc *sc;
419 	uint64_t freq;
420 	int rv;
421 
422 	sc = device_get_softc(dev);
423 	sc->dev = dev;
424 	sc->node = ofw_bus_get_node(device_get_parent(dev));
425 
426 	sc->process_id = tegra_sku_info.cpu_process_id;
427 	sc->speedo_id = tegra_sku_info.cpu_speedo_id;
428 	sc->speedo_value = tegra_sku_info.cpu_speedo_value;
429 
430 	sc->cpu_def = &tegra210_cpu_volt_def;
431 
432 	rv = get_fdt_resources(sc, sc->node);
433 	if (rv !=  0) {
434 		return (rv);
435 	}
436 
437 	build_speed_points(sc);
438 
439 	rv = clk_get_freq(sc->clk_cpu_g, &freq);
440 	if (rv != 0) {
441 		device_printf(dev, "Can't get CPU clock frequency\n");
442 		return (rv);
443 	}
444 	if (sc->speedo_id < nitems(cpu_max_freq))
445 		sc->cpu_max_freq = cpu_max_freq[sc->speedo_id];
446 	else
447 		sc->cpu_max_freq = cpu_max_freq[0];
448 	sc->act_speed_point = get_speed_point(sc, freq);
449 
450 	/* Set safe startup CPU frequency. */
451 	rv = set_cpu_freq(sc, 1632000000);
452 	if (rv != 0) {
453 		device_printf(dev, "Can't set initial CPU clock frequency\n");
454 		return (rv);
455 	}
456 
457 	/* This device is controlled by cpufreq(4). */
458 	cpufreq_register(dev);
459 
460 	return (0);
461 }
462 
463 static int
464 tegra210_cpufreq_detach(device_t dev)
465 {
466 	struct tegra210_cpufreq_softc *sc;
467 
468 	sc = device_get_softc(dev);
469 	cpufreq_unregister(dev);
470 
471 	if (sc->clk_cpu_g != NULL)
472 		clk_release(sc->clk_cpu_g);
473 	if (sc->clk_pll_x != NULL)
474 		clk_release(sc->clk_pll_x);
475 	if (sc->clk_pll_p != NULL)
476 		clk_release(sc->clk_pll_p);
477 	if (sc->clk_dfll != NULL)
478 		clk_release(sc->clk_dfll);
479 	return (0);
480 }
481 
482 static device_method_t tegra210_cpufreq_methods[] = {
483 	/* Device interface */
484 	DEVMETHOD(device_identify,	tegra210_cpufreq_identify),
485 	DEVMETHOD(device_probe,		tegra210_cpufreq_probe),
486 	DEVMETHOD(device_attach,	tegra210_cpufreq_attach),
487 	DEVMETHOD(device_detach,	tegra210_cpufreq_detach),
488 
489 	/* cpufreq interface */
490 	DEVMETHOD(cpufreq_drv_set,	tegra210_cpufreq_set),
491 	DEVMETHOD(cpufreq_drv_get,	tegra210_cpufreq_get),
492 	DEVMETHOD(cpufreq_drv_settings,	tegra210_cpufreq_settings),
493 	DEVMETHOD(cpufreq_drv_type,	tegra210_cpufreq_type),
494 
495 	DEVMETHOD_END
496 };
497 
498 static devclass_t tegra210_cpufreq_devclass;
499 static DEFINE_CLASS_0(tegra210_cpufreq, tegra210_cpufreq_driver,
500     tegra210_cpufreq_methods, sizeof(struct tegra210_cpufreq_softc));
501 DRIVER_MODULE(tegra210_cpufreq, cpu, tegra210_cpufreq_driver,
502     tegra210_cpufreq_devclass, NULL, NULL);
503