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
ecc_encode_nibble(uint8_t n)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
npcm7xx_otp_array_write(NPCM7xxOTPState * s,const void * data,unsigned int offset,unsigned int len)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. */
npcm7xx_otp_read(NPCM7xxOTPState * s,NPCM7xxOTPRegister reg)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. */
npcm7xx_otp_read_array(NPCM7xxOTPState * s)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. */
npcm7xx_otp_program_array(NPCM7xxOTPState * s)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. */
npcm7xx_otp_compute_fcfg(uint32_t cur_value,uint32_t new_value)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. */
npcm7xx_otp_write(NPCM7xxOTPState * s,NPCM7xxOTPRegister reg,uint32_t value)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. */
npcm7xx_fuse_array_read(void * opaque,hwaddr addr,unsigned int size)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. */
npcm7xx_fuse_array_write(void * opaque,hwaddr addr,uint64_t v,unsigned int size)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. */
npcm7xx_key_storage_read(void * opaque,hwaddr addr,unsigned int size)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. */
npcm7xx_key_storage_write(void * opaque,hwaddr addr,uint64_t v,unsigned int size)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
npcm7xx_otp_enter_reset(Object * obj,ResetType type)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
npcm7xx_otp_realize(DeviceState * dev,Error ** errp)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
npcm7xx_otp_class_init(ObjectClass * klass,void * data)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
npcm7xx_key_storage_class_init(ObjectClass * klass,void * data)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
npcm7xx_fuse_array_class_init(ObjectClass * klass,void * data)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