在嵌入式Linux开发中,网络不通是最让人头疼的问题之一。最近我在调试RK3568平台的千兆以太网时,就遇到了PHY无法识别的状况。经过一番折腾才发现,问题出在MDIO总线的配置上。今天我们就来深入探讨MDIO总线在RK3568上的完整驱动流程,从设备树配置到PHY注册的全过程。
MDIO(Management Data Input/Output)总线是IEEE 802.3标准定义的两线制串行接口,专门用于MAC控制器与PHY芯片之间的通信。它就像是一条电话线,让MAC能够"打电话"询问PHY的状态信息。在RK3568平台上,MDIO总线通常集成在GMAC控制器内部,通过22号引脚(MDC)和23号引脚(MDIO)与外部PHY连接。
PHY芯片是网络通信的"翻译官",负责将MAC的数字信号转换成适合在网线上传输的模拟信号。常见的PHY芯片如RTL8211F、YT8531等,都需要通过MDIO总线来配置工作模式、读取链路状态等。当MDIO总线出现问题时,往往会导致PHY无法正常工作,表现为ifconfig看不到网卡或者网络连接时断时续。
RK3568的设备树配置是驱动加载的起点。我们先来看一个典型的GMAC控制器配置:
dts复制gmac0: ethernet@fe2a0000 {
compatible = "rockchip,rk3568-gmac", "snps,dwmac-4.20a";
reg = <0x0 0xfe2a0000 0x0 0x10000>;
interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "macirq", "eth_wake_irq";
clocks = <&cru SCLK_GMAC0>, <&cru SCLK_GMAC0_RX_TX>;
clock-names = "stmmaceth", "mac_clk_rx";
resets = <&cru SRST_A_GMAC0>;
reset-names = "stmmaceth";
snps,axi-config = <&gmac0_stmmac_axi_setup>;
status = "disabled";
mdio0: mdio {
compatible = "snps,dwmac-mdio";
#address-cells = <1>;
#size-cells = <0>;
};
};
这里有几个关键点需要注意:
compatible属性必须包含"rockchip,rk3568-gmac",这是驱动匹配的关键mdio0子节点定义了MDIO总线控制器status默认为disabled,需要在板级设备树中启用在板级设备树中,我们需要启用GMAC并配置PHY:
dts复制&gmac0 {
phy-mode = "rgmii";
clock_in_out = "output";
snps,reset-gpio = <&gpio2 RK_PC5 GPIO_ACTIVE_LOW>;
snps,reset-active-low;
snps,reset-delays-us = <0 30000 150000>;
assigned-clocks = <&cru SCLK_GMAC0_RX_TX>;
assigned-clock-parents = <&cru SCLK_GMAC0_RGMII_SPEED>;
pinctrl-names = "default";
pinctrl-0 = <&gmac0_miim &gmac0_rgmii_bus>;
tx_delay = <0x25>;
rx_delay = <0x15>;
phy-handle = <&rgmii_phy0>;
status = "okay";
};
&mdio0 {
rgmii_phy0: phy@2 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <0x2>;
};
};
这里有几个容易出错的地方:
phy-mode必须与硬件连接方式一致(RGMII/RMII等)phy-handle指向的PHY节点必须在mdio0下定义reg属性必须与PHY芯片的硬件地址一致内核启动时,会通过of_device_id表匹配设备树节点:
c复制static const struct of_device_id rk_gmac_dwmac_match[] = {
{ .compatible = "rockchip,rk3568-gmac", .data = &rk3568_ops },
{}
};
匹配成功后,会调用rk_gmac_probe函数。这个函数主要完成以下工作:
stmmac_dvr_probe注册标准MAC驱动在stmmac_dvr_probe中,会调用stmmac_mdio_register注册MDIO总线:
c复制int stmmac_mdio_register(struct net_device *ndev)
{
struct mii_bus *new_bus;
new_bus = mdiobus_alloc();
new_bus->name = "stmmac";
new_bus->read = &stmmac_mdio_read;
new_bus->write = &stmmac_mdio_write;
new_bus->reset = &stmmac_mdio_reset;
of_mdiobus_register(new_bus, mdio_node);
}
这个过程中,内核会:
mii_bus结构体MDIO总线注册后,会通过mdiobus_scan扫描所有可能的PHY地址(0-31):
c复制for (i = 0; i < PHY_MAX_ADDR; i++) {
phydev = get_phy_device(bus, i, false);
if (!IS_ERR(phydev)) {
phy_device_register(phydev);
}
}
对于每个地址,驱动会:
phy_device如果发现PHY无法识别,可以按照以下步骤排查:
dmesg日志,确认MDIO总线是否注册成功reg属性一致当网络时断时续时,可能是MDIO通信存在问题:
tx_delay和rx_delay参数ethtool -m命令查看PHY的链路状态在实际调试中,我发现以下几个命令特别有用:
mii-tool -v:查看MDIO总线上的PHY状态devmem2 0xfe2a0000:直接读取GMAC寄存器echo 7 > /proc/sys/kernel/printk:提高内核日志级别RK3568的MDIO总线默认频率可能不是最优的,可以通过修改设备树调整:
dts复制&gmac0 {
snps,mdio-clock-freq = <2500000>; /* 2.5MHz */
};
为了减少MDIO访问延迟,可以启用PHY寄存器的预读取:
c复制struct phy_device *phydev;
phydev->mdio.bus->phy_pre_read = my_phy_pre_read;
phydev->mdio.bus->phy_post_read = my_phy_post_read;
相比轮询模式,中断模式可以降低CPU负载:
dts复制&mdio0 {
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&gic>;
};
最近在调试一个RK3568项目时,遇到了PHY无法识别的问题。通过以下步骤解决了问题:
dmesg发现MDIO总线注册成功,但PHY扫描失败dts复制&gmac0 {
pinctrl-0 = <&gmac0_miim_pullup>;
};
这个案例告诉我们,硬件设计细节对MDIO总线通信至关重要。当遇到问题时,需要结合软件日志和硬件信号分析,才能快速定位问题根源。