Linux MDIO子系统深度剖析:从原理到实践(4)
接前一篇文章:Linux MDIO子系统深度剖析:从原理到实践(3)
四、驱动匹配流程
1. 设备注册与扫描
MDIO子系统中的设备注册过程始于MDIO总线控制器的初始化。当MDIO总线控制器驱动被加载时,它会创建一个mii_bus实例,初始化必要的硬件资源,并调用mdiobus_register函数注册该总线。
总线注册的关键步骤是mdiobus_scan函数,它负责探测总线上连接的PHY设备。探测过程会遍历所有可能的PHY地址(0到31),对每个地址执行以下操作:
1)发送读取PHYID的请求:通过MDIO总线读取该地址上的PHY标识符寄存器(REG2和REG3);
2)验证响应数据:检查读取到的值是否为有效的PHY ID(全0或全1通常表示没有设备);
3)创建phy_device:如果检测到有效的PHY ID,系统会创建一个phy_device实例并初始化关键字段;
4)注册设备:将新创建的phy_device注册到MDIO总线上。
这个扫描过程可以类比于酒店前台在客人入住前检查所有房间,确定哪些房间已经有客人入住,哪些还空着。对于每个有客人的房间,前台会创建一份客户档案,记录客人的基本信息和房间号。
在实际代码中,扫描过程的核心逻辑如下(简化版):
// 简化的总线扫描逻辑
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
// 跳过已知无效的地址
if (mii_bus->phy_map[addr])
continue;
// 读取 PHY 标识符
phy_id = mdiobus_read(mii_bus, addr, MII_PHYSID1);
if (phy_id < 0)
continue;
if (phy_id == 0xffff || phy_id == 0x0000)
continue; // 无效的 PHY ID
// 创建并注册 phy_device
phy_dev = get_phy_device(mii_bus, addr, false);
if (!IS_ERR(phy_dev)) {
err = phy_device_register(phy_dev);
if (err) {
put_device(&phy_dev->dev);
} else {
mii_bus->phy_map[addr] = phy_dev;
}
}
}
值得注意的是,除了动态扫描,MDIO子系统也支持通过设备树静态指定PHY设备。在这种情况下,设备树中的MDIO节点会包含子节点,每个子节点描述一个PHY设备,指定其地址、兼容字符串和其它配置参数。这种方式在嵌入式系统中特别有用,可以避免自动扫描的不确定性,并提供更精确的设备配置。
2. 驱动匹配过程
驱动匹配是MDIO子系统中的核心环节,它确保了每个被探测到的PHY设备能够与合适的驱动程序关联起来。匹配过程遵循Linux设备模型的标准流程,由mdio_bus_match函数实现。
当一个新的PHY驱动通过phy_driver_register注册到系统时,内核会遍历MDIO总线上所有已注册但尚未绑定驱动的PHY设备,尝试将新驱动与这些设备匹配;反过来,当一个新的PHY设备注册时,内核也会遍历所有已注册的驱动,寻找匹配项。
匹配算法的核心是基于 PHY 标识符的比较,具体过程如下:
1)提取设备标识符:从phy_device中获取设备的phy_id字段,这个值是在总线扫描时从硬件的 PHYID寄存器读取的;
2)应用驱动掩码:对于每个候选驱动,使用驱动的phy_id_mask对设备的phy_id进行按位与操作,过滤掉不关心的位;
3)比较标识符:将掩码后的设备phy_id与驱动的phy_id进行比较,如果完全匹配,则表明该驱动支持这个设备;
4)执行探测函数:如果标识符匹配,内核会调用驱动的probe方法来完成设备初始化。
除了基于PHYID的主要匹配方式,MDIO子系统还支持其它匹配方法:
- 设备树兼容性匹配:如果PHY设备是通过设备树定义的,系统会检查设备的“compatible”属性是否与驱动定义的兼容性列表匹配。
- ACPI匹配:在支持ACPI的系统中,可以使用ACPI设备ID进行匹配。
- 手动绑定:通过 sysfs 接口手动将设备与驱动关联。
匹配成功后,phy_device的drv字段会被设置为指向对应的phy_driver,而驱动的probe函数会被调用以完成设备特定的初始化工作。
3. probe过程详解
驱动的probe方法是PHY设备初始化的核心环节,它负责将通用的phy_device与具体的硬件特性关联起来。probe过程通常包括以下步骤:
1)硬件验证:通过读取特定的状态寄存器或标识符寄存器,确认PHY硬件确实存在且功能正常;
2)功能检测:检测PHY支持的特性,如支持的速度、双工模式、自动协商能力等;
3)中断配置:如果PHY支持中断,配置中断处理函数以响应链路状态变化等事件;
4)初始化配置:设置PHY的初始工作参数,如广播能力、电源管理模式等;
5)注册网络设备:将PHY设备与上层的网络设备关联,建立从网络接口到PHY的完整数据路径。
以下是一个简化的probe函数示例:
static int example_phy_probe(struct phy_device *phydev)
{
int err;
// 读取额外的标识寄存器进行硬件验证
err = phy_read(phydev, EXAMPLE_EXTRA_ID_REG);
if (err < 0)
return err;
if ((err & EXTRA_ID_MASK) != EXPECTED_EXTRA_ID) {
dev_err(&phydev->dev, "Extra ID mismatch: expected 0x%x, got 0x%x\n",
EXPECTED_EXTRA_ID, err & EXTRA_ID_MASK);
return -ENODEV;
}
// 配置 PHY 特定的功能
err = phy_write(phydev, MII_EXAMPLE_CTRL_REG, CTRL_INIT_VALUE);
if (err < 0)
return err;
// 设置驱动特定的私有数据
struct example_priv *priv = devm_kzalloc(&phydev->dev,
sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
phydev->priv = priv;
// 初始化私有数据
priv->some_setting = DEFAULT_VALUE;
dev_info(&phydev->dev, "Example PHY driver successfully probed\n");
return 0;
}
probe函数成功返回后,PHY设备就进入了就绪状态,可以被上层网络子系统使用了。此时,PHY驱动会开始监控链路的连接状态,并在状态变化时通知上层,从而触发网络接口的UP或DOWN事件。
MDIO驱动匹配完整流程如下图所示:

图4:MDIO驱动匹配完整流程
更多内容请看下回。
更多推荐



所有评论(0)