4. RK平台以太网驱动调试:GMAC/RGMII接口配置、PHY芯片驱动调试、MDIO总线通信验证

好,咱们进入第四章。这一章是实打实的硬骨头——以太网驱动调试。说白了,就是让RK芯片的网口能正常工作,能ping通,能跑满带宽。

我个人习惯把以太网调试分成三层:MAC层(GMAC)、PHY层、以及连接它们的MDIO总线。这三层任何一个出问题,网口都起不来。咱们一个一个拆解。

4.1 GMAC/RGMII接口配置

GMAC是芯片内部的以太网控制器,RGMII是它和外部PHY芯片之间的接口标准。RK平台的GMAC驱动在drivers/net/ethernet/stmicro/stmmac/目录下,核心是dwmac-rk.c这个文件。

配置GMAC,主要看设备树。我见过太多人一上来就改驱动代码,其实90%的问题出在设备树配置上。

4.1.1 设备树关键节点

以RK3568为例,GMAC的设备树节点长这样:

&gmac1 {
    status = "okay";
    phy-mode = "rgmii";
    clock_in_out = "input";
    snps,reset-gpio = &gpio3 RK_PB1 GPIO_ACTIVE_LOW;
    snps,reset-active-low;
    snps,reset-delays-us = <0 10000 50000>;
    pinctrl-names = "default";
    pinctrl-0 = &gmac1_miim
              &gmac1_rx_bus2
              &gmac1_tx_bus2
              &gmac1_rgmii_clk
              &gmac1_rgmii_bus;
    tx_delay = <0x4e>;
    rx_delay = <0x26>;
    phy-handle = &phy0;
    phy0: ethernet-phy@0 {
        reg = <0x0>;
        compatible = "ethernet-phy-ieee802.3-c22";
    };
};

这里有几个关键点,我挨个说:

  • phy-mode:必须和硬件设计一致。RGMII就写"rgmii",如果是RMII就写"rmii"。写错了,PHY根本不通。
  • tx_delay / rx_delay:这是RGMII接口的时序补偿。RK芯片内部有可编程延迟链,用来对齐数据和时钟。这两个值需要根据PCB走线长度来调,我遇到过好几次因为走线不等长导致千兆不通,调一下delay值就好了。
  • snps,reset-gpio:PHY芯片的复位引脚。复位时序很重要,delay-us的三个值分别是:复位前等待、复位低电平持续时间、复位后等待。有些PHY芯片对复位时序很敏感,比如RTL8211F,复位时间不够它就不工作。
注意:tx_delay和rx_delay的单位是纳秒的2倍。比如0x4e换算成十进制是78,实际延迟就是78*2=156ps。这个值不是随便写的,需要根据示波器测量或者反复试错来确定。

4.1.2 RGMII时序调试

RGMII接口有四种时序模式:内部延迟和外部延迟,分别对应TX和RX。RK芯片默认使用内部延迟模式,也就是在芯片内部加延迟。

调试时序,我一般用这个方法:

  1. 先用默认值(比如tx_delay=0x30, rx_delay=0x30)试一下,看能不能link up。
  2. 如果能link up但ping不通,或者丢包严重,说明时序不对。
  3. 用ethtool -s eth0 speed 100 duplex full降到百兆试试。如果百兆正常千兆不正常,基本就是时序问题。
  4. 调整tx_delay和rx_delay,每次增减0x10,反复测试。

我曾经在一个项目上,PCB走线长了3厘米,千兆死活不通。调了整整一天的delay值,最后发现tx_delay要设到0x6e才能稳定工作。嗯,这种问题最磨人。

4.2 PHY芯片驱动调试

PHY芯片是物理层器件,负责把数字信号转换成模拟信号。RK平台常用的PHY芯片有:RTL8211F、YT8512C、AR8035等。

PHY驱动调试,核心是确认PHY芯片是否被正确识别和初始化。

4.2.1 PHY ID读取

系统启动时,内核会通过MDIO总线读取PHY芯片的ID寄存器(寄存器2和3)。如果读不到正确的ID,说明MDIO通信有问题,或者PHY芯片没工作。

查看PHY ID的方法:

# 查看系统识别的PHY
cat /sys/bus/mdio_bus/devices/stmmac-0:00/phy_id

# 或者用mdio工具直接读
mdio eth0 phy_addr 2
mdio eth0 phy_addr 3

常见的PHY ID:

PHY芯片 PHY ID (32位) 厂商
RTL8211F 0x001cc916 Realtek
YT8512C 0x000001c8 Motorcomm
AR8035 0x004dd072 Atheros
BCM89811 0x600d8490 Broadcom
小技巧:如果读到的PHY ID全是0xffffffff,说明MDIO通信失败。先检查PHY芯片供电和复位。如果读到的ID不对但接近某个值,可能是地址线有虚焊,或者PHY芯片的地址配置引脚没拉对。

4.2.2 PHY状态检查

PHY初始化完成后,可以用ethtool查看状态:

ethtool eth0

# 输出示例
Settings for eth0:
    Supported ports: [ TP MII ]
    Supported link modes:   10baseT/Half 10baseT/Full
                            100baseT/Half 100baseT/Full
                            1000baseT/Full
    Supported pause frame use: No
    Supports auto-negotiation: Yes
    Advertised link modes:  10baseT/Half 10baseT/Full
                            100baseT/Half 100baseT/Full
                            1000baseT/Full
    Advertised pause frame use: No
    Advertised auto-negotiation: Yes
    Speed: 1000Mb/s
    Duplex: Full
    Port: Twisted Pair
    PHYAD: 0
    Transceiver: internal
    Auto-negotiation: on
    Link detected: yes

重点关注Link detected和Speed。如果Link detected为no,说明物理层没通。先查网线、查对端设备、查PHY芯片供电。

如果Speed显示为10Mb/s或100Mb/s,但硬件支持千兆,可能是:

  • 网线质量差(千兆需要4对线,百兆只需要2对)
  • PHY芯片的千兆模式被关闭了
  • RGMII时序不对,导致千兆协商失败

4.2.3 自定义PHY驱动

有些PHY芯片内核没有现成驱动,需要自己写。其实大部分PHY芯片都兼容通用PHY驱动Generic PHY,但有些特殊功能(比如EEE、WOL)需要单独实现。

我写过一次YT8512C的驱动,核心就是实现phy_driver结构体:

static struct phy_driver yt8512_driver = {
    .phy_id         = 0x000001c8,
    .name           = "Motorcomm YT8512C",
    .phy_id_mask    = 0xfffffff0,
    .features       = PHY_GBIT_FEATURES,
    .soft_reset     = yt8512_soft_reset,
    .config_init    = yt8512_config_init,
    .config_aneg    = yt8512_config_aneg,
    .read_status    = yt8512_read_status,
    .suspend        = yt8512_suspend,
    .resume         = yt8512_resume,
};

注册驱动后,内核就能自动匹配并初始化这个PHY芯片了。

4.3 MDIO总线通信验证

MDIO总线是管理接口,用来读写PHY芯片的寄存器。它只有两根线:MDC(时钟)和MDIO(数据)。

MDIO通信验证,说白了就是确认CPU能不能正常读写PHY寄存器。

4.3.1 硬件层面验证

先用示波器抓MDC和MDIO的波形。MDC的频率一般是2.5MHz(标准模式)或12.5MHz(高速模式)。如果MDC没有波形,说明GMAC没正常工作,或者设备树里的pinctrl配置错了。

我遇到过最奇葩的问题:MDC有波形,MDIO也有数据,但PHY就是不响应。最后发现是PHY芯片的地址引脚没拉对,PHY地址是0x01,但设备树里配的是0x00。这种问题,用示波器一看就明白了。

4.3.2 软件层面验证

内核启动时,MDIO总线会扫描所有可能的PHY地址(0x00~0x1F)。如果扫描不到PHY,会有这样的日志:

stmmac - user: MDIO bus: error
mdio_bus: stmmac-0:00: PHY ID 0xffffffff at 0, ignored

看到这个日志,说明MDIO通信失败。排查步骤:

  1. 检查PHY芯片供电(1.8V或3.3V,看芯片手册)
  2. 检查PHY芯片复位引脚是否拉高
  3. 检查MDC和MDIO的上下拉电阻(一般需要4.7kΩ上拉)
  4. 检查设备树中的PHY地址是否正确

如果扫描成功,日志会显示:

mdio_bus: stmmac-0:00: PHY ID 0x001cc916 at 0, driver: RTL8211F Gigabit Ethernet

看到这个,说明MDIO通信正常,PHY芯片被正确识别了。

4.3.3 手动读写PHY寄存器

调试时,我经常手动读写PHY寄存器来验证功能。用mdio工具:

# 读PHY地址0的寄存器0(控制寄存器)
mdio eth0 0 0

# 写PHY地址0的寄存器0
mdio eth0 0 0x1140

# 读PHY地址0的寄存器1(状态寄存器)
mdio eth0 0 1

常用的PHY寄存器:

寄存器地址 名称 说明
0x00 BMCR 控制寄存器,可以复位、设置速度/双工
0x01 BMSR 状态寄存器,查看link状态、协商结果
0x02 PHY ID High PHY ID高16位
0x03 PHY ID Low PHY ID低16位
0x04 ANAR 自动协商通告寄存器
0x05 ANLPAR 自动协商对端能力寄存器
核心思路:MDIO通信验证是PHY调试的基础。如果MDIO不通,后面所有工作都白搭。我建议在硬件bring-up阶段,先用mdio工具手动读写寄存器,确认通信正常后,再加载驱动。

4.4 本章知识体系

下面这张图总结了以太网驱动调试的整体流程和关键节点:

RK平台以太网驱动调试知识体系 RK平台以太网驱动调试知识体系 GMAC 控制器 RGMII 接口配置 设备树配置 时序调试 PHY驱动调试 MDIO通信验证 tx_delay / rx_delay 调整 PHY ID识别 / 状态检查 示波器抓波形 / mdio工具 百兆/千兆切换验证 ethtool查看link状态 PHY地址/复位时序检查 核心原则:从硬件到软件,从底层到上层,逐层验证

这张图把调试流程分成了三条主线:时序调试、PHY驱动调试、MDIO通信验证。实际调试时,我建议先走MDIO验证,确认物理层通信正常后,再调时序和驱动。这样能快速定位问题,避免浪费时间。

调试口诀:先看MDIO通不通,再看PHY ID对不对,然后调时序,最后测性能。按这个顺序来,能解决90%的以太网问题。