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.
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