blob: e9b5259ac7010126469c07e06eed3aac1f1e7b9e [file] [log] [blame]
Svyatoslav Ryheld7f34ff2023-06-30 10:29:02 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2012-2013
4 * NVIDIA Corporation <www.nvidia.com>
5 *
6 * (C) Copyright 2022
7 * Svyatoslav Ryhel <clamor95@gmail.com>
8 */
9
Svyatoslav Ryheld7f34ff2023-06-30 10:29:02 +030010#include <linux/delay.h>
11#include <asm/io.h>
12
13#include <asm/arch/tegra.h>
14#include <asm/arch/gp_padctrl.h>
15#include <asm/arch/clock.h>
16#include <asm/arch-tegra/fuse.h>
17
18#include "cpu.h"
19
20#define FUSE_UID_LOW 0x108
21#define FUSE_UID_HIGH 0x10c
22
23#define FUSE_VENDOR_CODE 0x200
24#define FUSE_FAB_CODE 0x204
25#define FUSE_LOT_CODE_0 0x208
26#define FUSE_LOT_CODE_1 0x20c
27#define FUSE_WAFER_ID 0x210
28#define FUSE_X_COORDINATE 0x214
29#define FUSE_Y_COORDINATE 0x218
30
31#define FUSE_VENDOR_CODE_MASK 0xf
32#define FUSE_FAB_CODE_MASK 0x3f
33#define FUSE_WAFER_ID_MASK 0x3f
34#define FUSE_X_COORDINATE_MASK 0x1ff
35#define FUSE_Y_COORDINATE_MASK 0x1ff
36
37static u32 tegra_fuse_readl(unsigned long offset)
38{
39 return readl(NV_PA_FUSE_BASE + offset);
40}
41
42static void tegra_fuse_init(void)
43{
44 u32 reg;
45
46 /*
47 * Performed by downstream and is not
48 * documented by TRM. Whithout setting
49 * this bit fuse region will not work.
50 */
51 reg = readl_relaxed(NV_PA_CLK_RST_BASE + 0x48);
52 reg |= BIT(28);
53 writel(reg, NV_PA_CLK_RST_BASE + 0x48);
54
55 clock_enable(PERIPH_ID_FUSE);
56 udelay(2);
57 reset_set_enable(PERIPH_ID_FUSE, 0);
58}
59
60unsigned long long tegra_chip_uid(void)
61{
62 u64 uid = 0ull;
63 u32 reg;
64 u32 cid;
65 u32 vendor;
66 u32 fab;
67 u32 lot;
68 u32 wafer;
69 u32 x;
70 u32 y;
71 u32 i;
72
73 tegra_fuse_init();
74
75 /* This used to be so much easier in prior chips. Unfortunately, there
76 is no one-stop shopping for the unique id anymore. It must be
77 constructed from various bits of information burned into the fuses
78 during the manufacturing process. The 64-bit unique id is formed
79 by concatenating several bit fields. The notation used for the
80 various fields is <fieldname:size_in_bits> with the UID composed
81 thusly:
82 <CID:4><VENDOR:4><FAB:6><LOT:26><WAFER:6><X:9><Y:9>
83 Where:
84 Field Bits Position Data
85 ------- ---- -------- ----------------------------------------
86 CID 4 60 Chip id
87 VENDOR 4 56 Vendor code
88 FAB 6 50 FAB code
89 LOT 26 24 Lot code (5-digit base-36-coded-decimal,
90 re-encoded to 26 bits binary)
91 WAFER 6 18 Wafer id
92 X 9 9 Wafer X-coordinate
93 Y 9 0 Wafer Y-coordinate
94 ------- ----
95 Total 64
96 */
97
98 switch (tegra_get_chip()) {
99 case CHIPID_TEGRA20:
100 /* T20 has simple calculation */
101 return ((unsigned long long)tegra_fuse_readl(FUSE_UID_HIGH) << 32ull) |
102 (unsigned long long)tegra_fuse_readl(FUSE_UID_LOW);
103 case CHIPID_TEGRA30:
104 /* T30 chip id is 0 */
105 cid = 0;
106 break;
107 case CHIPID_TEGRA114:
108 /* T11x chip id is 1 */
109 cid = 1;
110 break;
111 case CHIPID_TEGRA124:
112 /* T12x chip id is 3 */
113 cid = 3;
114 break;
115 case CHIPID_TEGRA210:
116 /* T210 chip id is 5 */
117 cid = 5;
118 default:
119 return 0;
120 }
121
122 vendor = tegra_fuse_readl(FUSE_VENDOR_CODE) & FUSE_VENDOR_CODE_MASK;
123 fab = tegra_fuse_readl(FUSE_FAB_CODE) & FUSE_FAB_CODE_MASK;
124
125 /* Lot code must be re-encoded from a 5 digit base-36 'BCD' number
126 to a binary number. */
127 lot = 0;
128 reg = tegra_fuse_readl(FUSE_LOT_CODE_0) << 2;
129
130 for (i = 0; i < 5; ++i) {
131 u32 digit = (reg & 0xFC000000) >> 26;
132 lot *= 36;
133 lot += digit;
134 reg <<= 6;
135 }
136
137 wafer = tegra_fuse_readl(FUSE_WAFER_ID) & FUSE_WAFER_ID_MASK;
138 x = tegra_fuse_readl(FUSE_X_COORDINATE) & FUSE_X_COORDINATE_MASK;
139 y = tegra_fuse_readl(FUSE_Y_COORDINATE) & FUSE_Y_COORDINATE_MASK;
140
141 uid = ((unsigned long long)cid << 60ull)
142 | ((unsigned long long)vendor << 56ull)
143 | ((unsigned long long)fab << 50ull)
144 | ((unsigned long long)lot << 24ull)
145 | ((unsigned long long)wafer << 18ull)
146 | ((unsigned long long)x << 9ull)
147 | ((unsigned long long)y << 0ull);
148
149 return uid;
150}