PandaBoard SPI

To start using your funny SPI slave device with PandaBoard, you have to go through several steps. I will try to outline these steps in the current how-to. Basically, the process breaks into three pieces – connecting the device, patch the kernel and initiate data exchange with your device. In this document I assume that reader has experience with soldering/connecting wires and is able to build/start a custom Linux kernel.

What is SPI, and what devices can use it?
In short, SPI is a synchronous serial data link standard, named by Motorola, that operates in full duplex mode. Devices communicate in master/slave mode where the master device initiates the data frame. Multiple slave devices are allowed with individual slave select (chip select, CS#) lines. There is a wide range of SPI slave devices - from digital-analog converters to peripherals like ethernet adapters; even SD card can be connected in "SPI mode" to SPI masters. The PandaBoard has four SPI masters, each capable to drive SPI bus up to 48Mhz. Basics of SPI subsystem in linux are well described in Documentation/spi/spi-summary - and this is strongle recommended to read before soldering anything and making changes in code.

Connecting your SPI device
So, the pandaboard has four SPI masters. Likely, you will connect your device to the MCSPI1 master via the J3 expansion connector. Here is the picture of the expansion connector that shows where SPI pins are:



Designated pins are:

Here you can see that there are CS0/CS1/CS2/CS3 lines, so up to four devices can be connected to this master. You probably will connect your device to SIMO, SOMI, CLK and CS0. Don't forget to provide some power supply to it!

With device I've played with, the DS3234, please see the connection photo:



Also, using DS3234 with PandaBoard requires level translator(s) like PCA9306. These level shifters, provided with two reference voltages, do translation of levels bidirectionnaly: if level on SDA1 is high to VREF1, the level on SDA2 becomes high to VREF2, and vice versa. They are needed because PandaBoard provides voltage of 1.8V on control pins, and DS2334 wants voltage from 3.3 to 5V. Here is the sample schematics I used to connect the DS2334 chip:



Now you should have successfully connected and powered your device.

The SPI drivers stack
In most cases, you do not need to write any kernel code and study the kernel API for SPI. The kernel already contains several sort of device drivers, namely:


 * SPI master driver
 * SPI device driver

SPI master drivers are device drivers that responsible to make transfers over the SPI bus; in case of PandaBoard, this is something processor-specific. You will just implicitly use functions provided by these drivers.

SPI device drivers driving the specific SPI slaves. For example, in kernel there are drivers for SPI flashes, SPI ethernet devices, SPI RTC devices and many more. These device drivers know what bytes should be sent to the slave - and how to interpret the response (and masters knows [b]how to send[/b] these bytes and receive them back). For our sample, there is rtc-ds3234 driver, that is able to control our chip. The linux kernel also provides the spidev driver, which is very useful to control the device from the userspace.

Unfortunately, it is very rarely that SPI slaves support any way to automatically discover them. So the configuration of SPI bus should be set manually, and some kernel patching is necessary. The next paragraph explains how to modify your kernel to include the information about your SPI slave device.

Configuring and patching the kernel
Pandaboard's SPI masters are fully supported by the Linux kernel's mcspi driver. That being said, one will need to make only some minor modifications to the PandaBoard's board file (arch/arm/mach-omap2/board-omap4panda.c) in order to use them. Make sure that your kernel has these drivers compiled - check that CONFIG_SPI, CONFIG_SPI_MASTER and CONFIG_SPI_OMAP_24xx (In Ubuntu 12.04 source its CONFIG_SPI_OMAP24XX) are all set to 'y' or 'm' (in case of modules, one might have to rebuild and load the modules before one will be able to play with your SPI device). Then, the arch/arm/mach-omap2/board-omap4panda.c should be changed as follows:

0. On Ubuntu 12.04 you need to also add: #include 

1. Declare the array of structures spi_board_info that matches your device:

static const struct spi_board_info panda_spi[] __initconst = { {               .modalias = "spidev", .bus_num = 1, .chip_select = 0, .max_speed_hz = 1000, .mode = SPI_MODE_1, }, {               .modalias = "ds3234", .bus_num = 1, .chip_select = 1, .max_speed_hz = 400000, }, }; 2. Create the function to mux the pins and register the spi_board_info structure with SPI core:

static void __init panda_spi_devices_init(void) { /* muxing pins might be only required if       * you've connecting * device to CS other than CS0 *** This worked for me, add: omap_mux_init_signal("mcspi1_cs1", OMAP_PIN_OUTPUT); // sub "mcspi1_cs2 for CS2 etc.         to activate CS1 on header pin #10.          Make sure your call to panda_spi_devices_init is after omap_serial_init          in omap4_panda_init function.        *** using CS[1-3] will break UART1 ***        */       spi_register_board_info(panda_spi, ARRAY_SIZE(panda_spi));  }

3. Call this function from omap4_panda_init, like

panda_spi_devices_init

A few comments about code snippets above:

In 1, fields of the structure are:
 * modalias = “spidev” means that device will be controlled by spidev driver. You might want to reuse some driver from the kernel (for our example, the driver "ds3234" will work fine) or develop your own;
 * bus_num = 1 means that device is connected to the SPI master with number 1, the MCSPI1 in our case;
 * chip_select = 0 means that device uses the CS0 line, 1 means using CS1, etc.
 * max_speed_hz = 1000 is probably not the best timing in terms of performance, so you should consider to change this to, say, 4Mhz for our DS3234
 * mode – to set it correctly refer to the documenation of your SPI device. The DS3234 supports modes 1 and 3 (you have read the Documentation/spi/spi-summary, right?). If you plan to use the ds3234 driver, this field will be overwritten by the driver, so no need to worry about it.

Now everything is in place, let's compile your kernel and/or modules and try to start it on panda.

Accessing the device
If you decided to reuse a driver from the linux kernel, the access details will be hidden from you – in our example, the rtc-ds3223 driver will perform all SPI bus exchange and provide you with /dev/rtc interface. If you want to write your own device driver (are you sure that you want to do this?) please refer to samples from the kernel, and do not hesitate to ask questions. This section will be only useful if you decided to use spidev. Its interface is well-defined in Documentation/spi/spidev, however here is a simple example how to access a device (still our example, DS3224) from a python script. import os, sys def spidev_test(devnode): # 	# very simple. no exception handling, just five steps: # 	# 1. Open device spi = os.open(devnode, os.O_RDWR, 0777) # 2. Write single zero byte write_bytes = "\x00" w_res = os.write(spi, write_bytes, len(write_bytes)) if written != len(write_bytes): raise Exception("Wrote less bytes than requested") # 3. read 8 bytes back rd = 8 rd_bytes = os.read(spi, rd) if len(rd_bytes) != rd: raise Exception("Read less than excpected") # 4. print the result print ["%x" % ord(rd) for rd in rd_bytes] # 5. close the handle os.close(spi) if __name__ == "__main__": spidev_test("/dev/spidev1.0") sys.exit(0) else: print "How do you want to use spidev-test?"