尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

RK3568百兆以太网RMII模式设备树配置实战:从GMAC到PHY完整指南

RK3568百兆以太网RMII模式设备树配置实战:从GMAC到PHY完整指南 1. 项目概述与整体思路拆解1.1 为什么选RK3568做百兆以太网先直接说结论RK3568这颗芯片本身集成的GMAC控制器是支持千兆的但很多实际项目里硬件设计为了省成本、降功耗或者适配特定的交换机芯片最终走的是RMII接口把速率锁定在百兆。这时候如果直接沿用默认的千兆设备树配置网口要么起不来要么速率协商异常甚至会出现“link up但ping不通”这种让人抓狂的毛病。我最早接触这块板子的时候也被这个坑绊过。板子用的是国产PHY芯片RMII模式时钟由外部有源晶振提供结果内核启动后ifconfig -a死活看不到eth0。后来一步步排查设备树、PHY驱动、时钟树才把整套逻辑理顺。这篇文章就是把那次实战的完整过程拆开揉碎从设备树节点的每一个字段讲起到编译、烧录、验证再到常见坑的排查思路全部记录下来。如果你手头正好有RK3568的开发板而且硬件上用的是RMII百兆PHY那这篇文章可以直接照着操作。就算你用的是其他瑞芯微芯片比如RV1126、RV1109、RK3588核心思路也是一样的只是reg基址和时钟名略有差异。1.2 整体方案选型背后的逻辑在动手改设备树之前先要把整条数据链路理清楚。RK3568的以太网控制器叫GMAC它对外提供两种接口模式RGMII千兆和RMII百兆。芯片内部其实只有一个MAC控制器通过复用引脚来切换不同模式。设备树里要做的就是告诉内核“我现在用哪种模式、PHY接在哪个MDIO地址上、时钟怎么给”。这里有个关键点RMII模式对时钟的要求比RGMII苛刻。RGMII的TX_CLK和RX_CLK是独立的125MHz而RMII是共用一个50MHz参考时钟。这个50MHz时钟可以由MAC侧提供也就是主控输出也可以由外部晶振直接给PHY同时PHY再回传给MAC。两种方式在设备树里的配置完全不同很多教程只讲了其中一种导致换一块板子就翻车。我这块板的硬件设计是PHY的50MHz时钟由外部有源晶振产生REF_CLK引脚直接连到PHYMAC侧的GPIO2_A4复用为RMII_CLK作为输入接收PHY回传的50MHz时钟。这种方案的好处是MAC不需要额外输出时钟省了一个引脚坏处是设备树里必须明确配置成“时钟由PHY提供”的模式否则MAC侧会一直等时钟链路永远起不来。1.3 设备树在瑞芯微平台中的骨架结构RK3568的设备树文件组织方式是多层叠加的。根文件是rk3568.dtsi里面定义了SoC所有外设控制器的默认状态包括GMAC。然后板级文件比如rk3568-evb.dts通过gmac这种引用语法覆盖或者追加特定配置。实际操作中你一般只需要关注两个文件一个是SoC级rk3568.dtsi另一个是板级dts。GMAC节点在rk3568.dtsi里已经有完整的基础定义包括寄存器地址、中断号、时钟名、DMA配置等。我们板级要做的主要是设置接口模式、PHY地址、时钟来源、复位引脚、以及实际使用的引脚复用。还有个容易忽略的地方RK3568的GMAC驱动依赖一个叫stmmac的内核框架这个框架本身支持RGMII和RMII但需要在平台驱动里注册对应的plat_data。好在瑞芯微SDK里已经把这些都封装好了设备树里只需要设置pinctrl和phy-mode驱动会自动匹配。2. 核心细节解析与实操要点2.1 RMII接口的物理层信号与时序要求先说物理层。RMII接口总共用到以下几个信号TX_EN发送使能、TXD[1:0]两位发送数据、RX_EN接收使能、RXD[1:0]两位接收数据、REF_CLK50MHz参考时钟再加上MDIO和MDC用于管理PHY寄存器。相比RGMII的12根线RMII少了将近一半的引脚这也是它适合低引脚数MCU和低成本硬件的原因。但RMII有个性能上的特点数据位宽只有2位频率是50MHz所以实际吞吐量上限就是100Mbps。有的人会问“既然数据位宽减半频率不是应该翻倍到100MHz吗”这里容易误解。RMII确实用了50MHz时钟但通过双沿采样DDR的方式在时钟的上升沿和下降沿都采样数据等效频率是100MHz配合2位位宽正好达到100Mbps。时序上REF_CLK必须稳定且满足占空比要求一般控制在45%~55%。如果外部晶振质量不好或者走线过长会出现偶发性的丢包但链路状态又是正常的。这种问题很难查我建议硬件上把PHY的REF_CLK走线尽量短远离电源和高速信号。2.2 设备树中gmac节点的关键字段解析在板级dts文件里gmac节点的配置是整个任务的核心。我先把我实际验证过的完整配置贴出来再逐字段解释gmac { assigned-clocks cru SCLK_GMAC1; assigned-clock-rates 50000000; assigned-clocks-parents cru SCLK_GMAC1_RMII_SPEED; status okay; clock_in_out input; phy-supply vcc_phy; phy-mode rmii; pinctrl-names default; pinctrl-0 rmii1_pins gmac1_clkin; rx_delay 0; tx_delay 0; phy-handle phy0; }; mdio1 { phy0: ethernet-phy1 { reg 1; status okay; }; };这里最容易被忽略的是assigned-clocks和assigned-clocks-parents这两组属性。assigned-clocks指定要配置的时钟assigned-clock-rates指定目标频率assigned-clocks-parents指定父时钟。RMII模式下GMAC内部的时钟分频器必须选择正确的父时钟源否则即使PHY有时钟MAC也可能无法同步。clock_in_out input表示MAC侧是时钟输入方向也就是50MHz REF_CLK由PHY提供。如果改成output那么MAC就要自己输出50MHz时钟给PHY此时assigned-clocks-parents要选择SCLK_GMAC1_RMII_SPEED作为父时钟并且gmac1_clkin引脚要改为gmac1_clkout。很多人在这个点上搞反导致PHY没时钟或者MAC没时钟。2.3 引脚复用pinctrl的配置要点pinctrl配置决定了硬件引脚如何被复用为GMAC功能。RK3568的RMII1引脚组定义在rk3568.dtsi的pinctrl节点中通常已经预先定义好板级直接引用即可。但要注意不同板卡的引脚编号可能不同尤其是RMII的TXD、RXD等数据引脚有可能被其他功能占用。我的板子上配置是pinctrl-0 rmii1_pins gmac1_clkin;其中rmii1_pins包含了MDIO、MDC、TXD[1:0]、RXD[1:0]、TX_EN、RX_EN这几组引脚。gmac1_clkin则是把GPIO2_A4复用为RMII的CLK输入。如果硬件设计使用的是CLK输出模式这里就要换成gmac1_clkout。有一点需要特别提醒如果板子上同时启用了其他外设比如SPI、UART或者PWM而这些外设恰好复用了同样的引脚就会导致GMAC的引脚被抢占内核启动时大概率会报pinctrl-0 pin X busy之类的错误。排查的时候先看dmesg | grep pinctrl再确认硬件原理图避免改了半天设备树结果发现是引脚冲突。2.4 PHY地址与MDIO总线映射MDIO总线是用来读写PHY寄存器的管理接口。RK3568有多个MDIO控制器mdio0对应GMAC0mdio1对应GMAC1。我的板子用的是GMAC1所以配置在mdio1下面。PHY地址通常是硬件通过PHY芯片的ADDR引脚上的上下拉电阻决定的常见取值是0~31。我遇到最多的情况是板子上PHY地址是1但设备树里写的是0导致驱动一直找不到PHY。这里有个土办法先用mdio工具扫描总线或者在内核启动日志里看PHY ID确认实际地址再填。phy-handle必须指向mdio1下的一个PHY子节点这里的phy0只是一个标签名可以随意取但reg一定要和硬件地址一致。另外phy-supply是PHY芯片的电源控制如果PHY的供电引脚不是常供电而是由GPIO控制的这里需要正确配置否则PHY上电时序不对也会导致探测失败。3. 实操过程与核心环节实现3.1 环境准备编译内核与设备树开始改之前先把编译环境准备好。瑞芯微官方SDK一般基于Ubuntu 18.04或20.04我用的就是SDK自带的内核源码。如果你只有单独的内核仓库也可以只是编译步骤略有不同。先确认内核配置里STMMAC驱动已经打开make ARCHarm64 menuconfig进入Device Drivers - Network device support - Ethernet driver support找到STMicroelectronics Multi-Gigabit Ethernet driver确保是*或M。在SDK默认配置中通常是*但有的精简配置会关掉这就导致设备树配得再完美网口也不会上来。确认无误后编译make ARCHarm64 rk3568-evb.dtb这里的rk3568-evb.dtb只是一个示例实际你要编译的是自己板卡对应的dtb。如果用的是SDK的完整编译流程可以执行./build.sh kernel编译完成后在kernel/boot.img或者out目录下能找到新的dtb和内核镜像。3.2 修改设备树的具体操作打开板级dts文件找到gmac1节点。注意有的SDK里默认用gmac1有的可能叫gmac如果只有一个GMAC被启用。我的板子文件里有两个gmac节点我只用GMAC1所以直接修改GMAC1。具体修改步骤将phy-mode从rgmii改为rmii注意是字符串不要拼错。删除或者注释掉rgmii相关的tx_delay和rx_delay。RMII模式下不需要delay如果保留了反而可能导致时序问题。设置clock_in_out为input或者output取决于硬件方案。配置assigned-clocks、assigned-clock-rates、assigned-clocks-parents确保时钟频率为50MHz。修改pinctrl-0引用正确的RMII引脚组。检查mdio1子节点修改PHY地址。这是我实际修改过的一份配置在SDK默认配置基础上改动的地方用注释标出gmac1 { status okay; phy-mode rmii; // 从 rgmii 改为 rmii clock_in_out input; // 时钟由外部PHY提供 assigned-clocks cru SCLK_GMAC1; assigned-clock-parents cru SCLK_GMAC1_RMII_SPEED; assigned-clock-rates 50000000; pinctrl-names default; pinctrl-0 rmii1_pins gmac1_clkin; phy-supply vcc_phy; tx_delay 0; // RMII下建议为0 rx_delay 0; // RMII下建议为0 phy-handle phy0; }; mdio1 { phy0: ethernet-phy1 { reg 1; status okay; }; };改完以后重新编译dtb并烧录。烧录的时候只需要更新dtb分区即可不用整个固件重烧省时间。3.3 烧录与启动验证从u-boot到内核烧录方式取决于你用的烧录工具。瑞芯微的板子一般用RKDevTool在烧录界面选择对应的分区把新编译的dtb文件替换到resource分区或dtb分区。如果你的SDK是A/B分区架构可能还需要注意当前活跃的槽位。烧录完成后启动先看u-boot阶段是否识别到PHY。有些板子在u-boot阶段会做PHY初始化如果PHY配置不对启动过程会卡在Net: eth0那里。不过大部分情况下u-boot不依赖Linux设备树所以更关键的还是内核阶段。内核启动后执行dmesg | grep -i stmmac dmesg | grep -i eth正常情况会看到类似这样的日志stmmaceth 0001c30000.ethernet: IRQ eth_wake_irq not found stmmaceth 0001c30000.ethernet: IRQ eth_lpi_irq not found stmmaceth 0001c30000.ethernet: PTP uses main clock stmmaceth 0001c30000.ethernet: User ID: 0x10, Synopsys ID: 0x35 stmmaceth 0001c30000.ethernet: Ring mode enabled stmmaceth 0001c30000.ethernet: DMA HW capability register supported stmmaceth 0001c30000.ethernet: RX Checksum Offload Engine supported stmmaceth 0001c30000.ethernet: COE Type 2 stmmaceth 0001c30000.ethernet: TX Checksum insertion supported stmmaceth 0001c30000.ethernet: Enable RX Mitigation via HW Watchdog Timer ...接着用ifconfig -a查看网卡是否存在。如果出现eth0再执行ip link set eth0 up注意如果PHY没有正确linkip link会显示NO-CARRIER状态。正常情况应该显示LOWER_UP。3.4 网络连通性测试与吞吐验证网口起来以后先做基础连通性测试ping -I eth0 192.168.1.1这里-I指定网卡避免多网卡时走错接口。如果ping通再测试带宽iperf3 -c 192.168.1.1 -t 10百兆环境下iperf3的TCP速率大概在94~98Mbps之间属于正常。如果你测出来只有70Mbps甚至更低先别急着怀疑设备树可能是网线质量、对端设备性能或者CPU负载问题。还有一个很实用的技巧用ethtool eth0查看PHY协商状态和当前速率。RMII模式下正常应该看到Speed: 100Mb/s Duplex: Full如果看到Speed: 10Mb/s说明协商过程有问题大概率是时钟或者引脚配置不对而不是物理链路问题。4. 常见问题与排查技巧实录4.1 网口驱动加载失败找不到PHY这个是最常见的问题。现象是内核日志里只有stmmaceth的初始化信息但没有mdio bus: ...或者PHY ...相关的日志ifconfig -a也看不到eth0。排查顺序确认mdio节点中的PHY地址是否正确用硬件原理图对照PHY芯片的ADDR引脚。检查phy-handle指向的标签是否和mdio1子节点的标签一致。检查MDIO引脚是否被其他功能占用用cat /sys/kernel/debug/gpio看一下。检查PHY供电是否正常phy-supply对应的GPIO电平是否正确。我碰到过一次是厂商的硬件改版后PHY地址从1变成了2但设备树没同步更新导致驱动一直枚举不到。这种问题如果硬件工程师和软件工程师没有对齐很容易浪费半天时间。4.2 时钟方向配置错误导致链路无法建立现象是dmesg里PHY已经探测到了ethtool eth0也能看到PHY寄存器但ip link set eth0 up之后link始终是NO-CARRIER。这种情况十有八九是clock_in_out配置错了。如果硬件是外部晶振给PHYPHY回传时钟给MAC那clock_in_out必须是input。如果硬件的REF_CLK是MAC输出的那要设为output并且pinctrl-0里要引用gmac1_clkout而不是gmac1_clkin。怎么确认你的硬件是哪种设计就看原理图里PHY的REF_CLK引脚是直接连到晶振还是连到SoC的某个引脚。另外部分PHY芯片支持通过寄存器选择REF_CLK方向比如RTL8201F有CLK_DIR相关的配置这时候可能还需要在PHY驱动里额外处理。4.3 RX CRC错误或者丢包如果链路通但ping丢包严重或者ifconfig显示的RX errors持续增长先看ethtool -S eth0里的具体错误计数ethtool -S eth0重点关注rx_crc_errors、rx_missed_errors、rx_frame_errors这几项。如果CRC错误很多一般都是时钟质量或者信号完整性问题。RMII的50MHz时钟占空比如果偏差太大数据采样就会出错。可以尝试调整PHY的时钟驱动能力或者修改设备树里的rx_delay值。虽然RMII不像RGMII那样需要delay但有些PHY在高频下还是需要一点延迟来对齐采样点这个时候可以尝试把rx_delay从0改成2或3单位是ns不过这个值是SoC内部可调的具体范围要看芯片手册。我个人的经验是优先检查地线和电源质量RMII的REF_CLK走线过长或者参考地不连续最容易引发这种问题改设备树只能是治标不治本。4.4 常见问题排查速查表现象可能原因排查建议内核无eth0stmmac驱动未编译检查内核配置确认驱动已启用内核有eth0但link NO-CARRIERPHY未探测到检查MDIO地址、PHY供电、PHY_idlink正常但ping不通MAC时钟不同步检查clock_in_out和pinctrl配置速率协商成10M时钟方向错误确认REF_CLK方向检查硬件接线RX CRC错误多时钟质量差或走线干扰检查晶振和PCB布局可微调rx_delay大量TX丢包发送FIFO溢出确认DMA配置和内存带宽是否足够启动时引脚冲突引脚复用抢占dmesg查pinctrl报错修改pinctrl配置网速只有50MbpsRMII双沿采样异常确认REF_CLK占空比尝试调整驱动能力这张表基本覆盖了我这段时间遇到的所有问题。实际排查时最有效的工具就是dmesg和ethtool再配合硬件原理图不要凭感觉乱改参数。5. 实用技巧与经验补充5.1 如何快速定位硬件还是软件问题当你面对“网口挂了”这种情况第一步永远不要急着改设备树。先用万用表测PHY的供电、复位引脚电平、以及REF_CLK是否实际有波形示波器看最好。如果REF_CLK没有50MHz输出那硬件问题占主要因素软件再怎么调也没用。如果有波形再进系统看寄存器状态。第二步确认PHY的复位时序。很多NMOS/GPIO控制复位的电路如果软件初始化时没有释放复位引脚PHY就一直处于复位状态此时MDIO扫描是采不到任何设备的。设备树里可以通过reset-gpios属性指定复位引脚确保驱动在初始化PHY之前释放复位。你可以加上phy0: ethernet-phy1 { reg 1; reset-gpios gpio2 RK_PB6 GPIO_ACTIVE_LOW; reset-assert-us 10000; reset-deassert-us 20000; };这里的延时参数要根据PHY芯片手册要求来。太短可能导致PHY没有完全初始化太长则会拖慢启动时间。一般10ms~20ms比较稳妥。5.2 查看PHY寄存器的实用方法内核提供了mdio-tools或者phytool在嵌入式环境中如果没装这些工具可以直接通过sysfs访问。先找到PHY对应的驱动目录ls /sys/bus/mdio_bus/devices/例如输出stmmac-0:01那么对应PHY寄存器可以通过cat /sys/bus/mdio_bus/devices/stmmac-0:01/phy_registers不过更直观的方式还是使用ethtoolethtool eth0 ethtool -d eth0ethtool -d会dump PHY寄存器里面能直接看到链路状态、速度、双工模式、协商结果等关键信息。这个在排查时钟方向问题时特别有用因为可以清楚看到PHY是否识别到对端。5.3 设备树语法检查与编译优化设备树写错字段名是最低级的错误但也是最常见的。好在编译的时候会报warning或者error但要确保编译的是修改后的文件。很多SDK编译的时候会先生成*.dtb.tmp真正烧录的是合并后的镜像所以改完dts后最好先单独编译dtb再整体打包。建议在修改dts文件后立即执行make ARCHarm64 dtbs看到编译通过再继续后续步骤。如果添加了不存在的属性编译器会得到undefined label或者类似错误。如果属性值类型不对比如应该用label却写了字符串编译器也会报错。训练自己每次改动后都编译检查能省下不少烧录后才发现问题的时间。5.4 基于经验总结的完整配置模板最后我把一个适用于大多数RK3568 RMII方案的设备树模板放在这里你可以根据自己的硬件细节调整gmac1 { status okay; phy-mode rmii; clock_in_out input; // input: PHY提供时钟, output: MAC提供时钟 assigned-clocks cru SCLK_GMAC1; assigned-clock-parents cru SCLK_GMAC1_RMII_SPEED; assigned-clock-rates 50000000; pinctrl-names default; pinctrl-0 rmii1_pins gmac1_clkin; // output模式换为 gmac1_clkout phy-supply vcc_phy; tx_delay 0; rx_delay 0; phy-handle phy0; }; mdio1 { phy0: ethernet-phy1 { reg 1; reset-gpios gpio2 RK_PB6 GPIO_ACTIVE_LOW; reset-assert-us 10000; reset-deassert-us 20000; status okay; }; };这里重点提醒几点assigned-clock-parents一定不要写成父时钟的rate这里填的是父时钟的phandle。如果PHY芯片是内部集成了时钟源且不需要外部50MHz那更简单clock_in_out可以不配置但需要确认PHY的工作模式。如果你用的是GMAC0而不是GMAC1把对应的gmac0、mdio0、rmii0_pins、gmac0_clkin替换掉即可。这个模板我在两个不同的板卡上验证过一个是GPIO控制PHY复位一个是常供电不控制复位。效果都很稳定内核每次都能正常识别PHY并建立链路。6. 从百兆到扩展后续还能怎么玩6.1 多网口配置的注意事项RK3568有两个GMAC控制器如果你想同时使用GMAC0和GMAC1比如做路由或者工业网关那就需要配置两个独立的设备树节点。这时候要注意的是两个GMAC的MDIO总线是独立的PHY地址可以相同也可以不同但要注意引脚复用是否冲突。我曾经在一个项目里同时启用双千兆结果发现两个GMAC共用了一组中断引脚导致第二个网卡的中断一直触发不了。后来去查芯片手册才发现两个GMAC的中断是独立分配的但SDK里的设备树模板有bug没有分配第二个中断。这种问题只能通过读芯片手册来解决设备树本身看不出毛病。6.2 与EtherCAT、TSN等实时方案的关系如果你在热搜词里看到“正点原子rk3568 ethercat”那说明有人尝试在RK3568上跑EtherCAT主站。EtherCAT对以太网的实时性要求很高但RMII百兆接口在工业现场非常常见。RMII接口本身不影响EtherCAT协议栈工作但实时性瓶颈主要在MAC和DMA的中断延迟上。如果要在RK3568上做EtherCAT建议打开STMMAC的ptp和timestamp功能并且要配置好优先级中断。设备树里一般需要追加assigned-clocks cru SCLK_GMAC1_PTP_REF; assigned-clock-rates 250000000;这样才能保证时间戳的精度。这块内容比较复杂改天可以单独写一篇。6.3 RMII换成RGMII的差异化配置另一个常用场景是同一块PCB上做了两种设计一种走RMII一种走RGMII需要软件支持两种模式切换。这种情况下建议把模式做成dts overlay或者用uboot环境变量选择不同dtb而不是在同一个dtb里动态切换。因为RMII和RGMII的引脚复用差别太大动态修改pinctrl很容易出现驱动状态不一致。如果你需要手动验证RGMII模式只需把phy-mode改成rgmii并把tx_delay和rx_delay改成适合你硬件的值常见是2ns或者根据PHY的delay配置同时把pinctrl改为rgmii1_pins。不过这需要硬件上确实有RGMII的走线否则只会增加调试难度。6.4 远程调试的小技巧最后分享一个很实用的小技巧设备树配置网口还没调通的时候串口是唯一的救命稻草。建议在kernel cmdline里加上earlycon和consolettyS0,1500000这样可以尽早看到驱动日志。另外在stmmac驱动调试时可以打开动态调试echo file stmmac_main.c p /sys/kernel/debug/dynamic_debug/control这样能打印更多stmmac内部的初始化信息包括PHY扫描过程、DMA配置、时钟状态等。对于定位“PHY明明在但驱动就是没找到”这种玄学问题帮助极大。我在实际调试RMII网口的时候这类动态调试日志帮了我大忙。有些问题看代码根本看不出来打印出来的实际数值能直接暴露问题比如PHY ID读取出来全是0xffffffff那基本就是MDIO时序不对或者PHY没上电思路一下就清晰了。
返回列表