[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v5 2/6] hw/ssi: Add SPI model
From: |
Miles Glenn |
Subject: |
Re: [PATCH v5 2/6] hw/ssi: Add SPI model |
Date: |
Thu, 27 Jun 2024 10:04:12 -0500 |
Thanks!
Reviewed-by: Glenn Miles <milesg@linux.ibm.com>
Glenn
On Wed, 2024-06-26 at 04:05 -0500, Chalapathi V wrote:
> SPI controller device model supports a connection to a single SPI
> responder.
> This provide access to SPI seeproms, TPM, flash device and an ADC
> controller.
>
> All SPI function control is mapped into the SPI register space to
> enable full
> control by firmware. In this commit SPI configuration component is
> modelled
> which contains all SPI configuration and status registers as well as
> the hold
> registers for data to be sent or having been received.
>
> An existing QEMU SSI framework is used and SSI_BUS is created.
>
> Signed-off-by: Chalapathi V <chalapathi.v@linux.ibm.com>
> ---
> include/hw/ppc/pnv_xscom.h | 3 +
> include/hw/ssi/pnv_spi.h | 40 +++++++
> include/hw/ssi/pnv_spi_regs.h | 67 +++++++++++
> hw/ssi/pnv_spi.c | 214
> ++++++++++++++++++++++++++++++++++
> hw/ppc/Kconfig | 3 +
> hw/ssi/Kconfig | 4 +
> hw/ssi/meson.build | 1 +
> hw/ssi/trace-events | 6 +
> 8 files changed, 338 insertions(+)
> create mode 100644 include/hw/ssi/pnv_spi.h
> create mode 100644 include/hw/ssi/pnv_spi_regs.h
> create mode 100644 hw/ssi/pnv_spi.c
>
> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
> index c8dd42bffd..0020dd172f 100644
> --- a/include/hw/ppc/pnv_xscom.h
> +++ b/include/hw/ppc/pnv_xscom.h
> @@ -194,6 +194,9 @@ struct PnvXScomInterfaceClass {
> #define PNV10_XSCOM_PEC_PCI_BASE 0x8010800 /* index goes upwards
> ... */
> #define PNV10_XSCOM_PEC_PCI_SIZE 0x200
>
> +#define PNV10_XSCOM_PIB_SPIC_BASE 0xc0000
> +#define PNV10_XSCOM_PIB_SPIC_SIZE 0x20
> +
> void pnv_xscom_init(PnvChip *chip, uint64_t size, hwaddr addr);
> int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset,
> uint64_t xscom_base, uint64_t xscom_size,
> diff --git a/include/hw/ssi/pnv_spi.h b/include/hw/ssi/pnv_spi.h
> new file mode 100644
> index 0000000000..833042b74b
> --- /dev/null
> +++ b/include/hw/ssi/pnv_spi.h
> @@ -0,0 +1,40 @@
> +/*
> + * QEMU PowerPC SPI model
> + *
> + * Copyright (c) 2024, IBM Corporation.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * This model Supports a connection to a single SPI responder.
> + * Introduced for P10 to provide access to SPI seeproms, TPM, flash
> device
> + * and an ADC controller.
> + */
> +
> +#ifndef PPC_PNV_SPI_H
> +#define PPC_PNV_SPI_H
> +
> +#include "hw/ssi/ssi.h"
> +#include "hw/sysbus.h"
> +
> +#define TYPE_PNV_SPI "pnv-spi"
> +OBJECT_DECLARE_SIMPLE_TYPE(PnvSpi, PNV_SPI)
> +
> +#define PNV_SPI_REG_SIZE 8
> +#define PNV_SPI_REGS 7
> +
> +#define TYPE_PNV_SPI_BUS "pnv-spi-bus"
> +typedef struct PnvSpi {
> + SysBusDevice parent_obj;
> +
> + SSIBus *ssi_bus;
> + qemu_irq *cs_line;
> + MemoryRegion xscom_spic_regs;
> + /* SPI object number */
> + uint32_t spic_num;
> +
> + /* SPI registers */
> + uint64_t regs[PNV_SPI_REGS];
> + uint8_t seq_op[PNV_SPI_REG_SIZE];
> + uint64_t status;
> +} PnvSpi;
> +#endif /* PPC_PNV_SPI_H */
> diff --git a/include/hw/ssi/pnv_spi_regs.h
> b/include/hw/ssi/pnv_spi_regs.h
> new file mode 100644
> index 0000000000..5b6ff72d02
> --- /dev/null
> +++ b/include/hw/ssi/pnv_spi_regs.h
> @@ -0,0 +1,67 @@
> +/*
> + * QEMU PowerPC SPI model
> + *
> + * Copyright (c) 2024, IBM Corporation.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef PNV_SPI_CONTROLLER_REGS_H
> +#define PNV_SPI_CONTROLLER_REGS_H
> +
> +/*
> + * Macros from target/ppc/cpu.h
> + * These macros are copied from ppc target specific file
> target/ppc/cpu.h
> + * as target/ppc/cpu.h cannot be included here.
> + */
> +#define PPC_BIT(bit) (0x8000000000000000ULL >> (bit))
> +#define PPC_BIT8(bit) (0x80 >> (bit))
> +#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) |
> PPC_BIT(bs))
> +#define PPC_BITMASK8(bs, be) ((PPC_BIT8(bs) - PPC_BIT8(be)) |
> PPC_BIT8(bs))
> +#define MASK_TO_LSH(m) (__builtin_ffsll(m) - 1)
> +#define GETFIELD(m, v) (((v) & (m)) >> MASK_TO_LSH(m))
> +#define SETFIELD(m, v, val) \
> + (((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) &
> (m)))
> +
> +/* Error Register */
> +#define ERROR_REG 0x00
> +
> +/* counter_config_reg */
> +#define SPI_CTR_CFG_REG 0x01
> +
> +/* config_reg */
> +#define CONFIG_REG1 0x02
> +
> +/* clock_config_reset_control_ecc_enable_reg */
> +#define SPI_CLK_CFG_REG 0x03
> +#define SPI_CLK_CFG_HARD_RST 0x0084000000000000;
> +#define SPI_CLK_CFG_RST_CTRL PPC_BITMASK(24, 27)
> +
> +/* memory_mapping_reg */
> +#define SPI_MM_REG 0x04
> +
> +/* transmit_data_reg */
> +#define SPI_XMIT_DATA_REG 0x05
> +
> +/* receive_data_reg */
> +#define SPI_RCV_DATA_REG 0x06
> +
> +/* sequencer_operation_reg */
> +#define SPI_SEQ_OP_REG 0x07
> +
> +/* status_reg */
> +#define SPI_STS_REG 0x08
> +#define SPI_STS_RDR_FULL PPC_BIT(0)
> +#define SPI_STS_RDR_OVERRUN PPC_BIT(1)
> +#define SPI_STS_RDR_UNDERRUN PPC_BIT(2)
> +#define SPI_STS_TDR_FULL PPC_BIT(4)
> +#define SPI_STS_TDR_OVERRUN PPC_BIT(5)
> +#define SPI_STS_TDR_UNDERRUN PPC_BIT(6)
> +#define SPI_STS_SEQ_FSM PPC_BITMASK(8, 15)
> +#define SPI_STS_SHIFTER_FSM PPC_BITMASK(16, 27)
> +#define SPI_STS_SEQ_INDEX PPC_BITMASK(28, 31)
> +#define SPI_STS_GEN_STATUS PPC_BITMASK(32, 63)
> +#define SPI_STS_RDR PPC_BITMASK(1, 3)
> +#define SPI_STS_TDR PPC_BITMASK(5, 7)
> +
> +#endif
> diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c
> new file mode 100644
> index 0000000000..de3ffc4e56
> --- /dev/null
> +++ b/hw/ssi/pnv_spi.c
> @@ -0,0 +1,214 @@
> +/*
> + * QEMU PowerPC SPI model
> + *
> + * Copyright (c) 2024, IBM Corporation.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/ppc/pnv_xscom.h"
> +#include "hw/ssi/pnv_spi.h"
> +#include "hw/ssi/pnv_spi_regs.h"
> +#include "hw/ssi/ssi.h"
> +#include <libfdt.h>
> +#include "hw/irq.h"
> +#include "trace.h"
> +
> +/*
> + * Macro from include/hw/ppc/fdt.h
> + * fdt.h cannot be included here as it contain ppc target specific
> dependency.
> + */
> +#define _FDT(exp) \
> + do { \
> + int _ret = (exp); \
> + if (_ret < 0) { \
> + error_report("error creating device tree: %s: %s", \
> + #exp, fdt_strerror(_ret)); \
> + exit(1); \
> + } \
> + } while (0)
> +
> +static uint64_t pnv_spi_xscom_read(void *opaque, hwaddr addr,
> unsigned size)
> +{
> + PnvSpi *s = PNV_SPI(opaque);
> + uint32_t reg = addr >> 3;
> + uint64_t val = ~0ull;
> +
> + switch (reg) {
> + case ERROR_REG:
> + case SPI_CTR_CFG_REG:
> + case CONFIG_REG1:
> + case SPI_CLK_CFG_REG:
> + case SPI_MM_REG:
> + case SPI_XMIT_DATA_REG:
> + val = s->regs[reg];
> + break;
> + case SPI_RCV_DATA_REG:
> + val = s->regs[reg];
> + trace_pnv_spi_read_RDR(val);
> + s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 0);
> + break;
> + case SPI_SEQ_OP_REG:
> + val = 0;
> + for (int i = 0; i < PNV_SPI_REG_SIZE; i++) {
> + val = (val << 8) | s->seq_op[i];
> + }
> + break;
> + case SPI_STS_REG:
> + val = s->status;
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi_regs: Invalid xscom
> "
> + "read at 0x%" PRIx32 "\n", reg);
> + }
> +
> + trace_pnv_spi_read(addr, val);
> + return val;
> +}
> +
> +static void pnv_spi_xscom_write(void *opaque, hwaddr addr,
> + uint64_t val, unsigned size)
> +{
> + PnvSpi *s = PNV_SPI(opaque);
> + uint32_t reg = addr >> 3;
> +
> + trace_pnv_spi_write(addr, val);
> +
> + switch (reg) {
> + case ERROR_REG:
> + case SPI_CTR_CFG_REG:
> + case CONFIG_REG1:
> + case SPI_MM_REG:
> + case SPI_RCV_DATA_REG:
> + s->regs[reg] = val;
> + break;
> + case SPI_CLK_CFG_REG:
> + /*
> + * To reset the SPI controller write the sequence 0x5 0xA to
> + * reset_control field
> + */
> + if ((GETFIELD(SPI_CLK_CFG_RST_CTRL, s-
> >regs[SPI_CLK_CFG_REG]) == 0x5)
> + && (GETFIELD(SPI_CLK_CFG_RST_CTRL, val) == 0xA)) {
> + /* SPI controller reset sequence completed,
> resetting */
> + s->regs[reg] = SPI_CLK_CFG_HARD_RST;
> + } else {
> + s->regs[reg] = val;
> + }
> + break;
> + case SPI_XMIT_DATA_REG:
> + /*
> + * Writing to the transmit data register causes the transmit
> data
> + * register full status bit in the status register to be
> set. Writing
> + * when the transmit data register full status bit is
> already set
> + * causes a "Resource Not Available" condition. This is not
> possible
> + * in the model since writes to this register are not
> asynchronous to
> + * the operation sequence like it would be in hardware.
> + */
> + s->regs[reg] = val;
> + trace_pnv_spi_write_TDR(val);
> + s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 1);
> + s->status = SETFIELD(SPI_STS_TDR_UNDERRUN, s->status, 0);
> + break;
> + case SPI_SEQ_OP_REG:
> + for (int i = 0; i < PNV_SPI_REG_SIZE; i++) {
> + s->seq_op[i] = (val >> (56 - i * 8)) & 0xFF;
> + }
> + break;
> + case SPI_STS_REG:
> + /* other fields are ignore_write */
> + s->status = SETFIELD(SPI_STS_RDR_OVERRUN, s->status,
> + GETFIELD(SPI_STS_RDR, val));
> + s->status = SETFIELD(SPI_STS_TDR_OVERRUN, s->status,
> + GETFIELD(SPI_STS_TDR, val));
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi_regs: Invalid xscom
> "
> + "write at 0x%" PRIx32 "\n", reg);
> + }
> + return;
> +}
> +
> +static const MemoryRegionOps pnv_spi_xscom_ops = {
> + .read = pnv_spi_xscom_read,
> + .write = pnv_spi_xscom_write,
> + .valid.min_access_size = 8,
> + .valid.max_access_size = 8,
> + .impl.min_access_size = 8,
> + .impl.max_access_size = 8,
> + .endianness = DEVICE_BIG_ENDIAN,
> +};
> +
> +static Property pnv_spi_properties[] = {
> + DEFINE_PROP_UINT32("spic_num", PnvSpi, spic_num, 0),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void pnv_spi_realize(DeviceState *dev, Error **errp)
> +{
> + PnvSpi *s = PNV_SPI(dev);
> + g_autofree char *name = g_strdup_printf(TYPE_PNV_SPI_BUS ".%d",
> + s->spic_num);
> + s->ssi_bus = ssi_create_bus(dev, name);
> + s->cs_line = g_new0(qemu_irq, 1);
> + qdev_init_gpio_out_named(DEVICE(s), s->cs_line, "cs", 1);
> +
> + /* spi scoms */
> + pnv_xscom_region_init(&s->xscom_spic_regs, OBJECT(s),
> &pnv_spi_xscom_ops,
> + s, "xscom-spi",
> PNV10_XSCOM_PIB_SPIC_SIZE);
> +}
> +
> +static int pnv_spi_dt_xscom(PnvXScomInterface *dev, void *fdt,
> + int offset)
> +{
> + PnvSpi *s = PNV_SPI(dev);
> + g_autofree char *name;
> + int s_offset;
> + const char compat[] = "ibm,power10-spi";
> + uint32_t spic_pcba = PNV10_XSCOM_PIB_SPIC_BASE +
> + s->spic_num * PNV10_XSCOM_PIB_SPIC_SIZE;
> + uint32_t reg[] = {
> + cpu_to_be32(spic_pcba),
> + cpu_to_be32(PNV10_XSCOM_PIB_SPIC_SIZE)
> + };
> + name = g_strdup_printf("pnv_spi@%x", spic_pcba);
> + s_offset = fdt_add_subnode(fdt, offset, name);
> + _FDT(s_offset);
> +
> + _FDT(fdt_setprop(fdt, s_offset, "reg", reg, sizeof(reg)));
> + _FDT(fdt_setprop(fdt, s_offset, "compatible", compat,
> sizeof(compat)));
> + _FDT((fdt_setprop_cell(fdt, s_offset, "spic_num#", s-
> >spic_num)));
> + return 0;
> +}
> +
> +static void pnv_spi_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + PnvXScomInterfaceClass *xscomc =
> PNV_XSCOM_INTERFACE_CLASS(klass);
> +
> + xscomc->dt_xscom = pnv_spi_dt_xscom;
> +
> + dc->desc = "PowerNV SPI";
> + dc->realize = pnv_spi_realize;
> + device_class_set_props(dc, pnv_spi_properties);
> +}
> +
> +static const TypeInfo pnv_spi_info = {
> + .name = TYPE_PNV_SPI,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(PnvSpi),
> + .class_init = pnv_spi_class_init,
> + .interfaces = (InterfaceInfo[]) {
> + { TYPE_PNV_XSCOM_INTERFACE },
> + { }
> + }
> +};
> +
> +static void pnv_spi_register_types(void)
> +{
> + type_register_static(&pnv_spi_info);
> +}
> +
> +type_init(pnv_spi_register_types);
> diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
> index 78f83e78ce..4668d59eab 100644
> --- a/hw/ppc/Kconfig
> +++ b/hw/ppc/Kconfig
> @@ -39,6 +39,9 @@ config POWERNV
> select PCI_POWERNV
> select PCA9552
> select PCA9554
> + select SSI
> + select SSI_M25P80
> + select PNV_SPI
>
> config PPC405
> bool
> diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig
> index 83ee53c1d0..8d180de7cf 100644
> --- a/hw/ssi/Kconfig
> +++ b/hw/ssi/Kconfig
> @@ -24,3 +24,7 @@ config STM32F2XX_SPI
> config BCM2835_SPI
> bool
> select SSI
> +
> +config PNV_SPI
> + bool
> + select SSI
> diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build
> index b999aeb027..b7ad7fca3b 100644
> --- a/hw/ssi/meson.build
> +++ b/hw/ssi/meson.build
> @@ -12,3 +12,4 @@ system_ss.add(when: 'CONFIG_IMX', if_true:
> files('imx_spi.c'))
> system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_spi.c'))
> system_ss.add(when: 'CONFIG_IBEX', if_true:
> files('ibex_spi_host.c'))
> system_ss.add(when: 'CONFIG_BCM2835_SPI', if_true:
> files('bcm2835_spi.c'))
> +system_ss.add(when: 'CONFIG_PNV_SPI', if_true: files('pnv_spi.c'))
> diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events
> index 2d5bd2b83d..4388024a05 100644
> --- a/hw/ssi/trace-events
> +++ b/hw/ssi/trace-events
> @@ -32,3 +32,9 @@ ibex_spi_host_reset(const char *msg) "%s"
> ibex_spi_host_transfer(uint32_t tx_data, uint32_t rx_data) "tx_data:
> 0x%" PRIx32 " rx_data: @0x%" PRIx32
> ibex_spi_host_write(uint64_t addr, uint32_t size, uint64_t data)
> "@0x%" PRIx64 " size %u: 0x%" PRIx64
> ibex_spi_host_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 "
> size %u:"
> +
> +#pnv_spi.c
> +pnv_spi_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val
> 0x%" PRIx64
> +pnv_spi_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val
> 0x%" PRIx64
> +pnv_spi_read_RDR(uint64_t val) "data extracted = 0x%" PRIx64
> +pnv_spi_write_TDR(uint64_t val) "being written, data written = 0x%"
> PRIx64
- [PATCH v5 0/6] hw/ppc: SPI model, Chalapathi V, 2024/06/26
- [PATCH v5 1/6] ppc/pnv: Remove ppc target dependency from pnv_xscom.h, Chalapathi V, 2024/06/26
- [PATCH v5 3/6] hw/ssi: Extend SPI model, Chalapathi V, 2024/06/26
- [PATCH v5 2/6] hw/ssi: Add SPI model, Chalapathi V, 2024/06/26
- [PATCH v5 5/6] hw/ppc: SPI controller wiring to P10 chip, Chalapathi V, 2024/06/26
- [PATCH v5 4/6] hw/block: Add Microchip's 25CSM04 to m25p80, Chalapathi V, 2024/06/26
- [PATCH v5 6/6] tests/qtest: Add pnv-spi-seeprom qtest, Chalapathi V, 2024/06/26