RK3528 网络与 WiFi 驱动修改迁移说明
本文档整理本次 RK3528 板级调试中最终生效的修改,方便后续迁移到其它 SDK。
适用背景:
- SoC:RK3528
- 系统:Android 13 / Linux kernel 5.10
- WiFi:AP6275S,SDIO 接口
- 板载 GMAC PHY:RTL8211F,对应系统里的
eth1 - PCIe 网卡:RTL8111F/RTL8168,对应系统里的
eth2
最终验证结果:
- AP6275S WiFi 可正常工作,
wlan0可联网。 - RTL8211F 已由 Realtek PHY 驱动识别,
eth1可拿 IP 并 ping 通网关。 - RTL8111F/RTL8168 已由
r8168.ko驱动绑定,eth2可拿 IP 并 ping 通网关。 - RTL8111F/RTL8168 RJ45 指示灯已点亮,生效 LED 配置值为
0x00ca。
目前仍存在的问题:
- OpenWRT SDK百兆网口握手满,仅10Mbps,怀疑delay没调好;
- PCIE网卡跑不满千兆,约700Mbps;
- SDIO网卡遇到两个无线设备UDP测速,负载过高,会直接搞炸SDIO链路和驱动
1. AP6275S WiFi 修改
1.1 关闭 bcmdhd OOB 中断
文件:
external/wifi_driver/bcmdhd/Makefile修改:
-CONFIG_BCMDHD_OOB := y+CONFIG_BCMDHD_OOB := n原因:
- 当前板级 host-wake/OOB 中断没有按原 SDK 默认方式使用。
- 保留 OOB 时,bcmdhd 可能卡在 host-wake IRQ 相关初始化。
- 关闭 OOB 后,WiFi 模块可正常加载和联网。
1.2 AP6275S DTS 板级项
文件:
kernel-5.10/arch/arm64/boot/dts/rockchip/rk3528/rp-wifi-bt-vs2275s-rk3528.dtsi当前可工作的关键点:
wireless_wlan: wireless-wlan { compatible = "wlan-platdata"; rockchip,grf = <&grf>; wifi_chip_type = "ap6275s"; status = "okay";};SDIO power sequence 当前使用:
sdio_pwrseq: sdio-pwrseq { compatible = "mmc-pwrseq-simple"; pinctrl-names = "default"; pinctrl-0 = <&wifi_enable_h &clkm1_32k_out>; post-power-on-delay-ms = <200>; reset-gpios = <&gpio1 RK_PA7 GPIO_ACTIVE_LOW>;};SDIO 频率和模式当前做了保守处理:
&sdio0 { max-frequency = <50000000>; no-sd; no-mmc; supports-sdio; cap-sd-highspeed; keep-power-in-suspend; non-removable; mmc-pwrseq = <&sdio_pwrseq>; /delete-property/ rockchip,use-v2-tuning; /delete-property/ sd-uhs-sdr104; status = "okay";};迁移注意:
reset-gpios、wifi_enable_h、clkm1_32k_out必须按新板原理图确认。- 如果新板 host-wake IRQ 硬件连接可靠,可以重新评估 OOB;否则优先沿用
CONFIG_BCMDHD_OOB := n。 max-frequency = <50000000>是保守稳定值。新板信号质量确认后,可以再评估更高频率。
2. RTL8211F GMAC PHY 修改
2.1 内建 Realtek PHY 驱动
文件:
kernel-5.10/arch/arm64/configs/rockchip_defconfig修改:
CONFIG_R8168=mCONFIG_REALTEK_PHY=y原因:
- 未启用
CONFIG_REALTEK_PHY时,RTL8211F 会按Generic PHY处理。 - 启用后,系统可识别为 RTL8211F Gigabit Ethernet PHY。
- 本次验证中,启用后
eth1可正常千兆链路、获取 IPv4、ping 通网关。
迁移注意:
- 这项影响内核镜像,需要重新编译并刷入
boot。 - 如果其它 SDK 使用模块化 PHY,也可以设为
m,但要保证模块被打包并在 GMAC probe 前可用。当前项目直接设为y更稳。
3. RTL8111F/RTL8168 PCIe 网卡修改
3.1 确认 r8168 驱动配置
文件:
kernel-5.10/arch/arm64/configs/rockchip_defconfig需要保证:
CONFIG_R8168=m当前 SDK 原本已有该配置,但模块没有被打进 vendor_dlkm,所以设备枚举后没有生成可用网口。
3.2 把 r8168.ko 打进 vendor_dlkm
文件:
device/rockchip/rk3528/device.mk修改:
BOARD_VENDOR_KERNEL_MODULES += \ device/rockchip/rk3528/rkvtunnel.ko \ kernel-5.10/drivers/net/ethernet/realtek/r8168/r8168.ko原因:
CONFIG_R8168=m只表示会编译模块,不代表模块会被放进镜像。- Android vendor kernel module 需要通过
BOARD_VENDOR_KERNEL_MODULES等机制打包到vendor_dlkm。 - 打包并刷入后,PCIe 设备
10ec:8168可绑定到r8168,系统生成eth2。
3.3 DTS 增加 PCIe 子节点和 LED 配置
文件:
kernel-5.10/arch/arm64/boot/dts/rockchip/rk3528/rp-eth-pcie2gmac-rk3528.dtsi当前可工作的 RTL8111F/RTL8168 子节点:
&pcie2x1 { pinctrl-0 = <&custom_pcie_pins>; reset-gpios = <&gpio4 RK_PB4 GPIO_ACTIVE_HIGH>; vpcie3v3-supply = <&vcc3v3_pcie20>; status = "okay";
pcie@0,0 { reg = <0x000000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>;
rtl8111h: pcie-eth@0,0 { compatible = "pci10ec,8168"; reg = <0x000000 0 0 0 0>; realtek,led-data = <0xca>; }; };};当前可工作的 PCIe pinctrl:
&pinctrl { pcie { custom_pcie_pins: custom_pcie_pins { rockchip,pins = <4 RK_PC0 4 &pcfg_pull_none>, <4 RK_PB5 4 &pcfg_pull_none>, <4 RK_PC1 4 &pcfg_pull_none>; }; };};说明:
compatible = "pci10ec,8168"用来让内核给 PCIe 设备挂上 OF node。realtek,led-data = <0xca>是本板实测有效的 RJ45 LED 配置值。- 运行时读取 sysfs 会看到
ca000000,这是 device tree cell 的大端显示方式,对应值仍是0x000000ca。
迁移注意:
reset-gpios、CLKREQ#、PERST#、WAKE#pinctrl 必须按新板原理图确认。- 如果新 SDK PCIe 控制器节点名、bus 地址或 slot/function 不同,需要按实际枚举位置调整子节点。
0x00ca是本板 RTL8111F/RTL8168 指示灯实测有效值。其它磁性器件或 LED 接法不同,可能需要重新试 LED 值。
3.4 修改 r8168 驱动,消费 realtek,led-data
文件:
kernel-5.10/drivers/net/ethernet/realtek/r8168/r8168_n.c原厂 r8168 驱动只读取 CustomLED 寄存器,不会主动读取 DTS 里的 realtek,led-data。所以即使 DTS 写了 <0xca>,如果不改驱动,RJ45 灯仍可能不亮。
增加头文件:
#include <linux/of.h>增加 helper:
static voidrtl8168_apply_dt_led_data(struct rtl8168_private *tp){ struct pci_dev *pdev = tp->pci_dev; u32 led_data;
if (!pdev || !pdev->dev.of_node) return;
if (of_property_read_u32(pdev->dev.of_node, "realtek,led-data", &led_data)) return;
tp->NicCustLedValue = (u16)led_data; RTL_W16(tp, CustomLED, tp->NicCustLedValue); dev_info(&pdev->dev, "apply realtek,led-data=0x%04x to CustomLED\n", tp->NicCustLedValue);}在 rtl8168_init_software_variable() 中,读取默认 LED 后调用:
tp->NicCustLedValue = RTL_R16(tp, CustomLED);rtl8168_apply_dt_led_data(tp);在 rtl8168_init_one() 中,rtl8168_hw_reset(dev); 后再调用一次:
rtl8168_hw_init(dev);
rtl8168_hw_reset(dev);rtl8168_apply_dt_led_data(tp);为什么写两次:
- 第一次保证驱动内部变量
NicCustLedValue与 DTS 一致。 - 第二次放在硬件 reset 后,避免 reset 把
CustomLED恢复成芯片默认值。 - 本次验证日志中能看到两次打印:
r8168 0000:01:00.0: apply realtek,led-data=0x00ca to CustomLEDr8168 0000:01:00.0: apply realtek,led-data=0x00ca to CustomLED4. 编译步骤
以下命令以 SDK 根目录 /home/elf/rk3528 为例。
4.1 修改 defconfig 或 DTS 后重编 boot
涉及这些内容时需要重编并刷 boot:
rockchip_defconfig- DTS / DTB
- 内建驱动
命令:
cd /home/elf/rk3528./build.sh -C -K -J4生成的 boot 镜像位置按 SDK 输出为准,本次使用:
rockdev/Image-rk3528_box/boot.img4.2 只改 r8168.ko 后重编模块
如果只改了:
kernel-5.10/drivers/net/ethernet/realtek/r8168/r8168_n.c可以只重编 r8168 模块:
cd /home/elf/rk3528/kernel-5.10export PATH=/home/elf/rk3528/prebuilts/clang/host/linux-x86/clang-r450784d/bin:/home/elf/rk3528/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin:$PATHmake ARCH=arm64 LLVM=1 LLVM_IAS=1 CROSS_COMPILE=aarch64-linux-gnu- M=drivers/net/ethernet/realtek/r8168 -j4然后重新打包 vendor_dlkm.img:
cd /home/elf/rk3528source build/envsetup.shlunch rk3528_box-userdebugmake vendor_dlkmimage -j4输出位置:
out/target/product/rk3528_box/vendor_dlkm.img5. 刷机步骤
5.1 刷 boot
adb reboot bootloaderfastboot flash boot boot.imgfastboot reboot如果设备使用 A/B slot,需要按实际分区策略确认是否刷 boot_a / boot_b。
5.2 刷 vendor_dlkm
进入 userspace fastboot:
adb reboot fastbootfastboot getvar is-userspace确认输出:
is-userspace: yes刷入:
fastboot flash vendor_dlkm vendor_dlkm.imgfastboot reboot6. 验证命令
6.1 AP6275S WiFi
adb shell ip addr show wlan0adb shell dmesg | grep -iE "bcmdhd|dhd|wlan"预期:
wlan0存在。- WiFi 固件加载成功。
- 连接 AP 后可获取 IPv4。
6.2 RTL8211F eth1
adb shell dmesg | grep -iE "RTL8211F|realtek|stmmac|eth1"adb shell ip addr show eth1adb shell ping -I eth1 -c 3 192.168.1.1预期:
- dmesg 中能看到 RTL8211F / Realtek PHY。
- 插线后
eth1为LOWER_UP。 - 可获取 IPv4 并 ping 通网关。
6.3 RTL8111F/RTL8168 eth2
确认 PCIe 设备绑定到 r8168:
adb shell readlink /sys/bus/pci/devices/0000:01:00.0/driver预期:
../../../../../../bus/pci/drivers/r8168确认 DTS LED 属性存在:
adb shell od -An -tx4 /sys/bus/pci/devices/0000:01:00.0/of_node/realtek,led-data预期:
ca000000确认驱动写入 LED 配置:
adb shell dmesg | grep -iE "realtek,led-data|r8168|eth2"adb shell cat /proc/net/r8168/eth2/driver_var | grep NicCustLedValue预期:
apply realtek,led-data=0x00ca to CustomLEDNicCustLedValue 0xca确认链路和网络:
adb shell cat /sys/class/net/eth2/carrieradb shell ip addr show eth2adb shell ping -I eth2 -c 3 192.168.1.1预期:
carrier为1。eth2为LOWER_UP。- 获取 IPv4,本次实测为
192.168.1.226/24。 - ping 网关 3/3 成功,0% 丢包。
- RJ45 物理灯点亮。
7. 本次关键实测结果
RTL8111F/RTL8168 最终日志:
r8168 0000:01:00.0: apply realtek,led-data=0x00ca to CustomLEDr8168 0000:01:00.0: apply realtek,led-data=0x00ca to CustomLEDr8168: eth2: link updriver_var:
chipset_name RTL8168H/8111Hspeed 1000NicCustLedValue 0xca网络:
eth2: 192.168.1.226/24ping 192.168.1.1: 3 transmitted, 3 received, 0% packet loss物理结果:
RTL8111F RJ45 指示灯已亮8. 迁移到其它 SDK 时的检查顺序
- 先确认 DTS 文件路径和实际被编译的 board dts。不要只改名字相似但未参与编译的 dts。
- WiFi 先确认 AP6275S 电源、reset GPIO、32K 时钟、SDIO 频率,再决定是否关闭 OOB。
- RTL8211F 先确认
CONFIG_REALTEK_PHY已启用,再看 dmesg 是否仍是Generic PHY。 - RTL8111F/RTL8168 先确认 PCIe 枚举到
10ec:8168,再确认r8168.ko是否被打进vendor_dlkm。 - LED 不亮但网络正常时,优先检查
realtek,led-data是否进入 OF node,以及 r8168 是否实际写了CustomLED。 - 如果新板 LED 接法不同,保留驱动读取 DTS 的机制,只替换 DTS 中的
realtek,led-data数值重新刷测。
9. Android 跨板启动卡 U-Boot 的处理
9.1 现象
同一套 Android 镜像烧录到另一块 RK3528 板后,OpenWrt 可正常启动,但 Android 停在 U-Boot 命令行。
串口参数:
COM4, 115200典型日志第一阶段:
boot mode: recovery (misc)ANDROID: reboot reason: "recovery"avb_slot_verify.c:774: ERROR: recovery: Error verifying vbmeta image: invalid vbmeta headerAVB verify failedAndroid boot failed, error -1.这说明 U-Boot 不是找不到 eMMC,也不是 DDR 初始化失败,而是 misc 分区里的 Android BCB 残留了 recovery 请求。
9.2 清除 misc 中的 recovery BCB
本次实测在 misc 分区开头和 misc + 16KB 位置都能看到:
boot-recoveryrecovery--wipe_allU-Boot 分区表中 misc 起始 LBA 为:
0x00008000清除 misc 开头 0x40 个扇区即可同时覆盖 Android 10+ 的 0 偏移 BCB 和 Rockchip 老的 16KB 偏移 BCB:
mmc dev 0mw.b 0x10000000 0 0x8000mmc write 0x10000000 0x8000 0x40mmc read 0x10000000 0x8000 0x40md.b 0x10000000 0x100reset读回预期:
10000000: 00 00 00 00 00 00 00 00 ...清除后,启动日志应从:
boot mode: recovery (misc)RESC: 'recovery'变成:
boot mode: NoneRESC: 'boot'ANDROID: reboot reason: "(none)"9.3 清除 misc 后仍卡 AVB 的原因
清掉 BCB 后,本次又遇到第二阶段失败:
Vboot=0, AVB images, AVB verifylib/avb/libavb_user/avb_ops_user.c: trusty_read_rollback_index failedavb_slot_verify.c:884: ERROR: vbmeta: Error getting rollback index for location.AVB verify failedAndroid boot failed, error -1.同时 OP-TEE/RPMB 日志有:
tee_rpmb_verify_key_sync_counter: Verify key returning 0xffff000ftee_rpmb_init: Verify key failed!Make sure key here matches device key本项目 Android 构建输出为开发状态:
BOARD_AVB_ENABLE=false对应 vbmeta.img 信息:
Algorithm: NONERollback Index: 0Flags: 2也就是说,开发版镜像本来不应因为 RPMB rollback index 阻塞启动。但当前 U-Boot 配置启用了 CONFIG_OPTEE_CLIENT=y,read_rollback_index() 会去 OP-TEE/RPMB 读取回滚索引。换板后 RPMB key 不匹配,导致读取失败,最终让 AVB 校验失败。
9.4 U-Boot 修复补丁
文件:
u-boot/lib/avb/libavb_user/avb_ops_user.c在 read_rollback_index() 中增加:
static AvbIOResult read_rollback_index(AvbOps *ops, size_t rollback_index_location, uint64_t *out_rollback_index){ if (out_rollback_index) {#ifndef CONFIG_ANDROID_AVB_ROLLBACK_INDEX *out_rollback_index = 0;
return AVB_IO_RESULT_OK;#endif#ifdef CONFIG_OPTEE_CLIENT ...原因:
- 当前
u-boot/.config中CONFIG_ANDROID_AVB_ROLLBACK_INDEX未启用。 - 开发版
BOARD_AVB_ENABLE=false,vbmeta.img是 unsigned/debug 形态。 - 在未启用 rollback index 保护时,直接返回 0 更符合开发版启动预期,也符合
avb_ops_user.h中“read_rollback_index returns 0”的说明。 - 这样不会改 Android 分区内容,只修改 U-Boot 的 AVB rollback index 读取策略。
迁移注意:
- 这个补丁适合开发调试版 Android 镜像。
- 如果量产启用了 AVB、安全启动、RPMB rollback index 管理,不要直接套用该绕过逻辑,应改为正确初始化 RPMB key 和 rollback index。
9.5 重编与刷入 U-Boot
重编:
cd /home/elf/rk3528./build.sh -U -J4生成文件:
/home/elf/rk3528/rockdev/Image-rk3528_box/uboot.img本次拉到 Windows 后的文件:
D:\LLLLZZZZZPPPPP\牛马派\3528\uboot.avb_rollback_skip.imgRKDevTool 烧录参数:
分区名:uboot起始地址:0x00004000字节偏移:0x00800000大小:0x2000 扇区,也就是 4MB只刷 uboot.img,不要刷 MiniLoaderAll.bin。
9.6 本次验证结果
刷入补丁后的 uboot.img 后,Android 可以继续启动,不再卡在 U-Boot 的:
trusty_read_rollback_index failedAVB verify failedAndroid boot failed, error -110. ./build.sh 完整镜像未带入驱动的处理
10.1 现象
单独刷入 boot.img、vendor_dlkm.img 后,AP6275S、RTL8211F、RTL8111F/RTL8168 都能正常工作;但重新执行:
cd /home/elf/rk3528./build.sh再烧录生成的完整 update.img 后,发现前面修改的以太网和 AP6275S 驱动没有出现在系统里。
10.2 根因
当前 Android 13 工程启用了动态分区:
use_dynamic_partitions=truebuild_super_partition=truedynamic_partition_list= system system_dlkm system_ext vendor vendor_dlkm odm odm_dlkm productvendor_dlkm 不是独立放进 update.img 的普通分区,而是被打包进 super.img 里的动态分区。
本次问题的关键点是:
- 单独调试时刷
vendor_dlkm.img可以生效。 - 但完整镜像打包阶段使用的是
rockdev/Image-rk3528_box/super.img。 - 旧的
build.sh在重新打包前没有强制重建新的vendor_dlkm.img和super.img。 mkimage.sh只是把已有的out/target/product/rk3528_box/super.img复制到rockdev/Image-rk3528_box/super.img,不会自动判断vendor_dlkm里的模块是否已经更新。
所以最终生成的 update.img 可能带着旧的 super.img,表现为完整烧录后看不到新驱动。
10.3 build.sh 需要补的动作
建议在读取产品变量的位置增加:
PRODUCT_USE_DYNAMIC_PARTITIONS=`get_build_var PRODUCT_USE_DYNAMIC_PARTITIONS`在 kernel 构建完成、外部 WiFi 驱动构建完成后,显式构建 RTL8168/r8168 模块:
if [ -d "$LOCAL_KERNEL_PATH/drivers/net/ethernet/realtek/r8168" ] && grep -q '^CONFIG_R8168=m' "$LOCAL_KERNEL_PATH/.config"; thenecho "Start build r8168 kernel module"cd $LOCAL_KERNEL_PATH && make $ADDON_ARGS ARCH=$KERNEL_ARCH M=drivers/net/ethernet/realtek/r8168 -j$BUILD_JOBS && cd -if [ $? -eq 0 ]; then echo "Build r8168 kernel module ok!"else echo "Build r8168 kernel module failed!" exit 1fifi在执行 mkimage.sh 之前,动态分区项目需要重建 vendor_dlkm.img 和 super.img:
if [ "$PRODUCT_USE_DYNAMIC_PARTITIONS" = "true" ] ; then echo "rebuild vendor_dlkm.img and super.img for dynamic partitions" make vendor_dlkmimage -j$BUILD_JOBS if [ $? -ne 0 ]; then echo "Build vendor_dlkm image failed!" exit 1 fi make superimage-nodeps -j$BUILD_JOBS if [ $? -ne 0 ]; then echo "Build super image failed!" exit 1 fifi这样 ./build.sh 生成完整镜像时,会保证:
- AP6275S 的
bcmdhd.ko进入新的vendor_dlkm.img。 - RTL8111F/RTL8168 的
r8168.ko进入新的vendor_dlkm.img。 - 新的
vendor_dlkm.img被重新打进super.img。 - 最终
update.img使用的是新super.img。
10.4 本次验证结果
重新执行:
cd /home/elf/rk3528./build.sh -C -K -u -J4构建日志中确认出现:
Start build exteranl wifi driverBuild exteranl wifi driver ok!Start build r8168 kernel moduleBuild r8168 kernel module ok!rebuild vendor_dlkm.img and super.img for dynamic partitionsTarget vendor_dlkm fs image: out/target/product/rk3528_box/vendor_dlkm.imgDone writing image out/target/product/rk3528_box/super.imgMake update image ok!本次生成的完整镜像为:
/home/elf/rk3528/rockdev/Image-rk3528_box/update-rp-rk3528-android13-lcd-20260726-153445.img关键文件时间戳:
out/target/product/rk3528_box/vendor_dlkm.img 2026-07-26 15:31out/target/product/rk3528_box/super.img 2026-07-26 15:33rockdev/Image-rk3528_box/super.img 2026-07-26 15:33rockdev/Image-rk3528_box/update-rp-rk3528-android13-lcd-20260726-153445.img 2026-07-26 15:34已确认:
CONFIG_R8168=mCONFIG_REALTEK_PHY=yCONFIG_BCMDHD_OOB := nresource.img 中能找到:
ap6275s-20260723-01realtek,led-datar8168.ko 中能找到 LED 补丁日志字符串:
apply realtek,led-datasuper.img 解包查看动态分区元数据时,能看到:
Name: vendor_dlkmGroup: rockchip_dynamic_partitionsAttributes: readonly10.5 迁移到其它 SDK 时的检查点
- 先确认目标 SDK 是否启用动态分区。
- 如果
vendor_dlkm在super.img里,不能只重建vendor_dlkm.img,还必须重建super.img。 - 如果完整包由
mkimage.sh或厂商脚本生成,要确认它使用的是新super.img,不是历史产物。 CONFIG_R8168=m只代表模块会编译,不代表一定会进入镜像;还需要检查BOARD_VENDOR_KERNEL_MODULES。- AP6275S 的
bcmdhd.ko、RTL8111F/RTL8168 的r8168.ko都应在vendor_dlkm/lib/modules/下确认。 - RTL8211F 的
CONFIG_REALTEK_PHY=y属于 boot/kernel 侧修改,烧完整包时也要确认boot.img是最新生成的。