qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
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: Tue, 16 Feb 2016 18:15:00 +0100	[thread overview]
Message-ID: <56C35914.60308@gmail.com> (raw)
In-Reply-To: <56C2547B.1060105@tribudubois.net>



W dniu 15.02.2016 o 23:43, Jean-Christophe DUBOIS pisze:
> Le 15/02/2016 17:46, mar.krzeminski a écrit :
>>
>>
>> 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.
>
> Well, obviously, memory (or even LCD screen) are not good candidate 
> for access that are not byte aligned. I guess other type of devices 
> (like an ADC or a DAC) might work on 10, 12 (or other size) bits and 
> accept more uncommon access pattern. After all the i.MX SPI controller 
> could handle any SPI burst length between 1 and 4096 bits.
>
> Now I guess that such devices DAC/ADC are more difficult to emulate in 
> a useful way inside QEMU. So this might not be very popular and 
> therefore not very useful. It might not be worth the trouble inside 
> QEMU ...
>
Modern flash memories have dummy clock cycles for fast operation 
commands, so instead of sending 12 bits I need to call 12 times ssi_trasfer.
Having such feature could also allow to emulate QSPI and family transfer 
modes, but I guess all of this it is corner case...

Thanks,
Marcin
>>
>>
>> T hanks,
>> 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 */
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>
>>>
>>
>>
>
>

  reply	other threads:[~2016-02-16 17:15 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
2016-02-15 22:43             ` Jean-Christophe DUBOIS
2016-02-16 17:15               ` mar.krzeminski [this message]
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=56C35914.60308@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).