]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/nvidia/tegra210/tegra210_cpufreq.c
unbound: Vendor import 1.19.0
[FreeBSD/FreeBSD.git] / sys / arm64 / nvidia / tegra210 / tegra210_cpufreq.c
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);