
前阵子调试一块 STM32MP255F 的双网口板子eth0 好好的eth1 一启动就报 driver errordmesg 里刷了一屏 stm32-dwmac 的错误从 link down 到 cannot attach to PHY 轮着来。折腾了两天多把设备树、内核配置、PHY 电路挨个翻了一遍总算把问题挖干净了。STM32MP255F 的 eth1 driver error 看着是驱动报错实际上是系统性问题设备树、时钟、MDIO、PHY 驱动、硬件复位任何一环不对最终都会以 driver error 的形式暴露出来。这篇把我完整排障思路和最后定位的根因写出来给正在做这颗料双网口的工程师一个参考。1. 先理清 eth1 在 STM32MP255F 上的工作链路1.1 双 GMAC 控制器与 eth1 的生成逻辑STM32MP255F 这颗 MPU 集成了两个千兆以太网 MAC 控制器官方文档里通常叫 GMAC0 和 GMAC1对应到 Linux 网络接口就是 eth0 和 eth1。两个 MAC 内部有独立的 DMA、FIFO 和中断但物理层通信都得靠外接的 PHY 芯片来完成MAC 和 PHY 之间走的是 RGMII 或 RMII 接口。也就是说eth1 能正常工作依赖的是一条完整链路GMAC1 控制器初始化 - RGMII 信号送到 PHY - PHY 被 MDIO 总线识别 - PHY 驱动正确绑定 - 网络设备注册成功。这条链路上随便哪个环节断了最后都表现为 eth1 的 driver error。很多工程师第一次碰到 eth1 问题时会下意识去查内核驱动源码但我实际排查下来真正死在 stmmac 驱动代码 bug 上的情况极少绝大多数是设备树配置或者硬件设计细节没对上。stm32-dwmac 这个驱动在 ST 的 BSP 里已经非常成熟它做的事情无非是读取设备树节点、获取时钟和复位、注册 MDIO 总线、扫描 PHY、最后调用 register_netdev。所以排查思路也应该沿着这条链路走而不是一头扎进源码。1.2 eth1 的编号到底是谁定的这个细节值得单独拿出来说。Linux 下网口名字不是硬件自己决定的而是内核根据设备树里的 aliases 节点来命名的。如果设备树里这么写aliases { ethernet0 gmac0; ethernet1 gmac1; };那么 gmac0 对应的网络接口就是 eth0gmac1 就是 eth1。如果板级 BSP 里漏了 ethernet1 这个别名或者两个节点的引用顺序反了内核可能把 GMAC1 注册成 eth1 之外的编号或者干脆只在系统里看到一个网口。我遇到过一种情况设备树里别名写的是 ethernet1 gmac0结果 eth0 和 eth1 对应的物理网口刚好调换看起来就像 eth1 工作异常。所以排查 driver error 的第一步是先确认 dmesg 里 stm32-dwmac 为哪个节点注册了哪个接口名。2. 高概率根因五个方向逐个排查2.1 设备树节点状态和 phy-handle 指向eth1 起不来的第一大原因就是设备树里 GMAC1 节点没配好。具体来说有几个高频问题。第一status 属性不是 okay。很多参考设计里GMAC1 节点默认是 disabled因为部分产品只要单网口SDK 出厂就把它关了。如果板子上硬件有第 二路网口忘了把这个节点改成 okay那 eth1 根本不会 probe内核日志里连 stm32-dwmac 的报错都不会出现。第二phy-handle 指向的 PHY 节点有问题。设备树里通常会有一个 mdio 子节点下面挂着两个 PHY 节点比如gmac1 { status okay; phy-mode rgmii-id; phy-handle phy1; mdio { #address-cells 1; #size-cells 0; phy1: ethernet-phy1 { reg 1; reset-gpios gpiof 5 GPIO_ACTIVE_LOW; }; }; };phy-handle 指向 phy1phy1 的 reg 必须和硬件上 PHY 芯片的地址跳线一致。如果硬件上 PHY 地址是 4这里写 1那 MDIO 总线上当然扫不到设备probe 就会失败。另外reset-gpios 也容易出问题。有些 PHY 芯片上电后要靠 GPIO 拉高复位引脚才能工作如果设备树里没配置这个 GPIO或者引脚的极性写反了PHY 会一直处于复位状态驱动这边读到的 PHY ID 全是 0xffffffff也就识别不了。2.2 MDIO 总线冲突与 PHY 扫描失败STM32MP255F 的两个 GMAC 控制器通常共用一组 MDIO 引脚这种情况下两个 PHY 芯片的地址必须不同。比如 eth0 的 PHY 地址是 0eth1 的 PHY 地址就绝对不能也是 0否则 MDIO 总线上两个芯片同时应答驱动读回的数据是乱七八糟的会直接导致 eth1 的 PHY probe 失败。这个坑在参考设计里特别容易踩。很多开发板的 eth0 用的是地址 0eth1 的 PHY 芯片默认地址也是 0如果硬件设计没有通过 PHY 的 AD0/AD1 引脚把地址区分开那软件再怎么配也没用。遇到这种情况要么改硬件跳线要么确认 PHY 是否有 strapping 引脚可以改变默认地址。比如常见的 RTL8211F 地址由引脚复用决定可以配成 0x0 或 0x1KSZ9031 则可以根据 AD0/AD1 设置成不同的地址。我一般建议双网口方案里两个 PHY 地址要有意识的做区分比如一个 0x0、一个 0x1或者一个 0x4、一个 0x5这样 MDIO 扫描时不会互相干扰。stmmac 驱动在扫描 PHY 时如果设备树里没有明确指定 phy-handle它会尝试扫描整个 MDIO 地址空间。如果两个 PHY 共用一个 MDIO 总线且地址冲突驱动扫到的 PHY 可能一直是同一个导致第二个网口 bind 不到 PHY。日志里典型表现就是 cannot attach to PHY 或者 no PHY found后面跟一个 -19 的返回值。这个错误码对应 -ENODEV表示设备不存在但实际不是硬件不存在而是 MDIO 上没识别到正确的 PHY。2.3 时钟、复位和电源域没配齐stm32-dwmac 这个驱动对时钟的要求非常严格。设备树里要给 GMAC1 节点配齐一组时钟常见的有 stmmaceth、mac-clk-tx、ptp_ref、ethstp 这几个 clock entry。如果 clock-names 写少了或者时钟在 RCC 驱动里没有被正确使能驱动 probe 的时候会在 clk_prepare_enable 阶段失败返回 -EINVAL 或者 -EPROBE_DEFER。这里的坑在于eth0 正常不代表 eth1 的时钟就一定没问题。ST 的 RCC 时钟树里GMAC0 和 GMAC1 的时钟源可能是独立的比如 GMAC1 的 TX 时钟需要额外的 PLL 输出。我在一块板子上就遇到过 eth1 probe 时反复报 -EPROBE_DEFER原因就是 GMAC1 依赖的那个 PLL 在另一个驱动里才被使能两个驱动的 probe 顺序有先有后导致 eth1 的时钟一直申请不到。这种情况下要么把时钟相关的驱动编进内核而不是模块要么在设备树里调整依赖关系让时钟先于 GMAC1 就绪。电源域的问题也不能忽视。STM32MP2 系列大多配合 STPMIC1 电源管理芯片使用eth1 对应的 PHY 供电如果走的是一路独立 LDO且这路 LDO 在内核里由某个 regulator 驱动控制那设备树里 GMAC1 节点就要正确引用这个 regulator。如果 regulator 没使能PHY 不上电MDIO 读回来全是 0xff表现和地址冲突一模一样。这种问题光看驱动日志很难区分需要实测 PHY 供电引脚电压。2.4 内核配置缺少对应的 PHY 驱动这是一类特别隐蔽的问题因为 dmesg 不会直接告诉你缺驱动它只会告诉你 PHY probe 失败。很多国产板卡喜欢 eth0 和 eth1 用不同品牌的 PHY比如 eth0 用 Micrel 的 KSZ9031eth1 用 Motorcomm 的 YT8531。如果内核配置里只打开了 Micrel 的 PHY 驱动没有打开 Motorcomm 的 PHY 驱动那 YT8531 这个 PHY 在 MDIO 总线上能被识别到但找不到匹配的 driver 来完成后续初始化最终 eth1 就是起不来。排查这类问题要确认几个内核配置项CONFIG_STMMAC_ETH、CONFIG_STMMAC_PLATFORM 这两个是 MAC 控制器本身的驱动另一个关键是 PHY 驱动比如 CONFIG_PHY_MICREL、CONFIG_PHY_REALTEK、CONFIG_MOTORCOMM_PHY、CONFIG_DP83867_PHY 等。具体开了哪个取决于你板子上 PHY 芯片的型号。一个比较省事的办法是把常用 PHY 驱动都编进去代价是内核体积大一点但在调试阶段能省很多无谓的折腾。2.5 硬件连线与 RGMII 信号完整性问题软件层面全部排查完仍然报错时就要怀疑硬件了。最常见的是 RGMII 的 TX/RX delay 配置问题。RGMII 接口标准要求在 TX 或 RX 方向有大约 2ns 的延迟这个延迟可以由 MAC 内部提供也可以由 PHY 内部提供关键是设备树里 phy-mode 必须和硬件设计匹配。如果 PHY 芯片自己已经在 RX 和 TX 方向都加了延迟设备树里就该写成 rgmii-id如果 MAC 这边已经加了延迟PHY 那边不加可能是 rgmii 或者 rgmii-rxid、rgmii-txid 的组合。phy-mode 配错的话往往出现在这个现象eth1 能识别link 能起来但是 ping 不通或者丢包率极高。这不算传统意义上的 driver error但排查起来更折磨人。另外还有 PHY 的复位引脚时序问题。有些 PHY 芯片对上电时序有要求MAC 的复位和 PHY 的复位必须满足先后关系如果 PHY 复位信号拉高太快芯片内部还没准备好MDIO 通信就会失败。这种问题在示波器上才能确认软件上只能通过延长 reset 后延时来规避。3. 实操排查流程按顺序把问题揪出来3.1 第一步确认 eth1 有没有注册成功拿到一块出问题的板子我不会急着改代码先执行一组命令看当前状态。ip -d link show ls /sys/class/net/ dmesg | grep -iE stmmac|stm32-dwmac|eth1|mdio|phy这样能快速判断 eth1 处于哪个阶段。如果 /sys/class/net/ 下根本没有 eth1说明网络设备都没注册成功问题大概率出在设备树节点使能、时钟或 MDIO 初始化阶段。如果 eth1 存在但状态是 NO-CARRIER说明 MAC 已经成功注册只是 PHY 的 link 没起来这时候重点查 PHY 的 link 配置、网线、对端设备。如果 dmesg 里有 cannot attach to PHY 或 no PHY found 这类关键词问题基本锁定在 MDIO 和 PHY 识别环节。我通常还会执行ethtool eth1看 PHY 相关信息。如果 ethtool 能在 driver 信息里看到 phy address说明 PHY 识别这一步已经过去了接下来要查的是 link 建立和信号完整性。3.2 第二步反编译设备树核对节点内容拿到板子对应的 dtb 文件后用 dtc 工具反编译成 dts 文本直接搜索 gmac1 节点。这一步的要点是别只看我们以为的配置而是要对着 SDK 默认的 dts 和实际板子的硬件设计逐项核对。fdtdump board.dtb board.dts grep -A 60 gmac1 board.dts核对的字段包括 status、phy-mode、phy-handle、phy地址、reset-gpios、clock-names、pinctrl-0。pinctrl 这块尤其容易被忽略因为 GMAC1 的引脚和 GMAC0 的引脚可能复用同一组如果 pinctrl 节点里把某些引脚配置成了其他功能GMAC1 的信号根本送不出去probe 阶段可能直接超时失败。反编译设备树还有一个好处能看到最终编译进内核的实际生效配置避免出现源码里改了但没重新编译 dtb 这种低级错误。我见过有人在板级 dts 里改了 eth1 的配置结果 U-Boot 加载的 dtb 是从另一个路径拷贝的旧文件折腾半天改的是个寂寞。所以排查 driver error 前先确认当前系统实际跑的 dtb 是不是你以为的那一个。3.3 第三步沿着 MDIO 总线找 PHY如果 dmesg 里明确说了找不到 PHY那就要手动去 MDIO 总线上探测。比较简单的办法是看内核有没有把 mdio 设备注册出来ls /sys/bus/mdio_bus/devices/在设备树方式下你可能会看到形如stmmac-0:00、stmmac-1:01这样的目录前面的数字代表哪条 MDIO 总线冒号后面是这个 PHY 在总线上的地址。如果只有 eth0 对应的 MDIO 设备没有 eth1 对应的说明 eth1 的 MDIO 总线上根本没有 PHY 被识别到需要查 PHY 供电、复位和地址配置。如果想进一步确认 PHY 是否活着可以用 mdio-tools 里的 mdio 命令直接读 PHY 寄存器。比如读 PHY ID 寄存器mdio read 1 0x02 mdio read 1 0x030x02 和 0x03 寄存器存的是 PHY 的厂商 ID。如果读回来全是 0xffff基本可以判定 PHY 没正常工作或者 MDIO 通信失败。如果读回来有有效值再去和 PHY 芯片 datasheet 里的 ID 比对确认这是不是预期型号。这一步能非常快地缩小排查范围。要是系统里没装 mdio-tools也可以用 devmem 直接访问 GMAC 的 MDIO 寄存器手动发起读写但操作起来比较繁琐还是建议调试阶段把 mdio-tools 编进 rootfs。3.4 第四步最小改动验证定位到可疑点之后我的习惯是每次只改一个变量重新编译 dtb重启验证。不要一次性改好几个地方否则问题解决了也不知道是哪个改动起的作用。举一个我实际遇到的例子板子 eth1 PHY 用的是 RTL8211F设备树里 phy-mode 写的是 rgmii但硬件上 PHY 芯片没有加 TX/RX delay需要 MAC 侧提供。第一次开机 dmesg 里报的是一堆 stmmac open 错误link 能 up 但 ping 不通后面我把 phy-mode 改成 rgmii-id 后MAC 这边自动在 TX 和 RX 方向补 delay问题就消失了。再举个例子另一块板子 eth1 死活报 cannot attach to PHY查来查去发现 PHY 地址设置成了 0但设备树里 phy-handle 指向的节点 reg 写的是 1。MDIO 扫描时驱动在地址 1 处找不到 PHY自然 attach 不上。把 reg 改成 0 后eth1 一次就起来了。这种从现象到根因再到验证的闭环虽然看起来慢但每一步都有明确依据反而是最快的方式。4. 常见问题速查表与避坑经验报错现象可能根因解决方向dmesg 没有 eth1 相关信息设备树 gmac1 status 不是 okay节点 status 改为 ok确认别名eth1 存在但 NO-CARRIERPHY 复位脚没拉对 / 网线未连接核对复位 GPIO换网线对端测试cannot attach to PHY (error: -19)PHY 地址不匹配 / PHY 未上电核对设备树 reg 与硬件地址一致probe 反复 EPROBE_DEFER时钟依赖未就绪 / regulator 未使能确认 clock-names检查电源域link up 但 ping 不通RGMII delay 配置不对phy-mode 尝试 rgmii-idno PHY found / PHY ID 全 FMDIO 总线冲突 / PHY 驱动缺失检查两个 PHY 地址打开 PHY 驱动再补充几个只有踩过坑才会注意到的细节。第一双网口方案里两个 PHY 的电源最好分开控制或至少能独立测量。我遇到过 eth1 的 PHY 供电被一颗磁珠隔开磁珠虚焊导致 PHY 偶尔上电失败现象是每次冷启动 eth1 能不能起来全看运气热重启基本正常。这种问题软件排查根本无解最后是量电压才发现的。第二内核配置里 PHY 驱动尽量编成 y 而不是 m。PHY 驱动如果被编译成模块启动早期 MDIO 扫描时 PHY 驱动还没加载后面即使模块加载了PHY device 和 driver 的匹配时机也可能错过导致 eth1 一直 probe 不上。这个坑在根文件系统是 initramfs 时特别明显。第三多看几遍 dmesg 里 eth1 前后的完整日志而不只是搜 error。stm32-dwmac 是 platform_driverprobe 失败时可能打印的是 -517EPROBE_DEFER这种不算致命错误可能后面会被重试。真正致命的是 -22EINVAL、-19ENODEV、-16EBUSY这些它们对应的方向完全不同先把错误码查清楚比盲改设备树高效得多。5. 几个容易忽略的扩展场景5.1 两个 PHY 共用一组 MDIO 的配置要点STM32MP255F 支持两路 GMAC但某些板级设计为了省引脚会把两个 PHY 挂在同一组 MDIO 上。这种做法的好处是节省 IO坏处是设备树配置稍微复杂一些。通常在设备树里gmac0 的 mdio 子节点会被同时引用为 gmac1 的 mdio 总线否则两个节点各创建一条 MDIO 总线第二条总线上自然没有 PHY。我在一个项目里的做法是在 gmac0 的 mdio 节点下把两个 PHY 都声明出来然后 gmac1 通过 phy-handle 直接引用 gmac0 那边的 PHY 节点。这样驱动会在同一条 MDIO 总线上管理两个 PHY地址不同就能正确区分。如果板级设计本身给了两套独立的 MDIO 引脚那就各配各的反而更简单。5.2 不同 PHY 型号混合使用的驱动配置双网口板子最常用的 PHY 组合有几种KSZ9031RTL8211F、KSZ9031YT8531、DP83867YT8531不同厂商的 PHY 对内核配置的要求不一样。我踩过的坑是用了 Motorcomm 的 YT8531却没开 CONFIG_MOTORCOMM_PHY导致这个 PHY 在系统里被识别成一个 generic PHY驱动初始化不完整eth1 的协商速度不对只能协商到百兆千兆始终上不去。这种问题在 dmesg 里不是明显的 error而是 ethtool 查看的时候发现 phy driver 名是 genphy 而不是特定型号。所以 eth1 起来后也建议用ethtool -i eth1看一眼 driver 字段确认绑定的是不是正确的 PHY 驱动。5.3 中断引脚与 GPIO 冲突的排查有些 PHY 芯片的 interrupt 引脚会接到 MPU 的 GPIO 上用于链路状态变化时的异步通知。如果这个 GPIO 在设备树里被其他外设占用或者 pinctrl 配置冲突stm32-dwmac 在 request_irq 的时候会返回 -EBUSY导致 probe 失败。这种问题在日志里特征很明显多半会打印 IRQ 相关字样但也遇到过中断号被别的驱动抢走导致 eth1 注册成功但 link 状态不更新的情况。排查时可以用cat /proc/interrupts看看 eth1 的中断是否注册成功如果有其他设备占了同一个中断号就需要重新规划 GPIO 分配。6. 我自己的排障习惯与最后总结做嵌入式网络调试这么多年我慢慢发现一个问题越是报错信息看起来复杂的时候越要控制住自己改代码的冲动。STM32MP255F 的 eth1 driver error 就是个典型看着像是驱动的问题实际大多出在设备树、硬件配置和环境层面。我现在的排障顺序基本固定为先看接口有没有注册再看 MDIO 能不能扫到 PHY然后确认 PHY 驱动是否匹配最后才去查信号配合问题。还有一个小技巧是出问题时先把所有和网口相关的 dmesg 信息完整保存下来因为有些错误是间歇性的重启几次可能复现不了或者换个形态出现。日志存下来之后对比 eth0 和 eth1 的启动流程差异通常很快就能定位到是哪个环节掉了链子。最后想说的是双网口调试这种事有时候真的需要一点耐心。eth1 的问题往往不是单点故障而是设备树、时钟、PHY 驱动、硬件设计几个因素交叉作用的结果。但只要按照 MAC 初始化 - MDIO 识别 - PHY 驱动绑定 - link 建立这条链路一步步排查最终都能把问题揪出来。希望这篇记录能帮你少走点弯路。