I am developing a Linux kernel driver for a device which communicates via SPI for the AM335x using the Sitara SDK v7 (kernel 3.12.10) and the Beaglebone Black development hardware. I am able to send/receive messages via SPI up to 191 bytes in length. Messages > 191 bytes do not transmitted, however. Looking at the source of the TI OMAP2 McSPI kernel driver, it appears that DMA is used for transfers >= 160 bytes. This works correctly if the transfer is < 192 bytes, however any message longer than 192 bytes is never transmitted and thus never received (i.e. the callback function specified in the spi_message struct is never called after calling spi_async()). Some test code which demonstrates the problem is below.
Any help or suggestions are appreciated.
---
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/dma-mapping.h>
#define DRV_NAME "arcspi"
#define DRV_VERSION "0.001"
#define DRV_DESCRIPTION "SPI test module"
#define DRV_AUTHOR "xxx"
#define SPI_BUS 2
#define SPI_BUS_CS0 0
#define SPI_BUS_SPEED 6000000
struct arcspi_control {
struct spi_message SpiMsg;
struct spi_transfer SpiTransfer;
dma_addr_t TxBuffDmaHandle, RxBuffDmaHandle;
u8 *pTxBuff;
u8 *pRxBuff;
u8 bBusy;
};
static struct delayed_work TxWorkStruct;
static struct arcspi_control ArcspiControl;
static struct spi_device *pgSpi_device;
static const u8 TxMessage0[] = { 0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA };
static void arcspi_tx_done(void *arg)
{
ArcspiControl.bBusy = 0;
}
static int arcspi_send(struct spi_device *spi_device)
{
int iRetVal = 0;
spi_message_init(&ArcspiControl.SpiMsg);
ArcspiControl.SpiMsg.complete = arcspi_tx_done;
ArcspiControl.SpiMsg.context = NULL;
ArcspiControl.SpiMsg.is_dma_mapped = 1;
memcpy(ArcspiControl.pTxBuff, TxMessage0, sizeof(TxMessage0));
ArcspiControl.SpiTransfer.tx_buf = ArcspiControl.pTxBuff;
ArcspiControl.SpiTransfer.rx_buf = ArcspiControl.pRxBuff;
ArcspiControl.SpiTransfer.len = sizeof(TxMessage0);
ArcspiControl.SpiTransfer.tx_dma = ArcspiControl.TxBuffDmaHandle;
ArcspiControl.SpiTransfer.rx_dma = ArcspiControl.RxBuffDmaHandle;
spi_message_add_tail(&ArcspiControl.SpiTransfer, &ArcspiControl.SpiMsg);
iRetVal = spi_async(spi_device, &ArcspiControl.SpiMsg);
if (iRetVal)
printk(KERN_ALERT "spi_async() failed\n");
else
ArcspiControl.bBusy = 1;
return iRetVal;
}
static void arcspi_wq(struct work_struct *pWs)
{
int iRetVal, iRxCnt;
if (!ArcspiControl.bBusy)
{
for (iRxCnt = 0; iRxCnt < 8; iRxCnt++)
printk("0x%X ", ArcspiControl.pRxBuff[iRxCnt]);
printk("\n");
iRetVal = arcspi_send(pgSpi_device);
}
else
printk("Async send still in progress\n");
schedule_delayed_work(&TxWorkStruct, HZ * 5);
}
static int arcspi_probe(struct spi_device *spi_device)
{
int iRetVal = 0;
printk("TxMessage is %d bytes\n", sizeof(TxMessage0));
spi_device->dev.coherent_dma_mask = 0xffffffff;
ArcspiControl.pTxBuff = dma_zalloc_coherent(&spi_device->dev, sizeof(TxMessage0), &ArcspiControl.TxBuffDmaHandle, GFP_KERNEL);
if (!ArcspiControl.pTxBuff)
{
iRetVal = -ENOMEM;
goto err_malloc_tx;
}
ArcspiControl.pRxBuff = dma_zalloc_coherent(&spi_device->dev, sizeof(TxMessage0), &ArcspiControl.RxBuffDmaHandle, GFP_KERNEL);
if (!ArcspiControl.pRxBuff)
{
iRetVal = -ENOMEM;
goto err_malloc_rx;
}
INIT_DELAYED_WORK(&TxWorkStruct, (void *)arcspi_wq);
schedule_delayed_work(&TxWorkStruct, HZ * 5);
pgSpi_device = spi_device;
ArcspiControl.bBusy = 0;
return 0;
err_malloc_rx:
dma_free_coherent(&spi_device->dev, sizeof(TxMessage0), ArcspiControl.pTxBuff, ArcspiControl.TxBuffDmaHandle);
err_malloc_tx:
return iRetVal;
}
static int arcspi_remove(struct spi_device *spi_device)
{
cancel_delayed_work(&TxWorkStruct);
if (ArcspiControl.pRxBuff)
dma_free_coherent(&spi_device->dev, sizeof(TxMessage0), ArcspiControl.pRxBuff, ArcspiControl.RxBuffDmaHandle);
if (ArcspiControl.pTxBuff)
dma_free_coherent(&spi_device->dev, sizeof(TxMessage0), ArcspiControl.pTxBuff, ArcspiControl.TxBuffDmaHandle);
return 0;
}
static int __init add_arcspi_device(void)
{
struct spi_master *pSpiMaster;
struct spi_device *pSpiDevice;
struct device *pDev;
char cBuff[64];
int iStatus = 0;
pSpiMaster = spi_busnum_to_master(SPI_BUS);
if (!pSpiMaster)
{
printk(KERN_ALERT "spi_busnum_to_master(%d) returned NULL\n", SPI_BUS);
return -1;
}
pSpiDevice = spi_alloc_device(pSpiMaster);
if (!pSpiDevice)
{
put_device(&pSpiMaster->dev);
printk(KERN_ALERT "spi_alloc_device() failed\n");
return -1;
}
pSpiDevice->chip_select = SPI_BUS_CS0;
snprintf(cBuff, sizeof(cBuff), "%s.%u", dev_name(&pSpiDevice->master->dev), pSpiDevice->chip_select);
pDev = bus_find_device_by_name(pSpiDevice->dev.bus, NULL, cBuff);
if (pDev)
{
spi_dev_put(pSpiDevice);
if (pDev->driver && pDev->driver->name && strcmp(DRV_NAME, pDev->driver->name))
{
printk(KERN_ALERT "Driver [%s] already registered for %s\n", pDev->driver->name, cBuff);
iStatus = -1;
}
}
else
{
pSpiDevice->max_speed_hz = SPI_BUS_SPEED;
pSpiDevice->mode = SPI_MODE_1;
pSpiDevice->bits_per_word = 8;
pSpiDevice->irq = -1;
pSpiDevice->controller_state = NULL;
pSpiDevice->controller_data = NULL;
strlcpy(pSpiDevice->modalias, DRV_NAME, SPI_NAME_SIZE);
iStatus = spi_add_device(pSpiDevice);
if (iStatus < 0)
{
spi_dev_put(pSpiDevice);
printk(KERN_ALERT "spi_add_device() failed: %d\n", iStatus);
}
}
put_device(&pSpiMaster->dev);
return iStatus;
}
static struct spi_driver arcspi_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
.probe = arcspi_probe,
.remove = arcspi_remove,
};
static int arcspi_init(void)
{
int error;
error = spi_register_driver(&arcspi_driver);
if (error < 0)
{
printk(KERN_ALERT "spi_register_driver() failed %d\n", error);
return error;
}
printk("Initializing " DRV_DESCRIPTION " version " DRV_VERSION "\n");
error = add_arcspi_device();
if (error < 0)
{
printk(KERN_ALERT "add_arcspi_device() failed\n");
spi_unregister_driver(&arcspi_driver);
return error;
}
return 0;
}
static void arcspi_exit(void)
{
spi_unregister_device(pgSpi_device);
spi_unregister_driver(&arcspi_driver);
printk(DRV_DESCRIPTION " exiting\n");
}
module_init(arcspi_init);
module_exit(arcspi_exit);
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRV_AUTHOR);
MODULE_VERSION(DRV_VERSION);