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寄存器空间想象成一张地图:基本寄存器是主干道,厂商自定义寄存器就像是支路小巷。你想想看,一张完整的地图得先有主干道,对吧?
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 |
举个实际配置例子: 假设你要配置一个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等) |
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 = 版本号
读取能力代码:
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半双工 |
2.6 一张图看懂PHY寄存器层次
下面是我整理的PHY寄存器空间布局,重点标出了前16个标准寄存器的位置关系:
这张图展示了寄存器空间的整体布局。可以看到,基本寄存器被分成控制类(绿色)、状态类(绿色)、标识类(紫色)、自协商类(橙色和红色)。每个区域的功能界限非常清晰。
2.7 实战建议
最后拿我多年调试PHY的经验,分享几个实用建议:
- 初始化时一定要读PHYIDSR: 这是判断PHY型号的第一道防线。我曾经遇到过同一款板子,不同批次用了不同厂家的PHY,如果不做ID判断,驱动就会跑飞。
- BCR的复位位要谨慎操作: 不要在中断上下文中复位PHY,否则可能导致内核挂起。我习惯用一个workqueue来异步复位。
- 充分利用ANER诊断问题: 自协商失败时,ANER的Bits 3:0会给出原因(拥塞、冲突、翻页失败等)。很多工程师直接放弃自协商改为强制模式,其实读一下ANER就能找到根因。