做硬件调试的人几乎都碰过这种局面一块搭载YT8531国产千兆PHY的板子第一次上电串口日志里PHY已经link up网线一插ping也通速度也正常可板上那两颗网口灯就是不给面子——要么一直亮着分不清网线有没有断要么干脆全灭。如果你第一反应是PHY坏了或者LED焊反了那大概率会白忙一场。YT8531的LED行为包括指示内容、闪烁频率、输出极性、驱动电流全部由软件通过MDIO/MDC管理接口配置。也就是说灯不亮不一定代表PHY没干活更可能是寄存器里某个位被写成了不该有的值。这篇记录会从YT8531的LED工作机制讲起把我在RK3568平台上实际调板时用到的工具、命令、排错顺序和踩过的坑一次性说清楚。正在调板的工程师可以直接对照步骤操作第一次接触PHY的入门读者也能按这个思路走通一次完整的硬件调试流程。1. YT8531的LED工作机制引脚、状态机与MDIO入口1.1 PHY内部的灯由什么决定YT8531是裕太微Motorcomm出品的单口千兆以太网PHY在瑞芯微、全志等平台的网口方案里非常常见。虽然只是一颗物理层芯片它内部可观察的状态一点都不少链路是up还是down、协商出来的是10M/100M/1000M、是全双工还是半双工、有没有数据在收发、有没有碰撞、是否进入EEE节能状态这些内部信号全部是LED指示器的候选素材。芯片内部有一个LED逻辑模块本质上就是一张内部信号到外部引脚的映射表而这张映射表的内容就是由寄存器字段决定的。你可以把PHY理解成一套集中控制的灯控系统墙上有一排拨码开关每个开关决定某一盏灯接哪一路传感器信号。YT8531一般引出两到三路LED输出具体看封装常见的就是LED_0、LED_1部分封装还带LED_2。出厂时厂商会烧一个默认值但很多开发板实际拿到的芯片默认值恰好把所有LED功能都关掉了或者把极性设成了和你的电路相反的方向。这也是为什么拿到一块新板子千万不要凭默认应该怎样去判断规矩就是上电之后先把相关寄存器读一遍再谈配置。1.2 数据通路与管理通路要分开理解很多刚开始接触以太网硬件的人会把MAC和PHY之间的接口混在一起理解这里必须先掰开。MAC和PHY之间有两条完全不同的通路数据走RGMII负责搬运收发帧和收发时钟管理走MDIO/MDC也叫SMI接口负责读写PHY内部的寄存器。LED配置就是通过MDIO这条管理通路完成的和RGMII上没有半毛钱关系。这两条通路在调试中绝对不能搞混。有次我看到同事拿着示波器去抓RGMII的TXD/RXD波形想找灯为什么不亮的原因折腾半天当然什么都找不到。数据通路上跑的是以太网报文管理通路上跑的是寄存器访问命令LED状态由后者决定。MDIO协议本身不复杂MDC提供时钟MDIO线上一根数据线按帧格式传输包含前导码、操作码、PHY地址、寄存器地址、数据和应答位。标准Clause 22只支持5位PHY地址和5位寄存器地址也就是说正常可见范围就是0x00到0x1F这32个寄存器。YT8531这类功能比较多的PHY会把LED、测试模式等配置放到扩展页里这个后面第3节专门讲。1.3 strap引脚、复用关系和第一个默认值PHY芯片上的引脚很少是纯单功能的YT8531也不例外。很多引脚在上电瞬间会被芯片当作配置采样脚这类脚叫strap引脚外部上下拉电阻决定芯片启动时的模式比如PHY地址、内部延时、自协商开关等。采样完成后一部分引脚会复用作LED、中断或时钟输出。这个机制给LED调试埋了一个很隐蔽的坑如果原理图上把某个LED引脚同时挂了一组上下拉电阻当芯片把它切回LED功能时外部电阻可能仍然在钳位引脚电平导致你配置对了也亮不对。更常见的情况是LED引脚本身承担了strap功能芯片默认就把该脚当配置输入而非输出使用需要先在寄存器里把复用模式切到LED输出。所以动手写寄存器之前一定要把原理图上每一个LED相关引脚的上下拉、串联电阻、以及接到VDD还是GND这件事摸清楚。我在调第一块YT8531板子时就吃过原理图上LED脚被下拉到地量出来一直是低电平误以为PHY坏了的亏。2. 调试前的三板斧读寄存器、对状态、量引脚2.1 用mdio/phytool把PHY寄存器读出来Linux环境下读PHY寄存器最顺手的就是mdio-tools和phytool这两个小工具。mdio-tools装好之后基本用法是sudo mdio read eth0 0x00 0x11这条命令的含义是通过eth0对应的MDIO总线访问PHY地址0x00的寄存器0x11。写寄存器就是再加上要写的值sudo mdio write eth0 0x00 0x11 0x4000不同发行版对mdio命令参数顺序可能有细微差别第一次用先跑个mdio --help确认。phytool则是另一种路径式写法比如phytool read eth0/0/0x11效果一样看哪个顺手用哪个。如果目标系统连包都装不了也可以直接用经典的ioctl方法读写PHY寄存器下面这段代码是从mii-tool里继承下来的套路在绝大多数Linux平台上都能跑#include stdio.h #include string.h #include fcntl.h #include sys/ioctl.h #include net/if.h #include linux/mii.h #include linux/sockios.h static int phy_read(int fd, struct ifreq *ifr, int phy_id, int reg) { struct mii_ioctl_data *mii (struct mii_ioctl_data *)ifr-ifr_data; mii-phy_id phy_id; mii-reg_num reg; if (ioctl(fd, SIOCGMIIREG, ifr) 0) return -1; return mii-val_out; }这种通过socket ioctl读写的方式不依赖额外工具缺点是代码在不同架构上的兼容性不完全一样编译前先跑个小程序验证读写正常再集成进脚本。动手之前还要确认一件事MDIO总线上挂的PHY到底在哪个地址。执行ls /sys/bus/mdio_bus/devices/一般能看到类似stmmac-0:00或者stmmac-0:04这样的设备冒号后面的数字就是PHY地址。这个地址由原理图上的strap电阻决定写命令前一定要对上不然读回来的全是FF浪费时间。2.2 ethtool是这个环节里最可靠的判断基线在碰LED寄存器之前先让系统告诉你PHY的真实状态。ethtool eth0会输出当前连接状态、协商速度、双工模式这一步能帮你把问题范围缩小一半。比如输出显示Speed: 1000Mb/s、Duplex: Full、Link detected: yes说明物理链路是好的PHY也在正常工作那灯不亮就纯粹是指示逻辑的问题可以放心去查寄存器如果显示的是Link detected: no那灯不亮是正常行为应该先去查网线、变压器、PHY供电和MAC侧配置而不是赖LED。对应的内核串口日志也会在网卡probe时打印PHY的绑定信息[ 1.557232] eth0: PHY [stmmac-0:00] driver [YT8531 Gigabit Ethernet PHY]看到driver字段说明内核已经识别并绑定了motorcomm的PHY驱动这一步能和ethtool互相印证。按我现在的习惯第一步永远是串口日志确认PHY probe再ethtool确认链路状态最后才决定要不要查LED寄存器这三步顺序不能乱。2.3 万用表测引脚电平先分清是没输出还是方向反了寄存器折腾完还不亮就该上仪表了。LED属于慢信号万用表就够。先搞清楚你板子上的LED是哪一种接法高电平点亮还是低电平点亮。低电平点亮的典型电路是LED阳极经限流电阻接VDD阴极接PHY引脚PHY引脚输出低电平时电流流过LED高电平点亮则相反LED阴极接地阳极由PHY引脚经限流电阻驱动。用万用表直流档量PHY引脚的对地电压如果低电平点亮的电路里引脚一直是3.3V高电平说明PHY压根没把引脚拉低问题在PHY侧如果引脚电平已经在0V附近而LED还是不亮那问题就在限流电阻、LED本体或者LED接反了。还有一个容易漏掉的细节量引脚和量LED两端要分开看PHY引脚正常不代表LED回路正常中间还有一颗限流电阻等着你查。3. 定位LED控制寄存器扩展页访问与字段拆解3.1 扩展页切换别背地址但要背方法前面说过Clause 22只留了0x00到0x1F共32个寄存器地址厂商要放的东西远不止这些所以YT8531这类芯片普遍采用扩展页机制先在一个页选择寄存器里写入目标页号之后访问的某个寄存器地址就不再是默认页内容而是扩展页里对应的寄存器。访问完还要把页寄存器恢复默认否则后续所有寄存器访问都会漂移。以我手头某批次YT8531为例操作顺序长这样sudo mdio write eth0 0x00 0x1F 0x000A # 切到扩展页示例页号以手册为准 sudo mdio read eth0 0x00 0x11 # 读扩展页里的LED配置寄存器 sudo mdio write eth0 0x00 0x11 0x4000 # 改配置 sudo mdio write eth0 0x00 0x1F 0x0000 # 恢复默认页这里必须强调一句0x1F这个页选择寄存器和0x11这个LED寄存器地址是我手头这批料的实际值只当作示例。YT8531有多个子版本不同版本手册里LED相关寄存器在扩展页的偏移可能不一样字段宽度也可能不同。正确的做法是拿到自己板子对应的那版datasheet去寄存器表里搜索LED Control、LED Mode、LED Select几个关键词找到之后对着位定义表确认。调试最忌讳的就是拿别人的地址生搬硬套最后读回来一个假值还以为芯片有问题。3.2 LED控制字段的作用和各工作模式的选型YT8531的LED控制寄存器通常包含以下几类字段具体位数和偏移以手册为准这里给的是我这份datasheet里的典型布局字段示例控制内容典型取值LED0模式选择LED0指示哪种内部状态00link01link/activity10速率11双工LED1模式选择LED1指示哪种内部状态同上极性控制高电平点亮还是低电平点亮0低电平点亮1高电平点亮闪烁使能活动指示时是否闪烁0常亮1活动时闪烁驱动电流档输出级能拉或灌多大电流000最小111最大不同模式对应不同应用场景。路由器、开发板这种设备最常用的是link/activity复合模式链路建立后灯常亮有数据收发时闪烁一眼就能看出网通没通、有没有流量。工业现场如果希望区分速率就选速率模式让不同速率对应不同指示方式。还有一种link-loss闪烁模式链路丢失时灯会以固定频率闪烁或者保持一段时间方便人眼捕捉瞬断很多PHY把这个功能做成latch。要不要开取决于你的产品对链路瞬断可感知有没有要求。3.3 read-modify-write别把整个寄存器抹掉刚开始调LED的人最容易犯的错是看到手册里推荐值0x8642就直接mdio write把整个寄存器写成0x8642。这样写非常危险因为寄存器里除了LED字段往往还混着厂商保留位、时钟极性、压摆率之类的配置你一次性覆盖写入等于把其它功能也一起改了。正确姿势永远是read-modify-write先读回原值用位运算把目标字段清掉再把希望设置的值写进去。下面是一段在shell里做read-modify-write的示例假设要把LED0改成link/activity模式对应示例字段值01#!/bin/bash # 示例某批次YT8531页选择寄存器0x1FLED配置寄存器0x11 sudo mdio write eth0 0x00 0x1F 0x000A val$(sudo mdio read eth0 0x00 0x11) val${val#0x} # 去掉工具可能输出的0x前缀 val$((16#$val)) # 强制按16进制解析避免前导0被当成八进制 echo origin: $(printf 0x%04x $val) # 只改LED0模式字段示例中是bit15:14其它位保持原样 val$(( (val ~0xC000) | 0x4000 )) sudo mdio write eth0 0x00 0x11 $val val$(sudo mdio read eth0 0x00 0x11) val${val#0x} val$((16#$val)) echo after : $(printf 0x%04x $val) sudo mdio write eth0 0x00 0x1F 0x0000shell的算术扩展默认按整数处理MDIO的数据位宽是16位写值超过0xFFFF会出问题。上面val${val#0x}和16#$val这两行看着啰嗦但能避免一个真实存在的坑如果mdio工具输出的是0040这种前导零字符串直接扔进bash算术里会被当成八进制解析40就变成32配置自然不对。这种细节平常没人提线上真有人因为这个问题折腾了整整一天。4. 实战排障记录从双灯全灭到两灯正常的完整链路4.1 现象确认与第一轮硬件测量拿一个真实的案例来说。我手上一块RK3568B的核心板板上集成两颗YT8531做双千兆网口。系统起来后eth0插网线ethtool显示link up、1000Mb/s full duplexping网关也正常但板上两个网口LED一个都不亮。当时第一反应是查原理图LED用的是低电平点亮接法PHY引脚经1k限流电阻接LED阴极LED阳极接3.3V。测量发现PHY引脚一直是3.3V高电平而正常点亮时应该是接近0V。限流电阻和LED本体都量过没开路没短路焊接也正常。到这里基本可以确定芯片活着外部电路没坏问题出在芯片没按预期把引脚拉低。再结合ethtool结果链路状态是好的所以我判断不是PHY没干活而是LED相关的寄存器配置不对。4.2 寄存器读数把矛头指向软复位清配置接着用mdio命令切到扩展页读LED配置寄存器读回来一个0x0000LED模式字段全零等于所有LED功能被关掉了。这个结果本身不意外真正让我停下来想的是另一个问题是谁把它写成0的翻启动流程发现U-Boot阶段网卡驱动里其实配置过LED灯在boot阶段是正常的。但Linux内核的GMAC驱动和PHY驱动在probe初始化时会执行一次PHY软复位也就是BMCR寄存器bit15置1。软复位之后PHY所有寄存器都会恢复到上电默认值而这一刻那颗YT8531的上电默认值恰好是所有LED功能关闭。于是现象就成了boot阶段灯亮、内核启动后灯灭中间差一次谁都没注意的软复位。这条链路如果不把软复位这个环节想清楚很容易陷入反复改驱动却无效的死循环。这也是我想强调的排查思路遇到寄存器值不符合预期不要只盯着当前值是什么还要问谁在什么时间把值改过。复位、休眠唤醒、EEE切换任何一步都可能是元凶。4.3 修复把LED配置放进PHY驱动的初始化流程既然根因是软复位把配置清掉了修复方案就清楚了把LED配置放到每次软复位之后都会被执行的位置。在Linux的phy驱动框架里最合适的位置就是PHY驱动的config_init回调phylib在PHY初始化、软复位之后会调用它时机刚好。下面是我在motorcomm驱动里补的一段代码思路是先切扩展页、改LED字段、最后强制恢复默认页static int yt8531_config_init(struct phy_device *phydev) { int val; /* 切到扩展页示例页号以实际手册为准 */ phy_write(phydev, 0x1F, 0x000A); /* 读取LED配置寄存器修改LED0为link/activity低电平点亮 */ val phy_read(phydev, 0x11); val ~0xC000; val | 0x4000; val ~0x0040; phy_write(phydev, 0x11, val); /* 关键恢复默认页否则后续所有寄存器访问都会漂移 */ phy_write(phydev, 0x1F, 0x0000); return 0; }这里有两件事必须说透。第一件代码末尾恢复默认页那一步不是可选项。我之前在一个项目里偷懒没恢复结果系统起来后ethtool、PHY probe日志里的寄存器值全都不对位置全部漂到了扩展页对应的偏移上整整排查了两天才意识到是页没切回来。第二件如果只是想快速验证配置效果不必急着改驱动编译烧写完全可以先用mdio命令在系统起来后在线写值灯亮对了再固化进驱动。命令行验证和驱动固化是两个阶段分开做能省很多时间。4.4 验证拔插、协商、流量三关都过了才算完改完驱动、重新编译烧写验证不能只看插网线灯亮了就收工。我一般按三个关卡测第一关拔线灯灭、插线灯亮重复十几次确认没有偶发不亮第二关把对端从千兆交换机换成百兆设备确认协商速率变化后LED指示正确如果配的是纯link模式这一关可以简化第三关跑一轮iperf打流量确认activity指示灯闪烁频率符合预期不是那种亮瞎眼的常亮。我当时测完还顺手抓了一次串口日志确认PHY probe之后没有再次发生软复位LED配置一路保持到系统完全起完。这个配置是否被保持的验证其实比灯亮不亮本身更能暴露问题。5. 踩过这些坑之后我整理了一份LED调试检查表5.1 头号坑任何形式的PHY复位都会吞掉LED配置LED配置丢在软复位上这是最典型的坑但绝不是唯一的。硬件复位脚被看门狗拉起一下、系统suspend/resume、PHY的EEE唤醒流程、甚至某些MAC驱动在link状态变化时重新初始化PHY都可能导致寄存器回到默认值。如果LED配置只在用户态脚本里启动后写一次那么只要这些事件发生一次配置就丢了而且丢得很隐蔽。最省心的做法是把LED配置放在每次PHY初始化的必经路径里比如config_init回调或者放在通用PHY驱动里每次resume之后都会钩到的位置。这样无论复位来自哪个环节重新初始化后配置都会自动补上。量产阶段尤其重要因为你不可能保证每台设备的使用者都会去执行那个用户态脚本。5.2 极性、驱动电流与限流电阻的配合LED不亮的另一个高频原因是极性和电路不匹配。低电平点亮的电路如果寄存器配成了高电平点亮结果就是该亮时引脚是高电平、LED两端没有压差灯自然不亮看起来像没输出实际是方向反了。量引脚对地电平很容易判断低电平点亮电路里正常工作时引脚应该在0V附近而不是3.3V。驱动电流档位也值得检查。YT8531的输出驱动能力是可以编程的如果手册默认档位很小而板上的LED又是高亮型或串联了比较大的限流电阻亮光会微弱到白天完全看不见。把电流档调大之前记得看手册里输出级的最大拉灌电流不要为了亮把芯片推过规格。限流电阻的取值也会影响最终亮度同样一颗LED1k和2k的效果差很多这个要靠实测确定别只看原理图。5.3 最终检查表走一遍基本不会翻车把这么多年调PHY灯踩过的坑整理成一张表每次换平台、换颗料都能用步骤检查项说明1看原理图确认LED接法高低电平、复用/strap、限流电阻2找到对应datasheet从寄存器表定位页选择寄存器和LED控制寄存器3先读再改read-modify-write每次只改一个字段改完立即回读4对状态基线ethtool确认link、速率、双工避免白查5过复位验证确认配置在软复位或唤醒后依然有效6固化到驱动写进PHY驱动config_init或等价必经路径7全流程测试拔插、速率协商、流量吞吐、suspend/resume这张表看着不起眼但每次按照它走都能把灯的问题控制在半小时内定位完。最后说个我个人的习惯拿到一颗不熟悉的PHY先别急着翻驱动代码花半小时把手册里的寄存器表从头到尾过一遍把页选择、LED、中断相关的寄存器单独记在小本子上标注清楚哪一页、哪个偏移、复位默认值是多少。每次写寄存器之前先读原值改完马上回读确认这个动作看起来慢实际上能省掉至少半天的推测时间。YT8531这颗料性价比不错配套资料这两年也在逐步完善LED这一块一旦理清楚后面做产测脚本、远程指示灯诊断都会顺手很多。希望这篇记录能让你少走我走过的那段弯路。