This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/OMAP-L138: SPI interface reads out slow from Linux application

Part Number: OMAP-L138

Tool/software: Linux

We have three devices on an SPI bus.  The master is the OMAP/ARM processor which drives the clock at 2Mhz (this has been verified with an oscilloscope).  The two slave devices are on chipselect 2 and 3 on the OMAP.  Data is being sent and received successfully.  The issue is that the rate at which we are receiving data is an order of magnitude slower than it should be.  Instead of reading the data at 3584Hz (our ideal frequency), we are reading it out at 177Hz.  When we are not sending/receiving data, I have verified that the polling loop itself is running at 3584Hz.  When I turn on the function to read out the data from the devices, each device takes ~5.7 miliseconds to poll and receive the full data stream.  The request data size is 32 bytes and the maximum response size from either device is 32 bytes.  So a total round trip poll/response is 64 bytes or 512 bits. 

We are not using DMA partly because the documentation on how to use DMA is so poor.  This may be a solution if there are blocking conditions using the SPI to read data as we are in this instance.

Any thoughts on what the issue may be?  

Thank you,

Erik Jones

Supporting information:

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

Processor: SOMOMAPL138-10-1602QHIR-B

Compiler package: Codesourcery Sourcery G++ Lite  version 4.3.3 (Sourcery G++ Lite 2009q1-203) 

RootFS: arago 

SPI Application Code:


bool SPIController::sendData(spiDevices spiDevice, SPIRequestBase *Command) {
int spiDev = open(spiDeviceTable[spiDevice].deviceName.c_str(), O_RDWR);
int mode = SPI_MODE_0;
int res = 0;
struct spi_ioc_transfer spiControl;
memset(&spiControl, 0, sizeof(spiControl));

setCS(spiDevice, true);

char *xmitBuffer = new char [spiDeviceTable[spiDevice].max_packet_length];
memset(xmitBuffer, 0, spiDeviceTable[spiDevice].max_packet_length);
memcpy(xmitBuffer, Command->getPacketbuffer(), spiDeviceTable[spiDevice].max_packet_length);

char *recvBuffer = new char [spiDeviceTable[spiDevice].max_packet_length];
memset(recvBuffer, 0, spiDeviceTable[spiDevice].max_packet_length);

res = ioctl(spiDev, SPI_IOC_WR_MODE, &mode);

spiControl.tx_buf = (unsigned long)xmitBuffer;
spiControl.rx_buf = (unsigned long)recvBuffer;
spiControl.len = spiDeviceTable[spiDevice].max_packet_length;
spiControl.speed_hz = SPIController::spiClock;
spiControl.cs_change = 0;
spiControl.bits_per_word = 8;
spiControl.delay_usecs = 0;

if(spiDevice == PIM_DATALOGGER)
xmitBuffer[spiDeviceTable[spiDevice].max_packet_length-1] = 0xAA;

res = ioctl(spiDev, SPI_IOC_MESSAGE(1), &spiControl);

//Detect byteshift
if(spiDevice == PIM_PLATFORM && *(recvBuffer + spiDeviceTable[spiDevice].max_packet_length - 1) == 0) {
memmove(recvBuffer+1, recvBuffer, spiDeviceTable[spiDevice].max_packet_length-1);

*recvBuffer = 0;

//Sign extension
if( (*(recvBuffer+1) & 0x80) > 0 )
*recvBuffer = 0xFF;
}

SPIResponseBase *pr = SPIResponseBase::Dispatch(recvBuffer, spiDevice, spiDeviceTable[spiDevice].max_packet_length);

SPIController::currentTelemetry.u16_DatLog_ActiveFile_Index_HB = DataLogController::getFileNum() >> 16;
SPIController::currentTelemetry.u16_DatLog_ActiveFile_Index_LB = DataLogController::getFileNum() & 0xFF;
SPIController::currentTelemetry.u8_ConfigFile_ID = NetworkServerController::getConfigFileID();
SPIController::currentTelemetry.u8_mode = NetworkServerController::getCurrentMode();
SPIController::currentTelemetry.u32_counter++;

SPIController::currentTelemetry = *pr;

close(spiDev);

setCS(spiDevice, false);

delete [] xmitBuffer;
delete [] recvBuffer;

delete pr;

return true;

}

bool SPIController::setCS(spiDevices spiDevice, bool enable) {
std::string devicePin, enableStr;

enableStr = enable ? "0" : "1";

switch (spiDevice) {
case PIM_PLATFORM:
devicePin = SPIController::SPI_CS2;
break;
case PIM_DATALOGGER:
devicePin = SPIController::SPI_CS3;
break;
}

std::string setval_str = "/sys/class/gpio/gpio" + devicePin + "/value";
ofstream setvalgpio(setval_str.c_str()); // open value file for gpio
if (setvalgpio < 0){
cout << " OPERATION FAILED: Unable to set the value of GPIO"<< devicePin <<" ."<< endl;
return false;
}

setvalgpio << enableStr ;//write value to value file
setvalgpio.close();// close value file
return true;
}

Kernel: From ti-dvsdk-omap138-evm 4.02.0.6 - 2.6.33-rc4-psp03.2.0.14

SPI devices on chipselects 2 & 3. Clock set at 2Mhz via hardcoded SPI driver modifiecation. In mach-davinci/board-da850-evm.c:

static struct spi_board_info da850_spi_board_info[] = {
[0] = {
.modalias = "m25p80",
.platform_data = &spi_flash_data,
.mode = SPI_MODE_0,
.max_speed_hz = 2000000, /* max sample rate at 3V */
.bus_num = 1,
.chip_select = 0,
},


/* Programmable Isolation Mount */
[1] = {
.modalias = "spidev",
.mode = SPI_MODE_0,
.max_speed_hz = 2000000, /* max sample rate at 3V */
.bus_num = 1,
.chip_select = 2,
},
[2] = {

.modalias = "spidev",
.mode = SPI_MODE_0,
.max_speed_hz = 2000000, /* max sample rate at 3V */
.bus_num = 1,
.chip_select = 3,
},

};

  • Hi,

    Try assigning DMA tx & rx channels in drivers/spi-davinci.c. Currently these are set as:
    resource_size_t dma_rx_chan = SPI_NO_RESOURCE;
    resource_size_t dma_tx_chan = SPI_NO_RESOURCE;

    The spi dma rx & tx channel mapping is defined in arch/arm/mach-davinci/devices-da8xx.c.

    Additional information on how to use dma apis can be found in Documentation/DMA-API.txt, Documentation/DMA-API-HOWTO.txt, Documentation/DMA-attributes.txt, Documentation/dma-buf-sharing.txt & Documentation/dmaengine.txt

    Bet Regards,
    Yordan
  • HI Yordan,

    Thank you for the suggestions.  Do you think it's possible to enable DMA without having to recompile the kernel?  Our unit is on the space station currently and we aren't sure that we can update the kernel.  In our SOM configuration, the boot manager (U-boot) is used to download and store the kernel image.  I believe there is a way to update the flash copy of the kernel from Linux, but we haven't done that before.  

    Thank you for your time,

    Erik

  • Hi Erik,

    There are some options:
    1. Try and create userspace dma driver, and use the channels in your spi application. Some documentation about user space dma mapping can be found on the following community discussions:
    stackoverflow.com/.../linux-kernel-device-driver-to-dma-from-a-device-into-user-space-memory
    forums.xilinx.com/.../Linux DMA from User Space-public.pdf
    github.com/.../udmabuf

    2. You can cross compile the SPI driver from your kernel sources with the DMA enabled, then transfer the generated .ko file to the board, rmmod the old spi driver & insmod the new one (with dma enabled).

    This could be a tricky task since, I guess, you cannot afford to do trial and error... so you may need to test whichever approach you choose on a board on site, and once verified that it works you can transfer it on the unit on the space station.

    Best Regards,
    Yordan