RK3566 平台上跑 Android 11,网口调不通的坑我踩过不少,但要说哪个报错最唬人,DMA engine initialization failed绝对排得上号。开机串口里突然冒出这么一行,不懂的人第一反应是怀疑 SoC 内部 DMA 控制器坏掉了——实际上,我在 RK3566 的几个项目里遇到的类似情况,没有一次是 DMA 引擎本身真坏了。这个报错更像是一个"替罪羊":DMA 在初始化时依赖时钟、复位、总线访问这些前置条件,只要其中一个环节没到位,驱动就会把这个错误归到 DMA 头上。这篇文章就把这条报错的完整排查思路捋一遍,从 stmmac 驱动的初始化顺序,到设备树里几个关键配置,再到我实际处理过的几个根因案例,给正在啃 Rockchip 网口的兄弟做个参考。
1. 报错的真实身份:stmmac 驱动 probe 链路上的哪一步
1.1 这行 log 是从哪条代码路径打出来的
RK3566 的以太网 MAC 控制器走的是标准 stmicro stmmac 框架,Rockchip 在此基础上做了个 dwmac-rk 平台驱动。Android 11 内核里看到的设备名通常长这样:
rockchip-gmac-dwmac 10010000.ethernet: DMA engine initialization failed rockchip-gmac-dwmac 10010000.ethernet: stmmac_hw_init: DMA engine initialization failed rockchip-gmac-dwmac 10010000.ethernet: probe with error -2210010000.ethernet是 GMAC0 的寄存器基地址,GMAC1 对应10020000.ethernet。报错来源在 stmmac_dvr_probe 过程里,驱动在拿到设备树节点、完成时钟使能和引脚复用配置后,会调用 stmmac_hw_init,里面执行到dma->init(priv)这一步。这个函数指针指向的是 dwmac1000_dma_ops 里的初始化例程,它的核心工作有三件:
- 通过
dma_alloc_coherent分配 DMA 描述符 ring 和缓冲区,这块内存要保证物理连续,供 DMA 控制器和 CPU 共享访问。 - 写 DMA 总线模式寄存器,触发一次软件复位,让 DMA 控制器内部状态回到已知初始值。
- 配置 DMA 中断、通道和相关控制寄存器,让 MAC 和 DMA 之间的数据通路就绪。
如果这三步里有任何一步返回错误,驱动就会在 netdev_err 里打出DMA engine initialization failed,然后 probe 失败,eth0不会注册出来。注意一个点:分配内存失败时报的不一定是这个错,经常是直接背一个栈回溯或者显示 allocation failure 之类的信息。真正会走到这行 log 的,绝大多数是 DMA 软件复位那一步没有按时完成。
1.2 DMA init 真正卡住的地方:SWR 软件复位轮询
stmmac 的 DMA 软件复位逻辑其实很简单,就写一个寄存器位,然后循环等它清零。驱动写 DMA_BUS_MODE 寄存器,把 SWR(Software Reset)位置 1,接着不断读同一个寄存器,等硬件自己把这一位清掉。正常情况,几十微秒内复位就完成了。如果超过超时时间位还没清,驱动就判断复位失败,返回错误。
为什么 SWR 位会一直清不掉?从我在实际板子上遇到的情况看,无非三种原因:
- DMA 控制器的寄存器读不回来有效值,读取直接返回全 0xFFFFFFFF 或者全 0。这种情况通常是 GMAC 的时钟源没使能,或者总线访问路径不通,CPU 根本没碰到这个设备。
- GMAC 工作在 RGMII input 模式下,RX_CLK 由外部 PHY 芯片回灌。如果 PHY 芯片没有正常上电、没有配置完成导致 125MHz 时钟没送回来,DMA 内部逻辑缺少参考时钟,复位就永远等不到完成。
- 电源域没开或者复位信号一直被外部拉着,导致 MAC 内部逻辑处于非正常工作状态。
这里可以打个比方:DMA 控制器就像车间里的一条传送带,DMA engine initialization failed这行报错只表示传送带走不起来,但真正的原因可能是电闸没合、电机烧了、还是皮带卡死了,得挨个去查。上来就拆传送带,方向就错了。
1.3 报错前的上下文 log 怎么读
排错最大的忌讳是只看最后一行报错。这条 log 出现之前,驱动其实已经打了不少信息,这些上下文往往直接指向问题源。正常流程里,在 DMA 初始化之前会看到类似这样的输出:
[ 1.237] rockchip-gmac-dwmac 10010000.ethernet: PTP uses major cycle [ 1.237] rockchip-gmac-dwmac 10010000.ethernet: DWMAC1000, RGMII, able to checksum [ 1.238] rockchip-gmac-dwmac 10010000.ethernet: DMA engine initialization failedDWMAC1000, RGMII这一行能确认设备树里 phy-mode 解析成了 RGMII。如果这里显示的是 RMII 或者其他模式,说明设备树配置和硬件设计不对应,后面查 DMA 意义不大,得先回去把 phy-mode 改对。所以在接到这个报错时,第一步永远是往回翻完整 log,而不是盯着最后三行发愁。
2. 设备树里决定 DMA 生死的几个配置
2.1 时钟:SCLK_GMAC0_RX_TX 和 CLK_GMAC0_125M 的分工
RK3566 的 GMAC 时钟配置是这次排查里最常出问题的地方,绕不开。设备树里要同时处理两路时钟:SCLK_GMAC0_RX_TX和SCLK_GMAC0,其中 GMAC0_RX_TX 是 MAC 收发逻辑的工作时钟,RGMII 模式下必须稳定跑在 125MHz;SCLK_GMAC0是 GMAC 内部的参考时钟,同样要配置成 125MHz。
参考一段常见的 RK3566 GMAC0 设备树配置:
&gmac0 { phy-mode = "rgmii-id"; clock_in_out = "input"; snps,reset-gpio = <&gpio2 RK_PB6 GPIO_ACTIVE_LOW>; snps,reset-active-low; snps,reset-delays-us = <0 10000 50000>; assigned-clocks = <&cru SCLK_GMAC0_RX_TX>, <&cru SCLK_GMAC0>; assigned-clock-parents = <&cru SCLK_GMAC0_RGMII_SPEED>, <&cru CLK_GMAC0_125M>; assigned-clock-rates = <0>, <125000000>; pinctrl-names = "default"; pinctrl-0 = <&gmac0_miim &gmac0_tx_bus2 &gmac0_rx_bus2 &gmac0_rgmii_clk &gmac0_rgmii_bus>; tx-fifo-depth = <0x4000>; rx-fifo-depth = <0x4000>; status = "okay"; };这里最关键的是clock_in_out这个字段。配成"input"时,MAC 的 RX_CLK 来自外部 PHY 回灌的 125MHz,DMA 初始化的前提是 PHY 已经正常工作并输出了时钟。配成"output"时,由 MAC 自己产生参考时钟送给 PHY。很多板子用的都是 input 模式,对应出现的问题也非常典型:PHY 供电晚于 MAC 启动、或者 PHY 复位还没结束,MAC 先去等 PHY 的时钟,两边互相等,最后 DMA 复位超时。
如果你看到DMA engine initialization failed又同时确认硬件方案是 input 模式,优先怀疑 PHY 时钟链路,方向基本不会错。反过来,output 模式下如果CLK_GMAC0_125M配置缺失或者 parent 选错,MAC 自己也产生不了时钟,同样卡在同一个地方。
2.2 reset、pinctrl 和 power domain 的联动
除了时钟,复位资源和引脚复用对 DMA 初始化同样有直接影响。设备树里 GMAC 节点的 reset 配置长这样:
resets = <&cru SRST_GMAC0>; reset-names = "stmmaceth";reset-names必须写成"stmmaceth",驱动是通过这个名字去拿复位控制器的。如果这里漏写或者写错,驱动虽然不至于立刻报错,但后续复位控制会出问题,GMAC 可能在 DMA 初始化期间一直处于复位状态,SWR 位自然清不掉。
pinctrl 配置同样重要。RK3566 的 GMAC0 和 GMAC1 各有 M0/M1 两组引脚复用,设备树里必须按照原理图选择对应的组别。选错之后,要么时钟引脚根本没接到 PHY,要么 TX/RX 数据线对不上,表现出来可能就是 TX_CLK/RX_CLK 信号异常。尤其是 RGMII 的 clk 引脚,如果 pinctrl 配置覆盖掉了某个关键时钟引脚的状态,DMA 复位一样会挂住。
power domain 这块也值得确认。GMAC 节点对应的电源域必须使能,寄存器访问才会正常。如果之前裁剪内核或者改设备树时把power-domains = <&power RK3566_PD_GMAC>删掉了,驱动对寄存器的读写全部失败,报错就直接落在 DMA 初始化这一步。这种问题不太容易暴露,因为感觉上电源域和报错文案离得很远,但排查时一定要记得加上这条检查项。
2.3 phy-mode 和 fifo-depth 这些"看起来无关"的字段
phy-mode字段在排查 DMA 报错时容易被忽略,但它影响的是 PHY 和 MAC 之间时钟沿的对齐方式。RK3566 常见方案是 RGMII,但 RGMII 和 RGMII-ID 的区别在于 TX/RX 时钟是否内置延时。如果原理图上 PHY 芯片已经加了延时,设备树里又配了"rgmii"而不是"rgmii-id",会导致时钟采样沿不对,PHY 通信异常,甚至 PHY 一直处于 link down 状态,进而影响 MAC 对时钟链路的判断。PHY 不稳,DMA 初始化就跟着不稳。
tx-fifo-depth和rx-fifo-depth这两个字段我特意单独拿出来讲,是因为真的踩过坑。这两个值表示 DMA 使用 FIFO 的深度,单位是字节,必须和芯片内部 FIFO 硬件容量匹配。曾经遇到一个改动过的设备树,把rx-fifo-depth填成了0x400000(4M),但实际上 RK3566 的 GMAC FIFO 远没有这么大。stmmac 驱动在初始化 DMA 时会对 FIFO 配置做检查,配置超出硬件容量就会导致 DMA 配置失败,报出来的就是这行经典的DMA engine initialization failed。排查的时候可以把这两个字段还原成原厂 SDK 默认值,比如0x4000,问题经常就消失了。
reg-io-width虽然在大多数 SDK 里默认没问题,但如果你是从其它平台移植过来的设备树,最好也确认一下配置为<4>,保证寄存器访问宽度正确。
下面这个表可以把刚才说的几个关键字段和排查优先级整理一下:
| 字段 | 错误影响 | 排查优先级 |
|---|---|---|
| clock_in_out / assigned-clocks | DMA 复位超时 | 高 |
| pinctrl-0 选择 M0/M1 | 时钟或数据引脚不通 | 高 |
| resets / reset-names | MAC 一直处于复位 | 高 |
| phy-supply / power-domains | 寄存器访问异常或 PHY 无时钟 | 中 |
| phy-mode | 时钟采样沿不对、PHY 通信异常 | 中 |
| tx/rx-fifo-depth | DMA 配置失败 | 中 |
3. 完整排查过程:从 U-Boot 到寄存器现场
3.1 第一步:先在 U-Boot 里验证硬件通路
我在排查这类问题时的第一个动作,不是去抓内核 log,而是先在 U-Boot 里把网络拉通。Rockchip 的 U-Boot 自带 dwmac 驱动和网络命令,如果 U-Boot 下dhcp、ping能通,基本能说明以下几点:SoC 和 PHY 芯片之间的 RGMII 信号通路没问题,PHY 的上电和复位时序没问题,MAC 的时钟源整体能工作。这种情况下,问题大概率集中在 kernel 设备树配置和 kernel 驱动的差异上。
如果 U-Boot 下网络也通不了,那就要回头查原理图和硬件焊接:PHY 芯片供电、晶振、复位网络、RJ45 网口变压器等。U-Boot 阶段网络正常,kernel 起不来,最常见的就是内核设备树里把 phy-mode、clock_in_out、pinctrl 这几项改坏了。先分清楚是硬件问题还是软件问题,这是整个排查过程中最省时间的一步。
3.2 第二步:串口抓完整 log,看报错前五秒
确认 U-Boot 网络正常后,回过来重新刷机开机,串口完整抓取内核日志。重点看报错出现之前的驱动消息:
# 开机后在串口或者 adb shell 里执行 dmesg | grep -iE "stmmac|gmac|dwmac|mdio|phy|eth0"按时间顺序观察和 GMAC 相关的事件。除了 DMA 初始化失败之外,驱动通常还会先打出一些其它信息,比如:
failed to get clk—— 时钟获取失败,设备树里时钟配置有问题failed to get reset—— reset 获取失败No PHY found—— PHY 扫描失败,MDIO 总线或者 PHY 地址不对Link is Down—— PHY 初始化完成了,但 link 状态不对
这些信息每一条都有自己的指向,一定先把它们和 DMA 报错的相对顺序理清楚。比如先出现No PHY found后出现 DMA 失败,那问题在 PHY 侧比在 MAC 侧概率大得多;如果 DMA 失败前没有任何异常提示,那重点就放在时钟、pinctrl、电源域上。
3.3 第三步:用 clk_summary 确认 GMAC 时钟状态
如果 log 上下文中没有明显线索,下一步直接查时钟树。Android 系统下需要 root 后挂载 debugfs:
adb root adb shell "mount -t debugfs none /sys/kernel/debug" adb shell "cat /sys/kernel/debug/clk/clk_summary | grep gmac"正常情况下,能看到类似这样的输出:
clock enable_cnt prepare_cnt rate accuracy phase -------------------------------------------------------------------------------------------- clk_gmac0 2 2 125000000 0 0 clk_gmac0_ptp_ref 1 1 125000000 0 0 clk_gmac0_rx_tx 1 1 125000000 0 0排查时注意两个关键点:
enable_cnt和prepare_cnt是否大于 0。如果是 0,说明驱动根本没有成功使能某一路时钟,设备树 assigned-clocks 配置可能缺失。rate是否是 125000000(125MHz)。RGMII 模式下 MAC 的 RX/TX 时钟必须是 125MHz,如果是 0 或者其它频率,比如 25000000(25MHz),说明时钟源选择不对,或者 PHY 回灌的时钟没到位。
如果clk_gmac0_rx_tx的 rate 是 0,而且clock_in_out = "input",那基本可以断定 PHY 侧没有送出 RX_CLK。这时候要做的是往下查 PHY 的供电和复位状态,而不是继续在 MAC 侧折腾。
3.4 第四步:devmem 直接读 DMA 寄存器,确认访问通路
时钟树看完如果还定位不了,用 devmem 直接访问 GMAC 寄存器,确认 CPU 对 DMA 控制器的寄存器访问是否正常。RK3566 GMAC0 的基地址是0x10010000,GMAC1 是0x10020000。stmmac 的 DMA 寄存器空间从偏移0x1000开始,DMA_BUS_MODE 寄存器就在这个位置。
adb root adb shell "devmem 0x10011000 32"如果设备树里用的是 GMAC1,就改成读0x10021000。这条命令读回来的是 DMA_BUS_MODE 寄存器的当前值,正常情况下即使 DMA 复位没完成,寄存器也应该能读回一个有效值,通常是0x00000000或者其它带标志位的数值。如果读回来是0xFFFFFFFF,说明总线上根本没有响应,GMAC 模块的时钟或者电源域没有使能,寄存器读通路就是断的。如果是0x00000000且驱动仍报复位失败,那就需要再进一步确认 DMA 是否真的触发过复位操作,可以配合 dynamic debug 看驱动内部读写是否执行了。
devmem 是个很实用的工具,但在 Android 上如果系统没有集成 busybox,可以直接用adb shell devmem,Rockchip SDK 的内核镜像一般会带上这个工具。
3.5 第五步:打开 stmmac 的 dynamic debug,看驱动内部的读写细节
前几步都查过没定位,可以考虑把 stmmac 驱动内部的调试信息打开。kernel 4.19 时代的 stmmac 驱动支持 dynamic debug,在 root 权限下执行:
adb root adb shell "echo 'file drivers/net/ethernet/stmicro/stmmac/* +p' > /sys/kernel/debug/dynamic_debug/control"然后重新触发网口初始化,或者在开机时提前通过内核 cmdline 加上dyndbg="file drivers/net/ethernet/stmicro/stmmac/* +p"。打开后,驱动会打印更多的寄存器读写过程,特别是在 DMA 复位前期望写入了什么值、读回什么值。如果看到读回的值一直是0xFFFFFFFF,那可以和 devmem 的结果相互印证,问题锁定在时钟/电源域层级。如果读写值都正常,但 SWR 位就是不消失,那就要考虑是不是 GMAC 内部逻辑被外部复位一直拉住了。
4. 我实际遇到过根因和解法
4.1 案例一:PHY 芯片供电没上,RX_CLK 一直为 0
某块 RK3566 双网口板子,GMAC0 报 DMA init failed,GMAC1 完全正常。板子的差异在于 GMAC0 的 PHY 芯片使用了独立 LDO 供电,而 LDO 的 enable 引脚接在了一个 GPIO 上。U-Boot 下网络正常,因为 U-Boot 的 GPIO 初始化和内核不同,碰巧把这个 GPIO 拉高了。进入内核后,设备树里没配这个 GPIO,LDO 默认关闭,PHY 芯片没有供电,自然不会有 125MHz 时钟回灌给 MAC。
排查过程中印象最深的是 clk_summary 里clk_gmac0_rx_tx的 rate 直接是 0,一直没往供电方向想,后来用万用表量了 PHY 的电源 pin 才发现问题。解法是在设备树里为这个 PHY 增加 regulator 或者直接配置 GPIO 控制:
vcc_phy0: vcc-phy0-regulator { compatible = "regulator-fixed"; regulator-name = "vcc_phy0"; regulator-boot-on; regulator-always-on; gpio = <&gpio3 RK_PA1 GPIO_ACTIVE_HIGH>; enable-active-high; }; &gmac0 { phy-supply = <&vcc_phy0>; ... };这颗 PHY 的供电被正确配置后,RX_CLK 恢复 125MHz,DMA 初始化顺利通过。
这个案例的教训是:DMA init 失败时,don't assume 是 MAC 的问题,PHY 没起、时钟没回灌,同样是 DMA 失败的经典根因。检查完时钟树后,顺手用万用表查一下 PHY 供电是很有必要的。
4.2 案例二:GMAC1 引脚复用配错,ref clk 直接不出
另一个项目用的是 GMAC1,硬件上 GMAC1 走的是 M1 组引脚,但设备树 pinctrl 里写着 M0 组。结果 MAC 的时钟信号根本没有输出到对应的 PHY 芯片引脚,PHY 收不到参考时钟,自然无法正常工作。
这个问题的 log 特征非常明显:clk_gmac1的 rate 正常,但clk_gmac1_rx_tx的 rate 为 0,而且打开 dynamic debug 后能看到驱动读回来的是部分寄存器正常、部分 0xFFFFFFFF。一开始以为是驱动问题,后来对照 TRM 里的引脚复用表格,把 pinctrl 改成 M1 组:
pinctrl-0 = <&gmac1_miim &gmac1_tx_bus2 &gmac1_rx_bus2 &gmac1_rgmii_clk &gmac1_rgmii_bus>;改完 ref clk 正常输出,DMA 复位一秒通过。后来我在排查流程里养成了一个习惯:收到 dinglog 之后第一件事拿设备树里的 pinctrl 和原理图引脚组别做对照,尤其是 M0/M1 这种引脚复用配置,肉眼对比往往比代码调试更快。
4.3 案例三:rx-fifo-depth 填出历史遗留问题
这是最折磨人的一个案例。板子是一个老项目改过来的,设备树里 GMAC 节点的rx-fifo-depth和tx-fifo-depth被之前的人改成了0x100000,大概是为了"提高性能"。结果换到 RK3566 平台后,DMA 一直初始化失败,而且现象时好时坏——有时候重启一次能起来,有时候就一直卡在这个报错。
这个问题的核心原因前面已经提过:stmmac 驱动初始化 DMA 时,会用 FIFO depth 配置去和硬件能力做匹配,超范围就拒绝配置。这个值还影响 DMA 描述符 ring 的大小分配,配置过大可能导致分配失败或者硬件限制判断出错。排查这个案例时,clk_summary 全部正常、devmem 读寄存器也正常,后来是偶然翻 git history 看到这个字段被改动过,改回原厂默认值立刻正常。
tx-fifo-depth = <0x4000>; rx-fifo-depth = <0x4000>;这个案例给我的经验是:设备树里没有"看起来无关紧要"的字段。任何和 FIFO、burst、dma 相关的配置异常,最终都可能以DMA engine initialization failed的形式爆发出来。如果你在一个陌生的 SDK 上排查问题,先对比原厂 evb 设备树和你的设备树差异,逐项找不一致的地方,往往比硬读驱动代码效率高很多。
4.4 案例四:双 GMAC 场景下,另一路抢占了时钟资源
RK3566 有两路 GMAC,但并非所有引脚和时钟资源都能完全独立使用。一个双网口项目中,GMAC0 报 DMA init failed,GMAC1 正常工作。单独屏蔽 GMAC1 后,GMAC0 又能正常起来,怀疑是两路 GMAC 的 SCLK_GMAC_RX_TX 时钟存在关联依赖。
翻看内核时钟树和 TRM 后发现,RK3566 的两路 GMAC 时钟在 CRU 内部存在一定程度的父时钟共享和资源分配关系,部分 SDK 版本要求两路 GMAC 的 clock parent 必须显式分别配置,否则第二路初始化时会抢占或重置第一路的时钟状态。设备树里为 GMAC1 增加显式时钟配置,并且确认两路 GMAC 的 pinctrl 没有共用引脚后,问题解决。
这个案例比较依赖具体内核版本和 SDK 实现,但思路对所有双网口 RK3566 平台都有参考价值:当两路 GMAC 同时使能且只有一路报错时,试着禁用正常的那一路,反向验证是否存在资源竞争。
5. 验证网口真正可用的最终标准
5.1 确认 dmesg 里的 Link up 与 eth0 注册
DMA 初始化问题解决后,不能只看到probe with error消失就收工,还要确认整条链路真正可用。开机后再次抓取 log,正常情况能看到类似这样的输出:
[ 2.185] stmmaceth 10010000.ethernet eth0: Link is Up - 1Gbps/Full - flow control rx/tx [ 2.185] stmmaceth 10010000.ethernet eth0: Link is Up这说明 MAC 和 PHY 之间已经完成自动协商,工作在千兆全双工模式。如果这里一直显示Link is Down,说明 PHY 虽然没有影响 DMA 初始化,但数据链路还不通。可以检查网线、对端设备、PHY 芯片配置、MDIO 地址等。另外用ethtool eth0可以查看当前协商速率、双工模式、PHY 地址和驱动信息:
adb shell "ethtool eth0"输出里能看到Speed: 1000Mb/s、Duplex: Full之类的字段,确认实际协商结果和预期一致。
5.2 Android 11 侧的网络功能联调
在内核层面 eth0 正常注册后,还要确认 Android 上层能用。Android 11 的 EthernetService 会自动接管已注册的以太网设备,设置里会出现"以太网"选项。但在自定义固件上经常出现内核起来了、上层没自动配置 IP 的情况,可以手动验证:
adb root adb shell "ifconfig eth0 192.168.1.100 netmask 255.255.255.0 up" adb shell "ip addr show eth0" adb shell "ping -I eth0 192.168.1.1"如果能 ping 通网关,说明内核协议栈和网卡驱动都没问题。如果内网通了但外网不通,检查路由表、DNS 配置是否被 Android 的 connectivity 服务接管。如果有投屏、远程访问这些针对 Android 11 的玩法需求,以太网口的稳定性和 IRQ 分配直接决定性能上限,建议顺手看一眼中断:
adb shell "cat /proc/interrupts | grep -i gmac"正常能看到 GMAC 对应的中断号和累计触发计数。如果在大量 UDP 收发场景下中断数异常,比如一直卡在 0,那就不是网络不通的问题,而是中断配置或者 IRQ affinity 设置需要调整了。
5.3 长期稳定性的一些建议
问题解决、网络通了,并不代表可以高枕无忧。网口相关的问题很容易在批量环境里复发,特别是换了一批物料或者改过设备树之后。我的习惯是搭建一个"原始基准":在第一批验证通过的板子上,把 clk_summary、pinctrl pinmux 状态、eth0 ethtool 信息、dmesg 开头 20 行全部打包存档。后续任何板子出现 DMA 报错,直接拿报错板子的同类信息和基准对比,哪里偏离一眼就看出来。这个习惯在几十片板子同时调试时非常有用,能省掉大量重复排查时间。
写在最后
回到标题这个问题本身:Rockchip RK3566 Android 11 网口报DMA engine initialization failed,本质上不是 DMA 坏了,而是 DMA 赖以工作的时钟、复位、总线、甚至 PHY 侧的前置条件没有满足。排查这类问题,我最深的感受是"别只看错误行,要看错误行的上游"。从 U-Boot 硬件通路验证开始,到时钟树、寄存器访问、设备树对比,最后落到具体案例,这个顺序能覆盖绝大多数实际场景。另外,项目里用 RK3566 做双网口或者千兆网口设备的场景不少,遇到的报错也五花八门,但底层的排查逻辑都是通用的。把这套流程走一遍,绝大多数问题都能定位到根因。最后再分享一个小技巧:抓 log 时建议用串口完整保存开机全过程,不要只截取报错附近的内容,因为有些关键线索藏在 DMA 报错几十行之前的地方,丢了就真不好找了。