]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/nvidia/tegra210/tegra210_cpufreq.c
Notable upstream pull request merges:
[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/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/extres/clk/clk.h>
41 #include <dev/extres/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);