2、PHY寄存器标准定义:IEEE 802.3标准寄存器详解

咱们做嵌入式底层开发的,跟PHY芯片打交道是家常便饭。PHY芯片本身就像一个小型处理器,它有一堆寄存器用来控制物理层的行为。这些寄存器到底长什么样?IEEE 802.3标准早就帮咱们定好了规矩。今天就聊聊这些标准化寄存器。

2.1 从IEEE 802.3标准说起

IEEE 802.3标准定义了PHY芯片必须实现的基本寄存器集合。这些寄存器从地址0x00到0x1F,一共32个。实际上,前16个(0x00到0x0F)是强制实现的,后面的是可选或者由厂商自定义的。

我个人习惯把PHY寄存器空间想象成一张地图:基本寄存器是主干道,厂商自定义寄存器就像是支路小巷。你想想看,一张完整的地图得先有主干道,对吧?

核心思想: 所有符合802.3标准的PHY,其前16个寄存器的行为和定义必须一致。这是咱们做驱动移植最大的底气。
经验之谈: 我记得第一次做网卡驱动的时候,遇到一个完全不认识的PHY芯片,说明书都找不到。后来我就是通过读取这些标准寄存器,把芯片的厂商、型号、能力都摸清楚了。这招叫“无文档调试法”(开个玩笑)。

2.2 基本控制寄存器(BCR)——地址0x00

BCR全称是Basic Control Register。这玩意儿说白了就是PHY的“开关面板”——想知道PHY怎么工作、要不要自协商、速度怎么设,都在这里配置。

它的结构是16位的,每一位都有明确含义。我挑几个重要的讲讲:

位 名称 功能说明
Bit 15 Reset 软件复位PHY,写1复位,复位后自动回0
Bit 14 Loopback 使能回环模式。1=开启,0=关闭
Bit 13 Speed Selection (LSB) 与Bit 6组合选择速度:00=10M, 01=100M, 10=1000M
Bit 12 Auto-Negotiation Enable 自协商使能。1=开启,0=强制模式
Bit 11 Power Down 低功耗模式。1=掉电,0=正常工作
Bit 10 Isolate 隔离PHY与数据链路层。1=隔离,0=正常
Bit 9 Restart Auto-Negotiation 重新开始自协商。写1触发,完成后自动回0
Bit 8 Duplex Mode 双工模式。1=全双工,0=半双工
Bit 6 Speed Selection (MSB) 与Bit 13组合,见上
Bits 5:0 Reserved 保留位,必须写0
避坑指南: 我曾经吃过一个亏:写BCR的Reset位之后,寄存器值会立即改变。你不能用“写完再读”的方式验证复位是否成功——因为复位完成后,寄存器内容已经回到默认值了。正确的做法是:写1,然后等待一定时间(一般100ms足够),再去读其他状态寄存器判断是否就绪。

举个实际配置例子: 假设你要配置一个PHY以100M全双工模式工作,并且开启自协商。你写BCR的配置是:0x1200(Bit 13=0, Bit 12=1, Bit 8=1, 其他保持0)。等等,这里有个细节——很多新手会误以为“自协商开启+强制速度”可以同时生效。其实自协商开启后,PHY会忽略速度设置位。这就是为什么我遇到不少人配置了BCR = 0x1200却还是协商到了1G。

// 读取BCR寄存器示例(通过MDIO总线)
uint16_t phy_read_bcr(struct phy_device *phydev) {
    return phy_read(phydev, MII_BMCR);  // MII_BMCR就是BCR的宏定义
}

// 配置100M全双工(强制模式)
void phy_force_100m_full(struct phy_device *phydev) {
    uint16_t bcr = phy_read_bcr(phydev);
    // 关闭自协商
    bcr &= ~BMCR_ANENABLE;
    // 设置速度=100M: Bit13=0, Bit6=1
    bcr &= ~BMCR_SPEED100;  // 清除Bit13
    bcr |= BMCR_SPEED1000;   // 设置Bit6=1(实际上这个宏名有点误导,它表示100M的MSB位)
    // 设置全双工
    bcr |= BMCR_FULLDPLX;
    phy_write(phydev, MII_BMCR, bcr);
}

2.3 基本状态寄存器(BMSR)——地址0x01

BMSR全称是Basic Mode Status Register。这个寄存器用来报告PHY的当前状态和链路能力。说白了就是“面试官”,告诉你PHY都支持什么、现在工作得怎么样。

BMSR各位概览:

位 名称 含义
Bit 15 Extended Capability 1=扩展寄存器存在(即地址0x0F及以后)
Bit 14 Jabber Detect 1=检测到Jabber错误
Bit 13 Link Status 1=链路已建立,0=链路断开
Bit 12 Auto-Negotiation Complete 1=自协商完成
Bit 11 Remote Fault 1=对端报告了错误
Bit 10 Auto-Negotiation Ability 1=支持自协商
Bit 9 Link Status (Latching Low) 锁存型链路状态
Bits 8:6 Reserved 保留
Bits 5:0 Capabilities 支持的能力位图(如10BASE-T、100BASE-TX等)
实用技巧: Bit 14的Jabber Detect和Bit 9的锁存链路状态,读完后会自动清零。所以如果你想持续监控这些状态,千万不要只读一次。我在做网络设备测试时,经常用定时器每隔100ms读一次BMSR,就是怕漏掉瞬态的状态变化。

BMSR最常用的场景就是轮询Link Status。 我习惯这么写:

// 检查链路是否建立(标准做法)
int phy_link_up(struct phy_device *phydev) {
    uint16_t bmsr;
    // 读一次清除锁存位
    phy_read(phydev, MII_BMSR);
    // 等50ms再读一次,确保捕获最新状态
    usleep_range(40000, 50000);
    bmsr = phy_read(phydev, MII_BMSR);
    return (bmsr & BMSR_LSTATUS) ? 1 : 0;
}

为什么要读两次?因为BMSR的Link Status实际上是反映“最后一次读之后的状态变化”。第一次读会清除锁存位,第二次读才是当前真实状态。这个坑很多人不知道,我曾经就因为这个在调试时消耗了半天时间。

2.4 PHY标识符寄存器(PHYIDSR)——地址0x02和0x03

PHYIDSR由两个寄存器组成:地址0x02存储厂商OUI的前16位,地址0x03存储OUI后6位+型号+版本。

结构如下:

  • PHYIDSR1(地址0x02):Bit 15:0 = OUI的高16位
  • PHYIDSR2(地址0x03):
    • Bit 15:10 = OUI的低6位
    • Bit 9:4 = 厂商定义的型号编号
    • Bit 3:0 = 版本号
为什么要知道这个? 当你拿到一个来路不明的开发板时,通过PHYIDSR就能确定PHY的厂商和型号。比如Broadcom的MAC地址前缀是0x02A1,Realtek是0x01D4……知道这些,就能去查对应的Datasheet。

读取能力代码:

struct phy_id_info {
    uint32_t oui;        // 24位OUI
    uint8_t model;       // 6位型号
    uint8_t revision;    // 4位版本
};

struct phy_id_info phy_read_id(struct phy_device *phydev) {
    struct phy_id_info id;
    uint16_t reg2 = phy_read(phydev, MII_PHYSID1);  // 地址0x02
    uint16_t reg3 = phy_read(phydev, MII_PHYSID2);  // 地址0x03
    
    id.oui = (reg2 << 6) | (reg3 >> 10);
    id.model = (reg3 >> 4) & 0x3F;
    id.revision = reg3 & 0x0F;
    
    return id;
}

2.5 自协商相关寄存器

除了BCR和BMSR,802.3还定义了自协商必须用到的几个寄存器:

  • ANAR(地址0x04):自协商通告能力,告诉对端“我能干哪些速度/双工模式”。
  • ANLPAR(地址0x05):自协商对端能力,读出对端通告的能力。
  • ANER(地址0x06):自协商扩展状态,用来诊断自协商失败的原因。

ANAR的Bits 5:0定义了能力位:

Bit 能力
5 100BASE-T4
6 100BASE-TX全双工
7 100BASE-TX半双工
8 10BASE-T全双工
9 10BASE-T半双工
特别注意: 自协商的工作方式是“双方互相通告,然后取交集”。如果你的PHY宣城了自己支持1000M,但对端只支持100M,最终协商结果就是100M。我曾经遇到过一块板子,它的PHY BC寄存器里写死了只通告10M能力,结果无论如何都协商不到100M——最后查出来是硬件设计上把ANAR寄存器写死了。嗯,这种坑靠软件是填不上的。

2.6 一张图看懂PHY寄存器层次

下面是我整理的PHY寄存器空间布局,重点标出了前16个标准寄存器的位置关系:

IEEE 802.3 PHY寄存器空间 基本寄存器 (地址 0x00 - 0x0F) 0x00 (BCR) 基本控制寄存器 0x01 (BMSR) 基本状态寄存器 0x02-0x03 PHY标识符 0x04 (ANAR) 自协商通告 0x05 (ANLPAR) 对端能力 0x06 (ANER) 扩展状态 扩展寄存器 (地址 0x10 - 0x1F) 厂商自定义:如EEE能力、中断配置、测试模式等 说明:前16个寄存器(0x00-0x0F)为802.3强制实现 扩展寄存器可选,厂商可自定义功能

这张图展示了寄存器空间的整体布局。可以看到,基本寄存器被分成控制类(绿色)、状态类(绿色)、标识类(紫色)、自协商类(橙色和红色)。每个区域的功能界限非常清晰。

2.7 实战建议

最后拿我多年调试PHY的经验,分享几个实用建议:

  • 初始化时一定要读PHYIDSR: 这是判断PHY型号的第一道防线。我曾经遇到过同一款板子,不同批次用了不同厂家的PHY,如果不做ID判断,驱动就会跑飞。
  • BCR的复位位要谨慎操作: 不要在中断上下文中复位PHY,否则可能导致内核挂起。我习惯用一个workqueue来异步复位。
  • 充分利用ANER诊断问题: 自协商失败时,ANER的Bits 3:0会给出原因(拥塞、冲突、翻页失败等)。很多工程师直接放弃自协商改为强制模式,其实读一下ANER就能找到根因。
一个小技巧: 如果你不确定一个PHY芯片是否已正确复位,可以反复读取BMSR的Link Status位(Bit 13)。如果复位后5秒内Link Status还是0,大概率是硬件问题,不是软件配置的事。我自己的调试流程就是:先看Link Status,再看ANER,最后再回头看BCR配置是否正确——这个顺序能省很多时间。