1 // SPDX-License-Identifier: GPL-2.0
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/
11 * Copyright (C) 2020 SiFive, Inc
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.
21 #include <dm/device.h>
23 #include <linux/bitops.h>
24 #include <linux/delay.h>
28 #define BYTES_PER_FUSE 4
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
36 #define PCLK_ENABLE_VAL BIT(0)
37 #define PCLK_DISABLE_VAL 0x00
39 #define PWE_WRITE_ENABLE BIT(0)
40 #define PWE_WRITE_DISABLE 0x00
42 #define PTM_FUSE_PROGRAM_VAL BIT(1)
44 #define PCE_ENABLE_INPUT BIT(0)
45 #define PCE_DISABLE_INPUT 0x00
47 #define PPROG_ENABLE_INPUT BIT(0)
48 #define PPROG_DISABLE_INPUT 0x00
50 #define PTRIM_ENABLE_INPUT BIT(0)
51 #define PTRIM_DISABLE_INPUT 0x00
53 #define PDSTB_DEEP_STANDBY_ENABLE BIT(0)
54 #define PDSTB_DEEP_STANDBY_DISABLE 0x00
56 /* Tpw - Program Pulse width delay */
59 /* Tpwi - Program Pulse interval delay */
62 /* Tasp - Program address setup delay */
65 /* Tcd - read data access delay */
68 /* Tkl - clok pulse low delay */
71 /* Tms - PTM mode setup delay */
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) */
92 struct sifive_otp_plat {
93 struct sifive_otp_regs __iomem *regs;
98 * offset and size are assumed aligned to the size of the fuses (32-bit).
100 static int sifive_otp_read(struct udevice *dev, int offset,
103 struct sifive_otp_plat *plat = dev_get_plat(dev);
104 struct sifive_otp_regs *regs = (struct sifive_otp_regs *)plat->regs;
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",
113 int fuseidx = offset / BYTES_PER_FUSE;
114 int fusecount = size / BYTES_PER_FUSE;
117 if (offset < 0 || size < 0)
119 if (fuseidx >= plat->total_fuses)
121 if ((fuseidx + fusecount) > plat->total_fuses)
124 u32 fusebuf[fusecount];
127 writel(PDSTB_DEEP_STANDBY_ENABLE, ®s->pdstb);
128 writel(PTRIM_ENABLE_INPUT, ®s->ptrim);
129 writel(PCE_ENABLE_INPUT, ®s->pce);
131 /* read all requested fuses */
132 for (unsigned int i = 0; i < fusecount; i++, fuseidx++) {
133 writel(fuseidx, ®s->pa);
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);
142 fusebuf[i] = readl(®s->pdout);
146 writel(PCE_DISABLE_INPUT, ®s->pce);
147 writel(PTRIM_DISABLE_INPUT, ®s->ptrim);
148 writel(PDSTB_DEEP_STANDBY_DISABLE, ®s->pdstb);
151 memcpy(buf, fusebuf, size);
158 * OTP can be written only once, so use carefully.
160 * offset and size are assumed aligned to the size of the fuses (32-bit).
162 static int sifive_otp_write(struct udevice *dev, int offset,
163 const void *buf, int size)
165 struct sifive_otp_plat *plat = dev_get_plat(dev);
166 struct sifive_otp_regs *regs = (struct sifive_otp_regs *)plat->regs;
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",
175 int fuseidx = offset / BYTES_PER_FUSE;
176 int fusecount = size / BYTES_PER_FUSE;
177 u32 *write_buf = (u32 *)buf;
182 if (offset < 0 || size < 0)
184 if (fuseidx >= plat->total_fuses)
186 if ((fuseidx + fusecount) > plat->total_fuses)
190 writel(PDSTB_DEEP_STANDBY_ENABLE, ®s->pdstb);
191 writel(PTRIM_ENABLE_INPUT, ®s->ptrim);
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);
203 writel(PCE_ENABLE_INPUT, ®s->pce);
204 writel(PPROG_ENABLE_INPUT, ®s->pprog);
206 /* write all requested fuses */
207 for (i = 0; i < fusecount; i++, fuseidx++) {
208 writel(fuseidx, ®s->pa);
209 write_data = *(write_buf++);
211 for (pas = 0; pas < 2; pas++) {
212 writel(pas, ®s->pas);
214 for (bit = 0; bit < 32; bit++) {
215 writel(bit, ®s->paio);
216 writel(((write_data >> bit) & 1),
218 ndelay(TASP_DELAY * 1000);
220 writel(PWE_WRITE_ENABLE, ®s->pwe);
222 writel(PWE_WRITE_DISABLE, ®s->pwe);
227 writel(PAS_RESET_VAL, ®s->pas);
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);
236 writel(PTRIM_DISABLE_INPUT, ®s->ptrim);
237 writel(PDSTB_DEEP_STANDBY_DISABLE, ®s->pdstb);
242 static int sifive_otp_of_to_plat(struct udevice *dev)
244 struct sifive_otp_plat *plat = dev_get_plat(dev);
247 plat->regs = dev_read_addr_ptr(dev);
249 ret = dev_read_u32(dev, "fuse-count", &plat->total_fuses);
251 pr_err("\"fuse-count\" not found\n");
258 static const struct misc_ops sifive_otp_ops = {
259 .read = sifive_otp_read,
260 .write = sifive_otp_write,
263 static const struct udevice_id sifive_otp_ids[] = {
264 { .compatible = "sifive,fu540-c000-otp" },
268 U_BOOT_DRIVER(sifive_otp) = {
269 .name = "sifive_otp",
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,