Browse Month

April 2016

Using spidev on the Wandboard

No matter how well documented a particular device is, I usually stumble trough a considerable number of trial-and-error iterations whenever I try to do some development on it. That’s why, for the initial stages of development, I normally try to use some computer on board platform running an operating system, rather than creating my own microcontroller code from scratch.

Unfortunately, there’s a good chance that you’ll find issues no matter what path you follow, and trying to use the SPI bus on my Wandboard was no exception.

Just to make it clear, I think the Wandboard is a great development platform, very stable and well documented. But I faced a couple of problems: first, the SD image I downloaded had no spidev support out of the box, and second I messed with it and then read the manual, in that order. However, the effort spent on getting spidev going pays off very quickly, giving you the ability to talk to the SPI bus from userspace.

Note: This method uses device tree, so it won’t work with kernel versions < 3.10

Loading the spidev module

The first thing you want to do is try to load spidev in case it is already on your installation. To do so, make sure the module is loaded:

$ sudo modprobe spidev

At this point I got a “not found” error. So time to get the module myself. Since I didn’t have any prebuilt kernel in my system, I downloaded the kernel source (https://github.com/wandboard-org/linux selecting the right branch) and compiled a new kernel for the wandboard. The reason why I opted to upgrade the kernel instead of just adding the module is that the preinstalled kernel had a release version that didn’t completely match any of the available ones at github.

Building a kernel can be a whole topic on itself, but I’ll try to give a quick overview of what I did.

Once the kernel is downloaded in the Wandboard, get the kernel configuration ready:

$ make ARCH=arm wandboard_defconfig

Now we need to enable spidev as a module in the configuration. There are many ways to do this, probably the easiest is just to edit .config manually; however, I like using ncurses config:

$ make menuconfig

If you get an error, chances are you are missing libncurses headers. Run

$ sudo apt-get install libncurses5-dev

(or similar) to fix it.
The linux installation on my Wandboard was using zImage for booting. So I ran:

$ make ARCH=arm zImage
$ make ARCH=arm modules
$ sudo make modules_install

(You probably don’t need the “ARCH=arm” bit, especially since we are not cross compiling)

Make sure you backup the old zImage before replacing it with the new one. If things get bad and the new zImage fails to boot, you can always get the SD card off the Wandboard and restore the old zImage using a card reader on your desktop machine.

Finally, you should be able to modprobe spidev on your system. Have a look at /dev/, and see if you can find any file called spidev1.0 or similar. If you don’t see any, as in my case, it means you are not done yet… The following section may still be useful even if you have the file, to make sure your pin configuration is fine.

The device tree

What happens is, you have the spidev module with all the SPI stuff in (we didn’t talk about the IMX SPI driver, but that comes built-in by default), but spidev doesn’t know which wandboard pins to use. That’s because we haven’t defined them in the device tree yet.

In case you don’t know what I am talking about: in the olden days, there used to be lots of C files in the kernel describing the different computer boards out there. Those files have been replaced by device tree files. From devicetree.org:

The Device Tree is a data structure for describing hardware. Rather than hard coding every detail of a device into an operating system, many aspect of the hardware can be described in a data structure that is passed to the operating system at boot time.

You can find the device tree files for ARM boards in arch/arm/boot/dts/ in the kernel. The files we are going to look at are imx6qdl.dtsi and imx6qdl-wandobard.dtsi (This works for the Wandboard dual/quad, I have no Wandboard solo with me, but the process should be similar). Most of this I got from the Wandboard forums.

Of the 5 spi modules (actually called ecspi) in the Wandboard, we are going to use ecspi1, which is the one routed to the expansion connector. So you need an ecspi1 section in your imx6qdl-wandboard.dtsi file:

&ecspi1 {
	fsl,spi-num-chipselects = <2>;
	cs-gpios = <&gpio2 30 0>, <&gpio4 10 0>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_ecspi1_1>;
	status = "okay";

	spidev@0x00 {
		compatible = "spidev";
		spi-max-frequency = <16000000>;
		reg = <0>;
	};
	
	spidev@0x01 {
		compatible = "spidev";
		spi-max-frequency = <16000000>;
		reg = <1>;
	};
};

I am not much of an expert of device trees myself so don’t take my word for it, but what we are doing here is basically telling the kernel to probe the spidev module twice. To do so, we use the compatible property, in both spidev sections.

The reg property tells the spidev module which chip select to use; so we have an spidev module for chip select 0, and one for chip select 1. It is just an index though; the real chip select lines are defined in the cs-gpios property above. Also, pinctrl-0 defines the pin configuration for the whole SPI module. pinctrl-0 links to pinctrl_ecspi1_1, which can be found in imx6qdl.dtsi and should look like this:

		pinctrl_ecspi1_1: ecspi1grp-1 {
			fsl,pins = <
				MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1
				MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1
				MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1
				MX6QDL_PAD_EIM_EB2__GPIO2_IO30  0x000f0b0
				MX6QDL_PAD_KEY_COL2__GPIO4_IO10 0x000f0b0
			>;
		};

This includes the MISO, MOSI and SCLK lines, plus two chip select lines configured as GPIOs.

Pin naming on the i.MX6 can be a bit confusing sometimes. You can find a good explanation of i.MX6 gpios and pin naming at http://www.kosagi.com/w/index.php?title=Definitive_GPIO_guide. Anyway, I’ll try to briefly explain a way to deal with GPIOs in the device tree, for those who are aware of how important it is to understand Wandboard GPIOs for their daily survival. If, on the other hand, you are a normal mentally healthy person, you can safely skip the next section.

GPIO naming on the device tree
In order to name gpios in the device tree, the first thing you need to do is find out which pad you are using. The pad is the actual physical pin on the i.MX6. The chip select pins we are using here are the ones reserved on the expansion connector: CSPI1_CS0 and CSPI1_CS1, as shown in the Wandboard schematic.
spi_pinout

But beware! These names don’t really mean anything. They are just net names chosen for the carrier board of the Wandboard, and are not the pad names at the i.MX6 chip. I don’t know what’s the proper way of finding out the pad names, surely it is documented somewhere, but what I normally do is have a look at the schematics, and “follow” the net. Diving in the schematics a bit we see that the chip select lines are wired straight to the pins 228 and 230 of the EDM connector (the big connector putting the carrier and the module boards together):

spi_edm_carrier

…which doesn’t tell us much. But looking now at the same connector in the system on module schematics (the one with the actual i.MX6 chip on it), we find this:

spi_edm_module

So EIM_EB2 and KEY_COL2 are our new candidates for pad names. Happily, there is a file in the kernel tree with all the pad names and their possible functions: arch/arm/boot/dts/imx6q-pinfunc.h (versions for dual and solo available too). And it looks like EIM_EB2 and KEY_COL2 are right there:

...
#define MX6QDL_PAD_EIM_EB2__EIM_EB2_B               0x08c 0x3a0 0x000 0x0 0x0
#define MX6QDL_PAD_EIM_EB2__ECSPI1_SS0              0x08c 0x3a0 0x800 0x1 0x0
#define MX6QDL_PAD_EIM_EB2__IPU2_CSI1_DATA19        0x08c 0x3a0 0x8d4 0x3 0x0
#define MX6QDL_PAD_EIM_EB2__HDMI_TX_DDC_SCL         0x08c 0x3a0 0x890 0x4 0x0
#define MX6QDL_PAD_EIM_EB2__GPIO2_IO30              0x08c 0x3a0 0x000 0x5 0x0
#define MX6QDL_PAD_EIM_EB2__I2C2_SCL                0x08c 0x3a0 0x8a0 0x6 0x0
#define MX6QDL_PAD_EIM_EB2__SRC_BOOT_CFG30          0x08c 0x3a0 0x000 0x7 0x0
...
#define MX6QDL_PAD_KEY_COL2__ECSPI1_SS1             0x208 0x5d8 0x804 0x0 0x2
#define MX6QDL_PAD_KEY_COL2__ENET_RX_DATA2          0x208 0x5d8 0x850 0x1 0x1
#define MX6QDL_PAD_KEY_COL2__FLEXCAN1_TX            0x208 0x5d8 0x000 0x2 0x0
#define MX6QDL_PAD_KEY_COL2__KEY_COL2               0x208 0x5d8 0x000 0x3 0x0
#define MX6QDL_PAD_KEY_COL2__ENET_MDC               0x208 0x5d8 0x000 0x4 0x0
#define MX6QDL_PAD_KEY_COL2__GPIO4_IO10             0x208 0x5d8 0x000 0x5 0x0
#define MX6QDL_PAD_KEY_COL2__USB_H1_PWR_CTL_WAKE    0x208 0x5d8 0x000 0x6 0x0
...

The syntax here is MX6QDL_<PAD_NAME>__<FUNCTION>, so there are 7 different functions for every pad. Of these, the relevant functions for us are chip select and GPIO:

MX6QDL_PAD_EIM_EB2__ECSPI1_SS0
MX6QDL_PAD_EIM_EB2__GPIO2_IO30

MX6QDL_PAD_KEY_COL2__ECSPI1_SS1
MX6QDL_PAD_KEY_COL2__GPIO4_IO10

ECSPI1_SSx is the name of the native chip select function.

Chip select: native or GPIO
So we have two ways of configuring our chip select pins: native or GPIO.

Here is a summary of how you configure your device tree files for native or GPIO chip select behaviour:

File Native version GPIO version
imx6qdl-wandboard.dtsi cs-gpios = <0>, <0>; cs-gpios =
<&gpio2 30 0>, <&gpio4 10 0>;
imx6qdl.dtsi MX6QDL_PAD_EIM_EB2__ECSPI1_SS0
MX6QDL_PAD_KEY_COL2__ECSPI1_SS1
MX6QDL_PAD_EIM_EB2__GPIO2_IO30
MX6QDL_PAD_KEY_COL2__GPIO4_IO10

I would recommend using the GPIO configuration. I had trouble with the native chip select, where the chip select line would toggle between bytes in a transfer, where you would expect it to remain low.

Native chip select: Chip select (yellow) toggles between bytes
Native chip select: Chip select (yellow) toggles between bytes
GPIO chip select: Chip select remains low for the whole transfer
GPIO chip select: Chip select remains low for the whole transfer

These two results happen with the same code (using a SPI_IOC_MESSAGE ioctl on the spidev node).

Almost done!

Anyway, time to round up! Compile the device tree with:

$ make imx6dl-wandboard.dtb

or

$ make imx6q-wandboard.dtb

and copy it to your boot folder. (Again, the Wandboard solo should follow a similar approach). Reboot, modprobe spidev, and check you have your /dev/spidev1.0 and /dev/spidev1.1 files.

Don’t worry if they have a different bus number (in my case they were called /dev/spidev32766.x), the important thing is the pin configuration in the device tree is good.

Hope this helps. I wrote this to the best of my knowledge but I am not a guru of SPI at all, so please let me know of any correction!

5 Comments