2 * Nuvoton NPCM Peripheral SPI Module (PSPI)
4 * Copyright 2023 Google LLC
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 #include "qemu/osdep.h"
20 #include "hw/registerfields.h"
21 #include "hw/ssi/npcm_pspi.h"
22 #include "migration/vmstate.h"
23 #include "qapi/error.h"
24 #include "qemu/error-report.h"
26 #include "qemu/module.h"
27 #include "qemu/units.h"
33 FIELD(PSPI_CTL1
, SPIEN
, 0, 1)
34 FIELD(PSPI_CTL1
, MOD
, 2, 1)
35 FIELD(PSPI_CTL1
, EIR
, 5, 1)
36 FIELD(PSPI_CTL1
, EIW
, 6, 1)
37 FIELD(PSPI_CTL1
, SCM
, 7, 1)
38 FIELD(PSPI_CTL1
, SCIDL
, 8, 1)
39 FIELD(PSPI_CTL1
, SCDV
, 9, 7)
41 FIELD(PSPI_STAT
, BSY
, 0, 1)
42 FIELD(PSPI_STAT
, RBF
, 1, 1)
44 static void npcm_pspi_update_irq(NPCMPSPIState
*s
)
48 /* Only fire IRQ when the module is enabled. */
49 if (FIELD_EX16(s
->regs
[R_PSPI_CTL1
], PSPI_CTL1
, SPIEN
)) {
50 /* Update interrupt as BSY is cleared. */
51 if ((!FIELD_EX16(s
->regs
[R_PSPI_STAT
], PSPI_STAT
, BSY
)) &&
52 FIELD_EX16(s
->regs
[R_PSPI_CTL1
], PSPI_CTL1
, EIW
)) {
56 /* Update interrupt as RBF is set. */
57 if (FIELD_EX16(s
->regs
[R_PSPI_STAT
], PSPI_STAT
, RBF
) &&
58 FIELD_EX16(s
->regs
[R_PSPI_CTL1
], PSPI_CTL1
, EIR
)) {
62 qemu_set_irq(s
->irq
, level
);
65 static uint16_t npcm_pspi_read_data(NPCMPSPIState
*s
)
67 uint16_t value
= s
->regs
[R_PSPI_DATA
];
69 /* Clear stat bits as the value are read out. */
70 s
->regs
[R_PSPI_STAT
] = 0;
75 static void npcm_pspi_write_data(NPCMPSPIState
*s
, uint16_t data
)
79 if (FIELD_EX16(s
->regs
[R_PSPI_CTL1
], PSPI_CTL1
, MOD
)) {
80 value
= ssi_transfer(s
->spi
, extract16(data
, 8, 8)) << 8;
82 value
|= ssi_transfer(s
->spi
, extract16(data
, 0, 8));
83 s
->regs
[R_PSPI_DATA
] = value
;
85 /* Mark data as available */
86 s
->regs
[R_PSPI_STAT
] = R_PSPI_STAT_BSY_MASK
| R_PSPI_STAT_RBF_MASK
;
89 /* Control register read handler. */
90 static uint64_t npcm_pspi_ctrl_read(void *opaque
, hwaddr addr
,
93 NPCMPSPIState
*s
= opaque
;
98 value
= npcm_pspi_read_data(s
);
102 value
= s
->regs
[R_PSPI_CTL1
];
106 value
= s
->regs
[R_PSPI_STAT
];
110 qemu_log_mask(LOG_GUEST_ERROR
,
111 "%s: write to invalid offset 0x%" PRIx64
"\n",
112 DEVICE(s
)->canonical_path
, addr
);
115 trace_npcm_pspi_ctrl_read(DEVICE(s
)->canonical_path
, addr
, value
);
116 npcm_pspi_update_irq(s
);
121 /* Control register write handler. */
122 static void npcm_pspi_ctrl_write(void *opaque
, hwaddr addr
, uint64_t v
,
125 NPCMPSPIState
*s
= opaque
;
128 trace_npcm_pspi_ctrl_write(DEVICE(s
)->canonical_path
, addr
, value
);
132 npcm_pspi_write_data(s
, value
);
136 s
->regs
[R_PSPI_CTL1
] = value
;
140 qemu_log_mask(LOG_GUEST_ERROR
,
141 "%s: write to read-only register PSPI_STAT: 0x%08"
142 PRIx64
"\n", DEVICE(s
)->canonical_path
, v
);
146 qemu_log_mask(LOG_GUEST_ERROR
,
147 "%s: write to invalid offset 0x%" PRIx64
"\n",
148 DEVICE(s
)->canonical_path
, addr
);
151 npcm_pspi_update_irq(s
);
154 static const MemoryRegionOps npcm_pspi_ctrl_ops
= {
155 .read
= npcm_pspi_ctrl_read
,
156 .write
= npcm_pspi_ctrl_write
,
157 .endianness
= DEVICE_LITTLE_ENDIAN
,
159 .min_access_size
= 1,
160 .max_access_size
= 2,
164 .min_access_size
= 2,
165 .max_access_size
= 2,
170 static void npcm_pspi_enter_reset(Object
*obj
, ResetType type
)
172 NPCMPSPIState
*s
= NPCM_PSPI(obj
);
174 trace_npcm_pspi_enter_reset(DEVICE(obj
)->canonical_path
, type
);
175 memset(s
->regs
, 0, sizeof(s
->regs
));
178 static void npcm_pspi_realize(DeviceState
*dev
, Error
**errp
)
180 NPCMPSPIState
*s
= NPCM_PSPI(dev
);
181 SysBusDevice
*sbd
= SYS_BUS_DEVICE(dev
);
182 Object
*obj
= OBJECT(dev
);
184 s
->spi
= ssi_create_bus(dev
, "pspi");
185 memory_region_init_io(&s
->mmio
, obj
, &npcm_pspi_ctrl_ops
, s
,
187 sysbus_init_mmio(sbd
, &s
->mmio
);
188 sysbus_init_irq(sbd
, &s
->irq
);
191 static const VMStateDescription vmstate_npcm_pspi
= {
194 .minimum_version_id
= 0,
195 .fields
= (VMStateField
[]) {
196 VMSTATE_UINT16_ARRAY(regs
, NPCMPSPIState
, NPCM_PSPI_NR_REGS
),
197 VMSTATE_END_OF_LIST(),
202 static void npcm_pspi_class_init(ObjectClass
*klass
, void *data
)
204 ResettableClass
*rc
= RESETTABLE_CLASS(klass
);
205 DeviceClass
*dc
= DEVICE_CLASS(klass
);
207 dc
->desc
= "NPCM Peripheral SPI Module";
208 dc
->realize
= npcm_pspi_realize
;
209 dc
->vmsd
= &vmstate_npcm_pspi
;
210 rc
->phases
.enter
= npcm_pspi_enter_reset
;
213 static const TypeInfo npcm_pspi_types
[] = {
215 .name
= TYPE_NPCM_PSPI
,
216 .parent
= TYPE_SYS_BUS_DEVICE
,
217 .instance_size
= sizeof(NPCMPSPIState
),
218 .class_init
= npcm_pspi_class_init
,
221 DEFINE_TYPES(npcm_pspi_types
);