xref: /openbmc/u-boot/arch/arm/mach-mvebu/efuse.c (revision e8f80a5a)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2015-2016 Reinhard Pfau <reinhard.pfau@gdsys.cc>
4  */
5 
6 #include <config.h>
7 #include <common.h>
8 #include <errno.h>
9 #include <asm/io.h>
10 #include <asm/arch/cpu.h>
11 #include <asm/arch/efuse.h>
12 #include <asm/arch/soc.h>
13 #include <linux/mbus.h>
14 
15 #if defined(CONFIG_MVEBU_EFUSE_FAKE)
16 #define DRY_RUN
17 #else
18 #undef DRY_RUN
19 #endif
20 
21 #define MBUS_EFUSE_BASE 0xF6000000
22 #define MBUS_EFUSE_SIZE BIT(20)
23 
24 #define MVEBU_EFUSE_CONTROL (MVEBU_REGISTER(0xE4008))
25 
26 enum {
27 	MVEBU_EFUSE_CTRL_PROGRAM_ENABLE = (1 << 31),
28 };
29 
30 struct mvebu_hd_efuse {
31 	u32 bits_31_0;
32 	u32 bits_63_32;
33 	u32 bit64;
34 	u32 reserved0;
35 };
36 
37 #ifndef DRY_RUN
38 static struct mvebu_hd_efuse *efuses =
39 	(struct mvebu_hd_efuse *)(MBUS_EFUSE_BASE + 0xF9000);
40 #else
41 static struct mvebu_hd_efuse efuses[EFUSE_LINE_MAX + 1];
42 #endif
43 
44 static int efuse_initialised;
45 
get_efuse_line(int nr)46 static struct mvebu_hd_efuse *get_efuse_line(int nr)
47 {
48 	if (nr < 0 || nr > 63 || !efuse_initialised)
49 		return NULL;
50 
51 	return efuses + nr;
52 }
53 
enable_efuse_program(void)54 static void enable_efuse_program(void)
55 {
56 #ifndef DRY_RUN
57 	setbits_le32(MVEBU_EFUSE_CONTROL, MVEBU_EFUSE_CTRL_PROGRAM_ENABLE);
58 #endif
59 }
60 
disable_efuse_program(void)61 static void disable_efuse_program(void)
62 {
63 #ifndef DRY_RUN
64 	clrbits_le32(MVEBU_EFUSE_CONTROL, MVEBU_EFUSE_CTRL_PROGRAM_ENABLE);
65 #endif
66 }
67 
do_prog_efuse(struct mvebu_hd_efuse * efuse,struct efuse_val * new_val,u32 mask0,u32 mask1)68 static int do_prog_efuse(struct mvebu_hd_efuse *efuse,
69 			 struct efuse_val *new_val, u32 mask0, u32 mask1)
70 {
71 	struct efuse_val val;
72 
73 	val.dwords.d[0] = readl(&efuse->bits_31_0);
74 	val.dwords.d[1] = readl(&efuse->bits_63_32);
75 	val.lock = readl(&efuse->bit64);
76 
77 	if (val.lock & 1)
78 		return -EPERM;
79 
80 	val.dwords.d[0] |= (new_val->dwords.d[0] & mask0);
81 	val.dwords.d[1] |= (new_val->dwords.d[1] & mask1);
82 	val.lock |= new_val->lock;
83 
84 	writel(val.dwords.d[0], &efuse->bits_31_0);
85 	mdelay(1);
86 	writel(val.dwords.d[1], &efuse->bits_63_32);
87 	mdelay(1);
88 	writel(val.lock, &efuse->bit64);
89 	mdelay(5);
90 
91 	return 0;
92 }
93 
prog_efuse(int nr,struct efuse_val * new_val,u32 mask0,u32 mask1)94 static int prog_efuse(int nr, struct efuse_val *new_val, u32 mask0, u32 mask1)
95 {
96 	struct mvebu_hd_efuse *efuse;
97 	int res = 0;
98 
99 	res = mvebu_efuse_init_hw();
100 	if (res)
101 		return res;
102 
103 	efuse = get_efuse_line(nr);
104 	if (!efuse)
105 		return -ENODEV;
106 
107 	if (!new_val)
108 		return -EINVAL;
109 
110 	/* only write a fuse line with lock bit */
111 	if (!new_val->lock)
112 		return -EINVAL;
113 
114 	/* according to specs ECC protection bits must be 0 on write */
115 	if (new_val->bytes.d[7] & 0xFE)
116 		return -EINVAL;
117 
118 	if (!new_val->dwords.d[0] && !new_val->dwords.d[1] && (mask0 | mask1))
119 		return 0;
120 
121 	enable_efuse_program();
122 
123 	res = do_prog_efuse(efuse, new_val, mask0, mask1);
124 
125 	disable_efuse_program();
126 
127 	return res;
128 }
129 
mvebu_efuse_init_hw(void)130 int mvebu_efuse_init_hw(void)
131 {
132 	int ret;
133 
134 	if (efuse_initialised)
135 		return 0;
136 
137 	ret = mvebu_mbus_add_window_by_id(
138 		CPU_TARGET_SATA23_DFX, 0xA, MBUS_EFUSE_BASE, MBUS_EFUSE_SIZE);
139 
140 	if (ret)
141 		return ret;
142 
143 	efuse_initialised = 1;
144 
145 	return 0;
146 }
147 
mvebu_read_efuse(int nr,struct efuse_val * val)148 int mvebu_read_efuse(int nr, struct efuse_val *val)
149 {
150 	struct mvebu_hd_efuse *efuse;
151 	int res;
152 
153 	res = mvebu_efuse_init_hw();
154 	if (res)
155 		return res;
156 
157 	efuse = get_efuse_line(nr);
158 	if (!efuse)
159 		return -ENODEV;
160 
161 	if (!val)
162 		return -EINVAL;
163 
164 	val->dwords.d[0] = readl(&efuse->bits_31_0);
165 	val->dwords.d[1] = readl(&efuse->bits_63_32);
166 	val->lock = readl(&efuse->bit64);
167 	return 0;
168 }
169 
mvebu_write_efuse(int nr,struct efuse_val * val)170 int mvebu_write_efuse(int nr, struct efuse_val *val)
171 {
172 	return prog_efuse(nr, val, ~0, ~0);
173 }
174 
mvebu_lock_efuse(int nr)175 int mvebu_lock_efuse(int nr)
176 {
177 	struct efuse_val val = {
178 		.lock = 1,
179 	};
180 
181 	return prog_efuse(nr, &val, 0, 0);
182 }
183 
184 /*
185  * wrapper funcs providing the fuse API
186  *
187  * we use the following mapping:
188  *   "bank" ->	eFuse line
189  *   "word" ->	0: bits 0-31
190  *		1: bits 32-63
191  *		2: bit 64 (lock)
192  */
193 
194 static struct efuse_val prog_val;
195 static int valid_prog_words;
196 
fuse_read(u32 bank,u32 word,u32 * val)197 int fuse_read(u32 bank, u32 word, u32 *val)
198 {
199 	struct efuse_val fuse_line;
200 	int res;
201 
202 	if (bank < EFUSE_LINE_MIN || bank > EFUSE_LINE_MAX || word > 2)
203 		return -EINVAL;
204 
205 	res = mvebu_read_efuse(bank, &fuse_line);
206 	if (res)
207 		return res;
208 
209 	if (word < 2)
210 		*val = fuse_line.dwords.d[word];
211 	else
212 		*val = fuse_line.lock;
213 
214 	return res;
215 }
216 
fuse_sense(u32 bank,u32 word,u32 * val)217 int fuse_sense(u32 bank, u32 word, u32 *val)
218 {
219 	/* not supported */
220 	return -ENOSYS;
221 }
222 
fuse_prog(u32 bank,u32 word,u32 val)223 int fuse_prog(u32 bank, u32 word, u32 val)
224 {
225 	int res = 0;
226 
227 	/*
228 	 * NOTE: Fuse line should be written as whole.
229 	 * So how can we do that with this API?
230 	 * For now: remember values for word == 0 and word == 1 and write the
231 	 * whole line when word == 2.
232 	 * This implies that we always require all 3 fuse prog cmds (one for
233 	 * for each word) to write a single fuse line.
234 	 * Exception is a single write to word 2 which will lock the fuse line.
235 	 *
236 	 * Hope that will be OK.
237 	 */
238 
239 	if (bank < EFUSE_LINE_MIN || bank > EFUSE_LINE_MAX || word > 2)
240 		return -EINVAL;
241 
242 	if (word < 2) {
243 		prog_val.dwords.d[word] = val;
244 		valid_prog_words |= (1 << word);
245 	} else if ((valid_prog_words & 3) == 0 && val) {
246 		res = mvebu_lock_efuse(bank);
247 		valid_prog_words = 0;
248 	} else if ((valid_prog_words & 3) != 3 || !val) {
249 		res = -EINVAL;
250 	} else {
251 		prog_val.lock = val != 0;
252 		res = mvebu_write_efuse(bank, &prog_val);
253 		valid_prog_words = 0;
254 	}
255 
256 	return res;
257 }
258 
fuse_override(u32 bank,u32 word,u32 val)259 int fuse_override(u32 bank, u32 word, u32 val)
260 {
261 	/* not supported */
262 	return -ENOSYS;
263 }
264