U-Boot 移植(4)

📅 2026/8/25 11:02:11
U-Boot 移植(4)
3.4 网络驱动修改网络驱动的修改其复杂程度往往高于 LCD 屏幕因为屏幕只需要发送数据单向为主而网络涉及 MDIO 总线控制读写 PHY 芯片寄存器、RMII 时钟同步50MHz时钟是谁供给谁 以及 TCP/IP 协议栈的地址校验。3.4.1 网络驱动原理在嵌入式 Linux 系统中一个完整的硬件联网方案由 MAC 控制器、MDIO 管理接口、PHY 芯片以及网络变压器/RJ45 接口共同构成。1.以太网硬件三层架构MAC PHY 变压器内部 MAC介质访问控制层集成在 i.MX6ULL 芯片内部。它是一个数字逻辑外设主要负责处理网络数据帧的打包、拆包、CRC 校验以及流量控制。外部 PHY物理层芯片板载的独立芯片如 LAN8720A。它负责将 MAC 发出的数字信号转换成网线上的模拟电平信号或者将网线上的模拟信号转成数字信号送给 MAC。网络变压器与 RJ45起到电气隔离和信号滤波的作用保护主控芯片不受外部网线过压干扰。2.MAC 与 PHY 之间的两条“生命线”主控芯片的内部 MAC必须通过两套完全独立的接口与外部 PHY 芯片连接缺一不可① 数据接口RMII / MII用于传输实际的网络数据包。i.MX6ULL 常用的是 RMIIReduced MII精简媒体独立接口。它只需要 2 根数据发送线、2 根数据接收线、以及 1 根 50MHz 的同步时钟线REF_CLK。核心原理当网络有数据流入时PHY 芯片通过 RMII 接口将数据一位一位地推给 SoC 内部的 MAC。② 管理接口MDIO / MDC用于控制和配置 PHY 芯片。这是一组串行双线总线MDC 时钟线MDIO 双向数据线。SoC 内部的 MAC 充当主设备外部 PHY 芯片充当从设备通过不同的硬件 PHY Address 区分。核心原理U-Boot 或 Linux 内核的网络驱动就是通过 MDIO 总线去读写 PHY 芯片内部的寄存器从而获取网线有没有插好Link 状态、当前是 10M 还是 100M 速率、是全双工还是半双工。3.网络驱动的核心专用 DMA 控制器工业级 SoC如 i.MX6ULL之所以网络性能强悍关键在于其内部 MAC 外设自带了一个专用的以太网 DMADirect Memory Access直接内存访问控制器。它的工作流程完全不需要 CPU 的实时干预硬件层自动接收当数据包从网线通过 RMII 涌入 MAC 时专用 DMA 会自动将这些数据包搬运到你板子上的 DDR3 内存中排队也就是环形缓冲区 Ring Buffer。软件层轻量触发直到整个数据包搬运完毕DMA 才会给 CPU 发送一个中断信号。协议栈解析CPU 收到中断后直接去内存里把数据拿出来喂给 Linux 内核的 TCP/IP 协议栈驱动层的工作即宣告完成。硬件不同NXP 官方的 I.MX6ULL EVK 开发板使用 KSZ8081 PHY 芯片接下来我们观察 韦东山的板子硬件现状图中只有 E28型号为 ULC0544P10_AZ1045-04F 的 ESD 保护芯片和 J6带网络变压器的 RJ45 插座。对应关系左侧的信号直接打着 ENET1_TXP/N 和 ENET1_RXP/N 的网络标签。这意味着 ENET1 的 PHY 芯片已经集成在主控 SoC 内部了也就是所谓的高性能主控自带的内置百兆 MACPHY。主控芯片引脚直接输出差分信号经过 ESD 芯片保护后就直接进了网络变压器。总结ENET1 的 PHY 芯片主控 SoC 内置 PHY无需外挂独立 PHY 芯片。ENET2 的 PHY 芯片LAN8720A外挂的独立百兆 PHY 芯片。3.4.2 LAN8720A 修改内容说明我们以 ENET2 的 LAN8720A 芯片为基础进行讲解状态读取机制i.MX6ULL 通过 MDIO 接口包含时钟线 ENET_MDC 和数据线 ENET_MDIO来读取 LAN8720A 的内部寄存器从而判断网络的物理链接、连接速度10M/100M以及双工状态。多设备区分原理一个 MDIO 接口最多可管理 32 个 PHY 芯片系统依靠不同的器件地址PHY ID来精准识别和访问同一总线下的具体芯片如该板 ENET1 上的芯片地址为 0x0。驱动修改三大重点若要适配或修改网络驱动核心任务包括复位引脚初始化、确认并配置正确的 PHY 器件地址、以及实现/加载对应的 LAN8720 芯片驱动。1.复位引脚初始化从原理图可知ENET2 的复位引脚对应 SNVS_TAMPER62.PHY 器件地址对于 LAN8720A 芯片它的 PHY 地址是由 RXER/PHYAD0 引脚对应芯片的第 10 脚在复位那一刻的电平决定的如果该引脚被外接电阻下拉到地 (GND)则 PHYAD0 0芯片地址为 0x0。如果该引脚被外接电阻上拉到 VDD则 PHYAD0 1芯片地址为 0x1。3.LAN8720 芯片驱动前 16 个寄存器无需修改IEEE 标准化寄存器 0 ~ 15 的功能与位域定义由 IEEE 802.3 规范固定如自协商、网线插拔状态、基础网速读取。由于全球厂商统一标准通用驱动直接读取即可让芯片跑起来不需要任何修改。后 16 个寄存器必须修改厂商自定义寄存器 16 ~ 31 属于厂商特定自定义区。由于不同芯片如 LAN8720A的内部状态机不同驱动必须主动介入去修改/读取这些特定寄存器如 LAN8720A 的寄存器 31才能精准获取最终的网速、双工状态并同步给 SoC 的 MAC 控制器。时钟与硬件模式配置是修改的核心如时钟同步在特定的硬件电路设计中例如外部没有接晶振全靠 i.MX6ULL 提供 50MHz 时钟必须在网络初始化时通过 MDIO 总线改写 LAN8720A 的特殊模式寄存器寄存器 18将其强制配置为“外部时钟输入模式”。如果不动这个寄存器芯片与 CPU 时钟无法同步网络将彻底瘫痪。3.4.3 LAN8720A 修改流程1.复位引脚初始化ENET2 的复位引脚对应 SNVS_TAMPER6我们到 IMX6ULL 参考手册查阅具体位置被复用为了 GPIO5_IO06在 \board\freescale\mx6ull_kd\mx6ull_kd.c 中添加如下定义#defineENET2_RESETIMX_GPIO_NR(5,6)/* 网络引脚电气属性配置 */#defineENET_PAD_CTRL(PAD_CTL_PKE|PAD_CTL_PUE|\PAD_CTL_PUS_100K_UP|PAD_CTL_SPEED_LOW|\PAD_CTL_DSE_40ohm|PAD_CTL_SRE_SLOW)staticiomux_v3_cfg_tconstfec2_pads[]{/* 将 SNVS_TAMPER6 显式配置为普通的 GPIO5_IO06 */MX6_PAD_SNVS_TAMPER6__GPIO5_IO06|MUX_PAD_CTRL(ENET_PAD_CTRL),/* 如果后续网络依旧无法通信需要在这里继续把 RMII 的 TXD0, RXD0, MDIO 等引脚一并补齐 */};在 setup_fec 中实现硬复位逻辑找到源码中的 static int setup_fec(int fec_id) 函数在对应的 else 分支即 fec_id 1代表 ENET2中使用 imx_iomux_v3_setup_multiple_pads 初始化引脚并使用标准的 gpio_ 接口进行拉低→\rightarrow→延时→\rightarrow→拉高的硬复位操作。即在如图所示分支末尾添加代码/* 1. 设置引脚复用将该引脚配置为 GPIO 模式 */imx_iomux_v3_setup_multiple_pads(fec2_pads,ARRAY_SIZE(fec2_pads));/* 2. 申请 GPIO 资源 */retgpio_request(ENET2_RESET,enet2_reset);if(!ret){/* 3. 设置为输出方向并拉低电平进行硬复位 */gpio_direction_output(ENET2_RESET,0);udelay(10000);/* 持续拉低 10ms确保 PHY 芯片彻底复位 *//* 4. 拉高电平释放复位状态使 PHY 芯片开始工作 */gpio_set_value(ENET2_RESET,1);udelay(100);/* 略微延时等待 PHY 内部状态机稳定 */2.PHY 器件地址\include\configs\mx6ull_kd.h中可以看到#defineCONFIG_FEC_ENET_DEV1说明默认使用的 ENET 网口是 ENET20对应ENET1,1对应ENET2(这里我们顺手修改一下0对应的是 ENET1 地址应该是 0x0ENET2 的地址为 0x1 不需要修改。代码中还有一个宏定义#defineCONFIG_PHY_MICRELNXP使用的外部PHY芯片KSZ8081是Micrel公司生产的所以该宏为CONFIG_PHY_MICREL韦东山IMX6ULL开发板使用的外部PHY芯片是SMSC公司生产的所以应该删除原来的宏重新定义一个宏CONFIG_PHY_SMSC#defineCONFIG_PHY_SMSC3. 修改 drivers/net/phy/phy.c在 U-Boot 中如果使用通用 PHY 驱动Generic PHY去驱动 LAN8720A确实经常会遇到无法读取网络链接状态、速度或全双工状态的问题导致网络 Ping 不通。这并不是代码写错了而是因为 LAN8720A 的状态寄存器设计有一点非标准。为什么要修改 genphy_update_link在标准 drivers/net/phy/phy.c 的 genphy_update_link 函数中U-Boot 是通过读取通用寄存器 1BMSR来判断网线是否拔插的。但是LAN8720A 的自协商最终结果到底协商成了 10M 还是 100M全双工还是半双工虽然它也符合 IEEE 标准但在实际应用中直接用通用函数去读取它时它的寄存器状态在 U-Boot 极其快速的初始化链路中经常更新不及时或者由于时钟同步问题导致读出来的数据是错的。为了让它稳定可靠我们需要在这个函数里手动介入直接读取 LAN8720A 专属的寄存器 31特殊功能控制/状态寄存器SPSR强制让 U-Boot 拿到最准确的网速和双工状态。具体修改步骤打开 drivers/net/phy/phy.c找到 genphy_update_link 函数在unsigned int mii_reg;后添加以下代码staticintlan8720_flag0;intbmcr_reg0;if(lan8720_flag0){bmcr_regphy_read(phydev,MDIO_DEVAD_NONE,MII_BMCR);phy_write(phydev,MDIO_DEVAD_NONE,MII_BMCR,BMCR_RESET);while(phy_read(phydev,MDIO_DEVAD_NONE,MII_BMCR)0X8000){udelay(100);}phy_write(phydev,MDIO_DEVAD_NONE,MII_BMCR,bmcr_reg);lan8720_flag1;}4. 修改 \configs\mx6ull_kd_defconfiginclude/configs/mx6ull_kd.h会发现在文件靠后的地方有这样几行代码#ifdefCONFIG_DM_ETH#defineCONFIG_ETHPRIMEeth1#else#defineCONFIG_ETHPRIMEFEC1#endif核心冲突在 U-Boot 2017.03 中如果系统通过 Kconfig也就是 configs/mx6ull_kd_defconfig开启了 CONFIG_DM_ETHy那么U-Boot 的全局网络初始化函数 eth_initialize() 将只会去设备树Device Tree里寻找带有 compatible “fsl,imx6ul-fec” 属性的节点进行绑定。它会完全跳过并无视在 mx6ull_kd.c 里面写的所有传统初始化函数包括 board_eth_init 和 fecmxc_initialize_multi。也就是说哪怕底层引脚复用、电压、硬复位全部正确只要 CONFIG_DM_ETH 是开启的U-Boot 就会当 board_eth_init 根本不存在从而继续打印 No ethernet found.。解决办法修改 configs/mx6ull_kd_defconfig找到CONFIG_DM_ETHn改成# CONFIG_DM_ETH is not set3.4.4 验证完成以上步骤后编译烧录进开发板这种可以在串口看到以下信息NETFEC 说明找到了网络