1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * This is a driver for the eMemory EG004K32TQ028XW01 NeoFuse
4 * One-Time-Programmable (OTP) memory used within the SiFive FU540.
5 * It is documented in the FU540 manual here:
6 * https://www.sifive.com/documentation/chips/freedom-u540-c000-manual/
7 *
8 * Copyright (C) 2018 Philipp Hug <philipp@hug.cx>
9 * Copyright (C) 2018 Joey Hewitt <joey@joeyhewitt.com>
10 *
11 * Copyright (C) 2020 SiFive, Inc
12 */
13
14 /*
15 * The FU540 stores 4096x32 bit (16KiB) values.
16 * Index 0x00-0xff are reserved for SiFive internal use. (first 1KiB)
17 * Right now first 1KiB is used to store only serial number.
18 */
19
20 #include <common.h>
21 #include <dm/device.h>
22 #include <dm/read.h>
23 #include <linux/bitops.h>
24 #include <linux/delay.h>
25 #include <linux/io.h>
26 #include <misc.h>
27
28 #define BYTES_PER_FUSE 4
29
30 #define PA_RESET_VAL 0x00
31 #define PAS_RESET_VAL 0x00
32 #define PAIO_RESET_VAL 0x00
33 #define PDIN_RESET_VAL 0x00
34 #define PTM_RESET_VAL 0x00
35
36 #define PCLK_ENABLE_VAL BIT(0)
37 #define PCLK_DISABLE_VAL 0x00
38
39 #define PWE_WRITE_ENABLE BIT(0)
40 #define PWE_WRITE_DISABLE 0x00
41
42 #define PTM_FUSE_PROGRAM_VAL BIT(1)
43
44 #define PCE_ENABLE_INPUT BIT(0)
45 #define PCE_DISABLE_INPUT 0x00
46
47 #define PPROG_ENABLE_INPUT BIT(0)
48 #define PPROG_DISABLE_INPUT 0x00
49
50 #define PTRIM_ENABLE_INPUT BIT(0)
51 #define PTRIM_DISABLE_INPUT 0x00
52
53 #define PDSTB_DEEP_STANDBY_ENABLE BIT(0)
54 #define PDSTB_DEEP_STANDBY_DISABLE 0x00
55
56 /* Tpw - Program Pulse width delay */
57 #define TPW_DELAY 20
58
59 /* Tpwi - Program Pulse interval delay */
60 #define TPWI_DELAY 5
61
62 /* Tasp - Program address setup delay */
63 #define TASP_DELAY 1
64
65 /* Tcd - read data access delay */
66 #define TCD_DELAY 40
67
68 /* Tkl - clok pulse low delay */
69 #define TKL_DELAY 10
70
71 /* Tms - PTM mode setup delay */
72 #define TMS_DELAY 1
73
74 struct sifive_otp_regs {
75 u32 pa; /* Address input */
76 u32 paio; /* Program address input */
77 u32 pas; /* Program redundancy cell selection input */
78 u32 pce; /* OTP Macro enable input */
79 u32 pclk; /* Clock input */
80 u32 pdin; /* Write data input */
81 u32 pdout; /* Read data output */
82 u32 pdstb; /* Deep standby mode enable input (active low) */
83 u32 pprog; /* Program mode enable input */
84 u32 ptc; /* Test column enable input */
85 u32 ptm; /* Test mode enable input */
86 u32 ptm_rep;/* Repair function test mode enable input */
87 u32 ptr; /* Test row enable input */
88 u32 ptrim; /* Repair function enable input */
89 u32 pwe; /* Write enable input (defines program cycle) */
90 };
91
92 struct sifive_otp_plat {
93 struct sifive_otp_regs __iomem *regs;
94 u32 total_fuses;
95 };
96
97 /*
98 * offset and size are assumed aligned to the size of the fuses (32-bit).
99 */
sifive_otp_read(struct udevice * dev,int offset,void * buf,int size)100 static int sifive_otp_read(struct udevice *dev, int offset,
101 void *buf, int size)
102 {
103 struct sifive_otp_plat *plat = dev_get_plat(dev);
104 struct sifive_otp_regs *regs = (struct sifive_otp_regs *)plat->regs;
105
106 /* Check if offset and size are multiple of BYTES_PER_FUSE */
107 if ((size % BYTES_PER_FUSE) || (offset % BYTES_PER_FUSE)) {
108 printf("%s: size and offset must be multiple of 4.\n",
109 __func__);
110 return -EINVAL;
111 }
112
113 int fuseidx = offset / BYTES_PER_FUSE;
114 int fusecount = size / BYTES_PER_FUSE;
115
116 /* check bounds */
117 if (offset < 0 || size < 0)
118 return -EINVAL;
119 if (fuseidx >= plat->total_fuses)
120 return -EINVAL;
121 if ((fuseidx + fusecount) > plat->total_fuses)
122 return -EINVAL;
123
124 u32 fusebuf[fusecount];
125
126 /* init OTP */
127 writel(PDSTB_DEEP_STANDBY_ENABLE, ®s->pdstb);
128 writel(PTRIM_ENABLE_INPUT, ®s->ptrim);
129 writel(PCE_ENABLE_INPUT, ®s->pce);
130
131 /* read all requested fuses */
132 for (unsigned int i = 0; i < fusecount; i++, fuseidx++) {
133 writel(fuseidx, ®s->pa);
134
135 /* cycle clock to read */
136 writel(PCLK_ENABLE_VAL, ®s->pclk);
137 ndelay(TCD_DELAY * 1000);
138 writel(PCLK_DISABLE_VAL, ®s->pclk);
139 ndelay(TKL_DELAY * 1000);
140
141 /* read the value */
142 fusebuf[i] = readl(®s->pdout);
143 }
144
145 /* shut down */
146 writel(PCE_DISABLE_INPUT, ®s->pce);
147 writel(PTRIM_DISABLE_INPUT, ®s->ptrim);
148 writel(PDSTB_DEEP_STANDBY_DISABLE, ®s->pdstb);
149
150 /* copy out */
151 memcpy(buf, fusebuf, size);
152
153 return size;
154 }
155
156 /*
157 * Caution:
158 * OTP can be written only once, so use carefully.
159 *
160 * offset and size are assumed aligned to the size of the fuses (32-bit).
161 */
sifive_otp_write(struct udevice * dev,int offset,const void * buf,int size)162 static int sifive_otp_write(struct udevice *dev, int offset,
163 const void *buf, int size)
164 {
165 struct sifive_otp_plat *plat = dev_get_plat(dev);
166 struct sifive_otp_regs *regs = (struct sifive_otp_regs *)plat->regs;
167
168 /* Check if offset and size are multiple of BYTES_PER_FUSE */
169 if ((size % BYTES_PER_FUSE) || (offset % BYTES_PER_FUSE)) {
170 printf("%s: size and offset must be multiple of 4.\n",
171 __func__);
172 return -EINVAL;
173 }
174
175 int fuseidx = offset / BYTES_PER_FUSE;
176 int fusecount = size / BYTES_PER_FUSE;
177 u32 *write_buf = (u32 *)buf;
178 u32 write_data;
179 int i, pas, bit;
180
181 /* check bounds */
182 if (offset < 0 || size < 0)
183 return -EINVAL;
184 if (fuseidx >= plat->total_fuses)
185 return -EINVAL;
186 if ((fuseidx + fusecount) > plat->total_fuses)
187 return -EINVAL;
188
189 /* init OTP */
190 writel(PDSTB_DEEP_STANDBY_ENABLE, ®s->pdstb);
191 writel(PTRIM_ENABLE_INPUT, ®s->ptrim);
192
193 /* reset registers */
194 writel(PCLK_DISABLE_VAL, ®s->pclk);
195 writel(PA_RESET_VAL, ®s->pa);
196 writel(PAS_RESET_VAL, ®s->pas);
197 writel(PAIO_RESET_VAL, ®s->paio);
198 writel(PDIN_RESET_VAL, ®s->pdin);
199 writel(PWE_WRITE_DISABLE, ®s->pwe);
200 writel(PTM_FUSE_PROGRAM_VAL, ®s->ptm);
201 ndelay(TMS_DELAY * 1000);
202
203 writel(PCE_ENABLE_INPUT, ®s->pce);
204 writel(PPROG_ENABLE_INPUT, ®s->pprog);
205
206 /* write all requested fuses */
207 for (i = 0; i < fusecount; i++, fuseidx++) {
208 writel(fuseidx, ®s->pa);
209 write_data = *(write_buf++);
210
211 for (pas = 0; pas < 2; pas++) {
212 writel(pas, ®s->pas);
213
214 for (bit = 0; bit < 32; bit++) {
215 writel(bit, ®s->paio);
216 writel(((write_data >> bit) & 1),
217 ®s->pdin);
218 ndelay(TASP_DELAY * 1000);
219
220 writel(PWE_WRITE_ENABLE, ®s->pwe);
221 udelay(TPW_DELAY);
222 writel(PWE_WRITE_DISABLE, ®s->pwe);
223 udelay(TPWI_DELAY);
224 }
225 }
226
227 writel(PAS_RESET_VAL, ®s->pas);
228 }
229
230 /* shut down */
231 writel(PWE_WRITE_DISABLE, ®s->pwe);
232 writel(PPROG_DISABLE_INPUT, ®s->pprog);
233 writel(PCE_DISABLE_INPUT, ®s->pce);
234 writel(PTM_RESET_VAL, ®s->ptm);
235
236 writel(PTRIM_DISABLE_INPUT, ®s->ptrim);
237 writel(PDSTB_DEEP_STANDBY_DISABLE, ®s->pdstb);
238
239 return size;
240 }
241
sifive_otp_of_to_plat(struct udevice * dev)242 static int sifive_otp_of_to_plat(struct udevice *dev)
243 {
244 struct sifive_otp_plat *plat = dev_get_plat(dev);
245 int ret;
246
247 plat->regs = dev_read_addr_ptr(dev);
248
249 ret = dev_read_u32(dev, "fuse-count", &plat->total_fuses);
250 if (ret < 0) {
251 pr_err("\"fuse-count\" not found\n");
252 return ret;
253 }
254
255 return 0;
256 }
257
258 static const struct misc_ops sifive_otp_ops = {
259 .read = sifive_otp_read,
260 .write = sifive_otp_write,
261 };
262
263 static const struct udevice_id sifive_otp_ids[] = {
264 { .compatible = "sifive,fu540-c000-otp" },
265 {}
266 };
267
268 U_BOOT_DRIVER(sifive_otp) = {
269 .name = "sifive_otp",
270 .id = UCLASS_MISC,
271 .of_match = sifive_otp_ids,
272 .of_to_plat = sifive_otp_of_to_plat,
273 .plat_auto = sizeof(struct sifive_otp_plat),
274 .ops = &sifive_otp_ops,
275 };
276