Zynq平台下88E1512 PHY的RGMII to SGMII模式驱动配置详解
1. 认识88E1512 PHY与Zynq平台的组合在嵌入式网络设备开发中Marvell的88E1512 PHY芯片算是老熟人了。这颗芯片最大的特点就是灵活——支持RGMII、SGMII等多种接口模式还能通过寄存器配置实现不同模式之间的转换。我最早接触这颗PHY是在一个工业网关项目上当时需要将Zynq处理器的RGMII接口通过88E1512转换成SGMII再连接到交换芯片BCM5396。这种组合在需要扩展多端口的场景特别常见比如交换机、路由器和多网口嵌入式设备。88E1512属于Marvell Alaska系列PHY和88E1510、88E1518算是同门兄弟驱动代码都放在Linux内核的marvell.c文件中。不过在实际使用中发现虽然驱动框架相同但每颗芯片的寄存器配置细节还是有差异的。特别是当我们需要让PHY工作在RGMII转SGMII的混合模式时手册上那些寄存器配置说明看得人眼花缭乱我第一次配置时就因为漏掉了Page切换的步骤导致链路状态死活不正常。2. 硬件连接与设备树配置先来看看硬件连接示意图。在我们的场景中Zynq的GEM0控制器通过RGMII接口连接88E1512PHY的SGMII侧则连接到BCM5396交换芯片。这种拓扑在需要扩展网口的设备中很典型比如一个Zynq芯片本来只有两个RGMII接口通过88E1512BCM5396的组合可以轻松扩展到8个甚至更多端口。设备树配置是第一步也是最容易出错的地方之一。很多新手会直接照搬其他PHY的配置结果发现链路起不来。对于88E1512的RGMII模式正确的配置应该是这样的gem0 { status okay; phy-mode rgmii-id; phy-handle ethernet_phy0; ethernet_phy0: ethernet-phy0 { reg 0; device_type ethernet-phy; }; };这里有几个关键点需要注意phy-mode必须明确指定为rgmii-id表示RGMII接口需要PHY提供内部延迟reg属性对应PHY的MDIO地址如果板子上有多个PHY地址不能冲突虽然我们最终要使用SGMII连接交换芯片但设备树配置的还是RGMII模式因为这是PHY与Zynq之间的接口类型我曾经遇到过一个问题设备树配置看起来没问题但链路就是不稳定。后来发现是PCB布线时RGMII的时钟线长度不匹配导致时序违例。所以硬件设计阶段就要注意RGMII的TX_CLK和RX_CLK走线要等长长度差最好控制在50mil以内。3. 寄存器配置详解88E1512的寄存器配置是重头戏也是问题最多的环节。这个PHY的寄存器组织比较特别采用了分页机制——要先通过Register 22选择页面才能访问目标寄存器。这就好比你要找一本书得先知道它在哪个书架Page然后才能根据编号找到具体位置。3.1 工作模式设置根据手册Page 18的Register 20我们需要配置MODE[2:0]字段为0x4表示RGMII(System)转SGMII(Media)模式。对应的驱动代码片段如下phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 18); m88e1518_phy_writebits(phydev, MIIM_88E151x_GENERAL_CTRL, 0, 3, MII_88E151x_MODE_RGMII_TO_SGMII);这里有个坑要注意修改工作模式后必须执行软复位否则配置不会生效。复位操作是通过设置Register 20的bit 15来完成的m88e1518_phy_writebits(phydev, MIIM_88E151x_GENERAL_CTRL, MIIM_88E151x_RESET_OFFS, 1, 1);3.2 延迟调整RGMII接口的时序很关键88E1512提供了灵活的延迟调整功能。在Page 2的Register 21可以配置TX/RX延迟phy_write(phydev, MDIO_DEVAD_NONE, MII_MARVELL_PHY_PAGE, 2); reg phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E151x_PHY_MSCR); reg ~MIIM_88E151x_RGMII_RXTX_DELAY; if (phydev-interface PHY_INTERFACE_MODE_RGMII_ID) reg | MIIM_88E151x_RGMII_RXTX_DELAY; phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E151x_PHY_MSCR, reg);延迟值需要根据实际硬件调整。我的一般做法是先设置为默认值测试如果发现丢包或CRC错误逐步调整延迟值用示波器观察RGMII时序确保建立/保持时间满足要求3.3 EEE功能配置88E1512支持EEE(Energy Efficient Ethernet)功能但在某些交换芯片组合下可能会引起问题。建议先禁用EEE等基本功能稳定后再考虑开启。配置EEE需要操作多个寄存器phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x00ff); phy_write(phydev, MDIO_DEVAD_NONE, 17, 0x214B); phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x2144); ... phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0000);这段代码看起来有点复杂其实是按照Marvell的Errata文档要求写的。我曾经偷懒想跳过这部分结果发现PHY会随机出现链接断开的情况所以还是老老实实按手册配置比较稳妥。4. 关键问题排查在实际项目中88E1512的配置最容易遇到两个问题链路无法建立和链路速度不稳定。下面分享几个排查经验。4.1 链路状态异常如果发现PHY指示灯不亮或者一直闪烁首先检查Page切换是否正确所有寄存器配置完成后必须将Page切回到1(Fiber Page)否则MAC获取的是Copper Page的状态强制链路设置在SGMII模式下建议关闭自协商强制设置为千兆全双工phy_write(phydev, MDIO_DEVAD_NONE, MII_MARVELL_PHY_PAGE, MII_MARVELL_FIBER_PAGE); phydev-autoneg AUTONEG_DISABLE; phydev-speed SPEED_1000; phydev-duplex DUPLEX_FULL; phy_reset(phydev);4.2 速度协商问题有时候链路能起来但速度会卡在100M甚至10M。这种情况多半是SGMII协商参数没配好交换芯片侧的配置不匹配PCB走线质量问题导致信号完整性差建议的排查步骤用示波器检查SGMII信号质量确认BCM5396的相应端口也配置为SGMII模式在88E1512端关闭自协商强制千兆模式4.3 驱动加载问题有时候配置都正确但驱动就是不生效。这时候要检查设备树中的compatible字段是否正确PHY ID是否能正确识别驱动probe函数是否被调用可以在内核启动参数中添加debug和dyndbgfile drivers/net/phy/marvell.c p查看PHY初始化的详细日志。5. 实战调试技巧调试PHY问题最痛苦的就是现象不明显可能改了一行代码要等半天才能看到效果。下面分享几个提高效率的方法。5.1 使用mdio-toolmdio-tool是个超好用的调试工具可以直接读写PHY寄存器不用每次都重新编译驱动。安装和使用方法git clone https://github.com/wkz/mdio-tool make ./mdio-tool eth0 0x0 read 0x16 # 读取寄存器0x16的值 ./mdio-tool eth0 0x0 write 0x16 0x1234 # 写入寄存器5.2 信号质量测量RGMII和SGMII对信号质量要求很高建议用示波器检查时钟频率是否准确数据信号眼图是否清晰时钟与数据的相位关系如果发现信号质量差可以尝试调整PCB端接电阻减小走线长度启用PHY内部的预加重功能5.3 内核调试支持启用以下内核配置选项可以获得更多调试信息CONFIG_DEBUG_FSy CONFIG_MDIO_DEBUGy CONFIG_NETWORK_PHY_TIMESTAMPINGy挂载debugfs后可以查看详细的PHY状态mount -t debugfs none /sys/kernel/debug cat /sys/kernel/debug/mdio_bus/*/state6. 性能优化建议当基本功能调通后可以考虑做一些性能优化。以下是几个实测有效的方案。6.1 中断优化默认情况下PHY使用轮询方式检测链路状态可以改为中断模式降低CPU占用ethernet_phy0: ethernet-phy0 { reg 0; interrupts-extended gpio0 12 IRQ_TYPE_LEVEL_LOW; };6.2 DMA配置对于高流量场景调整Zynq的DMA缓冲区大小可以提高吞吐量gem0 { rx-fifo-depth 4096; tx-fifo-depth 4096; };6.3 节能配置如果设备对功耗敏感可以启用EEE功能但要注意兼容性问题phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x00ff); phy_write(phydev, MDIO_DEVAD_NONE, 17, 0x214B); phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x2144);7. 常见问题FAQ在实际项目中有些问题会反复出现。这里整理了几个高频问题的解决方案。Q1为什么修改了寄存器配置但没生效A88E1512配置后需要执行软复位或者等待自动复位完成。建议在关键配置后添加udelay(100)确保复位完成。Q2如何确认PHY当前的工作模式A可以通过读取Register 20的值来确认./mdio-tool eth0 0x0 read 0x14返回值的低3位就是当前模式。Q3SGMII链路不稳定怎么办A首先检查信号质量其次确认两端配置一致。可以尝试关闭自协商强制设置速率和双工模式调整SGMII的均衡器设置Q4如何测量实际吞吐量A推荐使用iperf3工具# 服务端 iperf3 -s # 客户端 iperf3 -c 服务器IP -t 60 -i 108. 进阶话题对于想要深入优化的开发者这里还有几个值得研究的进阶方向。8.1 硬件时间戳88E1512支持IEEE 1588硬件时间戳适合工业自动化等对时间同步要求高的场景。需要在驱动中启用相关功能phydev-ptp_clock_info.n_ext_ts 2; phydev-ptp_clock_info.n_per_out 2; phydev-ptp_clock_info.pps 1;8.2 自定义LED行为通过修改Register 24可以自定义LED的闪烁模式比如用不同颜色表示不同速率phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 3); phy_write(phydev, MDIO_DEVAD_NONE, 24, 0x1234);8.3 温度监控88E1512内置温度传感器可以通过Register 26读取芯片温度phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0006); temp phy_read(phydev, MDIO_DEVAD_NONE, 26);在最近的一个项目中我们通过监控PHY温度成功预防了一次过热故障。当时发现某台设备的PHY温度比其他设备高10°C左右检查后发现是散热垫片没贴好及时处理避免了批量返修。