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