
这篇移植记录拖了一段时间今天终于有空把它整理出来。事情的起因很简单我拿到了一套基于某NXP系Cortex-A7开发板的uboot移植教程参考板是A板手里要适配的却是另一家的B板。芯片型号一样外设布局却完全不同尤其是网络驱动这块第一次上电直接卡在PHY识别折腾了小半天才找到头绪。这篇文章就围绕“uboot移植里的网络驱动部分”把完整过程拆开讲清楚包括原理图怎么看、设备树怎么改、配置项怎么开、调试命令怎么用以及那些教程里不会写但实际必踩的坑。如果你也在做类似的板级适配或者刚开始学uboot移植想弄明白网络子系统是怎么工作的这篇应该能帮你省不少时间。1. 为什么uboot移植最先要解决网络驱动1.1 这是一次什么样的移植任务先交代一下背景。A板是教程配套的参考板uboot源码和编译流程都是现成的B板是目标板CPU和A板同系列但板子上的PHY芯片、网口变压器位置、复位引脚、引脚复用全都对不上。这类移植在嵌入式项目里非常常见——换板子不换芯片或者说芯片一样但外围重新画了。很多人拿到B板后第一反应是把参考板的uboot整个编译一遍烧进去。这样确实能跑起来但大概率只起个串口网络是死的。因为uboot镜像本身包含了板级配置文件、设备树和驱动而网络驱动依赖的硬件信息全在板级配置里参考板的配置自然只对参考板有效。而网络在移植阶段几乎是最关键的。后续要烧写内核、加载设备树、挂载根文件系统最方便的方式就是tftp。如果uboot阶段网络不通后面每一步都得靠SD卡或烧录器来回倒腾效率极低。所以我的习惯是拿到新板子先把uboot网络拉通再谈别的。1.2 uboot网络子系统不是一个“驱动”那么简单很多人以为网络驱动就是“一个网卡驱动文件”其实在uboot里它是一整套分层结构从上到下大致是网络协议层负责命令解析和协议处理比如ping、tftp、dhcp代码在net/目录下。以太网控制器驱动层负责收发数据帧完成DMA描述符管理、中断处理等。在NXP系芯片上这个控制器叫FECFast Ethernet Controller代码一般在drivers/net/fec_mxc.c。PHY驱动层负责管理物理层芯片读取PHY ID、协商速率、读取链路状态。代码在drivers/net/phy/目录下不同厂商PHY有不同驱动文件。MDIO/MII管理总线这是MAC和PHY之间的管理通道uboot通过它往PHY寄存器读写数据。用生活里的话说MAC控制器相当于网卡主芯片负责协议和数据处理PHY相当于变压器旁边的那个“物理层小芯片”负责把数字信号变成线缆上的电信号MDIO则是MAC和PHY之间那条“私聊通道”。移植过程中最难的就是让MAC通过MDIO通道正确识别并控制目标板上的PHY。在uboot启动日志里会有一段网络初始化输出类似Net: eth0: fec2188000。如果后面跟着PHY not found或者直接卡住那就是MAC没有读到PHY的ID而这正是绝大多数网络移植失败的原因。1.3 为什么参考板的网络配置不能直接用有的PHY芯片虽然型号相同但板子设计者会把PHY的地址引脚接到不同电平导致PHY地址从参考板的0变成目标板的1或2。设备树里默认写的是0直接调用当然读不出ID。还有的是接口模式变了参考板用RMII目标板用MII时钟方案完全不同这时即使PHY能识别也协商不出正确的10M/100M速率。所以我一直强调uboot网络移植不是改代码而是先改硬件认知。你以为你在调驱动其实你是在把目标板的电路细节翻译成设备树和配置项。下面就从原理图开始。2. 移植前必须搞懂的硬件信息原理图、PHY地址、时钟和复位2.1 从目标板原理图里抄下来的四组关键信号在改任何一行代码之前先去找目标板的原理图。重点看以太网相关的几页一般能搜到“Ethernet”或“PHY”字样。你需要确认的信息有PHY芯片的型号和封装。比如有些板子用A厂商的LAN8720A有些用M厂商的KSZ8081型号直接决定uboot里要开哪个PHY驱动。PHY地址的配置方式。PHY芯片上一般有PHYAD0、PHYAD1、PHYAD2之类的引脚原理图上会标注上拉电阻或下拉电阻对应二进制值就是PHY地址。这个地址一定要记下来。MAC和PHY之间的接口模式。常见的有MII、RMII、RGMII。不同模式需要的信号线数量和时钟频率不同。RMII模式下的REF_CLK由谁提供。是PHY自己用25MHz晶振产生50MHz时钟还是由MAC输出50MHz时钟给PHY。这个搞错了即使能识别PHY数据传输也是乱的。PHY复位引脚的GPIO编号和有效电平。一般PHY都有硬复位脚常见低电平复位但板子上可能用GPIO控制也可能是RC电路自动复位。如果uboot没有正确释放复位PHY会一直处于复位状态。这些信息我建议画成一张表一边对照参考板A一边填目标板B。实际操作中我经常遇到“PHY地址不确定”的情况那就用万用表量对应的PHYAD引脚电压或者直接翻原理图上的电阻标注。2.2 用一张表找出两板的网络差异下面是我整理这类信息时常用的表格模板供你参考项目参考板A目标板B影响范围PHY芯片型号PHY-APHY-Bselected PHY driverPHY地址01device tree regMAC与PHY接口RMIIRMIIphy-modeREF_CLK来源MAC提供50MHzPHY自带25MHz晶振引脚配置和时钟PHY复位引脚GPIO5_7低有效GPIO1_9高有效reset-gpios属性网口数量1路2路FEC1/FEC2节点这张表不是写给自己看的而是后面每次改完代码用来对照的依据。我曾经试过不填表直接改结果这个PHY地址问题是改了三次才意识到是抄错了PHYAD引脚。2.3 理解PHY地址为什么决定成败PHY是挂在MDIO总线上的设备每个PHY都有一到两个地址MDIO协议用地址来区分总线上的多个PHY。MAC通过MDIO发命令时会携带一个PHY地址只有地址匹配的PHY才会响应。uboot初始化网卡时设备树里的reg属性就指定了PHY地址。如果reg和硬件不一样MDIO读写就永远得不到有效应答PHY ID读出来全是0xffffffff。日志里通常会出现类似phy_connect failed或者Could not get PHY for eth0: addr -1。很多人在这一步卡了很久其实只要拿原理图看清楚PHYAD引脚接的什么电平然后改设备树里一个数字就能解决。但前提是你要先亲眼确认这个数字而不是靠猜。3. 动刀设备树、配置项、板级初始化到底改了哪几个地方3.1 设备树里fec节点与mdio子节点的改法现在uboot普遍使用设备树来描述板级硬件。以NXP系芯片为例设备树里有fec1和fec2两个以太网控制器的节点。打开目标板设备树找到对应的fec1节点重点检查以下属性。先看一个典型的节点结构fec1 { pinctrl-names default; pinctrl-0 pinctrl_fec1; phy-mode rmii; phy-handle ethphy0; phy-reset-gpios gpio5 7 GPIO_ACTIVE_LOW; phy-reset-duration 10; phy-reset-post-delay 20; status okay; mdio { #address-cells 1; #size-cells 0; ethphy0: ethernet-phy0 { compatible ethernet-phy-id0007.c0f0, ethernet-phy-ieee802.3-c22; reg 0; }; }; };这里每个属性都不是摆设phy-mode必须和硬件原理图一致不能想当然。如果是MII写mii如果是RMII写rmii。phy-handle指明MAC在MDIO总线上要和哪个PHY设备对象通信它引用下面mdio子节点里的ethphy0。phy-reset-gpiosPHY复位引脚GPIO_ACTIVE_LOW表示该引脚低电平复位。如果你的板子是高电平复位这里要改成GPIO_ACTIVE_HIGH否则PHY一直被按在复位状态。reg 0这个就是PHY地址。一定要改成从原理图里确认到的地址比如reg 1。在mdio子节点里ethernet-phy0这个名字中的0只是节点名称的一部分真正决定地址的是reg。有些PHY的compatible可以直接写ethernet-phy-id加具体ID也可以写通用ethernet-phy-ieee802.3-c22。uboot会尝试自动读取PHY ID如果驱动支持就直接匹配。3.2 pinctrl引脚配置复用、电气属性不能照抄设备树里的pinctrl-0指向一个引脚配置子节点这些配置通常在设备树的iomuxc节点里定义。以某芯片为例它的引脚宏定义形如pinctrl_fec1: fec1grp { fsl,pins MX6UL_PAD_ENET1_TX_DATA0__ENET1_TX_DATA0 0x1b0b0 MX6UL_PAD_ENET1_TX_DATA1__ENET1_TX_DATA1 0x1b0b0 MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0 MX6UL_PAD_ENET1_TX_CLK__ENET1_TX_CLK 0x1b0b0 MX6UL_PAD_ENET1_RX_DATA0__ENET1_RX_DATA0 0x1b0b0 MX6UL_PAD_ENET1_RX_DATA1__ENET1_RX_DATA1 0x1b0b0 MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0 MX6UL_PAD_ENET1_RX_CLK__ENET1_RX_CLK 0x1b0b0 MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0 ; };然后要在fec1节点里加上pinctrl-0 pinctrl_fec1;。注意引脚宏必须查目标板原理图上实际连接的信号。有的板子网口信号可能接到的是ENET2那一组引脚此时就应该用MX6UL_PAD_ENET2_XXX__ENET2_XXX并且要操作fec2节点。引脚宏后面的0x1b0b0是配置寄存器的值包含驱动能力、压摆率、上下拉等。不要随意抄最好对照芯片手册的IOMUX电气表格确认。例如RMII模式的REF_CLK引脚通常需要强驱动能力弱驱动可能导致数据采样错乱。还有一个容易踩的坑如果RMII模式下REF_CLK由MAC输出需要额外确认MAC的REF_CLK引脚是否配置正确如果由PHY提供那MAC侧的REF_CLK引脚要配置成输入模式电气属性可能不同。3.3 defconfig里要开哪些网络相关宏改完设备树接下来是uboot的defconfig。在板级默认配置文件里需要确保以下宏被正确启用CONFIG_DM_ETHy CONFIG_NETy CONFIG_CMD_NETy CONFIG_CMD_PINGy CONFIG_CMD_TFTPy CONFIG_PHYLIBy CONFIG_PHY_MICRELy CONFIG_PHY_MICREL_KSZ8XXXy CONFIG_CMD_MIIy CONFIG_MIIy CONFIG_DEFAULT_DEVICE_TREE目标板对应的dtb文件名其中CONFIG_DM_ETH是驱动模型下的以太网驱动框架现代uboot必须要开。CONFIG_PHYLIB开启PHY管理框架而CONFIG_PHY_MICREL这一类的宏要根据目标板PHY芯片厂商来选择。例如目标板PHY是MICREL芯片就开CONFIG_PHY_MICREL如果是SMSC芯片就开CONFIG_PHY_SMSC。如果开错了框架即使PHY在MDIO上能读到IDuboot也会因为没有对应驱动而无法完成协商。CONFIG_CMD_MII是非常有用的调试开关它让你能在uboot命令行下直接读写PHY寄存器。后面排查问题会频繁用到建议默认打开。另外不要忘了设置默认MAC地址。可以在defconfig里加CONFIG_ETHPRIMEfec1 CONFIG_NETDEVICESy然后在环境变量中预设ethaddr或者写在板级文件里。一次tftp把内核放到DDR后这个问题才会真正暴露。我建议从一开始就在uboot环境变量里设置好ethaddr比如setenv ethaddr 00:11:22:33:44:55 saveenv3.4 板级文件里真的要改东西吗很多教程告诉你只需要改设备树和defconfig就够了但现实中有时还要在板级C文件里补一点初始化代码。NXP系uboot通常在board/freescale/mx6ullevk/这类目录下板级文件负责时钟、电源、IO域的初始设置。对于网络来说最常见的是MII/RMII时钟源选择。例如i.MX6ULL的FEC时钟由CCM控制某些uboot版本里需要在board_early_init_f或soc.c中配置FEC1_CLK的时钟源和分频。如果设备树已配置assigned-clocksuboot的时钟驱动会自动处理但别指望所有版本都自动搞定遇到网络不通时就要去检查时钟寄存器。一个判断技巧如果启动日志中完全没有MDIO超时但PHY始终协商不起来多半和时钟或接口模式有关。这时去查SoC参考手册里FEC时钟树确认RMII模式需要的50MHz参考时钟确实到达了PHY。4. 编译、上电、调试网络起不来的完整排查实录4.1 编译阶段先避掉两个低级错误改完配置后重新编译uboot常见两种低级错误第一设备树编译报错比如引用的ethphy0没定义、pinctrl节点名字写错。这种问题报错信息会直接指出行号很好解决但前提是语法正确。建议先用make dtbs单独编译设备树二进制把错误提前暴露出来。第二配置项依赖冲突。比如开了CONFIG_PHY_MICREL却没有开CONFIG_PHYLIB或者开了CONFIG_DM_ETH但没有保留CONFIG_NET编译会像解方程组一样报一堆undefined reference。改配置时最好用make menuconfig查看依赖或者参考芯片默认defconfig不要自己凭空加。4.2 上电后PHY根本识别不到一段真实排障复盘我把整个调试过程压缩成一个典型复盘分享出来。第一次烧写后启动日志显示Net: eth0: fec2188000 PHY reset timed out Could not get PHY for eth0: addr -1第一反应是PHY地址不对。打开B板原理图找到PHY地址配置引脚看到PHYAD0接了下拉PHYAD1和PHYAD2都接了上拉所以地址应该是二进制110这里要注意引脚命名和权重我一开始错把PHYAD2当作最低位算出来是1改完reg 1重新烧写日志好了点mdio read: timeout这说明MDIO总线上根本没有设备响应。于是用uboot的mii info命令扫描 mii info结果只显示一行PHY 0x00: OUI 0x0000 ...其实如果是空总线命令会直接无输出或超时。问题不是地址而是PHY压根没工作。继续查硬件量PHY的供电正常但不注意发现PHY的复位引脚一直被外部上拉而板子上复位默认是低有效这说明PHY一直处于复位状态。原来B板的复位逻辑和参考板不同参考板用GPIO拉低复位B板却在复位引脚上外接了RC延时上电复位但RC时间太短uboot启动时PHY已经退复位了为什么还没工作再仔细看B板PHY的RESET脚还被一个GPIO连接了uboot在初始化时按设备树里的phy-reset-gpios主动去拉低但拉低之后又拉高直接把这个本来已经稳定的PHY又复位了一次而且GPIO输出高电平的驱动能力不够导致PHY卡在复位状态。最终方案是删除设备树里的phy-reset-gpios属性让uboot不要去碰复位引脚。重新编译烧写后日志变成Net: eth0: fec2188000 eth0: PHY 0007:c0f0 at address 0这里0007:c0f0是PHY ID“at address 0”说明PHY地址是0而不是我之前从PHYAD引脚推断的1。看来我原来看的原理图版本和实际板子有出入。所以这个例子说明两件事设备树真的不能盲目照抄以及mii info这种命令比人眼靠谱。4.3 PHY识别成功后ping不通怎么办PHY识别成功不代表网络就通了。比较常见的现象是启动日志里已经看到eth0但执行ping 192.168.1.1一直超时。先检查链路状态在uboot命令下执行 mii info如果显示link up说明物理层已经协商成功。如果link down检查网线、交换机、PHY模式和时钟。有时RMII时钟方向搞错PHY会反复尝试协商但永远不成功。链路起来后仍然ping不通多半是MAC地址的问题。uboot如果没有有效的ethaddr网卡发出的包会是一个非法源MAC地址有些交换机会直接丢弃这种帧。设置方法前面说过了。还可以在PC上用wireshark抓包看看有没有ARP请求发出。如果没有再检查ethact是否指向了正确接口。在双网口板子上很常见uboot默认选择了eth1但网线插在eth0上。执行 setenv ethact fec1 ping 192.168.1.1或者用dhcp命令测试DHCP流程能获取到地址就说明整条收发链路是正常的。4.4 把tftp跑通才算真正完成uboot网络驱动的最终目的是加载镜像。我的验收标准是在uboot里执行tftp 0x80800000 uImage能稳定下载。如果这一步成功说明ARP、IP、UDP、以太网收发全部正常。踩过的坑是tftp时偶尔卡在TFTP from server ...然后超时。后来发现是PC防火墙开了把防火墙关掉或者放行TFTP端口就好了。另一个问题是网线之间接了一个千兆交换机而PHY是百兆某些老旧交换机在自适应上表现很差强制把交换机端口设为百兆全双工反而更稳。5. 常见问题速查表与几条避坑心得5.1 uboot网络驱动故障快查表现象可能原因优先排查动作启动日志PHY not foundPHY地址不对、PHY没上电或一直复位查原理图PHYAD量硬件删掉错误reset-gpiosmdio read timeoutMDIO总线上无设备响应用mii info扫描所有地址确认PHY供电和时钟PHY ID能读到但link down接口模式、时钟方向不对检查phy-mode量RMII REF_CLK频率link up但ping不通MAC地址无效、网口选择错误、PC防火墙设置ethaddr检查ethact抓包每次重启MAC地址都变没有设置ethaddr在uboot环境变量中固定ethaddr并saveenvtftp超时服务器路径/Windows防火墙确认TFTP服务器配置关闭防火墙能ping通但下载大文件出错网线质量、阻抗匹配、DMA问题换短距离优质网线降低网卡速率试试5.2 我的几条避坑心得第一优先用mii info替代反复编译烧写。我调试PHY地址时一开始总是改一次设备树、编译一次、烧录一次效率太低。后来发现uboot有mdio或mii命令可以直接扫描总线几下就能定位。你甚至可以临时在命令行里指定PHY地址去读寄存器比如mii read 1 0如果读到非全F的值就说明PHY地址和寄存器都正常。第二设备树里的phy-reset-gpios不是必须的。如果板子的PHY复位引脚没有和GPIO连在一起就应该删掉这个属性让uboot不去操作它。我见过有人明明板子上没接GPIO却照搬参考板的reset-gpios结果用了错误的GPIO号把别的电路搞得乱跳网络反而不正常。第三遇到“PHY识别成功但数据传输错误”的情况优先怀疑RMII时钟。从A板到B板哪怕PHY型号相同只要REF_CLK来源从MAC输出变成了PHY晶振设备树和IOMUX配置就要跟着变。这种问题用万用表或示波器量50MHz时钟最容易定位。test point要提前在硬件上预留好否则只能用逻辑分析仪器去抓很麻烦。第四环境变量里的ethaddr一定要固定。uboot默认MAC地址可能从随机数生成或读自某个存储。目标板如果没有在存储介质里烧录MAC就会出现重启后每次MAC不同导致上层环境认为是新网卡。最省事的做法就是在第一次启动时用setenv ethaddr固定一个合法MAC然后saveenv。最后再说一句个人体会这次适配从原理图确认到网口下载跑通真正改的代码量其实很少绝大多数时间花在读电路和验证信号上。网络驱动移植不是“写驱动”而是“对齐硬件描述”。你把PHY地址、接口模式、时钟来源、复位逻辑这四件事搞对了uboot网络自然就起来了。后面再做内核里的同款网卡驱动时你会发现自己已经轻车熟路因为uboot阶段踩过的坑已经把硬件细节全部暴露过了。