misc: fuse: Lock 8ULP ECC-protected fuse when programming
[platform/kernel/u-boot.git] / drivers / misc / sentinel / fuse.c
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
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
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)
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)
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
279 int fuse_read(u32 bank, u32 word, u32 *val)
280 {
281         return fuse_sense(bank, word, val);
282 }
283
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
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 }