Custom Linux image built with Buildroot for Raspberry Pi Compute Module 4, carrier board dotstartech/wall-panel and 4 inch MIPI DSI LCD panel with ST7703 controller
- Board: Raspberry Pi Compute Module 4 (RAM: 1GB, Storage: 8GB eMMC, WiFi/BT: None)
- Carrier: dotstartech/wall-panel
- Display: GX040HD-30MB-A1 (720x720 IPS LCD with ST7703 controller)
- Touch: FocalTech FT6336U (I2C0 @ 0x48)
- NFC: NXP PN7150 (I2C1 @ 0x28)
- RTC: DS3231 (I2C1 @ 0x68)
- Linux Kernel 6.12 (Raspberry Pi fork)
- ST7703 display panel driver
- FT6336U touchscreen support
- PN7150 NFC with kernel driver (pn5xx_i2c) and libnfc-nci
- DS3231 RTC support
- I2C interface enabled (I2C0 and I2C1)
- USB Ethernet (smsc95xx for PoE backplate)
nfc-terminal-image/
├── buildroot/ # Buildroot source (cloned)
├── board/nfc-terminal/ # Board-specific files
│ ├── config.txt # Boot configuration
│ ├── cmdline.txt # Kernel command line
│ ├── genimage.cfg # Image generation config
│ ├── linux.config.fragment # Kernel config additions
│ ├── overlays/ # Device tree overlays
│ │ └── nfc-pn7150.dts # NFC PN7150 overlay
│ ├── post-build.sh # Post-build customization
│ └── post-image.sh # Image generation script
├── configs/ # Buildroot defconfigs
│ └── nfc_terminal_cm4_defconfig
├── docs/ # Reference documentation
│ └── AN11697.pdf # NXP PN7150 Linux integration guide
├── package/ # Custom packages
│ ├── libnfc-nci/ # NFC userspace library
│ ├── pn5xx-i2c/ # NFC kernel driver (patched)
│ └── st7703-gx040hd/ # Display driver package
├── external.desc # External tree descriptor
├── external.mk # External tree makefile
├── Config.in # External tree Kconfig
└── README.md # This file
Install build dependencies (Debian/Ubuntu):
sudo apt-get update
sudo apt-get install -y build-essential git wget cpio unzip rsync bc \
libncurses-dev libssl-dev python3 python3-dev python3-setuptools file- Clone Buildroot:
git clone https://github.com/buildroot/buildroot.git --branch 2024.02.x --depth 1- Build the image:
./build.shThis configures Buildroot with the external tree and builds everything.
- Optional: Customize configuration:
./build.sh menuconfig # Main buildroot config
./build.sh linux-menuconfig # Kernel config
./build.sh savedefconfig # Save changes- Output files will be in
buildroot/output/images/:nfc-terminal.img- Complete bootable imageImage- Linux kernelrootfs.ext4- Root filesystembcm2711-rpi-cm4.dtb- Device tree
- Install boot jumpers on IO board to disable eMMC boot
- Connect IO board USB to host
- Flash using rpiboot:
sudo ./buildroot/output/host/bin/rpiboot
# Wait for eMMC to appear as mass storage, then:
sudo dd if=buildroot/output/images/nfc-terminal.img of=/dev/sdX bs=4M status=progress
syncAlternatively Raspberry Pi Imager can be used to flash the image.
- Disconnect IO board USB from host
- Switch boot jumper back to eMMC boot
- Connect LAN cable to PoE switch
The image can be tested in QEMU without real hardware for basic validation of boot and rootfs.
Install QEMU for ARM64:
# Debian/Ubuntu
sudo apt-get install qemu-system-arm
# Fedora
sudo dnf install qemu-system-aarch64./qemu-run.shPress Ctrl-A X to exit QEMU.
QEMU doesn't have Raspberry Pi 4/CM4 machine emulation, so the script uses the generic ARM64 virt machine with a Cortex-A72 CPU. It performs direct kernel boot with the rootfs mounted as a virtio block device.
What works in QEMU:
- Kernel boot and init system
- Network via user-mode networking (SSH available on
localhost:2222) - Basic userspace and shell access
What doesn't work (requires real hardware):
- NFC (
/dev/pn544won't exist - nfc-console will timeout after 30s) - Display (ST7703) and touch
- I2C, GPIO, and other Pi-specific peripherals
SSH into the QEMU instance:
ssh -p 2222 root@localhost
# Password: nfc-terminalKey display, I2C, and peripheral settings in /boot/config.txt:
# Display
dtoverlay=vc4-kms-v3d
dtoverlay=st7703-gx040hd
dtoverlay=ft6336u-gx040hd
# I2C
dtparam=i2c_arm=on # I2C1 (GPIO 2/3) - RTC, NFC
dtparam=i2c_vc=on # I2C0 (GPIO 0/1) - Touch controller
# RTC
dtoverlay=i2c-rtc,ds3231| Bus | Address | Device | Driver |
|---|---|---|---|
| I2C0 (i2c_vc) | 0x48 | FT6336U Touch Controller | edt_ft5x06 |
| I2C1 (i2c_arm) | 0x28 | PN7150 NFC Controller | pn5xx_i2c |
| I2C1 (i2c_arm) | 0x68 | DS3231 RTC | rtc-ds1307 |
- I2C0 (GPIO 0/1): Reserved for DSI display peripherals (touch controller)
- I2C1 (GPIO 2/3): General peripherals (RTC, NFC, user devices)
The NFC subsystem uses NXP's PN7150 chip interfaced via I2C, with kernel-level GPIO control as recommended by NXP's Linux integration guide (AN11697, sections 3.1 and 4.2).
┌─────────────────────────────────────────────┐
│ nfcDemoApp (poll mode) │ User Application
├─────────────────────────────────────────────┤
│ libnfc-nci (R2.4) │ NFC Middleware
│ (dotstartech/linux_libnfc-nci) │
├─────────────────────────────────────────────┤
│ /dev/pn544 (char device) │ Device Node
├─────────────────────────────────────────────┤
│ pn5xx_i2c.ko (kernel) │ Kernel Driver
│ (GPIO control, I2C communication) │
├─────────────────────────────────────────────┤
│ i2c1 @ 0x28 │ GPIO5 (INT) │ Hardware
│ │ GPIO6 (VEN) │
├───────────────────┴─────────────────────────┤
│ NXP PN7150 Chip │
└─────────────────────────────────────────────┘
Key design choice: The --enable-alt flag for libnfc-nci (userspace GPIO control via sysfs) does not work on modern kernels/CM4 because the GPIO sysfs interface fails for GPIOs managed by pinctrl. The kernel driver approach handles GPIO control internally, which is more reliable.
The pn5xx_i2c driver from NXP required several modifications to compile on Linux kernel 6.x:
| Original Code | Fixed Code | Reason |
|---|---|---|
pr_warning(...) |
pr_warn(...) |
pr_warning removed in kernel 5.x |
no_llseek |
noop_llseek |
no_llseek removed in kernel 6.x |
of_get_named_gpio_flags(node, name, 0, &flags) |
of_get_named_gpio(node, name, 0) |
API simplified, flags parameter removed |
static int pn54x_probe(struct i2c_client *client, const struct i2c_device_id *id) |
static int pn54x_probe(struct i2c_client *client) |
i2c_device_id parameter removed in 6.3+ |
static int pn54x_remove(...) |
static void pn54x_remove(...) |
Return type changed to void in 6.1+ |
The patched source is located at package/pn5xx-i2c/pn5xx_i2c.c.
The NFC overlay (board/nfc-terminal/overlays/nfc-pn7150.dts) configures:
pn7150@28 {
compatible = "nxp,pn547"; // Driver compatible string
reg = <0x28>; // I2C address
interrupt-gpios = <&gpio 5 0>; // GPIO5 for data-ready interrupt
enable-gpios = <&gpio 6 0>; // GPIO6 for chip enable (VEN)
interrupts = <5 1>; // GPIO5, rising edge trigger
};
GPIO pin functions:
- GPIO5 (BCM): Interrupt - signals when NFC data is ready to read
- GPIO6 (BCM): Enable (VEN) - powers on/off the NFC chip
The nfcDemoApp poll command starts automatically at boot via the init script /etc/init.d/S95nfc. The script:
- Waits for
/dev/pn544device node (up to 15 seconds) - Sets appropriate permissions on the device
- Launches
nfcDemoApp pollin background
Logs are written to /var/log/nfc.log.
- Username: root
- Password: nfc-terminal
Edit configs/nfc_terminal_cm4_defconfig or use make menuconfig.
Modify board/nfc-terminal/linux.config.fragment for kernel options.
Edit board/nfc-terminal/config.txt for Raspberry Pi boot parameters.
# Check kernel messages
dmesg | grep -E "(st7703|dsi|panel|vc4)"
# Verify overlays loaded
dtoverlay -l# Check I2C devices
i2cdetect -y 0 # Should show 0x48
# Test touch events
evtest /dev/input/event0# List I2C buses
ls /dev/i2c-*
# Scan for devices
i2cdetect -y 1# Check if kernel module loaded
lsmod | grep pn5xx
# Verify device node exists
ls -la /dev/pn544
# Check kernel messages for NFC driver
dmesg | grep -i "pn54x\|nfc"
# View autostart log
cat /var/log/nfc.log
# Verify NFC chip on I2C bus (should show 28)
i2cdetect -y 1
# Manually test NFC polling
/etc/init.d/S95nfc stop
nfcDemoApp poll
# Check GPIO assignments
gpioinfo | grep -E "GPIO5|GPIO6|nfc"