Porting the SG200x Debian Image to the Pine64 Oz64

Reference Notes

CS 4250 — Computer Architecture

2026-11-04

Porting Checklist

Board identity

  1. Create configs/<board>/ (copy the closest existing board).
  2. settings.mk: chip, arch, DDR, U-Boot board, ION size, addons.
  3. Device tree: model string + board-specific GPIO/pin fixes.
  4. Reuse the boot chain if there is no U-Boot port; keep the DTB filename.

Peripherals

  1. For each radio/peripheral: power pin, bus, driver build options, firmware.

Image behavior

  1. Build-time config (preconfigure), init-script ordering, naming.

Repo Layout

Component Location
Board config configs/<board>/
Shared build scripts/Makefile, configs/chip/<chip>/chip.mk
Addons (rootfs) scripts/addons/*/addon.mk
Patches configs/{common,chip/<chip>,<board>}/patches/<component>/
Distro pins configs/settings.mk, configs/distro.mk

Selected by make BOARD=oz64 image; the builder runs in a container whose paths are /configs, /builder, /rootfs.

The Oz64 Config Deltas

File Change
dts/cv181x_milkv_duos_sd.dts model="Pine64 Oz64"; WiFi hog porta 15→porta 30
patches/osdrv/0001-aic8800-disable-sdio-bt.patch CONFIG_SDIO_BT=n, CONFIG_COEX=n
addons/aic8800-firmware/addon.mk also package aic8800DC/ (and D80X2)
settings.mk drop usb-switch, ethernet-leds, hciattach-uart; SECOND_CPU

Kernel defconfig is identical to the Duo S. U-Boot is the Duo S U-Boot.

Device Tree — The Key Change

/ { model = "Pine64 Oz64"; };

&porta {
    wifi_chip_en {                 /* was: wifi_pwr @ porta 15 */
        gpio-hog;
        gpios = <30 GPIO_ACTIVE_HIGH>;   /* XGPIOA[30] = AUX0 = GPIO 510 */
        output-high;
    };
};
  • gpio-hog = the GPIO controller drives the pin at probe, before drivers.
  • Powers the AIC8800DC CHIP_EN before mmc1 (wifisd) probes.
  • Wrong pin (Duo S PA15 = SPK_EN here) ⇒ no SDIO card ⇒ “power on fail”.

DTB Reuse Trick

  • Built DTB keeps the name cv181x_milkv_duos_sd.dtb.
  • The reused Duo S U-Boot’s fdtfile points at exactly that path.
  • The only thing that changes is the contents (model, hog).
strings .../cv181x_milkv_duos_sd.dtb | grep -i oz64     # Pine64 Oz64
cat /proc/device-tree/model                            # Pine64 Oz64

WiFi: Four Independent Requirements

# Layer Requirement Fix
1 Board power CHIP_EN before MMC probe DT wifi_chip_en hog, PA30
2 Bus SDIO card enumerates (follows from 1)
3 Driver BT-over-SDIO must be off patch CONFIG_SDIO_BT=n, CONFIG_COEX=n
4 Firmware AIC8800DC blobs in the one aic_fw_path aic8800-firmware addon
5 RF external 2.4 GHz antenna attach u.FL / IPEX MHF1

Failure Signatures

Symptom Meaning
aicbsp: fail to set AIC_WIFI power state to 1 chip unpowered ⇒ no SDIO card
mmc1 absent in /sys/bus/mmc/devices card not enumerated
TDLS_SDIO_BT_SEND_CFM + intstatus=ff, -110 BT-over-SDIO wedged the bus
wlan0 up, RSSI ≈ -80 dBm, ARP FAILED no antenna / dead RF path
ping <name> fails, ping -4 <name> works stale IPv6 AAAA / dual-homing

Driver Patch and Module Load

OSDRV patch (configs/chip/sg200x/patches/osdrv/0001-aic8800-disable-sdio-bt.patch)
  aic8800_bsp/Makefile : CONFIG_SDIO_BT  y -> n
  aic8800_fdrv/Makefile: CONFIG_SDIO_BT  y -> n
                         CONFIG_COEX     y -> n

Runtime (load-systemko addon):

# S00kmod   : base modules (+ ISP/vcodec only if ion_size != 0)
# S25wifimod:
insmod cfg80211.ko
insmod 3rd/aic8800_bsp.ko
insmod 3rd/aic8800_fdrv.ko

Firmware Path

Driver default (hard-coded):

/usr/lib/firmware/aic8800_sdio/aic8800_and_aic8800D80

The addon clones scpcom/aic8800-sdio-firmware and copies aic8800/, aic8800_and_aic8800D80/, aic8800DC/, aic8800D80X2/, and also drops the DC blobs into aic8800_and_aic8800D80/ (the single path).

find /lib/firmware -iname '*8800dc*'      # must be non-empty

Connector Cheat-Sheet

Name Aliases Size (receptacle) Use
MHF1 Hirose U.FL, AMC, UMCC ~3.0 × 3.1 mm most WiFi boards
MHF2 I-PEX MHF II small uncommon
MHF3 Hirose W.FL, AMMC ~55% smaller than U.FL niche
MHF4 I-PEX MHF 4, HSC smallest laptop/M.2 cards

Oz64 schematic part ZX-RF-3-Z1.30.85X, measured ~2.95 mm ⇒ MHF1 / Gen 1. Gen 1 ≠ Gen 3 ≠ Gen 4; they do not intermate.

Build-Time Knobs (preconfigure)

Variable Writes Effect
IMAGE_HOSTNAME /boot/hostname exact hostname
IMAGE_HOSTNAME_PREFIX /boot/hostname.prefix <prefix>-<hash>
WIFI_MODE /boot/wifi.sta/.ap station/AP/none
WIFI_SSID/WIFI_PASS /boot/wifi.ssid/.pass creds (empty ⇒ open)
WIFI_WPA_CONF /boot/wpa_supplicant.conf raw config
SECOND_CPU /boot/arduino C906L policy
make BOARD=oz64 SECOND_CPU=arduino IMAGE_HOSTNAME=oz64-nat \
     WIFI_MODE=sta WIFI_SSID=domenet image

Second CPU: camera vs arduino

SECOND_CPU ION_SIZE camera addons C906L
camera 74 sensor-config, tpusdk runs ISP
arduino 0 none free for remoteproc

ION_SIZE=0 ⇒ chip.mk zeroes the ISP heap and S00kmod skips the ISP/vcodec insmods; CONFIG_CVITEK_REMOTEPROC + mailbox remain, so Arduino firmware loads via /sys/class/remoteproc/.

Runtime Gotchas

  • systemd ordering: Before=ifup@wifi0.service (typo) → ifup@wlan0 read the default inet manual before S30wifi wrote the config.
  • echo -e under dash: generated wlan0 config got literal -e; use printf.
  • Open network: empty pass ⇒ wpa-key-mgmt NONE (not empty wpa-psk).
  • Hostname: /etc/hostname is rewritten each boot; use /boot/hostname.
  • Dual-homing: same subnet on end0+wlan0 breaks ARP; distinct DHCP names + arp_ignore=1/arp_announce=2 if both must be up.

Deterministic Networking

preconfigure edits /etc/dhcpcd.conf:

duid ll                      # stable DHCPv6 DUID
slaac hwaddr                 # stable EUI-64 SLAAC
nohook hostname              # don't rename the system host
interface wlan0
    hostname oz64-nat-wifi   # ethernet keeps "oz64-nat"

⇒ oz64-nat.lan (eth) / oz64-nat-wifi.lan (WiFi), each with stable A/AAAA; oz64-nat.local still resolves both via mDNS.

Command Reference

# build / flash
docker run --privileged -it --rm -v "$PWD/configs":/configs \
  -v "$PWD/image":/output ghcr.io/scpcom/sophgo-sg200x-debian:debian \
  make BOARD=oz64 SECOND_CPU=arduino IMAGE_HOSTNAME=oz64-nat \
       WIFI_MODE=sta WIFI_SSID=domenet image
lz4 -cd image/oz64-e_sd.img.lz4 | sudo dd of=/dev/sdX bs=4M status=progress

# diagnose on board
cat /proc/device-tree/model; ls /sys/bus/mmc/devices
lsmod | grep -E 'aic|cfg80211'; dmesg | grep -iE 'aicbsp|mmc1'
wpa_cli -i wlan0 signal_poll; ip neigh; ping -I wlan0 <gw>