ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RK3566网口DMA初始化失败排查:从时钟到设备树的完整实战

RK3566网口DMA初始化失败排查:从时钟到设备树的完整实战 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 寄存器把 SWRSoftware 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 模式下必须稳定跑在 125MHzSCLK_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 回灌的 125MHzDMA 初始化的前提是 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填成了0x4000004M但实际上 RK3566 的 GMAC FIFO 远没有这么大。stmmac 驱动在初始化 DMA 时会对 FIFO 配置做检查配置超出硬件容量就会导致 DMA 配置失败报出来的就是这行经典的DMA engine initialization failed。排查的时候可以把这两个字段还原成原厂 SDK 默认值比如0x4000问题经常就消失了。reg-io-width虽然在大多数 SDK 里默认没问题但如果你是从其它平台移植过来的设备树最好也确认一下配置为4保证寄存器访问宽度正确。下面这个表可以把刚才说的几个关键字段和排查优先级整理一下字段错误影响排查优先级clock_in_out / assigned-clocksDMA 复位超时高pinctrl-0 选择 M0/M1时钟或数据引脚不通高resets / reset-namesMAC 一直处于复位高phy-supply / power-domains寄存器访问异常或 PHY 无时钟中phy-mode时钟采样沿不对、PHY 通信异常中tx/rx-fifo-depthDMA 配置失败中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 后挂载 debugfsadb 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是否是 125000000125MHz。RGMII 模式下 MAC 的 RX/TX 时钟必须是 125MHz如果是 0 或者其它频率比如 2500000025MHz说明时钟源选择不对或者 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 的基地址是0x10010000GMAC1 是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 devmemRockchip 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 加上dyndbgfile drivers/net/ethernet/stmicro/stmmac/* p。打开后驱动会打印更多的寄存器读写过程特别是在 DMA 复位前期望写入了什么值、读回什么值。如果看到读回的值一直是0xFFFFFFFF那可以和 devmem 的结果相互印证问题锁定在时钟/电源域层级。如果读写值都正常但 SWR 位就是不消失那就要考虑是不是 GMAC 内部逻辑被外部复位一直拉住了。4. 我实际遇到过根因和解法4.1 案例一PHY 芯片供电没上RX_CLK 一直为 0某块 RK3566 双网口板子GMAC0 报 DMA init failedGMAC1 完全正常。板子的差异在于 GMAC0 的 PHY 芯片使用了独立 LDO 供电而 LDO 的 enable 引脚接在了一个 GPIO 上。U-Boot 下网络正常因为 U-Boot 的 GPIO 初始化和内核不同碰巧把这个 GPIO 拉高了。进入内核后设备树里没配这个 GPIOLDO 默认关闭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 恢复 125MHzDMA 初始化顺利通过。这个案例的教训是DMA init 失败时dont 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 failedGMAC1 正常工作。单独屏蔽 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 报错几十行之前的地方丢了就真不好找了。
返回列表