1.. SPDX-License-Identifier: GPL-2.0-only 2 3==================================== 4Booting Linux on ZTE zx297520v3 SoCs 5==================================== 6 7Author: Stefan Dösinger 8 9Date : 27 Jan 2026 10 111. Hardware description 12--------------------------- 13Zx297520v3 SoCs use a 64-bit capable Cortex-A53 CPU and GICv3, although they 14run in arm32 mode only. The CPU has support EL3, but no hypervisor (EL2) and 15it seems to lack VFP and NEON. 16 17The SoC is used in a number of cheap LTE to WiFi routers, both battery powered 18MiFis and stationary CPEs. In addition to the CPU these devices usually have 1964 MB Ram (although some is shared with the LTE chip), 128 MB NAND flash, an 20SDIO connected RTL8192-type Wifi chip limited to 2.4 ghz operation, USB 2, 21and buttons. Devices with as low as 32 MB or as high as 128 MB ram exist, as 22do devices with 8 or 16 MB of NOR flash. 23 24Some devices, especially the stationary ones, have 100 mbit Ethernet and an 25Ethernet switch. 26 27Usually the devices have LEDs for status indication, although some have SPI or 28I2C connected displays. 29 30Some have an SD card slot. If it exists, it is a better choice for the root 31file system because it easily outperforms the built-in NAND. 32 33The LTE interface runs on a separate DSP called ZSP880. It is probably derived 34from LSI ZSPs and has an undocumented instruction set. The ZSP communicates 35with the main CPU via SRAM and DRAM and a mailbox hardware that can generate 36IRQs on either ends. 37 38There is also a Cortex M0 CPU, which is responsible for early HW initialization 39and starting the Cortex A53 CPU. It does not have any essential purpose once 40U-Boot is started. An SRAM-based handover protocol exists to run custom code on 41this CPU. 42 432. Booting via USB 44--------------------------- 45 46The Boot ROM has support for booting custom code via USB. This mode can be 47entered by connecting a Boot PIN to GND or by modifying the third byte on NAND 48(set it to anything other than 0x5A aka 'Z'). A free software tool to start 49custom U-Boot and kernels can be found here: 50 51https://github.com/zx297520v3-mainline/zx297520v3-loader 52 53If USB download mode is entered but no boot commands are sent through USB, the 54device will proceed to boot normally after a few seconds. It is therefore 55possible to enable USB boot permanently and still leave the default boot files 56in place. 57 58https://github.com/zx297520v3-mainline/u-boot-mainline 59 60Contains an U-Boot version that can be used with the USB loader and sets up the 61CPU and interrupt controller to comply with Linux's booting requirements. 62 633. Building for built-in U-Boot 64------------------------------- 65The devices come with an ancient U-Boot that loads legacy uImages from NAND and 66boots them without a chance for the user to interrupt. The images are stored in 67files ap_cpuap.bin and ap_recovery.bin on a jffs2 partition named imagefs, 68usually mtd4. A file named "fotaflag" switches between the two modes. 69 70In addition to the uImage header, those files have a 384-byte signature header, 71which is used for authenticating the images on some devices. Most devices have 72this authentication disabled and it is enough to pad the uImage files with 384 73zero bytes. 74 75Builtin U-Boot also poorly sets up the CPU. Read the next section for details 76on this. It has no support for loading DTBs, so CONFIG_ARM_APPENDED_DTB is 77needed. 78 79So to build an image that boots from NAND the following steps are necessary: 80 811) Patch the assembly code from section 3 into arch/arm/kernel/head.S. 822) make zx29_defconfig 833) make [-j x] 844) cat arch/arm/boot/zImage arch/arm/boot/dts/zte/[device].dtb > kernel+dtb 855) mkimage -A arm -O linux -T kernel -C none -a 0x20008000 -d kernel+dtb uimg 866) dd if=/dev/zero bs=1 count=384 of=ap_recovery.bin 877) cat uimg >> ap_recovery.bin 888) Place this file onto imagefs on the device. Delete ap_cpuap.bin if the 89 free space is not enough. 909) Create the file fotaflag: echo -n FOTA-RECOVERY > fotaflag 91 92For development, booting ap_recovery.bin is recommended because the normal boot 93mode arms the watchdog before starting the kernel. 94 954. CPU and GIC Setup 96--------------------------- 97 98Generally CPU and GICv3 need to be set up according to the requirements spelled 99out in Documentation/arch/arm64/booting.rst. For zx297520v3 this means: 100 1011. GICD_CTLR.DS=1 to disable GIC security 1022. Enable access to ICC_SRE 1033. Disable trapping IRQs into monitor mode 1044. Configure EL2 and below to run in insecure mode. 1055. Configure timer PPIs to active-low. 106 107The kernel sources provided by ZTE do not boot either (interrupts do not work 108at all). They are incomplete in other aspects too, so it is assumed that there 109is some workaround similar to the one described in this document somewhere in 110the binary blobs. 111 112The assembly code below is given as an example of how to achieve this: 113 114:: 115 116 #include <linux/irqchip/arm-gic-v3.h> 117 #include <asm/assembler.h> 118 #include <asm/cp15.h> 119 120 @ Detect sane bootloaders and skip the hack 121 ldr r3, =0xf2000000 122 ldr r3, [r3] 123 ldr r4, =(GICD_CTLR_ARE_NS | GICD_CTLR_DS) 124 cmp r3, r4 125 beq skip_zx_hack 126 @ This allows EL1 to handle ints hat are normally handled by EL2/3. 127 ldr r3, =0xf2000000 128 str r4, [r3] 129 130 cps #MON_MODE 131 132 @ Work in non-secure physical address space: SCR_EL3.NS = 1. At least the UART 133 @ seems to respond only to non-secure addresses. I have taken insipiration from 134 @ Raspberry pi's armstub7.S here. 135 mov r3, #0x131 @ non-secure, Make F, A bits in CPSR writeable 136 @ Allow hypervisor call. 137 mcr p15, 0, r3, c1, c1, 0 138 139 @ AP_PPI_MODE_REG: Configure timer PPIs (10, 11, 13, 14) to active-low. 140 ldr r3, =0xF22020a8 141 ldr r4, =0x50 142 str r4, [r3] 143 ldr r3, =0xF22020ac 144 ldr r4, =0x14 145 str r4, [r3] 146 147 @ Enable EL2 access to ICC_SRE (bit 3, ICC_SRE_EL3.Enable). Enable system reg 148 @ access to GICv3 registers (bit 0, ICC_SRE_EL3.SRE) for EL1 and EL3. 149 mrc p15, 6, r3, c12, c12, 5 @ ICC_SRE_EL3 150 orr r3, #0x9 @ FIXME: No defines for SRE_EL3 values? 151 mcr p15, 6, r3, c12, c12, 5 152 mrc p15, 0, r3, c12, c12, 5 @ ICC_SRE_EL1 153 orr r3, #(ICC_SRE_EL1_SRE) 154 mcr p15, 0, r3, c12, c12, 5 155 156 @ Like ICC_SRE_EL3, enable EL1 access to ICC_SRE and system register access 157 @ for EL2. 158 mrc p15, 4, r3, c12, c9, 5 @ ICC_SRE_EL2 aka ICC_HSRE 159 orr r3, r3, #(ICC_SRE_EL2_ENABLE | ICC_SRE_EL2_SRE) 160 mcr p15, 4, r3, c12, c9, 5 161 isb 162 163 @ Back to SVC mode 164 cps #SVC_MODE 165 skip_zx_hack: 166 167