Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm64/nvidia/tegra210/tegra210_cpufreq.c
48266 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright 2020 Michal Meloun <[email protected]>
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/clk/clk.h>
41
#include <dev/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", DEVICE_UNIT_ANY) != NULL)
397
return;
398
if (BUS_ADD_CHILD(parent, 0, "tegra210_cpufreq", DEVICE_UNIT_ANY) == 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);
497
498