Logo

Using spidev with mainline Linux kernel on the Raspberry Pi 4

Linux Embedded

3 minutes

A couple of weeks ago I finally moved my Arch Linux Arm based Raspberries to the mainline Linux kernel (linux-aarch64) and u-boot. The transition was pretty smooth with one exception: the spidev driver, which is needed to show rainbows (and other information) on the attached Pimoroni Display-O-HAT. Most probably, the reason of this absence is because the spidev driver should not be declared in the Device Tree according to the kernel developers.
It is not rocket science, however I couldn’t find a quick explanation about how to add a Device Tree overlay to restore the functionality. So, here we are!
The first step is to create the Device Tree Overlay, check the source below, which can be put in a file spidev.dts. Note we are faking a particular device driven via spidev. Using spidev directly will trigger a loudly kernel WARNING. You can compile the overlay with:

dtc -@ -Hepapr -I dts -O dtb -o spidev.dto spidev.dts

The content of spidev.dts is:

/dts-v1/;
/plugin/;

/{
        compatible = "brcm,bcm2835";
        fragment@0 {
                target-path = "/soc/gpio@7e200000";
                __overlay__ {
                        spi0_pins: spi0_pins {
                                brcm,pins = <0x09 0x0a 0x0b>;
                                brcm,function = <0x04>;
                                phandle = <0x0d>;
                        };

                        spi0_cs_pins: spi0_cs_pins {
                                brcm,pins = <0x08 0x07>;
                                brcm,function = <0x01>;
                                phandle = <0x0e>;
                        };
        };
    };
        fragment@1 {
                target-path = "/soc/spi@7e204000";
                __overlay__ {
             pinctrl-names = "default";
             pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
             cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
             status = "okay";

             spidev0: spidev@0{
                 compatible = "lwn,bk4";
                 reg = <0>;      /* CE0 */
                 #address-cells = <1>;
                 #size-cells = <0>;
                 spi-max-frequency = <125000000>;
             };

             spidev1: spidev@1{
                 compatible = "lwn,bk4";
                 reg = <1>;      /* CE1 */
                 #address-cells = <1>;
                 #size-cells = <0>;
                 spi-max-frequency = <125000000>;
             };
                };
        };
};

The next step is to change the u-boot boot script to load the overlay. This is distribution dependent, for Arch Liunx Arm it is /boot/boot.txt. The added lines are marked by a comment. Don’t forget to copy spidev.dto to /boot/dtbs/ and to run ./mkscr.

# After modifying, run ./mkscr

# Set root partition to the second partition of boot device
part uuid ${devtype} ${devnum}:2 uuid

setenv bootargs console=ttyS1,115200 console=tty0 root=PARTUUID=${uuid} rw rootwait smsc95xx.macaddr="${usbethaddr}"

if load ${devtype} ${devnum}:${bootpart} ${kernel_addr_r} /Image; then
  if load ${devtype} ${devnum}:${bootpart} ${fdt_addr_r} /dtbs/${fdtfile}; then
    # Needed for DT Overlay
    fdt addr ${fdt_addr_r}
    fdt resize
    setexpr fdtovaddr ${fdt_addr_r} + F000
    load ${devtype} ${devnum}:${bootpart} ${fdtovaddr} /dtbs/spidev.dto && fdt apply ${fdtovaddr}
    # End of DT Overlay
    if load ${devtype} ${devnum}:${bootpart} ${ramdisk_addr_r} /initramfs-linux.img; then
      booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r};
    else
      booti ${kernel_addr_r} - ${fdt_addr_r};
    fi;
  fi;
fi

Last step, force load spidev.ko by creating /etc/modules-load.d/spidev.conf:

# For the PI hats
spidev

And you should be all set!