Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/nvidia/tegra124/tegra124_cpufreq.c
39507 views
1
/*-
2
* Copyright (c) 2016 Michal Meloun <[email protected]>
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/param.h>
28
#include <sys/systm.h>
29
#include <sys/bus.h>
30
#include <sys/cpu.h>
31
#include <sys/kernel.h>
32
#include <sys/lock.h>
33
#include <sys/malloc.h>
34
#include <sys/module.h>
35
36
#include <machine/bus.h>
37
#include <machine/cpu.h>
38
39
#include <dev/clk/clk.h>
40
#include <dev/regulator/regulator.h>
41
#include <dev/ofw/ofw_bus_subr.h>
42
43
#include <arm/nvidia/tegra_efuse.h>
44
45
#include "cpufreq_if.h"
46
47
#define XXX
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 tegra124_speedo_dpll_tbl[] =
72
{
73
{ 204000000ULL, 1112619, -29295, 402},
74
{ 306000000ULL, 1150460, -30585, 402},
75
{ 408000000ULL, 1190122, -31865, 402},
76
{ 510000000ULL, 1231606, -33155, 402},
77
{ 612000000ULL, 1274912, -34435, 402},
78
{ 714000000ULL, 1320040, -35725, 402},
79
{ 816000000ULL, 1366990, -37005, 402},
80
{ 918000000ULL, 1415762, -38295, 402},
81
{1020000000ULL, 1466355, -39575, 402},
82
{1122000000ULL, 1518771, -40865, 402},
83
{1224000000ULL, 1573009, -42145, 402},
84
{1326000000ULL, 1629068, -43435, 402},
85
{1428000000ULL, 1686950, -44715, 402},
86
{1530000000ULL, 1746653, -46005, 402},
87
{1632000000ULL, 1808179, -47285, 402},
88
{1734000000ULL, 1871526, -48575, 402},
89
{1836000000ULL, 1936696, -49855, 402},
90
{1938000000ULL, 2003687, -51145, 402},
91
{2014500000ULL, 2054787, -52095, 402},
92
{2116500000ULL, 2124957, -53385, 402},
93
{2218500000ULL, 2196950, -54665, 402},
94
{2320500000ULL, 2270765, -55955, 402},
95
{2320500000ULL, 2270765, -55955, 402},
96
{2422500000ULL, 2346401, -57235, 402},
97
{2524500000ULL, 2437299, -58535, 402},
98
};
99
100
static struct cpu_volt_def tegra124_cpu_volt_dpll_def =
101
{
102
.min_uvolt = 900000, /* 0.9 V */
103
.max_uvolt = 1260000, /* 1.26 */
104
.step_uvolt = 10000, /* 10 mV */
105
.speedo_scale = 100,
106
.speedo_nitems = nitems(tegra124_speedo_dpll_tbl),
107
.speedo_tbl = tegra124_speedo_dpll_tbl,
108
};
109
110
static struct speedo_entry tegra124_speedo_pllx_tbl[] =
111
{
112
{ 204000000ULL, 800000, 0, 0},
113
{ 306000000ULL, 800000, 0, 0},
114
{ 408000000ULL, 800000, 0, 0},
115
{ 510000000ULL, 800000, 0, 0},
116
{ 612000000ULL, 800000, 0, 0},
117
{ 714000000ULL, 800000, 0, 0},
118
{ 816000000ULL, 820000, 0, 0},
119
{ 918000000ULL, 840000, 0, 0},
120
{1020000000ULL, 880000, 0, 0},
121
{1122000000ULL, 900000, 0, 0},
122
{1224000000ULL, 930000, 0, 0},
123
{1326000000ULL, 960000, 0, 0},
124
{1428000000ULL, 990000, 0, 0},
125
{1530000000ULL, 1020000, 0, 0},
126
{1632000000ULL, 1070000, 0, 0},
127
{1734000000ULL, 1100000, 0, 0},
128
{1836000000ULL, 1140000, 0, 0},
129
{1938000000ULL, 1180000, 0, 0},
130
{2014500000ULL, 1220000, 0, 0},
131
{2116500000ULL, 1260000, 0, 0},
132
{2218500000ULL, 1310000, 0, 0},
133
{2320500000ULL, 1360000, 0, 0},
134
{2397000000ULL, 1400000, 0, 0},
135
{2499000000ULL, 1400000, 0, 0},
136
};
137
138
static struct cpu_volt_def tegra124_cpu_volt_pllx_def =
139
{
140
.min_uvolt = 1000000, /* XXX 0.9 V doesn't work on all boards */
141
.max_uvolt = 1260000, /* 1.26 */
142
.step_uvolt = 10000, /* 10 mV */
143
.speedo_scale = 100,
144
.speedo_nitems = nitems(tegra124_speedo_pllx_tbl),
145
.speedo_tbl = tegra124_speedo_pllx_tbl,
146
};
147
148
static uint64_t cpu_freq_tbl[] = {
149
204000000ULL,
150
306000000ULL,
151
408000000ULL,
152
510000000ULL,
153
612000000ULL,
154
714000000ULL,
155
816000000ULL,
156
918000000ULL,
157
1020000000ULL,
158
1122000000ULL,
159
1224000000ULL,
160
1326000000ULL,
161
1428000000ULL,
162
1530000000ULL,
163
1632000000ULL,
164
1734000000ULL,
165
1836000000ULL,
166
1938000000ULL,
167
2014000000ULL,
168
2116000000ULL,
169
2218000000ULL,
170
2320000000ULL,
171
2422000000ULL,
172
2524000000ULL,
173
};
174
175
static uint64_t cpu_max_freq[] = {
176
2014500000ULL,
177
2320500000ULL,
178
2116500000ULL,
179
2524500000ULL,
180
};
181
182
struct tegra124_cpufreq_softc {
183
device_t dev;
184
phandle_t node;
185
186
regulator_t supply_vdd_cpu;
187
clk_t clk_cpu_g;
188
clk_t clk_cpu_lp;
189
clk_t clk_pll_x;
190
clk_t clk_pll_p;
191
clk_t clk_dfll;
192
193
int process_id;
194
int speedo_id;
195
int speedo_value;
196
197
uint64_t cpu_max_freq;
198
struct cpu_volt_def *cpu_def;
199
struct cpu_speed_point *speed_points;
200
int nspeed_points;
201
202
struct cpu_speed_point *act_speed_point;
203
204
int latency;
205
};
206
207
static int cpufreq_lowest_freq = 1;
208
TUNABLE_INT("hw.tegra124.cpufreq.lowest_freq", &cpufreq_lowest_freq);
209
210
#define DIV_ROUND_CLOSEST(val, div) (((val) + ((div) / 2)) / (div))
211
212
#define ROUND_UP(val, div) roundup(val, div)
213
#define ROUND_DOWN(val, div) rounddown(val, div)
214
215
/*
216
* Compute requesetd voltage for given frequency and SoC process variations,
217
* - compute base voltage from speedo value using speedo table
218
* - round up voltage to next regulator step
219
* - clamp it to regulator limits
220
*/
221
static int
222
freq_to_voltage(struct tegra124_cpufreq_softc *sc, uint64_t freq)
223
{
224
int uv, scale, min_uvolt, max_uvolt, step_uvolt;
225
struct speedo_entry *ent;
226
int i;
227
228
/* Get speedo entry with higher frequency */
229
ent = NULL;
230
for (i = 0; i < sc->cpu_def->speedo_nitems; i++) {
231
if (sc->cpu_def->speedo_tbl[i].freq >= freq) {
232
ent = &sc->cpu_def->speedo_tbl[i];
233
break;
234
}
235
}
236
if (ent == NULL)
237
ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1];
238
scale = sc->cpu_def->speedo_scale;
239
240
/* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */
241
uv = DIV_ROUND_CLOSEST(ent->c2 * sc->speedo_value, scale);
242
uv = DIV_ROUND_CLOSEST((uv + ent->c1) * sc->speedo_value, scale) +
243
ent->c0;
244
step_uvolt = sc->cpu_def->step_uvolt;
245
/* Round up it to next regulator step */
246
uv = ROUND_UP(uv, step_uvolt);
247
248
/* Clamp result */
249
min_uvolt = ROUND_UP(sc->cpu_def->min_uvolt, step_uvolt);
250
max_uvolt = ROUND_DOWN(sc->cpu_def->max_uvolt, step_uvolt);
251
if (uv < min_uvolt)
252
uv = min_uvolt;
253
if (uv > max_uvolt)
254
uv = max_uvolt;
255
return (uv);
256
257
}
258
259
static void
260
build_speed_points(struct tegra124_cpufreq_softc *sc) {
261
int i;
262
263
sc->nspeed_points = nitems(cpu_freq_tbl);
264
sc->speed_points = malloc(sizeof(struct cpu_speed_point) *
265
sc->nspeed_points, M_DEVBUF, M_NOWAIT);
266
for (i = 0; i < sc->nspeed_points; i++) {
267
sc->speed_points[i].freq = cpu_freq_tbl[i];
268
sc->speed_points[i].uvolt = freq_to_voltage(sc,
269
cpu_freq_tbl[i]);
270
}
271
}
272
273
static struct cpu_speed_point *
274
get_speed_point(struct tegra124_cpufreq_softc *sc, uint64_t freq)
275
{
276
int i;
277
278
if (sc->speed_points[0].freq >= freq)
279
return (sc->speed_points + 0);
280
281
for (i = 0; i < sc->nspeed_points - 1; i++) {
282
if (sc->speed_points[i + 1].freq > freq)
283
return (sc->speed_points + i);
284
}
285
286
return (sc->speed_points + sc->nspeed_points - 1);
287
}
288
289
static int
290
tegra124_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count)
291
{
292
struct tegra124_cpufreq_softc *sc;
293
int i, j;
294
295
if (sets == NULL || count == NULL)
296
return (EINVAL);
297
298
sc = device_get_softc(dev);
299
memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count));
300
301
for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) {
302
if (sc->cpu_max_freq < sc->speed_points[j].freq)
303
continue;
304
sets[i].freq = sc->speed_points[j].freq / 1000000;
305
sets[i].volts = sc->speed_points[j].uvolt / 1000;
306
sets[i].lat = sc->latency;
307
sets[i].dev = dev;
308
i++;
309
}
310
*count = i;
311
312
return (0);
313
}
314
315
static int
316
set_cpu_freq(struct tegra124_cpufreq_softc *sc, uint64_t freq)
317
{
318
struct cpu_speed_point *point;
319
int rv;
320
321
point = get_speed_point(sc, freq);
322
323
if (sc->act_speed_point->uvolt < point->uvolt) {
324
/* set cpu voltage */
325
rv = regulator_set_voltage(sc->supply_vdd_cpu,
326
point->uvolt, point->uvolt);
327
DELAY(10000);
328
if (rv != 0)
329
return (rv);
330
}
331
332
/* Switch supermux to PLLP first */
333
rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_p);
334
if (rv != 0) {
335
device_printf(sc->dev, "Can't set parent to PLLP\n");
336
return (rv);
337
}
338
339
/* Set PLLX frequency */
340
rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN);
341
if (rv != 0) {
342
device_printf(sc->dev, "Can't set CPU clock frequency\n");
343
return (rv);
344
}
345
346
rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_x);
347
if (rv != 0) {
348
device_printf(sc->dev, "Can't set parent to PLLX\n");
349
return (rv);
350
}
351
352
if (sc->act_speed_point->uvolt > point->uvolt) {
353
/* set cpu voltage */
354
rv = regulator_set_voltage(sc->supply_vdd_cpu,
355
point->uvolt, point->uvolt);
356
if (rv != 0)
357
return (rv);
358
}
359
360
sc->act_speed_point = point;
361
362
return (0);
363
}
364
365
static int
366
tegra124_cpufreq_set(device_t dev, const struct cf_setting *cf)
367
{
368
struct tegra124_cpufreq_softc *sc;
369
uint64_t freq;
370
int rv;
371
372
if (cf == NULL || cf->freq < 0)
373
return (EINVAL);
374
375
sc = device_get_softc(dev);
376
377
freq = cf->freq;
378
if (freq < cpufreq_lowest_freq)
379
freq = cpufreq_lowest_freq;
380
freq *= 1000000;
381
if (freq >= sc->cpu_max_freq)
382
freq = sc->cpu_max_freq;
383
rv = set_cpu_freq(sc, freq);
384
385
return (rv);
386
}
387
388
static int
389
tegra124_cpufreq_get(device_t dev, struct cf_setting *cf)
390
{
391
struct tegra124_cpufreq_softc *sc;
392
393
if (cf == NULL)
394
return (EINVAL);
395
396
sc = device_get_softc(dev);
397
memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
398
cf->dev = NULL;
399
cf->freq = sc->act_speed_point->freq / 1000000;
400
cf->volts = sc->act_speed_point->uvolt / 1000;
401
/* Transition latency in us. */
402
cf->lat = sc->latency;
403
/* Driver providing this setting. */
404
cf->dev = dev;
405
406
return (0);
407
}
408
409
static int
410
tegra124_cpufreq_type(device_t dev, int *type)
411
{
412
413
if (type == NULL)
414
return (EINVAL);
415
*type = CPUFREQ_TYPE_ABSOLUTE;
416
417
return (0);
418
}
419
420
static int
421
get_fdt_resources(struct tegra124_cpufreq_softc *sc, phandle_t node)
422
{
423
int rv;
424
device_t parent_dev;
425
426
parent_dev = device_get_parent(sc->dev);
427
rv = regulator_get_by_ofw_property(parent_dev, 0, "vdd-cpu-supply",
428
&sc->supply_vdd_cpu);
429
if (rv != 0) {
430
device_printf(sc->dev, "Cannot get 'vdd-cpu' regulator\n");
431
return (rv);
432
}
433
434
rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_g", &sc->clk_cpu_g);
435
if (rv != 0) {
436
device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv);
437
return (ENXIO);
438
}
439
440
rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_lp", &sc->clk_cpu_lp);
441
if (rv != 0) {
442
device_printf(sc->dev, "Cannot get 'cpu_lp' clock\n");
443
return (ENXIO);
444
}
445
446
rv = clk_get_by_ofw_name(parent_dev, 0, "pll_x", &sc->clk_pll_x);
447
if (rv != 0) {
448
device_printf(sc->dev, "Cannot get 'pll_x' clock\n");
449
return (ENXIO);
450
}
451
rv = clk_get_by_ofw_name(parent_dev, 0, "pll_p", &sc->clk_pll_p);
452
if (rv != 0) {
453
device_printf(parent_dev, "Cannot get 'pll_p' clock\n");
454
return (ENXIO);
455
}
456
rv = clk_get_by_ofw_name(parent_dev, 0, "dfll", &sc->clk_dfll);
457
if (rv != 0) {
458
/* XXX DPLL is not implemented yet */
459
/*
460
device_printf(sc->dev, "Cannot get 'dfll' clock\n");
461
return (ENXIO);
462
*/
463
}
464
return (0);
465
}
466
467
static void
468
tegra124_cpufreq_identify(driver_t *driver, device_t parent)
469
{
470
phandle_t root;
471
472
root = OF_finddevice("/");
473
if (!ofw_bus_node_is_compatible(root, "nvidia,tegra124"))
474
return;
475
476
if (device_get_unit(parent) != 0)
477
return;
478
if (device_find_child(parent, "tegra124_cpufreq", DEVICE_UNIT_ANY) != NULL)
479
return;
480
if (BUS_ADD_CHILD(parent, 0, "tegra124_cpufreq", DEVICE_UNIT_ANY) == NULL)
481
device_printf(parent, "add child failed\n");
482
}
483
484
static int
485
tegra124_cpufreq_probe(device_t dev)
486
{
487
488
device_set_desc(dev, "CPU Frequency Control");
489
490
return (0);
491
}
492
493
static int
494
tegra124_cpufreq_attach(device_t dev)
495
{
496
struct tegra124_cpufreq_softc *sc;
497
uint64_t freq;
498
int rv;
499
500
sc = device_get_softc(dev);
501
sc->dev = dev;
502
sc->node = ofw_bus_get_node(device_get_parent(dev));
503
504
sc->process_id = tegra_sku_info.cpu_process_id;
505
sc->speedo_id = tegra_sku_info.cpu_speedo_id;
506
sc->speedo_value = tegra_sku_info.cpu_speedo_value;
507
508
/* Tegra 124 */
509
/* XXX DPLL is not implemented yet */
510
if (1)
511
sc->cpu_def = &tegra124_cpu_volt_pllx_def;
512
else
513
sc->cpu_def = &tegra124_cpu_volt_dpll_def;
514
515
rv = get_fdt_resources(sc, sc->node);
516
if (rv != 0) {
517
return (rv);
518
}
519
520
build_speed_points(sc);
521
522
rv = clk_get_freq(sc->clk_cpu_g, &freq);
523
if (rv != 0) {
524
device_printf(dev, "Can't get CPU clock frequency\n");
525
return (rv);
526
}
527
if (sc->speedo_id < nitems(cpu_max_freq))
528
sc->cpu_max_freq = cpu_max_freq[sc->speedo_id];
529
else
530
sc->cpu_max_freq = cpu_max_freq[0];
531
sc->act_speed_point = get_speed_point(sc, freq);
532
533
/* Set safe startup CPU frequency. */
534
rv = set_cpu_freq(sc, 1632000000);
535
if (rv != 0) {
536
device_printf(dev, "Can't set initial CPU clock frequency\n");
537
return (rv);
538
}
539
540
/* This device is controlled by cpufreq(4). */
541
cpufreq_register(dev);
542
543
return (0);
544
}
545
546
static int
547
tegra124_cpufreq_detach(device_t dev)
548
{
549
struct tegra124_cpufreq_softc *sc;
550
551
sc = device_get_softc(dev);
552
cpufreq_unregister(dev);
553
554
if (sc->supply_vdd_cpu != NULL)
555
regulator_release(sc->supply_vdd_cpu);
556
557
if (sc->clk_cpu_g != NULL)
558
clk_release(sc->clk_cpu_g);
559
if (sc->clk_cpu_lp != NULL)
560
clk_release(sc->clk_cpu_lp);
561
if (sc->clk_pll_x != NULL)
562
clk_release(sc->clk_pll_x);
563
if (sc->clk_pll_p != NULL)
564
clk_release(sc->clk_pll_p);
565
if (sc->clk_dfll != NULL)
566
clk_release(sc->clk_dfll);
567
return (0);
568
}
569
570
static device_method_t tegra124_cpufreq_methods[] = {
571
/* Device interface */
572
DEVMETHOD(device_identify, tegra124_cpufreq_identify),
573
DEVMETHOD(device_probe, tegra124_cpufreq_probe),
574
DEVMETHOD(device_attach, tegra124_cpufreq_attach),
575
DEVMETHOD(device_detach, tegra124_cpufreq_detach),
576
577
/* cpufreq interface */
578
DEVMETHOD(cpufreq_drv_set, tegra124_cpufreq_set),
579
DEVMETHOD(cpufreq_drv_get, tegra124_cpufreq_get),
580
DEVMETHOD(cpufreq_drv_settings, tegra124_cpufreq_settings),
581
DEVMETHOD(cpufreq_drv_type, tegra124_cpufreq_type),
582
583
DEVMETHOD_END
584
};
585
586
static DEFINE_CLASS_0(tegra124_cpufreq, tegra124_cpufreq_driver,
587
tegra124_cpufreq_methods, sizeof(struct tegra124_cpufreq_softc));
588
DRIVER_MODULE(tegra124_cpufreq, cpu, tegra124_cpufreq_driver, NULL, NULL);
589
590