From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:45476) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aVMHz-0005Bd-IR for qemu-devel@nongnu.org; Mon, 15 Feb 2016 11:46:22 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aVMHu-0006P2-FX for qemu-devel@nongnu.org; Mon, 15 Feb 2016 11:46:19 -0500 Received: from mail-lb0-x243.google.com ([2a00:1450:4010:c04::243]:35579) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aVMHt-0006Oi-OV for qemu-devel@nongnu.org; Mon, 15 Feb 2016 11:46:14 -0500 Received: by mail-lb0-x243.google.com with SMTP id dx9so6561102lbc.2 for ; Mon, 15 Feb 2016 08:46:13 -0800 (PST) References: <64ed6d560066507adbce3a7077e25ce3330eb2d2.1455379044.git.jcd@tribudubois.net> <56C06A6C.6090608@gmail.com> <56C0B1B5.2020004@tribudubois.net> <56C0D2B0.5060007@gmail.com> <56C1A60F.3000501@tribudubois.net> From: "mar.krzeminski" Message-ID: <56C200D2.90409@gmail.com> Date: Mon, 15 Feb 2016 17:46:10 +0100 MIME-Version: 1.0 In-Reply-To: <56C1A60F.3000501@tribudubois.net> Content-Type: text/plain; charset=iso-8859-2; format=flowed Content-Transfer-Encoding: 8bit Subject: Re: [Qemu-devel] [PATCH 2/3] i.MX: Add the Freescale SPI Controller List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Jean-Christophe DUBOIS , qemu-devel@nongnu.org, peter.maydell@linaro.org, crosthwaite.peter@gmail.com W dniu 15.02.2016 o 11:18, Jean-Christophe DUBOIS pisze: > Le 14/02/2016 20:17, mar.krzeminski a écrit : >> >> >> W dniu 14.02.2016 o 17:56, Jean-Christophe DUBOIS pisze: >>> Le 14/02/2016 12:52, mar.krzeminski a écrit : >>>> Hello, >>>> >>>> W dniu 13.02.2016 o 17:06, Jean-Christophe Dubois pisze: >>>>> Signed-off-by: Jean-Christophe Dubois >>>>> --- >>>>> hw/ssi/Makefile.objs | 1 + >>>>> hw/ssi/imx_spi.c | 449 >>>>> +++++++++++++++++++++++++++++++++++++++++++++++ >>>>> include/hw/ssi/imx_spi.h | 104 +++++++++++ >>>>> 3 files changed, 554 insertions(+) >>>>> create mode 100644 hw/ssi/imx_spi.c >>>>> create mode 100644 include/hw/ssi/imx_spi.h >>>>> >>>>> diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs >>>>> index 9555825..fcbb79e 100644 >>>>> --- a/hw/ssi/Makefile.objs >>>>> +++ b/hw/ssi/Makefile.objs >>>>> @@ -4,3 +4,4 @@ common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o >>>>> common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o >>>>> obj-$(CONFIG_OMAP) += omap_spi.o >>>>> +obj-$(CONFIG_IMX) += imx_spi.o >>>>> diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c >>>>> new file mode 100644 >>>>> index 0000000..9f7f4fe >>>>> --- /dev/null >>>>> +++ b/hw/ssi/imx_spi.c >>>>> @@ -0,0 +1,449 @@ >>>>> +/* >>>>> + * IMX SPI Controller >>>>> + * >>>>> + * Copyright (c) 2016 Jean-Christophe Dubois >>>>> + * >>>>> + * This work is licensed under the terms of the GNU GPL, version >>>>> 2 or later. >>>>> + * See the COPYING file in the top-level directory. >>>>> + * >>>>> + */ >>>>> + >>>>> +#include "hw/ssi/imx_spi.h" >>>>> +#include "sysemu/sysemu.h" >>>>> + >>>>> +#ifndef DEBUG_IMX_SPI >>>>> +#define DEBUG_IMX_SPI 0 >>>>> +#endif >>>>> + >>>>> +#define DPRINTF(fmt, args...) \ >>>>> + do { \ >>>>> + if (DEBUG_IMX_SPI) { \ >>>>> + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_SPI, \ >>>>> + __func__, ##args); \ >>>>> + } \ >>>>> + } while (0) >>>>> + >>>>> +static char const *imx_spi_reg_name(uint32_t reg) >>>>> +{ >>>>> + static char unknown[20]; >>>>> + >>>>> + switch (reg) { >>>>> + case ECSPI_RXDATA: >>>>> + return "ECSPI_RXDATA"; >>>>> + case ECSPI_TXDATA: >>>>> + return "ECSPI_TXDATA"; >>>>> + case ECSPI_CONREG: >>>>> + return "ECSPI_CONREG"; >>>>> + case ECSPI_CONFIGREG: >>>>> + return "ECSPI_CONFIGREG"; >>>>> + case ECSPI_INTREG: >>>>> + return "ECSPI_INTREG"; >>>>> + case ECSPI_DMAREG: >>>>> + return "ECSPI_DMAREG"; >>>>> + case ECSPI_STATREG: >>>>> + return "ECSPI_STATREG"; >>>>> + case ECSPI_PERIODREG: >>>>> + return "ECSPI_PERIODREG"; >>>>> + case ECSPI_TESTREG: >>>>> + return "ECSPI_TESTREG"; >>>>> + case ECSPI_MSGDATA: >>>>> + return "ECSPI_MSGDATA"; >>>>> + default: >>>>> + sprintf(unknown, "%d ?", reg); >>>>> + return unknown; >>>>> + } >>>>> +} >>>>> + >>>>> +static const VMStateDescription vmstate_imx_spi = { >>>>> + .name = TYPE_IMX_SPI, >>>>> + .version_id = 1, >>>>> + .minimum_version_id = 1, >>>>> + .fields = (VMStateField[]) { >>>>> + VMSTATE_FIFO32(tx_fifo, IMXSPIState), >>>>> + VMSTATE_FIFO32(rx_fifo, IMXSPIState), >>>>> + VMSTATE_UINT32_ARRAY(regs, IMXSPIState, ECSPI_MAX), >>>>> + VMSTATE_END_OF_LIST() >>>>> + }, >>>>> +}; >>>>> + >>>>> +static void imx_spi_txfifo_reset(IMXSPIState *s) >>>>> +{ >>>>> + fifo32_reset(&s->tx_fifo); >>>>> + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TE; >>>>> + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_TF; >>>>> +} >>>>> + >>>>> +static void imx_spi_rxfifo_reset(IMXSPIState *s) >>>>> +{ >>>>> + fifo32_reset(&s->rx_fifo); >>>>> + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RR; >>>>> + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RF; >>>>> + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RO; >>>>> +} >>>>> + >>>>> +static void imx_spi_update_irq(IMXSPIState *s) >>>>> +{ >>>>> + int level; >>>>> + >>>>> + if (fifo32_is_empty(&s->rx_fifo)) { >>>>> + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RR; >>>>> + } else { >>>>> + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RR; >>>>> + } >>>>> + >>>>> + if (fifo32_is_full(&s->rx_fifo)) { >>>>> + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RF; >>>>> + } else { >>>>> + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RF; >>>>> + } >>>>> + >>>>> + if (fifo32_is_empty(&s->tx_fifo)) { >>>>> + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TE; >>>>> + } else { >>>>> + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_TE; >>>>> + } >>>>> + >>>>> + if (fifo32_is_full(&s->tx_fifo)) { >>>>> + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TF; >>>>> + } else { >>>>> + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_TF; >>>>> + } >>>>> + >>>>> + level = s->regs[ECSPI_STATREG] & s->regs[ECSPI_INTREG] ? 1 : 0; >>>>> + >>>>> + if (s->previous_level != level) { >>>>> + DPRINTF("setting IRQ a level %d\n", level); >>>>> + s->previous_level = level; >>>>> + qemu_set_irq(s->irq, level); >>>>> + } >>>>> + >>>>> + DPRINTF("IRQ level is %d\n", level); >>>>> +} >>>>> + >>>>> +static uint8_t imx_spi_selected_channel(IMXSPIState *s) >>>>> +{ >>>>> + return EXTRACT(s->regs[ECSPI_CONREG], >>>>> ECSPI_CONREG_CHANNEL_SELECT); >>>>> +} >>>>> + >>>>> +static uint32_t imx_spi_burst_length(IMXSPIState *s) >>>>> +{ >>>>> + return EXTRACT(s->regs[ECSPI_CONREG], >>>>> ECSPI_CONREG_BURST_LENGTH) + 1; >>>>> +} >>>>> + >>>>> +static bool imx_spi_is_enabled(IMXSPIState *s) >>>>> +{ >>>>> + return (s->regs[ECSPI_CONREG] & ECSPI_CONREG_EN) ? true : false; >>>>> +} >>>>> + >>>>> +static bool imx_spi_channel_is_master(IMXSPIState *s) >>>>> +{ >>>>> + uint8_t mode = EXTRACT(s->regs[ECSPI_CONREG], >>>>> ECSPI_CONREG_CHANNEL_MODE); >>>>> + >>>>> + return (mode & (1 << imx_spi_selected_channel(s))) ? true : >>>>> false; >>>>> +} >>>>> + >>>>> +static bool imx_spi_is_multiple_master_burst(IMXSPIState *s) >>>>> +{ >>>>> + uint8_t wave = EXTRACT(s->regs[ECSPI_CONFIGREG], >>>>> ECSPI_CONFIGREG_SS_CTL); >>>>> + >>>>> + return imx_spi_channel_is_master(s) && >>>>> + !(s->regs[ECSPI_CONREG] & ECSPI_CONREG_SMC) && >>>>> + ((wave & (1 << imx_spi_selected_channel(s))) ? true : >>>>> false); >>>>> +} >>>>> + >>>>> +static void imx_spi_flush_txfifo(IMXSPIState *s) >>>>> +{ >>>>> + uint32_t tx; >>>>> + uint32_t rx; >>>>> + >>>>> + DPRINTF("Begin: TX Fifo Size = %d, RX Fifo Size = %d\n", >>>>> + fifo32_num_used(&s->tx_fifo), >>>>> fifo32_num_used(&s->rx_fifo)); >>>>> + >>>>> + while (!fifo32_is_empty(&s->tx_fifo)) { >>>>> + if (s->burst_length <= 0) { >>>>> + uint8_t current_channel = imx_spi_selected_channel(s); >>>>> + >>>>> + s->burst_length = imx_spi_burst_length(s); >>>>> + >>>>> + DPRINTF("Burst length = %d\n", s->burst_length); >>>>> + >>>>> + /* >>>>> + * We need to set the CS signal for the selected device >>>>> + * as a new burst is starting >>>>> + */ >>>>> + qemu_set_irq(s->cs_lines[current_channel], 1); >>>>> + >>>>> + if (imx_spi_is_multiple_master_burst(s)) { >>>>> + s->regs[ECSPI_CONREG] |= ECSPI_CONREG_XCH; >>>>> + } >>>>> + } >>>>> + >>>>> + tx = fifo32_pop(&s->tx_fifo); >>>>> + >>>>> + DPRINTF("data tx:0x%08x\n", tx); >>>>> + >>>>> + rx = ssi_transfer(s->bus, tx); >>>>> + >>>> You are transferring 4 bytes here. Serial flash (and maybe eeprom >>>> soon) expects, >>>> that one ssi_transfer call will transmit one byte. >>>> I can not see any device connected to this controllers in patch 3. >>>> Did you connect something for testing to controller? >>> >>> Yes, we are transferring up to 4 bytes at a time onto the "SPI bus" >>> (this is the size of our FIFO slot and the size of the SSI API). In >>> real world the size of a SPI burst is determined by the ECSPI_CONREG >>> register (from 1 to 4096 bits). So if the EEPROM is only accepting >>> one byte then you have to program 8 bits in the BURST_LENGTH part of >>> the ECSPI_CONREG register. It does mean that you are "wasting" 3 >>> bytes in each slot of the FIFO32 when transferring to a 8 bits >>> device but this is the way this SPI controller is working. Now this >>> information (the number of bits actually transferred) does not seem >>> to be conveyed by the QEMU SSI API. So it is my understanding that >>> it is up to the EEPROM to only consider the bits it is designed to >>> work with. >> Current SSI API does not have these information, even more, the flash >> model assumes that data width will be one byte - see m25p80_transfer8 >> in m25p80.c. There you can also find sst25vf016b model, >> and with you current implementation it will not work. In my comment I >> mean only current ssi usage not the real world. > > OK, so to summarize, at present time all QEMU SPI slave devices are > expecting to receive (and provide) data at a byte level only (even if > the QEMU SSI API is capable of 4 bytes access). So I need to split the > (up to) 4 bytes access of the i.MX SPI master in 1 to 4 single byte > access (depending on the burst size). > Yes. I think about your idea to add interface to set transfer length fo SSI API. The question is, if someone really need such feature. Thanks, Marcin > I'll do this and I will add the sst25vf016b to the sabrelite board. > > JC > >> >> Regards, >> Marcin >>> As for the test, I have not connected any SPI Slave to the SPI >>> controller yet. However the main goal was to add a "minimal" SPI >>> controller support to avoid Linux to hang on SPI slave access when >>> booting the Linux kernel from DTS tree for i.MX6. Now I believe the >>> SPI emulation is quite complete (I might be missing the DMA part) >>> and in any case it is enough to avoid the linux hang (This was Peter >>> Maydell wish on my previous patch for i.MX6). For the i.MX6 >>> sabrelite board I would need to add a sst25vf016b flash NOR memory >>> to the mix. I'll consider adding this next. >>> >>> Regards >>> >>> JC >>> >>>> >>>> Regards, >>>> Marcin >>>>> + DPRINTF("data rx:0x%08x\n", rx); >>>>> + >>>>> + /* Remove 32 bits from the actual burst */ >>>>> + s->burst_length -= 32; >>>>> + >>>>> + if (fifo32_is_full(&s->rx_fifo)) { >>>>> + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RO; >>>>> + } else { >>>>> + fifo32_push(&s->rx_fifo, (uint8_t)rx); >>>>> + } >>>>> + >>>>> + if (s->burst_length <= 0) { >>>>> + uint8_t current_channel = imx_spi_selected_channel(s); >>>>> + >>>>> + /* >>>>> + * We need to unset the CS signal for the selected >>>>> device >>>>> + * as the burst is over >>>>> + */ >>>>> + qemu_set_irq(s->cs_lines[current_channel], 0); >>>>> + >>>>> + s->regs[ECSPI_CONREG] &= ~ECSPI_CONREG_XCH; >>>>> + >>>>> + if (!imx_spi_is_multiple_master_burst(s)) { >>>>> + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TC; >>>>> + break; >>>>> + } >>>>> + } >>>>> + } >>>>> + >>>>> + if (fifo32_is_empty(&s->tx_fifo)) { >>>>> + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TC; >>>>> + } >>>>> + >>>>> + /* TODO: We should also use TDR and RDR bits */ >>>>> + >>>>> + DPRINTF("End: TX Fifo Size = %d, RX Fifo Size = %d\n", >>>>> + fifo32_num_used(&s->tx_fifo), >>>>> fifo32_num_used(&s->rx_fifo)); >>>>> +} >>>>> + >>>>> +static void imx_spi_reset(DeviceState *dev) >>>>> +{ >>>>> + IMXSPIState *s = IMX_SPI(dev); >>>>> + int i; >>>>> + >>>>> + DPRINTF("\n"); >>>>> + >>>>> + memset(s->regs, 0, ECSPI_MAX * sizeof(uint32_t)); >>>>> + >>>>> + s->regs[ECSPI_STATREG] = 0x00000003; >>>>> + >>>>> + imx_spi_rxfifo_reset(s); >>>>> + imx_spi_txfifo_reset(s); >>>>> + >>>>> + imx_spi_update_irq(s); >>>>> + >>>>> + s->burst_length = 0; >>>>> + >>>>> + for (i=0; i<4; i++) { >>>>> + qemu_set_irq(s->cs_lines[i], 0); >>>>> + } >>>>> +} >>>>> + >>>>> +static uint64_t imx_spi_read(void *opaque, hwaddr offset, >>>>> unsigned size) >>>>> +{ >>>>> + uint32 value = 0; >>>>> + IMXSPIState *s = (IMXSPIState *)opaque; >>>>> + uint32_t index = offset >> 2; >>>>> + >>>>> + if (index >= ECSPI_MAX) { >>>>> + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at >>>>> offset 0x%" >>>>> + HWADDR_PRIx "\n", TYPE_IMX_SPI, __func__, >>>>> offset); >>>>> + return 0; >>>>> + } >>>>> + >>>>> + switch (index) { >>>>> + case ECSPI_RXDATA: >>>>> + if (!imx_spi_is_enabled(s)) { >>>>> + value = 0; >>>>> + } else if (fifo32_is_empty(&s->rx_fifo)) { >>>>> + value = 0xdeadbeef; >>>>> + } else { >>>>> + /* read from the RX FIFO */ >>>>> + value = fifo32_pop(&s->rx_fifo); >>>>> + } >>>>> + >>>>> + break; >>>>> + case ECSPI_TXDATA: >>>>> + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read >>>>> from TX FIFO\n", >>>>> + TYPE_IMX_SPI, __func__); >>>>> + >>>>> + /* Reading from TXDATA gives 0 */ >>>>> + >>>>> + break; >>>>> + case ECSPI_MSGDATA: >>>>> + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read >>>>> from MSG FIFO\n", >>>>> + TYPE_IMX_SPI, __func__); >>>>> + >>>>> + /* Reading from MSGDATA gives 0 */ >>>>> + >>>>> + break; >>>>> + default: >>>>> + value = s->regs[index]; >>>>> + break; >>>>> + } >>>>> + >>>>> + DPRINTF("reg[%s] => 0x%" PRIx32 "\n", >>>>> imx_spi_reg_name(index), value); >>>>> + >>>>> + imx_spi_update_irq(s); >>>>> + >>>>> + return (uint64_t)value; >>>>> +} >>>>> + >>>>> +static void imx_spi_write(void *opaque, hwaddr offset, uint64_t >>>>> value, >>>>> + unsigned size) >>>>> +{ >>>>> + IMXSPIState *s = (IMXSPIState *)opaque; >>>>> + uint32_t index = offset >> 2; >>>>> + uint32_t change_mask; >>>>> + >>>>> + if (index >= ECSPI_MAX) { >>>>> + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at >>>>> offset 0x%" >>>>> + HWADDR_PRIx "\n", TYPE_IMX_SPI, __func__, >>>>> offset); >>>>> + return; >>>>> + } >>>>> + >>>>> + DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_spi_reg_name(index), >>>>> + (uint32_t)value); >>>>> + >>>>> + change_mask = s->regs[index] ^ value; >>>>> + >>>>> + switch (index) { >>>>> + case ECSPI_RXDATA: >>>>> + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to write >>>>> to RX FIFO\n", >>>>> + TYPE_IMX_SPI, __func__); >>>>> + break; >>>>> + case ECSPI_TXDATA: >>>>> + case ECSPI_MSGDATA: >>>>> + /* Is there any difference between TXDATA and MSGDATA ? */ >>>>> + /* I'll have to look in the linux driver */ >>>>> + if (!imx_spi_is_enabled(s)) { >>>>> + /* Ignore writes if device is disabled */ >>>>> + break; >>>>> + } else if (fifo32_is_full(&s->tx_fifo)) { >>>>> + /* Ignore writes if queue is full */ >>>>> + break; >>>>> + } >>>>> + >>>>> + fifo32_push(&s->tx_fifo, (uint32_t)value); >>>>> + >>>>> + if (imx_spi_channel_is_master(s) && >>>>> + (s->regs[ECSPI_CONREG] & ECSPI_CONREG_SMC)) { >>>>> + /* >>>>> + * Start emiting if current channel is master and SMC >>>>> bit is >>>>> + * set. >>>>> + */ >>>>> + imx_spi_flush_txfifo(s); >>>>> + } >>>>> + >>>>> + break; >>>>> + case ECSPI_STATREG: >>>>> + /* Clear RO and TC bits on write */ >>>>> + value &= ECSPI_STATREG_RO | ECSPI_STATREG_TC; >>>>> + s->regs[ECSPI_STATREG] &= ~value; >>>>> + >>>>> + break; >>>>> + case ECSPI_CONREG: >>>>> + s->regs[ECSPI_CONREG] = value; >>>>> + >>>>> + if (!imx_spi_is_enabled(s)) { >>>>> + /* device is diabled, so this is a reset */ >>>>> + imx_spi_reset(DEVICE(s)); >>>>> + return; >>>>> + } >>>>> + >>>>> + if (imx_spi_channel_is_master(s)) { >>>>> + /* We are in master mode */ >>>>> + >>>>> + if ((value & change_mask & ECSPI_CONREG_SMC) && >>>>> + !fifo32_is_empty(&s->tx_fifo)) { >>>>> + /* SMC bit is set and TX FIFO has some slots >>>>> filled in */ >>>>> + imx_spi_flush_txfifo(s); >>>>> + } else if ((value & change_mask & ECSPI_CONREG_XCH) && >>>>> + !(value & ECSPI_CONREG_SMC)) { >>>>> + /* This is a request to start emiting */ >>>>> + imx_spi_flush_txfifo(s); >>>>> + } >>>>> + } >>>>> + >>>>> + break; >>>>> + default: >>>>> + s->regs[index] = value; >>>>> + >>>>> + break; >>>>> + } >>>>> + >>>>> + imx_spi_update_irq(s); >>>>> +} >>>>> + >>>>> +static const struct MemoryRegionOps imx_spi_ops = { >>>>> + .read = imx_spi_read, >>>>> + .write = imx_spi_write, >>>>> + .endianness = DEVICE_NATIVE_ENDIAN, >>>>> + .valid = { >>>>> + /* >>>>> + * Our device would not work correctly if the guest was >>>>> doing >>>>> + * unaligned access. This might not be a limitation on >>>>> the real >>>>> + * device but in practice there is no reason for a guest >>>>> to access >>>>> + * this device unaligned. >>>>> + */ >>>>> + .min_access_size = 4, >>>>> + .max_access_size = 4, >>>>> + .unaligned = false, >>>>> + }, >>>>> +}; >>>>> + >>>>> +static void imx_spi_realize(DeviceState *dev, Error **errp) >>>>> +{ >>>>> + IMXSPIState *s = IMX_SPI(dev); >>>>> + int i; >>>>> + >>>>> + s->bus = ssi_create_bus(dev, "spi"); >>>>> + >>>>> + memory_region_init_io(&s->iomem, OBJECT(dev), &imx_spi_ops, s, >>>>> + TYPE_IMX_SPI, 0x1000); >>>>> + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); >>>>> + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); >>>>> + >>>>> + ssi_auto_connect_slaves(dev, s->cs_lines, s->bus); >>>>> + >>>>> + for (i = 0; i < 4; ++i) { >>>>> + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cs_lines[i]); >>>>> + } >>>>> + >>>>> + s->previous_level = -1; >>>>> + s->burst_length = 0; >>>>> + >>>>> + fifo32_create(&s->tx_fifo, ECSPI_FIFO_SIZE); >>>>> + fifo32_create(&s->rx_fifo, ECSPI_FIFO_SIZE); >>>>> +} >>>>> + >>>>> +static void imx_spi_class_init(ObjectClass *klass, void *data) >>>>> +{ >>>>> + DeviceClass *dc = DEVICE_CLASS(klass); >>>>> + >>>>> + dc->realize = imx_spi_realize; >>>>> + dc->vmsd = &vmstate_imx_spi; >>>>> + dc->reset = imx_spi_reset; >>>>> + dc->desc = "i.MX SPI Controller"; >>>>> +} >>>>> + >>>>> +static const TypeInfo imx_spi_info = { >>>>> + .name = TYPE_IMX_SPI, >>>>> + .parent = TYPE_SYS_BUS_DEVICE, >>>>> + .instance_size = sizeof(IMXSPIState), >>>>> + .class_init = imx_spi_class_init, >>>>> +}; >>>>> + >>>>> +static void imx_spi_register_types(void) >>>>> +{ >>>>> + type_register_static(&imx_spi_info); >>>>> +} >>>>> + >>>>> +type_init(imx_spi_register_types) >>>>> diff --git a/include/hw/ssi/imx_spi.h b/include/hw/ssi/imx_spi.h >>>>> new file mode 100644 >>>>> index 0000000..1e38cb1 >>>>> --- /dev/null >>>>> +++ b/include/hw/ssi/imx_spi.h >>>>> @@ -0,0 +1,104 @@ >>>>> +/* >>>>> + * IMX SPI Controller >>>>> + * >>>>> + * Copyright 2016 Jean-Christophe Dubois >>>>> + * >>>>> + * This work is licensed under the terms of the GNU GPL, version >>>>> 2 or later. >>>>> + * See the COPYING file in the top-level directory. >>>>> + */ >>>>> + >>>>> +#ifndef IMX_SPI_H >>>>> +#define IMX_SPI_H >>>>> + >>>>> +#include "hw/sysbus.h" >>>>> +#include "hw/ssi/ssi.h" >>>>> +#include "qemu/bitops.h" >>>>> +#include "qemu/fifo32.h" >>>>> + >>>>> +#define ECSPI_FIFO_SIZE 64 >>>>> + >>>>> +#define ECSPI_RXDATA 0 >>>>> +#define ECSPI_TXDATA 1 >>>>> +#define ECSPI_CONREG 2 >>>>> +#define ECSPI_CONFIGREG 3 >>>>> +#define ECSPI_INTREG 4 >>>>> +#define ECSPI_DMAREG 5 >>>>> +#define ECSPI_STATREG 6 >>>>> +#define ECSPI_PERIODREG 7 >>>>> +#define ECSPI_TESTREG 8 >>>>> +#define ECSPI_MSGDATA 16 >>>>> +#define ECSPI_MAX 17 >>>>> + >>>>> +/* ECSPI_CONREG */ >>>>> +#define ECSPI_CONREG_EN (1 << 0) >>>>> +#define ECSPI_CONREG_HT (1 << 1) >>>>> +#define ECSPI_CONREG_XCH (1 << 2) >>>>> +#define ECSPI_CONREG_SMC (1 << 3) >>>>> +#define ECSPI_CONREG_CHANNEL_MODE_SHIFT 4 >>>>> +#define ECSPI_CONREG_CHANNEL_MODE_LENGTH 4 >>>>> +#define ECSPI_CONREG_DRCTL_SHIFT 16 >>>>> +#define ECSPI_CONREG_DRCTL_LENGTH 2 >>>>> +#define ECSPI_CONREG_CHANNEL_SELECT_SHIFT 18 >>>>> +#define ECSPI_CONREG_CHANNEL_SELECT_LENGTH 2 >>>>> +#define ECSPI_CONREG_BURST_LENGTH_SHIFT 20 >>>>> +#define ECSPI_CONREG_BURST_LENGTH_LENGTH 12 >>>>> + >>>>> +/* ECSPI_CONFIGREG */ >>>>> +#define ECSPI_CONFIGREG_SS_CTL_SHIFT 8 >>>>> +#define ECSPI_CONFIGREG_SS_CTL_LENGTH 4 >>>>> + >>>>> +/* ECSPI_INTREG */ >>>>> +#define ECSPI_INTREG_TEEN (1 << 0) >>>>> +#define ECSPI_INTREG_TDREN (1 << 1) >>>>> +#define ECSPI_INTREG_TFEN (1 << 2) >>>>> +#define ECSPI_INTREG_RREN (1 << 3) >>>>> +#define ECSPI_INTREG_RDREN (1 << 4) >>>>> +#define ECSPI_INTREG_RFEN (1 << 5) >>>>> +#define ECSPI_INTREG_ROEN (1 << 6) >>>>> +#define ECSPI_INTREG_TCEN (1 << 7) >>>>> + >>>>> +/* ECSPI_DMAREG */ >>>>> +#define ECSPI_DMAREG_RXTDEN (1 << 31) >>>>> +#define ECSPI_DMAREG_RXDEN (1 << 23) >>>>> +#define ECSPI_DMAREG_TEDEN (1 << 7) >>>>> +#define ECSPI_DMAREG_RX_THRESHOLD_SHIFT 16 >>>>> +#define ECSPI_DMAREG_RX_THRESHOLD_LENGTH 6 >>>>> + >>>>> +/* ECSPI_STATREG */ >>>>> +#define ECSPI_STATREG_TE (1 << 0) >>>>> +#define ECSPI_STATREG_TDR (1 << 1) >>>>> +#define ECSPI_STATREG_TF (1 << 2) >>>>> +#define ECSPI_STATREG_RR (1 << 3) >>>>> +#define ECSPI_STATREG_RDR (1 << 4) >>>>> +#define ECSPI_STATREG_RF (1 << 5) >>>>> +#define ECSPI_STATREG_RO (1 << 6) >>>>> +#define ECSPI_STATREG_TC (1 << 7) >>>>> + >>>>> +#define EXTRACT(value, name) extract32(value, name##_SHIFT, >>>>> name##_LENGTH) >>>>> + >>>>> +#define TYPE_IMX_SPI "imx.spi" >>>>> +#define IMX_SPI(obj) OBJECT_CHECK(IMXSPIState, (obj), TYPE_IMX_SPI) >>>>> + >>>>> +typedef struct IMXSPIState { >>>>> + /* */ >>>>> + SysBusDevice parent_obj; >>>>> + >>>>> + /* */ >>>>> + MemoryRegion iomem; >>>>> + >>>>> + qemu_irq irq; >>>>> + int previous_level; >>>>> + >>>>> + qemu_irq cs_lines[4]; >>>>> + >>>>> + SSIBus *bus; >>>>> + >>>>> + uint32_t regs[ECSPI_MAX]; >>>>> + >>>>> + Fifo32 rx_fifo; >>>>> + Fifo32 tx_fifo; >>>>> + >>>>> + int16_t burst_length; >>>>> +} IMXSPIState; >>>>> + >>>>> +#endif /* IMX_SPI_H */ >>>> >>>> >>> >>> >> >> > >