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/arch/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 struct fsb_map_entry fsb_mapping_table[] = {
35 	{ 3, 8 },
36 	{ 4, 8 },
37 	{ 5, 8 },
38 	{ 6, 8 },
39 	{ -1, 48 }, /* Reserve 48 words */
40 	{ 8,  4, true },
41 	{ 24, 4, true },
42 	{ 26, 4, true },
43 	{ 27, 4, true },
44 	{ 28, 8 },
45 	{ 29, 8 },
46 	{ 30, 8 },
47 	{ 31, 8 },
48 	{ 37, 8 },
49 	{ 38, 8 },
50 	{ 39, 8 },
51 	{ 40, 8 },
52 	{ 41, 8 },
53 	{ 42, 8 },
54 	{ 43, 8 },
55 	{ 44, 8 },
56 	{ 45, 8 },
57 	{ 46, 8 },
58 };
59 
60 struct s400_map_entry s400_api_mapping_table[] = {
61 	{ 1, 8 },	/* LOCK */
62 	{ 2, 8 },	/* ECID */
63 	{ 7, 4, 0, 1 },	/* OTP_UNIQ_ID */
64 	{ 23, 1, 4, 2 }, /* OTFAD */
65 };
66 
map_fsb_fuse_index(u32 bank,u32 word,bool * redundancy)67 static s32 map_fsb_fuse_index(u32 bank, u32 word, bool *redundancy)
68 {
69 	s32 size = ARRAY_SIZE(fsb_mapping_table);
70 	s32 i, word_pos = 0;
71 
72 	/* map the fuse from ocotp fuse map to FSB*/
73 	for (i = 0; i < size; i++) {
74 		if (fsb_mapping_table[i].fuse_bank != -1 &&
75 		    fsb_mapping_table[i].fuse_bank == bank) {
76 			break;
77 		}
78 
79 		word_pos += fsb_mapping_table[i].fuse_words;
80 	}
81 
82 	if (i == size)
83 		return -1; /* Failed to find */
84 
85 	if (fsb_mapping_table[i].redundancy) {
86 		*redundancy = true;
87 		return (word >> 1) + word_pos;
88 	}
89 
90 	*redundancy = false;
91 	return word + word_pos;
92 }
93 
map_s400_fuse_index(u32 bank,u32 word)94 static s32 map_s400_fuse_index(u32 bank, u32 word)
95 {
96 	s32 size = ARRAY_SIZE(s400_api_mapping_table);
97 	s32 i;
98 
99 	/* map the fuse from ocotp fuse map to FSB*/
100 	for (i = 0; i < size; i++) {
101 		if (s400_api_mapping_table[i].fuse_bank != -1 &&
102 		    s400_api_mapping_table[i].fuse_bank == bank) {
103 			if (word >= s400_api_mapping_table[i].fuse_offset &&
104 			    word < (s400_api_mapping_table[i].fuse_offset +
105 			    s400_api_mapping_table[i].fuse_words))
106 				break;
107 		}
108 	}
109 
110 	if (i == size)
111 		return -1; /* Failed to find */
112 
113 	if (s400_api_mapping_table[i].s400_index != 0)
114 		return s400_api_mapping_table[i].s400_index;
115 
116 	return s400_api_mapping_table[i].fuse_bank * 8 + word;
117 }
118 
fuse_sense(u32 bank,u32 word,u32 * val)119 int fuse_sense(u32 bank, u32 word, u32 *val)
120 {
121 	s32 word_index;
122 	bool redundancy;
123 
124 	if (bank >= FUSE_BANKS || word >= WORDS_PER_BANKS || !val)
125 		return -EINVAL;
126 
127 	word_index = map_fsb_fuse_index(bank, word, &redundancy);
128 	if (word_index >= 0) {
129 		*val = readl((ulong)FSB_BASE_ADDR + 0x800 + (word_index << 2));
130 		if (redundancy)
131 			*val = (*val >> ((word % 2) * 16)) & 0xFFFF;
132 
133 		return 0;
134 	}
135 
136 	word_index = map_s400_fuse_index(bank, word);
137 	if (word_index >= 0) {
138 		u32 data[4];
139 		u32 res, size = 4;
140 		int ret;
141 
142 		/* Only UID return 4 words */
143 		if (word_index != 1)
144 			size = 1;
145 
146 		ret = ahab_read_common_fuse(word_index, data, size, &res);
147 		if (ret) {
148 			printf("ahab read fuse failed %d, 0x%x\n", ret, res);
149 			return ret;
150 		}
151 
152 		if (word_index == 1) {
153 			*val = data[word]; /* UID */
154 		} else if (word_index == 2) {
155 			/*
156 			 * OTFAD 3 bits as follow:
157 			 * bit 0: OTFAD_ENABLE
158 			 * bit 1: OTFAD_DISABLE_OVERRIDE
159 			 * bit 2: KEY_BLOB_EN
160 			 */
161 			*val = data[0] << 3;
162 		} else {
163 			*val = data[0];
164 		}
165 
166 		return 0;
167 	}
168 
169 	return -ENOENT;
170 }
171 
fuse_read(u32 bank,u32 word,u32 * val)172 int fuse_read(u32 bank, u32 word, u32 *val)
173 {
174 	return fuse_sense(bank, word, val);
175 }
176 
fuse_prog(u32 bank,u32 word,u32 val)177 int fuse_prog(u32 bank, u32 word, u32 val)
178 {
179 	u32 res;
180 	int ret;
181 
182 	if (bank >= FUSE_BANKS || word >= WORDS_PER_BANKS || !val)
183 		return -EINVAL;
184 
185 	ret = ahab_write_fuse((bank * 8 + word), val, false, &res);
186 	if (ret) {
187 		printf("ahab write fuse failed %d, 0x%x\n", ret, res);
188 		return ret;
189 	}
190 
191 	return 0;
192 }
193 
fuse_override(u32 bank,u32 word,u32 val)194 int fuse_override(u32 bank, u32 word, u32 val)
195 {
196 	printf("Override fuse to i.MX8ULP in u-boot is forbidden\n");
197 	return -EPERM;
198 }
199