SPI iMX25 burst length is failing

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

SPI iMX25 burst length is failing

2,051 Views
SantiagoVillafu
Contributor II

Hello

I am working with a CSPI peripheral on the i.MX25. I need to transfer data in 13 bit packets and in 123 bit packets. Unfortunately, the linux driver is having an unexpected behavior when configuring to send those packets. It sends repeated bursts of data and trash within packets... only when the bits per transfer configuration value is > 8.

I wrote the following basic functions in order to test the SPI linux driver...

------------

/**
 * This function initializes the SPI configuration.
 * @return a flag to indicate success or failure
 */
bool_t SPI_Init(void)
{
   /* SPI setup */
   char* spi_device = "/dev/spidev2.1";
   uint8_t mode = SPI_MODE_2; /* CLK Idle in 1, Phase 0 */
   uint8_t lsb = 0; /* this is not configurable, the iMX processor SPI does not support it */
   uint8_t bits = 123; /* First packet is sent with 123 bits */
   uint32_t speed = FUT_SPI_SPEED; /* Not very flexible */

   int fd;
   int result;
   int sd1_data3_config = 0;

   fd = open(spi_device, O_RDWR);
   PBC_Ensure_1(fd > 0, "SPI Open %s failed", spi_device);

   /* SPI Mode */
   result = ioctl(fd, SPI_IOC_WR_MODE, &mode);
   PBC_Ensure(result >= 0, "SPI Mode WR failed");

   result = ioctl(fd, SPI_IOC_RD_MODE, &mode);
   PBC_Ensure(result >= 0, "SPI Mode RD failed");
   Tr_Info_Lo_1("SPI Mode %02X", mode);

   /* Bits per word */
   result = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
   PBC_Ensure(result >= 0, "SPI bits per word WR failed");

   result = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
   PBC_Ensure(result >= 0, "SPI bits per word RD failed");
   Tr_Info_Lo_1("SPI bits per word %u", bits);

   /* Speed */
   result = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
   PBC_Ensure(result >= 0, "SPI speed setting WR failed");

   result = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
   PBC_Ensure(result >= 0, "SPI speed setting RD failed");
   Tr_Info_Lo_1("SPI speed %u", speed);

   /* LSB */
   result = ioctl(fd, SPI_IOC_WR_LSB_FIRST, &lsb);
   PBC_Ensure(result >= 0, "SPI LSB WR failed");

   result = ioctl(fd, SPI_IOC_RD_LSB_FIRST, &lsb);
   PBC_Ensure(result >= 0, "SPI LSB RD failed");
   Tr_Info_Lo_1("SPI lsb %02X", lsb);

   /* Manual CS pin */
   result = ioctl(fd, SPI_IOC_WR_DEA500_CHIP_SELECT, &sd1_data3_config);
   PBC_Ensure(result >= 0, "SPI CS setup failed");

   /* Lower CS pin */
   sd1_data3_config = 1;
   result = ioctl(fd, SPI_IOC_WR_DEA500_CHIP_SELECT, &sd1_data3_config);
   PBC_Ensure(result >= 0, "SPI lowering CS failed");

   close(fd);

   return (result >= 0);
}


/**
 * Send 13 bits to the display
 * @param [in] data - dimming data to send with latch mode bits
 */
void Send_13_bits(uint16_t data)
{
   /* SPI setup */
   char* spi_device = "/dev/spidev2.1";
   uint8_t bits;

   int fd;
   int result;
   int sd1_data3_config;
   struct spi_ioc_transfer xfer;
   int32_t i;

   uint32_t tr[5] = {0xC3FFAA55, 0xC3FFAA55, 0xC3FFAA55, 0xC3FFAA55, 0xC3FFAA55};
   uint8_t c;

   memset(&xfer, 0, sizeof(struct spi_ioc_transfer));
   xfer.len = 16;
   xfer.tx_buf = (unsigned long)&tr[1]; /* using a middle byte to avoid trash... didn't work */
   /* the following line is somehow ignored by the driver */
   /* xfer.bits_per_word = 32; */

   for(i = -16; i < 16; ++i)
   {
      c = *((uint8_t*)&tr[1] + i); /* print out neighbor bytes */
      Tr_Info_Lo_2("%d: %02X", i, c);
   }

   fd = open(spi_device, O_RDWR);
   PBC_Ensure_1(fd > 0, "SPI Open %s failed", spi_device);

   /* Bits per word */
   bits = 123;
   result = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
   PBC_Ensure(result >= 0, "SPI bits per word WR failed");

   /* Raise CS pin */
   sd1_data3_config = 0;
   result = ioctl(fd, SPI_IOC_WR_DEA500_CHIP_SELECT, &sd1_data3_config);
   PBC_Ensure(result >= 0, "SPI raising CS failed");

   /* Send TX data */
   result = ioctl(fd, SPI_IOC_MESSAGE(1), &xfer);
   PBC_Ensure(result >= 0, "SPI transfer failed");

   /* Lower CS pin */
   sd1_data3_config = 1;
   result = ioctl(fd, SPI_IOC_WR_DEA500_CHIP_SELECT, &sd1_data3_config);
   PBC_Ensure(result >= 0, "SPI lowering CS failed");

   close(fd);
}

-----------------

I ran tests with that source code. I created an array of 0xC3FFAA55 data (repeated 5 times) to make sure that no trash was sent, only known neighbor bytes should be seen:

uint32_t tr[5] = {0xC3FFAA55, 0xC3FFAA55, 0xC3FFAA55, 0xC3FFAA55, 0xC3FFAA55};
...
xfer.tx_buf = (unsigned long)&tr[1]; /* using a middle byte to avoid trash... didn't work */

But unfortunately, the tests continued sending unexpected trash. If bits per word is lower or equal to 8, everything is fine. If bits per word is greater than 8, unexpected data is sent.

I am attaching a series of pictures that show how data is being sent. Each picture contains "bits" which are bits per word (SPI_IOC_WR_BITS_PER_WORD) and "len" which is a number I still don't understand (xfer.len Bytes? Words? Transfers?). Please take a look to the pictures.

Those pictures were generated by modifying the function Send_13_bits() especifically in these lines:

xfer.len = 16; "len"
...
bits = 123; "bits"
result = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);


I would like to ask some questions about the behavior of the SPI driver...

1) Whenever I change the value "xfer.bits_per_word" in the "spi_ioc_transfer" structure, it is always ignored. Why does the driver do that?

2) I read online that the other values of the "spi_ioc_transfer" structure should be left at zero if we don't want to override the SPI current setup, is that correct? I see that "mxc_spi_test.c" makes use of that structure but it never cleans it (memset) before sending it (see function static int transfer()).

3) Is it OK to send "xfer.rx_buf" with a NULL pointer/address? I don't need to read anything from SPI. I've placed a buffer pointer in .rx_buf but there are no changes in the sent data whatsoever.

4) Why does the driver send "phantom/repeated" bursts of data when bits (bits per word) is greater than 8? Why is the first burst perfect, and the next one(s) contain trash?

Regarding this question, there is a note in the readme.txt file (rpm/BUILD/imx-test-10.07.11/test/mxc_spi_test/readme.txt):

" Note:
The spidev driver copies len of bytes to tx buffer, and sends len of units to
SPI device. So it will send much more data to the SPI device when bits per
word is larger than 8, we only care the first len of bytes for this test."

Why does the driver have to behave in that way?

5) I've read the iMX25 CSPI specs and it says that the module can actually send up to 4096 bits of consecuent data. Why does the Linux driver has a maximum of 4096 bytes when sending 8 or less bits bursts in a single SPI_IOC_MESSAGE(1) transfer?  (see #define BUF_MAX_SIZE       0x1000 in mxc_spi_test.c). Why is the variable .bits_per_word declared as a uint8 (1 to 255 bits to send)?

Please forgive me if I'm making so many questions, but I can't figure out why the driver is behaving in this way.


Thanks in advance!

Original Attachment has been moved to: 3-spifailurephotos.zip

Tags (2)
0 Kudos
3 Replies

881 Views
SantiagoVillafu
Contributor II

I received a recommendation to mask the non-used bits of the 32 bit data that was to be sent... this is the code... there were no improvements at all. :smileyconfused:

---------------------------------------------------------------------------

/**
* Send 13 bits to the display
* @param [in] data - dimming data to send with latch mode bits
*/
void Send_13_bits(uint16_t data)
{
   /* SPI setup */
   char* spi_device = "/dev/spidev2.1";
   uint8_t bits;

   int fd;
   int result;
   int sd1_data3_config;
   struct spi_ioc_transfer xfer;
   int32_t i;

   uint32_t tr[5] = {0xC3FFAA55, 0xC3FFAA55, 0xC3FFAA55, 0xC3FFAA55, 0xC3FFAA55};
   uint8_t c;

   memset(&xfer, 0, sizeof(struct spi_ioc_transfer));
   xfer.len = 16;
   xfer.tx_buf = (unsigned long)&tr[1]; /* using a middle byte to avoid trash... didn't work */
   /* the following line is somehow ignored by the driver */
   /* xfer.bits_per_word = 32; */

   for(i = -16; i < 16; ++i)
   {
      c = *((uint8_t*)&tr[1] + i); /* print out neighbor bytes */
      Tr_Info_Lo_2("%d: %02X", i, c);
   }

   fd = open(spi_device, O_RDWR);
   PBC_Ensure_1(fd > 0, "SPI Open %s failed", spi_device);

   /* Bits per word */
   bits = 123;
   result = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
   PBC_Ensure(result >= 0, "SPI bits per word WR failed");

   /* Raise CS pin */
   sd1_data3_config = 0;
   result = ioctl(fd, SPI_IOC_WR_DEA500_CHIP_SELECT, &sd1_data3_config);
   PBC_Ensure(result >= 0, "SPI raising CS failed");

   /* Send TX data */
   result = ioctl(fd, SPI_IOC_MESSAGE(1), &xfer);
   PBC_Ensure(result >= 0, "SPI transfer failed");

   /* Lower CS pin */
   sd1_data3_config = 1;
   result = ioctl(fd, SPI_IOC_WR_DEA500_CHIP_SELECT, &sd1_data3_config);
   PBC_Ensure(result >= 0, "SPI lowering CS failed");

   close(fd);
}

0 Kudos

881 Views
SantiagoVillafu
Contributor II

Is there a way to directly contact Freescale to ask for help? :smileyinfo:

0 Kudos

881 Views
Nouchi
Senior Contributor II

Hello,

You can send a service request, on Freescale website>>Support>>Technical Service Request.

0 Kudos