2026-08-17·8 min read

Allwinner H616 / H618 boot: AXP313 PMIC init and the two-name SoC problem

AllwinnerH616H618AXP313PMICU-Bootboot logOrangePiSBC

The AXP313 PMIC comes up before DRAM, SPL trains 2 GiB of LPDDR4, TF-A BL31 runs, then U-Boot, then Manjaro ARM 6.6. The interesting bit is that the same board identifies itself three different ways across those stages: a common Allwinner-family gotcha for people bringing up SBCs.

Full capture at /samples/bootintel-16.txt. Companion piece to the RISC-V lifecycle and TI K3 boot chain posts: same problem (multi-stage firmware bringing up an OS on modern ARM silicon), different vendor's naming.

The five stages

StageRuns onWhat it does
1. Boot ROM (SoC-internal)Cortex-A53 core 0, EL3Looks for the eGON.BT0 magic at sector 16 of MMC0 (SD), then MMC2 (eMMC), then SPI-NOR, then falls back to FEL over USB. Silent on the console.
2. U-Boot SPLCortex-A53, running from on-chip SRAMLoads from MMC. Talks to the AXP313 PMIC over the Reduced Serial Bus to bring up rail voltages before DRAM init. Then trains DRAM (2 GiB LPDDR4 on this Zero3) and loads TF-A + U-Boot into memory.
3. ARM Trusted Firmware (BL31)Cortex-A53, EL3EL3 runtime: services PSCI calls. The BL31 build detects the SoC by ID (0x1823 = H616 electrically) and reads the U-Boot DTB from the memory location SPL left it at.
4. U-Boot properCortex-A53, EL2 / non-secureEnumerates MMC/USB, loads /extlinux/extlinux.conf, reads the kernel + initramfs + FDT from the boot partition, hands off.
5. Linux kernelCortex-A53, EL2Manjaro Linux ARM 6.6.30. Loads the axp20x-i2c driver which redetects the PMIC as an AXP313a variant. All four CPUs start at EL2.

Why PMIC init happens before DRAM

The first eight lines of the log tell the whole SPL story:

U-Boot SPL 2023.10-2 (Jan 04 2024 - 19:06:10 +0000)
AXP313 init
AXP313 ret: 0
AXP313 axp_chip_id: 75
AXP313 finish
AXP313 read 40
AXP313 set 60
DRAM: 2048 MiB

The AXP313a is the X-Powers PMIC on this OrangePi Zero3. Pairing varies across the H616/H618 board catalog: the Zero2 (H616) ships with an AXP305 instead; the Zero3 (H618) and Zero 2W use the AXP313a. It sits on a proprietary two-wire bus called RSB (Reduced Serial Bus), electrically similar to I2C but with different framing and addressing, faster in Allwinner's implementation. Every DRAM controller needs its rail voltage set correctly before the training sequence begins, and on this SoC the DRAM rail comes from the AXP313. So the order is not optional: PMIC up, rail voltage set, then DRAM init.

The five "AXP313" lines break down as:

  • init / ret: 0: the RSB probe found a device and successfully talked to it.
  • axp_chip_id: 75: the AXP313 self-identifies as chip ID 0x75. That's how SPL knows which register map to use.
  • read 40 / set 60: SPL is programming a specific PMIC register (0x40) to value 0x60. On the AXP313 that's a rail-voltage or enable bit; the exact meaning is documented in Allwinner's AXP313 datasheet, which is not publicly available. This is standard Allwinner PMIC bring-up.
  • finish: SPL is done with PMIC; DRAM training starts next.
Later in the log, the kernel says it's an AXP313a

Around 1.5 seconds into the kernel boot: axp20x-i2c 0-0036: AXP20x variant AXP313a found. The kernel's axp20x driver family probes every variant it knows and matches on the register signature. SPL abbreviates the part as "AXP313"; the shipping chip is actually named AXP313A (that's what the public X-Powers datasheet is titled). Kernel-side support for the AXP313a landed in Linux 6.5.

The two-name SoC problem

Read these three lines and spot the discrepancies:

# From BL31 (line 12-13):
NOTICE:  BL31: Detected Allwinner H616 SoC (1823)
NOTICE:  BL31: Found U-Boot DTB at 0x4a0b2bf8, model: OrangePi Zero2

# From U-Boot proper (line 19-20):
CPU:   Allwinner H616 (SUN50I)
Model: OrangePi Zero2

# From U-Boot's extlinux load + kernel (line 51, 64):
Retrieving file: /dtbs/allwinner/sun50i-h618-orangepi-zero3.dtb
[    0.000000] Machine model: OrangePi Zero3

BL31 and U-Boot both call the SoC "H616" and the board an "OrangePi Zero2." The kernel calls it a "Zero3" and loads a "sun50i-h618" device tree.

Neither is wrong; each is right for its layer:

  • Electrically the SoC ID is H616. Reading the silicon-ID register returns 0x1823 for both H616 and H618 parts. TF-A prints whichever name it was compiled with (H616 here, because the OrangePi Zero2/H616 support landed in TF-A earlier). The H618 was launched by Allwinner in 2022 as a variant of the H616 with tweaks to power management and media-decoding blocks; from the SPL's point of view the two are close enough that the same SPL binary boots both silicons.
  • The U-Boot "Model:" string comes from whatever DTB was linked into U-Boot at build time. This Manjaro build linked the Zero2 DTB into the U-Boot binary itself (used only for the U-Boot pre-relocation phase); the real device tree that Linux sees comes from /dtbs/allwinner/sun50i-h618-orangepi-zero3.dtb on the boot partition.
  • The kernel calls it a Zero3 because the extlinux config points at the Zero3 DTB. That's the truth of the running system.

So the board is an OrangePi Zero3 (H618-based), and the U-Boot that boots it identifies as H616/Zero2. This is common in Allwinner-land: the U-Boot fork was built once for the H616 family, and the H618 boards inherited the same SPL + U-Boot pair, and only the kernel-loaded DTB updates the identifier. Reading a boot log without expecting this yields several minutes of "wait, which board am I looking at?"

RSB error (harmless)

Line 14 has one warning:

ERROR:   RSB: set run-time address: 0x10003

BL31 tries to set a run-time RSB address for the PMIC and gets return code 0x10003: an RSB-layer failure setting the run-time address for the PMIC. The exact decode isn't documented in mainline TF-A comments, but the same message appears on essentially every H616/H618 boot with an AXP313a, is non-fatal, and the board continues. The AXP313a is a lower-featured PMIC than the AXP803/AXP805 that TF-A's Allwinner sunxi platform was originally developed against on the H5/H6.

Extlinux boot: not a custom bootcmd

Rather than the classic U-Boot bootcmd environment variable, this build uses the U-Boot extlinux (Syslinux-format) loader:

Found /extlinux/extlinux.conf
Retrieving file: /extlinux/extlinux.conf
1:      Manjaro ARM
Retrieving file: /Image
Retrieving file: /initramfs-linux.img
append: initrd=/initramfs-linux.img console=ttyS0,115200 root=PARTUUID=… earlycon loglevel=7
Retrieving file: /dtbs/allwinner/sun50i-h618-orangepi-zero3.dtb

The extlinux config lives at /boot/extlinux/extlinux.conf on the first partition. Kernel image, initramfs, and DTB are separate files pointed at by relative paths. This is the same convention Debian and Fedora use for their ARM SBC images: U-Boot wired up the generic distro-boot infrastructure (which drives sysboot against extlinux.conf) in v2014.10 precisely so distro maintainers could ship a standard config file rather than one bootcmd variant per board.

Kernel handoff

Starting kernel ...
[    0.000000] Linux version 6.6.30-2-MANJARO-ARM (…) gcc (GCC) 12.1.0 (…)
[    0.000000] Machine model: OrangePi Zero3
…
[    0.177143] CPU: All CPU(s) started at EL2

Same "all CPUs started at EL2" pattern as the K3 log : U-Boot handed off at EL2 rather than dropping to EL1, which keeps KVM available. On a headless SBC that's usually the default and rarely wrong.

How this compares to the other chains

RoleAllwinner H616/H618TI K3 (AM62A)RISC-V (VF2)
PMIC bring-upSPL over RSBDMSC firmwareSPL over I2C
EL3 / M-mode runtimeTF-A BL31TF-A BL31OpenSBI
Non-secure boot payloadU-Boot as BL33U-Boot SPL as BL33U-Boot as S-mode payload
Distro handoff conventionextlinux.confU-Boot boot scriptextlinux.conf (typical)
Vendor-fork riskLow: upstream Allwinner support is community-maintainedMedium: TI ships BSPs that lag mainlineLow: StarFive is close to upstream

What to check on your own H616 / H618 board

  1. AXP313 vs AXP313a. SPL prints only the base name; the kernel prints the variant later. If your board's kernel driver says something other than AXP313 or AXP313a, the board may have a rework using a compatible PMIC (AXP305, AXP805) and your device tree needs to match.
  2. SoC vs DTB identity mismatch. If BL31 and U-Boot report H616 but the loaded DTB is for a different board than you thought, verify against linux-sunxi.org which maintains the community-authoritative device list.
  3. TF-A version. Log shows BL31: v2.9(release):4378d4b-dirty. TF-A is released quarterly; anything more than two releases behind current is worth updating.
  4. Whether there's an interruptable autoboot prompt: on this board the U-Boot countdown is 0 seconds (Hit any key to stop autoboot: 0 at line 45 of the log), which does not mean "defended." See the post for why.

Related

  • TI K3 boot on AM62A : the same TF-A BL31 stage, different vendor's take on the pre-A53 firmware split.
  • RISC-V bootloader lifecycle: the same problem class solved with OpenSBI + M-mode rather than TF-A + EL3.
  • Interruptable autoboot: this log has a 0-second countdown; why that's worse than the 3-second version.
  • UART adapters worth owning: the OrangePi Zero3 UART header is 3.3 V and 115200 8N1 by default; any of the three adapters covered there works.
  • Device Fingerprinter : paste the boot log, get the stages called out inline. Client-side, no upload.
  • Tracking a fleet of SBCs and want the "is this board actually an H616 or an H618?" question answered without reading logs one at a time? BootIntel's paid tiers diff bootloader identifiers across snapshots.