i.mx8qxp SPI no data observed on MOSI, remains in continuous mode

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

i.mx8qxp SPI no data observed on MOSI, remains in continuous mode

569 Views
fx73
Contributor I

Using QNX (7.1)

Re-using code, functional on the i.MX6, however, we run into two main problems with the imx8qxp:

First, once the SPI starts, we never see the CS return High, it appears that the transmit gets "stuck" in continuous mode.

Second, probably related, we see the clock, we see the chip select, we see the slave device (MISO) clocking in data, but the MOSI line goes low and we never see data. The clock remains on, the chip select and MOSI/SDO low.

This issue is observed on both SPI0 and SPI3.

SETUP (QNX BSP)

Board File:

 

display_msg Starting SPI3 driver (/dev/spi3)...
spi-master -u3 -d imx8lpspi base=0x5A030000,irq=251,verbose=2,clock_rate=1000000
waitfor /dev/spi3

 

 

Pins Config:

 

static int mx8qInitSpi3( const imx_startup_data_t * startup_data )
{
imx_pad_t pads[] =
{
{SC_P_SPI3_SCK, IMX_PAD_ALT_0, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, SC_PAD_28FDSOI_DSE_DV_HIGH, SC_PAD_28FDSOI_PS_PU}, // ADMA.SPI3.SCK
{SC_P_SPI3_SDI, IMX_PAD_ALT_0, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, SC_PAD_28FDSOI_DSE_DV_HIGH, SC_PAD_28FDSOI_PS_PU}, // ADMA.SPI3.SDI
{SC_P_SPI3_SDO, IMX_PAD_ALT_0, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, SC_PAD_28FDSOI_DSE_DV_HIGH, SC_PAD_28FDSOI_PS_PU}, // ADMA.SPI3.SDO
{SC_P_SPI3_CS0, IMX_PAD_ALT_0, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, SC_PAD_28FDSOI_DSE_DV_HIGH, SC_PAD_28FDSOI_PS_PU}, // ADMA.SPI3.CS0
};

 

 

App:

 

struct SpiCfg
{
uint32_t device;
spi_cfg_t spiCfg;
};

int spifd = open("/dev/spi3", O_RDWR);
SpiCfg cfg;
cfg.device = 0; (CS is CS0)
cfg.spiCfg.mode = 16; (we've tried 8 as well)
cfg.spiCfg.mode |= (int) (1 << SPI_MODE_CK_POLPHA_POS);
cfg.spiCfg.clock_rate = 6000000; (we've tried 500,750 KHz, 1-10 MHz)

devctl(spifd, (int)DCMD_SPI_SET_CONFIG, &cfg, sizeof(cfg), 0);

 

 

And Write/Read resource manager

 

SpiExchange(uint32_t wSize, uint16_t *wData, uint32_t rSize, uint16_t *rData)
{
	iov_t       iov[3];
	spi_msg_t   msg;
	uint32_t    cs = 0;

	memset((char *)&msg, 0, sizeof(msg));

	msg.msg_hdr.i.type        = (int)_IO_MSG;
	msg.msg_hdr.i.subtype     = (int)_SPI_IOMSG_EXCHANGE;
	msg.msg_hdr.i.mgrid       = (int)_IOMGR_SPI;
	msg.msg_hdr.i.combine_len = sizeof(spi_msg_t);

	msg.device                = cs;
	msg.xlen                  = wSize*sizeof(uint16_t );

	SETIOV(iov + 0, &msg, sizeof(spi_msg_t));
	SETIOV(iov + 1, (uint8_t *)wData, wSize*sizeof(uint16_t ));
	SETIOV(iov + 2, (uint8_t *)rData, rSize*sizeof(uint16_t ));

	MsgSendv(spifd, iov + 0, 2, iov + 2, 1);
}

 

 

MsgSendv returns an EIO error which is the default in the driver if anything goes wrong.

 

BSP driver:

 

/**
 * SPI data exchange function (interrupt mode).
 *
 * @Param hdl    Low level driver handle.
 * @Param device Chip select device (0, 1, 2, 3).
 * @Param buf    Pointer to data buffer.
 * @Param len    Pointer to data size to send/receive.
 */
void * imxspi_xfer(void *hdl, uint32_t device, uint8_t *buf, int *len)
{
    imx_spi_t   *dev = hdl;
    uintptr_t   base = dev->vbase;
    uint32_t    ssid;
    uint32_t    data;
    uint32_t    spi_word;
    uint32_t    burst;

    /* Device associated with slave select (ss0, ss1, ss2, ss3) */
    ssid = device & SPI_DEV_ID_MASK;
    if (ssid >= IMX_SPI_DEV_NUM) {
        *len = -1;
        return buf;
    }
    spi_word = devlist[ssid].cfg.mode & SPI_MODE_CHAR_LEN_MASK;
    dev->xlen = *len;
    dev->rlen = 0;
    dev->tlen = 0;
    dev->pbuf = buf;
    dev->dlen = (spi_word + 7) >> 3;
    /* Configure water-mark value
     * -1 is for water-mark logic, RX flags is set when more data than water-mark is in RX FIFO.
     * -1 is for CONT mode - without CS toggle controller does not recognize last data. */
    burst = min((dev->xlen - dev->tlen) / dev->dlen, dev->fifo_size);
    if (burst > 1) {
        burst -= 2;
    }
    if (burst == 1) {
        burst -= 1;
    }
    if (dev->xlen % dev->dlen) {
        *len = -1;
        return buf;
    }
    /* Module busy */
    if (in32(base + IMX_LPSPI_SR) & IMX_LPSPI_SR_MBF_MASK) {
        *len = -1;
        LOG_INFO("(spi  t%d::%s:%d) XFER Busy", pthread_self(), __func__, __LINE__);
        return buf;
    }
    /* Estimate transfer time in us... The calculated dtime is only used for
     * the timeout, so it doesn't have to be that accurate.  At higher clock
     * rates, a calculated dtime of 0 would mess-up the timeout calculation, so
     * round up to 1 us
     */
    dev->dtime = dev->dlen * (8 + dev->dbt * 2) * 1000 * 1000 / devlist[ssid].cfg.clock_rate;
    if (dev->dtime == 0) {
        dev->dtime = 1;
    }
    /* Configure SPI communication parameters */
    spi_setup_master(dev, ssid, burst);
    /* Write data to TXFIFO */
    while ((dev->xlen > dev->tlen) && ((uint32_t)(dev->tlen / dev->dlen) < dev->fifo_size)) {
        switch (dev->dlen) {
            case NBYTES_1:
                data = buf[dev->tlen];
                break;
            case NBYTES_2:
                data = *(uint16_t *)(&buf[dev->tlen]);
                break;
            case NBYTES_4:
                data = *(uint32_t *)(&buf[dev->tlen]);
                break;
            default:
                LOG_ERROR("(spi  t%d::%s:%d) Unsupported word length", pthread_self(), __func__, __LINE__);
                data = 0;
                break;
        }
        out32(base + IMX_LPSPI_TDR, data);
        dev->tlen += dev->dlen;
    }
    /* In case all data already sent toggle the CS */
    if (dev->tlen >= dev->xlen) {
        uint32_t tcr = in32(base + IMX_LPSPI_TCR);
        tcr &= ~IMX_LPSPI_TCR_CONT_MASK;
        out32(base + IMX_LPSPI_TCR, tcr);
    }
    /* Enable receive interrupt */
    out32(base + IMX_LPSPI_IER, IMX_LPSPI_IER_RDIE_MASK);
    /* Wait for exchange to finish */
    if (imxspi_wait(dev, dev->xlen)) {
        LOG_ERROR("(spi  t%d::%s:%d) XFER Interrupt Timeout", pthread_self(), __func__, __LINE__);
        dev->rlen = -1;
    }
    *len = dev->rlen;

    return buf;
}

 

 

wait.c

 

/**
 * Wait for exchange to finish.
 *
 * @Param dev Low level driver handle.
 * @Param len Transfer length.
 *
 * @return EOK if everything is OK, -1 otherwise.
 */
int imxspi_wait(imx_spi_t *dev, int len)
{
    struct _pulse pulse;
    uint64_t      to;
    uint32_t      tcr, der;

    while (1) {
        if (len) {
            to = dev->dtime;
            to *= len * 1000 * 50;    /* 50 times for time out */
            TimerTimeout(CLOCK_REALTIME, _NTO_TIMEOUT_RECEIVE, NULL, &to, NULL);
        }
        if (MsgReceivePulse(dev->chid, &pulse, sizeof(pulse), NULL) == -1) {
            return -1;
        }
        if (pulse.code == IMX_SPI_EVENT) {
            return EOK;
        }
        if (pulse.code == IMX_SPI_TX_DMA_EVENT) {
            /* Check DMA transfer status */
            if (dev->dmafuncs.xfer_complete(dev->edma_tx_handler)) {
                return -1;
            }
            /* Disable DMA request */
            der = in32(dev->vbase + IMX_LPSPI_DER);
            der &= ~IMX_LPSPI_DER_TDDE_MASK;
            out32(dev->vbase + IMX_LPSPI_DER, der);
            /* Toggle the CS line if all data is sent. */
            tcr = in32(dev->vbase + IMX_LPSPI_TCR);
            tcr &= ~(IMX_LPSPI_TCR_CONTC_MASK | IMX_LPSPI_TCR_CONT_MASK);
            out32(dev->vbase + IMX_LPSPI_TCR, tcr);
        }
        if (pulse.code == IMX_SPI_RX_DMA_EVENT) {
            /* Check DMA transfer status */
            if (dev->dmafuncs.xfer_complete(dev->edma_rx_handler)) {
                return -1;
            }
            /* Disable DMA request */
            out32(dev->vbase + IMX_LPSPI_DER, 0);
            return EOK;
        }
    }
    return EOK;
}

 

 

Problems:

out32(base + IMX_LPSPI_TDR, data);
we would expect the SDO to begin sending data, but it does not. Additionally, the FIFO count for TX (in FSR) continues growing as the "while loop" continues. We see data on MISO coming from the external device, CS goes low, clock starts, MOSI goes low, but never any data out and the clock continues to infinity and CS and MOSI stay low. 

"data" is 0x12, 0x34, 0x56, and then finally, 0x78 

We've also tried LSB first, MSB first, no difference, MOSI is always zero.

 

/* In case all data already sent toggle the CS */
if (dev->tlen >= dev->xlen) {
    uint32_t tcr = in32(base + IMX_LPSPI_TCR);     #1
    tcr &= ~IMX_LPSPI_TCR_CONT_MASK;             #2
    out32(base + IMX_LPSPI_TCR, tcr);                    #3
}

The TCR.CONT bit is set when read at point #1, while it is cleared at point #2 and written back in #3, when read back however, it is not cleared, TCR.CONT is still set. It is read later and remains set.

So it appears that TCR.CONT is never cleared and the CS never goes high

 

Lastly, imxspi_wait(dev, dev->xlen) always times out, and thus ultimately, returns -1 

 

 

 

 

 

0 Kudos
2 Replies

553 Views
Sanket_Parekh
NXP TechSupport
NXP TechSupport

Hi @fx73 

I hope you are doing well.

I am sorry to say but NXP is not supporting QNX OS. Please raise this query to QNX community.

Thanks & Regards

Sanket Parekh

0 Kudos

388 Views
Polygynyisnotadultery
Contributor I

ocb->chip does not get set unless the configuration.device parameter has the SPI_DEV_DEFAULT flag set. This will result in the ocb->chip value > num_cs which causes the EIO to be returned.

0 Kudos