Message ID | 20210126060007.12904-4-bmeng.cn@gmail.com |
---|---|
State | New |
Headers | show |
Series | hw/riscv: sifive_u: Add missing SPI support | expand |
On 1/26/21 7:00 AM, Bin Meng wrote: > From: Bin Meng <bin.meng@windriver.com> > > This adds the SiFive SPI controller model for the FU540 SoC. > The direct memory-mapped SPI flash mode is unsupported. > > Signed-off-by: Bin Meng <bin.meng@windriver.com> > > --- > > Changes in v3: > - Simplify flush txfifo logic > > Changes in v2: > - Log guest error when trying to write reserved registers > - Log guest error when trying to access out-of-bounds registers > - log guest error when writing to reserved bits for chip select > registers and watermark registers > - Log unimplemented warning when trying to write direct-map flash > interface registers > - Add test tx fifo full logic in sifive_spi_read(), hence remove > setting the tx fifo full flag in sifive_spi_write(). > - Populate register with their default value > > include/hw/ssi/sifive_spi.h | 47 +++++ > hw/ssi/sifive_spi.c | 358 ++++++++++++++++++++++++++++++++++++ > hw/ssi/Kconfig | 4 + > hw/ssi/meson.build | 1 + > 4 files changed, 410 insertions(+) > create mode 100644 include/hw/ssi/sifive_spi.h > create mode 100644 hw/ssi/sifive_spi.c Missing MAINTAINERS entry (if there are no other comments on this series, maybe the maintainer can directly add one).
On Mon, Jan 25, 2021 at 10:03 PM Bin Meng <bmeng.cn@gmail.com> wrote: > > From: Bin Meng <bin.meng@windriver.com> > > This adds the SiFive SPI controller model for the FU540 SoC. > The direct memory-mapped SPI flash mode is unsupported. > > Signed-off-by: Bin Meng <bin.meng@windriver.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Alistair > > --- > > Changes in v3: > - Simplify flush txfifo logic > > Changes in v2: > - Log guest error when trying to write reserved registers > - Log guest error when trying to access out-of-bounds registers > - log guest error when writing to reserved bits for chip select > registers and watermark registers > - Log unimplemented warning when trying to write direct-map flash > interface registers > - Add test tx fifo full logic in sifive_spi_read(), hence remove > setting the tx fifo full flag in sifive_spi_write(). > - Populate register with their default value > > include/hw/ssi/sifive_spi.h | 47 +++++ > hw/ssi/sifive_spi.c | 358 ++++++++++++++++++++++++++++++++++++ > hw/ssi/Kconfig | 4 + > hw/ssi/meson.build | 1 + > 4 files changed, 410 insertions(+) > create mode 100644 include/hw/ssi/sifive_spi.h > create mode 100644 hw/ssi/sifive_spi.c > > diff --git a/include/hw/ssi/sifive_spi.h b/include/hw/ssi/sifive_spi.h > new file mode 100644 > index 0000000000..47d0d6a47c > --- /dev/null > +++ b/include/hw/ssi/sifive_spi.h > @@ -0,0 +1,47 @@ > +/* > + * QEMU model of the SiFive SPI Controller > + * > + * Copyright (c) 2021 Wind River Systems, Inc. > + * > + * Author: > + * Bin Meng <bin.meng@windriver.com> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2 or later, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef HW_SIFIVE_SPI_H > +#define HW_SIFIVE_SPI_H > + > +#define SIFIVE_SPI_REG_NUM (0x78 / 4) > + > +#define TYPE_SIFIVE_SPI "sifive.spi" > +#define SIFIVE_SPI(obj) OBJECT_CHECK(SiFiveSPIState, (obj), TYPE_SIFIVE_SPI) > + > +typedef struct SiFiveSPIState { > + SysBusDevice parent_obj; > + > + MemoryRegion mmio; > + qemu_irq irq; > + > + uint32_t num_cs; > + qemu_irq *cs_lines; > + > + SSIBus *spi; > + > + Fifo8 tx_fifo; > + Fifo8 rx_fifo; > + > + uint32_t regs[SIFIVE_SPI_REG_NUM]; > +} SiFiveSPIState; > + > +#endif /* HW_SIFIVE_SPI_H */ > diff --git a/hw/ssi/sifive_spi.c b/hw/ssi/sifive_spi.c > new file mode 100644 > index 0000000000..0c9ebca3c8 > --- /dev/null > +++ b/hw/ssi/sifive_spi.c > @@ -0,0 +1,358 @@ > +/* > + * QEMU model of the SiFive SPI Controller > + * > + * Copyright (c) 2021 Wind River Systems, Inc. > + * > + * Author: > + * Bin Meng <bin.meng@windriver.com> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2 or later, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#include "qemu/osdep.h" > +#include "hw/irq.h" > +#include "hw/qdev-properties.h" > +#include "hw/sysbus.h" > +#include "hw/ssi/ssi.h" > +#include "sysemu/sysemu.h" > +#include "qemu/fifo8.h" > +#include "qemu/log.h" > +#include "qemu/module.h" > +#include "hw/ssi/sifive_spi.h" > + > +#define R_SCKDIV (0x00 / 4) > +#define R_SCKMODE (0x04 / 4) > +#define R_CSID (0x10 / 4) > +#define R_CSDEF (0x14 / 4) > +#define R_CSMODE (0x18 / 4) > +#define R_DELAY0 (0x28 / 4) > +#define R_DELAY1 (0x2C / 4) > +#define R_FMT (0x40 / 4) > +#define R_TXDATA (0x48 / 4) > +#define R_RXDATA (0x4C / 4) > +#define R_TXMARK (0x50 / 4) > +#define R_RXMARK (0x54 / 4) > +#define R_FCTRL (0x60 / 4) > +#define R_FFMT (0x64 / 4) > +#define R_IE (0x70 / 4) > +#define R_IP (0x74 / 4) > + > +#define FMT_DIR (1 << 3) > + > +#define TXDATA_FULL (1 << 31) > +#define RXDATA_EMPTY (1 << 31) > + > +#define IE_TXWM (1 << 0) > +#define IE_RXWM (1 << 1) > + > +#define IP_TXWM (1 << 0) > +#define IP_RXWM (1 << 1) > + > +#define FIFO_CAPACITY 8 > + > +static void sifive_spi_txfifo_reset(SiFiveSPIState *s) > +{ > + fifo8_reset(&s->tx_fifo); > + > + s->regs[R_TXDATA] &= ~TXDATA_FULL; > + s->regs[R_IP] &= ~IP_TXWM; > +} > + > +static void sifive_spi_rxfifo_reset(SiFiveSPIState *s) > +{ > + fifo8_reset(&s->rx_fifo); > + > + s->regs[R_RXDATA] |= RXDATA_EMPTY; > + s->regs[R_IP] &= ~IP_RXWM; > +} > + > +static void sifive_spi_update_cs(SiFiveSPIState *s) > +{ > + int i; > + > + for (i = 0; i < s->num_cs; i++) { > + if (s->regs[R_CSDEF] & (1 << i)) { > + qemu_set_irq(s->cs_lines[i], !(s->regs[R_CSMODE])); > + } > + } > +} > + > +static void sifive_spi_update_irq(SiFiveSPIState *s) > +{ > + int level; > + > + if (fifo8_num_used(&s->tx_fifo) < s->regs[R_TXMARK]) { > + s->regs[R_IP] |= IP_TXWM; > + } else { > + s->regs[R_IP] &= ~IP_TXWM; > + } > + > + if (fifo8_num_used(&s->rx_fifo) > s->regs[R_RXMARK]) { > + s->regs[R_IP] |= IP_RXWM; > + } else { > + s->regs[R_IP] &= ~IP_RXWM; > + } > + > + level = s->regs[R_IP] & s->regs[R_IE] ? 1 : 0; > + qemu_set_irq(s->irq, level); > +} > + > +static void sifive_spi_reset(DeviceState *d) > +{ > + SiFiveSPIState *s = SIFIVE_SPI(d); > + > + memset(s->regs, 0, sizeof(s->regs)); > + > + /* The reset value is high for all implemented CS pins */ > + s->regs[R_CSDEF] = (1 << s->num_cs) - 1; > + > + /* Populate register with their default value */ > + s->regs[R_SCKDIV] = 0x03; > + s->regs[R_DELAY0] = 0x1001; > + s->regs[R_DELAY1] = 0x01; > + > + sifive_spi_txfifo_reset(s); > + sifive_spi_rxfifo_reset(s); > + > + sifive_spi_update_cs(s); > + sifive_spi_update_irq(s); > +} > + > +static void sifive_spi_flush_txfifo(SiFiveSPIState *s) > +{ > + uint8_t tx; > + uint8_t rx; > + > + while (!fifo8_is_empty(&s->tx_fifo)) { > + tx = fifo8_pop(&s->tx_fifo); > + rx = ssi_transfer(s->spi, tx); > + > + if (!fifo8_is_full(&s->rx_fifo)) { > + if (!(s->regs[R_FMT] & FMT_DIR)) { > + fifo8_push(&s->rx_fifo, rx); > + } > + } > + } > +} > + > +static bool sifive_spi_is_bad_reg(hwaddr addr, bool allow_reserved) > +{ > + bool bad; > + > + switch (addr) { > + /* reserved offsets */ > + case 0x08: > + case 0x0C: > + case 0x1C: > + case 0x20: > + case 0x24: > + case 0x30: > + case 0x34: > + case 0x38: > + case 0x3C: > + case 0x44: > + case 0x58: > + case 0x5C: > + case 0x68: > + case 0x6C: > + bad = allow_reserved ? false : true; > + break; > + default: > + bad = false; > + } > + > + if (addr >= (SIFIVE_SPI_REG_NUM << 2)) { > + bad = true; > + } > + > + return bad; > +} > + > +static uint64_t sifive_spi_read(void *opaque, hwaddr addr, unsigned int size) > +{ > + SiFiveSPIState *s = opaque; > + uint32_t r; > + > + if (sifive_spi_is_bad_reg(addr, true)) { > + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read at address 0x%" > + HWADDR_PRIx "\n", __func__, addr); > + return 0; > + } > + > + addr >>= 2; > + switch (addr) { > + case R_TXDATA: > + if (fifo8_is_full(&s->tx_fifo)) { > + return TXDATA_FULL; > + } > + r = 0; > + break; > + > + case R_RXDATA: > + if (fifo8_is_empty(&s->rx_fifo)) { > + return RXDATA_EMPTY; > + } > + r = fifo8_pop(&s->rx_fifo); > + break; > + > + default: > + r = s->regs[addr]; > + break; > + } > + > + sifive_spi_update_irq(s); > + > + return r; > +} > + > +static void sifive_spi_write(void *opaque, hwaddr addr, > + uint64_t val64, unsigned int size) > +{ > + SiFiveSPIState *s = opaque; > + uint32_t value = val64; > + > + if (sifive_spi_is_bad_reg(addr, false)) { > + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write at addr=0x%" > + HWADDR_PRIx " value=0x%x\n", __func__, addr, value); > + return; > + } > + > + addr >>= 2; > + switch (addr) { > + case R_CSID: > + if (value >= s->num_cs) { > + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csid %d\n", > + __func__, value); > + } else { > + s->regs[R_CSID] = value; > + sifive_spi_update_cs(s); > + } > + break; > + > + case R_CSDEF: > + if (value >= (1 << s->num_cs)) { > + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csdef %x\n", > + __func__, value); > + } else { > + s->regs[R_CSDEF] = value; > + } > + break; > + > + case R_CSMODE: > + if (value > 3) { > + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csmode %x\n", > + __func__, value); > + } else { > + s->regs[R_CSMODE] = value; > + sifive_spi_update_cs(s); > + } > + break; > + > + case R_TXDATA: > + if (!fifo8_is_full(&s->tx_fifo)) { > + fifo8_push(&s->tx_fifo, (uint8_t)value); > + sifive_spi_flush_txfifo(s); > + } > + break; > + > + case R_RXDATA: > + case R_IP: > + qemu_log_mask(LOG_GUEST_ERROR, > + "%s: invalid write to read-only reigster 0x%" > + HWADDR_PRIx " with 0x%x\n", __func__, addr << 2, value); > + break; > + > + case R_TXMARK: > + case R_RXMARK: > + if (value >= FIFO_CAPACITY) { > + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid watermark %d\n", > + __func__, value); > + } else { > + s->regs[addr] = value; > + } > + break; > + > + case R_FCTRL: > + case R_FFMT: > + qemu_log_mask(LOG_UNIMP, > + "%s: direct-map flash interface unimplemented\n", > + __func__); > + break; > + > + default: > + s->regs[addr] = value; > + break; > + } > + > + sifive_spi_update_irq(s); > +} > + > +static const MemoryRegionOps sifive_spi_ops = { > + .read = sifive_spi_read, > + .write = sifive_spi_write, > + .endianness = DEVICE_LITTLE_ENDIAN, > + .valid = { > + .min_access_size = 4, > + .max_access_size = 4 > + } > +}; > + > +static void sifive_spi_realize(DeviceState *dev, Error **errp) > +{ > + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); > + SiFiveSPIState *s = SIFIVE_SPI(dev); > + int i; > + > + s->spi = ssi_create_bus(dev, "spi"); > + sysbus_init_irq(sbd, &s->irq); > + > + s->cs_lines = g_new0(qemu_irq, s->num_cs); > + for (i = 0; i < s->num_cs; i++) { > + sysbus_init_irq(sbd, &s->cs_lines[i]); > + } > + > + memory_region_init_io(&s->mmio, OBJECT(s), &sifive_spi_ops, s, > + TYPE_SIFIVE_SPI, 0x1000); > + sysbus_init_mmio(sbd, &s->mmio); > + > + fifo8_create(&s->tx_fifo, FIFO_CAPACITY); > + fifo8_create(&s->rx_fifo, FIFO_CAPACITY); > +} > + > +static Property sifive_spi_properties[] = { > + DEFINE_PROP_UINT32("num-cs", SiFiveSPIState, num_cs, 1), > + DEFINE_PROP_END_OF_LIST(), > +}; > + > +static void sifive_spi_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + device_class_set_props(dc, sifive_spi_properties); > + dc->reset = sifive_spi_reset; > + dc->realize = sifive_spi_realize; > +} > + > +static const TypeInfo sifive_spi_info = { > + .name = TYPE_SIFIVE_SPI, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(SiFiveSPIState), > + .class_init = sifive_spi_class_init, > +}; > + > +static void sifive_spi_register_types(void) > +{ > + type_register_static(&sifive_spi_info); > +} > + > +type_init(sifive_spi_register_types) > diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig > index 9e54a0c8dd..7d90a02181 100644 > --- a/hw/ssi/Kconfig > +++ b/hw/ssi/Kconfig > @@ -2,6 +2,10 @@ config PL022 > bool > select SSI > > +config SIFIVE_SPI > + bool > + select SSI > + > config SSI > bool > > diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build > index dee00c0da6..3d6bc82ab1 100644 > --- a/hw/ssi/meson.build > +++ b/hw/ssi/meson.build > @@ -2,6 +2,7 @@ softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c')) > softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-spi.c')) > softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c')) > softmmu_ss.add(when: 'CONFIG_PL022', if_true: files('pl022.c')) > +softmmu_ss.add(when: 'CONFIG_SIFIVE_SPI', if_true: files('sifive_spi.c')) > softmmu_ss.add(when: 'CONFIG_SSI', if_true: files('ssi.c')) > softmmu_ss.add(when: 'CONFIG_STM32F2XX_SPI', if_true: files('stm32f2xx_spi.c')) > softmmu_ss.add(when: 'CONFIG_XILINX_SPI', if_true: files('xilinx_spi.c')) > -- > 2.25.1 > >
On Mon, Jan 25, 2021 at 11:34 PM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote: > > On 1/26/21 7:00 AM, Bin Meng wrote: > > From: Bin Meng <bin.meng@windriver.com> > > > > This adds the SiFive SPI controller model for the FU540 SoC. > > The direct memory-mapped SPI flash mode is unsupported. > > > > Signed-off-by: Bin Meng <bin.meng@windriver.com> > > > > --- > > > > Changes in v3: > > - Simplify flush txfifo logic > > > > Changes in v2: > > - Log guest error when trying to write reserved registers > > - Log guest error when trying to access out-of-bounds registers > > - log guest error when writing to reserved bits for chip select > > registers and watermark registers > > - Log unimplemented warning when trying to write direct-map flash > > interface registers > > - Add test tx fifo full logic in sifive_spi_read(), hence remove > > setting the tx fifo full flag in sifive_spi_write(). > > - Populate register with their default value > > > > include/hw/ssi/sifive_spi.h | 47 +++++ > > hw/ssi/sifive_spi.c | 358 ++++++++++++++++++++++++++++++++++++ > > hw/ssi/Kconfig | 4 + > > hw/ssi/meson.build | 1 + > > 4 files changed, 410 insertions(+) > > create mode 100644 include/hw/ssi/sifive_spi.h > > create mode 100644 hw/ssi/sifive_spi.c > > Missing MAINTAINERS entry (if there are no other comments on > this series, maybe the maintainer can directly add one). Yep, I'm adding this section to the RISC-V machines: diff --git a/MAINTAINERS b/MAINTAINERS index 8d8b0bf966..c347d49bd2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1359,6 +1359,15 @@ F: include/hw/misc/mchp_pfsoc_dmc.h F: include/hw/misc/mchp_pfsoc_ioscb.h F: include/hw/misc/mchp_pfsoc_sysreg.h +SiFive Machines +M: Alistair Francis <Alistair.Francis@wdc.com> +M: Bin Meng <bin.meng@windriver.com> +M: Palmer Dabbelt <palmer@dabbelt.com> +L: qemu-riscv@nongnu.org +S: Supported +F: hw/*/*sifive*.c +F: include/hw/*/*sifive*.h + RX Machines ----------- rx-gdbsim Can I get an Ack from you Bin that you are ok with that? @Palmer Dabbelt let me know if you would prefer something else. Alistair >
On Tue, Feb 9, 2021 at 9:44 AM Alistair Francis <alistair23@gmail.com> wrote: > > On Mon, Jan 25, 2021 at 11:34 PM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote: > > > > On 1/26/21 7:00 AM, Bin Meng wrote: > > > From: Bin Meng <bin.meng@windriver.com> > > > > > > This adds the SiFive SPI controller model for the FU540 SoC. > > > The direct memory-mapped SPI flash mode is unsupported. > > > > > > Signed-off-by: Bin Meng <bin.meng@windriver.com> > > > > > > --- > > > > > > Changes in v3: > > > - Simplify flush txfifo logic > > > > > > Changes in v2: > > > - Log guest error when trying to write reserved registers > > > - Log guest error when trying to access out-of-bounds registers > > > - log guest error when writing to reserved bits for chip select > > > registers and watermark registers > > > - Log unimplemented warning when trying to write direct-map flash > > > interface registers > > > - Add test tx fifo full logic in sifive_spi_read(), hence remove > > > setting the tx fifo full flag in sifive_spi_write(). > > > - Populate register with their default value > > > > > > include/hw/ssi/sifive_spi.h | 47 +++++ > > > hw/ssi/sifive_spi.c | 358 ++++++++++++++++++++++++++++++++++++ > > > hw/ssi/Kconfig | 4 + > > > hw/ssi/meson.build | 1 + > > > 4 files changed, 410 insertions(+) > > > create mode 100644 include/hw/ssi/sifive_spi.h > > > create mode 100644 hw/ssi/sifive_spi.c > > > > Missing MAINTAINERS entry (if there are no other comments on > > this series, maybe the maintainer can directly add one). > > Yep, I'm adding this section to the RISC-V machines: > > diff --git a/MAINTAINERS b/MAINTAINERS > index 8d8b0bf966..c347d49bd2 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -1359,6 +1359,15 @@ F: include/hw/misc/mchp_pfsoc_dmc.h > F: include/hw/misc/mchp_pfsoc_ioscb.h > F: include/hw/misc/mchp_pfsoc_sysreg.h > > +SiFive Machines > +M: Alistair Francis <Alistair.Francis@wdc.com> > +M: Bin Meng <bin.meng@windriver.com> > +M: Palmer Dabbelt <palmer@dabbelt.com> > +L: qemu-riscv@nongnu.org > +S: Supported > +F: hw/*/*sifive*.c > +F: include/hw/*/*sifive*.h > + > RX Machines > ----------- > rx-gdbsim > > > Can I get an Ack from you Bin that you are ok with that? > Acked-by: Bin Meng <bin.meng@windriver.com> > @Palmer Dabbelt let me know if you would prefer something else. > Regards, Bin
On Mon, 08 Feb 2021 17:44:17 PST (-0800), alistair23@gmail.com wrote: > On Mon, Jan 25, 2021 at 11:34 PM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote: >> >> On 1/26/21 7:00 AM, Bin Meng wrote: >> > From: Bin Meng <bin.meng@windriver.com> >> > >> > This adds the SiFive SPI controller model for the FU540 SoC. >> > The direct memory-mapped SPI flash mode is unsupported. >> > >> > Signed-off-by: Bin Meng <bin.meng@windriver.com> >> > >> > --- >> > >> > Changes in v3: >> > - Simplify flush txfifo logic >> > >> > Changes in v2: >> > - Log guest error when trying to write reserved registers >> > - Log guest error when trying to access out-of-bounds registers >> > - log guest error when writing to reserved bits for chip select >> > registers and watermark registers >> > - Log unimplemented warning when trying to write direct-map flash >> > interface registers >> > - Add test tx fifo full logic in sifive_spi_read(), hence remove >> > setting the tx fifo full flag in sifive_spi_write(). >> > - Populate register with their default value >> > >> > include/hw/ssi/sifive_spi.h | 47 +++++ >> > hw/ssi/sifive_spi.c | 358 ++++++++++++++++++++++++++++++++++++ >> > hw/ssi/Kconfig | 4 + >> > hw/ssi/meson.build | 1 + >> > 4 files changed, 410 insertions(+) >> > create mode 100644 include/hw/ssi/sifive_spi.h >> > create mode 100644 hw/ssi/sifive_spi.c >> >> Missing MAINTAINERS entry (if there are no other comments on >> this series, maybe the maintainer can directly add one). > > Yep, I'm adding this section to the RISC-V machines: > > diff --git a/MAINTAINERS b/MAINTAINERS > index 8d8b0bf966..c347d49bd2 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -1359,6 +1359,15 @@ F: include/hw/misc/mchp_pfsoc_dmc.h > F: include/hw/misc/mchp_pfsoc_ioscb.h > F: include/hw/misc/mchp_pfsoc_sysreg.h > > +SiFive Machines > +M: Alistair Francis <Alistair.Francis@wdc.com> > +M: Bin Meng <bin.meng@windriver.com> > +M: Palmer Dabbelt <palmer@dabbelt.com> > +L: qemu-riscv@nongnu.org > +S: Supported > +F: hw/*/*sifive*.c > +F: include/hw/*/*sifive*.h > + > RX Machines > ----------- > rx-gdbsim > > > Can I get an Ack from you Bin that you are ok with that? > > @Palmer Dabbelt let me know if you would prefer something else. Sorry, I hadn't seen this go by. Acked-by: Palmer Dabbelt <palmerdabbelt@google.com> Thanks! > > Alistair > >>
diff --git a/include/hw/ssi/sifive_spi.h b/include/hw/ssi/sifive_spi.h new file mode 100644 index 0000000000..47d0d6a47c --- /dev/null +++ b/include/hw/ssi/sifive_spi.h @@ -0,0 +1,47 @@ +/* + * QEMU model of the SiFive SPI Controller + * + * Copyright (c) 2021 Wind River Systems, Inc. + * + * Author: + * Bin Meng <bin.meng@windriver.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_SIFIVE_SPI_H +#define HW_SIFIVE_SPI_H + +#define SIFIVE_SPI_REG_NUM (0x78 / 4) + +#define TYPE_SIFIVE_SPI "sifive.spi" +#define SIFIVE_SPI(obj) OBJECT_CHECK(SiFiveSPIState, (obj), TYPE_SIFIVE_SPI) + +typedef struct SiFiveSPIState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + qemu_irq irq; + + uint32_t num_cs; + qemu_irq *cs_lines; + + SSIBus *spi; + + Fifo8 tx_fifo; + Fifo8 rx_fifo; + + uint32_t regs[SIFIVE_SPI_REG_NUM]; +} SiFiveSPIState; + +#endif /* HW_SIFIVE_SPI_H */ diff --git a/hw/ssi/sifive_spi.c b/hw/ssi/sifive_spi.c new file mode 100644 index 0000000000..0c9ebca3c8 --- /dev/null +++ b/hw/ssi/sifive_spi.c @@ -0,0 +1,358 @@ +/* + * QEMU model of the SiFive SPI Controller + * + * Copyright (c) 2021 Wind River Systems, Inc. + * + * Author: + * Bin Meng <bin.meng@windriver.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "hw/ssi/ssi.h" +#include "sysemu/sysemu.h" +#include "qemu/fifo8.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/ssi/sifive_spi.h" + +#define R_SCKDIV (0x00 / 4) +#define R_SCKMODE (0x04 / 4) +#define R_CSID (0x10 / 4) +#define R_CSDEF (0x14 / 4) +#define R_CSMODE (0x18 / 4) +#define R_DELAY0 (0x28 / 4) +#define R_DELAY1 (0x2C / 4) +#define R_FMT (0x40 / 4) +#define R_TXDATA (0x48 / 4) +#define R_RXDATA (0x4C / 4) +#define R_TXMARK (0x50 / 4) +#define R_RXMARK (0x54 / 4) +#define R_FCTRL (0x60 / 4) +#define R_FFMT (0x64 / 4) +#define R_IE (0x70 / 4) +#define R_IP (0x74 / 4) + +#define FMT_DIR (1 << 3) + +#define TXDATA_FULL (1 << 31) +#define RXDATA_EMPTY (1 << 31) + +#define IE_TXWM (1 << 0) +#define IE_RXWM (1 << 1) + +#define IP_TXWM (1 << 0) +#define IP_RXWM (1 << 1) + +#define FIFO_CAPACITY 8 + +static void sifive_spi_txfifo_reset(SiFiveSPIState *s) +{ + fifo8_reset(&s->tx_fifo); + + s->regs[R_TXDATA] &= ~TXDATA_FULL; + s->regs[R_IP] &= ~IP_TXWM; +} + +static void sifive_spi_rxfifo_reset(SiFiveSPIState *s) +{ + fifo8_reset(&s->rx_fifo); + + s->regs[R_RXDATA] |= RXDATA_EMPTY; + s->regs[R_IP] &= ~IP_RXWM; +} + +static void sifive_spi_update_cs(SiFiveSPIState *s) +{ + int i; + + for (i = 0; i < s->num_cs; i++) { + if (s->regs[R_CSDEF] & (1 << i)) { + qemu_set_irq(s->cs_lines[i], !(s->regs[R_CSMODE])); + } + } +} + +static void sifive_spi_update_irq(SiFiveSPIState *s) +{ + int level; + + if (fifo8_num_used(&s->tx_fifo) < s->regs[R_TXMARK]) { + s->regs[R_IP] |= IP_TXWM; + } else { + s->regs[R_IP] &= ~IP_TXWM; + } + + if (fifo8_num_used(&s->rx_fifo) > s->regs[R_RXMARK]) { + s->regs[R_IP] |= IP_RXWM; + } else { + s->regs[R_IP] &= ~IP_RXWM; + } + + level = s->regs[R_IP] & s->regs[R_IE] ? 1 : 0; + qemu_set_irq(s->irq, level); +} + +static void sifive_spi_reset(DeviceState *d) +{ + SiFiveSPIState *s = SIFIVE_SPI(d); + + memset(s->regs, 0, sizeof(s->regs)); + + /* The reset value is high for all implemented CS pins */ + s->regs[R_CSDEF] = (1 << s->num_cs) - 1; + + /* Populate register with their default value */ + s->regs[R_SCKDIV] = 0x03; + s->regs[R_DELAY0] = 0x1001; + s->regs[R_DELAY1] = 0x01; + + sifive_spi_txfifo_reset(s); + sifive_spi_rxfifo_reset(s); + + sifive_spi_update_cs(s); + sifive_spi_update_irq(s); +} + +static void sifive_spi_flush_txfifo(SiFiveSPIState *s) +{ + uint8_t tx; + uint8_t rx; + + while (!fifo8_is_empty(&s->tx_fifo)) { + tx = fifo8_pop(&s->tx_fifo); + rx = ssi_transfer(s->spi, tx); + + if (!fifo8_is_full(&s->rx_fifo)) { + if (!(s->regs[R_FMT] & FMT_DIR)) { + fifo8_push(&s->rx_fifo, rx); + } + } + } +} + +static bool sifive_spi_is_bad_reg(hwaddr addr, bool allow_reserved) +{ + bool bad; + + switch (addr) { + /* reserved offsets */ + case 0x08: + case 0x0C: + case 0x1C: + case 0x20: + case 0x24: + case 0x30: + case 0x34: + case 0x38: + case 0x3C: + case 0x44: + case 0x58: + case 0x5C: + case 0x68: + case 0x6C: + bad = allow_reserved ? false : true; + break; + default: + bad = false; + } + + if (addr >= (SIFIVE_SPI_REG_NUM << 2)) { + bad = true; + } + + return bad; +} + +static uint64_t sifive_spi_read(void *opaque, hwaddr addr, unsigned int size) +{ + SiFiveSPIState *s = opaque; + uint32_t r; + + if (sifive_spi_is_bad_reg(addr, true)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read at address 0x%" + HWADDR_PRIx "\n", __func__, addr); + return 0; + } + + addr >>= 2; + switch (addr) { + case R_TXDATA: + if (fifo8_is_full(&s->tx_fifo)) { + return TXDATA_FULL; + } + r = 0; + break; + + case R_RXDATA: + if (fifo8_is_empty(&s->rx_fifo)) { + return RXDATA_EMPTY; + } + r = fifo8_pop(&s->rx_fifo); + break; + + default: + r = s->regs[addr]; + break; + } + + sifive_spi_update_irq(s); + + return r; +} + +static void sifive_spi_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + SiFiveSPIState *s = opaque; + uint32_t value = val64; + + if (sifive_spi_is_bad_reg(addr, false)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write at addr=0x%" + HWADDR_PRIx " value=0x%x\n", __func__, addr, value); + return; + } + + addr >>= 2; + switch (addr) { + case R_CSID: + if (value >= s->num_cs) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csid %d\n", + __func__, value); + } else { + s->regs[R_CSID] = value; + sifive_spi_update_cs(s); + } + break; + + case R_CSDEF: + if (value >= (1 << s->num_cs)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csdef %x\n", + __func__, value); + } else { + s->regs[R_CSDEF] = value; + } + break; + + case R_CSMODE: + if (value > 3) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csmode %x\n", + __func__, value); + } else { + s->regs[R_CSMODE] = value; + sifive_spi_update_cs(s); + } + break; + + case R_TXDATA: + if (!fifo8_is_full(&s->tx_fifo)) { + fifo8_push(&s->tx_fifo, (uint8_t)value); + sifive_spi_flush_txfifo(s); + } + break; + + case R_RXDATA: + case R_IP: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid write to read-only reigster 0x%" + HWADDR_PRIx " with 0x%x\n", __func__, addr << 2, value); + break; + + case R_TXMARK: + case R_RXMARK: + if (value >= FIFO_CAPACITY) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid watermark %d\n", + __func__, value); + } else { + s->regs[addr] = value; + } + break; + + case R_FCTRL: + case R_FFMT: + qemu_log_mask(LOG_UNIMP, + "%s: direct-map flash interface unimplemented\n", + __func__); + break; + + default: + s->regs[addr] = value; + break; + } + + sifive_spi_update_irq(s); +} + +static const MemoryRegionOps sifive_spi_ops = { + .read = sifive_spi_read, + .write = sifive_spi_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void sifive_spi_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + SiFiveSPIState *s = SIFIVE_SPI(dev); + int i; + + s->spi = ssi_create_bus(dev, "spi"); + sysbus_init_irq(sbd, &s->irq); + + s->cs_lines = g_new0(qemu_irq, s->num_cs); + for (i = 0; i < s->num_cs; i++) { + sysbus_init_irq(sbd, &s->cs_lines[i]); + } + + memory_region_init_io(&s->mmio, OBJECT(s), &sifive_spi_ops, s, + TYPE_SIFIVE_SPI, 0x1000); + sysbus_init_mmio(sbd, &s->mmio); + + fifo8_create(&s->tx_fifo, FIFO_CAPACITY); + fifo8_create(&s->rx_fifo, FIFO_CAPACITY); +} + +static Property sifive_spi_properties[] = { + DEFINE_PROP_UINT32("num-cs", SiFiveSPIState, num_cs, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sifive_spi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, sifive_spi_properties); + dc->reset = sifive_spi_reset; + dc->realize = sifive_spi_realize; +} + +static const TypeInfo sifive_spi_info = { + .name = TYPE_SIFIVE_SPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFiveSPIState), + .class_init = sifive_spi_class_init, +}; + +static void sifive_spi_register_types(void) +{ + type_register_static(&sifive_spi_info); +} + +type_init(sifive_spi_register_types) diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig index 9e54a0c8dd..7d90a02181 100644 --- a/hw/ssi/Kconfig +++ b/hw/ssi/Kconfig @@ -2,6 +2,10 @@ config PL022 bool select SSI +config SIFIVE_SPI + bool + select SSI + config SSI bool diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build index dee00c0da6..3d6bc82ab1 100644 --- a/hw/ssi/meson.build +++ b/hw/ssi/meson.build @@ -2,6 +2,7 @@ softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c')) softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-spi.c')) softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c')) softmmu_ss.add(when: 'CONFIG_PL022', if_true: files('pl022.c')) +softmmu_ss.add(when: 'CONFIG_SIFIVE_SPI', if_true: files('sifive_spi.c')) softmmu_ss.add(when: 'CONFIG_SSI', if_true: files('ssi.c')) softmmu_ss.add(when: 'CONFIG_STM32F2XX_SPI', if_true: files('stm32f2xx_spi.c')) softmmu_ss.add(when: 'CONFIG_XILINX_SPI', if_true: files('xilinx_spi.c'))