Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/nvidia/tegra_efuse.c
39483 views
1
/*-
2
* Copyright (c) 2015 Michal Meloun
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/clock.h>
31
#include <sys/kernel.h>
32
#include <sys/limits.h>
33
#include <sys/lock.h>
34
#include <sys/mutex.h>
35
#include <sys/module.h>
36
#include <sys/resource.h>
37
#include <sys/rman.h>
38
39
#include <machine/bus.h>
40
#include <machine/resource.h>
41
42
#include <dev/clk/clk.h>
43
#include <dev/hwreset/hwreset.h>
44
#include <dev/ofw/ofw_bus.h>
45
#include <dev/ofw/ofw_bus_subr.h>
46
47
#include <arm/nvidia/tegra_efuse.h>
48
49
#define FUSES_START 0x100
50
#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (FUSES_START + (_r)))
51
52
struct efuse_soc;
53
struct tegra_efuse_softc {
54
device_t dev;
55
struct resource *mem_res;
56
57
struct efuse_soc *soc;
58
clk_t clk;
59
hwreset_t reset;
60
};
61
62
struct tegra_efuse_softc *dev_sc;
63
struct tegra_sku_info tegra_sku_info;
64
static char *tegra_rev_name[] = {
65
[TEGRA_REVISION_UNKNOWN] = "unknown",
66
[TEGRA_REVISION_A01] = "A01",
67
[TEGRA_REVISION_A02] = "A02",
68
[TEGRA_REVISION_A03] = "A03",
69
[TEGRA_REVISION_A03p] = "A03 prime",
70
[TEGRA_REVISION_A04] = "A04",
71
};
72
73
struct efuse_soc {
74
void (*init)(struct tegra_efuse_softc *sc,
75
struct tegra_sku_info *sku);
76
};
77
78
static void tegra124_init(struct tegra_efuse_softc *sc,
79
struct tegra_sku_info *sku);
80
struct efuse_soc tegra124_efuse_soc = {
81
.init = tegra124_init,
82
};
83
84
static void tegra210_init(struct tegra_efuse_softc *sc,
85
struct tegra_sku_info *sku);
86
struct efuse_soc tegra210_efuse_soc = {
87
.init = tegra210_init,
88
};
89
90
static struct ofw_compat_data compat_data[] = {
91
{"nvidia,tegra124-efuse", (intptr_t)&tegra124_efuse_soc},
92
{"nvidia,tegra210-efuse", (intptr_t)&tegra210_efuse_soc},
93
{NULL, 0}
94
};
95
96
/* ---------------------- Tegra 124 specific code & data --------------- */
97
#define TEGRA124_CPU_PROCESS_CORNERS 2
98
#define TEGRA124_GPU_PROCESS_CORNERS 2
99
#define TEGRA124_SOC_PROCESS_CORNERS 2
100
101
#define TEGRA124_FUSE_SKU_INFO 0x10
102
#define TEGRA124_FUSE_CPU_SPEEDO_0 0x14
103
#define TEGRA124_FUSE_CPU_IDDQ 0x18
104
#define TEGRA124_FUSE_FT_REV 0x28
105
#define TEGRA124_FUSE_CPU_SPEEDO_1 0x2c
106
#define TEGRA124_FUSE_CPU_SPEEDO_2 0x30
107
#define TEGRA124_FUSE_SOC_SPEEDO_0 0x34
108
#define TEGRA124_FUSE_SOC_SPEEDO_1 0x38
109
#define TEGRA124_FUSE_SOC_SPEEDO_2 0x3c
110
#define TEGRA124_FUSE_SOC_IDDQ 0x40
111
#define TEGRA124_FUSE_GPU_IDDQ 0x128
112
113
enum {
114
TEGRA124_THRESHOLD_INDEX_0,
115
TEGRA124_THRESHOLD_INDEX_1,
116
TEGRA124_THRESHOLD_INDEX_COUNT,
117
};
118
119
static uint32_t tegra124_cpu_process_speedos[][TEGRA124_CPU_PROCESS_CORNERS] =
120
{
121
{2190, UINT_MAX},
122
{0, UINT_MAX},
123
};
124
125
static uint32_t tegra124_gpu_process_speedos[][TEGRA124_GPU_PROCESS_CORNERS] =
126
{
127
{1965, UINT_MAX},
128
{0, UINT_MAX},
129
};
130
131
static uint32_t tegra124_soc_process_speedos[][TEGRA124_SOC_PROCESS_CORNERS] =
132
{
133
{2101, UINT_MAX},
134
{0, UINT_MAX},
135
};
136
137
138
static void
139
tegra124_rev_sku_to_speedo_ids(struct tegra_efuse_softc *sc,
140
struct tegra_sku_info *sku, int *threshold)
141
{
142
143
/* Set default */
144
sku->cpu_speedo_id = 0;
145
sku->soc_speedo_id = 0;
146
sku->gpu_speedo_id = 0;
147
*threshold = TEGRA124_THRESHOLD_INDEX_0;
148
149
switch (sku->sku_id) {
150
case 0x00: /* Eng sku */
151
case 0x0F:
152
case 0x23:
153
/* Using the default */
154
break;
155
case 0x83:
156
sku->cpu_speedo_id = 2;
157
break;
158
159
case 0x1F:
160
case 0x87:
161
case 0x27:
162
sku->cpu_speedo_id = 2;
163
sku->soc_speedo_id = 0;
164
sku->gpu_speedo_id = 1;
165
*threshold = TEGRA124_THRESHOLD_INDEX_0;
166
break;
167
case 0x81:
168
case 0x21:
169
case 0x07:
170
sku->cpu_speedo_id = 1;
171
sku->soc_speedo_id = 1;
172
sku->gpu_speedo_id = 1;
173
*threshold = TEGRA124_THRESHOLD_INDEX_1;
174
break;
175
case 0x49:
176
case 0x4A:
177
case 0x48:
178
sku->cpu_speedo_id = 4;
179
sku->soc_speedo_id = 2;
180
sku->gpu_speedo_id = 3;
181
*threshold = TEGRA124_THRESHOLD_INDEX_1;
182
break;
183
default:
184
device_printf(sc->dev, " Unknown SKU ID %d\n", sku->sku_id);
185
break;
186
}
187
}
188
189
static void
190
tegra124_init(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku)
191
{
192
int i, threshold;
193
194
sku->sku_id = RD4(sc, TEGRA124_FUSE_SKU_INFO);
195
sku->soc_iddq_value = RD4(sc, TEGRA124_FUSE_SOC_IDDQ);
196
sku->cpu_iddq_value = RD4(sc, TEGRA124_FUSE_CPU_IDDQ);
197
sku->gpu_iddq_value = RD4(sc, TEGRA124_FUSE_GPU_IDDQ);
198
sku->soc_speedo_value = RD4(sc, TEGRA124_FUSE_SOC_SPEEDO_0);
199
sku->cpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_0);
200
sku->gpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_2);
201
202
if (sku->cpu_speedo_value == 0) {
203
device_printf(sc->dev, "CPU Speedo value is not fused.\n");
204
return;
205
}
206
207
tegra124_rev_sku_to_speedo_ids(sc, sku, &threshold);
208
209
for (i = 0; i < TEGRA124_SOC_PROCESS_CORNERS; i++) {
210
if (sku->soc_speedo_value <
211
tegra124_soc_process_speedos[threshold][i])
212
break;
213
}
214
sku->soc_process_id = i;
215
216
for (i = 0; i < TEGRA124_CPU_PROCESS_CORNERS; i++) {
217
if (sku->cpu_speedo_value <
218
tegra124_cpu_process_speedos[threshold][i])
219
break;
220
}
221
sku->cpu_process_id = i;
222
223
for (i = 0; i < TEGRA124_GPU_PROCESS_CORNERS; i++) {
224
if (sku->gpu_speedo_value <
225
tegra124_gpu_process_speedos[threshold][i])
226
break;
227
}
228
sku->gpu_process_id = i;
229
230
}
231
/* ----------------- End of Tegra 124 specific code & data --------------- */
232
233
/* -------------------- Tegra 201 specific code & data ------------------- */
234
#define TEGRA210_CPU_PROCESS_CORNERS 2
235
#define TEGRA210_GPU_PROCESS_CORNERS 2
236
#define TEGRA210_SOC_PROCESS_CORNERS 3
237
238
#define TEGRA210_FUSE_SKU_INFO 0x010
239
#define TEGRA210_FUSE_CPU_SPEEDO_0 0x014
240
#define TEGRA210_FUSE_CPU_IDDQ 0x018
241
#define TEGRA210_FUSE_FT_REV 0x028
242
#define TEGRA210_FUSE_CPU_SPEEDO_1 0x02c
243
#define TEGRA210_FUSE_CPU_SPEEDO_2 0x030
244
#define TEGRA210_FUSE_SOC_SPEEDO_0 0x034
245
#define TEGRA210_FUSE_SOC_SPEEDO_1 0x038
246
#define TEGRA210_FUSE_SOC_SPEEDO_2 0x03c
247
#define TEGRA210_FUSE_SOC_IDDQ 0x040
248
#define TEGRA210_FUSE_GPU_IDDQ 0x128
249
#define TEGRA210_FUSE_SPARE 0x270
250
251
enum {
252
TEGRA210_THRESHOLD_INDEX_0,
253
TEGRA210_THRESHOLD_INDEX_1,
254
TEGRA210_THRESHOLD_INDEX_COUNT,
255
};
256
257
static uint32_t tegra210_cpu_process_speedos[][TEGRA210_CPU_PROCESS_CORNERS] =
258
{
259
{2119, UINT_MAX},
260
{2119, UINT_MAX},
261
};
262
263
static uint32_t tegra210_gpu_process_speedos[][TEGRA210_GPU_PROCESS_CORNERS] =
264
{
265
{UINT_MAX, UINT_MAX},
266
{UINT_MAX, UINT_MAX},
267
};
268
269
static uint32_t tegra210_soc_process_speedos[][TEGRA210_SOC_PROCESS_CORNERS] =
270
{
271
{1950, 2100, UINT_MAX},
272
{1950, 2100, UINT_MAX},
273
};
274
275
static uint32_t
276
tegra210_get_speedo_revision(struct tegra_efuse_softc *sc)
277
{
278
uint32_t reg;
279
uint32_t val;
280
281
val = 0;
282
283
/* Revision i encoded in spare fields */
284
reg = RD4(sc, TEGRA210_FUSE_SPARE + 2 * 4);
285
val |= (reg & 1) << 0;
286
reg = RD4(sc, TEGRA210_FUSE_SPARE + 3 * 4);
287
val |= (reg & 1) << 1;
288
reg = RD4(sc, TEGRA210_FUSE_SPARE + 4 * 4);
289
val |= (reg & 1) << 2;
290
291
return (val);
292
}
293
294
295
static void
296
tegra210_rev_sku_to_speedo_ids(struct tegra_efuse_softc *sc,
297
struct tegra_sku_info *sku, int speedo_rev, int *threshold)
298
{
299
300
/* Set defaults */
301
sku->cpu_speedo_id = 0;
302
sku->soc_speedo_id = 0;
303
sku->gpu_speedo_id = 0;
304
*threshold = TEGRA210_THRESHOLD_INDEX_0;
305
306
switch (sku->sku_id) {
307
case 0x00: /* Eng sku */
308
case 0x01: /* Eng sku */
309
case 0x07:
310
case 0x17:
311
case 0x27:
312
/* Use defaults */
313
if (speedo_rev >= 2)
314
sku->gpu_speedo_id = 1;
315
break;
316
case 0x13:
317
if (speedo_rev >= 2)
318
sku->gpu_speedo_id = 1;
319
sku->cpu_speedo_id = 1;
320
break;
321
322
default:
323
device_printf(sc->dev, " Unknown SKU ID %d\n", sku->sku_id);
324
break;
325
}
326
}
327
328
329
static void
330
tegra210_init(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku)
331
{
332
int i, threshold, speedo_rev;
333
uint32_t cpu_speedo[3], soc_speedo[3];
334
335
cpu_speedo[0] = RD4(sc, TEGRA210_FUSE_CPU_SPEEDO_0);
336
cpu_speedo[1] = RD4(sc, TEGRA210_FUSE_CPU_SPEEDO_1);
337
cpu_speedo[2] = RD4(sc, TEGRA210_FUSE_CPU_SPEEDO_2);
338
soc_speedo[0] = RD4(sc, TEGRA210_FUSE_SOC_SPEEDO_0);
339
soc_speedo[1] = RD4(sc, TEGRA210_FUSE_SOC_SPEEDO_1);
340
soc_speedo[2] = RD4(sc, TEGRA210_FUSE_SOC_SPEEDO_2);
341
342
343
sku->cpu_iddq_value = RD4(sc, TEGRA210_FUSE_CPU_IDDQ);
344
sku->soc_iddq_value = RD4(sc, TEGRA210_FUSE_SOC_IDDQ);
345
sku->gpu_iddq_value = RD4(sc, TEGRA210_FUSE_GPU_IDDQ);
346
347
speedo_rev = tegra210_get_speedo_revision(sc);
348
device_printf(sc->dev, " Speedo revision: %u\n", speedo_rev);
349
350
if (speedo_rev >= 3) {
351
sku->cpu_speedo_value = cpu_speedo[0];
352
sku->gpu_speedo_value = cpu_speedo[2];
353
sku->soc_speedo_value = soc_speedo[0];
354
} else if (speedo_rev == 2) {
355
sku->cpu_speedo_value =
356
(-1938 + (1095 * cpu_speedo[0] / 100)) / 10;
357
sku->gpu_speedo_value =
358
(-1662 + (1082 * cpu_speedo[2] / 100)) / 10;
359
sku->soc_speedo_value =
360
( -705 + (1037 * soc_speedo[0] / 100)) / 10;
361
} else {
362
sku->cpu_speedo_value = 2100;
363
sku->gpu_speedo_value = cpu_speedo[2] - 75;
364
sku->soc_speedo_value = 1900;
365
}
366
367
tegra210_rev_sku_to_speedo_ids(sc, sku, speedo_rev, &threshold);
368
369
for (i = 0; i < TEGRA210_SOC_PROCESS_CORNERS; i++) {
370
if (sku->soc_speedo_value <
371
tegra210_soc_process_speedos[threshold][i])
372
break;
373
}
374
sku->soc_process_id = i;
375
376
for (i = 0; i < TEGRA210_CPU_PROCESS_CORNERS; i++) {
377
if (sku->cpu_speedo_value <
378
tegra210_cpu_process_speedos[threshold][i])
379
break;
380
}
381
sku->cpu_process_id = i;
382
383
for (i = 0; i < TEGRA210_GPU_PROCESS_CORNERS; i++) {
384
if (sku->gpu_speedo_value <
385
tegra210_gpu_process_speedos[threshold][i])
386
break;
387
}
388
sku->gpu_process_id = i;
389
390
}
391
392
/* ----------------- End of Tegra 210 specific code & data --------------- */
393
394
395
uint32_t
396
tegra_fuse_read_4(int addr) {
397
if (dev_sc == NULL)
398
panic("tegra_fuse_read_4 called too early");
399
return (RD4(dev_sc, addr));
400
}
401
402
static void
403
tegra_efuse_dump_sku(void)
404
{
405
printf(" TEGRA SKU Info:\n");
406
printf(" chip_id: %u\n", tegra_sku_info.chip_id);
407
printf(" sku_id: %u\n", tegra_sku_info.sku_id);
408
printf(" cpu_process_id: %u\n", tegra_sku_info.cpu_process_id);
409
printf(" cpu_speedo_id: %u\n", tegra_sku_info.cpu_speedo_id);
410
printf(" cpu_speedo_value: %u\n", tegra_sku_info.cpu_speedo_value);
411
printf(" cpu_iddq_value: %u\n", tegra_sku_info.cpu_iddq_value);
412
printf(" soc_process_id: %u\n", tegra_sku_info.soc_process_id);
413
printf(" soc_speedo_id: %u\n", tegra_sku_info.soc_speedo_id);
414
printf(" soc_speedo_value: %u\n", tegra_sku_info.soc_speedo_value);
415
printf(" soc_iddq_value: %u\n", tegra_sku_info.soc_iddq_value);
416
printf(" gpu_process_id: %u\n", tegra_sku_info.gpu_process_id);
417
printf(" gpu_speedo_id: %u\n", tegra_sku_info.gpu_speedo_id);
418
printf(" gpu_speedo_value: %u\n", tegra_sku_info.gpu_speedo_value);
419
printf(" gpu_iddq_value: %u\n", tegra_sku_info.gpu_iddq_value);
420
printf(" revision: %s\n", tegra_rev_name[tegra_sku_info.revision]);
421
}
422
423
static int
424
tegra_efuse_probe(device_t dev)
425
{
426
if (!ofw_bus_status_okay(dev))
427
return (ENXIO);
428
429
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
430
return (ENXIO);
431
432
return (BUS_PROBE_DEFAULT);
433
}
434
435
static int
436
tegra_efuse_attach(device_t dev)
437
{
438
int rv, rid;
439
struct tegra_efuse_softc *sc;
440
441
sc = device_get_softc(dev);
442
sc->dev = dev;
443
sc->soc = (struct efuse_soc *)ofw_bus_search_compatible(dev,
444
compat_data)->ocd_data;
445
446
/* Get the memory resource for the register mapping. */
447
rid = 0;
448
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
449
RF_ACTIVE);
450
if (sc->mem_res == NULL) {
451
device_printf(dev, "Cannot map registers.\n");
452
rv = ENXIO;
453
goto fail;
454
}
455
456
/* OFW resources. */
457
rv = clk_get_by_ofw_name(dev, 0, "fuse", &sc->clk);
458
if (rv != 0) {
459
device_printf(dev, "Cannot get fuse clock: %d\n", rv);
460
goto fail;
461
}
462
rv = clk_enable(sc->clk);
463
if (rv != 0) {
464
device_printf(dev, "Cannot enable clock: %d\n", rv);
465
goto fail;
466
}
467
rv = hwreset_get_by_ofw_name(sc->dev, 0, "fuse", &sc->reset);
468
if (rv != 0) {
469
device_printf(dev, "Cannot get fuse reset\n");
470
goto fail;
471
}
472
rv = hwreset_deassert(sc->reset);
473
if (rv != 0) {
474
device_printf(sc->dev, "Cannot clear reset\n");
475
goto fail;
476
}
477
478
sc->soc->init(sc, &tegra_sku_info);
479
480
dev_sc = sc;
481
482
if (bootverbose)
483
tegra_efuse_dump_sku();
484
bus_attach_children(dev);
485
return (0);
486
487
fail:
488
dev_sc = NULL;
489
if (sc->clk != NULL)
490
clk_release(sc->clk);
491
if (sc->reset != NULL)
492
hwreset_release(sc->reset);
493
if (sc->mem_res != NULL)
494
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
495
496
return (rv);
497
}
498
499
static int
500
tegra_efuse_detach(device_t dev)
501
{
502
struct tegra_efuse_softc *sc;
503
int error;
504
505
error = bus_generic_detach(dev);
506
if (error != 0)
507
return (error);
508
509
sc = device_get_softc(dev);
510
dev_sc = NULL;
511
if (sc->clk != NULL)
512
clk_release(sc->clk);
513
if (sc->reset != NULL)
514
hwreset_release(sc->reset);
515
if (sc->mem_res != NULL)
516
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
517
518
return (0);
519
}
520
521
static device_method_t tegra_efuse_methods[] = {
522
/* Device interface */
523
DEVMETHOD(device_probe, tegra_efuse_probe),
524
DEVMETHOD(device_attach, tegra_efuse_attach),
525
DEVMETHOD(device_detach, tegra_efuse_detach),
526
527
DEVMETHOD_END
528
};
529
530
static DEFINE_CLASS_0(efuse, tegra_efuse_driver, tegra_efuse_methods,
531
sizeof(struct tegra_efuse_softc));
532
EARLY_DRIVER_MODULE(tegra_efuse, simplebus, tegra_efuse_driver, NULL, NULL,
533
BUS_PASS_TIMER);
534
535