第2章 PHY寄存器体系:地址空间与分类

搞PHY驱动,第一件事就是摸清它的寄存器地图。

说白了,PHY芯片内部就是一排排寄存器,你通过MDIO/MDC这根串行总线去读写它们。但寄存器不是乱放的,IEEE 802.3规定了一套标准布局,厂家又在上面加了自己的私货。

我个人习惯先把地址空间画成三个区:0~15是必修课,16~31是选修课,再往后就是厂商的“黑盒”。

2.1 地址空间总览

每个PHY设备用5位地址线(ADDR[4:0]),最多32个寄存器地址(0x00~0x1F)。但这32个地址并非都归你自由使用:

  • 0x00~0x0F(0~15):MII标准寄存器。所有符合IEEE 802.3的PHY都必须实现,内容统一,读出来直接解析就是状态。
  • 0x10~0x1F(16~31):扩展寄存器。部分标准功能(如千兆协商、EEE)放在这里,但不是强制。
  • >0x1F(地址超过31):厂商自定义寄存器。一般通过“页”(Page)或“子地址”方式访问,每个厂家都不一样。
核心要点:你在调试PHY时,第一步永远是读寄存器0x01(状态寄存器),看Link是否起来、速度是否正确。如果读0x01返回全是0,大概率MDIO时序或PHY地址配错了。
PHY寄存器地址空间(0x00~0x1F) MII标准寄存器 0x00~0x0F (0~15) IEEE 802.3强制 控制/状态/速率/双工 PHY ID、自协商 寄存器0x00~0x0F 扩展寄存器 0x10~0x1F (16~31) IEEE 802.3建议 千兆参数、EEE 测试模式、中断 寄存器0x10~0x1F 厂商自定义 Page / 子地址 厂家私有的“黑盒” Marvell、Broadcom、Realtek 每个厂家寄存器完全不同 超过0x1F 注意:厂商自定义寄存器通常需要通过写页面控制寄存器切换“页”,否则读不到

2.2 MII标准寄存器(0x00~0x0F)

这部分是每个PHY工程师的必修课。你想想看,无论你用的PHY是哪家产的,0x00控制寄存器、0x01状态寄存器、0x02/0x03 PHY ID、0x04自协商能力……这些读出来的值,格式完全一致。

地址名称关键位/作用
0x00控制寄存器 (BMCR)位15: Reset, 位13: Speed(1000), 位12: 双工, 位8: 自协商使能
0x01状态寄存器 (BMSR)位2: Link状态, 位5: 自协商完成, 位6: 远端故障
0x02~0x03PHY ID 寄存器OUI + 型号 + 版本,识别芯片的唯一凭据
0x04自协商能力 (ANAR)速率/双工能力通告,用于与对端协商
0x05自协商伙伴能力 (ANLPAR)对端发过来的能力,看它支持什么
0x06自协商扩展 (ANER)LP自协商完成、LP页能力等
0x0C~0x0DMDI/MDI-X 状态/控制自动翻转状态,有的PHY用自定义寄存器代替
我的小经验:调试PHY时,先读0x02和0x03确定你找对了芯片。如果读出来全是0xFFFF或0x0000,MDIO总线上没设备,或者地址不对。我曾经花了一下午才发现PHY地址跳线焊错了,这种低级错误千万别犯。

2.3 扩展寄存器(0x10~0x1F)

这些寄存器不是必实现的,但大部分千兆PHY都会用到。比如0x10(千兆控制寄存器)用来指示1000Base-T的Master/Slave配置,0x11(千兆状态寄存器)告诉你本地和远端是否支持1000M。

嗯,这里要特别注意:有些PHY把中断使能、错误计数器等也映射在16~31区间,但不同厂家安排的位置不一样。比如我的一个项目用Broadcom BCM54616S,它的中断寄存器在0x1C,但换成Marvell 88E1512就在0x1B了。所以不能死记硬背,要勤查datasheet。

避坑指南:如果你用的PHY支持EEE(节能以太网),相关寄存器通常在0x14~0x17附近。我遇到过客户产品在低负载时断网,排查很久发现是EEE的沉睡计时器没配好,导致链路进入了不可恢复的休眠状态。扩展寄存器的默认值往往不是最优的,一定要根据实际场景调整。

2.4 厂商自定义寄存器

这部分是最让人头大的。每个厂家都有一堆私有的控制/状态/调试寄存器,通过“页”(Page)机制扩展。比如Marvell PHY的0x1E是“页控制寄存器”,你要先往0x1E写页码,然后才能读写其他地址。

  • Marvell:使用0x1E作为页选择,0x1D作为数据端口,部分型号还有0x1F做额外扩展。
  • Broadcom:常用0x1C、0x1D、0x1E组合,甚至用“影子寄存器”(Shadow Registers)方式,需要连续写特定幻数才能解锁。
  • Realtek:有些型号没有页机制,所有功能都挤在0~31里,但地址复用,需要先配置0x1F的“选择位”来切换功能。

说白了,厂商自定义寄存器就是厂家留给自己做魔改的地方。比如LED指示灯控制、信号检测、环回测试、功耗管理……你产品上每个LED亮灭逻辑,往往都在这些寄存器里配。

我个人习惯:拿到一个新PHY,先读0x02~0x03确定型号,然后照着datasheet的“寄存器映射表”把所有非标准地址全部人工扫描一遍。写个脚本循环读,看哪些地址返回非0xFF非0x00,这些很可能就是可配置的扩展寄存器。这比我以前傻傻对着手册逐行读快多了。

最后提一句:PHY寄存器体系虽然分层,但实际项目中标准区和扩展区经常混用。比如某些Realtek芯片的0x0A(MII标准区)被用来放自定义温度传感器读数,你如果严格按照手册读状态,可能会被坑。记住一个原则:datasheet比你经验更可靠,但经验能帮你少踩坑。