RK3566(泰山派 TSPI)主线U-Boot移植与主线WiFi驱动配置

发布于 — 2026 年 08 月 13 日

目标板:立创泰山派 目标:主线 U-Boot + rkbin 闭源二进制 + TEE 构建启动链,从零构建 Debian rootfs,调试 WiFi驱动.


一,启动链移植(主线 U-Boot + rkbin + OP-TEE)

1.1 组件来源(全部 GitHub 可查)

组件来源版本/内容
U-Bootgithub.com/u-boot/u-boottag v2026.07 + 板级 lckfb-tspi-rk3566_defconfig
DDR 初始化 (TPL)github.com/rockchip-linux/rkbinbin/rk35/rk3566_ddr_1056MHz_v1.25.bin(闭源)
BL31 (TF-A)github.com/ARM-software/arm-trusted-firmware主线(master),PLAT=rk3568 SPD=opteed 自编
OP-TEE (BL32)github.com/rockchip-linux/rkbin → 转 ELFrk3568_bl32_v2.16.bin(闭源)→ ELF 参与构建

OP-TEE 主线(optee_os)目前不支持 rk3568/rk3566,自编 TEE 不可用;ATF 主线已支持 rk3568,但必须用 SPD=opteed 编译,否则不加载 OP-TEE.BL32 只能使用瑞芯微闭源固件,通过 aarch64-linux-gnu-ld 把 bin 转成 ELF 交给 binman 打包(方法参考 OrangePi-3B 移植 Linux —— 构建 U-Boot1).TZDRAM 固定 0x08400000,与 rkbin RK3568TRUST.ini 的 BL32 地址一致,保证与闭源信任链兼容.

1.2 编译命令(开源 ATF + 闭源 BL32 转 ELF)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 1) ATF(必须 SPD=opteed,否则 BL31 不加载 OP-TEE)
git clone https://github.com/ARM-software/arm-trusted-firmware.git
cd arm-trusted-firmware
make CROSS_COMPILE=aarch64-linux-gnu- PLAT=rk3568 SPD=opteed all
# 产物: build/rk3568/release/bl31/bl31.elf

# 2) BL32(rkbin 闭源 bin 转 ELF,load/entry=0x8400000)
git clone https://github.com/rockchip-linux/rkbin.git
cd rkbin
aarch64-linux-gnu-ld -b binary -Ttext=0x08400000 \
    -o bin/rk35/rk3568_bl32_v2.16.elf bin/rk35/rk3568_bl32_v2.16.bin

# 3) U-Boot(主线 v2026.07,板级 defconfig;与 ATF/rkbin 同级目录,用相对路径)
cd u-boot
export CROSS_COMPILE=aarch64-linux-gnu-
export BL31=../arm-trusted-firmware/build/rk3568/release/bl31/bl31.elf
export ROCKCHIP_TPL=../rkbin/bin/rk35/rk3566_ddr_1056MHz_v1.25.bin
export TEE=../rkbin/bin/rk35/rk3568_bl32_v2.16.elf
make lckfb-tspi-rk3566_defconfig
make -j$(nproc)   # 或 make O=build -j$(nproc)

产物:idbloader.img(TPL+SPL),u-boot.itb(BL31+OP-TEE+U-Boot),u-boot-rockchip.bin(单镜像).

项目目录结构(Custom-uboot/):

Custom-uboot/
├── u-boot/                    # 主线 U-Boot v2026.07(板级 defconfig + O=build 产物)
├── arm-trusted-firmware/      # ATF 主线(SPD=opteed 自编)
│   └── build/rk3568/release/bl31/bl31.elf
├── rkbin/                     # 瑞芯微闭源固件
│   └── bin/rk35/
│       ├── rk3566_ddr_1056MHz_v1.25.bin      # ROCKCHIP_TPL
│       └── rk3568_bl32_v2.16.elf             # TEE(bin 转 ELF 后产物)
├── mainline-linux/            # 主线内核 7.2.0(Image + dtb + modules)
├── parameter.txt              # 分区表(RKDevTool 烧录用)
├── boot.img                   # boot 分区镜像(ext4:extlinux.conf + Image + dtb)
├── rootfs.img                 # rootfs 分区镜像(ext4,rsync 打包产物)
└── build-rootfs.sh            # Debian rootfs 构建脚本(增量 + 中科大源)

相对路径的基准:在 u-boot/ 目录内执行 make,../ 指向与 u-boot 平级的 arm-trusted-firmware/rkbin/.

参考:OrangePi-3B 移植 Linux —— 构建 U-Boot1.

1.3 为什么 BL32 要转 ELF:opteed 与 binman

主线 U-Boot 的 binman 靠 ELF 段信息生成 FIT 里 tee-1 的 load/entry(rockchip-u-boot.dtsi@tee-SEQsplit-elf),直接塞 bin 无法确定加载地址.aarch64-linux-gnu-ld -b binary -Ttext=0x08400000 转出的 ELF:entry=0x8400000,u-boot.itb 中 tee-1 镜像 load=0x8400000 / entry=0x8400000,ATF opteed 把它加载到 TZDRAM(0x8400000 起,32MB),与内核 dts 的保留内存 optee@8400000(no-map)一一对应.

(历史:曾尝试自编 optee_os 的 PR #7887 rk3568 移植——64 位 core 默认同时编译 ta_arm32 库会报 -mthumb,需加 CFG_USER_TA_TARGETS=ta_arm64 绕过——但该移植最终不可用,rk3566 只能走"闭源 BL32 转 ELF"路线.)


二,U-Boot 板级问题

2.1 开机自动引导:GPT bootable 标记

bootflow scan 无条件设置 BOOTFLOWIF_ONLY_BOOTABLE(cmd/bootflow.c).GPT 中没有任何分区带 bootable 标记时,bootstd 只扫描分区 1,导致 boot 分区(分区 3)被跳过 → 无法自动引导.

解决:parameter.txt 中 boot 分区加 :bootable 标记:

CMDLINE: mtdparts=:0x00002000@0x00004000(uboot),0x00002000@0x00006000(misc),0x00020000@0x00008000(boot:bootable),…

2.2 NPU 电源域 panic

内核启动时 rockchip-pm-domain ... failed to get ack on domain 'npu'panic_on_set_idle panic.

根因:Linux power-domain 驱动不知道 NPU 域的供电 regulator,而 U-Boot 未初始化 vdd_npu → NPU 上电失败.

解决(Armbian 官方已合并方案,armbian/build PR #7025,作者 Jonas Karlman):在板级 -u-boot.dtsi 给 vdd_npu 加:

1
2
3
4
5
&vdd_npu {
    regulator-always-on;
    regulator-boot-on;
    regulator-init-microvolt = <900000>;
};

2.3 OP-TEE 与 kernel_comp_addr_r 内存冲突

OP-TEE 布局:TZDRAM 0x08400000(32MB)+ SHMEM 0x0a400000(4MB),安全区到 0x0a800000.

主线 U-Boot rk3568_common.h 默认 kernel_comp_addr_r=0x0a000000(128MB),与安全区重叠.且 PR #7887 用 DDR 防火墙保护 TZDRAM,普通世界写入会 SError.

解决(2026-08-26 更新:改用 U-Boot 后期添加的 OPTEE 配置,不再覆盖板级 env):

不再改 ENV_MEM_LAYOUT_SETTINGS 板级 env,改为在 U-Boot 里启用官方 OPTEE 支持配置,让 u-boot.itb 直接打包 OP-TEE(BL32)由 ATF opteed 加载,并把 optee 节点/保留内存同步给内核(git diff 可查实际改动):

  1. defconfig 添加(configs/lckfb-tspi-rk3566_defconfig):

    CONFIG_OPTEE_LIB=y
    CONFIG_OPTEE_IMAGE=y
    CONFIG_OPTEE_TZDRAM_SIZE=0x02000000
  2. 构建时设 TEE=<bl32 转出的 ELF> 环境变量(rkbin 闭源 rk3568_bl32_v2.16.bin 转 ELF,方法见 1.2),binman 自动把 tee-1 镜像打进 u-boot.itb:load=0x8400000 / entry=0x8400000,与 rkbin RK3568TRUST.ini 的 BL32 地址一致;

  3. 设备树添加 optee 节点与保留内存(dts/upstream/src/arm64/rockchip/rk3566-lckfb-tspi.dts,U-Boot 与内核各一份):

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    firmware {
        optee {
            compatible = "linaro,optee-tz";
            method = "smc";
        };
    };
    
    reserved-memory {
        optee@8400000 {
            reg = <0x0 0x8400000 0x0 0x1000000>;
            no-map;
        };
    };
  4. U-Boot 的 lib/optee/optee.c(optee_copy_fdt_nodes,CONFIG_OPTEE_LIB)启动时把 /firmware/optee 与 reserved-memory 拷入内核 DT,内核 CONFIG_OPTEE=y 驱动直接探测;

  5. 内核侧无需额外启动参数(optee.arm_smccc_version 仅在 SMC 版本协商异常时兜底).


三,Debian rootfs 构建

本节内容与项目 build-rootfs.sh 一致:基础版本 Debian 13 (trixie),镜像源 中科大 USTC,支持增量构建(已存在 rootfs/ 时不清空,不重新 debootstrap,直接原地 apt 更新 + 覆盖配置).

3.1 debootstrap(arm64 + qemu-user-static + 国内镜像)

1
2
3
4
sudo debootstrap --arch=arm64 --variant=minbase --foreign \
    trixie "$ROOTFS" https://mirrors.ustc.edu.cn/debian/
sudo cp /usr/bin/qemu-aarch64-static "$ROOTFS/usr/bin/"
sudo chroot "$ROOTFS" /debootstrap/debootstrap --second-stage

chroot 内继续 apt 安装其余包.脚本里 apt 源统一切为中科大:deb822 格式(debian.sources)改 URIs:,deb 格式(sources.list)改 URL,并保留 non-free-firmware 组件(WiFi 固件包在此组件下);切 https 源前先装 ca-certificates,否则证书校验失败.

3.2 brcm-firmware 配置(重点)

WiFi/BT 模组 AP6212A(BCM43430A1)需要三样东西:

  1. WiFi 固件:firmware-brcm80211 包(位于 non-free-firmware 组件)提供 brcmfmac43430-sdio.bin(软链接到 ../cypress/cyfmac43430-sdio.bin)和一堆板级 nvram,其中就包含 AP6212 专用的 brcmfmac43430-sdio.AP6212.txt;

  2. WiFi nvram 软连接(最容易漏的一步):brcmfmac 驱动只请求固定文件名 brcmfmac43430-sdio.txt,而包内只有板级命名 brcmfmac43430-sdio.AP6212.txt,必须建软连接,否则报 Direct firmware load for brcm/brcmfmac43430-sdio.txt failed with error -2:

    1
    
    ln -s brcmfmac43430-sdio.AP6212.txt /lib/firmware/brcm/brcmfmac43430-sdio.txt

    为什么要用软连接而不是拷贝:Debian 固件包自身就是这么组织的(包内 brcmfmac43430-sdio.bin,各板级 .txt 之间大量用符号链接,例如 brcmfmac43430-sdio.sinovoip,bpi-m2-plus.txt -> brcmfmac43430-sdio.AP6212.txt).软连接在固件包升级时自动指向新版本,不会残留旧文件,也不会出现两份 AP6212 nvram 内容不一致的问题.

  3. 蓝牙固件:bluez-firmware 包的 BCM43430A1.hcd(30KB)与 AP6212A1 芯片补丁不兼容,会报 command 0xfc18 tx timeout,必须用瑞芯微 SDK 原配 48KB 版本(rkwifibt/AP6212A1/bt/BCM4343A1.hcd)覆盖到 /lib/firmware/brcm/BCM43430A1.hcd.

3.3 打包 ext4 镜像

1
2
3
4
5
6
truncate -s ${SIZE_MB}M rootfs.img
sudo mkfs.ext4 -q -F -m 0 -L rootfs rootfs.img
sudo mount -o loop rootfs.img $MNT
sudo rsync -a --exclude='/proc/*' --exclude='/sys/*' --exclude='/dev/*' \
    --exclude='/run/*' --exclude='/tmp/*' "$ROOTFS/" "$MNT/"
sync && sudo umount $MNT

/proc,/sys,/dev,/run,/tmp 是内核启动时才挂载的虚拟文件系统,不入镜像(保留空目录即可),否则体积虚增且拷入的是运行时垃圾. 坑:sudo -S 管道传密码时,若凭据已缓存,密码行会污染 stdin 写入文件内容,应改为"先写临时文件再 sudo cp".

3.4 linaro 镜像 emergency mode 教训

linaro 预置镜像的 /etc/fstab 残留 PARTLABEL=oem,PARTLABEL=userdata 挂载项,删掉这两个分区后 systemd 挂载失败进 emergency mode.自建 rootfs 时 fstab 只保留 //tmp 即可.


四,WiFi 调试(bcmdhd / AP6212)

说明:本章为使用 rkSDK(bcmdhd 驱动)时的排障记录;当前主线方案改用 brcmfmac 驱动(BCM43430A1 / AP6212A 模组),其中 4.3 的 MAC 地址问题只在 rkSDK 的 bcmdhd 下出现,brcmfmac 无"拒绝本地管理位 MAC"的行为.

4.1 驱动选型

rkSDK 的 bcmdhd 有 in-tree 与 external 两份源码,diff 0 行完全一致,仅编译宏不同:

版本关键宏固件加载
in-tree(CONFIG_AP6XXX=m)GET_OTP_MAC_ENABLE,DHD_REQUEST_FW_PATHrequest_firmware()/lib/firmware/
external(SDK 00-wifibt.sh 独立编译)无上述宏filp_open 支持绝对路径 → /vendor/etc/firmware/

两者都有 SDIO modalias,modprobe 均可自动加载.

4.2 固件路径的坑

in-tree 版用 request_firmware(),内核 6.1 没有绝对路径特判(drivers/base/firmware_loader/main.c 无条件 snprintf("%s/%s", ...)):驱动请求 /fw_bcm43438a1.bin 会被拼成 /lib/firmware//fw_bcm43438a1.bin.所以固件必须放 /lib/firmware/,否则报错:

1
Direct firmware load for /fw_bcm43438a1.bin failed with error -2

4.3 MAC 地址问题(仅 rkSDK/bcmdhd)

主线 brcmfmac 无"拒绝本地管理位 MAC"的行为,此坑只属于 bcmdhd.

现象:固件/NVRAM 加载成功,但 preinit 设 MAC 失败,随后驱动反复重启:

1
dhd_legacy_preinit_ioctls: can't set MAC address MAC=7e:2c:4c:fe:d0:33, error=-21

原因:bcmdhd 固件拒绝任何带"本地管理位"的 MAC(BCME_BADADDR=-21);udev 的 MACAddressPolicy=persistent 与 NetworkManager 默认的 scan-rand-mac-address=yes 都会给 wlan0 赋随机 MAC,驱动下次 dhd_open 硬 SET 被拒 → NM 反复重试 → 无限循环重启.

解决:让系统完全不碰 wlan0 的 MAC,preinit 直接用固件 OTP MAC:

  1. /etc/systemd/network/99-wlan0-nomac.link(systemd.link(5)):

    1
    2
    3
    4
    5
    
    [Match]
    OriginalName=wlan0
    
    [Link]
    MACAddressPolicy=none
  2. /etc/NetworkManager/conf.d/wifi-mac.conf(NetworkManager.conf(5)):

    1
    2
    3
    4
    5
    6
    
    [connection]
    wifi.cloned-mac-address=preserve
    
    [device]
    match-device=interface-name:wlan0
    wifi.scan-rand-mac-address=no

坑:match-device=driver:bcmdhd 匹配不到(SDIO 驱动名是 bcmsdh_sdmmc 而非 bcmdhd),必须用 match-device=interface-name:wlan0;wifi.scan-rand-mac-address 是 device 段属性,放 [connection] 段会报 unknown key.

4.4 加载方式与蓝牙(简述)

  • 加载:SDK 官方用 wifibt-init.service 早期 insmod(do_insmod 幂等);标准方式为 modprobe 自动加载(SDIO modalias 02d0:a9a6).教训:NetworkManager 若静态启用且无 MAC 保护,会反复重启驱动(buildroot 官方镜像里 NM 仅 D-Bus 激活).
  • 蓝牙:brcm_patchram_plus1 走 UART 加载 /lib/firmware/BCM4343A1.hcd;缺该工具时 wifibt-init 的 BT guardian 会每 1.5s 反复 rfkill 复位.测试:装 bluezbluetoothctl(list 看 hci0,scan on 扫设备).

五,烧录(RKDevTool / rkdeveloptool)

分区布局以本项目 parameter.txt 为准(LBA 以 512B 扇区计),CMDLINE:

mtdparts=rk29xxnand;mtdparts=:0x00003fc0@0x00000040(idbloader),0x00002000@0x00004000(uboot),0x00020000@0x00008000(boot:bootable),0x00c00000@0x00028000(rootfs:grow)

注意:parameter.txt 中实际为单行,此处以普通文本展示,由浏览器自动折行;分区之间用逗号分隔((boot:bootable) 后必须是逗号,不能用分号/无分隔).

分区偏移大小镜像
idbloader0x400x3FC0 (8160K)idbloader.img
uboot0x40000x2000 (4M)u-boot.itb
boot0x80000x20000 (64M)boot.img(ext4,extlinux.conf + Image + dtb,bootable)
rootfs0x280000xC00000(grow)rootfs.img

注意:Rockchip 工具按 CMDLINE 生成 GPT,每个分区必须带 @偏移,缺偏移的分区会被直接丢弃(曾导致 rootfs 分区丢失,part list 只剩 3 个分区).分区表用 mtdparts=rk29xxnand; 前缀声明 mtd-id.

  • LOADER 模式:U-Boot 下 rockusb 0 mmc 0(需 dr_mode=peripheral 修复),或按住烧录键上电
  • MaskROM 的 loader 文件:rkbin/tools/boot_merger RKBOOT/RK3566MINIALL.ini 合成

六,经验总结

  1. 闭源驱动(bcmdhd)与标准 Linux 工具链的兼容性差:固件路径(request_firmware 无绝对路径特判)和 MAC(拒绝本地管理位)两个坑都是 bcmdhd 特有问题,需要配置层绕过.
  2. NetworkManager 的 MAC 随机化策略是"看不见的坑":默认 scan-rand-mac-address=yes,配合 vendor 驱动极易触发反复重启.
  3. in-tree 与 external 驱动源码同源:先确认编译宏差异,不要盲目换驱动.
  4. SDK 官方镜像是最可靠的参考:buildroot 官方用 00-wifibt.sh 安装驱动/固件/服务,NM 仅 D-Bus 激活——复刻其服务布局是解决 WiFi 问题的关键线索.
  5. RK3566 主线支持尚不完整:OP-TEE 无主线支持(用"闭源 BL32 转 ELF"方案绕过,参考 6xyun 文章1),U-Boot 缺 vdd_npu 初始化,rockusb otg 模式不可用,都需要板级补丁或配置绕过.

  1. OrangePi-3B 移植 Linux —— 构建 U-Boot.该文章采用 BY-NC-ND 许可协议,本文仅引用其方法并注明出处. ↩︎ ↩︎ ↩︎