This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

DM365 SPI Slave with EDMA; one transfer and then no IRQ

I am using the Spectrum Digital DM365 EVM with DVSDK 3.10.00.12.  We have a need for SPI slave usage on the DM365, so we have modified the Linux drivers to include SPI slave support, including EDMA support.  I am focusing on SPI slave read at the moment.  With my current setup, I am able to successfully receive exactly one message per bootup on SPI2.  After completing one EDMA transfer, the davinci_spi_irq in davinci_spi.c never gets called on successive SPI messages until the DM365 is rebooted.  I have checked all of the global interrupt controller registers, the SPI2 registers, and the EDMA registers and do not see any register settings after the one successful reception which would prevent the IRQ from getting called.

For our setup, the first two bytes of the SPI message is the length of the message.  The IRQ reads the first two bytes of the message to obtain the length, with the length being the number of bytes in the message not including the length field (i.e., the first two bytes of the SPI message specify the length of the EDMA transfer).  The IRQ then disables SPI interrupts and finishes setting up the EDMA transfer with the known SPI message length.  In the RX callback for the EDMA transfer(davinci_spi_dma_rx_callback), the EDMA is disabled and the SPI interrupt is enabled.  Below is a summary of the functions that I believe are related to the issue.  I removed the parts relative to SPI master, since that is not applicable to this issue.

static irqreturn_t davinci_spi_irq(s32 irq, void *context_data)
{
    struct davinci_spi_platform_data *pdata;
    struct davinci_spi *davinci_spi = context_data;
    u32 int_status, rx_data = 0;
    irqreturn_t ret = IRQ_HANDLED;
    u32 rx_length;
    u32 data;
    u8 *tx_buf;
    u32 data1_reg_val;

    int_status = ioread32(davinci_spi->base + SPIFLG);

    if (likely(int_status & SPIFLG_RX_INTR_MASK)) {

        /* Disable Receive Interrupt */
        iowrite32(~(SPIINT_RX_INTR | SPIINT_TX_INTR), davinci_spi->base + SPIINT);

        /* Read in the lower 8 bits of the length field */
        rx_length = ioread32(davinci_spi->base + SPIBUF) & 0x000000FF;

        /* Write some more data and then wait for transfer to complete */
        data1_reg_val = ioread32(davinci_spi->base + SPIDAT1);
        data1_reg_val &= ~(0xFFFF);
        data1_reg_val |= (davinci_spi->current_tx_length >> 8) & 0xFF;

        iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1);

        /* Wait for transfer to complete */
        while ((data = ioread32(davinci_spi->base + SPIBUF))
                     & SPIBUF_RXEMPTY_MASK);

        /* Add the upper 8 bits to the length field */
        data &= 0x000000FF;
        data <<= 8;
        rx_length += data;

        /* Master actually has data to send */
        if (rx_length > 3) {

            /* Get free buffer from bitbang */
            u32 tx_length;
            u8 *rx_buf = spi_bitbang_get_free_rx_buf(&davinci_spi->bitbang);

            if (rx_buf == NULL) {
                /* If no free buffer is available, return error to master */
                tx_buf = rx_not_ready_buf;
            }
            else {
                // Put the length into buffer.
                rx_buf[0] = rx_length & 0xff;
                rx_buf[1] = (rx_length >> 8) & 0xff;
                   
                /* Get a buffer to transmit, if the user has provided one */
                tx_buf = spi_bitbang_get_tx_buf(&davinci_spi->bitbang, &tx_length);
            }

            /* Setup and start DMA */
            davinci_spi_slave_dma(davinci_spi, tx_buf, tx_length, &rx_buf[2], rx_length);
        }

    } else
        (void)davinci_spi_check_error(davinci_spi, int_status);

    int_status = ioread32(davinci_spi->base + SPIFLG);

    return ret;
}

static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data)
{
    struct spi_device *spi = (struct spi_device *)data;
    struct davinci_spi *davinci_spi;
    struct davinci_spi_dma *davinci_spi_dma;
    struct davinci_spi_platform_data *pdata;
    struct device *sdev;

    davinci_spi = spi_master_get_devdata(spi->master);
    davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]);
    pdata = davinci_spi->pdata;

    sdev = davinci_spi->bitbang.master->dev.parent;

    /* We must disable the DMA RX request */
    davinci_spi_set_dma_req(spi, 0);

    // Kill the EDMA so another transfer doesn't start
    edma_stop(davinci_spi_dma->dma_rx_channel);

    /* Enable Rx interrupt */
    set_io_bits(davinci_spi->base + SPIINT, SPIINT_RX_INTR);

    /* Notify bitbang that a DMA transfer has completed */
    spi_bitbang_notify_rx_complete(&davinci_spi->bitbang, davinci_spi_dma->dma_last_rx_length+2);
    edma_free_channel(davinci_spi_dma->dma_rx_channel);
    davinci_spi_dma->dma_rx_channel = -1;
}

 

Does anyone have any idea why I can receive only one message?  Is there an order to the EDMA shutdown/SPI setup that is causing problems?  Any suggestions at what to look at to determine why davinci_spi_irq is not called on successive SPI message receptions?

  • Bryan Evenson said:
    I have checked all of the global interrupt controller registers, the SPI2 registers, and the EDMA registers and do not see any register settings after the one successful reception which would prevent the IRQ from getting called.

    When the SPI is not generating interrupts in your system, are you seeing bits being set in the SPIFLG register? If you are seeing bits set here than you will want to look higher up the interrupt chain (ARM/Linux interrupt status) to see where the interrupt is not getting serviced, if the bits are not being set than there is something in the SPI configuration or hardware that is not causing the interrupts properly.

    Bryan Evenson said:

            /* Disable Receive Interrupt */
            iowrite32(~(SPIINT_RX_INTR | SPIINT_TX_INTR), davinci_spi->base + SPIINT);

    Just a comment on this line, it looks like iowrite32 just writes an entire value to the location it is given, so doing this would seem to clear the entire SPIINT register, which can be ok if you don't expect to have any bits set there, but it could also be bad if it inadvertently is clearing bits (such as perhaps DMAREQEN).

    Bryan Evenson said:
    Is there an order to the EDMA shutdown/SPI setup that is causing problems?

    In general you just need to make sure that the order corresponds to the reverse of the order of the event signal, such that the system is ready before an event happens and you don't have a potential race condition. For example, in this SPI slave situation, since the SPI is going to be generating the events, you want to ensure that the EDMA is configured and ready to go before the SPI, so that SPI events are not dropped. From a purely SPI perspective, the initialization order is important, the sequence described in section 2.6 of SPRUFH1b should be followed.