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