1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright 2020 NXP
4 */
5
6 #include <console.h>
7 #include <errno.h>
8 #include <fuse.h>
9 #include <asm/arch/sys_proto.h>
10 #include <asm/arch/imx-regs.h>
11 #include <env.h>
12 #include <asm/mach-imx/ele_api.h>
13 #include <asm/global_data.h>
14 #include <env.h>
15
16 DECLARE_GLOBAL_DATA_PTR;
17
18 #define WORDS_PER_BANKS 8
19
20 struct fsb_map_entry {
21 s32 fuse_bank;
22 u32 fuse_words;
23 bool redundancy;
24 };
25
26 struct ele_map_entry {
27 s32 fuse_bank;
28 u32 fuse_words;
29 u32 fuse_offset;
30 u32 ele_index;
31 };
32
33 #if defined(CONFIG_IMX8ULP)
34 #define FSB_OTP_SHADOW 0x800
35 #define IS_FSB_ALLOWED (true)
36
37 struct fsb_map_entry fsb_mapping_table[] = {
38 { 3, 8 },
39 { 4, 8 },
40 { -1, 48 }, /* Reserve 48 words */
41 { 5, 8 },
42 { 6, 8 },
43 { 8, 4, true },
44 { 24, 4, true },
45 { 26, 4, true },
46 { 27, 4, true },
47 { 28, 8 },
48 { 29, 8 },
49 { 30, 8 },
50 { 31, 8 },
51 { 37, 8 },
52 { 38, 8 },
53 { 39, 8 },
54 { 40, 8 },
55 { 41, 8 },
56 { 42, 8 },
57 { 43, 8 },
58 { 44, 8 },
59 { 45, 8 },
60 { 46, 8 },
61 };
62
63 /* None ECC banks such like Redundancy or Bit protect */
64 u32 nonecc_fuse_banks[] = {
65 0, 1, 8, 12, 16, 22, 24, 25, 26, 27, 36, 41, 51, 56
66 };
67
68 struct ele_map_entry ele_api_mapping_table[] = {
69 { 1, 8 }, /* LOCK */
70 { 2, 8 }, /* ECID */
71 { 7, 4, 0, 1 }, /* OTP_UNIQ_ID */
72 { 15, 8 }, /* OEM SRK HASH */
73 { 23, 1, 4, 2 }, /* OTFAD */
74 { 25, 8 }, /* Test config2 */
75 { 26, 8 }, /* PMU */
76 { 27, 8 }, /* Test flow/USB */
77 { 32, 8 }, /* GP1 */
78 { 33, 8 }, /* GP2 */
79 { 34, 8 }, /* GP3 */
80 { 35, 8 }, /* GP4 */
81 { 36, 8 }, /* GP5 */
82 { 49, 8 }, /* GP8 */
83 { 50, 8 }, /* GP9 */
84 { 51, 8 }, /* GP10 */
85 };
86 #elif defined(CONFIG_ARCH_IMX9)
87 #define FSB_OTP_SHADOW 0x8000
88 #define IS_FSB_ALLOWED (!IS_ENABLED(CONFIG_SCMI_FIRMWARE) && \
89 !(readl(BLK_CTRL_NS_ANOMIX_BASE_ADDR + 0x28) & BIT(0)))
90
91 struct fsb_map_entry fsb_mapping_table[] = {
92 { 0, 8 },
93 { 1, 8 },
94 { 2, 8 },
95 { 3, 8 },
96 { 4, 8 },
97 { 5, 8 },
98 { 6, 4 },
99 { -1, 260 },
100 { 39, 8 },
101 { 40, 8 },
102 { 41, 8 },
103 { 42, 8 },
104 { 43, 8 },
105 { 44, 8 },
106 { 45, 8 },
107 { 46, 8 },
108 { 47, 8 },
109 { 48, 8 },
110 { 49, 8 },
111 { 50, 8 },
112 { 51, 8 },
113 { 52, 8 },
114 { 53, 8 },
115 { 54, 8 },
116 { 55, 8 },
117 { 56, 8 },
118 { 57, 8 },
119 { 58, 8 },
120 { 59, 8 },
121 { 60, 8 },
122 { 61, 8 },
123 { 62, 8 },
124 { 63, 8 },
125 };
126
127 struct ele_map_entry ele_api_mapping_table[] = {
128 { 7, 1, 7, 63 },
129 { 16, 8, },
130 { 17, 8, },
131 { 22, 1, 6 },
132 { 23, 1, 4 },
133 };
134 #endif
135
map_fsb_fuse_index(u32 bank,u32 word,bool * redundancy)136 static s32 map_fsb_fuse_index(u32 bank, u32 word, bool *redundancy)
137 {
138 s32 size = ARRAY_SIZE(fsb_mapping_table);
139 s32 i, word_pos = 0;
140
141 /* map the fuse from ocotp fuse map to FSB*/
142 for (i = 0; i < size; i++) {
143 if (fsb_mapping_table[i].fuse_bank != -1 &&
144 fsb_mapping_table[i].fuse_bank == bank) {
145 break;
146 }
147
148 word_pos += fsb_mapping_table[i].fuse_words;
149 }
150
151 if (i == size)
152 return -1; /* Failed to find */
153
154 if (fsb_mapping_table[i].redundancy) {
155 if ((fsb_mapping_table[i].fuse_words << 1) <= word)
156 return -2; /* Not valid word */
157
158 *redundancy = true;
159 return (word >> 1) + word_pos;
160 } else if (fsb_mapping_table[i].fuse_words <= word) {
161 return -2; /* Not valid word */
162 }
163
164 *redundancy = false;
165 return word + word_pos;
166 }
167
map_ele_fuse_index(u32 bank,u32 word)168 static s32 map_ele_fuse_index(u32 bank, u32 word)
169 {
170 s32 size = ARRAY_SIZE(ele_api_mapping_table);
171 s32 i;
172
173 /* map the fuse from ocotp fuse map to FSB*/
174 for (i = 0; i < size; i++) {
175 if (ele_api_mapping_table[i].fuse_bank != -1 &&
176 ele_api_mapping_table[i].fuse_bank == bank) {
177 if (word >= ele_api_mapping_table[i].fuse_offset &&
178 word < (ele_api_mapping_table[i].fuse_offset +
179 ele_api_mapping_table[i].fuse_words))
180 break;
181 }
182 }
183
184 if (i == size)
185 return -1; /* Failed to find */
186
187 if (ele_api_mapping_table[i].ele_index != 0)
188 return ele_api_mapping_table[i].ele_index;
189
190 return ele_api_mapping_table[i].fuse_bank * 8 + word;
191 }
192
193 #if defined(CONFIG_IMX8ULP)
fuse_sense(u32 bank,u32 word,u32 * val)194 int fuse_sense(u32 bank, u32 word, u32 *val)
195 {
196 s32 word_index;
197
198 if (word >= WORDS_PER_BANKS || !val)
199 return -EINVAL;
200
201 word_index = map_ele_fuse_index(bank, word);
202 if (word_index >= 0) {
203 u32 data[4];
204 u32 res = 0, size = 4;
205 int ret;
206
207 /* Only UID return 4 words */
208 if (word_index != 1)
209 size = 1;
210
211 ret = ele_read_common_fuse(word_index, data, size, &res);
212 if (ret) {
213 printf("ahab read fuse failed %d, 0x%x\n", ret, res);
214 return ret;
215 }
216
217 if (word_index == 1) {
218 *val = data[word]; /* UID */
219 } else if (word_index == 2) {
220 /*
221 * OTFAD 3 bits as follow:
222 * bit 0: OTFAD_ENABLE
223 * bit 1: OTFAD_DISABLE_OVERRIDE
224 * bit 2: KEY_BLOB_EN
225 */
226 *val = data[0] << 3;
227 } else {
228 *val = data[0];
229 }
230
231 return 0;
232 }
233
234 return -ENOENT;
235 }
236
237 #elif defined(CONFIG_ARCH_IMX9)
fuse_sense(u32 bank,u32 word,u32 * val)238 int fuse_sense(u32 bank, u32 word, u32 *val)
239 {
240 s32 word_index;
241 bool redundancy;
242
243 if (word >= WORDS_PER_BANKS || !val)
244 return -EINVAL;
245
246 if (!IS_ENABLED(CONFIG_SCMI_FIRMWARE)) {
247 word_index = map_fsb_fuse_index(bank, word, &redundancy);
248
249 /* ELE read common fuse API supports all FSB fuse. */
250 if (word_index < 0)
251 word_index = map_ele_fuse_index(bank, word);
252 } else {
253 word_index = bank * 8 + word;
254 }
255
256 if (word_index >= 0) {
257 u32 data;
258 u32 res = 0, size = 1;
259 int ret;
260
261 ret = ele_read_common_fuse(word_index, &data, size, &res);
262 if (ret) {
263 printf("ahab read fuse failed %d, 0x%x\n", ret, res);
264 return ret;
265 }
266
267 *val = data;
268
269 return 0;
270 }
271
272 return -ENOENT;
273 }
274 #endif
275
fuse_read_default(u32 bank,u32 word,u32 * val)276 static int fuse_read_default(u32 bank, u32 word, u32 *val)
277 {
278 s32 word_index;
279 bool redundancy;
280
281 if (IS_FSB_ALLOWED) {
282 word_index = map_fsb_fuse_index(bank, word, &redundancy);
283 if (word_index >= 0) {
284 *val = readl((ulong)FSB_BASE_ADDR + FSB_OTP_SHADOW + (word_index << 2));
285 if (redundancy)
286 *val = (*val >> ((word % 2) * 16)) & 0xFFFF;
287
288 return 0;
289 }
290 }
291
292 return fuse_sense(bank, word, val);
293 }
294
fuse_read_ele_shd(u32 bank,u32 word,u32 * val)295 static int fuse_read_ele_shd(u32 bank, u32 word, u32 *val)
296 {
297 u32 res = 0;
298 int ret;
299 struct udevice *dev = gd->arch.ele_dev;
300
301 if (!dev)
302 return -ENODEV;
303
304 ret = ele_read_shadow_fuse((bank * 8 + word), val, &res);
305 if (ret) {
306 printf("ele read shadow fuse failed %d, 0x%x\n", ret, res);
307 return ret;
308 }
309
310 return 0;
311 }
312
fuse_read(u32 bank,u32 word,u32 * val)313 int fuse_read(u32 bank, u32 word, u32 *val)
314 {
315 if (word >= WORDS_PER_BANKS || !val)
316 return -EINVAL;
317
318 if (!IS_ENABLED(CONFIG_SPL_BUILD) &&
319 env_get_yesno("enable_ele_shd") == 1)
320 return fuse_read_ele_shd(bank, word, val);
321 else
322 return fuse_read_default(bank, word, val);
323 }
324
fuse_prog(u32 bank,u32 word,u32 val)325 int fuse_prog(u32 bank, u32 word, u32 val)
326 {
327 u32 res = 0;
328 int ret;
329 bool lock = false;
330
331 if (word >= WORDS_PER_BANKS || !val)
332 return -EINVAL;
333
334 /* Lock 8ULP ECC fuse word, so second programming will return failure.
335 * iMX9 OTP can protect ECC fuse, so not need it
336 */
337 #if defined(CONFIG_IMX8ULP)
338 u32 i;
339 for (i = 0; i < ARRAY_SIZE(nonecc_fuse_banks); i++) {
340 if (nonecc_fuse_banks[i] == bank)
341 break;
342 }
343
344 if (i == ARRAY_SIZE(nonecc_fuse_banks))
345 lock = true;
346 #endif
347
348 ret = ele_write_fuse((bank * 8 + word), val, lock, &res);
349 if (ret) {
350 printf("ahab write fuse failed %d, 0x%x\n", ret, res);
351 return ret;
352 }
353
354 return 0;
355 }
356
fuse_override(u32 bank,u32 word,u32 val)357 int fuse_override(u32 bank, u32 word, u32 val)
358 {
359 u32 res = 0;
360 int ret;
361
362 if (word >= WORDS_PER_BANKS || !val)
363 return -EINVAL;
364
365 ret = ele_write_shadow_fuse((bank * 8 + word), val, &res);
366 if (ret) {
367 printf("ahab write shadow fuse failed %d, 0x%x\n", ret, res);
368 return ret;
369 }
370
371 return 0;
372 }
373