OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 134|回复: 0

Linux驱动学习实战(1) 移植UBoot

[复制链接]

1

主题

1

帖子

0

精华

新手入门

积分
9
金钱
9
注册时间
2026-1-27
在线时间
1 小时
发表于 3 天前 | 显示全部楼层 |阅读模式


## 干什么

  昨天我跟随着正点原子的教程学习了U-Boot如何移植,今天我决定从官网上下载最新的U-Boot并尝试移植到自己的板子上。访问官网,下载最新版的U-Boot源码压缩包(截止文章创建时是2026.01),拖入虚拟机并解压。**(亲测在正点原子提供的16.04下会出许多问题,所以我在下载好编译器后转到了24.04的ubuntu版本运行。)**

<div align=center><img src="/img/linux驱动开发/hands-on-1/解压.png"  width=60%></div>
<div style="text-align:center;">图 1 文件目录</div>

## 怎么干

### 配置 Makefile

&emsp;&emsp;在根目录下的Makefile中添加以下语句,并保存

```Makefile
ARCH = arm
CROSS_COMPILE = arm-linux-gnueabihf-
```

### 尝试编译标准配置

&emsp;&emsp;在根目录下configs文件夹中找到mx6ull_14x14_evk_defconfig,在终端中输入如下命令:

    make clean
    make mx6ull_14x14_evk_defconfig
    make V=1 -j16

&emsp;&emsp;不出意外的话就要出意外了,正点原子提供的编译器版本过老,需要先升级一下。

<div align=center><img src="/img/linux驱动开发/hands-on-1/编译器过老.png"  width=60%></div>
<div style="text-align:center;">图 2 报错</div>

在网站下载11.2版本的GCC工具链,并在虚拟机中安装。先下载安装包,拖入后解压,将U-Boot根目录下Makefile改为

    CROSS_COMPILE = /home/alientek/gcc-arm-11.2-2022.02-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-

再次编译,会报一些无依赖的错误,我们依次安装。最终编译成功

<div align=center><img src="/img/linux驱动开发/hands-on-1/第一次编译成功.png"  width=60%></div>
<div style="text-align:center;">图 3 第一次编译成功</div>

将SD卡插入开发板,用串口连接输出以下信息

<div align=center><img src="/img/linux驱动开发/hands-on-1/第一次烧写输出.png"  width=60%></div>
<div style="text-align:center;">图 4 输出</div>

&emsp;&emsp;由上图可见,只有网络设备出了问题,我们就移植这一部分。

### 创建自己的开发板文件

#### 新建 defconfig 文件

&emsp;&emsp;在根目录下的configs文件夹中复制一份mx6ull_14x14_evk_defconfig并重命名为mx6ull_hcw_defconfig。

#### 新建include/configs/xxx.h文件

&emsp;&emsp;在此文件夹中复制一份mx6ull_14x14_evk.h,并将其改名为mx6ull_hcw.h

#### 新建板级文件

&emsp;&emsp;在board/freescale文件夹下,复制mx6ullevk并重命名为mx6ull_hcw,修改其中C文件为文件夹同名.c,并且修改其中Makefile为:

```Makefile
# SPDX-License-Identifier: GPL-2.0+
# (C) Copyright 2016 Freescale Semiconductor, Inc.

obj-y  := mx6ull_hcw.o
```

将 imximage.cfg 中的下面一句:

    PLUGIN board/freescale/mx6ullevk/plugin.bin 0x00907000

改为:

    PLUGIN board/freescale/mx6ull_hcw/plugin.bin 0x00907000

将原文件中的原目录名改成现目录名

```text
if TARGET_MX6UL_14X14_EVK || TARGET_MX6UL_9X9_EVK

config SYS_BOARD
    default "mx6ull_alientek_emmc"

config SYS_VENDOR
    default "freescale"

config SYS_CONFIG_NAME
    default "mx6ull_alientek_emmc"

endif
```

修改此目录下的MAINTAIN文件,同上几步,也是改成自己的文件目录

#### 试着编译一下

运行

```bash
make clean
make mx6ull_hcw_defconfig
make V=1 -j16
```

无报错,烧录,可以看到我们新建的板级文件可以正常运行,接下来让我们修改网口部分。

<div align=center><img src="/img/linux驱动开发/hands-on-1/第二次串口输出.png"  width=60%></div>
<div style="text-align:center;">图 5 输出</div>

### 移植网口部分

&emsp;&emsp;2026版本和2016不同的是使用了默认设备树方式进行配置,从mx6ull_hcw_defconfig中可以看出

```text
CONFIG_DEFAULT_DEVICE_TREE="imx6ull-14x14-evk"
```

&emsp;&emsp;进入/arch/arm/dts文件夹,打开imx6ull-14x14-evk.dts,可以看到

```dts
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
//
// Copyright (C) 2016 Freescale Semiconductor, Inc.

/dts-v1/;

#include "imx6ull.dtsi"
#include "imx6ul-14x14-evk.dtsi"

/ {
    model = "Freescale i.MX6 UltraLiteLite 14x14 EVK Board";
    compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
};

&clks {
    assigned-clocks = <&clks IMX6UL_CLK_PLL3_PFD2>;
    assigned-clock-rates = <320000000>;
};
```

dts和dtsi文件的关系如下

    .dts = 最终可用的“整板描述文件”
    .dtsi = 被包含的“设备树片段 / 模块”
    dts会被直接编译生成dtb,能被uboot使用

而我们涉及到的这几个文件的主要作用分别是:

```text
imx6ull.dtsi            ← 芯片出厂说明书
imx6ul-14x14-evk.dtsi   ← 官方参考板接线图
imx6ull-14x14-evk.dts   ← “把这块板子声明出来”
```

&emsp;&emsp;因此我们应着重修改imx6ul-14x14-evk.dtsi文件。

&emsp;&emsp;复制imx6ull-14x14-evk.dts和imx6ul-14x14-evk.dtsi,并将其重命名为imx6ull_hcw.dts和imx6ul-hcw.dtsi,将imx6ull-hcw.dts中的头文件包含改为。

```h
#include "imx6ull.dtsi"
#include "imx6ul-hcw.dtsi"
```

&emsp;&emsp;同时需要在 xxx_defconfig 中将CONFIG_DEFAULT_DEVICE_TREE修改为 "imx6ull-hcw"

&emsp;&emsp;修改好后,试着重新编译,发现无报错。接下来我们就分析imx6ul-hcw.dtsi文件,让网口驱动跑起来!

&emsp;&emsp;打开imx6ul-hcw.dtsi,找到原来的网口配置代码:

```dtsi
&fec2 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_enet2>;
    phy-mode = "rmii";
    phy-handle = <&ethphy1>;
    phy-supply = <&reg_peri_3v3>;
    status = "okay";

    mdio {
        #address-cells = <1>;
        #size-cells = <0>;

        ethphy0: ethernet-phy@2 {
            compatible = "ethernet-phy-ieee802.3-c22";
            reg = <2>;
            micrel,led-mode = <1>;
            clocks = <&clks IMX6UL_CLK_ENET_REF>;
            clock-names = "rmii-ref";

        };

        ethphy1: ethernet-phy@1 {
            compatible = "ethernet-phy-id0022.1560";
            reg = <1>;
            micrel,led-mode = <1>;
            clocks = <&clks IMX6UL_CLK_ENET2_REF>;
            clock-names = "rmii-ref";
        };
    };
};
```

|姓名|是什么|作用|
|---|---|---|
|FEC/MAC|i.MX6ULL 芯片内部的一个 硬件模块|帧收发、DMA、校验和、高速数据通道。|
|PHY|一颗真正的芯片|模拟信号、10/100M编码、网线插拔检测、LED驱动,网口与SOC之间的桥梁|
|MDIO|一条管理总线|读 PHY ID、读链路状态、配速率。MDIO 引脚是 从 FEC 出来的,PHY 只是“被管理对象”|

&emsp;&emsp;**SoC 与网口之间的数据传输通过 PHY 完成,而 PHY 的配置与状态管理通过 MDIO 进行。** 以 fec2(第二个 FEC MAC)为例,这份代码第2-3行配置了这个设备只有一种引脚模式,为default,并且配置是pinctrl_enet2,在下面代码中有定义。phy-mode = "rmii"表明在这颗phy和soc之间使用的是RMII接口模式,phy-handle = <&ethphy1>表明这个网口接的是哪个芯片,在下面的代码中有定义,phy-supply = <&reg_peri_3v3>告诉系统这个PHY的电源使用3.3V,status = "okay"表示设备启用。

&emsp;&emsp;mdio{...}是指MDIO总线节点,ethphy0是我的第一个phy,compatible = "ethernet-phy-ieee802.3-c22"表示这是一个符合 IEEE 802.3 Clause 22 规范的以太网 PHY,设备地址是2,clocks = <&clks IMX6UL_CLK_ENET_REF>;表明这个 PHY 使用 i.MX6ULL 输出的 ENET 参考时钟,clock-names 用于为该时钟命名以便驱动引用。

那么接下来我们开始移植,找到引脚配置代码

```dtsi
pinctrl_enet1: enet1grp {
    fsl,pins = <
        MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN    0x1b0b0
        MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER    0x1b0b0
        MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00    0x1b0b0
        MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01    0x1b0b0
        MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN    0x1b0b0
        MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00    0x1b0b0
        MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01    0x1b0b0
        MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1    0x4001b031
    >;
};

pinctrl_enet2: enet2grp {
    fsl,pins = <
        MX6UL_PAD_GPIO1_IO07__ENET2_MDC     0x1b0b0
        MX6UL_PAD_GPIO1_IO06__ENET2_MDIO    0x1b0b0
        MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN  0x1b0b0
        MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER  0x1b0b0
        MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0
        MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0
        MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN  0x1b0b0
        MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0
        MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0
        MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2  0x4001b031
    >;
};
```

&emsp;&emsp;以pinctrl_enet1: enet1grp为例,这段代码定义了一组ENET1网口要用到的引脚,并指定每个引脚的功能,每个引脚用什么电气参数(PAD控制),其格式为

    [物理引脚]__ [复用成什么功能]    [电气参数]

我们新建一个网口复位引脚,如下

```btsi
pinctrl_enet1_reset: enet1resetgrp {
    fsl,pins = <
        MX6UL_PAD_SNVS_TAMPER7__GPIO5_IO07  0x000000
    >;
};
pinctrl_enet2_reset: enet2resetgrp {
    fsl,pins = <
        MX6UL_PAD_SNVS_TAMPER8__GPIO5_IO08  0x000000
    >;
};
```

&emsp;&emsp;FEC配置代码修改后如下

```btsi
&fec1 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_enet1>;
    phy-mode = "rmii";
    phy-handle = <&ethphy0>;
    phy-supply = <&reg_peri_3v3>;
    status = "disabled";
};

&fec2 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_enet2>;
    phy-mode = "rmii";
    phy-handle = <&ethphy1>;
    phy-supply = <&reg_peri_3v3>;
    status = "okay";
    local-mac-address = [02 12 34 56 78 9a];

    mdio {
        #address-cells = <1>;
        #size-cells = <0>;

        ethphy0: ethernet-phy@2 {
            compatible = "ethernet-phy-ieee802.3-c22";
            reg = <2>;
            micrel,led-mode = <1>;
            clocks = <&clks IMX6UL_CLK_ENET_REF>;
            clock-names = "rmii-ref";

            reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>;
            reset-assert-us = <10000>;
            reset-deassert-us = <10000>;

        };

        ethphy1: ethernet-phy@1 {
            compatible = "ethernet-phy-ieee802.3-c22";
            reg = <1>;
            micrel,led-mode = <1>;
            clocks = <&clks IMX6UL_CLK_ENET2_REF>;
            clock-names = "rmii-ref";


            reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
            reset-assert-us = <10000>;
            reset-deassert-us = <10000>;
        };
    };
};
```

&emsp;&emsp;主要修改了以下几个部分

    ①、关闭FEC1,只使能FEC2,因为正点原子的MINI板上与ENET相连的是FEC2。
    ②、修改 compatible ,告诉内核 PHY 是一个标准 Clause22 PHY,让内核自己识别。
    ③、添加复位代码,当 U-Boot 决定要用这个 PHY 时,拉这根复位脚。
    ④、设定本地 MAC 地址

<div align=center><img src="/img/linux驱动开发/hands-on-1/网卡驱动成功.png"  width=60%></div>
<div style="text-align:center;">图 6 网卡驱动成功</div>

## 结语

在这个过程中,我逐步理清了几个过去容易混淆的概念:FEC 只是 SoC 内部的 MAC 控制器,真正与网线打交道的是 PHY;RMII 承载的是高速数据,而 MDIO 只是一个低速的“管理通道”;设备树的职责不是“写驱动”,而是准确描述硬件连接关系,让通用驱动能够工作。

接下来,可以在此基础上继续深入:例如将 MAC 地址移入 EEPROM、分析 PHY 自协商过程,或者进一步对接 Linux 内核网络子系统。

但无论走到哪一步,这次移植经历已经证明了一点——理解结构,比跑通结果更重要。



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-1-30 12:51

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表