环境准备

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

修改目标板内容

  1. 修改 mx6ull_topeet_emmc.c
cd mx6ull_topeet_emmc/

mv mx6ullevk.c mx6ull_topeet_emmc.c
  1. 修改 makefile
obj-y := mx6ull_topeet_emmc.o
  1. 修改 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
  1. 修改 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
  1. 修改 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 芯片器件地址;

  1. 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
  1. PHY芯片复位引脚修改

I.MX6ULL EVK 上复位引脚使用 74LV595 扩展后的引脚控制,开发板则是直接到芯片的如下引脚上,所以需要修改引脚配置和复位控制相关代码:

信号引脚
ENET1_RSTSNVS_TAMPER7/GPIO5_IO07
ENET2_RSTSNVS_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 相关信息导致,有如下两种方法解决

  1. 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
....
  1. 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 镜像文件

  1. 不使用设备树的话启动 Linux 内核的命令如下:
# addr 是 uImage 镜像在 DRAM 中的首地址
bootm addr
  1. 使用设备树命令格式如下:
# 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
文章作者: 路西法
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 以梦为马
imx6ull imx6ull Linux
喜欢就支持一下吧