sifive_u_otp.c (8729B)
1/* 2 * QEMU SiFive U OTP (One-Time Programmable) Memory interface 3 * 4 * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com> 5 * 6 * Simple model of the OTP to emulate register reads made by the SDK BSP 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms and conditions of the GNU General Public License, 10 * version 2 or later, as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along with 18 * this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21#include "qemu/osdep.h" 22#include "qapi/error.h" 23#include "hw/qdev-properties.h" 24#include "hw/qdev-properties-system.h" 25#include "hw/sysbus.h" 26#include "qemu/error-report.h" 27#include "qemu/log.h" 28#include "qemu/module.h" 29#include "hw/misc/sifive_u_otp.h" 30#include "sysemu/blockdev.h" 31#include "sysemu/block-backend.h" 32 33#define WRITTEN_BIT_ON 0x1 34 35#define SET_FUSEARRAY_BIT(map, i, off, bit) \ 36 map[i] = bit ? (map[i] | bit << off) : (map[i] & ~(0x1 << off)) 37 38#define GET_FUSEARRAY_BIT(map, i, off) \ 39 ((map[i] >> off) & 0x1) 40 41static uint64_t sifive_u_otp_read(void *opaque, hwaddr addr, unsigned int size) 42{ 43 SiFiveUOTPState *s = opaque; 44 45 switch (addr) { 46 case SIFIVE_U_OTP_PA: 47 return s->pa; 48 case SIFIVE_U_OTP_PAIO: 49 return s->paio; 50 case SIFIVE_U_OTP_PAS: 51 return s->pas; 52 case SIFIVE_U_OTP_PCE: 53 return s->pce; 54 case SIFIVE_U_OTP_PCLK: 55 return s->pclk; 56 case SIFIVE_U_OTP_PDIN: 57 return s->pdin; 58 case SIFIVE_U_OTP_PDOUT: 59 if ((s->pce & SIFIVE_U_OTP_PCE_EN) && 60 (s->pdstb & SIFIVE_U_OTP_PDSTB_EN) && 61 (s->ptrim & SIFIVE_U_OTP_PTRIM_EN)) { 62 63 /* read from backend */ 64 if (s->blk) { 65 int32_t buf; 66 67 if (blk_pread(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD, &buf, 68 SIFIVE_U_OTP_FUSE_WORD) < 0) { 69 error_report("read error index<%d>", s->pa); 70 return 0xff; 71 } 72 73 return buf; 74 } 75 76 return s->fuse[s->pa & SIFIVE_U_OTP_PA_MASK]; 77 } else { 78 return 0xff; 79 } 80 case SIFIVE_U_OTP_PDSTB: 81 return s->pdstb; 82 case SIFIVE_U_OTP_PPROG: 83 return s->pprog; 84 case SIFIVE_U_OTP_PTC: 85 return s->ptc; 86 case SIFIVE_U_OTP_PTM: 87 return s->ptm; 88 case SIFIVE_U_OTP_PTM_REP: 89 return s->ptm_rep; 90 case SIFIVE_U_OTP_PTR: 91 return s->ptr; 92 case SIFIVE_U_OTP_PTRIM: 93 return s->ptrim; 94 case SIFIVE_U_OTP_PWE: 95 return s->pwe; 96 } 97 98 qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n", 99 __func__, addr); 100 return 0; 101} 102 103static void sifive_u_otp_write(void *opaque, hwaddr addr, 104 uint64_t val64, unsigned int size) 105{ 106 SiFiveUOTPState *s = opaque; 107 uint32_t val32 = (uint32_t)val64; 108 109 switch (addr) { 110 case SIFIVE_U_OTP_PA: 111 s->pa = val32 & SIFIVE_U_OTP_PA_MASK; 112 break; 113 case SIFIVE_U_OTP_PAIO: 114 s->paio = val32; 115 break; 116 case SIFIVE_U_OTP_PAS: 117 s->pas = val32; 118 break; 119 case SIFIVE_U_OTP_PCE: 120 s->pce = val32; 121 break; 122 case SIFIVE_U_OTP_PCLK: 123 s->pclk = val32; 124 break; 125 case SIFIVE_U_OTP_PDIN: 126 s->pdin = val32; 127 break; 128 case SIFIVE_U_OTP_PDOUT: 129 /* read-only */ 130 break; 131 case SIFIVE_U_OTP_PDSTB: 132 s->pdstb = val32; 133 break; 134 case SIFIVE_U_OTP_PPROG: 135 s->pprog = val32; 136 break; 137 case SIFIVE_U_OTP_PTC: 138 s->ptc = val32; 139 break; 140 case SIFIVE_U_OTP_PTM: 141 s->ptm = val32; 142 break; 143 case SIFIVE_U_OTP_PTM_REP: 144 s->ptm_rep = val32; 145 break; 146 case SIFIVE_U_OTP_PTR: 147 s->ptr = val32; 148 break; 149 case SIFIVE_U_OTP_PTRIM: 150 s->ptrim = val32; 151 break; 152 case SIFIVE_U_OTP_PWE: 153 s->pwe = val32 & SIFIVE_U_OTP_PWE_EN; 154 155 /* PWE is enabled. Ignore PAS=1 (no redundancy cell) */ 156 if (s->pwe && !s->pas) { 157 if (GET_FUSEARRAY_BIT(s->fuse_wo, s->pa, s->paio)) { 158 qemu_log_mask(LOG_GUEST_ERROR, 159 "write once error: idx<%u>, bit<%u>\n", 160 s->pa, s->paio); 161 break; 162 } 163 164 /* write bit data */ 165 SET_FUSEARRAY_BIT(s->fuse, s->pa, s->paio, s->pdin); 166 167 /* write to backend */ 168 if (s->blk) { 169 if (blk_pwrite(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD, 170 &s->fuse[s->pa], SIFIVE_U_OTP_FUSE_WORD, 171 0) < 0) { 172 error_report("write error index<%d>", s->pa); 173 } 174 } 175 176 /* update written bit */ 177 SET_FUSEARRAY_BIT(s->fuse_wo, s->pa, s->paio, WRITTEN_BIT_ON); 178 } 179 180 break; 181 default: 182 qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx 183 " v=0x%x\n", __func__, addr, val32); 184 } 185} 186 187static const MemoryRegionOps sifive_u_otp_ops = { 188 .read = sifive_u_otp_read, 189 .write = sifive_u_otp_write, 190 .endianness = DEVICE_NATIVE_ENDIAN, 191 .valid = { 192 .min_access_size = 4, 193 .max_access_size = 4 194 } 195}; 196 197static Property sifive_u_otp_properties[] = { 198 DEFINE_PROP_UINT32("serial", SiFiveUOTPState, serial, 0), 199 DEFINE_PROP_DRIVE("drive", SiFiveUOTPState, blk), 200 DEFINE_PROP_END_OF_LIST(), 201}; 202 203static void sifive_u_otp_realize(DeviceState *dev, Error **errp) 204{ 205 SiFiveUOTPState *s = SIFIVE_U_OTP(dev); 206 DriveInfo *dinfo; 207 208 memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_u_otp_ops, s, 209 TYPE_SIFIVE_U_OTP, SIFIVE_U_OTP_REG_SIZE); 210 sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); 211 212 dinfo = drive_get_next(IF_NONE); 213 if (dinfo) { 214 int ret; 215 uint64_t perm; 216 int filesize; 217 BlockBackend *blk; 218 219 blk = blk_by_legacy_dinfo(dinfo); 220 filesize = SIFIVE_U_OTP_NUM_FUSES * SIFIVE_U_OTP_FUSE_WORD; 221 if (blk_getlength(blk) < filesize) { 222 error_setg(errp, "OTP drive size < 16K"); 223 return; 224 } 225 226 qdev_prop_set_drive_err(dev, "drive", blk, errp); 227 228 if (s->blk) { 229 perm = BLK_PERM_CONSISTENT_READ | 230 (blk_supports_write_perm(s->blk) ? BLK_PERM_WRITE : 0); 231 ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp); 232 if (ret < 0) { 233 return; 234 } 235 236 if (blk_pread(s->blk, 0, s->fuse, filesize) != filesize) { 237 error_setg(errp, "failed to read the initial flash content"); 238 } 239 } 240 } 241} 242 243static void sifive_u_otp_reset(DeviceState *dev) 244{ 245 SiFiveUOTPState *s = SIFIVE_U_OTP(dev); 246 247 /* Initialize all fuses' initial value to 0xFFs */ 248 memset(s->fuse, 0xff, sizeof(s->fuse)); 249 250 /* Make a valid content of serial number */ 251 s->fuse[SIFIVE_U_OTP_SERIAL_ADDR] = s->serial; 252 s->fuse[SIFIVE_U_OTP_SERIAL_ADDR + 1] = ~(s->serial); 253 254 if (s->blk) { 255 /* Put serial number to backend as well*/ 256 uint32_t serial_data; 257 int index = SIFIVE_U_OTP_SERIAL_ADDR; 258 259 serial_data = s->serial; 260 if (blk_pwrite(s->blk, index * SIFIVE_U_OTP_FUSE_WORD, 261 &serial_data, SIFIVE_U_OTP_FUSE_WORD, 0) < 0) { 262 error_report("write error index<%d>", index); 263 } 264 265 serial_data = ~(s->serial); 266 if (blk_pwrite(s->blk, (index + 1) * SIFIVE_U_OTP_FUSE_WORD, 267 &serial_data, SIFIVE_U_OTP_FUSE_WORD, 0) < 0) { 268 error_report("write error index<%d>", index + 1); 269 } 270 } 271 272 /* Initialize write-once map */ 273 memset(s->fuse_wo, 0x00, sizeof(s->fuse_wo)); 274} 275 276static void sifive_u_otp_class_init(ObjectClass *klass, void *data) 277{ 278 DeviceClass *dc = DEVICE_CLASS(klass); 279 280 device_class_set_props(dc, sifive_u_otp_properties); 281 dc->realize = sifive_u_otp_realize; 282 dc->reset = sifive_u_otp_reset; 283} 284 285static const TypeInfo sifive_u_otp_info = { 286 .name = TYPE_SIFIVE_U_OTP, 287 .parent = TYPE_SYS_BUS_DEVICE, 288 .instance_size = sizeof(SiFiveUOTPState), 289 .class_init = sifive_u_otp_class_init, 290}; 291 292static void sifive_u_otp_register_types(void) 293{ 294 type_register_static(&sifive_u_otp_info); 295} 296 297type_init(sifive_u_otp_register_types)