feat(deploy): add aarch64 Raspberry Pi 4 target

The Pi 4 twin of the Pi 5 build: `rpi4-installed` (in-place rebuild target),
`rpi4-image`, and `packages.aarch64-linux.{sd-image-rpi4,atm-app-rpi4}`, all
through the board-keyed machinery of the previous commit. The shared runtime
is untouched; only the board pair is new.

deploy/nixos/hardware/raspberry-pi-4.nix mirrors raspberry-pi-5.nix line for
line except where the boards differ:
- KMS for the kiosk display is an opt-in on the Pi 4
  (`hardware.raspberry-pi."4".fkms-3d`), which also injects the CMA + vc4
  device-tree overlays; the Pi 5 gets it by default. Without it X falls back
  to the framebuffer and Electron renders in software.
- fkms-3d sets videoDrivers itself, so the module doesn't.
Everything else — extlinux boot, console pinned to tty0 so the GPIO UART is
free for a validator, no-suspend, the ttyValidator{0,1,2} udev symlinks — is
identical by design.

Evaluation-verified only: rpi4-installed/rpi4-image instantiate, and against
rpi5 they differ solely in the expected places (bcm2711 device tree, the two
fkms overlays, the rpiVersion=4 kernel, no clk-rp1 in initrd, machine model
in the env seed). Not yet booted on hardware; the doc says so.

docs/raspberry-pi-setup.md covers both boards — build, flash, first boot +
provisioning via the spire seed, in-place updates, peripherals — since #87
shipped the Pi 5 without one. It replaces a never-committed Pi 4 sketch
(parked on wip/rpi4-sketch) whose flake wiring didn't evaluate and whose
provisioning section predated the pairing seed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013A6683cCHnQxFUosx1krY4
This commit is contained in:
Padreug 2026-09-20 11:26:17 +02:00
commit 91c6994dd4
4 changed files with 238 additions and 4 deletions

View file

@ -0,0 +1,69 @@
# Raspberry Pi 4 hardware module (aarch64).
#
# The Pi 4 twin of raspberry-pi-5.nix. Kernel, firmware, bootloader and device
# tree come from the nixos-hardware `raspberry-pi-4` module (paired with this
# file in flake.nix's piBoards); here we set only the bitSpire-specific
# hardware glue: serial for the bill validators, the kiosk display driver, and
# no-suspend. The wiring notes in raspberry-pi-5.nix apply unchanged — same
# validators over USB-serial, same QR scanner, same touchscreen options.
#
# What differs from the Pi 5:
# - GPU/KMS: the Pi 5 module enables vc4/v3d modesetting by default; on the
# Pi 4 it is an opt-in (`fkms-3d`) that also injects the CMA + vc4 device
# tree overlays. Without it X falls back to the plain framebuffer and
# Electron renders in software.
# - Memory: 4 GB is the floor for Electron + the kiosk; 8 GB is comfortable.
# The shared Pi runtime's MemoryMax=2G leaves headroom on either.
# - No PCIe (the Pi 5's NVMe path); boot/root is SD or USB-SATA only.
{ config, lib, pkgs, ... }:
{
# aarch64 target. (The flake instantiates this config with aarch64 pkgs; this
# line documents/asserts it.)
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
# Bootloader: the aarch64 sd-image uses the extlinux-compatible generator;
# nixos-hardware's rpi4 module wires the firmware/u-boot. No systemd-boot.
boot.loader.grub.enable = lib.mkDefault false;
boot.loader.generic-extlinux-compatible.enable = lib.mkDefault true;
# Primary UART (GPIO 14/15) available for a GPIO-wired validator. Keep the
# serial console OFF it so the validator owns the line — mirrors upboard.nix
# keeping ttyS4 free for the dispenser. USB-serial adapters are unaffected.
#
# Not mkDefault: kernelParams is list-merged, and only definitions at the
# highest priority survive. nixpkgs sets loglevel/lsm at normal priority, so
# a mkDefault list here is dropped entirely — and with no console= at all
# the kernel falls back to the device tree's stdout-path, i.e. this UART.
boot.kernelParams = [ "console=tty0" ];
# Kiosk display: vc4/v3d kernel modesetting via the firmware KMS overlay.
# This is the Pi 4's equivalent of the Pi 5's default KMS path — it also
# sets services.xserver.videoDrivers to modesetting (fbdev fallback), so we
# don't set that here. Electron renders through it as on the UP Board.
hardware.raspberry-pi."4".fkms-3d.enable = true;
hardware.enableRedistributableFirmware = true;
# Kiosk: never sleep.
systemd.targets = {
sleep.enable = false;
suspend.enable = false;
hibernate.enable = false;
hybrid-sleep.enable = false;
};
# Stable device symlinks for USB-serial bill-validator adapters, so the ATM
# config can point at /dev/ttyValidator0 regardless of enumeration order.
# Identical to the Pi 5 module — same adapters, same bridges. If two adapters
# of the SAME chip are used, disambiguate by KERNELS/serial instead — tune
# during bring-up.
services.udev.extraRules = lib.mkAfter ''
# FTDI (e.g. FT232R) → ttyValidator0
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="ttyValidator0"
# Silicon Labs CP210x → ttyValidator1
SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", SYMLINK+="ttyValidator1"
# WCH CH340 → ttyValidator2
SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="ttyValidator2"
'';
}