le_spi_WriteHD() doesn't work right for SPI with 24 bits per word


#1

Hi,
my application runs on a custom board with WP76xx and a Legato system based upon 18.08.

I use spidev1.0, and at startup my application calls le_spi_Configure(spiHandle, mode, bitsPerWord, speed, msb)
with following parameters:
mode = 3
speed = 960000
msb =0
The SPI data is sent with le_spi_WriteHD(spiHandle, buffer, length).
I used an oscilloscope to check the output signals for different possibilities of the ‘bitsPerWord’ parameter and ‘length’ parameter.

4 bits per word
length = 2, results in 2 correct SPI words
length = 8, results in 8 correct SPI words

10 bits per word
length = 2, results in 1 correct SPI words
length = 8, results in 4 SPI words (???)

16 bits per word
length = 2, results in 1 correct SPI word
length = 8, results in 4 correct SPI words

24 bits per word
length = 4, results in 2 SPI words, but, the highest 16 bits of the 2nd word are 0
length = 8, results in 3 SPI words, but, the highest 8 bits of the 3rd word are 0
When the length is 1, 3, 5, 6, 9 or 10,
I always get the error “Transfer failed with error -1 : 22 (Invalid argument) … can’t send spi message”

Apparently, it’s impossible to send SPI words with a length of 24 bits.
It would be logical that the length should be a multiple of ‘3’, but, even that doesn’t work.

Is there something wrong with the spidev kernel implementation ?

Greetings,

annaertd


#2

Hello,
Can we have more details about your program - application? and provide us the result of the “fwupdate query”
command, please.
Regard


#3

Hi,

I’ve created a small SPI test program which runs on my mangOH Green board with a WP76xx controller.
fwupdate query returns:
Firmware Version: SWI9X07Y_02.18.05.00 000000 jenkins 2018/07/19 17:40:21
Bootloader Version: SWI9X07Y_02.18.05.00 000000 jenkins 2018/07/19 17:40:21
Linux Version: 3.18.44 #2 PREEMPT Thu Sep 20 16:20:02 UTC 2018

My mangOH system and testapplication have been built with the most recent Sierra Wireless firmware release package R10.1.1 (Legato 18.06.3).

The testapplication uses spidev1.0 and configures the SPI bus with
le_spi_Configure(spiHandle, 3, 24, 960000, 0);

I try to use API function le_spi_WriteHD( handle, writeDataPtr, writeDataSize) to put 24 bit data-words on the SPI bus.
So, basically you need a pointer to an array of uint8_t, and a datalength as parameters.
I assumed that a 24 bit data-word would need an arraylength which is a multiple of 3 :slight_smile:
Apparently, the API function returns an error in that case.

My testapplication tries to call le_spi_WriteHD() with several lengths.
The data array contains a list of byte values 0x0F.

  • length = 3 ==> error
  • length = 4 ==> LE_OK, but, I get two 24 bit data-words on the bus
  • length = 6 ==> error
  • length = 8 ==> LE_OK, but, I get three 24 bit data-words on the bus
  • length = 9 ==> error

In attachment, you can find:

  • testapplication sources
  • application log-file (spiService.adef contains LE_LOG_LEVEL = DEBUG)
  • screenshots from the oscilloscope

In the screenshots you can see that the SPI bus contains 24 bit data-words.
For an array length of 4, the data-words are 0x0F0F0F and 0x00000F
For an array length of 8, the data-words are 0x0F0F0F, 0x0F0F0F and 0x000F0F

Why does the API function le_spi_WriteHD() only work with arrays of 4 or 8 bytes? (related to uint32_t handling in Legato drivers?)
Why does this fail for arrays of 3 bytes, which is needed to create a 24 bit data-word?

spitest - log.txt (3.5 KB)
test.tar.gz (1.9 KB)

SPI%2024bits%20per%20word%20-%20overview
SPI%2024bits%20per%20word%20-%20detailed%20view
SPI%2024bits%20per%20word%20-%20use%204%20data%20bytes
SPI%2024bits%20per%20word%20-%20use%208%20data%20bytes

Greetings,
annaertd


#4

Hi,

I just want to explain something :

In the case :
length = 4 ==> LE_OK, but, I get two 24 bit data-words on the bus … it’s normal to get 2x24 data words because the number of bytes-arrays is 4, it means one word contains 3 bytes + another word that contains 1 byte and the 2 bytes that remain are at 0.

length = 8 ==> LE_OK, but, I get three 24 bit data-words on the bus … the same principle, you have 8 bytes to send, so you need 2 words (0x0F x3) and the 3rd with (0x0F x1)

** it is always better to use bitsPerWord = 8, 16 or 24

@Vasantha_lax have you an idea why when we use 24 bits per word + [NUM_ARRAY_MEMBERS (write_buffer_tx) = 1, 3, 5, 6, 9 or 10 doesn’t work.

I tracked the issue “Transfer failed with error -1 : 22 (Invalid argument) … can’t send spi message” in le_spiLibrary.c and I found the source of this message :

> le_result_t le_spiLib_WriteHD
> (
>     int fd,                   ///< [in] open file descriptor of SPI port
>     const uint8_t* writeData, ///< [in] command/address being sent to slave
>     size_t writeDataLength    ///< [in] number of bytes in tx message
> )
> {
>     int transferResult;
>     le_result_t result;
> 
>     struct spi_ioc_transfer tr[] =
>     {
>         {
>             .tx_buf = (unsigned long)writeData,
>             .rx_buf = (unsigned long)NULL,
>             .len = writeDataLength,
>             // .delay_usecs = delay_out,
>             .cs_change = 0
>         }
>     };
> 
> 
>     LE_DEBUG("Transferring this message...len: %zu", writeDataLength);
>     for (size_t i = 0; i < writeDataLength; i++)
>     {
>         LE_DEBUG("%.2X ", writeData[i]);
>     }
> 
>     transferResult = ioctl(fd, SPI_IOC_MESSAGE(1), tr);
>     if (transferResult < 1)
>     {
>         LE_ERROR("Transfer failed with error %d : %d (%m)", transferResult, errno);
>         LE_ERROR("can't send spi message");
>         result = LE_FAULT;

I think it’s a problem of the parameter not supported in transferResult.


#5

Hi,
I found the reason why function call ioctl(fd, SPI_IOC_MESSAGE(1), tr) fails when using 24 bits per word, in case the writeDataLength is 1, 3, 5, 6, 9 or 10.

The yocto-2.2 source tree contains a file /kernel/drivers/spi/spi.c with the function below:
static int __spi_validate(struct spi_device *spi, struct spi_message *message)
{

	/*
	 * SPI transfer length should be multiple of SPI word size
	 * where SPI word size should be power-of-two multiple
	 */
	if (xfer->bits_per_word <= 8)
		w_size = 1;
	else if (xfer->bits_per_word <= 16)
		w_size = 2;
	else
		w_size = 4;

	/* No partial transfers accepted */
	if (xfer->len % w_size)
		return -EINVAL;


}

So, this means we can only use buffer lengths of multiples of 4 for the 24 bits SPI mode.

Does anyone know how to fix the last part of this issue?
How can we put 1 24-bit SPI message on the bus if the buffer contains 4 data bytes?
As you can see in the images above, the current SPI implementation always creates 2 messages: it adds an extra 24-bit message where the upper 16 bits are zero.

Greetings,
annaertd