Skip to content

Device Tree Overlays

The device-tree configuration baked into the system image: config.txt snippets, the GPIO pin reservation table, and the validation steps that confirm a freshly-flashed SD has its peripherals wired up correctly. Read this if you are adding a new SPI / I2C / GPIO peripheral or if a peripheral is silently failing to enumerate.


The KN-86 image ships with a config.txt derived from pi-gen’s Pi OS Lite default plus the project-specific overlays below. The full file lives in tools/sd-provision/pi-gen-stages/stage-kn86-firmware/files/boot/config.txt. The relevant additions:

# --- KN-86 device-tree overlays ---
# Primary display: HDMI to Elecrow 7" IPS @ 1024x600
hdmi_force_hotplug=1
hdmi_group=2
hdmi_mode=87
hdmi_cvt=1024 600 60 6 0 0 0
disable_overscan=1
# UART0 to Pi Pico 2 coprocessor (ADR-0017)
# GPIO14 = TXD0 -> Pico RX (GPIO1)
# GPIO15 = RXD0 <- Pico TX (GPIO0)
enable_uart=1
dtoverlay=disable-bt # release UART0 from BT module to /dev/serial0
init_uart_baud=1000000
# GPIO22 = Pico BOOTSEL control, GPIO23 = Pico RESET control (ADR-0017 §6)
# Driven by the coprocessor daemon during firmware-update flow; reserved here.
gpio=22=op,dh
gpio=23=op,dh
# I2S audio is OWNED BY THE PICO 2 (ADR-0017). Pi-side I2S overlay disabled.
# (Historical: pre-ADR-0017 used dtoverlay=hifiberry-dac. No longer applies.)
# SPI0 is FREE (post-ADR-0017). Pre-ADR-0017 the OLED was on Pi SPI0; the
# OLED is now driven from the Pico's SPI bus. SPI0 is disabled here so any
# kernel SPI driver doesn't claim the pins.
dtparam=spi=off
# USB peripheral mode for the updater image (ADR-0011)
# Used only by the kexec'd updater rootfs, not by the normal nOSh boot.
# config.txt loads dwc2 with peripheral mode; cmdline.txt in the updater
# bootfs slot carries `modules-load=dwc2,g_mass_storage`.
dtoverlay=dwc2,dr_mode=peripheral

config.txt lives on p1 (the common boot region per ../../adr/ADR-0011-device-firmware-update-system.md). Each bootfs slot (p2, p3) carries its own cmdline.txt. Slot-shared firmware-level config goes in config.txt; slot-specific kernel command-line lives in the per-slot cmdline.txt.

enable_uart=1
dtoverlay=disable-bt
init_uart_baud=1000000

dtoverlay=disable-bt re-routes the BCM43438 Bluetooth module off UART0 onto the lower-quality mini-uart (UART1). UART0 (PL011) is the full-feature UART with hardware flow control and stable timing — exactly what the 1 Mbps Pi↔Pico link requires. Bluetooth is unused on the KN-86, so this trade is free.

After boot, /dev/serial0 is a symlink to /dev/ttyAMA0. The coprocessor daemon (boot-and-systemd.md) opens /dev/serial0 at 1,000,000 baud, 8N1, and runs the §5.3 bootstrap from coprocessor-protocol.md.

The Pi Zero 2 W’s OTG port fans out through an internal USB 2.0 hub IC (TUSB2036, FE1.1s, or equivalent — exact part TBD per ADR-0018 KBD-03) to two downstream devices: the QMK-compatible keyboard controller, and the USB-to-SD card reader bridge IC for cartridges (ADR-0019). This is not a device-tree-overlay matter on the Pi side — USB hubs and USB-MSC bridges are class-compliant and surface through the kernel’s standard USB stack with no DTB hint required. The only Pi-side config.txt interaction is dtoverlay=dwc2,dr_mode=peripheral for the updater image’s USB-MSC mode (above); the host-mode that the normal boot uses is the default and does not need an explicit overlay.

Keyboard enumeration appears as /dev/input/event* once the udev pipeline settles. Cartridge insertion appears as a /dev/sd* block device subscribed by the udev rule shipped in stage-kn86-runtime (boot-and-systemd.md, ../../software/runtime/cartridge-lifecycle.md).

SSD1322 OLED — Pi side has no overlay (ADR-0015 + ADR-0017)

Section titled “SSD1322 OLED — Pi side has no overlay (ADR-0015 + ADR-0017)”

Pre-ADR-0017, the SSD1322 CIPHER-LINE OLED was driven from Pi SPI0 with a dtoverlay=spi0-2cs plus a custom SSD1322 driver. That entire path is obsolete. The OLED is now driven from the Pico 2’s SPI bus, commanded by the Pi over UART using OLED frame types from coprocessor-protocol.md §4.

Concretely on the Pi:

  • dtparam=spi=off is set in config.txt so Linux does not claim SPI0/SPI1.
  • No SSD1322 driver is shipped in the kernel module set; the OLED never appears as a Linux character device.
  • The Pico-side SPI pin assignment to the SSD1322 is finalised at the Pico firmware F2 bring-up (coprocessor-firmware.md) and is not visible to Linux at all.

If a future hardware revision moves the OLED back to the Pi, the original Pi-side overlay would re-enter — but that is not in scope under ADR-0017 + ADR-0019.

The Pi Zero 2 W has 26 usable GPIO pins on the 40-pin header. The KN-86 v1 claims a small subset; the rest are explicitly unclaimed to leave headroom for future peripherals.

Pin (BCM)HeaderClaimed byFunction
GPIO 14pin 8UART0 (this doc)TXD0 → Pico GPIO1 (RX)
GPIO 15pin 10UART0 (this doc)RXD0 ← Pico GPIO0 (TX)
GPIO 22pin 15Coprocessor daemon (ADR-0017 §6)Pico BOOTSEL control. TBD pending bring-up — see ADR-0017 KU#9 if a conflict surfaces.
GPIO 23pin 16Coprocessor daemon (ADR-0017 §6)Pico RESET control. TBD pending bring-up — see ADR-0017 KU#9.
GPIO 2 / 3pin 3 / 5(free — reserved I2C0)Available for a future I2C peripheral (battery fuel gauge candidate).
GPIO 7–11pin 24/26/19/21/23(free — reserved SPI0)Available; not claimed because OLED moved to Pico per ADR-0017.
GPIO 18, 19, 21pin 12 / 35 / 40(free — reserved I2S)Available; not claimed because audio moved to Pico per ADR-0017.
All others(free)No project claim.

Conflict-avoidance rule: any new peripheral that wants a GPIO must update this table in the same PR that lands the overlay change, and must not collide with an existing claim. CLAUDE.md Spec Hygiene Rule 3 applies — the table is the source of truth, not a hint.

After flashing a fresh SD and first-boot, run these from an SSH session in dev mode (or from the bench-rig serial console — see ../hardware/build-specification.md §5):

Terminal window
# 1. Confirm UART0 is live and routed to /dev/serial0:
ls -l /dev/serial0
# expected: lrwxrwxrwx /dev/serial0 -> ttyAMA0
# 2. Confirm the BT module is OFF UART0:
dmesg | grep -i bluetooth
# expected: nothing on the PL011, "Bluetooth: disabled" or "no UART"
# 3. Confirm GPIO claims match the table above:
raspi-gpio get
# Spot-check rows for GPIO 14, 15, 22, 23 — should show the assignments above.
# GPIO 7-11 and 18/19/21 should be inputs (free).
# 4. Confirm SPI is OFF:
ls /dev/spidev* 2>/dev/null && echo "SPI is ON (BAD)" || echo "SPI is OFF (good)"
# 5. Confirm the USB hub + downstream devices enumerated:
lsusb -t
# Expected tree: Pi root hub -> internal hub IC -> {keyboard controller, SD card reader bridge}
# (Cartridge bridge appears even with no SD inserted; the SD card itself appears under it on insertion.)
# 6. Confirm the keyboard:
ls /dev/input/by-id/ | grep -i kn86
# expected: a /dev/input/by-id/usb-KN86_*-event-kbd symlink

If any of these fail, the most likely cause is a stale config.txt — verify the file on p1 matches stage-kn86-firmware/files/boot/config.txt from the source tree. If the file is correct and the validation still fails, the overlay snippet may be syntactically valid but blocked by a Pi firmware version mismatch (see system-image-build.md “Base Debian release pinning”) — capture /boot/config.txt, dmesg | head -100, and vcgencmd version, and escalate.