Implementing SPI on an OMAP-based board design
What are the necessary kernel changes?The SPI bus and USB, for example, are different. USB devices provide information about themselves and can be queried by the bus controller driver. SPI devices, on the other hand, cannot do this. The SPI master driver never has a chance to know what is connected to some specific CS# (and even know if there is something connected) unless it is provided with some information.
The board-dependent code does the registration by calling the function spi_register_board_info. It takes two parameters: list of devices connected and the size of this list. The example might look similar to the following:
static const struct spi_board_info panda_spi[] __initconst = {
{
.modalias = "spidev",
.bus_num = 1,
.chip_select = 1,
.max_speed_hz = 1000,
.mode = SPI_MODE_1,
}, {
.modalias = "ds3234",
.bus_num = 1,
.chip_select = 0,
.max_speed_hz = 400000,
},
};
Also, register this spi_board_info array in the function omap4_panda_init:
spi_register_board_info(panda_spi, ARRAY_SIZE(panda_spi));
In this code snippet, two SPI devices are registered. The first device is something generic attached to CS#1 on the SPI bus 1 (McSPI1) controlled by the “spidev.” It has a maximum speed of 1kHz, which is just for the sample because the real speed can be up to 48MHz.
The second device is connected to CS#0, has a maximum speed of 400kHz (enough for RTC) and is controlled by the “ds3234” driver. It is a good “SPI functional device driver,” and is doing all the work to exchange data over the SPI bus.
Working with a device attached to the PandaBoard
First of all, because the kernel has been modified, it has to be recompiled. It is a good idea to make sure that all the necessary options are switched on:
CONFIG_SPI = y
CONFIG_SPI_MASTER = y
CONFIG_SPI_OMAP_24xx = y
CONFIG_RTC_DRV_DS3234 = y
CONFIG_SPI_SPIDEV = y
The last option can be set to “m,” meaning this will be a separate module. In this case, do not forget to update the modules directory. After the kernel is compiled and started, the following messages will appear:
ds3234 spi1.0: rtc core: registered ds3234 as rtc0
This means that the ds3234 driver has been attached to the SPI device, and exposes its RTC interface via /dev/rtc0. Now one can run any tools to access the RTC (hwclock is a very useful utility):
# hwclock –show
You can also access the device via the spidev interface, which allows userspace application to send/receive bytes on its own, for example, using this 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 expected")
# 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?"
The script will try to send a zero byte to the device, and print the response of 8 bytes. If one attaches the spidev driver instead of ds3234 to the DS3234 device (in the code snipped above, just replace “ds3234” with “spidev”), the output might look as follows:
# python spi-sample.py
['5', '10', '12', '2', '1', '5', '12']
This is just a sample to demonstrate the SPI exchange. Parsing of this data is usually done by the ds3234 driver. However, it might be a good starting point to start development of one’s own protocol driver, especially if something new and unknown was connected to the Linux kernel.


Loading comments... Write a comment