Enabling SPI on the Raspberry Pi 3

In a project I’m working on I need to communicate with a BrickPi3 using a Raspberry Pi 3 Model B. For this reason I need to enable SPI. If I were using Raspbian it would mean just adding dtparam=spi=on to /boot/config.txt, but this file is not part of the NixOS boot process. I also want to just flash an SD-card and plug it into the Pi without having to run some actual installation:

{ pkgs, lib, ... }: {
  imports = [
    <nixpkgs/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix>
  ];

  # We can only flash an uncompressed image.
  # Additionally, compression whilst emulating the platform takes a looong time.
  sdImage.compressImage = false;

  # Latest release of major 5 doesn't always play ball with the hardware.
  # Relase 4.19 is stable and "battle-tested".
  # See <https://github.com/NixOS/nixpkgs/issues/82455>.
  boot.kernelPackages = pkgs.linuxPackages_4_19;

  # Other system configurations (services, software environments)
  # omitted for brevity
}

A nixos-generate of the above derivation gives me an image that boots without problems.

Now, according to the NixOS wiki on the Raspberry, to enable audio one needs to add boot.loader.raspberryPi.firmwareConfig = ''dtparam=audio=on'' to the configuration, but mirroring this with dtparam=spi=on instead has no effect. Does anyone know how it should be done? In the end I expect some SPI driver to be listed by lsmod.

sd-image-aarch64.nix has a section that looks similar to what is usually written into a /boot/config.txt:

  sdImage = {
    populateFirmwareCommands = let
      configTxt = pkgs.writeText "config.txt" ''
        kernel=u-boot-rpi3.bin

        # Boot in 64-bit mode.
        arm_control=0x200

        # U-Boot used to need this to work, regardless of whether UART is actually used or not.
        # TODO: check when/if this can be removed.
        enable_uart=1

        # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel
        # when attempting to show low-voltage or overtemperature warnings.
        avoid_warnings=1
      '';
      in ''
        (cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/firmware/)
        cp ${pkgs.ubootRaspberryPi3_64bit}/u-boot.bin firmware/u-boot-rpi3.bin
        cp ${configTxt} firmware/config.txt
      '';
    populateRootCommands = ''
      mkdir -p ./files/boot
      ${extlinux-conf-builder} -t 3 -c ${config.system.build.toplevel} -d ./files/boot
    '';
  };

but appending dtparam=spi=on to the generated config.txt seems to have no effect.

The Nix approach to this problem seems to be by applying device tree overlays: nixos/hardware.deviceTree: new module by kwohlfahrt · Pull Request #60422 · NixOS/nixpkgs · GitHub. I’ll experiment a bit and see if I get things working.

I got some help over on the issue tracker. My final working configuration is linked in the tail-end of the thread.

1 Like

sorry for resurrecting a three year old thread here but I just can’t seem to get this working on my Raspberry Pi 3 Model B even applying your overlay @Tmplt - perhaps something changed in the last three years? :laughing: I know it’s a shot in the dark but if you still have this working I’d take any pointers. I’ve tried a few things but alas! it says it applies during a rebuild but I don’t see /dev/spi

  hardware.deviceTree = {
    enable = true;
    filter = "*rpi*.dtb";
    overlays = [
      { name = "spi"; dtsFile = ./dts/spi.dts; }
   ];
  };

  users.groups.spi = {};

  services.udev.extraRules = ''
  SUBSYSTEM=="spidev", KERNEL=="spidev0.0", GROUP="spi", MODE="0660"
  '';


I can’t help you more than pointing to the project repo where the Pi was in use: GitHub - tmplt/ed7039e: Model, simulation and implementation of a mechatronical system for use in a mimicked industrial environment using UWB localization. Task dispatch via Arrowhead. Reproducibly built via Nix.. See mmc-image.nix, it’s a nix-shell shebang that builds a ready-to-use image. build.sh might also be of interest. Unfortunately there are no lock files, and I don’t know what nixpkgs rev I was on. It was probably the small stable release at the time.

I recall that I had to set the kernel to 4.19. After that HW played nice.

Also, consider checking feat(nix): pin nixpkgs, enable spi via dtso · tmplt/ed7039e@f45470d · GitHub out if you haven’t already.

Edit, found the nixpkgs pin: Commits · tmplt/nixpkgs · GitHub

yep, I used the bits from there to get the rebuild to work and it says it applies but no dice after a reboot. Anyway, I’m sure I’ll figure it out or just give up at some point. Thanks so much for the reply three years down the road!

For another datapoint, try https://github.com/hexamon-tech/nixos-lorawan-gateway/blob/f248cab90e560aea38d7c03963f7eedac9bfd497/modules/dts/spi.dts

Working for years for me with mainline Linux, currently Linux ttn-gw 5.10.93. Think I’ve built it last year, using boot.kernelPackages = pkgs.linuxPackages; (NixOS 22.05).

2 Likes

thanks @sorki that’s what I copied before posting here and it appears to do something during a rebuild but I can’t seem to see /dev/spi :crying_cat_face: I’m probably just applying the config wrong is my guess. but I see this during rebuild (using latest kernel as well (all this in an attempt to get some silly epaper HAT working)):

Applying overlay spi to bcm2837-rpi-cm3-io3.dtb... ok
Applying overlay spi to bcm2837-rpi-3-b.dtb... ok
Applying overlay spi to bcm2711-rpi-4-b.dtb... ok
Applying overlay spi to bcm2711-rpi-400.dtb... ok
Applying overlay spi to bcm2837-rpi-3-a-plus.dtb... ok
Applying overlay spi to bcm2837-rpi-zero-2-w.dtb... ok
Applying overlay spi to bcm2837-rpi-3-b-plus.dtb... ok
Skipping overlay spi: incompatible with imx8mm-venice-gw73xx-0x-rpidsi.dtb
Skipping overlay spi: incompatible with imx8mp-venice-gw74xx-rpidsi.dtb
Skipping overlay spi: incompatible with imx8mm-venice-gw72xx-0x-rpidsi.dtb
Skipping overlay spi: incompatible with msm8996pro-xiaomi-scorpio.dtb
created 6803 symlinks in user environment

so I assumed it was working :man_shrugging: but I really have no idea what I’m doing :slight_smile:

@rjpc did you ever get this working?
I also see my SPI overlay being applied to all the DTBs but the spidev devices don’t show up. Even after adding boot.kernelModules = [ "spi_bcm2835" "spidev" ]; to force the SPI modules to be run, when I lsmod I see them showing up but the ref/use count is 0 and the /dev/spidev* devices don’t exist.

no, never did get it working. Would love to figure it out.

@jhollowe @rjpc: I had similar problems but managed to get it to work. Might be a different issue, but just in case, here’s a quick description what caused my problems:

I was struggling with getting /dev/spidev* devices via device tree overlays using the mainline kernel, even though lsmod showed both spi_bcm2835 and spidev as being loaded. In the end, it turned out to be caused by having the wrong compatible string in the spidev@... devices. In particular, many dts overlays one finds online (like this one which was also linked in this thread) contain the lines

			spidev@0 {
				...
				compatible = "spidev";
			};
			spidev@1 {
				...
				compatible = "spidev";
			};

However, the mainline kernel discourages the use of such spidev compatible strings and throws an error if it is used. For some reason the error doesn’t pop up in dmsg for me, but once I changed the compatible string to something recognized by the mainline kernel (like lwn,bk4), the /dev/spidev* devices popped up after a reboot.

The compatible = "spidev"; version one finds online in many dts files works with the raspberry pi kernel, since it disables the error message and adds the corresponding id string.

(To be clear: Some other comments in this thread already had links that pointed towards the same fix, but it just took me quite a bit to figure that out…)

1 Like

Good catch!

This wasn’t the case before - the kernel just issued a loud warning but continued happily, which is why I didn’t remember my kernel patch adding back spidev to the list of compatible devices (that was optional as it just removed the warning message but is required now).

Managed to find the upstream commit changing the behavior spi: spidev: Make probe to fail early if a spidev compatible is used · torvalds/linux@fffc84f · GitHub

1 Like