4、U-Boot网络驱动移植:以太网控制器驱动适配、PHY芯片驱动配置、网络命令调试
网络,是嵌入式设备的命脉。没有网络,你的板子就是个孤岛。
U-Boot的网络驱动移植,说白了就是让bootloader能跟外界通信。我见过太多工程师,内核跑得飞起,U-Boot的网络却死活不通。嗯,这章我们就来彻底搞定它。
4.1 网络驱动栈的整体架构
先看一张图,把U-Boot的网络栈理清楚。从上到下,一共四层:
看到没?我们真正需要动手的,只有最下面两层:以太网控制器驱动和PHY芯片驱动。上面的协议栈,U-Boot已经帮你写好了。
4.2 以太网控制器驱动适配
U-Boot的以太网驱动,核心是一个叫 eth_ops 的结构体。你想想看,它就像一张接口清单,你填好,系统就能用。
4.2.1 eth_ops 结构体
这个结构体定义在 include/net.h 里。我挑几个最重要的说:
| 函数指针 | 作用 | 必须实现? |
|---|---|---|
start |
初始化硬件,启动网络接口 | 是 |
stop |
关闭网络接口,释放资源 | 是 |
send |
发送一个数据包 | 是 |
recv |
接收一个数据包 | 是 |
write_hwaddr |
写入MAC地址到硬件 | 推荐实现 |
free_pkt |
释放接收缓冲区 | 可选 |
核心要点:start/send/recv/stop 这四个函数,是U-Boot网络驱动的四根柱子。少一个,网络就起不来。
4.2.2 实际移植步骤
以我手头一个 i.MX6ULL 板子为例,它的以太网控制器是 FEC (Fast Ethernet Controller)。U-Boot 源码里已经有 drivers/net/fec_mxc.c,但板级适配还得我们自己来。
具体怎么做?三步走:
- 使能驱动配置:在
include/configs/your_board.h里加上宏定义 - 注册设备:在板级初始化函数里调用
fecmxc_initialize - 配置管脚:确保IOMUX把以太网管脚配成功能模式
看一段配置代码:
/* include/configs/my_imx6ull.h */
#define CONFIG_FEC_MXC
#define CONFIG_FEC_XCV_TYPE RMII
#define CONFIG_FEC_MXC_PHYADDR 0x01
#define CONFIG_ETHPRIME "FEC"
/* 板级初始化文件 board/my_company/my_board.c */
int board_eth_init(struct bd_info *bis)
{
int ret = -1;
/* 配置FEC的IOMUX管脚 */
imx_iomux_v3_setup_pad(MX6_PAD_GPIO1_IO06__ENET_REF_CLK);
imx_iomux_v3_setup_pad(MX6_PAD_GPIO1_IO07__ENET_MDC);
/* ... 其他管脚 ... */
/* 初始化FEC控制器 */
ret = fecmxc_initialize(bis);
if (ret < 0)
printf("FEC init failed\n");
return ret;
}
个人经验:我习惯在 board_eth_init 里先打印一下PHY地址,确认硬件连接没问题。用 md 命令读一下PHY寄存器,比瞎猜强多了。
4.3 PHY芯片驱动配置
PHY芯片,就是物理层收发器。它负责把数字信号变成模拟信号,送到网线上去。
U-Boot 里 PHY 驱动框架已经很成熟了。你只要告诉它:你的PHY是什么型号,挂在哪个MDIO总线上,地址是多少。
4.3.1 PHY驱动匹配流程
U-Boot 启动时,会扫描MDIO总线,读取每个PHY的 PHY_IDENTIFIER 寄存器(寄存器2和3),然后跟驱动列表里的 phy_driver->uid 做匹配。
匹配上了,就加载对应的驱动。匹配不上?嗯,那就用通用PHY驱动,功能受限,但至少能通。
| PHY型号 | UID (32位) | U-Boot驱动文件 |
|---|---|---|
| LAN8720A | 0x0007C0F0 | drivers/net/phy/lan87xx.c |
| KSZ8081 | 0x00221560 | drivers/net/phy/ksz8xxx.c |
| RTL8211F | 0x001CC916 | drivers/net/phy/realtek.c |
| DP83848 | 0x20005C90 | drivers/net/phy/dp83848.c |
4.3.2 配置PHY的两种方式
方式一:在板级头文件里直接指定
/* 最简单粗暴的方式 */
#define CONFIG_PHY_LAN87XX
#define CONFIG_FEC_MXC_PHYADDR 0x01
方式二:通过设备树描述(如果U-Boot支持DM)
/* 设备树节点示例 */
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fec1>;
phy-mode = "rmii";
phy-handle = <ðphy0>;
status = "okay";
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethphy0: ethernet-phy@1 {
compatible = "ethernet-phy-id0007.c0f0";
reg = <0x01>;
};
};
};
我曾经踩过的坑:有一次PHY地址配成了0x00,但实际硬件是0x01。U-Boot扫描不到PHY,网络死活不通。查了两天才发现,是原理图上的地址线拉法跟我想的不一样。记住:PHY地址不是猜的,要用万用表量,或者读寄存器确认。
4.4 网络命令调试
驱动写好了,怎么验证?U-Boot提供了一套网络命令,非常实用。
4.4.1 常用网络命令一览
| 命令 | 用途 | 典型用法 |
|---|---|---|
ping |
测试网络连通性 | ping 192.168.1.100 |
tftp |
通过TFTP下载文件 | tftp 0x80800000 uImage |
dhcp |
自动获取IP地址 | dhcp |
nfs |
挂载NFS文件系统 | nfs 0x80800000 /nfs/uImage |
mdio |
读写PHY寄存器 | mdio list / mdio read 0x01 0x00 |
4.4.2 调试三板斧
网络不通怎么办?别慌,按顺序来:
- 先看PHY:
mdio list看看能不能扫到PHY。再mdio read 0x01 0x00读寄存器0(BMCR),看看bit 15是不是1(表示复位中)。 - 再看链路:读PHY寄存器1(BMSR),bit 2是链接状态。为1表示网线插好了。
- 最后ping:设置好IP,
ping 服务器IP。通了,万事大吉。
调试口诀:先PHY,再MAC,最后协议栈。一层一层排查,别跳步。
4.4.3 一个完整的调试案例
假设你敲了 ping 192.168.1.100,结果卡住了。我会这么做:
# 第一步:检查PHY是否存在
=> mdio list
PHY 0x01: OUI = 0x0007, Model = 0x20, Rev = 0x01
--> 嗯,PHY地址0x01,型号LAN8720A,没问题
# 第二步:检查链接状态
=> mdio read 0x01 0x01
Read from MDIO: 0x796D
--> bit 2 = 1,链接正常
# 第三步:设置IP并ping
=> setenv ipaddr 192.168.1.10
=> setenv serverip 192.168.1.100
=> ping 192.168.1.100
Using FEC device
host 192.168.1.100 is alive
--> 通了!
如果 mdio list 没输出?那问题大概率在硬件:管脚配置错了,或者PHY没上电。我曾经遇到过,PHY的复位引脚被拉低了,芯片一直处于复位状态。量一下电压,就发现了。
4.5 常见问题与避坑指南
- PHY扫描不到:检查PHY地址、MDIO上拉电阻、复位时序。我习惯在PHY复位后加个10ms延时。
- 能ping通但tftp超时:多半是MTU问题,或者服务器防火墙没关。试试
tftp -l小文件。 - MAC地址全是FF:没设置
ethaddr环境变量。用setenv ethaddr 00:11:22:33:44:55设一个。 - 速率协商不对:强制固定速率试试。在PHY驱动里把
autoneg关掉,设成100M全双工。
我的习惯:每次移植新板子,我都会在 board_eth_init 里加一段调试打印,把PHY的ID、链接状态、速率都打出来。这样一上电就知道硬件有没有问题,不用猜。
网络驱动移植,说难不难,说简单也不简单。关键是思路清晰,工具用好。记住:PHY是基础,MAC是桥梁,命令是验证手段。这三样搞定了,你的板子就能上网了。