From: "mar.krzeminski" <mar.krzeminski@gmail.com>
To: Jean-Christophe DUBOIS <jcd@tribudubois.net>,
qemu-devel@nongnu.org, peter.maydell@linaro.org,
crosthwaite.peter@gmail.com
Subject: Re: [Qemu-devel] [PATCH 2/3] i.MX: Add the Freescale SPI Controller
Date: Mon, 15 Feb 2016 17:46:10 +0100 [thread overview]
Message-ID: <56C200D2.90409@gmail.com> (raw)
In-Reply-To: <56C1A60F.3000501@tribudubois.net>
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 <jcd@tribudubois.net>
>>>>> ---
>>>>> 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 <jcd@tribudubois.net>
>>>>> + *
>>>>> + * 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 <jcd@tribudubois.net>
>>>>> + *
>>>>> + * 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 {
>>>>> + /* <private> */
>>>>> + SysBusDevice parent_obj;
>>>>> +
>>>>> + /* <public> */
>>>>> + 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 */
>>>>
>>>>
>>>
>>>
>>
>>
>
>
next prev parent reply other threads:[~2016-02-15 16:46 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-02-13 16:06 [Qemu-devel] [PATCH 0/3] Add support for i.MX SPI Controller Jean-Christophe Dubois
2016-02-13 16:06 ` [Qemu-devel] [PATCH 1/3] FIFO: Add a FIFO32 implementation Jean-Christophe Dubois
2016-02-13 16:06 ` [Qemu-devel] [PATCH 2/3] i.MX: Add the Freescale SPI Controller Jean-Christophe Dubois
2016-02-14 11:52 ` mar.krzeminski
2016-02-14 16:56 ` Jean-Christophe DUBOIS
2016-02-14 19:17 ` mar.krzeminski
2016-02-15 10:18 ` Jean-Christophe DUBOIS
2016-02-15 16:46 ` mar.krzeminski [this message]
2016-02-15 22:43 ` Jean-Christophe DUBOIS
2016-02-16 17:15 ` mar.krzeminski
2016-03-17 17:03 ` Peter Crosthwaite
2016-02-13 16:06 ` [Qemu-devel] [PATCH 3/3] i.MX: Add SPI controllers to i.MX6 SOC Jean-Christophe Dubois
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=56C200D2.90409@gmail.com \
--to=mar.krzeminski@gmail.com \
--cc=crosthwaite.peter@gmail.com \
--cc=jcd@tribudubois.net \
--cc=peter.maydell@linaro.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).