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