imx6ull uboot移植
环境准备
uboot获取
NXP官方uboot仓库地址
https://github.com/nxp-imx/uboot-imx/tree/nxp/imx_v2016.03_4.1.15_2.0.0_ga
uboot官方获取地址
ftp://ftp.denx.de/pub/u-boot/ (后缀有rcx 表示测试版本)
注:强烈建议使用 NXP 官方 uboot,移植起来方便很多。
编译链获取
linaro官网下载编译链
gcc-linaro-7.5.0-2019-12-x86_64_arm_linux_gnueabihf.tar.xz
编译环境安装
下载完成后拷贝到新建的Linux开发环境 /usr/local/arm 目录:
cd /usr/local/
mkdir arm
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
# 为当前用户添加环境变量
cd home/xxxx
vim .zshrc #or vim .bashrc
# 在最后一行添加如下,保存并退出
export PATH=$PATH:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin
移植适配
新建单板
基于NXP官方EVK新建目标板
board/freescale 目录
cd board/freescale
cp -rf mx6ullevk/ mx6ull_topeet_emmc
修改目标板内容
- 修改 mx6ull_topeet_emmc.c
cd mx6ull_topeet_emmc/
mv mx6ullevk.c mx6ull_topeet_emmc.c
- 修改 makefile
obj-y := mx6ull_topeet_emmc.o
- 修改 imximage.cfg
#ifdef CONFIG_USE_PLUGIN
/*PLUGIN plugin-binary-file IRAM_FREE_START_ADDR*/
PLUGIN board/freescale/mx6ull_topeet_emmc/plugin.bin 0x00907000
#else
...
#endif
- 修改 Kconfig
if TARGET_MX6ULL_TOPEET_EMMC
config SYS_BOARD
default "mx6ull_topeet_emmc"
config SYS_VENDOR
default "freescale"
config SYS_VENDOR
default "mx6"
config SYS_CONFIG_NAME
default "mx6ull_topeet_emmc"
endif
- 修改 MAINTAINERS
MX6ULLITOP BOARD
M: chanshang <chanshang@xxxx.com>
S: Maintained
F: board/freescale/mx6ull_topeet_emmc/
F: include/configs/mx6ull_topeet_emmc.h
F: configs/mx6ull_topeet_emmc_defconfig
修改目标板配置
configs 目录
cd configs/
# 基于 emmc 版本的配置
cp mx6ull_14x14_evk_emmc_defconfig mx6ull_topeet_emmc_defconfig
修改 mx6ull_topeet_emmc_defconfig 配置内容
CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ull_topeet_emmc/imximage.cfg,MX6ULL_EVK_EMMC_REWORK"
CONFIG_ARM=y
CONFIG_ARCH_MX6=y
CONFIG_TARGET_MX6ULL_TOPEET_EMMC=y
CONFIG_CMD_GPIO=y
修改目标板头文件
include/configs 目录
cd include/configs
cp mx6ullevk.h mx6ull_topeet_emmc.h
修改mx6ull_topeet_emmc.h 头文件内容
#ifndef __MX6ULL_TOPEET_EMMC_CONFIG_H
#define __MX6ULL_TOPEET_EMMC_CONFIG_H
增加目标板 menuconfig 配置
arch/arm/cpu/armv7/mx6/Kconfig 目录
cd arch/arm/cpu/armv7/mx6/Kconfig
# 增加如下
config TARGET_MX6ULL_TOPEET_EMMC
bool "Support mx6ull_topeet_emmc"
select MX6ULL
select DM
select DM_THERMAL
# 尾部增加
source "board/freescale/mx6ull_topeet_emmc/Kconfig"
修改驱动
网络驱动修改
开发板使用的 PHY芯片为 KSZ8081,与NXP官方的 I.MX6ULL EVK 相同,所以驱动代码无需修改,但是需要修改 KSZ8081 reset 引脚和 PHY 芯片器件地址;
- PHY芯片器件地址修改
mx6ull_topeet_emmc.h 中修改如下
#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 // 网口0的PHY器件地址(根据原理图确定)
#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 // 网口1的PHY器件地址(根据原理图确定)
#define CONFIG_FEC_XCV_TYPE RMII
#endif
- PHY芯片复位引脚修改
I.MX6ULL EVK 上复位引脚使用 74LV595 扩展后的引脚控制,开发板则是直接到芯片的如下引脚上,所以需要修改引脚配置和复位控制相关代码:
信号 | 引脚 |
---|---|
ENET1_RST | SNVS_TAMPER7/GPIO5_IO07 |
ENET2_RST | SNVS_TAMPER8/GPIO5_IO08 |
mx6ull_topeet_emmc.c 中修改如下:
2.1 引脚定义修改
/* 注释以下代码 */
#if 0
#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)
static iomux_v3_cfg_t const iox_pads[] = {
/* IOX_SDI */
MX6_PAD_BOOT_MODE0__GPIO5_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL),
/* IOX_SHCP */
MX6_PAD_BOOT_MODE1__GPIO5_IO11 | MUX_PAD_CTRL(NO_PAD_CTRL),
/* IOX_STCP */
MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),
/* IOX_nOE */
MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),
};
#endif
/* 增加如下复位引脚定义,原理图确认 */
#define ENET1_RESET IMX_GPIO_NR(5, 7)
#define ENET2_RESET IMX_GPIO_NR(5, 8)
2.2. 删除 74LV595 相关控制代码
// 删除如下两个函数
static void iox74lv_init(void)
void iox74lv_set(int index)
// 修改如下函数
int board_init(void)
{
......
// 删除如下两行
imx_iomux_v3_setup_multiple_pads(iox_pads, ARRAY_SIZE(iox_pads));
iox74lv_init();
......
}
2.3. 增加直连复位引脚配置
static iomux_v3_cfg_t const fec1_pads[] = {
......
/* Lux: 以太网口1 PHY 芯片复位脚默认配置 */
MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),
};
static iomux_v3_cfg_t const fec2_pads[] = {
......
/* Lux: 以太网口2 PHY 芯片复位脚默认配置 */
MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),
};
/* PHY 芯片初始化后,增加复位操作 */
static void setup_iomux_fec(int fec_id)
{
if (fec_id == 0) {
imx_iomux_v3_setup_multiple_pads(fec1_pads,
ARRAY_SIZE(fec1_pads));
/* 以太网口1 PHY 芯片复位 */
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));
/* 以太网口2 PHY 芯片复位 */
gpio_direction_output(ENET2_RESET, 1);
gpio_set_value(ENET2_RESET, 0);
mdelay(20);
gpio_set_value(ENET2_RESET, 1);
}
}
编译测试
添加环境变量
修改根目录下makefile,增加如下宏指定芯片架构和编译链前缀
# set default to nothing for native builds
ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
endif
# Lux: 添加环境变量
ARCH = arm
CROSS_COMPILE = arm_linux_gnueabihf-
KCONFIG_CONFIG ?= .config
export KCONFIG_CONFIG
开始编译
make distclean
make mx6ull_topeet_emmc_defconfig
make
编译产物
编译后的产物 u-boot.imx 位于根目录下,u-boot.bin 是二进制的文件格式,u-boot.imx 是增加了特定头部信息的 imx 专用格式。
问题解决
链接问题
> arm-linux-gnueabihf-ld.bfd:u-boot.lds:1: syntax error
出现以上问题,是因为 -ansi 选项无法处理注释符号 "//", 全部修改为 "/**/" 就好了。
库文件缺失
安装如下库文件即可
sudo apt-get install lzop
sudo apt-get install u-boot-tools
sudo apt-get install libncurses5-dev
uboot版本号显示问题
uboot 版本号总是显示 dirty,这个问题主要是由于我们开启了 LOCALVERSION_AUTO 选项导致在编译的时候通过git 检测到有改动未提交导致的,只需要在根目录的 Kconfig 修改如下
config LOCALVERSION_AUTO
bool "Automatically append version information to the version string"
default n #此处的y修改成 n
效果呈现
# 修改前
U-Boot 2016.03-g4708f7f-dirty (Jul 17 2023 - 16:15:08 +0800)
# 修改后
U-Boot 2016.03
显示 Error: FEC1 address not set
主要是由于没有设置 ip 相关信息导致,有如下两种方法解决
- mx6ull_topeet_emmc.h 头文件中添加 ip 地址信息
#ifdef CONFIG_CMD_NET
...
#define CONFIG_IPADDR 192.168.31.55
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_GATEWAYIP 192.168.31.1
#define CONFIG_SERVERIP 192.168.31.50
#define CONFIG_ETHADDR 00:04:9f:04:d2:35
....
- uboot 启动后命令行中设置环境变量
setenv ipaddr 192.168.31.55
setenv gatewayip 192.168.31.1
setenv netmask 255.255.255.0
setenv serverip 192.168.31.50
setenv ethaddr 00:04:9f:04:d2:35
saveenv
设备端 ping 主机时出现 data abort 重启
> ping 192.168.1.170
Using FEC1 device
data abort
pc : [<9ff83da8>] lr : [<9ff85078>]
reloc pc : [<8783bda8>] lr : [<8783d078>]
sp : 9ef45d08 ip : 00000055 fp : 9ff535f0
r10: 00000002 r9 : 9ef45eb8 r8 : 00000000
r7 : 00000001 r6 : 00000000 r5 : 0000002a r4 : 9ffed4ce
r3 : 14000045 r2 : 8c01a8c0 r1 : aa01a8c0 r0 : 9ffed4ce
Flags: nZCv IRQs off FIQs off Mode SVC_32
Resetting CPU ...
resetting ...
此问题的根本原因是 imx6ull 在 /arch/arm/cpu/armv7/start.S 下启动了内存地址对齐错误的检查功能,而我们使用的 7.5.0 版本的编译链缺在编译后存在地址不对齐的情况,所以执行地址非对齐指令时就是会报 data abort 异常;
针对以上问题可以降低编译链版本,但是可能有其他编译链问题,或者是修改 start.S 代码支持不进行地址对齐检查(ARMv7上可以支持地址非对齐访问),修改 /arch/arm/cpu/armv7/start.S 如下代码:
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
/* orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align */
/* 关闭地址对齐检查 */
orr r0, r0, #0x00000000 @ set bit 0 (--A-) Align
orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
bootz、bootm 和 boot
内核的启动必须要使用相关命令来进行,要启动 Linux,需要先将 Linux 镜像文件拷贝到 DRAM 中,如果使用到设备树的话也需要将设备树拷贝到 DRAM 中。可以从 EMMC 或者 NAND 等存储设备中将 Linux 镜像和设备树文件拷贝到 DRAM,也可以通过 nfs 或者 tftp 将 Linux 镜像文件和设备树文件下载到 DRAM 中。不管用那种方法,只要能将 Linux 镜像和设备树文件存到 DRAM 中就行,然后使用 bootz 命令来启动, bootz 命令用于启动 zImage 镜像文件
bootz命令
bootz 用于启动 zImage 镜像文件
# addr 是 uImage 在 DRAM 中的首地址, initrd 是 initrd 的地址, fdt 是设备树(.dtb)文件在 DRAM 中的首地址, initrd 为空使用"-"替代
bootz [addr [initrd[:size]] [fdt]]
#例如:
bootz 80800000 - 83000000
bootm命令
bootm 用于启动 uImage 镜像文件
- 不使用设备树的话启动 Linux 内核的命令如下:
# addr 是 uImage 镜像在 DRAM 中的首地址
bootm addr
- 使用设备树命令格式如下:
# addr 是 uImage 在 DRAM 中的首地址, initrd 是 initrd 的地址, fdt 是设备树(.dtb)文件在 DRAM 中的首地址, initrd 为空使用"-"替代
bootm [addr [initrd[:size]] [fdt]]
\#例如:
bootm 80800000 - 83000000
boot命令
以上两命令是在把 kernel image 和 dtb 设备树存放在指定的 DRAM 后通过指定地址进行启动,而 boot 会读取环境变量 bootcmd 来启动 Linux 系统, bootcmd 是一个很重要的环境变量!包括了“引导”和“命令两个部分”,如下所命令是在 uboot 命令行下保存的 bootcmd 环境变量,boot 先读取 bootcmd 并执行,来通过 tftp 直接把 zImage 和 dtb 设备树下载到了指定的 DRAM 地址上,然后再进行地址跳转引导:
# bootcmd 环境变量设置
setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-evk.dtb; bootz 80800000 - 83000000'
saveenv
# boot 启动
boot
2.6 bootcmd分析
boot 所作的事情总结如下:
mmc dev 1 # 切换到 EMMC
fatload mmc 1:1 0x80800000 zImage # 读取 zImage 到 0x80800000 处
fatload mmc 1:1 0x83000000 imx6ull-14x14-evk.dtb # 读取设备树到 0x83000000 处
bootz 0x80800000 - 0x83000000 # 启动 Linux