嵌入式Linux PHY芯片调试:从飞腾到RK3568的RGMII寄存器实战
做嵌入式Linux的总会在某个项目里跟PHY芯片杠上。我最近连续在两个平台间来回切换——一边是飞腾D2000的板卡一边是RK3568的板卡PHY芯片则固定用YT8521和AR8035。一开始我以为换个CPU而已设备树抄过来改改引脚就能跑结果整整折腾了三天最后发现问题全在PHY寄存器调试这个环节上。这篇文章不是芯片手册的翻译也不是内核驱动源码解析而是我实际在两个平台之间移植、调试PHY的记录。核心会围绕YT8521和AR8035这两颗芯片讲清楚怎么用MDIO读寄存器、怎么判断link/协商/CRC问题、设备树里各种delay配置到底怎么配对、以及从飞腾平台换到RK3568平台时最容易踩的几个坑。适合正在做BSP适配、SCM平台切换、国产化替换的嵌入式工程师参考尤其适合被“百兆正常千兆CRC错误”折磨的朋友。1. 从飞腾D2000到RK3568换了CPUPHY为什么先翻车1.1 背景两块板卡、两颗PHY一次交叉替换先说项目背景。上一块板子是飞腾D2000平台跑Linux 4.19内核GMAC接口外接一颗AR8035千兆PHY设备树里配的是rgmii-id模式链路稳如老狗。新板子是RK3568平台用的还是那颗AR8035我当时很自信地照着飞腾的设备树改了一版结果插上网线link是有的但千兆模式下PING丢包ethtool -S里rx_crc_errors蹭蹭往上涨。后来另一块RK3568板卡用了国产YT8521问题更隐蔽百兆一切正常切到千兆后接收方向大量硬件CRC错误协商却显示1000M成功。这两次经历让我意识到平台换了PHY寄存器调试思路必须跟着换不能只搬设备树。如果你也遇到类似情况先别怀疑PHY芯片坏了。绝大多数情况下问题出在MAC和PHY之间的RGMII接口——特别是时钟延迟delay配置。RGMII不像MII那样有独立的RXDV采样窗口它靠边沿对齐和2ns延迟来保证数据正确性这个2ns到底是MAC补还是PHY补不同的平台、不同的PHY默认情况完全不一样。1.2 平台差异点不只是CPU指令集变了MAC和MDIO实现也不同飞腾D2000和RK3568都是ARM架构但外设控制器设计差别很大。飞腾的GMAC和瑞芯微的GMAC虽然都可能挂到Linux的stmmac框架下但硬件寄存器、时钟树、复位逻辑、DMA描述符布局都有差异。也就是说哪怕设备树语法差不多内核驱动实际操作的是两套完全不同的硬件。最直接的体感是MDIO总线。飞腾平台在默认内核配置下MDIO时钟分频、上拉强度、PHY地址映射都比较宽松RK3568这边如果管脚复用没配好或者MDIO上拉电阻没贴会出现“PHY地址扫不到”“读ID读一半”的现象。而且不同平台自带的内核配置差别很大比如飞腾内核可能默认开启了CONFIG_PHYLIB和CONFIG_AT803X_PHYRK3568则默认启用的是瑞芯微自己的GMAC驱动PHY驱动不一定匹配。另外时钟源差异也容易被忽略。飞腾板卡上GMAC参考时钟一般固定由外部晶振提供而RK3568开发板设计里GMAC的125MHz时钟经常是由CPU内部PLL输出经过SoC的clk_gmac1_ethernet引脚送给PHY的。设备树里少了assigned-clock-rates配置或者clock_in_out字段不对会导致PHY根本没有参考时钟线上完全没信号。所以跨平台调试PHY的第一步不是翻PHY的数据手册而是先把两个平台的MAC、MDIO、时钟、复位这四件事对齐。很多寄存器问题本质上是物理链路时钟不对不是寄存器值不对。1.3 调试思路先别动硬件先拿寄存器说话我常用的调试顺序是这样的确认PHY能被MDIO访问到。如果PHY ID都读不出来后面所有操作都是空谈。确认PHY的link状态。用基本状态寄存器看link up没有协商到多少速率是全双工还是半双工。确认收发路径。通过ethtool -S看错误计数比如CRC错误、alignment error、missed packets。最后才去动delay、EEE、流控这些高级寄存器。这个顺序看起来简单但很多人容易跳步。我在调试YT8521的时候一开始直接看设备树里的delay配置改来改去也没找到问题后来老老实实把PHY寄存器从头到尾dump了一遍才发现是PHY内部的接收延迟没有使能导致RK3568 GMAC采样窗口不对。再补充一句调试PHY寄存器的时候建议手边放一个万用表先把PHY供电、复位引脚、参考时钟这些物理电平等量出来。尤其是RK3568平台有些公板设计GPIO默认上下拉会自动把PHY的某个strap引脚拉到非预期电平这个用寄存器看不出来只能靠硬件排查。2. 熟悉两颗PHYYT8521与AR8035的寄存器基础2.1 怎么用MDIO把PHY内裤翻出来PHY寄存器调试的核心工具是MDIO总线。不管是标准Clause 22还是扩展的Clause 45调试第一步都建议用mdio命令行工具而不是直接在驱动里打日志。Linux下的mdio-tools装好之后用法很简单# 扫描当前系统里的PHY地址 mdio list # dump一个PHY的所有标准寄存器 mdio dump phy0 # 读某个寄存器的值 mdio read phy0 0x02mdio dump phy0输出0x00到0x1F共32个寄存器这是诊断PHY问题最直接的“体检报告”。如果你发现某一项值明显不对再去查数据手册对应寄存器位基本能定位方向。对PHY来说最重要的几个寄存器如下寄存器地址寄存器名关键信息0x00BMCR速度、双工、自协商、回环、Power Down0x01BMSRLink状态、自协商能力、支持的速度0x02/0x03PHY ID Reg读取PHY芯片型号和版本号0x04ANAR本端自协商通告能力0x05ANLPAR对端自协商能力接收结果0x0A扩展状态寄存器1000BASE-T半双工/全双工支持通过BMSR的bit2可以确认link状态。注意这个位是锁存的如果曾经link up过然后再断掉必须重新读才会更新。所以排查时经常要连续读两次第一次读把锁存状态清掉第二次读才是当前真实状态。2.2 YT8521和AR8035的常见寄存器差异YT8521和AR8035虽然都是千兆PHY寄存器兼容IEEE 802.3通用部分但在扩展寄存器上差异非常大。AR8035属于高通的QCA8033系列PHY ID在设备树里常见写法是ethernet-phy-id004d.d072它的扩展寄存器有一部分叫Debug Register需要通过两个寄存器间接访问。YT8521则是国产裕太微的芯片同样支持千兆RGMII/SGMII但扩展寄存器访问方式更接近“Page寄存器”模式经常要先往某个寄存器写入页选择值再去另一个寄存器读写具体数据。具体页号和寄存器地址不同型号版本会有差异但这个访问套路一定要知道否则你看到的寄存器值全是默认值改了半天等于白改。另外YT8521支持以太网供电检测、wake-on-lan、节能以太网EEE这些功能。EEE在普通网络下问题不大但在EtherCAT这类实时工业以太网场景下默认打开EEE或者MDI节能功能会引入额外的延迟和丢包调试时最好先在设备树或者寄存器层面把它关掉。AR8035这边还有个典型特征是“Smart Speed”在某些百兆/千兆切换场景下会自动调整信号补偿。这个功能本意是好的但如果在板级匹配不好的时候反而会导致千兆接收误码。我遇到过AR8035在低温环境下出现大量CRC错误最后不是寄存器配置问题而是接收端走线串扰但一开始也怀疑过Smart Speed相关寄存器。2.3 用寄存器判断link、speed、duplex和CRC问题当你怀疑“协商到了1000M但收发不正常”时不要只信ethtool eth0的输出。ethtool显示的Speed/Duplex是MAC驱动从PHY那边拉过来的状态它只代表PHY认为链路协商好了并不代表物理信号质量没问题。标准做法是同时看这几个寄存器BMSR (0x01)确认link up并且当前PHY有能力工作在千兆模式。ANLPAR (0x05)对端是否通告了1000BASE-T全双工。如果对端只通告百兆那协商下来自然是百兆。扩展状态寄存器 (0x0A)确认PHY确实支持1000BASE-T并且协商结果为1000M全双工。厂商状态寄存器例如某些PHY在特定寄存器里统计接收Symbol误差、CRC错误次数这个比MAC侧统计更靠近物理层。CRC错误多通常说明PHY接收到的信号没问题但是时钟采样错位了。在RGMII模式下最典型的两个原因一是TX/RX delay配置错误二是参考时钟质量不佳。前者靠寄存器能查后者就需要用示波器看时钟边沿和数据边沿的相位关系了。我在遇到“百兆正常千兆CRC错误”时先用寄存器确认了协商结果确实是1000M全双工然后看MAC收包错误计数。错误集中在rx_crc_errors基本排除软件栈问题直接定位到RGMII时钟采样窗口。接下来就是反复修改delay配置做二选一实验。3. 从飞腾设备树到RK3568设备树PHY部分改哪里3.1 管脚复用和MDIO总线先查清楚从飞腾平台移植到RK3568最不能偷懒的就是管脚复用。飞腾D2000的GMAC管脚不一定和RK3568的管脚号、复用功能一一对应直接复制设备树会导致MDIO读写异常。在RK3568设备树里需要确认gmac1_miim、gmac1_rgmii_clk、gmac1_rgmii_bus这些pinctrl节点是否被正确引用了。以gmac1为例经常是这样定义的gmac1 { phy-mode rgmii-id; clock_in_out output; assigned-clocks cru CLK_GMAC1_TX_RX, cru CLK_GMAC1_ETHERNET; assigned-clock-rates 0, 125000000; pinctrl-names default; pinctrl-0 gmac1_miim gmac1_rgmii_clk gmac1_rgmii_bus; phy-handle rgmii_phy1; status okay; };注意clock_in_out这个字段它告诉驱动MAC的参考时钟是输出给PHY还是从外部输入。如果设成output就意味着RGMII参考时钟由RK3568产生并以125MHz送出如果板上有独立晶振给PHY可能应该设成input。这个字段配错PHY寄存器是能读到的但链路根本起不来。另外MDIO总线上如果挂了两颗PHY不要假设它们在同一个地址。YT8521和AR8035的默认PHY地址都由strap引脚决定可能是0x00、0x01、0x04等必须通过mdio list确认真实地址再去改设备树reg字段。3.2 phy-mode与tx/rx delay一定要和PHY内部delay配对这是整篇最核心的问题。RGMII协议要求数据在时钟上升沿/下降沿附近采样为了满足时序需要为参考时钟增加大约2ns的延迟。这个延迟可以加在TX方向也可以加在RX方向可以由MAC加也可以由PHY加关键是不能两边都加也不能两边都不加。在Linux设备树里通常这么分类phy-mode含义rgmii双方都不加内部delay所有延迟由外部PCB走线或MAC内部delay配置保证rgmii-id由PHY内部同时提供TX和RX delayrgmii-txid只由PHY提供TX delayrgmii-rxid只由PHY提供RX delay飞腾D2000平台那套板卡AR8035的strap电阻已经选择让PHY内部delay全开所以设备树里写rgmii-id是没问题的。但RK3568平台如果照抄rgmii-id而板上YT8521的默认strap并没有把delay打开那就会出现link能起来但数据采样不对的现象。相反如果RK3568内部delay也打开PHY内部也打开延迟叠到4ns同样会出错。调试时可以借助一个简单实验先把phy-mode分别改成rgmii、rgmii-id、rgmii-txid、rgmii-rxid配合ethtool -S观察CRC错误的变化。哪个模式的错误明显下降就说明这个方向上的delay来源匹配对了。3.3 reset GPIO时序、phy地址和compatible字段别想当然RK3568设备树里PHY节点的reset-gpios不是摆设。很多开发板默认模板会给一个GPIO做PHY复位但飞腾平台的老设备树可能没有这个字段。移植时必须确认复位GPIO是否存在、极性是否正确以及复位时间是否足够长。常见写法mdio1 { rgmii_phy1: ethernet-phy0 { reg 0; compatible ethernet-phy-id004d.d072, ethernet-phy-ieee802.3-c22; reset-gpios gpio3 RK_PB6 GPIO_ACTIVE_LOW; reset-assert-us 10000; reset-deassert-us 50000; }; };reset-assert-us表示复位拉低保持10msreset-deassert-us表示释放复位后等待50ms再访问PHY。不同PHY需要的复位时间不一样AR8035和YT8521我都遇到过释放太快导致PHY ID读不对的情况。特别是YT8521上电初始化比较慢50ms未必够如果发现第一次MDIO访问不到可以把这个时间加长到100ms试试。compatible字段里的ethernet-phy-idxxxx.xxxx是PHY驱动绑定用的。很多人直接抄AR8035的ID到YT8521节点里结果内核用AR8035驱动去访问YT8521的扩展寄存器越调越乱。建议先不加compatible让内核自动探测PHY ID正常识别后再把正确的ID写回设备树。3.4 RK3568特殊点gmac没有内部delay补偿时的处理RK3568的GMAC在设计上支持内部延迟线调整但不同内核版本的驱动对设备树delay字段的支持程度不一样。有些RK3568 SDK设备树里会有tx_delay和rx_delaygmac1 { tx_delay 0x2f; rx_delay 0x1b; phy-mode rgmii; };这里的值不是随便写的它们代表MAC内部delay line的档位。具体换算关系每个SoC不同需要参考RK3568 TRM。如果phy-mode设置成rgmii-id驱动一般会认为delay由PHY负责从而忽略tx_delay/rx_delay如果设置成rgmii则MAC会去配置这些内部延迟值。但这里有个坑rgmii模式下如果驱动没有写入delay寄存器或者默认值落在采样窗口边缘就会复现“CRC错误很多”的现象。我建议调试时优先走PHY内部delay这条路也就是把phy-mode设成rgmii-id然后在PHY端通过寄存器确认delay确实打开了。这样MAC和PHY职责清晰排查起来更快。4. 实操RK3568平台YT8521/AR8035寄存器调试记录4.1 环境准备与工具我调试用的环境是RK3568跑Linux 5.10内核根文件系统里安装了mdio-tools和ethtool。系统起来后先确认网络接口名和PHY地址ifconfig eth0 up mdio list默认情况下mdio list会显示类似mdio-bus:01这样的名字或者phy0。如果没有任何PHY输出检查MDIO管脚是否被复用、PHY复位GPIO是否正确、供电是否正常。排除物理问题后再去查驱动。如果系统里没有mdio命令也可以用devmem直接读写GMAC的MDIO寄存器但那样效率太低不推荐。另外强烈建议打开内核的PHY debugfs# 确认内核挂载了debugfs后可以看phy状态 cat /sys/kernel/debug/mdio_bus/*/*这些节点能看到PHY寄存器快照比反复敲命令更方便分析。内核需要打开CONFIG_MDIO_DEVID相关选项不同内核实现略有差异。4.2 复现问题百兆正常、千兆CRC错误一屏我的第一块RK3568板子用的是AR8035第二块用的是YT8521。两块板子都出现了“百兆正常千兆大量接收CRC错误”的现象但详细迹象不同AR8035那块的ethtool eth0显示Speed: 1000Mb/s Duplex: Fullethtool -S eth0里rx_crc_errors: 1048573YT8521那块更典型一开始强制千兆ethtool -s eth0 speed 1000 duplex fulllink是起来了但是PING包不稳定rx_crc_errors每秒钟涨几十万个。切回百兆ethtool -s eth0 speed 100 duplex full立刻全部正常0错误。这里有个重要现象手段强制到千兆后PHY不协商直接在物理层建链。CRC错误多基本上不是协商问题而是RGMII数据线上的时钟/数据对齐问题。也就是说问题大概率在MAC与PHY之间的digital接口而不在线缆和变压器端。4.3 定位与修复delay配置导致RX时钟采样窗口不对我先把YT8521的PHY ID读出来确认驱动绑定的正确性mdio read phy0 0x02 mdio read phhy0 0x03读出的ID和Linux驱动匹配无误。再读BMSR和扩展状态寄存器确认千兆能力没问题。接下来把重点放在RGMII delay上。我在不同phy-mode下做了几组对比实验设备树phy-modeAR8035结果YT8521结果rgmiiCRC错误很多CRC错误很多rgmii-id正常仍然有CRC错误rgmii-txidCRC错误很多CRC错误很多rgmii-rxid基本正常仍然有CRC错误这个结果说明AR8035的PHY内部delay配合rgmii-id是没问题的但YT8521即使设了rgmii-idPHY内部的RX delay可能没有真正打开。我后来在YT8521数据手册里找到扩展寄存器通过MDIO手动开启内部delay后再配合rgmii-id千兆CRC错误立刻清零。这里要强调的是不要以为设备树写rgmii-id就万事大吉。rgmii-id只是告诉MAC驱动“delay由PHY提供”但PHY到底有没有默认打开delay由芯片的strap引脚和寄存器默认值决定。如果PHY默认关闭必须额外配置。4.4 用寄存器确认最终配置并固化到设备树修复之后我用以下命令确认最终状态# 读PHY basic control register确认自协商开启 mdio read phy0 0x00 # 读PHY link状态确认千兆全双工 mdio read phhy0 0x01 # 读扩展状态寄存器确认1000BASE-T全双工能力 mdio read phy0 0x0A确认无误后将PHY内部delay的初始化配置写成设备树或内核PHY驱动补丁。有些方案是用udev/systemd在系统启动后执行mdio write但那只是临时方案重启就丢。我建议要么改PHY驱动在config_init阶段调用phy_write要么在板级设备树的PHY节点里挂载一个小的PHY fixup这两条路都比启动脚本可靠。如果做EtherCAT主站还需要额外把PHY的EEE功能关掉并且把自协商关闭强制工频100M全双工避免PHY在实时通信过程中重新协商。用MDIO命令就是# 0x2100代表100M全双工关闭自协商 mdio write phy0 0x00 0x2100这个配置同样建议写到驱动初始化代码里而不是依赖开机脚本。5. 常见问题与排查技巧实录5.1 问题速查表调试过程中积累了一些典型问题我把它们整理成速查表现象可能原因排查方法MDIO读不到PHY ID复位未释放、时钟没给、管脚复用错误、供电异常示波器量复位和时钟检查设备树reset-gpios、pinctrlPHY ID能读但link一直down变压器/网口焊接问题、对端未协商、PHY被Power Down读BMSR和BMCR确认没置power down换网线/对端设备试link up但ping不通MAC的RX/TX delay配置错误、PHY mode不匹配切换phy-mode观察CRC错误用ethtool看packets no管用百兆正常千兆CRC很多RGMII采样窗口不对、delay叠加或缺失、信号质量差分别测rgmii/rgmii-id/rgmii-txid/rgmii-rxid检查PCB走线千兆协商成功但吞吐很低EEE、流控、对端不支持千兆、MDI-X问题读ANLPAR确认对端能力关闭EEE和流控重测重启后PHY配置丢失PHY strap引脚默认值改变、复位时序不对量strap引脚电平延长复位时间将配置固化到驱动这张表不是万能的但覆盖了绝大多数常规调试卡点。很多问题最终排查发现都是“寄存器设置没固化”导致的——调试时手动写寄存器好了一重启又恢复原样。5.2 排查步骤不看文档只看寄存器怎么判断如果你手头没有完整的数据手册依靠通用寄存器也可以做一轮有效的判断。我建议按下面几步走读PHY ID寄存器确认PHY型号。如果ID全是0xFFFF或0x0000先解决物理访问问题。读BMSR确认link状态。如果link为0检查媒体接口和PHY供电。读ANAR和ANLPAR对比两端协商能力。如果对端只通告百兆PHY自然协商到百兆不是配置问题。强制千兆或者百兆看错误计数变化。如果强制百兆正常、千兆异常优先级指向RGMII接口delay。用示波器抓RGMII时钟和数据信号的相位关系。理想的TCLK边沿应该落在数据有效窗口中间如果边沿和数据跳变对齐就是delay缺失或过大。这套流程我在多个平台验证过基本能排除九成以上的PHY问题。而且它不依赖某个具体PHY厂商驱动通用性很强。5.3 实操心得与避坑清单最后分享几条我个人很深的体会第一PHY调试最忌“经验主义”。AR8035那套在飞腾D2000上跑得很顺的配置直接搬到RK3568和YT8521上就是不行。不同PHY的默认delay策略不一样不同CPU平台的GMAC delay策略也不一样必须逐板实测。第二多准备几个phy-mode测试。我遇到CRC错误后会把四种rgmii模式都跑一遍然后看错误计数。这个实验成本很低但信息量极大能快速区分是谁的责任。第三不要轻信ethtool的link状态。协商成功不代表数据通路正常ethtool -S里的CRC、alignment error、rx_fifo_errors这些计数才是数据链路的真实体检结果。特别是“link up但没有流量”的时候一定要去翻这些计数。第四EtherCAT时间敏感网络场景下PHY的初始化不能只依赖自动协商。建议强制100M全双工并关闭EEE同时把PHY的所有状态中断、回环、Power Save功能关掉减少实时通信中的不确定因素。第五所有调试用的寄存器修改最终都要固化到代码里。用命令行敲寄存器只能验证想法线上设备一重启就会回到原点。我踩过好几次“调试好了忘了固化换一台设备又出问题”的坑后来直接在驱动初始化代码里加了一个板级PHY fixup函数统一处理YT8521和AR8035的delay、EEE、自协商配置。所以说从飞腾D2000到RK3568换的不只是SoC还有整套硬件调试思维方式。PHY寄存器调试这件事说白了就是跟信号时序和芯片默认行为打交道平台再变核心思路不变先物理通再寄存器通最后才谈性能优化。