从一根网线到一个 skb:RK3588 STMMAC 与 RTL8211F 网卡驱动全景解析
很多网卡问题都有一种迷惑性:
- MDIO 能读到 PHY ID,但接口始终
NO-CARRIER; ethtool显示 1000 Mbps、Link detected: yes,却 ping 不通;- 百兆稳定,千兆一跑 iperf3 就出现 CRC error;
- 开机前几秒能收包,随后 RX 停止;
- 低流量正常,并发或大包时 TX timeout;
- 关闭 checksum/TSO/GRO 后“神奇恢复”。
这些现象之所以难排,是因为我们口中的“网卡驱动”其实横跨多个软硬件层:RK3588 内部的 Synopsys DWMAC、Rockchip 的 GRF/clock/reset glue、外置 RTL8211F PHY、RGMII 数据总线、MDIO 管理总线、Linux stmmac、phylink/phylib、NAPI、DMA 和网络协议栈。
如果只盯着 stmmac_main.c 的某一个函数,或者只盯着 PHY 的 link 状态,就很容易在错误的层次上修问题。
本文以本地 Firefly RK3588 SDK 的 Linux 5.10.198 为样本,建立一套可迁移到其他 SoC 网卡的完整理解框架:
- MAC、PHY、RGMII、MDIO 各自负责什么;
- 一个 Linux 网卡驱动到底要实现哪些能力;
dwmac-rk、stmmac、phylink、phylib 和realtek.c如何分工;- probe、open、link、TX、RX、IRQ/NAPI 的完整调用链;
- descriptor、DMA buffer 与 skb 的所有权如何转移;
- 为什么 RGMII delay 是 RK3588 千兆网口最关键的板级边界;
- 面对“找不到 PHY”“link up 不通”“千兆不稳”时如何分层定位。
本文讨论的是 RK3588 + 外置 RTL8211F 千兆 PHY 的典型 RGMII 场景。不同 Firefly 板型可能连接 GMAC0 或 GMAC1,也可能使用其他 PHY;最终应以原理图和实际启动 DTB 为准。
一、先拆掉第一个误区:网卡不是一个硬件块
1. MAC:处理以太网帧与 DMA
RK3588 集成 Synopsys DesignWare Ethernet MAC。MAC 处于数字世界,主要负责:
- 接收和发送以太网帧;
- 生成、检查 FCS;
- MAC 地址过滤、组播过滤;
- VLAN、checksum、TSO、RSS、时间戳等硬件卸载;
- 管理 MAC/MTL FIFO 和多队列;
- 通过 DMA descriptor 在 FIFO 与 DDR packet buffer 之间搬运数据;
- 产生 RX、TX completion、DMA error、LPI 等中断。
Linux stmmac 的主要工作对象就是这个 MAC、MTL 和 DMA。
2. PHY:处理双绞线上的物理信号
RTL8211F 是独立的千兆以太网 PHY。它负责:
- 10BASE-T、100BASE-TX、1000BASE-T 的模拟前端和物理编码;
- 自动协商、千兆主从训练;
- 速度、双工和 pause 能力协商;
- 均衡、回波抵消等 PHY 内部信号处理;
- 报告 link、speed、duplex、pause;
- PHY 私有低功耗、中断和 delay 配置。
PHY 不知道 struct sk_buff,也不管理 DMA ring。它面对的是 RGMII 数字接口和 MDI 双绞线接口。
3. RGMII:MAC 与 PHY 的数据通道
RGMII 承载实际帧数据。GMAC 与 RTL8211F 之间通过 TXD/RXD、TXC/RXC 和控制线交换数据。
千兆模式下时钟为 125 MHz,并在时钟双沿传输。正因为采样窗口很窄,RGMII 对 clock-data skew 极其敏感,这也是“百兆正常、千兆异常”的高发根因。
4. MDIO/MDC:MAC 管理 PHY 的控制通道
MDIO 是另一条独立总线:
- MAC 内的 MDIO controller 是 bus master;
- PHY 是具有地址的从设备;
- Linux 通过 MDIO 读取 PHY ID、基本状态和厂商扩展寄存器。
因此必须牢记两条判断:
MDIO 能读到 PHY ID,只能证明管理通路基本正常,不能证明 RGMII 数据通路正确。
PHY 显示 link up,只能证明 PHY 与链路对端完成了协商,不能证明 MAC DMA 与 Linux 收发路径正确。
二、Linux 为什么把它拆成五层驱动
RK3588 网口不是靠一个驱动文件完成,而是多层组合:
| 软件层 | 本地源码 | 解决的问题 |
|---|---|---|
| 设备树 | arch/arm64/boot/dts/rockchip/ |
板子到底怎么连 |
| Rockchip glue | drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c |
通用 DWMAC 如何接入 RK3588 |
| stmmac core | drivers/net/ethernet/stmicro/stmmac/ |
MAC/DMA 如何作为 Linux netdev 工作 |
| phylink/phylib | drivers/net/phy/ |
MAC 与 PHY 如何协商能力和链路状态 |
| Realtek PHY | drivers/net/phy/realtek.c |
RTL8211F 有哪些私有初始化和寄存器 |
1. Rockchip glue 不负责逐包收发
dwmac-rk.c 的价值在于处理 SoC 差异:
- GRF/PHP_GRF 接口 mux;
- GMAC0/GMAC1 选择;
- RGMII/RMII 模式;
- 参考时钟输入/输出;
- 10/100/1000 Mbps 分频;
- SoC 侧 TX/RX delay line;
- clock、reset、power domain;
- suspend/resume 包装。
它完成板级准备后调用通用的 stmmac_dvr_probe()。
2. stmmac 是 MAC 数据路径主体
stmmac 负责:
- 创建和注册
net_device; ndo_open、ndo_stop、ndo_start_xmit;- DMA descriptor ring;
- RX buffer 与 TX DMA mapping;
- IRQ 和 NAPI;
- 多 TX/RX queue;
- checksum、TSO、VLAN、RSS、PTP、EEE;
- ethtool、统计、TX timeout、PM;
- 注册 MDIO bus;
- 通过 phylink 接收链路变化。
3. phylink 是 MAC 与 PHY 的合同
PHY 能发现链路协商到了 1000 Mbps 全双工,但它不知道 RK3588 应该把哪个 GRF 分频位改成 DIV1;MAC 知道怎样配置自己,却不应该依赖某一个 Realtek 型号。
phylink 负责把双方隔离并协调:
- MAC 支持哪些 interface 和速率;
- PHY/PCS 当前解析出了什么状态;
- link up/down 时调用哪个 MAC 回调;
- speed、duplex、pause 变化如何传递。
4. phylib 提供 PHY 通用状态机
phylib 负责:
- 创建
phy_device; - 读取 PHY ID 并匹配
phy_driver; - 通用自动协商;
- 定时状态机或 PHY IRQ;
- link/speed/duplex 状态更新;
- suspend/resume 和通用 Clause 22/45 操作。
5. Realtek driver 只实现芯片差异
RTL8211F 的驱动无需重新实现整个自动协商协议。它主要补充:
- 芯片私有初始化;
- RGMII internal delay;
- PHY 中断 enable/ack;
- paged register 访问;
- 低功耗或硬件 quirk。
这种分层不是“复杂化”,而是让一个 stmmac core 可以服务许多 SoC,让同一个 Realtek PHY driver 可以连接许多 MAC。
三、从设备树读懂一块板的网口
1. SoC 节点声明 DWMAC 实例
RK3588 的 GMAC0 定义在:
1 | firefly_rk3588_SDK/kernel/arch/arm64/boot/dts/rockchip/rk3588.dtsi:690 |
GMAC1 定义在:
1 | firefly_rk3588_SDK/kernel/arch/arm64/boot/dts/rockchip/rk3588s.dtsi:5500 |
两个节点都包含:
1 | compatible = "rockchip,rk3588-gmac", "snps,dwmac-4.20a"; |
这两个 compatible 分别选择:
- RK3588 平台 glue;
- Synopsys DWMAC 4.20a 通用能力模型。
SoC 节点还描述 MMIO、MAC IRQ、wake IRQ、GRF、clock、reset、power domain、TSO 和 MDIO 子节点。
2. 板级 DTS 决定 PHY 和 RGMII 怎么接
Firefly SDK 的 rk3588-firefly-port.dtsi:179 提供了一个典型配置:
1 | &gmac1 { |
这段 DTS 同时回答了八个问题:
- 使用 GMAC1;
- 数据接口是 RGMII;
- PHY 负责插入 RX internal delay;
- 参考时钟由 SoC 输出;
- PHY reset 使用哪个 GPIO;
- reset 前后延迟多长;
- RK3588 SoC 侧 TX delay tap 是多少;
- PHY 的 MDIO 地址是 1。
3. 不要只看某个 dtsi
这个示例文件中还出现了:
1 | status = "disbaled"; |
对设备树来说,通常只有 okay/ok 表示可用,其他值都等价于不可用。真实板级文件可能在后面覆盖它,也可能没有。
所以判断运行配置时,证据优先级应当是:
1 | 实际启动 DTB > 最终板级 DTS 展开结果 > 某个被 include 的 dtsi |
可以反编译实际 DTB:
1 | dtc -I dtb -O dts -o /tmp/running.dts /sys/firmware/fdt |
四、最危险的板级参数:RGMII delay
1. 为什么 RGMII 需要 delay
RGMII 为减少引脚数量,在时钟上升沿和下降沿都发送数据。接收端需要在数据稳定窗口中间采样,因此时钟与数据之间需要大约 1.5~2 ns 的偏移。
这个偏移可以来自:
- PCB 走线;
- PHY 内部 delay;
- SoC/GMAC 内部 delay line。
问题在于三者都可能“帮忙”。如果同一方向重复加入 delay,或者没有任何一方加入,就会把采样点推到窗口边缘之外。
2. phy-mode 是从 PHY 视角定义的
| 模式 | PHY TX delay | PHY RX delay |
|---|---|---|
rgmii |
无 | 无 |
rgmii-rxid |
无 | 有 |
rgmii-txid |
有 | 无 |
rgmii-id |
有 | 有 |
这里最容易发生方向混淆:
- PHY TX:PHY → MAC,对应 MAC RX;
- PHY RX:MAC → PHY,对应 MAC TX。
3. RTL8211F 会真的读取 phy-mode
rtl8211f_config_init() 位于:
1 | firefly_rk3588_SDK/kernel/drivers/net/phy/realtek.c:181 |
函数按 phydev->interface 选择:
RGMII:PHY 不加 delay;RGMII_RXID:PHY 加 RX delay;RGMII_TXID:PHY 加 TX delay;RGMII_ID:PHY 两边都加。
所以 phy-mode 不是一个只供 MAC 参考的标签,它会改变 RTL8211F 私有寄存器。
4. RK3588 自己也能加 delay
RK3588 专用代码位于:
1 | firefly_rk3588_SDK/kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c:1881-2018 |
rk3588_set_to_rgmii() 会:
- 选择 GMAC0/1 的 RGMII mux;
- 配置 RGMII clock mode;
- 打开或关闭 SoC 侧 TX/RX delay;
- 写入
tx_delay/rx_delaytap。
Firefly 示例选择 rgmii-rxid,PHY 负责 RX delay,DTS 又给 SoC 配 tx_delay = <0x42>。这是一种明确分工,而不是“两个地方都随便填一个值”。
5. RGMII 时序问题的典型症状
- 10/100 Mbps 正常,1000 Mbps 不稳定;
- ping 小包正常,iperf3 大流量丢包;
- 单向正常、反向异常;
ethtool -S中 CRC、alignment、receive error 增长;- 不同温度、不同线缆或不同对端表现不一致。
调试时不要问“delay 应该填多大”,而要先问:
- 原理图和 PCB 由哪一侧提供 skew?
- PHY internal delay 开了哪一边?
- SoC internal delay 开了哪一边?
- 最终 DTB 的配置是否和设想一致?
- 经过双向压力测试后,稳定窗口在哪里?
五、probe:把一个 DWMAC IP 注册成 Linux 网卡
1. 从 compatible 进入 rk_gmac_probe()
RK3588 match 表在 dwmac-rk.c:2879-2940,其中:
1 | { .compatible = "rockchip,rk3588-gmac", .data = &rk3588_ops }, |
platform driver 的 probe 是 rk_gmac_probe(),位于 dwmac-rk.c:2774。
它的主干是:
1 | rk_gmac_probe() |
2. 平台层准备什么
stmmac_get_platform_resources() 取得:
- MMIO resource;
macirq;- 可选 wake/LPI IRQ。
stmmac_probe_config_dt() 解析:
- MAC 地址;
phy-mode和phy-handle;- MDIO 子节点;
- FIFO/ring/queue 参数;
- DMA burst;
- TSO、RSS 等能力;
- AXI/MTL 队列配置。
Rockchip 私有路径再取得 GRF/PHP_GRF、clock、reset、power 和 delay 属性。
3. rk3588_ops 是 SoC 差异接口
1 | rk3588_ops |
链路速度变化时,stmmac 会经平台 fix_mac_speed 回调进入 rk3588_set_gmac_speed():
| 链路速率 | RGMII clock divider |
|---|---|
| 1000 Mbps | DIV1 |
| 100 Mbps | DIV5 |
| 10 Mbps | DIV50 |
PHY 协商到 1 Gbps 后,如果 SoC clock 仍停留在百兆分频,link 灯可以亮,数据仍然无法正确传输。
4. stmmac_dvr_probe() 创建 Linux netdev
通用 probe 位于 stmmac_main.c:5061-5332,主要完成:
devm_alloc_etherdev_mqs();- 初始化
stmmac_priv与多队列; - 识别 DWMAC 版本和硬件 feature;
- 创建 phylink;
- 注册每个 channel 的 RX/TX NAPI;
- 安装 netdev ops 和 ethtool ops;
register_netdev()。
probe 成功只代表接口被内核管理。真正启动 ring、DMA 和 PHY 要等 ip link set ethX up 调用 ndo_open。
5. net_device_ops 是阅读入口
stmmac_netdev_ops 位于 stmmac_main.c:4789-4822,最关键的三个回调是:
1 | ndo_open → stmmac_open |
如果要系统学习任意 Linux 网卡驱动,先找到这三个入口,再找 IRQ/NAPI 注册位置,通常就能搭起主骨架。
六、MDIO、PHY ID 与 RTL8211F 驱动绑定
1. stmmac 注册一个 mii_bus
MDIO 实现在 stmmac_mdio.c:
1 | stmmac_mdio_read() :222 |
stmmac_mdio_register() 创建 mii_bus,把 read/write/reset 回调指向 DWMAC MDIO controller,然后由 phylib 枚举 PHY。
2. DTS reg 必须与 strap 地址一致
1 | phy@1 { reg = <1>; }; |
地址通常由 PHY strap 电阻决定。DTS 写 1、硬件实际 strap 为 0 时,常见现象是:
- MDC 有时钟;
- MDIO 读回
0xffff或0x0000; /sys/bus/mdio_bus/devices/没有期望设备;- open 报找不到 PHY。
3. phylib 用真实 PHY ID 匹配 Realtek driver
RTL8211F 的 PHY ID 是 0x001cc916。驱动表位于 realtek.c:691-700,安装:
rtl8211f_config_init();rtl8211f_ack_interrupt();rtl8211f_config_intr();- genphy suspend 与 Realtek resume;
- paged register access。
板级 DTS 使用通用的:
1 | compatible = "ethernet-phy-ieee802.3-c22"; |
并不妨碍匹配 Realtek driver,因为最终依据是 MDIO 读出的 PHY ID。
4. PHY IRQ 和 MAC IRQ 完全不同
RTL8211F 的 link interrupt enable/ack 位于 realtek.c:93-150。这个中断只用于通知 PHY 链路状态变化。
MAC/DMA IRQ 则通知:
- RX descriptor 已完成;
- TX completion;
- DMA error;
- MAC/MTL/LPI/PTP 事件。
把 PHY IRQ 配错,通常影响 link 变化检测;把 MAC IRQ 配错,数据路径会停止。两者不能混为一谈。
七、phylink:link up 时到底谁配置谁
stmmac_init_phy() 位于 stmmac_main.c:1145,优先通过:
1 | phylink_of_phy_connect() |
按 phy-handle 连接 PHY。如果没有标准 handle,则尝试从 MDIO bus 按地址连接。
stmmac 提供的 phylink MAC ops 位于 stmmac_main.c:865-1110:
stmmac_validate():限制 MAC 支持的接口和速率;stmmac_mac_config():配置接口状态;stmmac_mac_link_down():关闭 MAC TX/RX;stmmac_mac_link_up():配置 speed/duplex/pause,并通知平台改速率。
完整链路是:
1 | 网线插入 |
phylib 的 phy_state_machine() 位于 drivers/net/phy/phy.c:1197,它运行在 delayed work 上下文,可以执行 MDIO 事务;这与收包的 hardirq/NAPI 路径是两套不同上下文。
八、open:什么时候 ring、IRQ 和 DMA 真正开始工作
stmmac_open() 位于 stmmac_main.c:2901。将接口置为 UP 后,典型流程是:
1 | stmmac_open() |
其中 stmmac_hw_setup() 位于 stmmac_main.c:2753-2898,负责把软件配置落到硬件:
- DMA reset 和 channel 初始化;
- descriptor base/tail;
- MAC core 与 packet filter;
- RX/TX queue 和 DMA channel 映射;
- MTL scheduling;
- checksum、TSO、RSS、CBS;
- 中断合并;
- PTP 与 EEE。
顺序非常重要:ring 未完成前不能启动 DMA;NAPI 未 enable 前不应开放 RX 中断;关闭路径也必须按相反方向阻止新工作后再释放 DMA memory。
九、TX:一个 skb 如何变成网线上的帧
1. 调用链
1 | send()/sendmsg() |
stmmac_xmit() 位于 stmmac_main.c:3468。
2. ndo_start_xmit 必须完成什么
- 根据
skb->queue_mapping选 TX queue; - 计算 linear data 和 frags 需要多少 descriptor;
- 检查 ring 空间,不足则停止 netdev queue;
- 处理 checksum、TSO、VLAN 和 timestamp metadata;
- 用 DMA API 映射 skb 数据;
- 写 descriptor 地址、长度、first/last segment;
- 保存 descriptor 到 skb/DMA mapping 的软件关系;
- 用内存屏障保证描述符字段先发布;
- 设置 OWN,把 descriptor 交给 DMA;
- 更新 tail pointer/doorbell。
3. TX completion 不是可选项
DMA 完成发送后,TX NAPI 调用 stmmac_tx_clean(),入口在 stmmac_main.c:2087:
- 检查 descriptor 已归 CPU;
- 解析发送错误;
dma_unmap_*();- 释放 skb;
- 更新统计;
- ring 空间恢复后唤醒 queue。
TX 所有权变化可以概括为:
1 | 协议栈拥有 skb |
如果 ndo_start_xmit 返回 NETDEV_TX_OK,驱动已经消费 skb;如果返回 NETDEV_TX_BUSY,驱动不能偷偷释放或修改 skb。
4. 常见 TX bug
- descriptor 数量估算不足;
- DMA map 失败后回滚不完整;
- OWN 设置过早,设备读到半写 descriptor;
- 忘记更新 tail pointer;
- completion 漏掉 unmap/free;
- queue stop 后没有 wake;
- close 时 DMA 仍访问已释放 ring。
十、RX:DMA buffer 如何变成 skb
1. 调用链
1 | 网线 |
2. RX 先有 buffer,后有数据
驱动必须提前为 RX ring 准备 buffer:
1 | 分配 page |
如果 refill 断掉,网口可能在启动时收几个包,耗尽 ring 后完全停止。
3. stmmac_rx() 的核心职责
stmmac_rx() 位于 stmmac_main.c:3862-4069,它会:
- 检查 descriptor ownership;
- 解析 first/last segment、长度和错误;
- 做 DMA sync;
- 构造 skb linear area 和 frags;
- 处理 checksum、VLAN、RSS hash、timestamp;
- 调用
eth_type_trans()设置协议和 MAC header 语义; - 记录来源 RX queue;
- 调用
napi_gro_receive(); - 保存跨 descriptor 或跨 budget 的半包状态;
- refill 新 buffer。
关键提交点在:
1 | stmmac_main.c:4048 skb_record_rx_queue() |
调用 napi_gro_receive() 后,skb 所有权已交给 GRO/协议栈,驱动不能继续访问或释放它。
4. page 所有权比 skb 更容易出错
RX 中必须区分两种情况:
- 数据被 copy 到 skb linear head:原 RX page 可较早回收;
- page 被挂到 skb frag:page 生命周期转交 skb。
如果已经挂入 frag 的 page 又立即 refill 给 DMA,设备可能覆盖协议栈仍在读取的数据,造成 use-after-free、数据随机损坏或难以复现的 checksum error。
十一、IRQ 与 NAPI:为什么中断里不直接把包收完
stmmac 主 ISR 是 stmmac_interrupt(),位于 stmmac_main.c:4254-4307。它负责:
- 检查设备是否 DOWN;
- 读取 MAC/MTL 状态;
- 处理 LPI、PCS、安全特性等事件;
- 进入 DMA interrupt handler;
- 对活跃 channel 屏蔽 IRQ 并调度 NAPI。
RX/TX NAPI 入口分别是:
1 | stmmac_napi_poll_rx() stmmac_main.c:4071 |
NAPI 的设计目标不是“减少一次函数调用”,而是避免高包速率下每个包都打断 CPU:
1 | 第一个包触发 IRQ |
执行上下文必须分清
| 路径 | 上下文 | 能否睡眠 |
|---|---|---|
| probe/open/close/PM | 进程上下文 | 可以 |
ndo_start_xmit |
网络发送 softirq 上下文 | 不可以 |
| MAC ISR | hardirq | 不可以 |
| NAPI poll | NET_RX softirq | 不可以 |
| PHY state machine | workqueue | 可以 |
因此:
- hardirq 中不能做长时间 MDIO;
- NAPI 中不能调用可能睡眠的 clock/regulator API;
- open/close 要和 IRQ、NAPI、DMA 并发退出同步;
- IRQ/NAPI 共享短状态时通常使用 spinlock;
- PHY paged register 应走 phylib 的锁与 helper。
十二、DMA 驱动最核心的不是寄存器,而是所有权
理解网卡驱动时,建议始终追踪三套 owner:
- descriptor 属于 CPU 还是 DMA;
- DMA buffer/page 属于 ring、DMA、驱动还是 skb;
- skb 属于协议栈、qdisc、驱动还是 GRO/socket。
1. TX 所有权
| 时点 | skb | descriptor | mapping |
|---|---|---|---|
| 进入 xmit | 驱动接管 | CPU | 建立中 |
| 发布 descriptor | 驱动保留回收责任 | DMA | DMA 可读 |
| completion | 待释放 | CPU | 待 unmap |
| clean 完成 | 已释放 | free | 已解除 |
2. RX 所有权
| 时点 | page/buffer | descriptor | skb |
|---|---|---|---|
| refill | DMA/ring | DMA | 无 |
| DMA 写完 | 驱动 | CPU | 无 |
| 构造 skb | 驱动或 skb | CPU | 驱动 |
| GRO 提交 | skb/协议栈 | 待 refill | 协议栈 |
3. 为什么需要屏障
CPU 写 descriptor 的普通字段后,必须保证设备先看见地址和长度,再看见 OWN:
1 | write address/length/control |
这不是编译器风格问题,而是 CPU、互连、cache 和 DMA 对内存观察顺序的问题。
4. 为什么不能绕过 DMA API
即使 arm64 平台常见 cache coherent,DMA API 仍负责:
- DMA address translation;
- IOMMU;
- DMA mask;
- mapping 生命周期;
- 非一致性平台的 cache sync。
把 CPU virtual address 直接写到 descriptor,是不可移植也不可靠的。
十三、一个网卡驱动还要照顾哪些“非收发”能力
一个可用于产品的驱动不能只实现 ping:
- MAC 地址管理和 RX filter;
- MTU 与 jumbo frame;
- multicast/promiscuous;
- checksum offload;
- scatter-gather、GSO/TSO;
- VLAN insert/strip/filter;
- RSS 与多队列;
- interrupt coalescing;
- ethtool feature/statistics/ring/coalesce;
- PTP hardware timestamp;
- EEE/LPI;
- WoL;
- tc/CBS;
- TX timeout 和 DMA error recovery;
- system/runtime suspend/resume。
RK3588 DTS 的 DWMAC 节点声明了 snps,tso。如果怀疑 offload 路径,可以先用最小配置缩小范围:
1 | ethtool -k eth0 |
如果关闭某项后恢复正常,下一步应检查 descriptor bit、skb metadata、MTU/frags 和 feature advertisement,而不是永久关闭功能后结束分析。
十四、关闭与挂起:先阻止新工作,再释放资源
stmmac_release() 位于 stmmac_main.c:3060。关闭路径的核心顺序是:
1 | 停止 TX queue |
最危险的错误是先释放 ring,后停止 DMA。此时设备仍可能按旧 DMA address 写内存。
RK3588 的 PM 包装位于 dwmac-rk.c:2848-2877:
- 不需要 WoL 时,suspend 会关闭 Rockchip GMAC 电源/时钟;
- 需要 WoL 时,必须保留足够的 PHY、MAC 和唤醒 IRQ 资源;
- resume 先恢复平台资源,再恢复 stmmac。
WoL 经常跨越 PHY 电源、MAC wake IRQ、clock domain、pinctrl sleep state 和系统电源策略,不能只看一个 ethtool -s wol g。
十五、故障定位:先判断坏在哪一层
场景 1:驱动没有 probe
1 | dmesg | grep -Ei 'stmmac|dwmac|gmac|ethernet' |
检查:
- 最终 DT 节点是否
okay; - compatible 是否正确;
- clock/reset/power-domain;
- pinctrl 冲突;
- 内核配置。
本地 BSP .config 中已启用:
1 | CONFIG_STMMAC_ETH=y |
场景 2:probe 成功,但找不到 PHY
1 | dmesg | grep -Ei 'mdio|phy|no phy|attached PHY' |
检查:
- MDC/MDIO pinmux;
- PHY 电源;
- reset GPIO 与延时;
- strap 地址与 DTS
reg; - 读取 ID 是否为
0x001cc916。
场景 3:PHY 存在,但 no link
1 | ethtool eth0 |
优先检查:线缆、网络变压器、PHY 模拟供电、晶振、自动协商、PHY reset 和对端能力。此时 DMA ring 通常还不是首要矛盾。
场景 4:link up,但 ping 不通
1 | ip -s link show eth0 |
分层判断:
- 没有 RX IRQ:看 RGMII、pinmux、clock、MAC enable;
- 有 IRQ、无 RX packet:看 descriptor ownership、NAPI、RX buffer;
- CRC/error 增长:看 RGMII delay 与信号完整性;
- ARP 可以、大包不行:看 MTU、checksum、TSO、DMA/cache;
- 单向不通:分别检查 TX 与 RX 时序。
场景 5:百兆正常、千兆异常
优先级通常是:
- RGMII delay 重复或缺失;
- link up 后 RK3588 clock divider 没切换;
- 125 MHz clock;
- PCB 等长和信号完整性;
- PHY 供电、变压器和四对线;
- 对端兼容性。
可短暂强制百兆做对照:
1 | ethtool -s eth0 speed 100 duplex full autoneg off |
场景 6:高负载丢包、hang 或 TX timeout
1 | ethtool -S eth0 |
重点检查:
- RX missed/overflow、buffer unavailable;
- NAPI budget 和 softnet backlog;
- IRQ affinity、RPS/XPS;
- ring size、coalescing;
- RX refill;
- TX queue stop/wake;
- DMA mapping/IOMMU error;
- 温度、clock 和电源管理。
十六、推荐的真实开发板 bring-up 路线
第一步:静态硬件闭环
- 原理图确认 GMAC0/GMAC1;
- PHY 型号、strap 地址、reset、IRQ、clock source;
- RGMII delay 由 PCB、PHY、SoC 哪一方承担;
- DTS status、pinmux、phy-mode、delay、phy-handle;
- Kconfig。
第二步:只验证控制面
- platform probe;
- clock/reset/power;
- MDIO 稳定读取 PHY ID;
ethtool能显示 PHY 能力。
第三步:验证链路状态机
- 10/100/1000 自动协商;
- 网线插拔 carrier 变化;
- speed change 时 SoC 分频变化;
- PHY IRQ 或轮询稳定。
第四步:最小数据路径
- ARP、ping;
- 临时关闭复杂 offload;
- 观察 RX/TX packets、IRQ、NAPI、DMA error。
第五步:性能、温度和电源回归
- 单向与双向 iperf3;
- 小包、大包、并发流;
- 长时间压力;
- suspend/resume;
- 网线热插拔和对端速率变化;
- 逐项恢复 offload。
QEMU
virt无法真实模拟 RK3588 GRF、RGMII 时序、外置 RTL8211F、变压器和板级信号完整性。这类问题可以在 QEMU 中学习通用网络栈,却必须在真实开发板上完成最终验证。
十七、源码阅读地图
如果希望沿本文继续读代码,推荐按这个顺序:
rk3588.dtsi:690、rk3588s.dtsi:5500:SoC 资源;rk3588-firefly-port.dtsi:179:板级 RGMII 与 PHY;dwmac-rk.c:1881-2018:RK3588 interface/clock/delay;dwmac-rk.c:2774:平台 probe;stmmac_main.c:5061:通用 probe;stmmac_main.c:4789:netdev ops;stmmac_main.c:2901:open;stmmac_main.c:3468:TX;stmmac_main.c:4254与:4071:IRQ/NAPI;stmmac_main.c:3862:RX;stmmac_mdio.c:406:MDIO bus;stmmac_main.c:865-1110:phylink MAC callbacks;realtek.c:181:RTL8211F internal delay;drivers/net/phy/phy.c:1197:PHY 状态机。
把这些入口串起来后,一个 SoC 网卡驱动的全景就会从“很多不相关的文件”变成四条清晰主线:
1 | 板级资源线:DTS → dwmac-rk → clock/reset/GRF/delay |
小结
RK3588 + RTL8211F 网卡驱动最值得掌握的,不是某个寄存器魔数,而是分层边界和状态闭环:
- MAC 与 PHY 是两个硬件世界,RGMII 传数据,MDIO 做管理;
- dwmac-rk 解决 SoC 接入问题,stmmac 解决通用 MAC/DMA 数据路径;
- phylink 协调 MAC 与 PHY,Realtek driver 只实现 RTL8211F 差异;
- probe 与 open 不同:前者注册设备,后者才启动 ring、IRQ、DMA 和 PHY;
- IRQ 只负责快速调度,NAPI 批量处理数据;
- TX/RX 的核心是不丢失 descriptor、DMA mapping、page 和 skb 的所有权;
- RGMII delay 是板级时序设计,不是可以盲目复制的经验值;
- 排错要先判断层次:probe、MDIO、PHY link、RGMII、DMA/NAPI、协议栈,不能看到 link up 就跳过中间所有环节。
一旦用这套模型分析问题,link up but no traffic、100M works but 1G fails、RX stops after seconds 这类现象就不再是随机故障,而会变成可以逐层收集证据、逐层排除的工程问题。
