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