3. mdio-tool 的四大金刚:read、write、dump、list

好了,咱们直接切入正题。mdio-tool 这个工具,说白了就是咱们和 PHY 芯片对话的“对讲机”。它最核心的操作就四个:read、write、dump 和 list。我个人习惯把这四个命令称为“四大金刚”,搞定它们,PHY 调试就入门了。

很多新手一看命令手册,满屏的十六进制就头晕。别急,咱们一个一个拆开揉碎了讲。我会结合我调试 PHY 时的实际场景,让你明白每个命令到底在干什么。

3.1 list:先看看你家门口有几台 PHY

刚开始调试,你总得知道设备上挂了几颗 PHY 吧?它们挂在哪条 MDIO 总线上?list 就是干这个的。

# 查看系统上所有注册的 MDIO 总线及其 PHY 地址
mdio-tool list

输出大概是这样的:

mdio_bus: /sys/bus/mdio_bus/devices/
stmmac-0:00  /sys/bus/mdio_bus/devices/stmmac-0:00
stmmac-0:01  /sys/bus/mdio_bus/devices/stmmac-0:01
stmmac-1:00  /sys/bus/mdio_bus/devices/stmmac-1:00

你看到 stmmac-0:00,stmmac-0 就是 MDIO 总线名(通常是 MAC 驱动注册的),00 是 PHY 地址,一般是 0x00 到 0x1f。嗯,这里要注意,并不是所有的地址都能通信,只有 PHY 芯片真正占用的地址才有反应。

我的小技巧: 如果 list 出来一堆地址,你又不知道哪个是哪个,可以先用 dump 命令快速看一眼每个地址的 PHY ID 寄存器(地址 2 和 3)。Marvell 的 PHY ID 开头一般是 0x0141,Broadcom 是 0x0020。一眼就能分辨出厂家,省得猜半天。

3.2 read:窥探 PHY 的内心世界

这是最常用的命令。你想知道当前网线有没有插上?想知道协商速率是 100M 还是 1G?那就去读对应的寄存器。

# 读取 PHY 地址 0x00 的寄存器 0x01(状态寄存器)
mdio-tool read stmmac-0 0x00 0x01

命令格式我总结了一下:mdio-tool read [总线名] [PHY地址] [寄存器地址]。

返回的是一串 16 位的数据,比如 0x796d。你会问,这 0x796d 是什么意思?你想想看,这需要对照 PHY 芯片的数据手册(Datasheet)去解析每一位。

比如 0x01 寄存器(基本状态寄存器),Bit 13 表示是否 Link Up(链路已建立),Bit 12 表示是否支持自动协商,Bit 9 表示是否处于全双工模式。0x796d 转换成二进制,对应的位就能看出状态了。我刚开始总觉得对着数据手册数 bit 很麻烦,后来习惯了就好了。

我曾经踩过的坑: 读寄存器时,特别是读“自清零”寄存器(比如中断状态寄存器),读一次就会被硬件自动清除。如果你在中断服务函数里读它,一定要小心!否则你会丢失中断事件。我记得有一次读错了一个 PHY 的中断状态寄存器,导致 PHY 一直能 link 上,但就是 ping 不通。查了我整整一下午……嗯,从那以后,我读任何寄存器之前,都会先看一眼数据手册,确认它是不是“读即清除”的类型。

3.3 write:给 PHY 发号施令

光读不行,咱们还得能写。比如你想让 PHY 软复位、修改 LED 灯的模式、开启或者关闭某个测试模式,这都需要写寄存器。

# 向 PHY 地址 0x00 的寄存器 0x00(控制寄存器)写入 0x9140,实现软复位+开启自动协商
mdio-tool write stmmac-0 0x00 0x00 0x9140

说白了,就是把你想设置的值直接怼到寄存器里。但这里有个坑,我差点栽跟头:写操作通常只操作寄存器中的某几个 bit,你不能直接写死整个寄存器。比如你要改第 12 位,正确的做法是先读(Read),修改对应的 bit(Modify),再写回去(Write)。这就是 MDIO 驱动的精髓——“读-改-写”。

mdio-tool 没有提供“读-改-写”一条命令,所以你得手动来两步:

  1. read 出原始值,记下来。
  2. 用计算器算出要写入的新值,再用 write 写进去。

在 Linux 用户态做这个确实有点繁琐,但你如果是写内核驱动,可以用 phy_read 和 phy_write API,并配合 __set_bit 宏来完成。

记住这个定律: 对 PHY 寄存器,除了控制寄存器(0x00)中的全局复位位,永远不要直接用 read 出来的值再写一遍。你要用“读-改-写”的方式操作。我曾经在调试一个 Realtek PHY 时,直接写死了某个保留位,导致 PHY 工作异常。数据手册里写着“这个位保留,必须保持为 0”,我手动改成了 1……教训惨痛啊。

3.4 dump:一键快照,全盘托出

当你遇到奇怪的问题,比如网口瞬间掉线了,或者协商速率不对,最好的办法是什么?对整个 PHY 做一个快照。 dump 命令就是干这个的。

# 将 PHY 地址 0x00 的所有 32 个寄存器(0x00 ~ 0x1F)一次性全部读出来
mdio-tool dump stmmac-0 0x00

输出会按照地址顺序,列出所有寄存器的值。拿到这个快照,你就可以和“正常情况”下的快照做对比。你想想看,如果能抓到“故障前”和“故障中”两组 dump 数据,基本上问题就定位出七七八八了。

我个人习惯在调试板的网络驱动启动后,先做一次 dump 保存为 phy_dump_good.txt。

我的一个实战案例: 有一次,客户反馈产品在高温下偶尔丢包。我用 dump 命令在常温下抓了一次,又在 85°C 环境下抓了一次。一对比发现,某颗 PHY 的 信号质量检测寄存器(SQI)在高温下跳变得很厉害。借助这个线索,我定位到了电源噪声耦合的问题。所以,dump 命令不仅仅能看到寄存器,还能看到物理层的“健康状况”。

下面这张图,帮你理清这四个命令分别用在调试的哪个阶段:

mdio-tool 四大命令与调试流程 PHY 调试开始 list - 发现所有PHY read - 诊断具体寄存器 write - 调整PHY参数 dump - 全寄存器快照 针对具体寄存器 read 保存快照作基线 输出:PHY 状态、配置、诊断
表 1:mdio-tool 四种操作对比
命令 功能 典型场景 避坑提示
list 枚举总线上所有 PHY 确认硬件是否识别、地址是否正确 无
read 读指定寄存器(16位) 查看状态、诊断问题 注意“读即清除”型寄存器
write 写指定寄存器(16位) 软复位、配置工作模式、使能测试 必须“读-改-写”,别写死保留位
dump 批量读全部寄存器 抓快照、对比正常/异常状态 快照量大,建议保存到文件