1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2020 NXP
4  */
5 
6 #include <common.h>
7 #include <console.h>
8 #include <errno.h>
9 #include <fuse.h>
10 #include <asm/arch/sys_proto.h>
11 #include <asm/arch/imx-regs.h>
12 #include <env.h>
13 #include <asm/mach-imx/s400_api.h>
14 #include <asm/global_data.h>
15 
16 DECLARE_GLOBAL_DATA_PTR;
17 
18 #define FUSE_BANKS	64
19 #define WORDS_PER_BANKS 8
20 
21 struct fsb_map_entry {
22 	s32 fuse_bank;
23 	u32 fuse_words;
24 	bool redundancy;
25 };
26 
27 struct s400_map_entry {
28 	s32 fuse_bank;
29 	u32 fuse_words;
30 	u32 fuse_offset;
31 	u32 s400_index;
32 };
33 
34 #if defined(CONFIG_IMX8ULP)
35 #define FSB_OTP_SHADOW	0x800
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 s400_map_entry s400_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 
89 struct fsb_map_entry fsb_mapping_table[] = {
90 	{ 0, 8 },
91 	{ 1, 8 },
92 	{ 2, 8 },
93 	{ 3, 8 },
94 	{ 4, 8 },
95 	{ 5, 8 },
96 	{ 6, 4 },
97 	{ -1, 260 },
98 	{ 39, 8 },
99 	{ 40, 8 },
100 	{ 41, 8 },
101 	{ 42, 8 },
102 	{ 43, 8 },
103 	{ 44, 8 },
104 	{ 45, 8 },
105 	{ 46, 8 },
106 	{ 47, 8 },
107 	{ 48, 8 },
108 	{ 49, 8 },
109 	{ 50, 8 },
110 	{ 51, 8 },
111 	{ 52, 8 },
112 	{ 53, 8 },
113 	{ 54, 8 },
114 	{ 55, 8 },
115 	{ 56, 8 },
116 	{ 57, 8 },
117 	{ 58, 8 },
118 	{ 59, 8 },
119 	{ 60, 8 },
120 	{ 61, 8 },
121 	{ 62, 8 },
122 	{ 63, 8 },
123 };
124 
125 struct s400_map_entry s400_api_mapping_table[] = {
126 	{ 7, 1, 7, 63 },
127 	{ 16, 8, },
128 	{ 17, 8, },
129 	{ 22, 1, 6 },
130 	{ 23, 1, 4 },
131 };
132 #endif
133 
map_fsb_fuse_index(u32 bank,u32 word,bool * redundancy)134 static s32 map_fsb_fuse_index(u32 bank, u32 word, bool *redundancy)
135 {
136 	s32 size = ARRAY_SIZE(fsb_mapping_table);
137 	s32 i, word_pos = 0;
138 
139 	/* map the fuse from ocotp fuse map to FSB*/
140 	for (i = 0; i < size; i++) {
141 		if (fsb_mapping_table[i].fuse_bank != -1 &&
142 		    fsb_mapping_table[i].fuse_bank == bank &&
143 		    fsb_mapping_table[i].fuse_words > word) {
144 			break;
145 		}
146 
147 		word_pos += fsb_mapping_table[i].fuse_words;
148 	}
149 
150 	if (i == size)
151 		return -1; /* Failed to find */
152 
153 	if (fsb_mapping_table[i].redundancy) {
154 		*redundancy = true;
155 		return (word >> 1) + word_pos;
156 	}
157 
158 	*redundancy = false;
159 	return word + word_pos;
160 }
161 
map_s400_fuse_index(u32 bank,u32 word)162 static s32 map_s400_fuse_index(u32 bank, u32 word)
163 {
164 	s32 size = ARRAY_SIZE(s400_api_mapping_table);
165 	s32 i;
166 
167 	/* map the fuse from ocotp fuse map to FSB*/
168 	for (i = 0; i < size; i++) {
169 		if (s400_api_mapping_table[i].fuse_bank != -1 &&
170 		    s400_api_mapping_table[i].fuse_bank == bank) {
171 			if (word >= s400_api_mapping_table[i].fuse_offset &&
172 			    word < (s400_api_mapping_table[i].fuse_offset +
173 			    s400_api_mapping_table[i].fuse_words))
174 				break;
175 		}
176 	}
177 
178 	if (i == size)
179 		return -1; /* Failed to find */
180 
181 	if (s400_api_mapping_table[i].s400_index != 0)
182 		return s400_api_mapping_table[i].s400_index;
183 
184 	return s400_api_mapping_table[i].fuse_bank * 8 + word;
185 }
186 
187 #if defined(CONFIG_IMX8ULP)
fuse_sense(u32 bank,u32 word,u32 * val)188 int fuse_sense(u32 bank, u32 word, u32 *val)
189 {
190 	s32 word_index;
191 	bool redundancy;
192 
193 	if (bank >= FUSE_BANKS || word >= WORDS_PER_BANKS || !val)
194 		return -EINVAL;
195 
196 	word_index = map_fsb_fuse_index(bank, word, &redundancy);
197 	if (word_index >= 0) {
198 		*val = readl((ulong)FSB_BASE_ADDR + FSB_OTP_SHADOW + (word_index << 2));
199 		if (redundancy)
200 			*val = (*val >> ((word % 2) * 16)) & 0xFFFF;
201 
202 		return 0;
203 	}
204 
205 	word_index = map_s400_fuse_index(bank, word);
206 	if (word_index >= 0) {
207 		u32 data[4];
208 		u32 res, size = 4;
209 		int ret;
210 
211 		/* Only UID return 4 words */
212 		if (word_index != 1)
213 			size = 1;
214 
215 		ret = ahab_read_common_fuse(word_index, data, size, &res);
216 		if (ret) {
217 			printf("ahab read fuse failed %d, 0x%x\n", ret, res);
218 			return ret;
219 		}
220 
221 		if (word_index == 1) {
222 			*val = data[word]; /* UID */
223 		} else if (word_index == 2) {
224 			/*
225 			 * OTFAD 3 bits as follow:
226 			 * bit 0: OTFAD_ENABLE
227 			 * bit 1: OTFAD_DISABLE_OVERRIDE
228 			 * bit 2: KEY_BLOB_EN
229 			 */
230 			*val = data[0] << 3;
231 		} else {
232 			*val = data[0];
233 		}
234 
235 		return 0;
236 	}
237 
238 	return -ENOENT;
239 }
240 #elif defined(CONFIG_ARCH_IMX9)
fuse_sense(u32 bank,u32 word,u32 * val)241 int fuse_sense(u32 bank, u32 word, u32 *val)
242 {
243 	s32 word_index;
244 	bool redundancy;
245 
246 	if (bank >= FUSE_BANKS || word >= WORDS_PER_BANKS || !val)
247 		return -EINVAL;
248 
249 	word_index = map_fsb_fuse_index(bank, word, &redundancy);
250 	if (word_index >= 0) {
251 		*val = readl((ulong)FSB_BASE_ADDR + FSB_OTP_SHADOW + (word_index << 2));
252 		if (redundancy)
253 			*val = (*val >> ((word % 2) * 16)) & 0xFFFF;
254 
255 		return 0;
256 	}
257 
258 	word_index = map_s400_fuse_index(bank, word);
259 	if (word_index >= 0) {
260 		u32 data;
261 		u32 res, size = 1;
262 		int ret;
263 
264 		ret = ahab_read_common_fuse(word_index, &data, size, &res);
265 		if (ret) {
266 			printf("ahab read fuse failed %d, 0x%x\n", ret, res);
267 			return ret;
268 		}
269 
270 		*val = data;
271 
272 		return 0;
273 	}
274 
275 	return -ENOENT;
276 }
277 #endif
278 
fuse_read(u32 bank,u32 word,u32 * val)279 int fuse_read(u32 bank, u32 word, u32 *val)
280 {
281 	return fuse_sense(bank, word, val);
282 }
283 
fuse_prog(u32 bank,u32 word,u32 val)284 int fuse_prog(u32 bank, u32 word, u32 val)
285 {
286 	u32 res;
287 	int ret;
288 	bool lock = false;
289 
290 	if (bank >= FUSE_BANKS || word >= WORDS_PER_BANKS || !val)
291 		return -EINVAL;
292 
293 	/* Lock 8ULP ECC fuse word, so second programming will return failure.
294 	 * iMX9 OTP can protect ECC fuse, so not need it
295 	 */
296 #if defined(CONFIG_IMX8ULP)
297 	u32 i;
298 	for (i = 0; i < ARRAY_SIZE(nonecc_fuse_banks); i++) {
299 		if (nonecc_fuse_banks[i] == bank)
300 			break;
301 	}
302 
303 	if (i == ARRAY_SIZE(nonecc_fuse_banks))
304 		lock = true;
305 #endif
306 
307 	ret = ahab_write_fuse((bank * 8 + word), val, lock, &res);
308 	if (ret) {
309 		printf("ahab write fuse failed %d, 0x%x\n", ret, res);
310 		return ret;
311 	}
312 
313 	return 0;
314 }
315 
fuse_override(u32 bank,u32 word,u32 val)316 int fuse_override(u32 bank, u32 word, u32 val)
317 {
318 	printf("Override fuse to i.MX8ULP in u-boot is forbidden\n");
319 	return -EPERM;
320 }
321