ROCK 4D 搭配 Router HAT 时 FPC PCIe 与 NVMe 枚举问题排查
说明
本文记录一次 ROCK 4D 搭配 Radxa Dual 2.5G Router HAT V1.5 时的 PCIe 排查过程。HAT 上的两块 RTL8125 可以正常工作,但通过 16Pin FPC 接入 ASM1182E PCIe Switch 扩展板后,扩展板及其后的 Intel Optane NVMe 在冷启动时无法枚举。
最后确认,问题不在 PCIe 数据线、ASM1182E 或 NVMe 本身,而是 FPC 的 POWER_EN 没有在 PCIe 第一次扫描前拉高。用户态临时上电可以让 ASM1182E 出现,但此时 PCI bus number 已经分配完毕,Switch 后面的 NVMe 仍然无法展开。最终通过设备树 GPIO hog 在启动早期拉高使能脚,完成了整条链路的冷启动枚举。
本文对应的设备树修改保存在我的 GitHub 仓库 ovo-ukiyo/openwrt 的 rock-4d-aic8800-router-hat 分支中。完整补丁和提交记录会在后文给出。
硬件与原始拓扑
测试环境如下:
- ROCK 4D,SoC 为 RK3576;
- Radxa Dual 2.5G Router HAT V1.5,主芯片为 ASM2806;
- ASM1182E 双口 PCIe Gen2 Switch 扩展板;
- Intel Optane NVMe;
- OpenWrt 25.12 开发版本,Linux 6.12。
原始冷启动的 PCIe 拓扑为:
1 | -[0000:00]---00.0-[01-ff]----00.0-[02-06]--+-00.0-[03]----00.0 |
对应的硬件关系为:
1 | RK3576 |
ASM2806 的固定下游设备能够正常识别,只有 02:02.0 后面的 FPC 分支为空。
先排除扩展板和 NVMe
将 ASM1182E 扩展板绕过 Router HAT,直接连接到 ROCK 4D 后,得到完整拓扑:
1 | 00:00.0 RK3576 Root Port |
这套连接可以稳定运行在 PCIe Gen2 x1。由此可以排除 ASM1182E、Optane 以及扩展板本身的基本硬件故障,排查重点转向 Router HAT 的 FPC 接口及启动时序。
定位 FPC 的电源使能脚
Router HAT 使用 ROCK 4D 40Pin 的物理 Pin16 控制 FPC 侧带信号。引脚对应关系为:
1 | ROCK 4D 40Pin Pin16 |
OpenWrt 启动后查询该 GPIO:
1 | gpioinfo -c gpiochip2 14 |
输出为:
1 | gpiochip2 14 unnamed input |
继续分别读取默认、上拉和下拉状态:
1 | gpioget -c gpiochip2 14 |
三种情况下都得到:
1 | "14"=inactive |
万用表测量结果为:
1 | 40Pin Pin16:0 V |
断电后 Pin16 对地电阻约为 51.2 kΩ,没有硬短路。
用用户态 GPIO 验证因果关系
为了确认这个引脚的实际功能,启动后临时将它输出为高电平:
1 | gpioset -c gpiochip2 14=1 |
操作后的变化很直接:
1 | 40Pin Pin16:0 V → 3.3 V |
随后执行 PCIe rescan,系统立即识别到:
1 | 04:00.0 PCI bridge: ASMedia Technology Inc. ASM1182e 2-Port PCIe x1 Gen2 Packet Switch |
电平、指示灯和 PCIe 枚举同步变化,能够确定 GPIO2_B6 控制的 FPC Pin13 就是该扩展板的 POWER_EN。原始 OpenWrt 设备树没有配置这个信号,导致扩展板冷启动时没有真正 Enable。
FPC 上测得 5 V、PERST# 约为 3.3 V,并不能说明扩展板已经完成上电。对这块扩展板来说,POWER_EN 仍是必需条件。
热使能后 NVMe 仍然不出现
将 GPIO 拉高后,ASM1182E 本身已经出现在 lspci 中,但它后面的 NVMe 仍无法使用。内核日志给出了明确原因:
1 | pci 0000:04:00.0: devices behind bridge are unusable because [bus 05] cannot be assigned for them |
热使能后的拓扑为:
1 | -[0000:00]---00.0-[01-ff]----00.0-[02-06]--+-00.0-[03]----00.0 |
两个分支都试图使用 bus 05。
系统第一次扫描时,02:02.0 后面没有设备,所以内核只为这个空分支分配了一个 bus:
1 | primary=02 |
用户态给扩展板上电后,ASM1182E 可以出现在 bus 04,但 ASM1182E 自身还是一颗 PCIe Switch。它需要新的 bus number 才能继续枚举下游桥和 NVMe。此时 bus 05 已经分配给另一条 ASM2806 分支,上级桥的 subordinate 也被限制为 04,因此无法容纳完整拓扑。
普通 NVMe 是 Endpoint,挂在 bus 04 后通常不再要求额外的下游 bus。ASM1182E 则不同:
1 | 普通 NVMe:ASM2806 → bus 04 → NVMe |
所以 gpioset 和 PCI rescan 只能用于确认硬件链路,不能作为最终启动方案。
在设备树中提前使能
修复的关键是让 GPIO2_B6 在 RK3576 PCIe Host 第一次扫描之前就输出高电平。本次在 ROCK 4D 设备树的 gpio2 节点中加入 GPIO hog:
1 | &gpio2 { |
对应的 OpenWrt 补丁为:
1 | target/linux/rockchip/patches-6.12/051-12-rock4d-router-hat-pcie-enable.patch |
完整修改可以直接查看:
这份补丁本身很短,关键在生效时间。GPIO hog 会在 gpio2 控制器注册时立即申请 GPIO2_B6 并输出高电平,不需要等待 init 脚本、hotplug 脚本或网络服务启动。ASM1182E 因此能在 PCIe Host 第一次遍历总线之前完成供电,内核看到的是一棵完整的桥拓扑,而不是启动时为空、运行后才出现的端口。
GPIO hog 会在 GPIO 控制器注册时申请并拉高该引脚,时间早于 PCIe 主机完成初次枚举。这样 ASM1182E 在扫描开始前已经工作,Linux 可以根据完整拓扑统一分配 bus number。
冷启动验证
验证时需要彻底断电再上电,不能预先执行 gpioset、PCI rescan 或链路重训。启动后检查:
1 | gpioinfo -c gpiochip2 14 |
最终结果为:
- GPIO line 显示名称
router-hat-pcie-enable,方向和电平为 output-high; - ASM1182E upstream bridge 出现在
04:00.0; - ASM1182E 下游桥出现在
05:03.0和05:07.0; - Intel Optane NVMe 出现在
06:00.0; - 两块 RTL8125 的 bus number 自动向后顺延,不再与 ASM1182E 分支冲突;
- 原来的 bus number 报错消失;
- NVMe 可以正常挂载到
/opt。
小结
这次故障由启动时序和 PCIe 拓扑共同造成。GPIO2_B6 没有拉高,使 FPC 下游 ASM1182E 在第一次扫描时处于关闭状态;用户态上电虽然能找回 Switch,但 PCI bus number 已经按照空分支分配,无法继续容纳 Switch 后面的设备。
最终修复不是增加延时扫描,也不是启动脚本中反复 rescan,而是在设备树中尽早拉高 POWER_EN。完整拓扑在第一次扫描时可见后,Linux 会自动完成正确的 bus 分配。
这项设置与 Router HAT 的硬件连接有关。若后续提交上游,更适合评估为 HAT overlay 或其他板卡专用描述,避免在所有 ROCK 4D 的基础设备树中无条件占用该 GPIO。





