xref: /qemu/hw/nvram/npcm7xx_otp.c (revision c752bb079beb57a8527e55859ce4c416fb1663c3)
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