mtw(4) remove misplaced DEBUG_FLAGS
[freebsd/src.git] / sys / arm64 / nvidia / tegra210 / tegra210_cpufreq.c
blob9b248a09bd5892d0161c73e1dfd6afa12b31315d
1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright 2020 Michal Meloun <mmel@FreeBSD.org>
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.
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.
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>
37 #include <machine/bus.h>
38 #include <machine/cpu.h>
40 #include <dev/clk/clk.h>
41 #include <dev/regulator/regulator.h>
42 #include <dev/ofw/ofw_bus_subr.h>
44 #include <arm/nvidia/tegra_efuse.h>
46 #include "cpufreq_if.h"
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 */
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 */
65 struct cpu_speed_point {
66 uint64_t freq; /* Frequecy */
67 int uvolt; /* Requested voltage */
70 static struct speedo_entry tegra210_speedo_tbl[] =
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},
94 static struct cpu_volt_def tegra210_cpu_volt_def =
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,
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,
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,
141 struct tegra210_cpufreq_softc {
142 device_t dev;
143 phandle_t node;
145 clk_t clk_cpu_g;
146 clk_t clk_pll_x;
147 clk_t clk_pll_p;
148 clk_t clk_dfll;
150 int process_id;
151 int speedo_id;
152 int speedo_value;
154 uint64_t cpu_max_freq;
155 struct cpu_volt_def *cpu_def;
156 struct cpu_speed_point *speed_points;
157 int nspeed_points;
159 struct cpu_speed_point *act_speed_point;
161 int latency;
164 static int cpufreq_lowest_freq = 1;
165 TUNABLE_INT("hw.tegra210.cpufreq.lowest_freq", &cpufreq_lowest_freq);
167 #define DIV_ROUND_CLOSEST(val, div) (((val) + ((div) / 2)) / (div))
169 #define ROUND_UP(val, div) roundup(val, div)
170 #define ROUND_DOWN(val, div) rounddown(val, div)
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
178 static int
179 freq_to_voltage(struct tegra210_cpufreq_softc *sc, uint64_t freq)
181 int uv, scale, min_uvolt, max_uvolt, step_uvolt;
182 struct speedo_entry *ent;
183 int i;
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;
193 if (ent == NULL)
194 ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1];
195 scale = sc->cpu_def->speedo_scale;
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);
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);
217 static void
218 build_speed_points(struct tegra210_cpufreq_softc *sc) {
219 int i;
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]);
231 static struct cpu_speed_point *
232 get_speed_point(struct tegra210_cpufreq_softc *sc, uint64_t freq)
234 int i;
236 if (sc->speed_points[0].freq >= freq)
237 return (sc->speed_points + 0);
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);
244 return (sc->speed_points + sc->nspeed_points - 1);
247 static int
248 tegra210_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count)
250 struct tegra210_cpufreq_softc *sc;
251 int i, j;
253 if (sets == NULL || count == NULL)
254 return (EINVAL);
256 sc = device_get_softc(dev);
257 memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count));
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++;
268 *count = i;
270 return (0);
273 static int
274 set_cpu_freq(struct tegra210_cpufreq_softc *sc, uint64_t freq)
276 struct cpu_speed_point *point;
277 int rv;
279 point = get_speed_point(sc, freq);
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);
288 sc->act_speed_point = point;
290 return (0);
293 static int
294 tegra210_cpufreq_set(device_t dev, const struct cf_setting *cf)
296 struct tegra210_cpufreq_softc *sc;
297 uint64_t freq;
298 int rv;
300 if (cf == NULL || cf->freq < 0)
301 return (EINVAL);
303 sc = device_get_softc(dev);
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);
313 return (rv);
316 static int
317 tegra210_cpufreq_get(device_t dev, struct cf_setting *cf)
319 struct tegra210_cpufreq_softc *sc;
321 if (cf == NULL)
322 return (EINVAL);
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;
334 return (0);
338 static int
339 tegra210_cpufreq_type(device_t dev, int *type)
342 if (type == NULL)
343 return (EINVAL);
344 *type = CPUFREQ_TYPE_ABSOLUTE;
346 return (0);
349 static int
350 get_fdt_resources(struct tegra210_cpufreq_softc *sc, phandle_t node)
352 int rv;
353 device_t parent_dev;
355 parent_dev = device_get_parent(sc->dev);
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);
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);
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);
373 rv = clk_get_by_ofw_name(parent_dev, 0, "dfll", &sc->clk_dfll);
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);
381 #endif
382 return (0);
385 static void
386 tegra210_cpufreq_identify(driver_t *driver, device_t parent)
388 phandle_t root;
390 root = OF_finddevice("/");
391 if (!ofw_bus_node_is_compatible(root, "nvidia,tegra210"))
392 return;
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");
402 static int
403 tegra210_cpufreq_probe(device_t dev)
406 device_set_desc(dev, "CPU Frequency Control");
408 return (0);
411 static int
412 tegra210_cpufreq_attach(device_t dev)
414 struct tegra210_cpufreq_softc *sc;
415 uint64_t freq;
416 int rv;
418 sc = device_get_softc(dev);
419 sc->dev = dev;
420 sc->node = ofw_bus_get_node(device_get_parent(dev));
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;
426 sc->cpu_def = &tegra210_cpu_volt_def;
428 rv = get_fdt_resources(sc, sc->node);
429 if (rv != 0) {
430 return (rv);
433 build_speed_points(sc);
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);
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);
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);
453 /* This device is controlled by cpufreq(4). */
454 cpufreq_register(dev);
456 return (0);
459 static int
460 tegra210_cpufreq_detach(device_t dev)
462 struct tegra210_cpufreq_softc *sc;
464 sc = device_get_softc(dev);
465 cpufreq_unregister(dev);
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);
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),
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),
491 DEVMETHOD_END
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);