xref: /openbmc/qemu/hw/ssi/npcm_pspi.c (revision 69fbfb8f)
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