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