On Sat, Apr 25, 2015 at 1:18 AM, Alistair Francis <alistai...@gmail.com> wrote: > Add the STM32F2xx SPI device. > > Signed-off-by: Alistair Francis <alist...@alistair23.me> > --- > > default-configs/arm-softmmu.mak | 1 + > hw/ssi/Makefile.objs | 1 + > hw/ssi/stm32f2xx_spi.c | 211 > ++++++++++++++++++++++++++++++++++++++++ > include/hw/ssi/stm32f2xx_spi.h | 74 ++++++++++++++ > 4 files changed, 287 insertions(+) > create mode 100644 hw/ssi/stm32f2xx_spi.c > create mode 100644 include/hw/ssi/stm32f2xx_spi.h > > diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak > index 2b16590..580fac3 100644 > --- a/default-configs/arm-softmmu.mak > +++ b/default-configs/arm-softmmu.mak > @@ -85,6 +85,7 @@ CONFIG_STM32F2XX_TIMER=y > CONFIG_STM32F2XX_USART=y > CONFIG_STM32F2XX_SYSCFG=y > CONFIG_STM32F2XX_ADC=y > +CONFIG_STM32F2XX_SPI=y > CONFIG_STM32F205_SOC=y > > CONFIG_VERSATILE_PCI=y > diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs > index 9555825..c674247 100644 > --- a/hw/ssi/Makefile.objs > +++ b/hw/ssi/Makefile.objs > @@ -2,5 +2,6 @@ common-obj-$(CONFIG_PL022) += pl022.o > common-obj-$(CONFIG_SSI) += ssi.o > common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o > common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o > +common-obj-$(CONFIG_STM32F2XX_SPI) += stm32f2xx_spi.o > > obj-$(CONFIG_OMAP) += omap_spi.o > diff --git a/hw/ssi/stm32f2xx_spi.c b/hw/ssi/stm32f2xx_spi.c > new file mode 100644 > index 0000000..11bbdef > --- /dev/null > +++ b/hw/ssi/stm32f2xx_spi.c > @@ -0,0 +1,211 @@ > +/* > + * STM32F405 SPI > + * > + * Copyright (c) 2014 Alistair Francis <alist...@alistair23.me> > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > copy > + * of this software and associated documentation files (the "Software"), to > deal > + * in the Software without restriction, including without limitation the > rights > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell > + * copies of the Software, and to permit persons to whom the Software is > + * furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING > FROM, > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN > + * THE SOFTWARE. > + */ > + > +#include "hw/ssi/stm32f2xx_spi.h" > + > +#ifndef STM_SPI_ERR_DEBUG > +#define STM_SPI_ERR_DEBUG 0 > +#endif > + > +#define DB_PRINT_L(lvl, fmt, args...) do { \ > + if (STM_SPI_ERR_DEBUG >= lvl) { \ > + qemu_log("%s: " fmt, __func__, ## args); \ > + } \ > +} while (0); > + > +#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) > + > +static void stm32f2xx_spi_reset(DeviceState *dev) > +{ > + STM32F2XXSpiState *s = STM32F2XX_SPI(dev); > + > + s->spi_cr1 = 0x00000000; > + s->spi_cr2 = 0x00000000; > + s->spi_sr = 0x0000000A; > + s->spi_dr = 0x0000000C; > + s->spi_crcpr = 0x00000007; > + s->spi_rxcrcr = 0x00000000; > + s->spi_txcrcr = 0x00000000; > + s->spi_i2scfgr = 0x00000000; > + s->spi_i2spr = 0x00000002; > +} > + > +static void stm32f2xx_spi_transfer(STM32F2XXSpiState *s) > +{ > + DB_PRINT("Data to send: 0x%x\n", s->spi_dr); > + > + s->spi_dr = ssi_transfer(s->ssi, s->spi_dr); > + s->spi_sr |= STM_SPI_SR_RXNE; > + > + DB_PRINT("Data recieved: 0x%x\n", s->spi_dr);
"received". I usually just us "tx" and "rx" in the strings though. Using a %s __func__ you could probably dropthe sentance altogether. > +} > + > +static uint64_t stm32f2xx_spi_read(void *opaque, hwaddr addr, > + unsigned int size) > +{ > + STM32F2XXSpiState *s = opaque; > + uint32_t retval; > + > + DB_PRINT("Read 0x%"HWADDR_PRIx"\n", addr); __func__ Makes it sef contained and also consistent with ADC (and other devs). > + > + switch (addr) { > + case STM_SPI_CR1: > + return s->spi_cr1; > + case STM_SPI_CR2: > + qemu_log_mask(LOG_UNIMP, "%s: Interrupts and DMA are not > implemented\n", > + __func__); > + return s->spi_cr2; > + case STM_SPI_SR: > + retval = s->spi_sr; > + return retval; > + case STM_SPI_DR: > + s->spi_sr |= STM_SPI_SR_BSY; > + stm32f2xx_spi_transfer(s); This seems unusual to trigger the actual SPI transfer using the read txn. This would mean that this would be a very long bus transaction as you need to wait for the actual SPI byte txrx to happen during a single MMIO read. This can be very slow. Does the transfer occur on the write instead? > + s->spi_sr &= ~STM_SPI_SR_RXNE; > + s->spi_sr &= ~STM_SPI_SR_BSY; This (and the |= of same above) should be unneeded as it will never be visible to the guest. > + return s->spi_dr; > + case STM_SPI_CRCPR: > + qemu_log_mask(LOG_UNIMP, "%s: CRC is not implemented, the registers > " \ > + "are includded for compatability\n", __func__); "included" > + return s->spi_crcpr; > + case STM_SPI_RXCRCR: > + qemu_log_mask(LOG_UNIMP, "%s: CRC is not implemented, the registers > " \ > + "are includded for compatability\n", __func__); > + return s->spi_rxcrcr; > + case STM_SPI_TXCRCR: > + qemu_log_mask(LOG_UNIMP, "%s: CRC is not implemented, the registers > " \ > + "are includded for compatability\n", __func__); You could perhaps factor out a lot of this string into a const variable to avoid the repetition. > + return s->spi_txcrcr; > + case STM_SPI_I2SCFGR: > + qemu_log_mask(LOG_UNIMP, "%s: I2S is not implemented, the registers > " \ > + "are includded for compatability\n", __func__); > + return s->spi_i2scfgr; > + case STM_SPI_I2SPR: > + qemu_log_mask(LOG_UNIMP, "%s: I2S is not implemented, the registers > " \ > + "are includded for compatability\n", __func__); > + return s->spi_i2spr; > + default: > + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n", > + __func__, addr); > + } > + > + return 0; > +} > + > +static void stm32f2xx_spi_write(void *opaque, hwaddr addr, > + uint64_t val64, unsigned int size) > +{ > + STM32F2XXSpiState *s = opaque; > + uint32_t value = val64; > + > + DB_PRINT("Write 0x%x at 0x%"HWADDR_PRIx"\n", value, addr); > + > + switch (addr) { > + case STM_SPI_CR1: > + s->spi_cr1 = value; > + return; > + case STM_SPI_CR2: > + qemu_log_mask(LOG_UNIMP, "%s: " \ > + "Interrupts and DMA are not implemented\n", __func__); > + s->spi_cr2 = value; > + return; > + case STM_SPI_SR: > + /* Read only register, except for clearing the CRCERR bit, which > + * is not supported > + */ > + return; > + case STM_SPI_DR: > + s->spi_sr |= STM_SPI_SR_BSY; > + s->spi_sr &= ~STM_SPI_SR_TXE; > + s->spi_dr = value; > + stm32f2xx_spi_transfer(s); So it does a transfer on the write as well. This means there are actually two transfers per write/read pair. One will be read only, the other write only. It is possible to do concurrent read and write with this IP? > + s->spi_sr |= STM_SPI_SR_TXE; > + s->spi_sr &= ~STM_SPI_SR_BSY; > + return; > + case STM_SPI_CRCPR: > + qemu_log_mask(LOG_UNIMP, "%s: CRC is not implemented\n", __func__); > + return; > + case STM_SPI_RXCRCR: > + qemu_log_mask(LOG_GUEST_ERROR, "%s: Read only register: " \ > + "0x%"HWADDR_PRIx"\n", __func__, addr); > + return; > + case STM_SPI_TXCRCR: > + qemu_log_mask(LOG_GUEST_ERROR, "%s: Read only register: " \ > + "0x%"HWADDR_PRIx"\n", __func__, addr); > + return; > + case STM_SPI_I2SCFGR: > + qemu_log_mask(LOG_UNIMP, "%s: " \ > + "I2S is not implemented\n", __func__); > + return; > + case STM_SPI_I2SPR: > + qemu_log_mask(LOG_UNIMP, "%s: " \ > + "I2S is not implemented\n", __func__); > + return; > + default: > + qemu_log_mask(LOG_GUEST_ERROR, > + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); > + } > +} > + > +static const MemoryRegionOps stm32f2xx_spi_ops = { > + .read = stm32f2xx_spi_read, > + .write = stm32f2xx_spi_write, > + .endianness = DEVICE_NATIVE_ENDIAN, > +}; > + > +static void stm32f2xx_spi_init(Object *obj) > +{ > + STM32F2XXSpiState *s = STM32F2XX_SPI(obj); > + DeviceState *dev = DEVICE(obj); > + > + memory_region_init_io(&s->mmio, obj, &stm32f2xx_spi_ops, s, > + TYPE_STM32F2XX_SPI, 0x400); > + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); > + > + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); > + > + s->ssi = ssi_create_bus(dev, "ssi"); > +} > + > +static void stm32f2xx_spi_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + dc->reset = stm32f2xx_spi_reset; > +} > + > +static const TypeInfo stm32f2xx_spi_info = { > + .name = TYPE_STM32F2XX_SPI, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(STM32F2XXSpiState), > + .instance_init = stm32f2xx_spi_init, > + .class_init = stm32f2xx_spi_class_init, > +}; > + > +static void stm32f2xx_spi_register_types(void) > +{ > + type_register_static(&stm32f2xx_spi_info); > +} > + > +type_init(stm32f2xx_spi_register_types) > diff --git a/include/hw/ssi/stm32f2xx_spi.h b/include/hw/ssi/stm32f2xx_spi.h > new file mode 100644 > index 0000000..75dd6a8 > --- /dev/null > +++ b/include/hw/ssi/stm32f2xx_spi.h > @@ -0,0 +1,74 @@ > +/* > + * STM32F2XX SPI > + * > + * Copyright (c) 2014 Alistair Francis <alist...@alistair23.me> > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > copy > + * of this software and associated documentation files (the "Software"), to > deal > + * in the Software without restriction, including without limitation the > rights > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell > + * copies of the Software, and to permit persons to whom the Software is > + * furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING > FROM, > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN > + * THE SOFTWARE. > + */ > + > +#ifndef HW_STM32F2XX_SPI_H > +#define HW_STM32F2XX_SPI_H > + > +#include "hw/sysbus.h" > +#include "hw/hw.h" > +#include "hw/ssi.h" > + > +#define STM_SPI_CR1 0x00 > +#define STM_SPI_CR2 0x04 > +#define STM_SPI_SR 0x08 > +#define STM_SPI_DR 0x0C > +#define STM_SPI_CRCPR 0x10 > +#define STM_SPI_RXCRCR 0x14 > +#define STM_SPI_TXCRCR 0x18 > +#define STM_SPI_I2SCFGR 0x1C > +#define STM_SPI_I2SPR 0x20 > + > +#define STM_SPI_CR1_SPE (1 << 6) > +#define STM_SPI_CR1_MSTR (1 << 2) > + > +#define STM_SPI_SR_BSY (1 << 7) > +#define STM_SPI_SR_TXE (1 << 1) > +#define STM_SPI_SR_RXNE 1 > + > +#define TYPE_STM32F2XX_SPI "stm32f2xx-spi" > +#define STM32F2XX_SPI(obj) \ > + OBJECT_CHECK(STM32F2XXSpiState, (obj), TYPE_STM32F2XX_SPI) > + > +typedef struct { > + /* <private> */ > + SysBusDevice parent_obj; > + > + /* <public> */ > + MemoryRegion mmio; > + > + uint32_t spi_cr1; > + uint32_t spi_cr2; > + uint32_t spi_sr; > + uint32_t spi_dr; > + uint32_t spi_crcpr; > + uint32_t spi_rxcrcr; > + uint32_t spi_txcrcr; > + uint32_t spi_i2scfgr; > + uint32_t spi_i2spr; > + > + qemu_irq irq; > + SSIBus *ssi; > +} STM32F2XXSpiState; STM32F2XXSPIState Regards, Peter > + > +#endif /* HW_STM32F2XX_SPI_H */ > -- > 2.1.4 > >