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