1*69fbfb8fSHao Wu /*
2*69fbfb8fSHao Wu * Nuvoton NPCM Peripheral SPI Module (PSPI)
3*69fbfb8fSHao Wu *
4*69fbfb8fSHao Wu * Copyright 2023 Google LLC
5*69fbfb8fSHao Wu *
6*69fbfb8fSHao Wu * This program is free software; you can redistribute it and/or modify it
7*69fbfb8fSHao Wu * under the terms of the GNU General Public License as published by the
8*69fbfb8fSHao Wu * Free Software Foundation; either version 2 of the License, or
9*69fbfb8fSHao Wu * (at your option) any later version.
10*69fbfb8fSHao Wu *
11*69fbfb8fSHao Wu * This program is distributed in the hope that it will be useful, but WITHOUT
12*69fbfb8fSHao Wu * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13*69fbfb8fSHao Wu * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14*69fbfb8fSHao Wu * for more details.
15*69fbfb8fSHao Wu */
16*69fbfb8fSHao Wu
17*69fbfb8fSHao Wu #include "qemu/osdep.h"
18*69fbfb8fSHao Wu
19*69fbfb8fSHao Wu #include "hw/irq.h"
20*69fbfb8fSHao Wu #include "hw/registerfields.h"
21*69fbfb8fSHao Wu #include "hw/ssi/npcm_pspi.h"
22*69fbfb8fSHao Wu #include "migration/vmstate.h"
23*69fbfb8fSHao Wu #include "qapi/error.h"
24*69fbfb8fSHao Wu #include "qemu/error-report.h"
25*69fbfb8fSHao Wu #include "qemu/log.h"
26*69fbfb8fSHao Wu #include "qemu/module.h"
27*69fbfb8fSHao Wu #include "qemu/units.h"
28*69fbfb8fSHao Wu
29*69fbfb8fSHao Wu #include "trace.h"
30*69fbfb8fSHao Wu
31*69fbfb8fSHao Wu REG16(PSPI_DATA, 0x0)
32*69fbfb8fSHao Wu REG16(PSPI_CTL1, 0x2)
33*69fbfb8fSHao Wu FIELD(PSPI_CTL1, SPIEN, 0, 1)
34*69fbfb8fSHao Wu FIELD(PSPI_CTL1, MOD, 2, 1)
35*69fbfb8fSHao Wu FIELD(PSPI_CTL1, EIR, 5, 1)
36*69fbfb8fSHao Wu FIELD(PSPI_CTL1, EIW, 6, 1)
37*69fbfb8fSHao Wu FIELD(PSPI_CTL1, SCM, 7, 1)
38*69fbfb8fSHao Wu FIELD(PSPI_CTL1, SCIDL, 8, 1)
39*69fbfb8fSHao Wu FIELD(PSPI_CTL1, SCDV, 9, 7)
40*69fbfb8fSHao Wu REG16(PSPI_STAT, 0x4)
41*69fbfb8fSHao Wu FIELD(PSPI_STAT, BSY, 0, 1)
42*69fbfb8fSHao Wu FIELD(PSPI_STAT, RBF, 1, 1)
43*69fbfb8fSHao Wu
npcm_pspi_update_irq(NPCMPSPIState * s)44*69fbfb8fSHao Wu static void npcm_pspi_update_irq(NPCMPSPIState *s)
45*69fbfb8fSHao Wu {
46*69fbfb8fSHao Wu int level = 0;
47*69fbfb8fSHao Wu
48*69fbfb8fSHao Wu /* Only fire IRQ when the module is enabled. */
49*69fbfb8fSHao Wu if (FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, SPIEN)) {
50*69fbfb8fSHao Wu /* Update interrupt as BSY is cleared. */
51*69fbfb8fSHao Wu if ((!FIELD_EX16(s->regs[R_PSPI_STAT], PSPI_STAT, BSY)) &&
52*69fbfb8fSHao Wu FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, EIW)) {
53*69fbfb8fSHao Wu level = 1;
54*69fbfb8fSHao Wu }
55*69fbfb8fSHao Wu
56*69fbfb8fSHao Wu /* Update interrupt as RBF is set. */
57*69fbfb8fSHao Wu if (FIELD_EX16(s->regs[R_PSPI_STAT], PSPI_STAT, RBF) &&
58*69fbfb8fSHao Wu FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, EIR)) {
59*69fbfb8fSHao Wu level = 1;
60*69fbfb8fSHao Wu }
61*69fbfb8fSHao Wu }
62*69fbfb8fSHao Wu qemu_set_irq(s->irq, level);
63*69fbfb8fSHao Wu }
64*69fbfb8fSHao Wu
npcm_pspi_read_data(NPCMPSPIState * s)65*69fbfb8fSHao Wu static uint16_t npcm_pspi_read_data(NPCMPSPIState *s)
66*69fbfb8fSHao Wu {
67*69fbfb8fSHao Wu uint16_t value = s->regs[R_PSPI_DATA];
68*69fbfb8fSHao Wu
69*69fbfb8fSHao Wu /* Clear stat bits as the value are read out. */
70*69fbfb8fSHao Wu s->regs[R_PSPI_STAT] = 0;
71*69fbfb8fSHao Wu
72*69fbfb8fSHao Wu return value;
73*69fbfb8fSHao Wu }
74*69fbfb8fSHao Wu
npcm_pspi_write_data(NPCMPSPIState * s,uint16_t data)75*69fbfb8fSHao Wu static void npcm_pspi_write_data(NPCMPSPIState *s, uint16_t data)
76*69fbfb8fSHao Wu {
77*69fbfb8fSHao Wu uint16_t value = 0;
78*69fbfb8fSHao Wu
79*69fbfb8fSHao Wu if (FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, MOD)) {
80*69fbfb8fSHao Wu value = ssi_transfer(s->spi, extract16(data, 8, 8)) << 8;
81*69fbfb8fSHao Wu }
82*69fbfb8fSHao Wu value |= ssi_transfer(s->spi, extract16(data, 0, 8));
83*69fbfb8fSHao Wu s->regs[R_PSPI_DATA] = value;
84*69fbfb8fSHao Wu
85*69fbfb8fSHao Wu /* Mark data as available */
86*69fbfb8fSHao Wu s->regs[R_PSPI_STAT] = R_PSPI_STAT_BSY_MASK | R_PSPI_STAT_RBF_MASK;
87*69fbfb8fSHao Wu }
88*69fbfb8fSHao Wu
89*69fbfb8fSHao Wu /* Control register read handler. */
npcm_pspi_ctrl_read(void * opaque,hwaddr addr,unsigned int size)90*69fbfb8fSHao Wu static uint64_t npcm_pspi_ctrl_read(void *opaque, hwaddr addr,
91*69fbfb8fSHao Wu unsigned int size)
92*69fbfb8fSHao Wu {
93*69fbfb8fSHao Wu NPCMPSPIState *s = opaque;
94*69fbfb8fSHao Wu uint16_t value;
95*69fbfb8fSHao Wu
96*69fbfb8fSHao Wu switch (addr) {
97*69fbfb8fSHao Wu case A_PSPI_DATA:
98*69fbfb8fSHao Wu value = npcm_pspi_read_data(s);
99*69fbfb8fSHao Wu break;
100*69fbfb8fSHao Wu
101*69fbfb8fSHao Wu case A_PSPI_CTL1:
102*69fbfb8fSHao Wu value = s->regs[R_PSPI_CTL1];
103*69fbfb8fSHao Wu break;
104*69fbfb8fSHao Wu
105*69fbfb8fSHao Wu case A_PSPI_STAT:
106*69fbfb8fSHao Wu value = s->regs[R_PSPI_STAT];
107*69fbfb8fSHao Wu break;
108*69fbfb8fSHao Wu
109*69fbfb8fSHao Wu default:
110*69fbfb8fSHao Wu qemu_log_mask(LOG_GUEST_ERROR,
111*69fbfb8fSHao Wu "%s: write to invalid offset 0x%" PRIx64 "\n",
112*69fbfb8fSHao Wu DEVICE(s)->canonical_path, addr);
113*69fbfb8fSHao Wu return 0;
114*69fbfb8fSHao Wu }
115*69fbfb8fSHao Wu trace_npcm_pspi_ctrl_read(DEVICE(s)->canonical_path, addr, value);
116*69fbfb8fSHao Wu npcm_pspi_update_irq(s);
117*69fbfb8fSHao Wu
118*69fbfb8fSHao Wu return value;
119*69fbfb8fSHao Wu }
120*69fbfb8fSHao Wu
121*69fbfb8fSHao Wu /* Control register write handler. */
npcm_pspi_ctrl_write(void * opaque,hwaddr addr,uint64_t v,unsigned int size)122*69fbfb8fSHao Wu static void npcm_pspi_ctrl_write(void *opaque, hwaddr addr, uint64_t v,
123*69fbfb8fSHao Wu unsigned int size)
124*69fbfb8fSHao Wu {
125*69fbfb8fSHao Wu NPCMPSPIState *s = opaque;
126*69fbfb8fSHao Wu uint16_t value = v;
127*69fbfb8fSHao Wu
128*69fbfb8fSHao Wu trace_npcm_pspi_ctrl_write(DEVICE(s)->canonical_path, addr, value);
129*69fbfb8fSHao Wu
130*69fbfb8fSHao Wu switch (addr) {
131*69fbfb8fSHao Wu case A_PSPI_DATA:
132*69fbfb8fSHao Wu npcm_pspi_write_data(s, value);
133*69fbfb8fSHao Wu break;
134*69fbfb8fSHao Wu
135*69fbfb8fSHao Wu case A_PSPI_CTL1:
136*69fbfb8fSHao Wu s->regs[R_PSPI_CTL1] = value;
137*69fbfb8fSHao Wu break;
138*69fbfb8fSHao Wu
139*69fbfb8fSHao Wu case A_PSPI_STAT:
140*69fbfb8fSHao Wu qemu_log_mask(LOG_GUEST_ERROR,
141*69fbfb8fSHao Wu "%s: write to read-only register PSPI_STAT: 0x%08"
142*69fbfb8fSHao Wu PRIx64 "\n", DEVICE(s)->canonical_path, v);
143*69fbfb8fSHao Wu break;
144*69fbfb8fSHao Wu
145*69fbfb8fSHao Wu default:
146*69fbfb8fSHao Wu qemu_log_mask(LOG_GUEST_ERROR,
147*69fbfb8fSHao Wu "%s: write to invalid offset 0x%" PRIx64 "\n",
148*69fbfb8fSHao Wu DEVICE(s)->canonical_path, addr);
149*69fbfb8fSHao Wu return;
150*69fbfb8fSHao Wu }
151*69fbfb8fSHao Wu npcm_pspi_update_irq(s);
152*69fbfb8fSHao Wu }
153*69fbfb8fSHao Wu
154*69fbfb8fSHao Wu static const MemoryRegionOps npcm_pspi_ctrl_ops = {
155*69fbfb8fSHao Wu .read = npcm_pspi_ctrl_read,
156*69fbfb8fSHao Wu .write = npcm_pspi_ctrl_write,
157*69fbfb8fSHao Wu .endianness = DEVICE_LITTLE_ENDIAN,
158*69fbfb8fSHao Wu .valid = {
159*69fbfb8fSHao Wu .min_access_size = 1,
160*69fbfb8fSHao Wu .max_access_size = 2,
161*69fbfb8fSHao Wu .unaligned = false,
162*69fbfb8fSHao Wu },
163*69fbfb8fSHao Wu .impl = {
164*69fbfb8fSHao Wu .min_access_size = 2,
165*69fbfb8fSHao Wu .max_access_size = 2,
166*69fbfb8fSHao Wu .unaligned = false,
167*69fbfb8fSHao Wu },
168*69fbfb8fSHao Wu };
169*69fbfb8fSHao Wu
npcm_pspi_enter_reset(Object * obj,ResetType type)170*69fbfb8fSHao Wu static void npcm_pspi_enter_reset(Object *obj, ResetType type)
171*69fbfb8fSHao Wu {
172*69fbfb8fSHao Wu NPCMPSPIState *s = NPCM_PSPI(obj);
173*69fbfb8fSHao Wu
174*69fbfb8fSHao Wu trace_npcm_pspi_enter_reset(DEVICE(obj)->canonical_path, type);
175*69fbfb8fSHao Wu memset(s->regs, 0, sizeof(s->regs));
176*69fbfb8fSHao Wu }
177*69fbfb8fSHao Wu
npcm_pspi_realize(DeviceState * dev,Error ** errp)178*69fbfb8fSHao Wu static void npcm_pspi_realize(DeviceState *dev, Error **errp)
179*69fbfb8fSHao Wu {
180*69fbfb8fSHao Wu NPCMPSPIState *s = NPCM_PSPI(dev);
181*69fbfb8fSHao Wu SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
182*69fbfb8fSHao Wu Object *obj = OBJECT(dev);
183*69fbfb8fSHao Wu
184*69fbfb8fSHao Wu s->spi = ssi_create_bus(dev, "pspi");
185*69fbfb8fSHao Wu memory_region_init_io(&s->mmio, obj, &npcm_pspi_ctrl_ops, s,
186*69fbfb8fSHao Wu "mmio", 4 * KiB);
187*69fbfb8fSHao Wu sysbus_init_mmio(sbd, &s->mmio);
188*69fbfb8fSHao Wu sysbus_init_irq(sbd, &s->irq);
189*69fbfb8fSHao Wu }
190*69fbfb8fSHao Wu
191*69fbfb8fSHao Wu static const VMStateDescription vmstate_npcm_pspi = {
192*69fbfb8fSHao Wu .name = "npcm-pspi",
193*69fbfb8fSHao Wu .version_id = 0,
194*69fbfb8fSHao Wu .minimum_version_id = 0,
195*69fbfb8fSHao Wu .fields = (VMStateField[]) {
196*69fbfb8fSHao Wu VMSTATE_UINT16_ARRAY(regs, NPCMPSPIState, NPCM_PSPI_NR_REGS),
197*69fbfb8fSHao Wu VMSTATE_END_OF_LIST(),
198*69fbfb8fSHao Wu },
199*69fbfb8fSHao Wu };
200*69fbfb8fSHao Wu
201*69fbfb8fSHao Wu
npcm_pspi_class_init(ObjectClass * klass,void * data)202*69fbfb8fSHao Wu static void npcm_pspi_class_init(ObjectClass *klass, void *data)
203*69fbfb8fSHao Wu {
204*69fbfb8fSHao Wu ResettableClass *rc = RESETTABLE_CLASS(klass);
205*69fbfb8fSHao Wu DeviceClass *dc = DEVICE_CLASS(klass);
206*69fbfb8fSHao Wu
207*69fbfb8fSHao Wu dc->desc = "NPCM Peripheral SPI Module";
208*69fbfb8fSHao Wu dc->realize = npcm_pspi_realize;
209*69fbfb8fSHao Wu dc->vmsd = &vmstate_npcm_pspi;
210*69fbfb8fSHao Wu rc->phases.enter = npcm_pspi_enter_reset;
211*69fbfb8fSHao Wu }
212*69fbfb8fSHao Wu
213*69fbfb8fSHao Wu static const TypeInfo npcm_pspi_types[] = {
214*69fbfb8fSHao Wu {
215*69fbfb8fSHao Wu .name = TYPE_NPCM_PSPI,
216*69fbfb8fSHao Wu .parent = TYPE_SYS_BUS_DEVICE,
217*69fbfb8fSHao Wu .instance_size = sizeof(NPCMPSPIState),
218*69fbfb8fSHao Wu .class_init = npcm_pspi_class_init,
219*69fbfb8fSHao Wu },
220*69fbfb8fSHao Wu };
221*69fbfb8fSHao Wu DEFINE_TYPES(npcm_pspi_types);
222