<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)

<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)

第一部分链接:系统移植-之-uboot移植第一部分

第一部分主要讲解了,uboot移植过程中使用的一些工具的安装,以及测试nxp远程uboot,是否能再正点原子Linux开发板正常运行。

本部分主要讲的是,将nxp原厂uboot,移植,适配到正点原子的linux开发板上。由于NXP官方demo板和正点原子linux开发板,在硬件上存在差异,所以,需要修改NXP原厂uboot的一些内容,才能适配正点原子linux开发板,这个过程叫移植。接下来就讲解,作者移植过程的一些记录。

本文参考正点原子linux驱动开发手册,如有不详之处,可查找正点原子官方资料,或联系作者。
联系方式QQ:759521350

一、移植开发板对应的文件
在第一部分操作后,开始移植到正点原子linux开发板,主要涉及部分文件内容的修改等,具体如。
1、将第一部分解压后的文件夹复制一份,并命令“uboot_onefu”,如下图:
命令:cp uboot-imx-rel_imx_4.1.15_2.1.0_ga uboot_onefu -rf
在这里插入图片描述
2、添加开发板默认配置文件
进入“uboot_onefu/configs”,复制“mx6ull_14x14_evk_emmc_defconfig”,并将复制后的文件命名为“mx6ull_onefu_emmc_defconfig”。相关命令如下:

cd uboot_onefu/configs/
cp mx6ull_14x14_evk_emmc_defconfig  mx6ull_onefu_emmc_defconfig

在这里插入图片描述
将“mx6ull_onefu_emmc_defconfig”中的内容修改成下面的:

CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ull_onefu_emmc/imximage.cfg,MX6ULL_EVK_EMMC_REWORK"
CONFIG_ARM=y
CONFIG_ARCH_MX6=y
CONFIG_TARGET_MX6ULL_ONEFU_EMMC=y
CONFIG_CMD_GPIO=y

可看出,只对第1行和第4行做了修改。

3、添加开发板对应的头文件
在目录“include/configs”下添加使用的Linux开发板对应的头文件。进入“uboot_onefu/include/configs”,复制“mx6ullevk.h”,并将复制后的文件命名为“mx6ull_onefu_emmc.h”。相关命令如下:

 cd include/configs/
 cp mx6ullevk.h  mx6ull_onefu_emmc.h

在这里插入图片描述
将“mx6ull_onefu_emmc.h”中的:

#ifndef __MX6ULLEVK_CONFIG_H
#define __MX6ULLEVK_CONFIG_H

更改为:

#ifndef __MX6ULL_ONEFU_EMMC_CONFIG_H
#define __MX6ULL_ONEFU_EMMC_CONFIG_H

mx6ull_onefu_emmc.h里面有很多宏定义,这些宏定义基本用于配置uboot,也有一些I.MX6ULL的配置项目。如果我们自己要想使能或者禁止uboot的某些功能,那就在mx6ull_onefu_emmc.h里面做修改即可。mx6ull_onefu_emmc.h里面的内容比较多,可以去掉一些用不到的配置;这里作者不做删减。

4、添加开发板对应的板极文件夹
boot中每个板子都有一个对应的文件夹来存放板级文件,比如开发板上外设驱动文件等等。NXP的I.MX系列芯片的所有板级文件夹都存放在board/freescale目录下,在这个目录下有个名为mx6ullevk的文件夹,这个文件夹就是NXP官方I.MX6ULL EVK开发板的板级文件夹。复制mx6ullevk,将其重命名为mx6ull_onefu_emmc,命令如下:

cd board/freescale/
cp mx6ullevk/ -r mx6ull_onefu_emmc

在这里插入图片描述
接下来对“mx6ull_onefu_emmc”目录下的文件进行一些修改
(1)进入“mx6ull_onefu_emmc”将其中的mx6ullevk.c文件重命名为mx6ull_onefu_emmc.c,命令如下:

cd board/freescale/mx6ull_onefu_emmc/
mv mx6ullevk.c  mx6ull_onefu_emmc.c

在这里插入图片描述

(2)修改该目录下的Makefile文件,修改后如下:

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

obj-y  := mx6ull_onefu_emmc.o

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

<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第1张图片
重点是第6行的obj-y,改为mx6ull_onefu_emmc.o,这样才会编译mx6ull_onefu_emmc.c这个文件。

(3)修改mx6ull_onefu_emmc目录下的imximage.cfg文件
将imximage.cfg中的下面一句:
PLUGIN board/freescale/mx6ullevk/plugin.bin 0x00907000
更改为:
PLUGIN board/freescale/mx6ull_onefu_emmc /plugin.bin 0x00907000
<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第2张图片
(4)修改mx6ull_onefu_emmc目录下的Kconfig文件
修改Kconfig文件,修改后的内容如下:

if TARGET_MX6ULL_ONEFU_EMMC

config SYS_BOARD
	default "mx6ull_onefu_emmc"

config SYS_VENDOR
	default "freescale"

config SYS_SOC 
	default "mx6"

config SYS_CONFIG_NAME
	default "mx6ull_onefu_emmc"

endif

<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第3张图片
(5)修改mx6ull_onefu_emmc目录下的MAINTAINERS文件
修改MAINTAINERS文件,修改后的内容如下:

MX6ULL_ONEFU_EMMC BOARD
M:	Peng Fan <peng.fan@nxp.com>
S:	Maintained
F:	board/freescale/mx6ull_onefu_emmc/
F:	include/configs/mx6ull_onefu_emmc.h

<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第4张图片
5、修改U-Boot图形界面配置文件
uboot是支持图形界面配置,修改文件arch/arm/cpu/armv7/mx6/Kconfig(如果用的I.MX6UL的话,应该修改arch/arm/Kconfig这个文件),在207行加入如下内容:

config TARGET_MX6ULL_ONEFU_EMMC
	bool "Support mx6ull_onefu_emmc"
	select MX6ULL
	select DM
	select DM_THERMAL

<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第5张图片
末尾添加如下:

source "board/freescale/mx6ull_onefu_emmc/Kconfig"

<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第6张图片

至此,开发板对应名称的相关文件的已经添加到uboot中了,加下来编译这个新添加的开发板的uboot。

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

在uboot的跟目录“uboot_onefu”下创建名为mx6ull_onefu_emmc.sh的shell脚本,内容如下:

#!/bin/bash
#清理工程
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
#执行配置uboot
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_onefu_emmc_defconfig
#执行编译
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16

#注1: ARCH=arm  设置目标为arm架构
#注2: CROSS_COMPILE=arm-linux-gnueabihf-    指定编译工具链前缀
#注3: V=1  用于设置编译过程的信息输出级别
#注4-j16 使用16核编译uboot

<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第7张图片
给予“mx6ull_onefu_emmc.sh”,相关权限,并运行脚本,命令如下:

chmod 777 mx6ull_onefu_emmc.sh  //给予可执行权限
./mx6ull_onefu_emmc.sh			//运行脚本,编译uboot

编译结果如下:
在这里插入图片描述
7、烧写入SD卡,运行测试
烧写如下:
<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第8张图片
开发板设置为SD卡启动,将SD卡插入开发板,打开CRT,按下复位键,显示如下:
<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第9张图片
可以看出,此时的Board还是“MX6ULL 14x14 EVK”,因为我们参考的NXP官方的I.MX6ULL开发板来添加自己的开发板。如果接了LCD屏幕的话会发现LCD屏幕并没有显示NXP的logo,而且从图33.2.5.1可以看出此时的网络同样也没识别出来。前面已经说了,默认uboot中的LCD驱动和网络驱动在正点原子的I.MX6U-ALPHA开发板上是有问题的,因为NXP官方的开发板和正点原子的开发板,部分电路不同,所以需要修改。
接下来就是修改。

二、移植修改开发板对应的文件
1、LCD驱动修改
一般uboot中修改驱动基本都是在xxx.h和xxx.c这两个文件中进行的,xxx为板子名称,比如mx6ull_onefu_emmc.h和mx6ull_onefu_emmc.c这两个文件。
一般修改LCD驱动重点注意以下几点:
①、LCD所使用的GPIO,查看uboot中LCD的IO配置是否正确。 ②、LCD背光引脚GPIO的配置。
③、LCD配置参数是否正确。
正点原子的I.MX6U-ALPHA开发板LCD原理图和NXP官方I.MX6ULL开发板一致,也就是LCD的IO和背光IO都一样的,所以IO部分就不用修改了。需要修改的之后LCD参数,打开文件"board/freescale/mx6ull_onefu_emmc/mx6ull_onefu_emmc.c",找到"struct display_info_t const displays",修改后如下所示内容:

struct display_info_t const displays[] = {{
	.bus = MX6UL_LCDIF1_BASE_ADDR,
	.addr = 0,
	.pixfmt = 24,
	.detect = NULL,
	.enable	= do_enable_parallel_lcd,
	.mode	= {
		.name			= "TFT7016",
		.xres           = 1024,
		.yres           = 600,
		.pixclock       = 19531,
		.left_margin    = 140,		//HBPD
		.right_margin   = 160,		//HFPD
		.upper_margin   = 20,		//VBPD
		.lower_margin   = 12,		//VFBD
		.hsync_len      = 20,		//HSPW
		.vsync_len      = 3,		//VSPW
		.sync           = 0,
		.vmode          = FB_VMODE_NONINTERLACED
} } };

修改LCD参数时,根据所使用的LCD的具体参数信息修改,详细可参考对应LCD的开发手册。作者的LCD是正点原子配套LCD,7寸、1024x600。

将“include/configs/mx6ull_onefu_emmc.h”中所有的“panel=TFT43AB”更改为“panel=TFT7016”

修改完成以后重新编译一遍uboot并烧写到SD中启动。
重启以后LCD驱动一般就会工作正常了,LCD上回显示NXP的logo。但是有可能会遇到LCD并没有工作,还是黑屏,这是什么原因呢?在uboot命令模式输入“print”来查看环境变量panel的值,会发现panel的值要是TFT43AB(或其他的,反正不是TFT7016)。如下图:
<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第10张图片
这是因为之前有将环境变量保存到EMC中,uboot启动以后会先从EMMC中读取环境变量,如果EMMC中没有环境变量的话才会使用mx6ull_onefu_emmc.h中的默认环境变量。如果EMMC中的环境变量panel不等于TFT7016,那么LCD显示肯定不正常,我们只需要在uboot中修改panel的值为TFT7016即可,在uboot的命令模式下输入如下命令:

setenv panel TFT7016 
saveenv 

上述命令修改环境变量panel为TFT7016,然后保存,重启uboot,此时LCD驱动就工作正常了。如果LCD还是没有正常工作的,那就要检查自己哪里有没有改错,或者还有哪里没有修改。

2、网络驱动修改
正点原子使用的网络芯片和NXP使用的网络芯片不同。详细可查看正点原子Linux开发板原理图。
网络PHY地址修改:
首先修改uboot中的ENET1和ENET2的PHY地址和驱动,打开“include/configs/mx6ull_onefu_emmc.h”这个文件,找到325行处,修改后如下所示:

#ifdef CONFIG_CMD_NET
#define CONFIG_CMD_PING
#define CONFIG_CMD_DHCP
#define CONFIG_CMD_MII
#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          0x0
#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_SMSC
#endif

<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第11张图片
删除uboot中74LV595的驱动代码
uboot中网络PHY芯片地址修改完成以后就是网络复位引脚的驱动修改了,打开"board/freescale/mx6ull_onefu_emmc/mx6ull_onefu_emmc.c",找到如下代码:

#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)

继续在mx6ull_onefu_emmc.c中找到如下代码,然后删除:

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),
};

继续在mx6ull_onefu_emmc.c中找到函数iox74lv_init和iox74lv_set,并将这个两个函数全删除。

static void iox74lv_init(void)
{
	int i;

	gpio_direction_output(IOX_OE, 0);

	for (i = 7; i >= 0; i--) {
		gpio_direction_output(IOX_SHCP, 0);
		gpio_direction_output(IOX_SDI, seq[qn_output[i]][0]);
		udelay(500);
		gpio_direction_output(IOX_SHCP, 1);
		udelay(500);
	}
......省略
	/*
	 * shift register will be output to pins
	 */
	gpio_direction_output(IOX_STCP, 1);
};

void iox74lv_set(int index)
{
	int i;

	for (i = 7; i >= 0; i--) {
		gpio_direction_output(IOX_SHCP, 0);

		if (i == index)
			gpio_direction_output(IOX_SDI, seq[qn_output[i]][0]);
		else
			gpio_direction_output(IOX_SDI, seq[qn_output[i]][1]);
		udelay(500);
		gpio_direction_output(IOX_SHCP, 1);
		udelay(500);
	}
......省略
	/*
	  * shift register will be output to pins
	  */
	gpio_direction_output(IOX_STCP, 1);
};

在mx6ull_onefu_emmc.c中找到board_init函数,此函数是板子初始化函数,会被board_init_r调用,board_init函数内容如下:

int board_init(void)
{
	/* Address of boot parameters */
	gd->bd->bi_boot_params = PHYS_SDRAM + 0x100;

	imx_iomux_v3_setup_multiple_pads(iox_pads, ARRAY_SIZE(iox_pads));

	iox74lv_init();

#ifdef CONFIG_SYS_I2C_MXC
	setup_i2c(0, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info1);
#endif

#ifdef	CONFIG_FEC_MXC
	setup_fec(CONFIG_FEC_ENET_DEV);
#endif

#ifdef CONFIG_USB_EHCI_MX6
	setup_usb();
#endif

#ifdef CONFIG_FSL_QSPI
	board_qspi_init();
#endif

#ifdef CONFIG_NAND_MXS
	setup_gpmi_nand();
#endif

	return 0;
}

删除imx_iomux_v3_setup_multiple_pads(iox_pads, ARRAY_SIZE(iox_pads)); 和iox74lv_init();

至此,mx6ull_onefu_emmc.c中关于74LV595芯片的驱动代码都删除掉了。

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

在mx6ull_onefu_emmc.c中找到修改后如下代码所示:

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),
};

<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第12张图片
继续在文件mx6ull_onefu_emmc.c中找到函数setup_iomux_fec,修改后如下所示:

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);
	}
}

<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第13张图片
修改drivers/net/phy/phy.c文件中的函数genphy_update_link
uboot中的LAN8720A驱动有点问题,打开文件drivers/net/phy/phy.c,找到函数genphy_update_link,这是个通用PHY驱动函数,此函数用于更新PHY的连接状态和速度。使用LAN8720A的时候需要在此函数中添加一些代码,修改后的函数genphy_update_link如下所示:

int genphy_update_link(struct phy_device *phydev)
{
	unsigned int mii_reg;

#ifdef CONFIG_PHY_SMSC
	static int lan8720_flag = 0;
	int bmcr_reg = 0;
	if (lan8720_flag == 0) {
		bmcr_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
		phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
		while(phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR) & 0X8000) {
			udelay(100);
		}
		phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr_reg); 
		lan8720_flag = 1;
	}
#endif
	/*
	 * Wait if the link is up, and autonegotiation is in progress
	 * (ie - we're capable and it's not done)
	 */
	mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
	.....省略
}

至此 网络驱动复位引脚驱动修改完成。
修改完成以后重新编译一遍uboot并烧写到SD中启动。
测试
开发板链接电脑,并打开CRT,将开发板设置为SD启动,将SD卡插入开发板,并按下复位键,当CRT倒计时时,按下回车,使开发板运行在boot状态。
然后输入一下内容:

 setenv ipaddr 192.168.1.145		//设置本地开发板IP
 setenv ethaddr 00:04:9f:04:2d:35	//设置MAC地址	
 setenv gatewayip 192.168.1.1    	//设置网关IP
 setenv netmask 255.255.255.0		//设置DNS
 setenv serverip 192.168.1.144		//设置服务器IP,这里用的时ubuntu,即虚拟机的IP
 saveenv							//保存环境变量

完成后如下图所示:
<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第14张图片
上图中,红色部分是因为IP输入错误,然后输出提示,可以看出,当输出的IP格式错误时,uboot会有提示错误的功能。

接下来就ping一下主机ubuntu,输入命令:

ping 192.168.1.144

稍等一下,会有下图所示:
在这里插入图片描述
上图有“host 192.168.1.144 is alive”这句,说明ping主机成功,说明ENET2网络工作正常。再来测试一下ENET1的网络是否正常工作,打开mx6ull_onefu_emmc.h,将 CONFIG_FEC_ENET_DEV改为0,然后重新编译一下uboot并烧写到SD卡中重启,重启后设置IP后,重新ping主机,查看输出是否和上面一致,即可验证ENET1的网络了。

3、其它修改
<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第15张图片
开机后进入uboot后,CRT输出的提示有“Board: MX6ULL 14x14 EVK”,这句说的时板子的名称,那我们要更改为我们的开发板的名称。
打开"board/freescale/mx6ull_onefu_emmc/mx6ull_onefu_emmc.c",找到函数“checkboard”,将其内容更改后如下:

int checkboard(void)
{
	if (is_mx6ull_9x9_evk())
		puts("Board: MX6ULL 9x9 EVK\n");
	else
		puts("Board: MX6ULL ONEFU EMMC\n");

	return 0;
}

修改完成以后重新编译uboot并烧写到SD卡中验证,uboot启动信息如下图所示:
<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第16张图片
至此uboot的驱动部分就修改完成了,uboot移植也完成了,uboot的最终目的就是启动Linux内核,所以需要通过启动Linux内核来判断uboot移植是否成功。在启动Linux内核之前我们先来学习两个重要的环境变量bootcmd和bootargs。
注:以上引用自“【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.1.pdf”
三、bootcmd和bootargs环境变量
uboot中有两个非常重要的环境变量bootcmd和bootargs,接下来看一下这两个环境变量。bootcmd和bootagrs是采用类似shell脚本语言编写的,里面有很多的变量引用,这些变量其实都是环境变量,有很多是NXP自己定义的。文件mx6ull_alientek_emmc.h中的宏CONFIG_EXTRA_ENV_SETTINGS保存着这些环境变量的默认值。宏CONFIG_EXTRA_ENV_SETTINGS是个条件编译语句,使用NAND和EMMC的时候宏CONFIG_EXTRA_ENV_SETTINGS的值是不同的。
1、环境变量bootcmd
bootcmd保存着uboot默认命令,uboot倒计时结束以后就会执行bootcmd中的命令。这些命令一般都是用来启动Linux内核的,比如读取EMMC或者NAND Flash中的Linux内核镜像文件和设备树文件到DRAM中,然后启动Linux内核。可以在uboot启动以后进入命令行设置bootcmd环境变量的值。如果EMMC或者NAND中没有保存bootcmd的值,那么uboot就会使用默认的值,板子第一次运行uboot的时候都会使用默认值来设置bootcmd环境变量。相关默认值在文件“include/env_default.h”。
文件中指定了很多环境变量的默认值,比如bootcmd的默认值就是CONFIG_BOOTCOMMAND,bootargs的默认值就是CONFIG_BOOTARGS。我们可以在mx6ull_onefu_emmc.h文件中通过设置宏CONFIG_BOOTCOMMAND来设置bootcmd的默认值。NXP官方设置的CONFIG_BOOTCOMMAND值如下:

#define CONFIG_BOOTCOMMAND \
	   "run findfdt;" \
	   "mmc dev ${mmcdev};" \
	   "mmc dev ${mmcdev}; if mmc rescan; then " \
		   "if run loadbootscript; then " \
			   "run bootscript; " \
		   "else " \
			   "if run loadimage; then " \
				   "run mmcboot; " \
			   "else run netboot; " \
			   "fi; " \
		   "fi; " \
	   "else run netboot; fi"
#endif

<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第17张图片

第205行,run findfdt;使用的是uboot的run命令来运行findfdt,findfdt是NXP自行添加的环境变量。findfdt是用来查找开发板对应的设备树文件(.dtb)。IMX6ULL EVK的设备树文件为imx6ull-14x14-evk.dtb,findfdt内容如下:

"findfdt="\ 
"if test $fdt_file = undefined; then " \ 
"if test $board_name = EVK && test $board_rev = 9X9; then " \  
		"setenv fdt_file imx6ull-9x9-evk.dtb; fi; " \  
	"if test $board_name = EVK && test $board_rev = 14X14; then " \  
		"setenv fdt_file imx6ull-14x14-evk.dtb; fi; " \ 
 "if test $fdt_file = undefined; then " \  
 		"echo WARNING: Could not determine dtb to use; fi; " \ 
 "fi;\0" \ 

findfdt里面用到的变量有fdt_file,board_name,board_rev,这三个变量内容如下:

fdt_file=undefined,board_name=EVK,board_rev=14X14

findfdt做的事情就是判断,fdt_file是否为undefined,如果fdt_file为undefined的话那就要根据板子信息得出所需的.dtb文件名。此时fdt_file为undefined,所以根据board_name和board_rev来判断实际所需的.dtb文件,如果board_name为EVK并且board_rev=9x9的话fdt_file就为imx6ull-9x9-evk.dtb。如果board_name为EVK并且board_rev=14x14的话fdt_file就设置为imx6ull-14x14-evk.dtb。因此IMX6ULL EVK板子的设备树文件就是imx6ull-14x14-evk.dtb, 因此run findfdt的结果就是设置fdt_file为imx6ull-14x14-evk.dtb。
第206行,mmc dev ${mmcdev}用于切换mmc设备,mmcdev为1,因此这行代码就是:mmc dev 1,也就是切换到EMMC上。
第207行,先执行mmc dev ${mmcdev}切换到EMMC上,然后使用命令mmc rescan扫描看有没有SD卡或者EMMC存在,如果没有的话就直接跳到216行,执行run netboot,netboot也是一个自定义的环境变量,这个变量是从网络启动Linux的。如果mmc设备存在的话就从mmc设备启动。
第208行,运行loadbootscript环境变量,此环境变量内容如下:

loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script}; 

其中mmcdev=1,mmcpart=1,loadaddr=0x80800000,script= boot.scr,因此展开以后就是:

loadbootscript=fatload mmc 1:1 0x80800000 boot.scr; 

loadbootscript就是从mmc1的分区1中读取文件boot.src到DRAM的0X80800000处。但是mmc1的分区1中没有boot.src这个文件,可以使用命令“ls mmc 1:1”查看一下mmc1分区1中的所有文件,看看有没有boot.src这个文件。
第209行,如果加载boot.src文件成功的话就运行bootscript环境变量,bootscript的内容如下:

bootscript=echo Running bootscript from mmc ...; 
source

因为boot.src文件不存在,所以bootscript也就不会运行。
第211行,如果loadbootscript没有找到boot.src的话就运行环境变量loadimage,环境变量loadimage内容如下:

loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image} 

其中mmcdev=1,mmcpart=1,loadaddr=0x80800000,image = zImage,展开以后就是:

loadimage=fatload mmc 1:1 0x80800000 zImage 

可以看出loadimage就是从mmc1的分区中读取zImage到内存的0X80800000处,而mmc1的分区1中存在zImage。
第212行,加载linux镜像文件zImage成功以后就运行环境变量mmcboot,否则的话运行netboot环境变量。mmcboot环境变量如下:

"mmcboot=echo Booting from mmc ...; " \
		"run mmcargs; " \
		"if test ${boot_fdt} = yes || test ${boot_fdt} = try; then " \
			"if run loadfdt; then " \
				"bootz ${loadaddr} - ${fdt_addr}; " \
			"else " \
				"if test ${boot_fdt} = try; then " \
					"bootz; " \
				"else " \
					"echo WARN: Cannot load the DT; " \
				"fi; " \
			"fi; " \
		"else " \
			"bootz; " \
		"fi;\0" \

<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第18张图片
第154行,输出信息“Booting from mmc …”。
第155行,运行环境变量mmcargs,mmcargs用来设置bootargs。
第156行,判断boot_fdt是否为yes或者try,根据uboot输出的环境变量信息可知boot_fdt=try。因此会执行157行的语句。
第157行,运行环境变量loadfdt,环境变量loadfdt定义如下:

loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}

展开以后就是:

loadfdt=fatload mmc 1:1 0x83000000 imx6ull-14x14-evk.dtb 

因此loadfdt的作用就是从mmc1的分区1中读取imx6ull-14x14-evk.dtb文件并放到0x83000000处。
第158行,如果读取.dtb文件成功的话那就调用命令bootz启动linux,调用方法如下:

bootz ${loadaddr} - ${fdt_addr}; 

展开就是:

bootz 0x80800000 - 0x83000000 (注意‘-’前后要有空格) 

至此Linux内核启动,如此复杂的设置就是为了从EMMC中读取zImage镜像文件和设备树文件。经过分析,浓缩出来的仅仅是4行精华:

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

NXP官方将CONFIG_BOOTCOMMAND写的这么复杂只有一个目的:为了兼容多个板子,所以写了个很复杂的脚本。当我们明确知道我们所使用的板子的时候就可以大幅简化宏CONFIG_BOOTCOMMAND的设置,比如我们要从EMMC启动,那么宏CONFIG_BOOTCOMMAND就可简化为:

#define CONFIG_BOOTCOMMAND \  
	"mmc dev 1;" \  
	"fatload mmc 1:1 0x80800000 zImage;" \  
	"fatload mmc 1:1 0x83000000 imx6ull-onefu-emmc.dtb;" \  
	"bootz 0x80800000 - 0x83000000;"

或者可以直接在uboot中设置bootcmd的值,这个值就是保存到EMMC中的,命令如下:

etenv bootcmd 'mmc dev 1; fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-onefu-emmc.dtb; bootz 80800000 - 83000000;' 

2、环境变量bootargs
bootargs保存着uboot传递给Linux内核的参数,在上一小节讲解bootcmd的时候说过,bootargs环境变量是由mmcargs设置的,mmcargs环境变量如下:

mmcargs=setenv bootargs console=${console},${baudrate} root=${mmcroot} 

其中console=ttymxc0,baudrate=115200,mmcroot=/dev/mmcblk1p2 rootwait rw,因此将mmcargs展开以后就是:

mmcargs=setenv bootargs console= ttymxc0, 115200 root= /dev/mmcblk1p2 rootwait rw

可以看出环境变量mmcargs就是设置bootargs的值为“console= ttymxc0, 115200 root= /dev/mmcblk1p2 rootwait rw”,bootargs就是设置了很多的参数的值,这些参数Linux内核会使用到。
(1)console用来设置linux终端(或者叫控制台),也就是通过什么设备来和Linux进行交互,是串口还是LCD屏幕?如果是串口的话应该是串口几等等。一般设置串口作为Linux终端,这样我们就可以在电脑上通过SecureCRT来和linux交互了。这里设置console为ttymxc0,因为linux启动以后I.MX6ULL的串口1在linux下的设备文件就是/dev/ttymxc0,在Linux下,一切皆文件。
ttymxc0后面有个“,115200”,这是设置串口的波特率,console=ttymxc0,115200综合起来就是设置ttymxc0(也就是串口1)作为Linux的终端,并且串口波特率设置为115200。
(2)root用来设置根文件系统的位置,root=/dev/mmcblk1p2用于指明根文件系统存放在mmcblk1设备的分区2中。EMMC版本的核心板启动linux以后会存在/dev/mmcblk0、/dev/mmcblk1、/dev/mmcblk0p1、/dev/mmcblk0p2、/dev/mmcblk1p1和/dev/mmcblk1p2这样的文件,其中/dev/mmcblkx(x=0n)表示mmc设备,而/dev/mmcblkxpy(x=0n,y=1~n)表示mmc设备x的分区y。在I.MX6U-ALPHA开发板中/dev/mmcblk1表示EMMC,而/dev/mmcblk1p2表示EMMC的分区2。 root后面有“rootwait rw”,rootwait表示等待mmc设备初始化完成以后再挂载,否则的话mmc设备还没初始化完成就挂载根文件系统会出错的。rw表示根文件系统是可以读写的,不加rw的话可能无法在根文件系统中进行写操作,只能进行读操作。
(3)此选项一般配置root一起使用,rootfstype用于指定根文件系统类型,如果根文件系统为ext格式的话此选项无所谓。如果根文件系统是yaffs、jffs或ubifs的话就需要设置此选项,指定根文件系统的类型。 bootargs常设置的选项就这三个。

四、uboot启动Linux测试
测试两种启动Linux内核的方法,一种是直接从EMMC启动,一种是从网络启动。
1、从EMMC启动Linux系统
从EMMC启动也就是将编译出来的Linux镜像文件zImage和设备树文件保存在EMMC中,uboot从EMMC中读取这两个文件并启动,这个是我们产品最终的启动方式。但是目前还没有讲解如何移植linux和设备树文件,以及如何将zImage和设备树文件保存到EMMC中。不过作者拿到手的I.MX6U-ALPHA开发板(EMMC版本)已经将zImage文件和设备树文件烧写到了EMMC中,所以我们可以直接读取来测试(如果没有看下一小节,通过网络启动,等移植好linux和设备树,在测试也可)。先检查一下EMMC的分区1中有没有zImage文件和设备树文件,输入命令“ls mmc 1:1”,结果如下图所示:
<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第19张图片
从上图中可以看出,此时EMMC分区1中存在zimage和imx6ull-alientek-emmc.dtb这两个文件,所以我们可以测试新移植的uboot能不能启动linux内核。设置bootargs和bootcmd这两个环境变量,这里作者,借用正点原子已有的linux和设备树,后续,等讲解到linux和设备树的移植后,再测试一边。

以下是通过进入uboot后,再设置环境变量中的bootargs和bootcmd的方式,也可以在上一节的uboot源码中修改,并重新编译uboot。

进入uboot后,环境变量设置如下:

setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
setenv bootcmd 'mmc dev 1; fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000;' 
saveenv

在这里插入图片描述
设置好以后直接输入boot,或者run bootcmd即可启动Linux内核,如果Linux内核启动成功的话就会输出如下图所示的启动信息:
<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第20张图片
2、从EMMC启动Linux系统
从网络启动linux系统的唯一目的就是为了调试!不管是为了调试linux系统还是linux下的驱动。每次修改linux系统文件或者linux下的某个驱动以后都要将其烧写到EMMC中去测试,这样太麻烦了。我们可以设置linux从网络启动,也就是将linux镜像文件和根文件系统都放到Ubuntu下某个指定的文件夹中,这样每次重新编译linux内核或者某个linux驱动以后只需要使用cp命令将其拷贝到这个指定的文件夹中即可,这样就不用需要频繁的烧写EMMC,这样就加快了开发速度。我们可以通过nfs或者tftp从Ubuntu中下载zImage和设备树文件,根文件系统的话也可以通过nfs挂载,不过本小节我们不讲解如何通过nfs挂载根文件系统,这个在讲解根文件系统移植的时候再讲解。这里我们使用tftp从Ubuntu中下载zImage和设备树文件,前提是要将zImage和设备树文件放到Ubuntu下的tftp目录中。
ubuntu中tftp相关使用可自行百度,或参考正点原子linux开发板教程,或参考tftp。

设置bootargs和bootcmd这两个环境变量,设置如下:

setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw' 
setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000' 
saveenv 
boot

一开始是通过tftp下载zImage和imx6ull-alientek-emmc.dtb这两个文件,然后启动linux,过程如下图所示:
<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)_第21张图片
五、结语
uboot移植到此结束,简单总结一下uboot移植的过程:
①、不管是购买的开发板还是自己做的开发板,基本都是参考半导体厂商的dmeo板,而半导体厂商会在他们自己的开发板上移植好uboot、linux kernel和rootfs等,最终制作好BSP包提供给用户。我们可以在官方提供的BSP包的基础上添加我们的板子,也就是俗称的移植。
②、我们购买的开发板或者自己做的板子一般都不会原封不动的照抄半导体厂商的demo板,都会根据实际的情况来做修改,既然有修改就必然涉及到uboot下驱动的移植。
③、一般uboot中需要解决串口、NAND、EMMC或SD卡、网络和LCD驱动,因为uboot的主要目的就是启动Linux内核,所以不需要考虑太多的外设驱动。
④、在uboot中添加自己的板子信息,根据自己板子的实际情况来修改uboot中的驱动。

以上内容均参考正点原子linux开发板配套资料,如有不足,遗漏,错误之处,可联系作者,进一步改善。
如若获取更详细的资料可到正点原子官方网址下载。

你可能感兴趣的:(Linux,uboot,linux系统移植,linux,uboot)