Strange SPI behavior on MOSI pin

Hi everyone,

I’m seeing some very strange behavior on the output of the MOSI pin when using the spidev driver provided by Toradex. When performing a loopback test, I noticed that, for certain values, my test application would receive gibberish. This was further confirmed by viewing the MOSI signal on the scope. In my case, with SPI_CPHA and SPI_CPOL both set to zero, writing 0x48 or 0x49 to spidev causes the MOSI signal to go haywire, while the clock and chip select signals remain normal. If these values are sent as single byte transactions, it appears to work fine. But if I send 0x48 or 0x49 repeatedly in a multi-byte transaction, the first byte is correct while the remaining are wrong.

Using the Colibri Evaluation Board, below is the waveform I see for an 8 byte SPI transaction, where only 0x48 is being sent. Looking at the purple signal (MOSI), it is clear that the first byte is sent with no issue. The signal then behaves unexpectedly for the remaining bytes.
1136-spidev-trouble.png

My test application is given below to hopefully allow you to reproduce this issue. It performs 1000 SPI transactions using 0x48 for the data and complains if TX does not match RX when setup for loopback.

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

uint32_t mode = 0, speed = 1000;
uint8_t bits = 8;

void pabort(const char *s)
{
	perror(s);
	abort();
}

void config_spi(int fd)
{
    int ret;

    /*
	 * spi mode
	 */
	ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
	if (ret == -1)
		pabort("can't set spi mode");

	ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
	if (ret == -1)
		pabort("can't get spi mode");

    printf("spi mode: 0x%04x\n", mode);

	/*
	 * bits per word
	 */
	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
	if (ret == -1)
		pabort("can't set bits per word");

	ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
	if (ret == -1)
		pabort("can't get bits per word");

	/*
	 * max speed hz
	 */
	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
	if (ret == -1)
		pabort("can't set max speed hz");

	ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
	if (ret == -1)
		pabort("can't get max speed hz");
}

#define NUM_BYTES 8
void write_spi(int fd)
{
	int status;
	uint8_t tx[NUM_BYTES];
	uint8_t rx[NUM_BYTES];

    struct spi_ioc_transfer xfer = {
        .tx_buf = (unsigned long)tx,
        .rx_buf = (unsigned long)rx,
        .len = NUM_BYTES,
        .delay_usecs = 0,
        .speed_hz = speed,
        .bits_per_word = bits,
    };

    uint8_t val = 0x48;

    for(int i = 0; i < NUM_BYTES; i++)
    {
        tx[i] = val; // transmit the same value NUM_BYTES times
    }

    for(int i = 0; i < 1000; i++)
    {
        status = ioctl(fd, SPI_IOC_MESSAGE(1), &xfer);
        if(status < 0)
        {
            pabort("Cannot write to fd\n");
        }

        for(int j = 0; j < NUM_BYTES; j++)
        {
            if(tx[j] != rx[j])
            {
                printf("Mismatch! Expected: 0x%02x Received: 0x%02x\n", tx[j], rx[j]);
            }
        }

        usleep(500000);
    }
}

int main()
{
    int fd;
    fd = open("/dev/spidev0.0", O_RDWR);
    if(fd < 0)
    {
        pabort("Could not open device\n");
    }

    config_spi(fd);
    write_spi(fd);

    close(fd);
    return 0;
}

And here is my modified portion of the device tree (imx6ull-colibri-eval-v3.dtsi) for enabling spidev:

&ecspi1 {
	status = "okay";

	mcp258x0: mcp258x@0 {
		compatible = "microchip,mcp2515";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_can_int>;
		reg = <0>;
		clocks = <&clk16m>;
		interrupt-parent = <&gpio2>;
		interrupts = <4 IRQ_TYPE_EDGE_FALLING>;
		spi-max-frequency = <10000000>;
		status = "disabled";
	};

	spidev0: spidev@0 {
		compatible = "toradex,evalspi";
		reg = <0>;
		spi-max-frequency = <23000000>;
		status = "okay";
	};
};

This issue is causing me some headaches, as I would ideally like to stream a relatively large file (around 4 MB) using spidev, and I cannot guarantee that these troublesome bytes will not be present in the file. My hunch is that there may be a bug in the underlying SPI driver.

Please let me know if there is anything else I can provide for troubleshooting this issue.

Regards,

Jeff

The issue was solved by removing the jumper (X11AH) connecting SODIMM_90 to SSP_RX0 and measuring the output from the SODIMM side of the header. It’s clear that the MCP2515 was driving that same pin, which was messing with the loopback test. Not sure why this only appeared for values 0x48 and 0x49, perhaps something to do with the command set of the MCP2515.

Simple fix, but hopefully this helps others using the Colibri Evaluation Board.

Thank you for the update. Glad that you solved this problem. If you are not using CAN controller I’d recommend to disconnect its all 4 lines by removing jumpers:

X11AH
X11AI
X11AF
X11AG