1*c752bb07SHavard Skinnemoen /* 2*c752bb07SHavard Skinnemoen * Nuvoton NPCM7xx OTP (Fuse Array) Interface 3*c752bb07SHavard Skinnemoen * 4*c752bb07SHavard Skinnemoen * Copyright 2020 Google LLC 5*c752bb07SHavard Skinnemoen * 6*c752bb07SHavard Skinnemoen * This program is free software; you can redistribute it and/or modify it 7*c752bb07SHavard Skinnemoen * under the terms of the GNU General Public License as published by the 8*c752bb07SHavard Skinnemoen * Free Software Foundation; either version 2 of the License, or 9*c752bb07SHavard Skinnemoen * (at your option) any later version. 10*c752bb07SHavard Skinnemoen * 11*c752bb07SHavard Skinnemoen * This program is distributed in the hope that it will be useful, but WITHOUT 12*c752bb07SHavard Skinnemoen * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13*c752bb07SHavard Skinnemoen * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14*c752bb07SHavard Skinnemoen * for more details. 15*c752bb07SHavard Skinnemoen */ 16*c752bb07SHavard Skinnemoen 17*c752bb07SHavard Skinnemoen #include "qemu/osdep.h" 18*c752bb07SHavard Skinnemoen 19*c752bb07SHavard Skinnemoen #include "hw/nvram/npcm7xx_otp.h" 20*c752bb07SHavard Skinnemoen #include "migration/vmstate.h" 21*c752bb07SHavard Skinnemoen #include "qapi/error.h" 22*c752bb07SHavard Skinnemoen #include "qemu/bitops.h" 23*c752bb07SHavard Skinnemoen #include "qemu/log.h" 24*c752bb07SHavard Skinnemoen #include "qemu/module.h" 25*c752bb07SHavard Skinnemoen #include "qemu/units.h" 26*c752bb07SHavard Skinnemoen 27*c752bb07SHavard Skinnemoen /* Each module has 4 KiB of register space. Only a fraction of it is used. */ 28*c752bb07SHavard Skinnemoen #define NPCM7XX_OTP_REGS_SIZE (4 * KiB) 29*c752bb07SHavard Skinnemoen 30*c752bb07SHavard Skinnemoen /* 32-bit register indices. */ 31*c752bb07SHavard Skinnemoen typedef enum NPCM7xxOTPRegister { 32*c752bb07SHavard Skinnemoen NPCM7XX_OTP_FST, 33*c752bb07SHavard Skinnemoen NPCM7XX_OTP_FADDR, 34*c752bb07SHavard Skinnemoen NPCM7XX_OTP_FDATA, 35*c752bb07SHavard Skinnemoen NPCM7XX_OTP_FCFG, 36*c752bb07SHavard Skinnemoen /* Offset 0x10 is FKEYIND in OTP1, FUSTRAP in OTP2 */ 37*c752bb07SHavard Skinnemoen NPCM7XX_OTP_FKEYIND = 0x0010 / sizeof(uint32_t), 38*c752bb07SHavard Skinnemoen NPCM7XX_OTP_FUSTRAP = 0x0010 / sizeof(uint32_t), 39*c752bb07SHavard Skinnemoen NPCM7XX_OTP_FCTL, 40*c752bb07SHavard Skinnemoen NPCM7XX_OTP_REGS_END, 41*c752bb07SHavard Skinnemoen } NPCM7xxOTPRegister; 42*c752bb07SHavard Skinnemoen 43*c752bb07SHavard Skinnemoen /* Register field definitions. */ 44*c752bb07SHavard Skinnemoen #define FST_RIEN BIT(2) 45*c752bb07SHavard Skinnemoen #define FST_RDST BIT(1) 46*c752bb07SHavard Skinnemoen #define FST_RDY BIT(0) 47*c752bb07SHavard Skinnemoen #define FST_RO_MASK (FST_RDST | FST_RDY) 48*c752bb07SHavard Skinnemoen 49*c752bb07SHavard Skinnemoen #define FADDR_BYTEADDR(rv) extract32((rv), 0, 10) 50*c752bb07SHavard Skinnemoen #define FADDR_BITPOS(rv) extract32((rv), 10, 3) 51*c752bb07SHavard Skinnemoen 52*c752bb07SHavard Skinnemoen #define FDATA_CLEAR 0x00000001 53*c752bb07SHavard Skinnemoen 54*c752bb07SHavard Skinnemoen #define FCFG_FDIS BIT(31) 55*c752bb07SHavard Skinnemoen #define FCFG_FCFGLK_MASK 0x00ff0000 56*c752bb07SHavard Skinnemoen 57*c752bb07SHavard Skinnemoen #define FCTL_PROG_CMD1 0x00000001 58*c752bb07SHavard Skinnemoen #define FCTL_PROG_CMD2 0xbf79e5d0 59*c752bb07SHavard Skinnemoen #define FCTL_READ_CMD 0x00000002 60*c752bb07SHavard Skinnemoen 61*c752bb07SHavard Skinnemoen /** 62*c752bb07SHavard Skinnemoen * struct NPCM7xxOTPClass - OTP module class. 63*c752bb07SHavard Skinnemoen * @parent: System bus device class. 64*c752bb07SHavard Skinnemoen * @mmio_ops: MMIO register operations for this type of module. 65*c752bb07SHavard Skinnemoen * 66*c752bb07SHavard Skinnemoen * The two OTP modules (key-storage and fuse-array) have slightly different 67*c752bb07SHavard Skinnemoen * behavior, so we give them different MMIO register operations. 68*c752bb07SHavard Skinnemoen */ 69*c752bb07SHavard Skinnemoen struct NPCM7xxOTPClass { 70*c752bb07SHavard Skinnemoen SysBusDeviceClass parent; 71*c752bb07SHavard Skinnemoen 72*c752bb07SHavard Skinnemoen const MemoryRegionOps *mmio_ops; 73*c752bb07SHavard Skinnemoen }; 74*c752bb07SHavard Skinnemoen 75*c752bb07SHavard Skinnemoen #define NPCM7XX_OTP_CLASS(klass) \ 76*c752bb07SHavard Skinnemoen OBJECT_CLASS_CHECK(NPCM7xxOTPClass, (klass), TYPE_NPCM7XX_OTP) 77*c752bb07SHavard Skinnemoen #define NPCM7XX_OTP_GET_CLASS(obj) \ 78*c752bb07SHavard Skinnemoen OBJECT_GET_CLASS(NPCM7xxOTPClass, (obj), TYPE_NPCM7XX_OTP) 79*c752bb07SHavard Skinnemoen 80*c752bb07SHavard Skinnemoen static uint8_t ecc_encode_nibble(uint8_t n) 81*c752bb07SHavard Skinnemoen { 82*c752bb07SHavard Skinnemoen uint8_t result = n; 83*c752bb07SHavard Skinnemoen 84*c752bb07SHavard Skinnemoen result |= (((n >> 0) & 1) ^ ((n >> 1) & 1)) << 4; 85*c752bb07SHavard Skinnemoen result |= (((n >> 2) & 1) ^ ((n >> 3) & 1)) << 5; 86*c752bb07SHavard Skinnemoen result |= (((n >> 0) & 1) ^ ((n >> 2) & 1)) << 6; 87*c752bb07SHavard Skinnemoen result |= (((n >> 1) & 1) ^ ((n >> 3) & 1)) << 7; 88*c752bb07SHavard Skinnemoen 89*c752bb07SHavard Skinnemoen return result; 90*c752bb07SHavard Skinnemoen } 91*c752bb07SHavard Skinnemoen 92*c752bb07SHavard Skinnemoen void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data, 93*c752bb07SHavard Skinnemoen unsigned int offset, unsigned int len) 94*c752bb07SHavard Skinnemoen { 95*c752bb07SHavard Skinnemoen const uint8_t *src = data; 96*c752bb07SHavard Skinnemoen uint8_t *dst = &s->array[offset]; 97*c752bb07SHavard Skinnemoen 98*c752bb07SHavard Skinnemoen while (len-- > 0) { 99*c752bb07SHavard Skinnemoen uint8_t c = *src++; 100*c752bb07SHavard Skinnemoen 101*c752bb07SHavard Skinnemoen *dst++ = ecc_encode_nibble(extract8(c, 0, 4)); 102*c752bb07SHavard Skinnemoen *dst++ = ecc_encode_nibble(extract8(c, 4, 4)); 103*c752bb07SHavard Skinnemoen } 104*c752bb07SHavard Skinnemoen } 105*c752bb07SHavard Skinnemoen 106*c752bb07SHavard Skinnemoen /* Common register read handler for both OTP classes. */ 107*c752bb07SHavard Skinnemoen static uint64_t npcm7xx_otp_read(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg) 108*c752bb07SHavard Skinnemoen { 109*c752bb07SHavard Skinnemoen uint32_t value = 0; 110*c752bb07SHavard Skinnemoen 111*c752bb07SHavard Skinnemoen switch (reg) { 112*c752bb07SHavard Skinnemoen case NPCM7XX_OTP_FST: 113*c752bb07SHavard Skinnemoen case NPCM7XX_OTP_FADDR: 114*c752bb07SHavard Skinnemoen case NPCM7XX_OTP_FDATA: 115*c752bb07SHavard Skinnemoen case NPCM7XX_OTP_FCFG: 116*c752bb07SHavard Skinnemoen value = s->regs[reg]; 117*c752bb07SHavard Skinnemoen break; 118*c752bb07SHavard Skinnemoen 119*c752bb07SHavard Skinnemoen case NPCM7XX_OTP_FCTL: 120*c752bb07SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 121*c752bb07SHavard Skinnemoen "%s: read from write-only FCTL register\n", 122*c752bb07SHavard Skinnemoen DEVICE(s)->canonical_path); 123*c752bb07SHavard Skinnemoen break; 124*c752bb07SHavard Skinnemoen 125*c752bb07SHavard Skinnemoen default: 126*c752bb07SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, "%s: read from invalid offset 0x%zx\n", 127*c752bb07SHavard Skinnemoen DEVICE(s)->canonical_path, reg * sizeof(uint32_t)); 128*c752bb07SHavard Skinnemoen break; 129*c752bb07SHavard Skinnemoen } 130*c752bb07SHavard Skinnemoen 131*c752bb07SHavard Skinnemoen return value; 132*c752bb07SHavard Skinnemoen } 133*c752bb07SHavard Skinnemoen 134*c752bb07SHavard Skinnemoen /* Read a byte from the OTP array into the data register. */ 135*c752bb07SHavard Skinnemoen static void npcm7xx_otp_read_array(NPCM7xxOTPState *s) 136*c752bb07SHavard Skinnemoen { 137*c752bb07SHavard Skinnemoen uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR]; 138*c752bb07SHavard Skinnemoen 139*c752bb07SHavard Skinnemoen s->regs[NPCM7XX_OTP_FDATA] = s->array[FADDR_BYTEADDR(faddr)]; 140*c752bb07SHavard Skinnemoen s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY; 141*c752bb07SHavard Skinnemoen } 142*c752bb07SHavard Skinnemoen 143*c752bb07SHavard Skinnemoen /* Program a byte from the data register into the OTP array. */ 144*c752bb07SHavard Skinnemoen static void npcm7xx_otp_program_array(NPCM7xxOTPState *s) 145*c752bb07SHavard Skinnemoen { 146*c752bb07SHavard Skinnemoen uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR]; 147*c752bb07SHavard Skinnemoen 148*c752bb07SHavard Skinnemoen /* Bits can only go 0->1, never 1->0. */ 149*c752bb07SHavard Skinnemoen s->array[FADDR_BYTEADDR(faddr)] |= (1U << FADDR_BITPOS(faddr)); 150*c752bb07SHavard Skinnemoen s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY; 151*c752bb07SHavard Skinnemoen } 152*c752bb07SHavard Skinnemoen 153*c752bb07SHavard Skinnemoen /* Compute the next value of the FCFG register. */ 154*c752bb07SHavard Skinnemoen static uint32_t npcm7xx_otp_compute_fcfg(uint32_t cur_value, uint32_t new_value) 155*c752bb07SHavard Skinnemoen { 156*c752bb07SHavard Skinnemoen uint32_t lock_mask; 157*c752bb07SHavard Skinnemoen uint32_t value; 158*c752bb07SHavard Skinnemoen 159*c752bb07SHavard Skinnemoen /* 160*c752bb07SHavard Skinnemoen * FCFGLK holds sticky bits 16..23, indicating which bits in FPRGLK (8..15) 161*c752bb07SHavard Skinnemoen * and FRDLK (0..7) that are read-only. 162*c752bb07SHavard Skinnemoen */ 163*c752bb07SHavard Skinnemoen lock_mask = (cur_value & FCFG_FCFGLK_MASK) >> 8; 164*c752bb07SHavard Skinnemoen lock_mask |= lock_mask >> 8; 165*c752bb07SHavard Skinnemoen /* FDIS and FCFGLK bits are sticky (write 1 to set; can't clear). */ 166*c752bb07SHavard Skinnemoen value = cur_value & (FCFG_FDIS | FCFG_FCFGLK_MASK); 167*c752bb07SHavard Skinnemoen /* Preserve read-only bits in FPRGLK and FRDLK */ 168*c752bb07SHavard Skinnemoen value |= cur_value & lock_mask; 169*c752bb07SHavard Skinnemoen /* Set all bits that aren't read-only. */ 170*c752bb07SHavard Skinnemoen value |= new_value & ~lock_mask; 171*c752bb07SHavard Skinnemoen 172*c752bb07SHavard Skinnemoen return value; 173*c752bb07SHavard Skinnemoen } 174*c752bb07SHavard Skinnemoen 175*c752bb07SHavard Skinnemoen /* Common register write handler for both OTP classes. */ 176*c752bb07SHavard Skinnemoen static void npcm7xx_otp_write(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg, 177*c752bb07SHavard Skinnemoen uint32_t value) 178*c752bb07SHavard Skinnemoen { 179*c752bb07SHavard Skinnemoen switch (reg) { 180*c752bb07SHavard Skinnemoen case NPCM7XX_OTP_FST: 181*c752bb07SHavard Skinnemoen /* RDST is cleared by writing 1 to it. */ 182*c752bb07SHavard Skinnemoen if (value & FST_RDST) { 183*c752bb07SHavard Skinnemoen s->regs[NPCM7XX_OTP_FST] &= ~FST_RDST; 184*c752bb07SHavard Skinnemoen } 185*c752bb07SHavard Skinnemoen /* Preserve read-only and write-one-to-clear bits */ 186*c752bb07SHavard Skinnemoen value &= ~FST_RO_MASK; 187*c752bb07SHavard Skinnemoen value |= s->regs[NPCM7XX_OTP_FST] & FST_RO_MASK; 188*c752bb07SHavard Skinnemoen break; 189*c752bb07SHavard Skinnemoen 190*c752bb07SHavard Skinnemoen case NPCM7XX_OTP_FADDR: 191*c752bb07SHavard Skinnemoen break; 192*c752bb07SHavard Skinnemoen 193*c752bb07SHavard Skinnemoen case NPCM7XX_OTP_FDATA: 194*c752bb07SHavard Skinnemoen /* 195*c752bb07SHavard Skinnemoen * This register is cleared by writing a magic value to it; no other 196*c752bb07SHavard Skinnemoen * values can be written. 197*c752bb07SHavard Skinnemoen */ 198*c752bb07SHavard Skinnemoen if (value == FDATA_CLEAR) { 199*c752bb07SHavard Skinnemoen value = 0; 200*c752bb07SHavard Skinnemoen } else { 201*c752bb07SHavard Skinnemoen value = s->regs[NPCM7XX_OTP_FDATA]; 202*c752bb07SHavard Skinnemoen } 203*c752bb07SHavard Skinnemoen break; 204*c752bb07SHavard Skinnemoen 205*c752bb07SHavard Skinnemoen case NPCM7XX_OTP_FCFG: 206*c752bb07SHavard Skinnemoen value = npcm7xx_otp_compute_fcfg(s->regs[NPCM7XX_OTP_FCFG], value); 207*c752bb07SHavard Skinnemoen break; 208*c752bb07SHavard Skinnemoen 209*c752bb07SHavard Skinnemoen case NPCM7XX_OTP_FCTL: 210*c752bb07SHavard Skinnemoen switch (value) { 211*c752bb07SHavard Skinnemoen case FCTL_READ_CMD: 212*c752bb07SHavard Skinnemoen npcm7xx_otp_read_array(s); 213*c752bb07SHavard Skinnemoen break; 214*c752bb07SHavard Skinnemoen 215*c752bb07SHavard Skinnemoen case FCTL_PROG_CMD1: 216*c752bb07SHavard Skinnemoen /* 217*c752bb07SHavard Skinnemoen * Programming requires writing two separate magic values to this 218*c752bb07SHavard Skinnemoen * register; this is the first one. Just store it so it can be 219*c752bb07SHavard Skinnemoen * verified later when the second magic value is received. 220*c752bb07SHavard Skinnemoen */ 221*c752bb07SHavard Skinnemoen break; 222*c752bb07SHavard Skinnemoen 223*c752bb07SHavard Skinnemoen case FCTL_PROG_CMD2: 224*c752bb07SHavard Skinnemoen /* 225*c752bb07SHavard Skinnemoen * Only initiate programming if we received the first half of the 226*c752bb07SHavard Skinnemoen * command immediately before this one. 227*c752bb07SHavard Skinnemoen */ 228*c752bb07SHavard Skinnemoen if (s->regs[NPCM7XX_OTP_FCTL] == FCTL_PROG_CMD1) { 229*c752bb07SHavard Skinnemoen npcm7xx_otp_program_array(s); 230*c752bb07SHavard Skinnemoen } 231*c752bb07SHavard Skinnemoen break; 232*c752bb07SHavard Skinnemoen 233*c752bb07SHavard Skinnemoen default: 234*c752bb07SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 235*c752bb07SHavard Skinnemoen "%s: unrecognized FCNTL value 0x%" PRIx32 "\n", 236*c752bb07SHavard Skinnemoen DEVICE(s)->canonical_path, value); 237*c752bb07SHavard Skinnemoen break; 238*c752bb07SHavard Skinnemoen } 239*c752bb07SHavard Skinnemoen if (value != FCTL_PROG_CMD1) { 240*c752bb07SHavard Skinnemoen value = 0; 241*c752bb07SHavard Skinnemoen } 242*c752bb07SHavard Skinnemoen break; 243*c752bb07SHavard Skinnemoen 244*c752bb07SHavard Skinnemoen default: 245*c752bb07SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, "%s: write to invalid offset 0x%zx\n", 246*c752bb07SHavard Skinnemoen DEVICE(s)->canonical_path, reg * sizeof(uint32_t)); 247*c752bb07SHavard Skinnemoen return; 248*c752bb07SHavard Skinnemoen } 249*c752bb07SHavard Skinnemoen 250*c752bb07SHavard Skinnemoen s->regs[reg] = value; 251*c752bb07SHavard Skinnemoen } 252*c752bb07SHavard Skinnemoen 253*c752bb07SHavard Skinnemoen /* Register read handler specific to the fuse array OTP module. */ 254*c752bb07SHavard Skinnemoen static uint64_t npcm7xx_fuse_array_read(void *opaque, hwaddr addr, 255*c752bb07SHavard Skinnemoen unsigned int size) 256*c752bb07SHavard Skinnemoen { 257*c752bb07SHavard Skinnemoen NPCM7xxOTPRegister reg = addr / sizeof(uint32_t); 258*c752bb07SHavard Skinnemoen NPCM7xxOTPState *s = opaque; 259*c752bb07SHavard Skinnemoen uint32_t value; 260*c752bb07SHavard Skinnemoen 261*c752bb07SHavard Skinnemoen /* 262*c752bb07SHavard Skinnemoen * Only the Fuse Strap register needs special handling; all other registers 263*c752bb07SHavard Skinnemoen * work the same way for both kinds of OTP modules. 264*c752bb07SHavard Skinnemoen */ 265*c752bb07SHavard Skinnemoen if (reg != NPCM7XX_OTP_FUSTRAP) { 266*c752bb07SHavard Skinnemoen value = npcm7xx_otp_read(s, reg); 267*c752bb07SHavard Skinnemoen } else { 268*c752bb07SHavard Skinnemoen /* FUSTRAP is stored as three copies in the OTP array. */ 269*c752bb07SHavard Skinnemoen uint32_t fustrap[3]; 270*c752bb07SHavard Skinnemoen 271*c752bb07SHavard Skinnemoen memcpy(fustrap, &s->array[0], sizeof(fustrap)); 272*c752bb07SHavard Skinnemoen 273*c752bb07SHavard Skinnemoen /* Determine value by a majority vote on each bit. */ 274*c752bb07SHavard Skinnemoen value = (fustrap[0] & fustrap[1]) | (fustrap[0] & fustrap[2]) | 275*c752bb07SHavard Skinnemoen (fustrap[1] & fustrap[2]); 276*c752bb07SHavard Skinnemoen } 277*c752bb07SHavard Skinnemoen 278*c752bb07SHavard Skinnemoen return value; 279*c752bb07SHavard Skinnemoen } 280*c752bb07SHavard Skinnemoen 281*c752bb07SHavard Skinnemoen /* Register write handler specific to the fuse array OTP module. */ 282*c752bb07SHavard Skinnemoen static void npcm7xx_fuse_array_write(void *opaque, hwaddr addr, uint64_t v, 283*c752bb07SHavard Skinnemoen unsigned int size) 284*c752bb07SHavard Skinnemoen { 285*c752bb07SHavard Skinnemoen NPCM7xxOTPRegister reg = addr / sizeof(uint32_t); 286*c752bb07SHavard Skinnemoen NPCM7xxOTPState *s = opaque; 287*c752bb07SHavard Skinnemoen 288*c752bb07SHavard Skinnemoen /* 289*c752bb07SHavard Skinnemoen * The Fuse Strap register is read-only. Other registers are handled by 290*c752bb07SHavard Skinnemoen * common code. 291*c752bb07SHavard Skinnemoen */ 292*c752bb07SHavard Skinnemoen if (reg != NPCM7XX_OTP_FUSTRAP) { 293*c752bb07SHavard Skinnemoen npcm7xx_otp_write(s, reg, v); 294*c752bb07SHavard Skinnemoen } 295*c752bb07SHavard Skinnemoen } 296*c752bb07SHavard Skinnemoen 297*c752bb07SHavard Skinnemoen static const MemoryRegionOps npcm7xx_fuse_array_ops = { 298*c752bb07SHavard Skinnemoen .read = npcm7xx_fuse_array_read, 299*c752bb07SHavard Skinnemoen .write = npcm7xx_fuse_array_write, 300*c752bb07SHavard Skinnemoen .endianness = DEVICE_LITTLE_ENDIAN, 301*c752bb07SHavard Skinnemoen .valid = { 302*c752bb07SHavard Skinnemoen .min_access_size = 4, 303*c752bb07SHavard Skinnemoen .max_access_size = 4, 304*c752bb07SHavard Skinnemoen .unaligned = false, 305*c752bb07SHavard Skinnemoen }, 306*c752bb07SHavard Skinnemoen }; 307*c752bb07SHavard Skinnemoen 308*c752bb07SHavard Skinnemoen /* Register read handler specific to the key storage OTP module. */ 309*c752bb07SHavard Skinnemoen static uint64_t npcm7xx_key_storage_read(void *opaque, hwaddr addr, 310*c752bb07SHavard Skinnemoen unsigned int size) 311*c752bb07SHavard Skinnemoen { 312*c752bb07SHavard Skinnemoen NPCM7xxOTPRegister reg = addr / sizeof(uint32_t); 313*c752bb07SHavard Skinnemoen NPCM7xxOTPState *s = opaque; 314*c752bb07SHavard Skinnemoen 315*c752bb07SHavard Skinnemoen /* 316*c752bb07SHavard Skinnemoen * Only the Fuse Key Index register needs special handling; all other 317*c752bb07SHavard Skinnemoen * registers work the same way for both kinds of OTP modules. 318*c752bb07SHavard Skinnemoen */ 319*c752bb07SHavard Skinnemoen if (reg != NPCM7XX_OTP_FKEYIND) { 320*c752bb07SHavard Skinnemoen return npcm7xx_otp_read(s, reg); 321*c752bb07SHavard Skinnemoen } 322*c752bb07SHavard Skinnemoen 323*c752bb07SHavard Skinnemoen qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__); 324*c752bb07SHavard Skinnemoen 325*c752bb07SHavard Skinnemoen return s->regs[NPCM7XX_OTP_FKEYIND]; 326*c752bb07SHavard Skinnemoen } 327*c752bb07SHavard Skinnemoen 328*c752bb07SHavard Skinnemoen /* Register write handler specific to the key storage OTP module. */ 329*c752bb07SHavard Skinnemoen static void npcm7xx_key_storage_write(void *opaque, hwaddr addr, uint64_t v, 330*c752bb07SHavard Skinnemoen unsigned int size) 331*c752bb07SHavard Skinnemoen { 332*c752bb07SHavard Skinnemoen NPCM7xxOTPRegister reg = addr / sizeof(uint32_t); 333*c752bb07SHavard Skinnemoen NPCM7xxOTPState *s = opaque; 334*c752bb07SHavard Skinnemoen 335*c752bb07SHavard Skinnemoen /* 336*c752bb07SHavard Skinnemoen * Only the Fuse Key Index register needs special handling; all other 337*c752bb07SHavard Skinnemoen * registers work the same way for both kinds of OTP modules. 338*c752bb07SHavard Skinnemoen */ 339*c752bb07SHavard Skinnemoen if (reg != NPCM7XX_OTP_FKEYIND) { 340*c752bb07SHavard Skinnemoen npcm7xx_otp_write(s, reg, v); 341*c752bb07SHavard Skinnemoen return; 342*c752bb07SHavard Skinnemoen } 343*c752bb07SHavard Skinnemoen 344*c752bb07SHavard Skinnemoen qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__); 345*c752bb07SHavard Skinnemoen 346*c752bb07SHavard Skinnemoen s->regs[NPCM7XX_OTP_FKEYIND] = v; 347*c752bb07SHavard Skinnemoen } 348*c752bb07SHavard Skinnemoen 349*c752bb07SHavard Skinnemoen static const MemoryRegionOps npcm7xx_key_storage_ops = { 350*c752bb07SHavard Skinnemoen .read = npcm7xx_key_storage_read, 351*c752bb07SHavard Skinnemoen .write = npcm7xx_key_storage_write, 352*c752bb07SHavard Skinnemoen .endianness = DEVICE_LITTLE_ENDIAN, 353*c752bb07SHavard Skinnemoen .valid = { 354*c752bb07SHavard Skinnemoen .min_access_size = 4, 355*c752bb07SHavard Skinnemoen .max_access_size = 4, 356*c752bb07SHavard Skinnemoen .unaligned = false, 357*c752bb07SHavard Skinnemoen }, 358*c752bb07SHavard Skinnemoen }; 359*c752bb07SHavard Skinnemoen 360*c752bb07SHavard Skinnemoen static void npcm7xx_otp_enter_reset(Object *obj, ResetType type) 361*c752bb07SHavard Skinnemoen { 362*c752bb07SHavard Skinnemoen NPCM7xxOTPState *s = NPCM7XX_OTP(obj); 363*c752bb07SHavard Skinnemoen 364*c752bb07SHavard Skinnemoen memset(s->regs, 0, sizeof(s->regs)); 365*c752bb07SHavard Skinnemoen 366*c752bb07SHavard Skinnemoen s->regs[NPCM7XX_OTP_FST] = 0x00000001; 367*c752bb07SHavard Skinnemoen s->regs[NPCM7XX_OTP_FCFG] = 0x20000000; 368*c752bb07SHavard Skinnemoen } 369*c752bb07SHavard Skinnemoen 370*c752bb07SHavard Skinnemoen static void npcm7xx_otp_realize(DeviceState *dev, Error **errp) 371*c752bb07SHavard Skinnemoen { 372*c752bb07SHavard Skinnemoen NPCM7xxOTPClass *oc = NPCM7XX_OTP_GET_CLASS(dev); 373*c752bb07SHavard Skinnemoen NPCM7xxOTPState *s = NPCM7XX_OTP(dev); 374*c752bb07SHavard Skinnemoen SysBusDevice *sbd = &s->parent; 375*c752bb07SHavard Skinnemoen 376*c752bb07SHavard Skinnemoen memset(s->array, 0, sizeof(s->array)); 377*c752bb07SHavard Skinnemoen 378*c752bb07SHavard Skinnemoen memory_region_init_io(&s->mmio, OBJECT(s), oc->mmio_ops, s, "regs", 379*c752bb07SHavard Skinnemoen NPCM7XX_OTP_REGS_SIZE); 380*c752bb07SHavard Skinnemoen sysbus_init_mmio(sbd, &s->mmio); 381*c752bb07SHavard Skinnemoen } 382*c752bb07SHavard Skinnemoen 383*c752bb07SHavard Skinnemoen static const VMStateDescription vmstate_npcm7xx_otp = { 384*c752bb07SHavard Skinnemoen .name = "npcm7xx-otp", 385*c752bb07SHavard Skinnemoen .version_id = 0, 386*c752bb07SHavard Skinnemoen .minimum_version_id = 0, 387*c752bb07SHavard Skinnemoen .fields = (VMStateField[]) { 388*c752bb07SHavard Skinnemoen VMSTATE_UINT32_ARRAY(regs, NPCM7xxOTPState, NPCM7XX_OTP_NR_REGS), 389*c752bb07SHavard Skinnemoen VMSTATE_UINT8_ARRAY(array, NPCM7xxOTPState, NPCM7XX_OTP_ARRAY_BYTES), 390*c752bb07SHavard Skinnemoen VMSTATE_END_OF_LIST(), 391*c752bb07SHavard Skinnemoen }, 392*c752bb07SHavard Skinnemoen }; 393*c752bb07SHavard Skinnemoen 394*c752bb07SHavard Skinnemoen static void npcm7xx_otp_class_init(ObjectClass *klass, void *data) 395*c752bb07SHavard Skinnemoen { 396*c752bb07SHavard Skinnemoen ResettableClass *rc = RESETTABLE_CLASS(klass); 397*c752bb07SHavard Skinnemoen DeviceClass *dc = DEVICE_CLASS(klass); 398*c752bb07SHavard Skinnemoen 399*c752bb07SHavard Skinnemoen QEMU_BUILD_BUG_ON(NPCM7XX_OTP_REGS_END > NPCM7XX_OTP_NR_REGS); 400*c752bb07SHavard Skinnemoen 401*c752bb07SHavard Skinnemoen dc->realize = npcm7xx_otp_realize; 402*c752bb07SHavard Skinnemoen dc->vmsd = &vmstate_npcm7xx_otp; 403*c752bb07SHavard Skinnemoen rc->phases.enter = npcm7xx_otp_enter_reset; 404*c752bb07SHavard Skinnemoen } 405*c752bb07SHavard Skinnemoen 406*c752bb07SHavard Skinnemoen static void npcm7xx_key_storage_class_init(ObjectClass *klass, void *data) 407*c752bb07SHavard Skinnemoen { 408*c752bb07SHavard Skinnemoen NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass); 409*c752bb07SHavard Skinnemoen 410*c752bb07SHavard Skinnemoen oc->mmio_ops = &npcm7xx_key_storage_ops; 411*c752bb07SHavard Skinnemoen } 412*c752bb07SHavard Skinnemoen 413*c752bb07SHavard Skinnemoen static void npcm7xx_fuse_array_class_init(ObjectClass *klass, void *data) 414*c752bb07SHavard Skinnemoen { 415*c752bb07SHavard Skinnemoen NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass); 416*c752bb07SHavard Skinnemoen 417*c752bb07SHavard Skinnemoen oc->mmio_ops = &npcm7xx_fuse_array_ops; 418*c752bb07SHavard Skinnemoen } 419*c752bb07SHavard Skinnemoen 420*c752bb07SHavard Skinnemoen static const TypeInfo npcm7xx_otp_types[] = { 421*c752bb07SHavard Skinnemoen { 422*c752bb07SHavard Skinnemoen .name = TYPE_NPCM7XX_OTP, 423*c752bb07SHavard Skinnemoen .parent = TYPE_SYS_BUS_DEVICE, 424*c752bb07SHavard Skinnemoen .instance_size = sizeof(NPCM7xxOTPState), 425*c752bb07SHavard Skinnemoen .class_size = sizeof(NPCM7xxOTPClass), 426*c752bb07SHavard Skinnemoen .class_init = npcm7xx_otp_class_init, 427*c752bb07SHavard Skinnemoen .abstract = true, 428*c752bb07SHavard Skinnemoen }, 429*c752bb07SHavard Skinnemoen { 430*c752bb07SHavard Skinnemoen .name = TYPE_NPCM7XX_KEY_STORAGE, 431*c752bb07SHavard Skinnemoen .parent = TYPE_NPCM7XX_OTP, 432*c752bb07SHavard Skinnemoen .class_init = npcm7xx_key_storage_class_init, 433*c752bb07SHavard Skinnemoen }, 434*c752bb07SHavard Skinnemoen { 435*c752bb07SHavard Skinnemoen .name = TYPE_NPCM7XX_FUSE_ARRAY, 436*c752bb07SHavard Skinnemoen .parent = TYPE_NPCM7XX_OTP, 437*c752bb07SHavard Skinnemoen .class_init = npcm7xx_fuse_array_class_init, 438*c752bb07SHavard Skinnemoen }, 439*c752bb07SHavard Skinnemoen }; 440*c752bb07SHavard Skinnemoen DEFINE_TYPES(npcm7xx_otp_types); 441