Tool/software: Linux
I am using ti-processor-sdk-linux-rt-am335x-evm-05.00.00.15 on an 800MHz AM3354, for a custom board. On my board, I connect the AM3354 to a STM32 MCU via SPI bus. AM3354 acts as the master. There is no other SPI devices on the SPI master. When the MCU need to send data to AM3354, it triggers an interrupt.
On the AM3354 side, I use an interrupt thread to access the SPI bus synchronuously. As the first several read-in data contains the length of the total packet, I split the read MCU procedure to two part, first read-in the part contains the length, then read the total packet according to the length.
The problem is the access of SPI is too slow. I have tried to increase the irq thread, spi0 thread and the application thread priority to SCHED_FIFO, 99. Please see the following picture:

1. From the picture, you can see first the WRITE signal goes low. It is corresponding to the source line gpio_set_value(p_drvdata->boot0, 0) in function ruifu_mcu_controller_write;
2. then the CS is activated, but the delay is also huge, over 200us.
3. Then after the actual write, CS goes high. MCU handles the command and drive the INT low very quickly. But at this time, the system call write(kernel space ruifu_mcu_controller_write) didn't finish yet.
4. After about 100us, the WRITE line goes high, it corresponding to the line gpio_set_value(p_drvdata->boot0, 1) in ruifu_mcu_controller_write. At this point, the user space application finished the command sending to MCU. But obviously, MCU already finished handling the command and is waiting for the following SPI read.
5. After about 900us, AM3354 begin to read the MCU response in the irq thread. It start to read the first part, which contains the length info of the total packet.
6. After another 200us, the remaining part of the total packet are read out.
driver code:
static ssize_t
ruifu_mcu_controller_sync(struct ruifu_mcu_controller_data *p_drvdata, struct spi_message *message)
{
int status;
struct spi_device *spi;
spin_lock_irq(&p_drvdata->spi_lock);
spi = p_drvdata->spi;
spin_unlock_irq(&p_drvdata->spi_lock);
if (spi == NULL)
status = -ESHUTDOWN;
else
status = spi_sync(spi, message);
if (status == 0)
status = message->actual_length;
return status;
}
static inline ssize_t
ruifu_mcu_controller_sync_write(struct ruifu_mcu_controller_data *p_drvdata, size_t len)
{
struct spi_transfer t;
struct spi_message m;
memset(&t, 0, sizeof(struct spi_transfer));
t.tx_buf = p_drvdata->tx_buffer;
t.len = len;
t.speed_hz = p_drvdata->speed_hz;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return ruifu_mcu_controller_sync(p_drvdata, &m);
}
static ssize_t
ruifu_mcu_controller_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
struct ruifu_mcu_controller_data *p_drvdata;
ssize_t status = 0;
unsigned long missing;
/* chipselect only toggles at start or end of operation */
if (count > bufsiz)
return -EMSGSIZE;
p_drvdata = filp->private_data;
missing = copy_from_user(p_drvdata->tx_buffer, buf, count);
if(missing)
status = -EFAULT;
else{
mutex_lock(&p_drvdata->buf_lock);
gpio_set_value(p_drvdata->boot0, 0);
status = ruifu_mcu_controller_sync_write(p_drvdata, count);
gpio_set_value(p_drvdata->boot0, 1);
}
mutex_unlock(&p_drvdata->buf_lock);
return status;
}
const u16 CONST_MCU_PACKET_HEADER_RESERVE = 2;
static irqreturn_t ruifu_mcu_controller_irq(int irq, void *handle)
{
struct ruifu_mcu_controller_data *p_drvdata = handle;
struct spi_transfer x[2];
struct spi_message m;
u16 readsize = CONST_MCU_PACKET_HEADER_LEN-CONST_MCU_PACKET_HEADER_RESERVE;
u16 tx_size, transferred_size;
u16 remaining_bufsize;
int i, j;
int read_error = 0;
#ifdef RUIFU_MCU_CONTROLLER_DEBUG
dev_notice(&p_drvdata->spi->dev, "interrupt triggered\n");
#endif
spi_message_init(&m);
mutex_lock(&p_drvdata->buf_lock);
/* iterate twice, first for header, then for the payload */
for(i=0; i<2; i++)
{
/* first make sure there is enough buffer remained */
if(p_drvdata->head >= p_drvdata->tail) {
remaining_bufsize = bufsiz -1 - (p_drvdata->head - p_drvdata->tail);
}else{
remaining_bufsize = bufsiz -1 + p_drvdata->head - p_drvdata->tail;
}
if(readsize>(bufsiz/2))
{
dev_err(&p_drvdata->spi->dev, "device request to read too many bytes: %d, rx buffer overflow\n", readsize);
readsize = bufsiz/2;
}
if(remaining_bufsize < readsize)
{
dev_err(&p_drvdata->spi->dev, "rx buffer overflow\n");
}
tx_size = readsize;
if((p_drvdata->head+readsize) > bufsiz)
{
/* we need two separate transfers */
tx_size = bufsiz - p_drvdata->head;
}
memset(&x[0], 0, sizeof(struct spi_transfer));
x[0].rx_buf = p_drvdata->rx_buffer+p_drvdata->head;
x[0].len = tx_size;
x[0].speed_hz= p_drvdata->speed_hz;
if(tx_size != readsize)
{
memset(&x[1], 0, sizeof(struct spi_transfer));
x[1].rx_buf = p_drvdata->rx_buffer;
x[1].len = readsize - tx_size;
x[1].speed_hz= p_drvdata->speed_hz;
spi_message_add_tail(&x[0], &m);
if(i==0){
x[1].cs_change = 1;
}
spi_message_add_tail(&x[1], &m);
}else{
if(i==0){
x[0].cs_change = 1;
}
spi_message_add_tail(&x[0], &m);
}
transferred_size = ruifu_mcu_controller_sync(p_drvdata, &m);
if(transferred_size != readsize)
{
dev_err(&p_drvdata->spi->dev, "request rx size: %d, actual size: %d\n", readsize, transferred_size);
}
#ifdef RUIFU_MCU_CONTROLLER_DEBUG
dev_notice(&p_drvdata->spi->dev, "read:");
for(j=0; j<readsize; j++)
{
pr_notice("0x%02x", p_drvdata->rx_buffer[(p_drvdata->head+j)%bufsiz]);
}
#endif
if(i==0){
/* calculate the length for the next packet */
tx_size = p_drvdata->rx_buffer[(p_drvdata->head+CONST_MCU_PACKET_LENGTH_POS)%bufsiz];
tx_size = (tx_size<<8)|p_drvdata->rx_buffer[(p_drvdata->head+CONST_MCU_PACKET_LENGTH_POS+1)%bufsiz];
if(tx_size<CONST_MCU_PACKET_LENGTH_REDUCTION){
dev_err(&p_drvdata->spi->dev, "protocol length voilation detected, packet length error\n");
readsize = CONST_MCU_PACKET_HEADER_RESERVE;
read_error = 1;
}else{
p_drvdata->head = (p_drvdata->head+readsize)%bufsiz;
readsize = (tx_size-CONST_MCU_PACKET_LENGTH_REDUCTION)*2+CONST_MCU_PACKET_HEADER_RESERVE;
}
spi_message_init(&m);
}else{
if(!read_error){
p_drvdata->head = (p_drvdata->head+readsize)%bufsiz;
}
}
}
mutex_unlock(&p_drvdata->buf_lock);
if(!read_error){
wake_up_interruptible(&p_drvdata->wait);
}
return IRQ_HANDLED;
}
My questions are:
1. The delay from step 1 to 2 is too large. Is there any way to reduce it?
2. The delay from 3 to 4 seems so large. Is there any way to reduce it?
3. It seems the interrupt is not serviced in time. The delay from 4 to 5 seems so large. This is the most severe problem I think.
4. How could I improve the two consecutive SPI message transfer? It is the delay from step 5 to 6, corresponding to the two call to ruifu_mcu_controller_sync in ruifu_mcu_controller_irq
I didn't start any other user APP, and from the command top output. The system is not busy.
I found someone use PRU to do the SPI transfer, like github.com/.../wiki. Is that applicable to AM3354? And is there some easier way to improve my case? I feel the PRU method overkill for my case.