I.MX6ULL网络驱动源码分析:
一、fec_probe()函数:网络驱动的“启动程序”
这是I.MX6ULL网络驱动的核心入口,设备树和驱动匹配成功后,它就会开始搭建网络功能,核心工作分四步:
1. 先备齐硬件资源
- 从设备树里拿到FEC控制器的寄存器物理地址,转换成内核能直接用的虚拟地址;
- 获取IPG、AHB、enet_out这些关键时钟,并且把时钟启动起来;
- 找到设备树里指定的PHY芯片节点,同时确认PHY的工作模式,比如是RMII还是MII,并记到驱动私有结构里。
2. 搭建网络设备框架
- 用`alloc_etherdev_mqs`分配核心的网络设备结构`net_device`,同时初始化驱动自己的私有数据结构;
- 给网络设备绑定操作函数,比如`netdev_ops`管基础的网络操作,`ethtool_ops`管网卡配置,还注册了NAPI机制,让后续数据接收用轮询方式处理,提升效率。
3. 搞定中断和MDIO通道
- 申请TX、RX、错误处理这些中断,中断来了就交给`fec_enet_interrupt`处理;
- 调用`fec_enet_mii_init`初始化MDIO总线,把读写PHY芯片的函数(`fec_enet_mdio_read/write`)挂载好,打通MAC和PHY的通信通道。
4. 正式注册网卡
最后调用`register_netdev`,把配置好的网卡注册到Linux网络子系统里,这样系统就能识别并使用这块网卡了。
整个过程完全依赖设备树的配置,像PHY节点、时钟、中断这些,都得提前在设备树里写对,驱动才能正常工作。
二、MDIO总线与PHY注册:打通MAC和PHY的“沟通桥梁”
Linux用MDIO总线来管理外接的PHY芯片,核心逻辑很简单:
1. MDIO总线的核心载体:mii_bus
它里面有读写PHY的函数指针,还有记录PHY设备的数组。不同芯片的MDIO控制器不一样,所以这些读写函数必须由芯片厂商(比如NXP)自己写,I.MX6ULL里就是FEC控制器对应的读写函数。
2. 注册流程:一步步连上PHY
整个注册是层层调用的关系:
先从`fec_probe`进入`fec_enet_mii_init`,再调用`of_mdiobus_register`,它会从设备树的mdio子节点里,找到所有挂载的PHY节点,然后为每个节点创建PHY设备,最后调用`phy_device_register`把PHY注册到内核里,这样MAC和PHY就成功关联起来了。
3. PHY的兼容性匹配
设备树里PHY的`compatible`属性决定了识别方式,比如`"ethernet-phy-ieee802.3-c22"`是传统的Clause 22协议,大部分普通PHY用这个;如果是10G这类高端PHY,就用Clause 45协议,属性写成`"ethernet-phy-ieee802.3-c45"`即可。
简单说,注册MDIO总线的时候,会自动扫描设备树里的PHY节点,逐个完成PHY设备的注册,让内核能统一管理这些物理层芯片。
三、fec_drv_remove()函数:驱动的“收尾清理”
当要卸载I.MX6ULL网络驱动时,这个函数负责把资源全部回收,避免内存泄漏和硬件残留:
- 先取消之前注册的延时任务和超时任务,比如定时检测链路的`time_keep`和处理发送超时的`tx_timeout_work`;
- 调用`unregister_netdev`注销之前注册的网络设备,让系统不再识别这块网卡;
- 进入`fec_enet_mii_remove`,先注销MDIO总线,同时会自动注销所有关联的PHY设备,切断MAC和PHY的关联;
- 接着关闭PHY相关的电源(regulator)、注销PTP时钟,释放设备树节点的引用;
- 最后用`free_netdev`释放之前申请的网络设备内存,完成整个清理流程。
四、核心总结
I.MX6ULL以太网驱动的核心逻辑:
NXP提供的FEC驱动(管MAC)→ 搭建MDIO总线 → 注册PHY设备 → 最终把网卡注册到系统网络栈实际开发中,只要设备树配置正确,比如PHY地址、工作模式、时钟、引脚这些和硬件一致,完全不用自己写驱动,直接用NXP的通用FEC驱动就能让网络功能跑起来。
理清这个流程,对调试网络问题特别有用,比如遇到网络不通、PHY识别失败、RMII时钟异常,都能顺着这个流程快速定位问题,重点检查设备树里的PHY地址、模式配置和时钟是否正确即可。