本文基于 U-Boot v2025.10 中 DWC Ethernet QOS 驱动相关代码。
eqos_probe & eqos_remove
eqos->dev = dev;
eqos->config = (void *)dev_get_driver_data(dev);
eqos->regs = dev_read_addr(dev);
if (eqos->regs == FDT_ADDR_T_NONE) {
pr_err("dev_read_addr() failed\n");
return -ENODEV;
}
eqos->mac_regs = (void *)(eqos->regs + EQOS_MAC_REGS_BASE);
eqos->mtl_regs = (void *)(eqos->regs + EQOS_MTL_REGS_BASE);
eqos->dma_regs = (void *)(eqos->regs + EQOS_DMA_REGS_BASE);
eqos->tegra186_regs = (void *)(eqos->regs + EQOS_TEGRA186_REGS_BASE);
eqos->max_speed = dev_read_u32_default(dev, "max-speed", 0);
在 probe 函数的一开始,首先对驱动私有数据结构进行了进行初始化,主要是“绑定”了一些关键的结构体以及获取设备树中的一些信息进行填充,依次为:
- 与 device 绑定;
- 与 driver data 绑定 (
struct eqos_config
); - 从设备树中获取设备基地址;
- 根据设备基地址与各个寄存器段的偏移获取
mac_regs
、mtl_regs
、dma_regs
、tegra186_regs
; - 从设备树中获取网卡支持的最大速度;
之后,在 eqos_probe_resources_core()
中申请并分配了接收发送描述符 (Descriptor) 以及数据缓冲区,Descriptor 与 DMA 是以太网驱动的核心。以太网描述符与 DMA 结合使用,是 CPU 与 MAC 之间实现高效以太网帧传输的“桥梁”,它为控制器提供有关数据位置(缓冲区地址)以及如何处理数据(缓冲区大小、状态等)的信息。
ret = eqos->config->ops->eqos_probe_resources(dev);
if (ret < 0) {
pr_err("eqos_probe_resources() failed: %d\n", ret);
goto err_remove_resources_core;
}
ret = eqos->config->ops->eqos_start_clks(dev);
if (ret < 0) {
pr_err("eqos_start_clks() failed: %d\n", ret);
goto err_remove_resources_tegra;
}
为了适配不同的平台,驱动里对平台相关的操作分离,并以函数指针结构体 (struct eqos_ops
) 的形式方便调用。eqos_probe_resources()
中进一步的对设备树进行了解析,获取时钟信息、复位引脚等平台相关配置。eqos_start_clks()
中则是对设备所需的时钟进行配置以及使能等操作。
#ifdef CONFIG_DM_ETH_PHY
eqos->mii = eth_phy_get_mdio_bus(dev);
#endif
if (!eqos->mii) {
eqos->mii = mdio_alloc();
if (!eqos->mii) {
pr_err("mdio_alloc() failed\n");
ret = -ENOMEM;
goto err_stop_clks;
}
eqos->mii->read = eqos_mdio_read;
eqos->mii->write = eqos_mdio_write;
eqos->mii->priv = eqos;
strcpy(eqos->mii->name, dev->name);
ret = mdio_register(eqos->mii);
if (ret < 0) {
pr_err("mdio_register() failed: %d\n", ret);
goto err_free_mdio;
}
}
#ifdef CONFIG_DM_ETH_PHY
eth_phy_set_mdio_bus(dev, eqos->mii);
#endif
这里是为该设备准备 MDIO 总线接口(struct mii_dev
),主要是将驱动中提供的 MDIO 读写接口绑定到总线设备上,以便 PHY 驱动可以通过 MDIO 访问物理层设备,并且对非 Driver Model 和 Driver Model 两种模型做了兼容处理。
如果开启了 Driver Model 的以太网 PHY 支持 (CONFIG_DM_ETH_PHY
),首先会在 eth_phy_get_mdio_bus()
通过设备树中的 phy-handle
节点找到 MDIO 总线设备 (struct mii_dev
),并在最后将 MDIO 总线设备与以太网设备绑定,以使 Driver Model 的 PHY 驱动后续能够找到正确的 MDIO 总线。
对于没有开启 Driver Model 或者获取 MDIO 总线失败的情况,会手动创建总线设备并绑定驱动中提供的读写接口,最后注册到总线中(将刚创建的设备添加到 struct list_head mii_devs
中。
到此,驱动的 Probe 操作就全部完成了,总结来说就是创建和初始化了驱动所需的数据结构、获取设备树中的一些配置信息、初始化时钟以及绑定了 MDIO 总线。
而 Remove 操作如下,主要就是一些清理操作,这里就不赘述了。
mdio_unregister(eqos->mii);
mdio_free(eqos->mii);
eqos->config->ops->eqos_stop_clks(dev);
eqos->config->ops->eqos_remove_resources(dev);
eqos_remove_resources_core(dev);