Message ID | aa8d1c539de63c12476732d90ebde19aefb761de.1429949062.git.alistair@alistair23.me |
---|---|
State | New |
Headers | show |
On Sat, Apr 25, 2015 at 1:18 AM, Alistair Francis <alistair23@gmail.com> wrote: > Add the STM32F2xx SPI device. > > Signed-off-by: Alistair Francis <alistair@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 <alistair@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 <alistair@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 > >
On Sun, Apr 26, 2015 at 4:43 AM, Peter Crosthwaite <peter.crosthwaite@xilinx.com> wrote: > On Sat, Apr 25, 2015 at 1:18 AM, Alistair Francis <alistair23@gmail.com> wrote: >> Add the STM32F2xx SPI device. >> >> Signed-off-by: Alistair Francis <alistair@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 <alistair@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 <alistair@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 >> >>
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 <alistair@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); +} + +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); + + 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); + s->spi_sr &= ~STM_SPI_SR_RXNE; + s->spi_sr &= ~STM_SPI_SR_BSY; + 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__); + 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__); + 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); + 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 <alistair@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; + +#endif /* HW_STM32F2XX_SPI_H */
Add the STM32F2xx SPI device. Signed-off-by: Alistair Francis <alistair@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