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