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