Merge tag 'u-boot-atmel-fixes-2020.07-a' of https://gitlab.denx.de/u-boot/custodians...
[oweals/u-boot.git] / arch / arm / mach-mvebu / efuse.c
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
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
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
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
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
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
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
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
172 int mvebu_write_efuse(int nr, struct efuse_val *val)
173 {
174         return prog_efuse(nr, val, ~0, ~0);
175 }
176
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
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
219 int fuse_sense(u32 bank, u32 word, u32 *val)
220 {
221         /* not supported */
222         return -ENOSYS;
223 }
224
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
261 int fuse_override(u32 bank, u32 word, u32 val)
262 {
263         /* not supported */
264         return -ENOSYS;
265 }