GPIO Chip Selects with the BeagleBone

| | 3 Comments| 10:56 PM
Categories:

In my previous post I made mention that I could not use GPIO-based SPI chip selects on the BeagleBone Black with the default McSPI driver (what you are using if you’re opening /dev/spidev. The exact quote I had run across (much to my chagrin at the time) was

Incidentally, the spi-omap2-mcspi.c driver does not support a GPIO as a chip select.

Well, fortunately, a little persistence paid off (I confess, I can be stubborn at times), and I found this patch from Michael Welling that adds GPIO chip select support to the spi-omap2-mcspi.c driver.

As I mentioned in the article on using the 4-slot mikroBUS cape I was using a workaround to lower the chip select by hand. With Linux 4.3 and one additional patch you no longer need it. I’ve included everything in the BBB-420mA repository on the linux-4.3 branch, but wanted to share some additional tips for those looking to work with the cs-gpios tag in DTS files.

Declare your Pins

The snippet below cannot be taken out of context from the larger DTS overlay, but illustrates that we put our GPIO chip select pin in the same group as the rest of the SPI1 pins.

    bb_spi1_pins: pinmux_bb_spi1_pins {
    pinctrl-single,pins = <
      0x190 0x33    /* mcasp0_aclkx.spi1_sclk, INPUT_PULLUP | MODE3 */
      0x194 0x33    /* mcasp0_fsx.spi1_d0, INPUT_PULLUP | MODE3 */
      0x198 0x13    /* mcasp0_axr0.spi1_d1, OUTPUT_PULLUP | MODE3 */
      0x19c 0x13    /* mcasp0_ahclkr.spi1_cs0, OUTPUT_PULLUP | MODE3 */
      0x164 0x12    /* eCAP0_in_PWM0_out.spi1_cs1 OUTPUT_PULLUP | MODE2 */
      0x098 0x17    /* P8 10 gpio2_4.spi1_cs2 OUTPUT_PULLUP | MODE7 */
      >;
      };

In particular the “magic” 0x098 0x17 refers to P8.10 on the Black (and actually the 0x098 refers to the pin and the 0x17 refers to the settings, which in this case we want OUTPUT_PULLUP and MODE7), which is gpio68 in /sys/class/gpio. Another name for it, which we’ll see below, is <&gpio2 4 0>. So many ways are referring to the same thing. No wonder it gets confusing.

Next, let’s revisit our DTS for SPI1 on the Black:

  fragment@1 {
    target = <&spi1>;   /* spi1 is numbered correctly */
    __overlay__ {
      status = "okay";
      pinctrl-names = "default";
      pinctrl-0 = <&bb_spi1_pins>;

#address-cells = <1>;
#size-cells = <0>;

      cs-gpios = <0>, <0>, <&gpio2 4 0>;

      spi1@0 {
#address-cells = <1>;
#size-cells = <0>;
    compatible = "spidev";
    reg = <0>;
    spi-max-frequency = <16000000>;
    spi-cpol;
    spi-cpha;          
      };

      spi1@1 {
#address-cells = <1>;
#size-cells = <0>;
    compatible = "spidev";
    reg = <1>;
    spi-max-frequency = <16000000>;
    spi-cpol;
    spi-cpha;
      };

      spi1@2 {
#address-cells = <1>;
#size-cells = <0>;
    compatible = "spidev";
    reg = <2>;
    spi-max-frequency = <16000000>;
    spi-cpol;
    spi-cpha;
      };
    };
  };

The SPI bus documentation is a little unclear, but the format for the cs-gpios tag is:

cs-gpios =  <GPIO>|<0>, <GPIO>|<0>, ..., <GPIO>|<0>;

where

  • <0> means “use the default pin” for this chip select
  • GPIO is expanded to something like &gpio 4 0 to identify the gpio pin

So in our example, the line

cs-gpios = <0>, <0>, <&gpio2 4 0>;

in the DTS overlay is telling the OMAP SPI driver that SPI1 chip select 0 (or CS0 you will see on the BeagleBone Black SPI pinouts) will use its default pin assignment, chip select 1 will also use its default pin assignment, and chip select 2 will use <&gpio2 4 0>, which as we said, is P8.10, aka GPIO 68. If you’re confused as to why I want to use P8.10, that’s because on the mikroBUS cape the clickboard in slot 4 has the chip select line running to P8.10 on the Black.

Host 4 Chip Select is P8.10
Host 4 Chip Select is P8.10
           CS0     CS1       CS2
         default default    P8.10
            |       |         |
            |       |         |
            v       v         v
cs-gpios = <0>,    <0>, <&gpio 2 4 0>;

See the GPIO overlay structure for details on the naming structure for GPIO pins, keeping in mind that “Exact meaning of each specifier cell is controller specific, and must be documented in the device tree binding for the device.” This is another way of saying that the structure of the GPIO names aren’t necessarily the same between the BeagleBone Black and say, a Raspberry Pi.

Get onto Linux 4.3

I want to make sure it’s absolutely clear, you need to be on Linux 4.3 on your BeagleBone Black for anything below to work properly! The following are quick instructions that, as of the time of this writing (November 30, 2015), should get you a Black that’s good to go.

First, start off with a latest Debian Jessie image from the BeagleBone Black images page. In my case I’m going to use the 2015-11-29 Debian 8.2 Flasher image which will flash my BeagleBone. For details on how to use a flasher image with a BeagleBone, see these instructions.

Once you’ve flashed the Black, upgrade the kernel to latest 4.3 release candidate (for the BeagleBone), which, as of this writing, was 4.3.0-rc7-bone1. Upgrading is quite easy:

apt-get update
apt-get install -y linux-image-4.3.0-rc7-bone1

You’ll probably see something like:

Setting up linux-image-4.3.0-rc7-bone1 (1jessie) ...
Error! Error! Your kernel headers for kernel 4.3.0-rc7-bone1 cannot be found.
Your kernel headers for kernel 4.3.0-rc7-bone1 cannot be found.
Please install the linux-headers-4.3.0-rc7-bone1 package,
Please install the linux-headers-4.3.0-rc7-bone1 package,
or use the --kernelsourcedir option to tell DKMS where it's located
or use the --kernelsourcedir option to tell DKMS where it's located
Error! Your kernel headers for kernel 4.3.0-rc7-bone1 cannot be found.
Please install the linux-headers-4.3.0-rc7-bone1 package,
or use the --kernelsourcedir option to tell DKMS where it's located
update-initramfs: Generating /boot/initrd.img-4.3.0-rc7-bone1
zz-uenv_txt: Updating /boot/uEnv.txt [uname_r=4.3.0-rc7-bone1]

Ignore the warnings and reboot!

Getting the Kernel Source

Unfortunately, having the 4.3 kernel image on your BeagleBone isn’t enough; you’ll have to patch the spi-omap2 driver, and while I’d love to say it is a piece of cake, well, let’s just say its a bit of a doberge cake (pronounced “doebash”).

I highly recommend this be done on an ARM system such as a Wandboard Quad, or soon-to-be-released BeagleBoard X15. You can also accomplish this with a cross-compiling environment though it is a bit more cumbersome. We’re going to assume you’re compiling with a native ARM system, and then look at how to cross-compile in another installment.

To get started, on a sufficiently sized filesystem (>4G free space), let’s get Robert C. Nelson’s awesome bb-kernel repository:

root@beagleboard-x15.local:/mnt/sd# git clone https://github.com/RobertCNelson/bb-kernel

Since we are going to compile the module for a 4.3-rc7-bone1 kernel, let’s check out against that tag in the repository.

root@beagleboard-x15.local:/mnt/sd/bb-kernel# git checkout 4.3-rc7-bone1
Note: checking out '4.3-rc7-bone1'.
...
HEAD is now at 1f00cb4... 4.3-rc7-bone1 release

This repository provides a great out-of-the-box script to check out the Linux source tree and build everything to make a bootstrapped BeagleBoard kernel, but we want to skip a lot of steps and just build our SPI driver. So you will want to edit the build_kernel.sh script here and comment out everything starting with the AUTO_BUILD if statement:

If you read through the script you’ll see that it checks out the Linux source tree from the appropriate git repository and also patches it for building against the BeagleBone Black. If you didn’t comment anything out it would keep running and build the kernel, modules, create a deployment package, etc. We don’t need that here.

Run the modified build_kernel.sh script. This still may take some time (~20 minutes) as it checks out everything, applies patches, etc.

Before we build our module we want to apply this patch from Michael Welling which fixes an issue with trying to claim the GPIO-based chip select line twice:

Make sure you are in the KERNEL directory which was checked out by running build_kernel.sh:

root@beagleboard-x15.local:/mnt/sd/bb-kernel # cd KERNEL
root@beagleboard-x15.local:/mnt/sd/bb-kernel/KERNEL # wget http://dev.iachieved.it/downloads/spi-omap2-mcspi.c.patch
root@beagleboard-x15.local:/mnt/sd/bb-kernel/KERNEL # git apply spi-omap2-mcspi.c.patch

If you run git diff now you’ll see the diff generated by application of the patch.

Before we can recompile our module we need Module.symvers file from the Linux headers package (pro tip: we wouldn’t need the Linux headers package from Module.symvers if we had built the kernel ourselves, but we’re skipping all of that and building a single module). In the KERNEL directory:

root@BeagleBoard-X15:/mnt/sd/bb-kernel/KERNEL# apt-get install -y linux-headers-4.3.0-rc7-bone1
root@BeagleBoard-X15:/mnt/sd/bb-kernel/KERNEL# cp /usr/src/linux-headers-4.3.0-rc7-bone1/Module.symvers .

Now! Let’s recompile our SPI modules! Three make steps are necessary: prepare, modules_prepare and modules M=drivers/spi:

root@BeagleBoard-X15:/mnt/sd/bb-kernel/KERNEL# make prepare
...
root@BeagleBoard-X15:/mnt/sd/bb-kernel/KERNEL# make modules_prepare
...
root@BeagleBoard-X15:/mnt/sd/bb-kernel/KERNEL# make modules M=drivers/spi
  CC [M]  drivers/spi/spi-dln2.o
  CC [M]  drivers/spi/spi-omap2-mcspi.o
  Building modules, stage 2.
  MODPOST 2 modules
  CC      drivers/spi/spi-dln2.mod.o
  LD [M]  drivers/spi/spi-dln2.ko
  CC      drivers/spi/spi-omap2-mcspi.mod.o
  LD [M]  drivers/spi/spi-omap2-mcspi.ko

Now let’s copy our newly built spi-omap2-driver.ko module over to the BeagleBone Black:


root@BeagleBoard-X15:/mnt/sd/bb-kernel/KERNEL# scp drivers/spi/spi-omap2-mcspi.ko root@192.168.1.106:/lib/modules/4.3.0-rc7-bone1/kernel/drivers/spi/
spi-omap2-mcspi.ko                            100%   21KB  21.4KB/s   00:00

Loading the Overlay

Recall from the previous tutorial you will need to compile and install the overlay into /lib/firmware. Assuming you’ve done this (see the previous post for details) and your /boot/uEnv.txt is in order, load the overlay and then try the transmitReceive420.js script in the linux-4.3 branch of the BBB-420mA repository.

Without a patched spi-omap2 module you would expect to see:

/root/BBB-420mA/node_modules/spi/spi.js:65
    return this._spi.open(this.device);
                     ^

TypeError: Unable to set SPI_IOC_WR_MODE
    at TypeError (native)
    at Spi.open (/root/BBB-420mA/node_modules/spi/spi.js:65:22)
...

With the updated SPI module:

root@beaglebone:~/BBB-420mA# node transmitReceive420.js 7 14
Milliamps:                7
Output to Transmitter:  1417.8125
Input from Receiver:    1405
Millamps:               6.937784262
Milliamps:                14
Output to Transmitter:  2859.375
Input from Receiver:    2870
Millamps:               14.051592792

This script was run with a fully populated mikroBUS cape and 4 clickboards! Two 4-20mA transmitters and two 4-20mA receivers. The transmitter in slot 2 is looped to the receiver in slot 1. Likewise, the transmitter in slot 3 is looped to the receiver in slot 4.

4 SPI Clickboards!
4 SPI Clickboards!

Getting the Code

Everything for this tutorial is on the linux-4.3 branch of the BBB-420mA repository. I highly suggest you look at the README and review the instructions there. There are admittedly a lot of moving parts involved here: compiling overlays, adjusting uEnv.txt, understanding SPI and chip select, 4-20mA clickboards, compiling kernel modules, and oh, NodeJS too! Hopefully though when you Google gpio chip select on beaglebone you’ll find your way here and enjoy the read.

3 thoughts on “GPIO Chip Selects with the BeagleBone”

  1. With kernel 4.4.14-bone11 i can verify that the DTS overlay works “out of the box”, no kernel patching needed. Probably also works with some earlier kernels, as long as they are newer than the kernel in this article. There is no mention of GPIO chip select on other resources i can find so just wanted to update this for everybody else on the internet looking to do this on their Beaglebone.

  2. Why not just use a quad 2-input OR gate (74HC32) to multiply the /CS pin? No kernel hacking required. Route BBBs /CS to one input of all 4 gates, and 4 GPIOs to the remaining 4 inputs. When the GPIO is high, the output will be high, when the GPIO is low, the output will follow BBBs /CS.

  3. How can i change
    spi-cpol;
    spi-cpha;
    values
    In tried to set to all the 4 combination but always the driver reports mode 3
    in dts I did
    spi-cpol = ;
    spi-cpha =;

Leave a Reply

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