0%

Linux驱动学习日记(9) Uboot学习(四)

NXP官方开发板 uboot 编译测试

查找配置文件并编译

  我们先在uboot中找到参考的开发平台,一般是原厂的开发板,参考原厂开发板的uboot移植到我们的开发板上。

  方便起见,我们编写如下图所示的make.sh文件

1
2
3
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12

如下图所示,NXP官方编译成功,但是这些文件是 NXP 官方 I.MX6ULL EVK 开发板。能不能用到正点原子的 I.MX6ULL 开发板上呢?让我们做一下方案验证。

图 1 编译成功

烧写验证和驱动测试

  将NXP官方bin文件烧录至SD卡中并插入开发板启动。

图 2 启动

  由图3可以看出,U-Boot虽然正常启动,但是有些设备会识别失败,移植就是为了让这些设备可以识别正确。我们现在依次检查各个设备是否正常。

检查存储设备

图 3 检查存储设备

  通过输入mmc设备命令检查EMMC和SD卡都正常。

图 4 检查EMMC

网络驱动和LCD

  从图 2 中可以看出:以太网驱动和LCD都初始化失败。那么接下来我们就分析原因并移植其到我们的开发板上。

在 U-Boot 中添加自己的开发板

添加开发板默认配置文件

  先在 configs 目录下创建默认配置文件,复制 mx6ull_14x14_evk_emmc_defconfig,然后重命名为 mx6ull_alientek_emmc_defconfig,并做以下改动

1
2
3
4
5
CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ull_alientek_emmc/imximage.cfg,MX6ULL_EVK_EMMC_REWORK"
CONFIG_ARM=y
CONFIG_ARCH_MX6=y
CONFIG_TARGET_MX6ULL_14X14_EVK=y
CONFIG_CMD_GPIO=y

添加开发板对应的头文件

  在目录 include/configs 下添加 I.MX6ULL-MINI 开发板对应的头文件,复制 include/configs/mx6ulevk.h,并重命名为 mx6ull_alientek_emmc.h。完成后改头文件定义。mx6ull_alientek_emmc.h 里面有很多宏定义,这些宏定义基本用于配置 uboot,也有一些 I.MX6ULL 的配置项目。如果我们自己要想使能或者禁止 uboot 的某些功能,那就在 mx6ull_alientek_emmc.h 里面做修改即可。 mx6ull_alientek_emmc.h 里面的内容比较多。

1
2
#ifndef __MX6ULL_ALIENTEK_EMMC_H
#define __MX6ULL_ALIENTEK_EMMC_H
作用 说明
#include <configs/mx6_common.h> 引入 i.MX6 系列公共配置 很多“你没写但生效”的宏就在这里
PHYS_SDRAM_SIZE DRAM 总大小 必须和板子实际 DDR 容量一致
CONFIG_DISPLAY_CPUINFO 启动时打印 CPU 信息 CPU: Freescale i.MX6ULL ...
CONFIG_DISPLAY_BOARDINFO 启动时打印板子信息 Board: MX6ULL ...
CONFIG_SYS_MALLOC_LEN malloc 内存池大小 U-Boot 动态内存,常见 8–16MB
CONFIG_NR_DRAM_BANKS DRAM bank 数量 i.MX6ULL 只有 1 个
PHYS_SDRAM DRAM 物理起始地址 固定 0x80000000
CONFIG_SYS_SDRAM_BASE DRAM 起始地址 通常等于 PHYS_SDRAM
CONFIG_BOARD_EARLY_INIT_F 使能 board_early_init_f() 串口 / pinmux 早期初始化
CONFIG_BOARD_LATE_INIT 使能 board_late_init() 环境变量 / 外设后期处理
CONFIG_MXC_UART 使能 i.MX 串口驱动
CONFIG_MXC_UART_BASE 串口寄存器基地址 UART1 = 0x02020000
CONFIG_SYS_FSL_ESDHC_ADDR ESDHC 寄存器基地址 eMMC / SD 控制器
CONFIG_SYS_FSL_USDHC_NUM USDHC 控制器数量 无 NAND 时通常为 2
CONFIG_SYS_MMC_ENV_DEV 默认 MMC 设备 eMMC 常为 USDHC2
CONFIG_SYS_MMC_ENV_PART 环境变量分区 默认 0
CONFIG_CMD_I2C 使能 i2c 命令 i2c probe
CONFIG_SYS_I2C_SPEED I2C 总线速度 常用 100k / 400k
CONFIG_SYS_I2C_SLAVE 默认从地址
CONFIG_NAND_MXS 使能 GPMI NAND
CONFIG_MTD_PARTITIONS NAND 分区机制
CONFIG_MTDIDS_DEFAULT MTD 设备名
CONFIG_MTDPARTS_DEFAULT NAND 分区布局
CONFIG_ENV_SIZE env 大小 eMMC 常 8KB
CONFIG_ENV_OFFSET env 偏移地址 相对存储器起始
CONFIG_EXTRA_ENV_SETTINGS 默认环境变量 bootargs / bootcmd
CONFIG_MFG_ENV_SETTINGS 量产工具环境变量 MfgTool 使用
CONFIG_BOOTCOMMAND 默认 bootcmd 自动启动 Linux
CONFIG_SYS_LOAD_ADDR kernel 加载地址 常用 0x80800000
CONFIG_LOADADDR 同上
CONFIG_SYS_INIT_RAM_ADDR OCRAM 起始地址 0x00900000
CONFIG_SYS_INIT_RAM_SIZE OCRAM 大小 128KB
CONFIG_SYS_INIT_SP_OFFSET 初始 SP 偏移
CONFIG_SYS_INIT_SP_ADDR 初始 SP 地址
CONFIG_SYS_HZ 系统 tick 1000Hz
CONFIG_STACKSIZE 栈大小 128KB
CONFIG_FEC_MXC 使能 FEC 网卡
CONFIG_FEC_ENET_DEV 使用哪个 ENET 0=ENET1
IMX_FEC_BASE ENET 寄存器基地址
CONFIG_FEC_MXC_PHYADDR PHY 地址 必须匹配硬件
CONFIG_FEC_XCV_TYPE PHY 接口类型 RMII / RGMII
CONFIG_CMD_DHCP dhcp 命令
CONFIG_CMD_PING ping 命令
CONFIG_VIDEO 使能 LCD
CONFIG_VIDEO_LOGO 显示 logo
CONFIG_CMD_BMP bmp 显示命令

mx6ull_alientek_emmc.h ≠ 配置全集,真正的实现分散在mx6_common.h、arch/arm/mach-imx/、drivers/*

添加开发板对应的板级文件

创建并修改Makefile文件

  uboot 中每个板子都有一个对应的文件夹来存放板级文件,比如开发板上外设驱动文件等等。 NXP 的 I.MX 系列芯片的所有板级文件夹都存放在 board/freescale 目录下,在这个目录下有个名为 mx6ullevk 的文件夹,这个文件夹就是 NXP 官方 I.MX6ULL EVK 开发板的板级文件夹。复制 mx6ullevk,将其重命名为 mx6ull_alientek_emmc。进入 mx6ull_alientek_emmc 目录中,将其中的 mx6ullevk.c 文件重命名为 mx6ull_alientek_emmc.c。并在Makefile中做出如下修改

1
2
3
4
5
obj-y  := mx6ull_alientek_emmc.o

extra-$(CONFIG_USE_PLUGIN) := plugin.bin
$(obj)/plugin.bin: $(obj)/plugin.o
$(OBJCOPY) -O binary --gap-fill 0xff $< $@

修改此目录下的imximage.cfg文件

将 imximage.cfg 中的下面一句:

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

改为:

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

修改此目录下的 Kconfig 文件

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

1
2
3
4
5
6
7
8
9
10
11
12
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

修改此目录下的 MAINTAINERS 文件

  同上述几步,也就是改成自己的文件目录

修改U-Boot图形界面配置文件

uboot 是支持图形界面配置,关于 uboot 的图形界面配置下一章会详细的讲解。修改文件 arch/arm/cpu/armv7/mx6/Kconfig(如果用的 I.MX6UL 的话,应该修改 arch/arm/Kconfig 这个文件),在 207 行加入如下内容:

1
2
3
4
5
config TARGET_MX6ULL_ALIENTEK_EMMC
bool "Support mx6ull_alientek_emmc"
select MX6ULL
select DM
select DM_THERMAL

并在最后一行的endif前加入以下内容

source "board/freescale/mx6ull_alientek_emmc/Kconfig"

到此为止, I.MX6U-MINI 开发板就已经添加到 uboot 中了,接下来就是编译这个新添加
的开发板。

使用新添加的板子配置编译uboot

  编写如下脚本,运行

1
2
3
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_alientek_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
图 5 编译结果
图 6 烧录结果

  由图可见,此时的 Board 还是“MX6ULL 14x14 EVK”,因为我们参考的 NXP 官方的 I.MX6ULL 开发板来添加自己的开发板。前面已经说了,默认 uboot 中的 LCD 驱动和网络驱动在正点原子的 I.MX6U-MINI 开发板上是有问题的,需要修改。

网口驱动修改

  正点原子的 I.MX6U-MINI 开发板ENET 网口使用 SR8201F 作为 PHY 芯片。NXP 官方的 I.MX6ULL EVK 开发板使用 KSZ8081 这颗 PHY 芯片, SR8201F 相比 KSZ8081 具有体积小、外围器件少、价格便宜等优点。直接使用 KSZ8081 固然可以,但是我们在实际的产品中不一定会使用 KSZ8081,有时候为了降低成本会选择其他的 PHY 芯片,这个时候就有个问题:换了 PHY 芯片以后网络驱动怎么办?为此,正点原子的 I.MX6U-ALPHA 开发板将 ENET1 和 ENET2的 PHY 换成了 SR8201F,这样就可以给大家讲解更换 PHY 芯片以后如何调整网络驱动,使网络工作正常。

图 7 网卡原理图

  ENET1 的网络 PHY 芯片为 SR8201F,通过 RMII 接口与 I.MX6ULL 相连,正点原子 I.MX6U-ALPHA 开发板的 ENET1 引脚与 NXP 官方的 I.MX6ULL EVK 开发板基本一样,唯独复位引脚不同。从图 33.2.8.1 可以看出,正点原子 I.MX6U-ALPHA 开发板的 ENET1 复位引脚 ENET1_RST 接到了 I.M6ULL 的 SNVS_TAMPER7 这个引脚上。
  SR8201F 内部是有寄存器的, I.MX6ULL 会读取 SR8201F 内部寄存器来判断当前的物理链接状态、连接速度(10M 还是 100M)和双工状态(半双工还是全双工)。 I.MX6ULL 通过 MDIO 接口来读取 PHY 芯片的内部寄存器, MDIO 接口有两个引脚, ENET_MDC 和 ENET_MDIO, ENET_MDC 提供时钟, ENET_MDIO 进行数据传输。一个 MDIO 接口可以管理 32 个 PHY 芯片,同一个 MDIO 接口下的这些 PHY 使用不同的器件地址来做区分, MIDO 接口通过不同的器件地址即可访问到相应的 PHY 芯片。 I.MX6U-ALPHA 开发板 ENET1 上连接的 SR8201F 器件地址为 0X2,所示我们要修改 ENET1 网络驱动的话重点就三点:
①、 ENET1 复位引脚初始化。
①、 ENET1 复位引脚初始化。
③、 SR8201F 驱动

网络 PHY 地址修改

  首先修改 uboot 中的 ENET1 和 ENET2 的 PHY 地址和驱动,打开 mx6ull_alientek_emmc.h这个文件,找到如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifdef CONFIG_CMD_NET
#define CONFIG_FEC_MXC
#define CONFIG_MII
#define CONFIG_FEC_ENET_DEV 1

#if (CONFIG_FEC_ENET_DEV == 0)
#define IMX_FEC_BASE ENET_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR 0x2
#define CONFIG_FEC_XCV_TYPE RMII
#elif (CONFIG_FEC_ENET_DEV == 1)
#define IMX_FEC_BASE ENET2_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR 0x1
#define CONFIG_FEC_XCV_TYPE RMII
#endif
#define CONFIG_ETHPRIME "FEC"

#define CONFIG_PHYLIB
#define CONFIG_PHY_MICREL
#endif

①、修改 ENET1 网络 PHY 的地址。
②、修改 ENET2 网络 PHY 的地址。
③、使能 REALTEK 公司的 PHY 驱动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifdef CONFIG_CMD_NET
#define CONFIG_FEC_MXC
#define CONFIG_MII
#define CONFIG_FEC_ENET_DEV 1

#if (CONFIG_FEC_ENET_DEV == 0)
#define IMX_FEC_BASE ENET_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR 0x2
#define CONFIG_FEC_XCV_TYPE RMII
#elif (CONFIG_FEC_ENET_DEV == 1)
#define IMX_FEC_BASE ENET2_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR 0x1
#define CONFIG_FEC_XCV_TYPE RMII
#endif
#define CONFIG_ETHPRIME "FEC"

#define CONFIG_PHYLIB
#define CONFIG_PHY_REALTEK
#endif

删除74LV595的驱动代码

  打开mx6ull_alientek_emmc.c,将以下代码替换:

1
2
3
4
5
6
7
8
9
//替换前
#define IOX_SDI IMX_GPIO_NR(5, 10)
#define IOX_STCP IMX_GPIO_NR(5, 7)
#define IOX_SHCP IMX_GPIO_NR(5, 11)
#define IOX_OE IMX_GPIO_NR(5, 8)

//替换后
#define ENET1_RESET IMX_GPIO_NR(5, 7)
#define ENET2_RESET IMX_GPIO_NR(5, 8)

删掉static iomux_v3_cfg_t const iox_pads[]配置,删除iox74lv_init、iox74lv_set函数,并在board_init函数中将他们的调用删除。

添加 I.MX6U-ALPHA 开发板网络复位引脚驱动

结构体数组 fec1_pads 和 fec2_pads 是 ENET1 和 ENET2 这两个网口的 IO 配置参数,在这两个数组中添加两个网口的复位 IO 配置参数,完成以后如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
static iomux_v3_cfg_t const fec1_pads[] = {
MX6_PAD_GPIO1_IO06__ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
MX6_PAD_GPIO1_IO07__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET1_TX_DATA0__ENET1_TDATA00 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET1_TX_DATA1__ENET1_TDATA01 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET1_TX_EN__ENET1_TX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 | MUX_PAD_CTRL (ENET_CLK_PAD_CTRL),
MX6_PAD_ENET1_RX_DATA0__ENET1_RDATA00 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET1_RX_DATA1__ENET1_RDATA01 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET1_RX_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),
};

static iomux_v3_cfg_t const fec2_pads[] = {
MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),

MX6_PAD_ENET2_TX_DATA0__ENET2_TDATA00 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET2_TX_DATA1__ENET2_TDATA01 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 | MUX_PAD_CTRL (ENET_CLK_PAD_CTRL),
MX6_PAD_ENET2_TX_EN__ENET2_TX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),

MX6_PAD_ENET2_RX_DATA0__ENET2_RDATA00 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET2_RX_DATA1__ENET2_RDATA01 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),
}

函数 setup_iomux_fec 就是根据 fec1_pads 和 fec2_pads 这两个网络 IO 配置数组来初始化 I.MX6ULL 的网络 IO。我们需要在其中添加网络复位 IO 的初始化代码,并且复位一下 PHY 芯片,修改后的 setup_iomux_fec 函数如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static void setup_iomux_fec(int fec_id)
{
if (fec_id == 0)
{
imx_iomux_v3_setup_multiple_pads(fec1_pads,
ARRAY_SIZE(fec1_pads));

gpio_direction_output(ENET1_RESET, 1);
gpio_set_value(ENET1_RESET, 0);
mdelay(20);
gpio_set_value(ENET1_RESET, 1);
}
else
{
imx_iomux_v3_setup_multiple_pads(fec2_pads,
ARRAY_SIZE(fec2_pads));
gpio_direction_output(ENET2_RESET, 1);
gpio_set_value(ENET2_RESET, 0);
mdelay(20);
gpio_set_value(ENET2_RESET, 1);
}
mdelay(150);
}

fec1_pads 就是“ENET1 要用到的每个脚,该怎么复用、该用什么电气参数”的清单.imx_iomux_v3_setup_multiple_pads函数的作用是把一组 pad(引脚)的 IOMUX + PAD 控制配置批量写进 IOMUXC 寄存器

编译后烧录:

图 8 网卡原理图

在uboot使用网络之前先要设置几个环境变量

setenv ipaddr 192.168.1.55 //开发板 IP 地址
setenv ethaddr b8:ae:1d:01:00:00 //开发板网卡 MAC 地址
setenv gatewayip 192.168.1.1 //开发板默认网关
setenv netmask 255.255.255.0 //开发板子网掩码
setenv serverip 192.168.1.250 //服务器地址,也就是 Ubuntu 地址 saveenv //保存环境变量

LCD

主播手上没有LCD屏幕,因此这一部分暂时不做。

结语

至此,I.MX6ULL 平台下 U-Boot 板级移植的完整流程已经走通。从最初复制 NXP 官方 EVK 的板级代码,到创建新的 defconfig、Kconfig、board 目录,再到逐步调试 LCD 和以太网驱动,整个过程并非简单的“改文件名 + 改宏定义”,而是一次对 U-Boot 架构理解的系统性实践。后续还会做RK3588平台的驱动实战。