Using the i.MX28 SSP without DMA

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Using the i.MX28 SSP without DMA

1,351 Views
stathisv
Contributor II
Hi all, It is not clear to me what is the right way to transfer data to/from the SSP without using DMA. The general method for reading data is to poll the FIFO_EMPTY bit of the SSP_STATUS register and then read the actual data from the SSP_DATA register. However, when the status bit gets cleared can we always assume that we can read 4 bytes of data from the SSP_DATA register or anything between 1 and 4 bytes maybe available? The same issue exists when we want to write data to the SSP in which case we poll the FIFO_FULL status bit before doing a write to the data register. The available SPI drivers for the i.MX28 seem to program the SSP to do single byte transfers and therefore do no run into this issue. However this is not efficient.
Labels (1)
0 Kudos
Reply
2 Replies

778 Views
stathisv
Contributor II

Hi Wolfgang,

Thanks for your reply. I have a driver loosely based on the u-boot code, that works with my SPI flash in 4-bit mode with single byte transfers. However, I cannot get multi-byte transfers to work reliably. That is why I am wondering about the semantics of the FIFO_FULL and FIFO_EMPTY bits. Do they mean at least 1 byte or 1 word of space in the FIFO? 

0 Kudos
Reply

778 Views
wolfgang_gaerbe
Contributor II

Hi,

clear read fifo, send as much bytes as you want to receive..

Single Byte - as documented - wait fifo empty, send byte, wait fifo empty(tx), read out received byte from data register.

As far as I reversed engineered it - you can write 4Bytes into the 32Bit output register to send... same is with receive.

If you look for the spansion multi i-o driver on this forum - there are the needed u-boot src files which support spi with more than one data line. Anyway, you can see a way how to use the fifo in polled mode there.

I wanted to boot my machine from spi reading 8MByte from SPI flash.

This took initial up to 30 seconds.

By tweaking the u-boot spi_xfer routine - I speeded up the process down to 8 seconds, loop unrolling, explicit encoding from r/w/rw situation, removed timeouts.... tweaked - but still single byte transfer.

==========================

int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
        const void *dout, void *din, unsigned long flags)
{
    struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
    struct mx28_ssp_regs *ssp_regs = mxs_slave->regs;
    int len = bitlen / 8;
    const char *tx = dout;
    char *rx = din;
    char dummy;

    if (bitlen == 0) {
        if (flags & SPI_XFER_END) {
            rx = &dummy;
            len = 1;
        } else
            return 0;
    }

    if (!rx && !tx)
        return 0;

    if (flags & SPI_XFER_BEGIN)
        mxs_spi_start_xfer(ssp_regs);
    
    writel(1, &ssp_regs->hw_ssp_xfer_size);
    
    if(tx&&(!rx))
    {
        while(--len)
        {
            writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_clr);
            writel(SSP_CTRL0_RUN, &ssp_regs->hw_ssp_ctrl0_set);

            if (mx28_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg,
                SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
                printf("MXS SPI: Timeout waiting for start\n");
                return -ETIMEDOUT;
            }
            writel(*tx++, &ssp_regs->hw_ssp_data);
            writel(SSP_CTRL0_DATA_XFER, &ssp_regs->hw_ssp_ctrl0_set);
            if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg,
                SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
                printf("MXS SPI: Timeout waiting for finish\n");
                return -ETIMEDOUT;
            }
        }
        if(flags & SPI_XFER_END) mxs_spi_end_xfer(ssp_regs);
        writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_clr);
        writel(SSP_CTRL0_RUN, &ssp_regs->hw_ssp_ctrl0_set);

        if (mx28_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg,
            SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
            printf("MXS SPI: Timeout waiting for start\n");
            return -ETIMEDOUT;
        }
        writel(*tx++, &ssp_regs->hw_ssp_data);
        writel(SSP_CTRL0_DATA_XFER, &ssp_regs->hw_ssp_ctrl0_set);
        if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg,
            SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
            printf("MXS SPI: Timeout waiting for finish\n");
            return -ETIMEDOUT;
        }
    }
    else if((!tx)&&rx)
    {
        while (--len)
                {
                    while(readl(0x80014000)&SSP_CTRL0_RUN){}
                    writel(SSP_CTRL0_READ|SSP_CTRL0_RUN|SSP_CTRL0_DATA_XFER, 0x80014004/*&ssp_regs->hw_ssp_ctrl0_set*/);
                    while(readl(0x80014100)&SSP_STATUS_FIFO_EMPTY){}
                    *rx = readl(0x80014090);
                    rx++;
                }
                while(readl(0x80014000)&SSP_CTRL0_RUN){}
                if (flags & SPI_XFER_END) mxs_spi_end_xfer(ssp_regs);
                writel(SSP_CTRL0_READ|SSP_CTRL0_RUN|SSP_CTRL0_DATA_XFER, 0x80014004/*&ssp_regs->hw_ssp_ctrl0_set*/);
                while(readl(0x80014100)&SSP_STATUS_FIFO_EMPTY){}
                *rx = readl(0x80014090);
                rx++;
    }
    else
    {    
    while (len--) {
        /* We transfer 1 byte */
        

        if ((flags & SPI_XFER_END) && !len)
            mxs_spi_end_xfer(ssp_regs);

        if (tx)
            writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_clr);
        else
            writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_set);

        writel(SSP_CTRL0_RUN, &ssp_regs->hw_ssp_ctrl0_set);

        if (mx28_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg,
            SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
            printf("MXS SPI: Timeout waiting for start\n");
            return -ETIMEDOUT;
        }

        if (tx)
            writel(*tx++, &ssp_regs->hw_ssp_data);

        writel(SSP_CTRL0_DATA_XFER, &ssp_regs->hw_ssp_ctrl0_set);

        if (rx) {
            if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_status_reg,
                SSP_STATUS_FIFO_EMPTY, MXS_SPI_MAX_TIMEOUT)) {
                printf("MXS SPI: Timeout waiting for data\n");
                return -ETIMEDOUT;
            }

            *rx = readl(&ssp_regs->hw_ssp_data);
            rx++;
        }

        if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg,
            SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
            printf("MXS SPI: Timeout waiting for finish\n");
            return -ETIMEDOUT;
        }
    }
    }

    return 0;
}

0 Kudos
Reply