xref: /openbmc/qemu/hw/ssi/pnv_spi.c (revision 29318db133d0b2523bda771f76aa50c08842527f)
1*29318db1SChalapathi V /*
2*29318db1SChalapathi V  * QEMU PowerPC SPI model
3*29318db1SChalapathi V  *
4*29318db1SChalapathi V  * Copyright (c) 2024, IBM Corporation.
5*29318db1SChalapathi V  *
6*29318db1SChalapathi V  * SPDX-License-Identifier: GPL-2.0-or-later
7*29318db1SChalapathi V  */
8*29318db1SChalapathi V 
9*29318db1SChalapathi V #include "qemu/osdep.h"
10*29318db1SChalapathi V #include "qemu/log.h"
11*29318db1SChalapathi V #include "hw/qdev-properties.h"
12*29318db1SChalapathi V #include "hw/ppc/pnv_xscom.h"
13*29318db1SChalapathi V #include "hw/ssi/pnv_spi.h"
14*29318db1SChalapathi V #include "hw/ssi/pnv_spi_regs.h"
15*29318db1SChalapathi V #include "hw/ssi/ssi.h"
16*29318db1SChalapathi V #include <libfdt.h>
17*29318db1SChalapathi V #include "hw/irq.h"
18*29318db1SChalapathi V #include "trace.h"
19*29318db1SChalapathi V 
20*29318db1SChalapathi V /*
21*29318db1SChalapathi V  * Macro from include/hw/ppc/fdt.h
22*29318db1SChalapathi V  * fdt.h cannot be included here as it contain ppc target specific dependency.
23*29318db1SChalapathi V  */
24*29318db1SChalapathi V #define _FDT(exp)                                                  \
25*29318db1SChalapathi V     do {                                                           \
26*29318db1SChalapathi V         int _ret = (exp);                                          \
27*29318db1SChalapathi V         if (_ret < 0) {                                            \
28*29318db1SChalapathi V             qemu_log_mask(LOG_GUEST_ERROR,                         \
29*29318db1SChalapathi V                     "error creating device tree: %s: %s",          \
30*29318db1SChalapathi V                     #exp, fdt_strerror(_ret));                     \
31*29318db1SChalapathi V             exit(1);                                               \
32*29318db1SChalapathi V         }                                                          \
33*29318db1SChalapathi V     } while (0)
34*29318db1SChalapathi V 
35*29318db1SChalapathi V static uint64_t pnv_spi_xscom_read(void *opaque, hwaddr addr, unsigned size)
36*29318db1SChalapathi V {
37*29318db1SChalapathi V     PnvSpi *s = PNV_SPI(opaque);
38*29318db1SChalapathi V     uint32_t reg = addr >> 3;
39*29318db1SChalapathi V     uint64_t val = ~0ull;
40*29318db1SChalapathi V 
41*29318db1SChalapathi V     switch (reg) {
42*29318db1SChalapathi V     case ERROR_REG:
43*29318db1SChalapathi V     case SPI_CTR_CFG_REG:
44*29318db1SChalapathi V     case CONFIG_REG1:
45*29318db1SChalapathi V     case SPI_CLK_CFG_REG:
46*29318db1SChalapathi V     case SPI_MM_REG:
47*29318db1SChalapathi V     case SPI_XMIT_DATA_REG:
48*29318db1SChalapathi V         val = s->regs[reg];
49*29318db1SChalapathi V         break;
50*29318db1SChalapathi V     case SPI_RCV_DATA_REG:
51*29318db1SChalapathi V         val = s->regs[reg];
52*29318db1SChalapathi V         trace_pnv_spi_read_RDR(val);
53*29318db1SChalapathi V         s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 0);
54*29318db1SChalapathi V         break;
55*29318db1SChalapathi V     case SPI_SEQ_OP_REG:
56*29318db1SChalapathi V         val = 0;
57*29318db1SChalapathi V         for (int i = 0; i < PNV_SPI_REG_SIZE; i++) {
58*29318db1SChalapathi V             val = (val << 8) | s->seq_op[i];
59*29318db1SChalapathi V         }
60*29318db1SChalapathi V         break;
61*29318db1SChalapathi V     case SPI_STS_REG:
62*29318db1SChalapathi V         val = s->status;
63*29318db1SChalapathi V         break;
64*29318db1SChalapathi V     default:
65*29318db1SChalapathi V         qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi_regs: Invalid xscom "
66*29318db1SChalapathi V                  "read at 0x%" PRIx32 "\n", reg);
67*29318db1SChalapathi V     }
68*29318db1SChalapathi V 
69*29318db1SChalapathi V     trace_pnv_spi_read(addr, val);
70*29318db1SChalapathi V     return val;
71*29318db1SChalapathi V }
72*29318db1SChalapathi V 
73*29318db1SChalapathi V static void pnv_spi_xscom_write(void *opaque, hwaddr addr,
74*29318db1SChalapathi V                                  uint64_t val, unsigned size)
75*29318db1SChalapathi V {
76*29318db1SChalapathi V     PnvSpi *s = PNV_SPI(opaque);
77*29318db1SChalapathi V     uint32_t reg = addr >> 3;
78*29318db1SChalapathi V 
79*29318db1SChalapathi V     trace_pnv_spi_write(addr, val);
80*29318db1SChalapathi V 
81*29318db1SChalapathi V     switch (reg) {
82*29318db1SChalapathi V     case ERROR_REG:
83*29318db1SChalapathi V     case SPI_CTR_CFG_REG:
84*29318db1SChalapathi V     case CONFIG_REG1:
85*29318db1SChalapathi V     case SPI_MM_REG:
86*29318db1SChalapathi V     case SPI_RCV_DATA_REG:
87*29318db1SChalapathi V         s->regs[reg] = val;
88*29318db1SChalapathi V         break;
89*29318db1SChalapathi V     case SPI_CLK_CFG_REG:
90*29318db1SChalapathi V         /*
91*29318db1SChalapathi V          * To reset the SPI controller write the sequence 0x5 0xA to
92*29318db1SChalapathi V          * reset_control field
93*29318db1SChalapathi V          */
94*29318db1SChalapathi V         if ((GETFIELD(SPI_CLK_CFG_RST_CTRL, s->regs[SPI_CLK_CFG_REG]) == 0x5)
95*29318db1SChalapathi V              && (GETFIELD(SPI_CLK_CFG_RST_CTRL, val) == 0xA)) {
96*29318db1SChalapathi V                 /* SPI controller reset sequence completed, resetting */
97*29318db1SChalapathi V             s->regs[reg] = SPI_CLK_CFG_HARD_RST;
98*29318db1SChalapathi V         } else {
99*29318db1SChalapathi V             s->regs[reg] = val;
100*29318db1SChalapathi V         }
101*29318db1SChalapathi V         break;
102*29318db1SChalapathi V     case SPI_XMIT_DATA_REG:
103*29318db1SChalapathi V         /*
104*29318db1SChalapathi V          * Writing to the transmit data register causes the transmit data
105*29318db1SChalapathi V          * register full status bit in the status register to be set.  Writing
106*29318db1SChalapathi V          * when the transmit data register full status bit is already set
107*29318db1SChalapathi V          * causes a "Resource Not Available" condition.  This is not possible
108*29318db1SChalapathi V          * in the model since writes to this register are not asynchronous to
109*29318db1SChalapathi V          * the operation sequence like it would be in hardware.
110*29318db1SChalapathi V          */
111*29318db1SChalapathi V         s->regs[reg] = val;
112*29318db1SChalapathi V         trace_pnv_spi_write_TDR(val);
113*29318db1SChalapathi V         s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 1);
114*29318db1SChalapathi V         s->status = SETFIELD(SPI_STS_TDR_UNDERRUN, s->status, 0);
115*29318db1SChalapathi V         break;
116*29318db1SChalapathi V     case SPI_SEQ_OP_REG:
117*29318db1SChalapathi V         for (int i = 0; i < PNV_SPI_REG_SIZE; i++) {
118*29318db1SChalapathi V             s->seq_op[i] = (val >> (56 - i * 8)) & 0xFF;
119*29318db1SChalapathi V         }
120*29318db1SChalapathi V         break;
121*29318db1SChalapathi V     case SPI_STS_REG:
122*29318db1SChalapathi V         /* other fields are ignore_write */
123*29318db1SChalapathi V         s->status = SETFIELD(SPI_STS_RDR_OVERRUN, s->status,
124*29318db1SChalapathi V                                   GETFIELD(SPI_STS_RDR, val));
125*29318db1SChalapathi V         s->status = SETFIELD(SPI_STS_TDR_OVERRUN, s->status,
126*29318db1SChalapathi V                                   GETFIELD(SPI_STS_TDR, val));
127*29318db1SChalapathi V         break;
128*29318db1SChalapathi V     default:
129*29318db1SChalapathi V         qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi_regs: Invalid xscom "
130*29318db1SChalapathi V                  "write at 0x%" PRIx32 "\n", reg);
131*29318db1SChalapathi V     }
132*29318db1SChalapathi V     return;
133*29318db1SChalapathi V }
134*29318db1SChalapathi V 
135*29318db1SChalapathi V static const MemoryRegionOps pnv_spi_xscom_ops = {
136*29318db1SChalapathi V     .read = pnv_spi_xscom_read,
137*29318db1SChalapathi V     .write = pnv_spi_xscom_write,
138*29318db1SChalapathi V     .valid.min_access_size = 8,
139*29318db1SChalapathi V     .valid.max_access_size = 8,
140*29318db1SChalapathi V     .impl.min_access_size = 8,
141*29318db1SChalapathi V     .impl.max_access_size = 8,
142*29318db1SChalapathi V     .endianness = DEVICE_BIG_ENDIAN,
143*29318db1SChalapathi V };
144*29318db1SChalapathi V 
145*29318db1SChalapathi V static Property pnv_spi_properties[] = {
146*29318db1SChalapathi V     DEFINE_PROP_UINT32("spic_num", PnvSpi, spic_num, 0),
147*29318db1SChalapathi V     DEFINE_PROP_END_OF_LIST(),
148*29318db1SChalapathi V };
149*29318db1SChalapathi V 
150*29318db1SChalapathi V static void pnv_spi_realize(DeviceState *dev, Error **errp)
151*29318db1SChalapathi V {
152*29318db1SChalapathi V     PnvSpi *s = PNV_SPI(dev);
153*29318db1SChalapathi V     g_autofree char *name = g_strdup_printf(TYPE_PNV_SPI_BUS ".%d",
154*29318db1SChalapathi V                     s->spic_num);
155*29318db1SChalapathi V     s->ssi_bus = ssi_create_bus(dev, name);
156*29318db1SChalapathi V     s->cs_line = g_new0(qemu_irq, 1);
157*29318db1SChalapathi V     qdev_init_gpio_out_named(DEVICE(s), s->cs_line, "cs", 1);
158*29318db1SChalapathi V 
159*29318db1SChalapathi V     /* spi scoms */
160*29318db1SChalapathi V     pnv_xscom_region_init(&s->xscom_spic_regs, OBJECT(s), &pnv_spi_xscom_ops,
161*29318db1SChalapathi V                           s, "xscom-spi", PNV10_XSCOM_PIB_SPIC_SIZE);
162*29318db1SChalapathi V }
163*29318db1SChalapathi V 
164*29318db1SChalapathi V static int pnv_spi_dt_xscom(PnvXScomInterface *dev, void *fdt,
165*29318db1SChalapathi V                              int offset)
166*29318db1SChalapathi V {
167*29318db1SChalapathi V     PnvSpi *s = PNV_SPI(dev);
168*29318db1SChalapathi V     g_autofree char *name;
169*29318db1SChalapathi V     int s_offset;
170*29318db1SChalapathi V     const char compat[] = "ibm,power10-spi";
171*29318db1SChalapathi V     uint32_t spic_pcba = PNV10_XSCOM_PIB_SPIC_BASE +
172*29318db1SChalapathi V         s->spic_num * PNV10_XSCOM_PIB_SPIC_SIZE;
173*29318db1SChalapathi V     uint32_t reg[] = {
174*29318db1SChalapathi V         cpu_to_be32(spic_pcba),
175*29318db1SChalapathi V         cpu_to_be32(PNV10_XSCOM_PIB_SPIC_SIZE)
176*29318db1SChalapathi V     };
177*29318db1SChalapathi V     name = g_strdup_printf("pnv_spi@%x", spic_pcba);
178*29318db1SChalapathi V     s_offset = fdt_add_subnode(fdt, offset, name);
179*29318db1SChalapathi V     _FDT(s_offset);
180*29318db1SChalapathi V 
181*29318db1SChalapathi V     _FDT(fdt_setprop(fdt, s_offset, "reg", reg, sizeof(reg)));
182*29318db1SChalapathi V     _FDT(fdt_setprop(fdt, s_offset, "compatible", compat, sizeof(compat)));
183*29318db1SChalapathi V     _FDT((fdt_setprop_cell(fdt, s_offset, "spic_num#", s->spic_num)));
184*29318db1SChalapathi V     return 0;
185*29318db1SChalapathi V }
186*29318db1SChalapathi V 
187*29318db1SChalapathi V static void pnv_spi_class_init(ObjectClass *klass, void *data)
188*29318db1SChalapathi V {
189*29318db1SChalapathi V     DeviceClass *dc = DEVICE_CLASS(klass);
190*29318db1SChalapathi V     PnvXScomInterfaceClass *xscomc = PNV_XSCOM_INTERFACE_CLASS(klass);
191*29318db1SChalapathi V 
192*29318db1SChalapathi V     xscomc->dt_xscom = pnv_spi_dt_xscom;
193*29318db1SChalapathi V 
194*29318db1SChalapathi V     dc->desc = "PowerNV SPI";
195*29318db1SChalapathi V     dc->realize = pnv_spi_realize;
196*29318db1SChalapathi V     device_class_set_props(dc, pnv_spi_properties);
197*29318db1SChalapathi V }
198*29318db1SChalapathi V 
199*29318db1SChalapathi V static const TypeInfo pnv_spi_info = {
200*29318db1SChalapathi V     .name          = TYPE_PNV_SPI,
201*29318db1SChalapathi V     .parent        = TYPE_SYS_BUS_DEVICE,
202*29318db1SChalapathi V     .instance_size = sizeof(PnvSpi),
203*29318db1SChalapathi V     .class_init    = pnv_spi_class_init,
204*29318db1SChalapathi V     .interfaces    = (InterfaceInfo[]) {
205*29318db1SChalapathi V         { TYPE_PNV_XSCOM_INTERFACE },
206*29318db1SChalapathi V         { }
207*29318db1SChalapathi V     }
208*29318db1SChalapathi V };
209*29318db1SChalapathi V 
210*29318db1SChalapathi V static void pnv_spi_register_types(void)
211*29318db1SChalapathi V {
212*29318db1SChalapathi V     type_register_static(&pnv_spi_info);
213*29318db1SChalapathi V }
214*29318db1SChalapathi V 
215*29318db1SChalapathi V type_init(pnv_spi_register_types);
216