On Sun, Apr 26, 2015 at 4:43 AM, Peter Crosthwaite <peter.crosthwa...@xilinx.com> wrote: > 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.
Good catch. I prefer using the full string as it explicitly describes what is going on. Plus it's not that long. > Using a %s __func__ you could probably dropthe sentance altogether. The %s __func__ is included in the DB_PRINT() macro. Although as mentioned I still like including the string, especially to indicate the direction. > >> +} >> + >> +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). Ok, I'll make it the same as the ADC, which is actually a little different to some of the others :\ > >> + >> + 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? >From the data sheet it looks like the SPI transfer occurs on both reads/writes, as there is only one register. >From the data sheet: "Data received or to be transmitted. The data register is split into 2 buffers - one for writing (Transmit Buffer) and another one for reading (Receive buffer). A write to the data register will write into the Tx buffer and a read from the data register will return the value held in the Rx buffer." > >> + 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. True, will remove. > >> + 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" Good catch, will fix them all. > >> + 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. That's true, but I don't think it's nesesary. It's only in the code a few times. > >> + 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? >From what I get from the data sheet I don't think so. See the quote from above. > >> + 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 Ok, will fix. Thanks, Alistair > > Regards, > Peter > >> + >> +#endif /* HW_STM32F2XX_SPI_H */ >> -- >> 2.1.4 >> >>