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