Modernizing an Existing Embedded Linux Device
Take an existing embedded Linux product and create a modern, maintainable BSP — from kernel and U-Boot to a bootable SD card.
CASE STUDY
BREWIE B20 · ALLWINNER A13 · LINUX 3.4 (2016)
X.425 Session 4:
BoardSupportPackage (BSP) Engineering
The BSP Engineering Workflow
Every step of this process fits into one of six stages. Keep asking: "Which piece of information does Buildroot need?"
Build System
Hardware Description
Board Identification
Information Gathering
Discover Hardware
Investigation feeds the build system. The build system produces the image. The image boots the hardware.
Generic Information Gathering
This checklist applies to every embedded board. Fill it in before writing a single line of config.
Category
Information to Collect
CPU
SoC model, core count, RAM size
Bootloader
U-Boot? Barebox? Version?
Kernel
Version, patches, config
Storage
SD, eMMC, NAND — which is primary?
Boot Sequence
bootargs, bootcmd, load addresses
Device Tree / FEX
Hardware description format
Display / Touch
Resolution, interface, controller
USB / Network / Audio
Host vs OTG, Ethernet, WiFi, codec
GPIO / Peripherals
LEDs, buttons, SPI, I²C, UART
Generic Investigation Checklist
On-Board Commands
uname -a
cat /proc/cpuinfo
cat /proc/cmdline
dmesg
lsmod
mount
lsblk
find /boot
Identify the BSP Type
Look for these files to determine your starting point:
Found *.dtb?
→ Modern kernel with Device Tree support
Found script.bin?
→ Legacy Allwinner BSP using FEX format
Case Study: Brewie Investigation
Finding script.bin was the breakthrough — it confirmed a legacy Allwinner BSP and gave us the complete hardware description in a convertible format.
CPU
Allwinner A13
Kernel
3.4.90
Build System
Buildroot 2014
Hardware Desc.
script.bin
Bootloader
U-Boot
Storage
SD Card
Recovering the Hardware Description
From FEX to Device Tree
FEX (Flat EXchange format) is Allwinner's proprietary hardware configuration format used by older (pre-Device Tree) Linux kernels, especially the vendor 3.x BSP kernels.
It serves the same purpose that a Device Tree Source (.dts) serves today.
Example Conversion
; script.fex
lcd_x=480
lcd_y=272
lcd_dclk_freq=9
; sun5i-brewie.dts
display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <9000000>;
hactive = <480>;
vactive = <272>;
};
};
Nearly all hardware information is preserved through this conversion.
01
script.bin
02
bin2fex → script.fex
03
Manual conversion → .dts
Build the BSP
Everything before this point was investigation. Now Buildroot takes over.
Generate SD Image
Buildroot: Linux & RootFS
Buildroot: U-Boot
script.fex → DT
The conversion pipeline flows from hardware description through Buildroot configuration to a bootable SD image.
Files You Create
board/brewie/
genimage.cfg
post-build.sh
linux.config
sun5i-brewie.dts
Generic BSP Migration Strategy
What to Replace
Existing
Replace With
Vendor U-Boot
Mainline U-Boot
Vendor Kernel
Mainline Linux
script.bin
Device Tree
Vendor RootFS
Buildroot
Vendor BusyBox
Buildroot BusyBox
What to Reuse
Don't rewrite what upstream already supports. Preserve these values from the original BSP:
LCD Timings
Touchscreen
RTC
SPI / I²C
UART
GPIO Assignments
Build and Test
Build Commands
make brewie_defconfig
make
Output
output/images/sdcard.img
Flash Options
Boot Verification Checklist
Treat each peripheral independently. Verify one, then move to the next.
U-Boot prompt
Kernel boots
Login prompt
LCD display
Touch input
WiFi / Network
RTC
Results and Lessons Learned
The Transformation
Before
Linux 3.4 · script.bin · Vendor BSP · Unmaintained
After
Linux 6.x · Device Tree · Buildroot BSP · Repeatable
Key Lessons
Gather information first
Never start building until the hardware is documented.
Preserve the original description
script.bin → FEX → Device Tree preserves everything.
Don't rewrite upstream drivers
If mainline supports it, use mainline.
A BSP is documentation
Buildroot automates the complete, reproducible image.
"A successful BSP migration is about understanding the hardware — not rewriting the software."
What do I need? | Where do I find it? |
CPU | /proc/cpuinfo |
Kernel | uname -a |
Boot arguments | /proc/cmdline |
Hardware description | script.bin or *.dtb |
Display | fbset, dmesg, FEX/DT |
Touchscreen | dmesg, i2cdetect |
I²C devices | i2cdetect |
SPI devices | /dev/spidev*, FEX/DT |
Kernel configuration | /proc/config.gz or /boot/config-* |
Bootloader | /boot, serial console |
Root filesystem | mount, lsblk |
Modules | lsmod |
The BSP Discovery Cheat Sheet
What is a Device Tree?
A Device Tree is not code — it is structured data describing hardware. The kernel reads it at boot to discover what's connected, replacing hard-coded board logic with a flexible hardware contract.
Device Tree: The Hardware Contract
Linux Driver
Hardware contract uses DTS answers, not hard-coded pins
Device Tree (DTS)
Board-specific mapping of hardware to logical names
Core Hardware
Physical pins, buses, and peripherals
Drivers no longer hard-code pins like gpio = PB7;. Instead, they ask the DTS: "Which GPIO did the board designer connect?" The answer comes from the Device Tree — cleanly separating driver logic from board layout.
Anatomy of a DTS
/ {
model = "Brewie B20";
compatible = "brewie,b20",
"allwinner,sun5i-a13";
};
aliases {
serial0 = &uart1;
};
chosen {
stdout-path = "serial0:115200n8";
};
model & compatible
The most important property. Tells Linux: "I am an Allwinner A13 board." Matching drivers load automatically.
aliases
Maps logical names to hardware nodes. Software requesting serial0 gets UART1.
chosen
Directs kernel boot messages to UART1 at 115200 baud.
Peripherals: How Drivers Get Bound
&i2c0 {
status = "okay";
rtc@51 {
compatible = "nxp,pcf8563";
reg = <0x51>;
};
};
&i2c0 — Enable Controller
Activates the I²C bus controller on the SoC.
rtc@51 — Device Address
RTC chip at I²C address 0x51.
compatible — Driver Match
Linux finds nxp,pcf8563 in its driver table and binds it. No
The Complete Brewie B20 DTS
This DTS covers every peripheral on the board — each node engineered from real hardware sources.
Core
Model, compatible strings, UART1 console (PG3/PG4)
I²C Buses
I²C0: AXP209 PMIC + PCF8563 RTC · I²C2: ft5x06 touchscreen
Storage & SPI
MMC0 SD card (4-bit) · SPI2 with spidev
Display & USB
LCD RGB24 (PD0–PD27) · PWM backlight (PB02) · Framebuffer 480×272 · USB EHCI0 + OHCI0
Where Did This DTS Come From?
Every node was engineered, not copied. The DTS is assembled from multiple real-world sources.
DTS Node
Source
Method
model / compatible
Created manually
Board identity
LCD timings
script.fex
Allwinner config
Touch (ft5x06)
dmesg
Runtime probe
RTC address
dmesg
I²C scan
UART console
Boot log
Serial output
Backlight / GPIO / Memory
script.fex
Allwinner config
Not copied — Engineered. Every line traces back to hardware evidence.
Building with Buildroot
Required Files
brewie_b20_defconfig
Selects kernel, U-Boot, ext4 rootfs, and genimage. Place in buildroot/configs/.
sun5i-brewie.dts
Board hardware description. Place in board/brewie/.
genimage.cfg + post-image.sh
Defines sdcard.img layout and runs genimage after the build.
Run make brewie_b20_defconfig then make — builds everything including the kernel and final SD card image.
How Linux Boots: The Full Picture
ROM Start
U-Boot Loads
Kernel Init
The Device Tree is the bridge between bootloader and kernel. U-Boot passes the DTB to the kernel, which probes hardware, binds drivers by compatible string, mounts the root filesystem, and hands off to user-space applications.
ROM → U-Boot
Hardware init, load kernel + DTB
Kernel + DTB
Parse tree, bind drivers
Rootfs → Apps
Mount filesystem, start userspace
Part II: Engineering the Unknowns
Goal
Turn a bootable image into a working product, while preserving a reproducible build.
Method
Collect evidence, form a hypothesis, change one subsystem, and test on hardware.
The Difficult Work: Operating at Boundaries
• Legacy FEX versus Device Tree
• Bootloader versus Kernel
• Driver versus Application ABI
• Board Wiring versus Generic Bindings
Teaching Point:BSP engineering continues after the login prompt.
Boot Failure #1: The Kernel Was Not Necessarily Crashing
The Illusion of the Last Line
The last printed line was often only the last visible event; several boots returned to U-Boot without an oops or panic.
Identical Symptoms
A watchdog timeout, an early reset, a failed root mount, and a deferred probe can look deceptively similar from a serial console.
Useful Instrumentation
To diagnose these ambiguous failures, rely on: earlycon, initcall_debug, ignore_loglevel, deferred_probe_timeout, and the U-Boot reset/boot transcript.
Teaching Point: Timestamp the failure, but do not confuse the last log line with the root cause.
Use the Old BSP as a Measurement Instrument
Inputs Analyzed
Inputs included dmesg.txt, script.fex, fex-analysis.json, update.zip, and live inspection of the old machine.
Revealed Subsystems
The old system revealed the real RTC (PCF8563 at 0x51), touch controller (0x38), framebuffer modes, GPIO states, WiFi device, and application paths.
The Reference Mismatch
An Olimex/A13 reference assumption was wrong for the actual Brewie board; GPIO numbering and pin ownership differed.
Teaching Point: Legacy software is valuable evidence, but board identity must be verified before copying a configuration.
Isolate Subsystems Before Re-enabling Them
AXP209 & Stabilization
AXP209 probing caused I2C timeouts (-110), so it was disabled while the rest of the boot path was stabilized.
Incremental Reintroduction
RTC, touch, USB, WiFi, watchdog, audio, video decode, and display were reintroduced incrementally.
Dependency Discoveries
CPU Frequency: A CPU frequency change exposed a power/voltage dependency: the reported 384 MHz initial state was safer than blindly forcing a new rate. MMC/rootfs: MMC/rootfs was separated from kernel startup: the kernel ran correctly but needed built-in MMC support to mount /dev/mmcblk0p2.
Teaching Point: A minimal boot is a diagnostic instrument, not the final product.
Deferred Probe Is a Dependency-Graph Problem
EPROBE_DEFER Loop
The display backend and LCD controller repeatedly returned -EPROBE_DEFER; the device was present but a supplier, binding, or dependency was incomplete.
System Investigation
Inspection of /sys/devices, device links, waiting_for_supplier, compatible strings, and driver symlinks narrowed the problem.
Resolution & Alignment
The panel compatible was corrected from the invalid generic value to panel-dpi; LCD timing, pins, endpoints, and backlight dependencies were aligned. DRM card1 reported a connected Unknown-1 connector and the sun4i DRM framebuffer appeared.
Teaching Point: Read deferred-probe output as a graph of dependencies, not as a random delay.
The Backlight Was a Board-Level Signal
PWM & Enable Signal
PWM PB2 alone did not illuminate the panel. The actual Brewie hardware also needed the PB10 enable/latch signal.
GPIO Mapping Discovery
The old system's gpio7_pb10 test identified the board signal; the modern GPIO mapping was GPIO42, not the initially assumed GPIO35.
DTS Integration & Product Rules
DTS Configuration: The working DTS combines PWM backlight control with the panel enable GPIO and correct polarity/ownership. Brightness Rule: Hardware observation found an additional product rule: brightness 0/10 was off, while brightness 9 enabled the light.
Teaching Point: A backlight may require PWM, a power gate, sequencing, polarity, and a board-specific threshold.
Framebuffer Compatibility Matters to Applications
DRM Display Stack
The modern display stack was working as DRM: card0 was GPU, card1 was the display engine, and the connector was connected.
linuxfb Platform Failure
Qt's linuxfb platform still failed because /dev/fb0 was absent; DRM_FBDEV_EMULATION alone did not provide the legacy framebuffer device node.
CONFIG_FB_DEVICE Resolution
Enabling CONFIG_FB_DEVICE produced the compatibility node required by the existing Qt5 test path.
Teaching Point: A kernel driver can be healthy while the user-space ABI expected by an application is missing.
Reproducing the Product Display Path
Buildroot & Test Path
Buildroot now contains Qt5 GUI, linuxfb, QML/Quick, and a small board-side test program. The test uses target mode: QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0
Display Isolation
The six-color-square QML screen separates display bring-up from the original Brewie application, minimizing side effects.
Test Sequence
Execute the command sequence to enable the panel backlight and launch the test screen application:
echo 9 > /sys/class/backlight/backlight/brightness; qt-screen-test
Teaching Point: Validate the product interface with a minimal independent application before integrating legacy application behavior.
Engineering Lessons from the Brewie B20
1. Verify the Board
Preserve evidence, but verify the board. Old logs and scripts are clues, not an authoritative hardware description.
2. Isolate Failures
Keep bootloader, kernel, Device Tree, root filesystem, and application failures separate in the diagnosis.
3. Controlled Reduction & Signal Tracking
Experiments: Use controlled reduction and one-variable experiments; record both successful and misleading hypotheses. Tracking: Monitor pin mux, ownership, polarity, electrical enable signals, timing, and the expected application ABI.
Final Result: A reproducible Buildroot image with MMC/rootfs, RTC, touch, WiFi, DRM display, board backlight, framebuffer compatibility, and Qt5 test coverage.