EmbLogic's Blog

spi device driver

In the user space

Once you will have this set you can boot your sunxi device and you will have in your dev in /dev/spidevn.0
Transfer size is limited to 64 bytes on sun4i and 128 bytes on sun6i. You have to loop over longer messages in your code. Some SPI devices may require that you prefix each message fragment with a header, other may not. YMMV. Look up transfer diagrams in device datasheet.
Known problems: Using the spidev_test.c example you will receive [spi]: drivers/spi/spi_sunxi.c(L1025) cpu tx data time out!
Using the spidev_fdx.c method it works like a charm! :)

I’ve made a user friendlier library (C functions) to comunicate using SPIdev:
(Note, this library supose the read and write address to be 2 bytes)

#include
#include
#include
#include
#include
#include
#include
#include
#include

char buf[10];
char buf2[10];
extern int com_serial;
extern int failcount;

struct spi_ioc_transfer xfer[2];

//////////
// Init SPIdev
//////////
int spi_init(char filename[40])
{
int file;
__u8 mode, lsb, bits;
__u32 speed=2500000;

if ((file = open(filename,O_RDWR)) < 0)
{
printf("Failed to open the bus.");
/* ERROR HANDLING; you can check errno to see what went wrong */
com_serial=0;
exit(1);
}

///////////////
// Verifications
///////////////
//possible modes: mode |= SPI_LOOP; mode |= SPI_CPHA; mode |= SPI_CPOL; mode |= SPI_LSB_FIRST; mode |= SPI_CS_HIGH; mode |= SPI_3WIRE; mode |= SPI_NO_CS; mode |= SPI_READY;
//multiple possibilities using |
/*
if (ioctl(file, SPI_IOC_WR_MODE, &mode)<0) {
perror("can't set spi mode");
return;
}
*/

if (ioctl(file, SPI_IOC_RD_MODE, &mode) < 0)
{
perror("SPI rd_mode");
return;
}
if (ioctl(file, SPI_IOC_RD_LSB_FIRST, &lsb) < 0)
{
perror("SPI rd_lsb_fist");
return;
}
//sunxi supports only 8 bits
/*
if (ioctl(file, SPI_IOC_WR_BITS_PER_WORD, 8)<0)
{
perror("can't set bits per word");
return;
}
*/
if (ioctl(file, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0)
{
perror("SPI bits_per_word");
return;
}
/*
if (ioctl(file, SPI_IOC_WR_MAX_SPEED_HZ, &speed)<0)
{
perror("can't set max speed hz");
return;
}
*/
if (ioctl(file, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0)
{
perror("SPI max_speed_hz");
return;
}

printf("%s: spi mode %d, %d bits %sper word, %d Hz max\n",filename, mode, bits, lsb ? "(lsb first) " : "", speed);

//xfer[0].tx_buf = (unsigned long)buf;
xfer[0].len = 3; /* Length of command to write*/
xfer[0].cs_change = 0; /* Keep CS activated */
xfer[0].delay_usecs = 0, //delay in us
xfer[0].speed_hz = 2500000, //speed
xfer[0].bits_per_word = 8, // bites per word 8

//xfer[1].rx_buf = (unsigned long) buf2;
xfer[1].len = 4; /* Length of Data to read */
xfer[1].cs_change = 0; /* Keep CS activated */
xfer[0].delay_usecs = 0;
xfer[0].speed_hz = 2500000;
xfer[0].bits_per_word = 8;

return file;
}

//////////
// Read n bytes from the 2 bytes add1 add2 address
//////////

char * spi_read(int add1,int add2,int nbytes,int file)
{
int status;

memset(buf, 0, sizeof buf);
memset(buf2, 0, sizeof buf2);
buf[0] = 0×01;
buf[1] = add1;
buf[2] = add2;
xfer[0].tx_buf = (unsigned long)buf;
xfer[0].len = 3; /* Length of command to write*/
xfer[1].rx_buf = (unsigned long) buf2;
xfer[1].len = nbytes; /* Length of Data to read */
status = ioctl(file, SPI_IOC_MESSAGE(2), xfer);
if (status =1) buf[3] = value[0];
if (nbytes>=2) buf[4] = value[1];
if (nbytes>=3) buf[5] = value[2];
if (nbytes>=4) buf[6] = value[3];
xfer[0].tx_buf = (unsigned long)buf;
xfer[0].len = nbytes+3; /* Length of command to write*/
status = ioctl(file, SPI_IOC_MESSAGE(1), xfer);
if (status < 0)
{
perror("SPI_IOC_MESSAGE");
return;
}
//printf("env: %02x %02x %02x\n", buf[0], buf[1], buf[2]);
//printf("ret: %02x %02x %02x %02x\n", buf2[0], buf2[1], buf2[2], buf2[3]);

com_serial=1;
failcount=0;
}

Usage example:
char *buffer;
char buf[10];

file=spi_init("/dev/spidev0.0"); //dev

buf[0] = 0×41;
buf[1] = 0xFF;
spi_write(0xE6,0x0E,2,buf,file); //this will write value 0x41FF to the address 0xE60E

buffer=(char *)spi_read(0xE6,0x0E,4,file); //reading the address 0xE60E

close(file);

For info it is possible to use all the 12000000 Hz frequency limit transfers.
In the kernel space

If you are coding a driver for a SPI device, it makes most sense to code it as a kernel module. Instead of using /dev/spidevX.X you should register a new (slave) device and exchange data through it. If you are wondering what bus number you should use, you can find available buses by listing /sys/class/spi_master. There should be nodes like spi0, spi1… Number after spi is bus number. What number gets spi master depends on device-tree configuration.

Here is an example of module, that writes 0×00 to SPI when module is initialized and 0xff when uninitialized. It is using bus number 0 and communicating at the speed of 1Hz:

#include
#include
#include

#define MY_BUS_NUM 0
static struct spi_device *spi_device;

static int __init spi_init(void)
{
int ret;
unsigned char ch = 0×00;
struct spi_master *master;

//Register information about your slave device:
struct spi_board_info spi_device_info = {
.modalias = “my-device-driver-name”,
.max_speed_hz = 1, //speed your device (slave) can handle
.bus_num = MY_BUS_NUM,
.chip_select = 0,
.mode = 3,
};

/*To send data we have to know what spi port/pins should be used. This information
can be found in the device-tree. */
master = spi_busnum_to_master( spi_device_info.bus_num );
if( !master ){
printk(“MASTER not found.\n”);
return -ENODEV;
}

// create a new slave device, given the master and device info
spi_device = spi_new_device( master, &spi_device_info );

if( !spi_device ) {
printk(“FAILED to create slave.\n”);
return -ENODEV;
}

spi_device->bits_per_word = 8;

ret = spi_setup( spi_device );

if( ret ){
printk(“FAILED to setup slave.\n”);
spi_unregister_device( spi_device );
return -ENODEV;
}

spi_write(spi_device, &ch, sizeof(ch));

return 0;
}

static void __exit spi_exit(void)
{
unsigned char ch = 0Xff;

if( spi_device ){
spi_write(spi_device, &ch, sizeof(ch));
spi_unregister_device( spi_device );
}
}

module_init(spi_init);
module_exit(spi_exit);

MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“Piktas Zuikis “);
MODULE_DESCRIPTION(“SPI module example”);

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>