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