U-Boot 移植

目录

  • NXP 官方开发板uboot 编译测试
    • 查找NXP 官方的开发板默认配置文件
    • 编译NXP 官方开发板对应的uboot
    • 烧写验证与驱动测试
  • 在U-Boot 中添加自己的开发板
    • 添加开发板默认配置文件
    • 添加开发板对应的头文件
    • 添加开发板对应的板级文件夹
    • 修改U-Boot 图形界面配置文件
    • 使用新添加的板子配置编译uboot
    • LCD 驱动修改
    • V2.4 版本以前底板网络驱动修改
    • V2.4 及以后版本底板网络驱动修改
    • 其他需要修改的地方
  • bootcmd 和bootargs 环境变量
    • 环境变量bootcmd
    • 环境变量bootargs
  • uboot 启动Linux 测试
    • 从EMMC 启动Linux 系统
    • 从网络启动Linux 系统

上一章节我们详细的分析了uboot 的启动流程,对uboot 有了一个初步的了解。前两章我们都是使用的正点原子提供的uboot,本章我们就来学习如何将NXP 官方的uboot 移植到正点原子的I.MX6ULL 开发板上,学习如何在uboot 中添加我们自己的板子。

NXP 官方开发板uboot 编译测试

查找NXP 官方的开发板默认配置文件

uboot 的移植并不是说我们完完全全的从零开始将uboot 移植到我们现在所使用的开发板或者开发平台上。这个对于我们来说基本是不可能的,这个工作一般是半导体厂商做的,半导体厂商负责将uboot 移植到他们的芯片上,因此半导体厂商都会自己做一个开发板,这个开发板就叫做原厂开发板,比如大家学习STM32 的时候听说过的discover 开发板就是ST 自己做的。
半导体厂商会将uboot 移植到他们自己的原厂开发板上,测试好以后就会将这个uboot 发布出去,这就是大家常说的原厂BSP 包。我们一般做产品的时候就会参考原厂的开发板做硬件,然后在原厂提供的BSP 包上做修改,将uboot 或者linux kernel 移植到我们的硬件上。这个就是uboot 移植的一般流程:
①、在uboot 中找到参考的开发平台,一般是原厂的开发板。
②、参考原厂开发板移植uboot 到我们所使用的开发板上。
正点原子的I.MX6ULL 开发板参考的是NXP 官方的I.MX6ULL EVK 开发板做的硬件,因此我们在移植uboot 的时候就可以以NXP 官方的I.MX6ULL EVK 开发板为蓝本。
本章我们是将NXP 官方的uboot 移植到正点原子的I.MX6ULL 开发板上,NXP 官方的uboot 放到了开发板光盘中,路径为:1、例程源码->4、NXP 官方原版Uboot 和Linux->uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2。将uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2 发送到Ubuntu中并解压,然后创建VSCode 工程。
在移植之前,我们先编译一下NXP 官方I.MX6ULL EVK 开发板对应的uboot,首先是配置uboot,configs 目录下有很多跟I.MX6UL/6ULL 有关的配置如图33.1.1.1 所示,
U-Boot 移植_第1张图片
图33.1.1.1 NXP 官方I.MX6UL/6ULL 默认配置文件
从图33.1.1.1 可以看出有很多的默认配置文件,其中以mx6ul 开头的是I.MX6UL 芯片的,mx6ull 开头的是I.MX6ULL 开发板的。I.MX6UL/6ULL 有9x9mm 和14x14mm 两种尺寸的,所以我们可以看到会有mx6ull_9x9 和mx6ull_14x14 开头的默认配置文件。我们使用的是14x14mm的芯片,所以关注mx6ull_14x14 开头的默认配置文件。正点原子的I.MX6ULL 有EMMC 和NAND 两个版本的,因此我们最终只需要关注mx6ull_14x14_evk_emmc_defconfig 和mx6ull_14x14_evk_nand_defconfig 这两个配置文件就行了。本章我们讲解EMMC 版本的移植(NAND 版本移植很多类似),所以使用mx6ull_14x14_evk_emmc_defconfig 作为默认配置文件。

编译NXP 官方开发板对应的uboot

找到NXP 官方I.MX6ULL EVK 开发板对应的默认配置文件以后就可以编译一下,使用如下命令编译uboot:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16

编译完成以后结果如图33.1.2.1 所示:
U-Boot 移植_第2张图片
图33.1.2.1 编译结果
从图33.1.2.1 可以看出,编译成功。我们在编译的时候需要输入ARCH 和CORSS_COMPILE这两个变量的值,这样太麻烦了。我们可以直接在顶层Makefile 中直接给ARCH 和CORSS_COMPILE 赋值,修改如图33.1.2.2 所示:
U-Boot 移植_第3张图片

图33.1.2.2 添加ARCH 和CROSS_COMPILE 值
图33.1.2.2 中的250、251 行就是直接给ARCH 和CROSS_COMPILE 赋值,这样我们就可以使用如下简短的命令来编译uboot 了:

make mx6ull_14x14_evk_emmc_defconfig
make V=1 -j16

如果既不想修改uboot 的顶层Makefile,又想编译的时候不用输入那么多,那么就直接创建个shell 脚本就行了,shell 脚本名为mx6ull_14x14_emmc.sh,然后在shell 脚本里面输入如下内容:

示例代码33.1.2.1 mx6ull_14x14_emmc.sh 文件
1 #!/bin/bash
2 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
3 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_emmc_defconfig
4 make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16

记得给mx6ull_14x14_emmc.sh 这个文件可执行权限,使用mx6ull_14x14_emmc.sh 脚本编译uboot 的时候每次都会清理一下工程,然后全部重新编译,编译的时候直接执行这个脚本就行了,命令如下:

./mx6ull_14x14_evk_emmc.sh

编译完成以后会生成u-boot.bin、u-boot.imx 等文件,但是这些文件是NXP 官方I.MX6ULL EVK 开发板。能不能用到正点原子的I.MX6ULL 开发板上呢?试一下不就知道了!

烧写验证与驱动测试

将imxdownload 软件拷贝到uboot 源码根目录下,然后使用imxdownload 软件将u-boot.bin烧写到SD 卡中,烧写命令如下:

chmod 777 imxdownload //给予imxdownload 可执行权限
./imxdownload u-boot.bin /dev/sdd //烧写到SD 卡中,不能烧写到/dev/sda 或sda1 里面

烧写完成以后将SD 卡插入I.MX6U-ALPHA 开发板的TF 卡槽中,最后设置开发板从SD卡启动。打开SecureCRT,设置好开发板所使用的串口并打开,复位开发板,SecureCRT 接收到如下图33.1.3.1 所示信息:
U-Boot 移植_第4张图片

图33.1.3.1 uboot 启动信息
从图33.1.3.1 可以看出,uboot 启动正常,虽然我们用的是NXP 官方I.MX6ULL 开发板的uboot,但是在正点原子的I.MX6ULL 开发板上是可以正常启动的。而且DRAM 识别正确,为512MB,如果用的NAND 版本的核心版的话uboot 启动会失败!因为NAND 核心版用的256MB的DRAM。
1、SD 卡和EMMC 驱动检查
检查一下SD 卡和EMMC 驱动是否正常,使用命令mmc list 列出当前的MMC 设备,结果如图33.1.3.2 所示:
U-Boot 移植_第5张图片

图33.1.3.2 emmc 设备检查
从图33.1.3.2 可以看出当前有两个MMC 设备,检查每个MMC 设备信息,先检查MMC 设备0,输入如下命令:

mmc dev 0
mmc info

结果如图33.1.3.3 所示:
U-Boot 移植_第6张图片
图33.1.3.3 mmc 设备0 信息
从图33.1.3.3 可以看出,mmc 设备0 是SD 卡,SD 卡容量为14.8GB,这个和我所使用的SD 卡信息相符,说明SD 卡驱动正常。再来检查MMC 设备1,输入如下命令:

mmc dev 1
mmc info

结果如图33.1.3.4 所示:
U-Boot 移植_第7张图片

图33.1.3.4 mmc 设备1 信息
从图33.1.3.4 可以看出,mmc 设备1 为EMMC,容量为7.3GB,说明EMMC 驱动也成功,SD 卡和EMMC 的驱动都没问题。
2、LCD 驱动检查
如果uboot 中的LCD 驱动正确的话,启动uboot 以后LCD 上应该会显示出NXP 的logo,如下图33.1.3.5 所示:
U-Boot 移植_第8张图片
图33.1.3.5 uboot LCD 界面
如果你用的不是正点原子的4.3 寸480x272 分辨率的屏幕的话,那么LCD 就不会显示33.1.3.5 所示logo 界面。因为NXP 官方I.MX6ULL 开发板的屏幕就是4.3 寸480x272 分辨率的,所以uboot 默认LCD 驱动是4.3 寸480x272 分辨率的。如果使用其他分辨率的LCD 就需要修改LCD 驱动,这里我们先不修改LCD 驱动了,稍后我们在讲解如何修改uboot 中的LCD驱动,我们只需要记得,uboot 的LCD 需要修改就行了。
3、网络驱动
uboot 启动的时候提示“Board Net Initialization Failed”和“No ethernet found.”这两行,说明网络驱动也有问题,正常情况下应该是如图33.1.3.6 所示提示:
U-Boot 移植_第9张图片

图33.1.3.6 网络信息
现在没有图33.1.3.6 中的信息,那更别说ping 一下ubuntu 主机了,说明当前uboot 的网络部驱动也是有问题的,这是因为正点原子开发板的网络芯片复位引脚和NXP 官方开发板不一样,因此需要修改驱动。
总结一下NXP 官方I.MX6ULL EVK 开发板的uboot 在正点原子EMMC 版本I.MX6ULL开发板上的运行情况:
①、uboot 启动正常,DRAM 识别正确,SD 卡和EMMC 驱动正常。
②、uboot 里面的LCD 驱动默认是给4.3 寸480x272 分辨率的,如果使用的其他分辨率的屏幕需要修改驱动。
③、网络不能工作,识别不出来网络信息,需要修改驱动。
接下来我们要做的工作如下:
①、前面我们一直使用着NXP 官方开发板的uboot 配置,接下来需要在uboot 中添加我们自己的开发板,也就是正点原子的I.MX6ULL 开发板。
②、解决LCD 驱动和网络驱动的问题。

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

NXP 官方uboot 中默认都是NXP 自己的开发板,虽说我们可以直接在官方的开发板上直接修改,使uboot 可以完整的运行在我们的板子上。但是从学习的角度来讲,这样我们就不能了解到uboot 是如何添加新平台的。接下来我们就参考NXP 官方的I.MX6ULL EVK 开发板,学习如何在uboot 中添加我们的开发板或者开发平台。

添加开发板默认配置文件

先在configs 目录下创建默认配置文件,复制mx6ull_14x14_evk_emmc_defconfig,然后重命名为mx6ull_alientek_emmc_defconfig,命令如下:

cd configs
cp mx6ull_14x14_evk_emmc_defconfig mx6ull_alientek_emmc_defconfig

然后将文件mx6ull_alientek_emmc_defconfig 中的内容改成下面的:

示例代码33.2.1.1 mx6ull_alientek_emmc_defconfig 文件
1 CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ull_alientek_
emmc/imximage.cfg,MX6ULL_EVK_EMMC_REWORK"
2 CONFIG_ARM=y
3 CONFIG_ARCH_MX6=y
4 CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y
5 CONFIG_CMD_GPIO=y

可以看出,mx6ull_alientek_emmc_defconfig 基本和mx6ull_14x14_evk_emmc_defconfig 中的内容一样,只是第1 行和第4 行做了修改。

添加开发板对应的头文件

在目录include/configs 下添加I.MX6ULL-ALPHA 开发板对应的头文件,复制
include/configs/mx6ullevk.h,并重命名为mx6ull_alientek_emmc.h,命令如下:

cp include/configs/mx6ullevk.h include/configs/mx6ull_alientek_emmc.h

拷贝完成以后将:

#ifndef __MX6ULLEVK_CONFIG_H
#define __MX6ULLEVK_CONFIG_H

改为:

#ifndef __MX6ULL_ALIENTEK_EMMC_CONFIG_H
#define __MX6ULL_ALIENTEK_EMMC_CONFIG_H

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

示例代码33.2.2.1 mx6ull_alientek_emmc.h 文件
1 /*
2 * Copyright (C) 2016 Freescale Semiconductor, Inc.
3 *
4 * Configuration settings for the Freescale i.MX6UL 14x14 EVK board.
5 *
6 * SPDX-License-Identifier: GPL-2.0+
7 */
8 #ifndef __MX6ULL_ALEITENK_EMMC_CONFIG_H
9 #define __MX6ULL_ALEITENK_EMMC_CONFIG_H
10
11
12 #include <asm/arch/imx-regs.h>
13 #include <linux/sizes.h>
14 #include "mx6_common.h"
15 #include <asm/imx-common/gpio.h>
16
......
28
29 #define is_mx6ull_9x9_evk() CONFIG_IS_ENABLED(TARGET_MX6ULL_9X9_EVK)
30
31 #ifdef CONFIG_TARGET_MX6ULL_9X9_EVK
32 #define PHYS_SDRAM_SIZE SZ_256M
33 #define CONFIG_BOOTARGS_CMA_SIZE "cma=96M "
34 #else
35 #define PHYS_SDRAM_SIZE SZ_512M
36 #define CONFIG_BOOTARGS_CMA_SIZE ""
37 /* DCDC used on 14x14 EVK, no PMIC */
38 #undef CONFIG_LDO_BYPASS_CHECK
39 #endif
40
41 /* SPL options */
42 /* We default not support SPL
43 * #define CONFIG_SPL_LIBCOMMON_SUPPORT
44 * #define CONFIG_SPL_MMC_SUPPORT
45 * #include "imx6_spl.h"
46 */
47
48 #define CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
49
50 #define CONFIG_DISPLAY_CPUINFO
51 #define CONFIG_DISPLAY_BOARDINFO
52
53 /* Size of malloc() pool */
54 #define CONFIG_SYS_MALLOC_LEN (16 * SZ_1M)
55
56 #define CONFIG_BOARD_EARLY_INIT_F
57 #define CONFIG_BOARD_LATE_INIT
58
59 #define CONFIG_MXC_UART
60 #define CONFIG_MXC_UART_BASE UART1_BASE
61
62 /* MMC Configs */
63 #ifdef CONFIG_FSL_USDHC
64 #define CONFIG_SYS_FSL_ESDHC_ADDR USDHC2_BASE_ADDR
65
66 /* NAND pin conflicts with usdhc2 */
67 #ifdef CONFIG_SYS_USE_NAND
68 #define CONFIG_SYS_FSL_USDHC_NUM 1
69 #else
70 #define CONFIG_SYS_FSL_USDHC_NUM 2
71 #endif
72 #endif
73
74 /* I2C configs */
75 #define CONFIG_CMD_I2C
76 #ifdef CONFIG_CMD_I2C
77 #define CONFIG_SYS_I2C
78 #define CONFIG_SYS_I2C_MXC
79 #define CONFIG_SYS_I2C_MXC_I2C1 /* enable I2C bus 1 */
80 #define CONFIG_SYS_I2C_MXC_I2C2 /* enable I2C bus 2 */
81 #define CONFIG_SYS_I2C_SPEED 100000
82
......
89
90 #define CONFIG_SYS_MMC_IMG_LOAD_PART 1
91
92 #ifdef CONFIG_SYS_BOOT_NAND
93 #define CONFIG_MFG_NAND_PARTITION "mtdparts=gpmi-nand:64m(boot),16m(kernel),16m(dtb),1m(misc),-(rootfs) "
94 #else
95 #define CONFIG_MFG_NAND_PARTITION ""
96 #endif
97
98 #define CONFIG_MFG_ENV_SETTINGS \
99 "mfgtool_args=setenv bootargs console=${console},${baudrate} " \
......
111 "bootcmd_mfg=run mfgtool_args;bootz ${loadaddr} ${initrd_addr} ${fdt_addr};\0" \
112
113 #if defined(CONFIG_SYS_BOOT_NAND)
114 #define CONFIG_EXTRA_ENV_SETTINGS \
115 CONFIG_MFG_ENV_SETTINGS \
116 "panel=TFT43AB\0" \
......
126 "bootz ${loadaddr} - ${fdt_addr}\0"
127
128 #else
129 #define CONFIG_EXTRA_ENV_SETTINGS \
130 CONFIG_MFG_ENV_SETTINGS \
131 "script=boot.scr\0" \
......
202 "fi;\0" \
203
204 #define CONFIG_BOOTCOMMAND \
205 "run findfdt;" \
......
216 "else run netboot; fi"
217 #endif
218
219 /* Miscellaneous configurable options */
220 #define CONFIG_CMD_MEMTEST
221 #define CONFIG_SYS_MEMTEST_START 0x80000000
222 #define CONFIG_SYS_MEMTEST_END (CONFIG_SYS_MEMTEST_START + 0x8000000)
223
224 #define CONFIG_SYS_LOAD_ADDR CONFIG_LOADADDR
225 #define CONFIG_SYS_HZ 1000
226
227 #define CONFIG_STACKSIZE SZ_128K
228
229 /* Physical Memory Map */
230 #define CONFIG_NR_DRAM_BANKS 1
231 #define PHYS_SDRAM MMDC0_ARB_BASE_ADDR
232
233 #define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM
234 #define CONFIG_SYS_INIT_RAM_ADDR IRAM_BASE_ADDR
235 #define CONFIG_SYS_INIT_RAM_SIZE IRAM_SIZE
236
237 #define CONFIG_SYS_INIT_SP_OFFSET \
238 (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE)
239 #define CONFIG_SYS_INIT_SP_ADDR \
240 (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)
241
242 /* FLASH and environment organization */
243 #define CONFIG_SYS_NO_FLASH
244
......
255
256 #define CONFIG_SYS_MMC_ENV_DEV 1 /* USDHC2 */
257 #define CONFIG_SYS_MMC_ENV_PART 0 /* user area */
258 #define CONFIG_MMCROOT "/dev/mmcblk1p2" /* USDHC2 */
259
260 #define CONFIG_CMD_BMODE
261
......
275
276 /* NAND stuff */
277 #ifdef CONFIG_SYS_USE_NAND
278 #define CONFIG_CMD_NAND
279 #define CONFIG_CMD_NAND_TRIMFFS
280
281 #define CONFIG_NAND_MXS
282 #define CONFIG_SYS_MAX_NAND_DEVICE 1
283 #define CONFIG_SYS_NAND_BASE 0x40000000
284 #define CONFIG_SYS_NAND_5_ADDR_CYCLE
285 #define CONFIG_SYS_NAND_ONFI_DETECTION
286
287 /* DMA stuff, needed for GPMI/MXS NAND support */
288 #define CONFIG_APBH_DMA
289 #define CONFIG_APBH_DMA_BURST
290 #define CONFIG_APBH_DMA_BURST8
291 #endif
292
293 #define CONFIG_ENV_SIZE SZ_8K
294 #if defined(CONFIG_ENV_IS_IN_MMC)
295 #define CONFIG_ENV_OFFSET (12 * SZ_64K)
296 #elif defined(CONFIG_ENV_IS_IN_SPI_FLASH)
297 #define CONFIG_ENV_OFFSET (768 * 1024)
298 #define CONFIG_ENV_SECT_SIZE (64 * 1024)
299 #define CONFIG_ENV_SPI_BUS CONFIG_SF_DEFAULT_BUS
300 #define CONFIG_ENV_SPI_CS CONFIG_SF_DEFAULT_CS
301 #define CONFIG_ENV_SPI_MODE CONFIG_SF_DEFAULT_MODE
302 #define CONFIG_ENV_SPI_MAX_HZ CONFIG_SF_DEFAULT_SPEED
303 #elif defined(CONFIG_ENV_IS_IN_NAND)
304 #undef CONFIG_ENV_SIZE
305 #define CONFIG_ENV_OFFSET (60 << 20)
306 #define CONFIG_ENV_SECT_SIZE (128 << 10)
307 #define CONFIG_ENV_SIZE CONFIG_ENV_SECT_SIZE
308 #endif
309
310
311 /* USB Configs */
312 #define CONFIG_CMD_USB
313 #ifdef CONFIG_CMD_USB
314 #define CONFIG_USB_EHCI
315 #define CONFIG_USB_EHCI_MX6
316 #define CONFIG_USB_STORAGE
317 #define CONFIG_EHCI_HCD_INIT_AFTER_RESET
318 #define CONFIG_USB_HOST_ETHER
319 #define CONFIG_USB_ETHER_ASIX
320 #define CONFIG_MXC_USB_PORTSC (PORT_PTS_UTMI | PORT_PTS_PTW)
321 #define CONFIG_MXC_USB_FLAGS 0
322 #define CONFIG_USB_MAX_CONTROLLER_COUNT 2
323 #endif
324
325 #ifdef CONFIG_CMD_NET
326 #define CONFIG_CMD_PING
327 #define CONFIG_CMD_DHCP
328 #define CONFIG_CMD_MII
329 #define CONFIG_FEC_MXC
330 #define CONFIG_MII
331 #define CONFIG_FEC_ENET_DEV 1
332
333 #if (CONFIG_FEC_ENET_DEV == 0)
334 #define IMX_FEC_BASE ENET_BASE_ADDR
335 #define CONFIG_FEC_MXC_PHYADDR 0x2
336 #define CONFIG_FEC_XCV_TYPE RMII
337 #elif (CONFIG_FEC_ENET_DEV == 1)
338 #define IMX_FEC_BASE ENET2_BASE_ADDR
339 #define CONFIG_FEC_MXC_PHYADDR 0x1
340 #define CONFIG_FEC_XCV_TYPE RMII
341 #endif
342 #define CONFIG_ETHPRIME "FEC"
343
344 #define CONFIG_PHYLIB
345 #define CONFIG_PHY_MICREL
346 #endif
347
348 #define CONFIG_IMX_THERMAL
349
350 #ifndef CONFIG_SPL_BUILD
351 #define CONFIG_VIDEO
352 #ifdef CONFIG_VIDEO
353 #define CONFIG_CFB_CONSOLE
354 #define CONFIG_VIDEO_MXS
355 #define CONFIG_VIDEO_LOGO
356 #define CONFIG_VIDEO_SW_CURSOR
357 #define CONFIG_VGA_AS_SINGLE_DEVICE
358 #define CONFIG_SYS_CONSOLE_IS_IN_ENV
359 #define CONFIG_SPLASH_SCREEN
360 #define CONFIG_SPLASH_SCREEN_ALIGN
361 #define CONFIG_CMD_BMP
362 #define CONFIG_BMP_16BPP
363 #define CONFIG_VIDEO_BMP_RLE8
364 #define CONFIG_VIDEO_BMP_LOGO
365 #define CONFIG_IMX_VIDEO_SKIP
366 #endif
367 #endif
368
369 #define CONFIG_IOMUX_LPSR
370
......
375 #endif


从示例代码33.2.2.1 可以看出,mx6ull_alientek_emmc.h 文件中基本都是“CONFIG_”开头的宏定义,这也说明mx6ull_alientek_emmc.h 文件的主要功能就是配置或者裁剪uboot。如果需要某个功能的话就在里面添加这个功能对应的CONFIG_XXX 宏即可,如果不需要某个功能的话就删除掉对应的宏即可。我们以示例代码33.2.2.1 为例,详细的看一下mx6ull_alientek_emmc.h
中这些宏都是什么功能。
第14 行,添加了头文件mx6_common.h,如果在mx6ull_alientek_emmc.h 中没有发现配置某个功能或命令,但是实际却存在的话,可以到mx6_common.h 文件里面去找一下。
第29~39 行,设置DRAM 的大小,宏PHYS_SDRAM_SIZE 就是板子上DRAM 的大小,如果用的NXP 官方的9X9 EVK 开发板的话DRAM 大小就为256MB。否则的话默认512MB,正点原子的I.MX6U-ALPHA 开发板用的是512MB DDR3。
第50 行,定义宏CONFIG_DISPLAY_CPUINFO,uboot 启动的时候可以输出CPU 信息。
第51 行,定义宏CONFIG_DISPLAY_BOARDINFO,uboot 启动的时候可以输出板子信息。
第54 行,CONFIG_SYS_MALLOC_LEN 为malloc 内存池大小,这里设置为16MB。
第56 行,定义宏CONFIG_BOARD_EARLY_INIT_F,这样board_init_f 函数就会调用
board_early_init_f 函数。
第57 行,定义宏CONFIG_BOARD_LATE_INIT ,这样board_init_r 函数就会调用board_late_init 函数。
第59、60 行,使能I.MX6ULL 的串口功能,宏CONFIG_MXC_UART_BASE 表示串口寄存器基地址,这里使用的串口1,基地址为UART1_BASE,UART1_BASE 定义在文件
arch/arm/include/asm/arch-mx6/imx-regs.h 中,imx-regs.h 是I.MX6ULL 寄存器描述文件,根据imx-regs.h 可得到UART1_BASE 的值如下:

UART1_BASE= (ATZ1_BASE_ADDR + 0x20000)
=AIPS1_ARB_BASE_ADDR + 0x20000
=0x02000000 + 0x20000
=0X02020000

查阅I.MX6ULL 参考手册,UART1 的寄存器基地址正是0X02020000,如图33.2.2.1 所示:
U-Boot 移植_第10张图片
图33.2.2.1 UART1 寄存器地址表
第63、64行,EMMC接在I.MX6ULL的USDHC2 上,宏CONFIG_SYS_FSL_ESDHC_ADDR为EMMC 所使用接口的寄存器基地址,也就是USDHC2 的基地址。
第67~72 行,跟NAND 相关的宏,因为NAND 和USDHC2 的引脚冲突,因此如果使用NAND 的只能使用一个USDHC 设备(SD 卡)。如果没有使用NAND,那么就有两个USDHC 设备(EMMC 和SD 卡),宏CONFIG_SYS_FSL_USDHC_NUM 表示USDHC 数量。EMMC 版本的核心版没有用到NAND,所以CONFIG_SYS_FSL_USDHC_NUM=2。
第75~81,和I2C 有关的宏定义,用于控制使能哪个I2C,I2C 的速度为多少。
第92~96 行,NAND 的分区设置,如果使用NAND 的话,默认的NAND 分区为:
“mtdparts=gpmi-nand:64m(boot),16m(kernel),16m(dtb),1m(misc),-(rootfs) “,分区结果如表33.2.2.1所示:
U-Boot 移植_第11张图片
表33.2.2.1 NAND 分区设置
NAND 的分区是可以调整的,比如boot 分区我们用不了64M 这么大,因此可以将其改小,其他的分区一样的。
第98~111 行,宏CONFIG_MFG_ENV_SETTINGS 定义了一些环境变量,使用MfgTool 烧写系统时候会用到这里面的环境变量。
第113~202 行,通过条件编译来设置宏CONFIG_EXTRA_ENV_SETTINGS ,宏
CONFIG_EXTRA_ENV_SETTINGS 也是设置一些环境变量,此宏会设置bootargs 这个环境变量,后面我们会详细分析这个宏定义。
第204~217 行,设置宏CONFIG_BOOTCOMMAND,此宏就是设置环境变量bootcmd 的值。后面会详细的分析这个宏定义。
第220~222 行,设置命令memtest 相关宏定义,比如使能命令memtest,设置memtest 测试的内存起始地址和内存大小。
第224 行,宏CONFIG_SYS_LOAD_ADDR 表示linux kernel 在DRAM 中的加载地址,也就是linux kernel 在DRAM 中的存储首地址,CONFIG_LOADADDR=0X80800000。
第225 行,宏CONFIG_SYS_HZ 为系统时钟频率,这里为1000Hz。
第227 行,宏CONFIG_STACKSIZE 为栈大小,这里为128KB。
第230 行,宏CONFIG_NR_DRAM_BANKS 为DRAM BANK 的数量,I.MX6ULL 只有一个DRAM BANK,我们也只用到了一个BANK,所以为1。
第231 行,宏PHYS_SDRAM 为I.MX6ULL 的DRAM 控制器MMDC0 所管辖的DRAM 范围起始地址,也就是0X80000000。
第233 行,宏CONFIG_SYS_SDRAM_BASE 为DRAM 的起始地址。
第234 行,宏CONFIG_SYS_INIT_RAM_ADDR 为I.MX6ULL 内部IRAM 的起始地址(也就是OCRAM 的起始地址),为0X00900000。
第235 行,宏CONFIG_SYS_INIT_RAM_SIZE 为I.MX6ULL 内部IRAM 的大小(OCRAM的大小),为0X00040000=128KB。
第237~240 行,宏CONFIG_SYS_INIT_SP_OFFSET 和CONFIG_SYS_INIT_SP_ADDR 与初始SP 有关,第一个为初始SP 偏移,第二个为初始SP 地址。
第256 行,宏CONFIG_SYS_MMC_ENV_DEV 为默认的MMC 设备,这里默认为USDHC2,也就是EMMC。
第257 行,宏CONFIG_SYS_MMC_ENV_PART 为模式分区,默认为第0 个分区。
第258 行,宏CONFIG_MMCROOT 设置进入linux 系统的根文件系统所在的分区,这里设置为”/dev/mmcblk1p2”,也就是EMMC 设备的第2 个分区。第0 个分区保存uboot,第1 个分区保存linux 镜像和设备树,第2 个分区为Linux 系统的根文件系统。
第277~291 行,与NAND 有关的宏定义,如果使用NAND 的话。
第293 行,宏CONFIG_ENV_SIZE 为环境变量大小,默认为8KB。
第294~308 行,宏CONFIG_ENV_OFFSET 为环境变量偏移地址,这里的偏移地址是相对于存储器的首地址。如果环境变量保存在EMMC 中的话,环境变量偏移地址为1264KB。如果环境变量保存在SPI FLASH 中的话,偏移地址为7681024。如果环境变量保存在NAND 中的话,偏移地址为60<<20(60MB),并且重新设置环境变量的大小为128KB。
第312~323 行,与USB 相关的宏定义。
第325~342 行,与网络相关的宏定义,比如使能dhcp、ping 等命令。第331 行的宏
CONFIG_FEC_ENET_DEV 指定uboot 所使用的网口,I.MX6ULL 有两个网口,为0 的时候使用ENET1,为1 的时候使用ENET2。宏IMX_FEC_BASE 为ENET 接口的寄存器首地址,宏
CONFIG_FEC_MXC_PHYADDR 为网口PHY 芯片的地址。宏CONFIG_FEC_XCV_TYPE 为PHY 芯片所使用的接口类型,I.MX6U-ALPHA 开发板的两个PHY 都使用的RMII 接口。
第344~END,剩下的都是一些配置宏,比如CONFIG_VIDEO 宏用于开启LCD ,CONFIG_VIDEO_LOGO 使能LOGO 显示,CONFIG_CMD_BMP 使能BMP 图片显示指令。这样就可以在uboot 中显示图片了,一般用于显示logo。
关于mx6ull_alientek_emmc.h 就讲解到这里,其中以CONFIG_CMD 开头的宏都是用于使能相应命令的,其他的以CONFIG 开头的宏都是完成一些配置功能的。以后会频繁的和mx6ull_alientek_emmc.h 这个文件打交道。

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

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

cd board/freescale/
cp mx6ullevk/ -r mx6ull_alientek_emmc

进入mx6ull_alientek_emmc 目录中,将其中的mx6ullevk.c 文件重命名为mx6ull_alientek_emmc.c,命令如下:

cd mx6ull_alientek_emmc
mv mx6ullevk.c mx6ull_alientek_emmc.c

我们还需要对mx6ull_alientek_emmc 目录下的文件做一些修改:
1、修改mx6ull_alientek_emmc 目录下的Makefile 文件
将mx6ull_alientek_emmc 下的Makefile 文件内容改为如下所示:

示例代码33.2.3.1 Makefile 文件
1 # (C) Copyright 2015 Freescale Semiconductor, Inc.
2 #
3 # SPDX-License-Identifier: GPL-2.0+
4 #
5
6 obj-y := mx6ull_alientek_emmc.o
7
8 extra-$(CONFIG_USE_PLUGIN) := plugin.bin
9 $(obj)/plugin.bin: $(obj)/plugin.o
10 $(OBJCOPY) -O binary --gap-fill 0xff $< $@

重点是第6 行的obj-y,改为mx6ull_alientek_emmc.o,这样才会编译mx6ull_alientek_emmc.c这个文件。
2、修改mx6ull_alientek_emmc 目录下的imximage.cfg 文件将imximage.cfg 中的下面一句:

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

改为:

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

3、修改mx6ull_alientek_emmc 目录下的Kconfig 文件修改Kconfig 文件,修改后的内容如下:

示例代码33.2.3.2 Kconfig 文件
1 if TARGET_MX6ULL_ALIENTEK_EMMC
2
3 config SYS_BOARD
4 default "mx6ull_alientek_emmc"
5
6 config SYS_VENDOR
7 default "freescale"
8
9 config SYS_SOC
10 default "mx6"
11
12 config SYS_CONFIG_NAME
13 default "mx6ull_alientek_emmc"
14
15 endif

4、修改mx6ull_alientek_emmc 目录下的MAINTAINERS 文件
修改MAINTAINERS 文件,修改后的内容如下:

1 MX6ULL_ALIENTEK_EMMC BOARD
2 M: Peng Fan <peng.fan@nxp.com>
3 S: Maintained
4 F: board/freescale/mx6ull_alientek_emmc/
5 F: include/configs/mx6ull_alientek_emmc.h

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

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

示例代码33.2.4.1 Kconfig 文件
1 config TARGET_MX6ULL_ALIENTEK_EMMC
2 bool "Support mx6ull_alientek_emmc"
3 select MX6ULL
4 select DM
5 select DM_THERMAL

在最后一行的endif 的前一行添加如下内容:

示例代码33.2.4.2 Kconfig 文件
1 source "board/freescale/mx6ull_alientek_emmc/Kconfig"

添加完成以后的Kconfig 文件如图33.2.4.1 所示:
U-Boot 移植_第12张图片

图33.2.4.1 修改后的Kconfig 文件
到此为止,I.MX6U-ALPHA 开发板就已经添加到uboot 中了,接下来就是编译这个新添加的开发板。

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

在uboot 根目录下新建一个名为mx6ull_alientek_emmc.sh 的shell 脚本,在这个shell 脚本里面输入如下内容:

示例代码33.2.5.1 mx6ull_alientek_emmc.sh 脚本文件
1 #!/bin/bash
2 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
3 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- (加空格)
mx6ull_alientek_emmc_defconfig
4 make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16

第3 行我们使用的默认配置文件就是33.2.1 节中新建的mx6ull_alientek_emmc_defconfig 这个配置文件。给予mx6ll_alientek_emmc.sh 可执行权限,然后运行脚本来完成编译,命令如下:

chmod 777 mx6ull_alientek_emmc.sh //给予可执行权限,一次即可
./mx6ull_alientek_emmc.sh //运行脚本编译uboot

等待编译完成,编译完成以后输入如下命令,查看一下33.2.2 小节中添加的mx6ull_alientek_emmc.h 这个头文件有没有被引用。

grep -nR "mx6ull_alientek_emmc.h"

如果有很多文件都引用了mx6ull_alientek_emmc.h 这个头文件,那就说明新板子添加成功,
如图33.2.5.1 所示:
U-Boot 移植_第13张图片

图33.2.5.1 查找结果
编译完成以后就使用imxdownload 将新编译出来的u-boot.bin 烧写到SD 卡中测试,SecureCRT 输出结果如图33.2.5.2 所示:
U-Boot 移植_第14张图片

图33.2.5.1 uboot 启动过程
从图33.2.5.1 可以看出,此时的Board 还是“MX6ULL 14x14 EVK”,因为我们参考的NXP官方的I.MX6ULL 开发板来添加自己的开发板。如果接了LCD 屏幕的话会发现LCD 屏幕并没有显示NXP 的logo,而且从图33.2.5.1 可以看出此时的网络同样也没识别出来。前面已经说了,默认uboot 中的LCD 驱动和网络驱动在正点原子的I.MX6U-ALPHA 开发板上是有问题的,需要修改。

LCD 驱动修改

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

示例代码33.2.6.1 LCD 驱动参数
1 struct display_info_t const displays[] = {{
2 .bus = MX6UL_LCDIF1_BASE_ADDR,
3 .addr = 0,
4 .pixfmt = 24,
5 .detect = NULL,
6 .enable = do_enable_parallel_lcd,
7 .mode = {
8 .name = "TFT43AB",
9 .xres = 480,
10 .yres = 272,
11 .pixclock = 108695,
12 .left_margin = 8,
13 .right_margin = 4,
14 .upper_margin = 2,
15 .lower_margin = 4,
16 .hsync_len = 41,
17 .vsync_len = 10,
18 .sync = 0,
19 .vmode = FB_VMODE_NONINTERLACED
20 } } };

示例代码33.2.6.1 中定义了一个变量displays,类型为display_info_t,这个结构体是LCD信息结构体,其中包括了LCD 的分辨率,像素格式,LCD 的各个参数等。display_info_t 定义在文件arch/arm/include/asm/imx-common/video.h 中,定义如下:

示例代码33.2.6.2 display_info 结构体
1 struct display_info_t {
2 int bus;
3 int addr;
4 int pixfmt;
5 int (*detect)(struct display_info_t const *dev);
6 void (*enable)(struct display_info_t const *dev);
7 struct fb_videomode mode;
8 };

pixfmt 是像素格式,也就是一个像素点是多少位,如果是RGB565 的话就是16 位,如果是888 的话就是24 位,一般使用RGB888。结构体display_info_t 还有个mode 成员变量,此成员变量也是个结构体,为fb_videomode,定义在文件include/linux/fb.h 中,定义如下:

示例代码33.2.6.3 fb_videomode 结构体
1 struct fb_videomode {
2 const char *name; /* optional */
3 u32 refresh; /* optional */
4 u32 xres;
5 u32 yres;
6 u32 pixclock;
7 u32 left_margin;
8 u32 right_margin;
9 u32 upper_margin;
10 u32 lower_margin;
11 u32 hsync_len;
12 u32 vsync_len;
13 u32 sync;
14 u32 vmode;
15 u32 flag;
16 };

结构体fb_videomode 里面的成员变量为LCD 的参数,这些成员变量函数如下:
name:LCD 名字,要和环境变量中的panel 相等。
xres、yres:LCD X 轴和Y 轴像素数量。
pixclock:像素时钟,每个像素时钟周期的长度,单位为皮秒。
left_margin:HBP,水平同步后肩。
right_margin:HFP,水平同步前肩。
upper_margin:VBP,垂直同步后肩。
lower_margin:VFP,垂直同步前肩。
hsync_len:HSPW,行同步脉宽。
vsync_len:VSPW,垂直同步脉宽。
vmode:大多数使用FB_VMODE_NONINTERLACED,也就是不使用隔行扫描。
可以看出,这些参数和我们第二十四章讲解RGB LCD 的时候参数基本一样,唯一不同的像素时钟pixclock 的含义不同,以正点原子的7 寸1024*600 分辨率的屏幕(ATK7016)为例,屏幕要求的像素时钟为51.2MHz,因此:

pixclock=(1/51200000)*10^12=19531

在根据其他的屏幕参数,可以得出ATK7016 屏幕的配置参数如下:

示例代码33.2.6.4 ATK7016 屏幕配置参数
1 struct display_info_t const displays[] = {{
2 .bus = MX6UL_LCDIF1_BASE_ADDR,
3 .addr = 0,
4 .pixfmt = 24,
5 .detect = NULL,
6 .enable = do_enable_parallel_lcd,
7 .mode = {
8 .name = "TFT7016",
9 .xres = 1024,
10 .yres = 600,
11 .pixclock = 19531,
12 .left_margin = 140, //HBPD
13 .right_margin = 160, //HFPD
14 .upper_margin = 20, //VBPD
15 .lower_margin = 12, //VFBD
16 .hsync_len = 20, //HSPW
17 .vsync_len = 3, //VSPW
18 .sync = 0,
19 .vmode = FB_VMODE_NONINTERLACED
20 } } };

使用示例代码33.2.6.4 中的屏幕参数替换掉mx6ull_alientek_emmc.c 中uboot 默认的屏幕参数。
打开mx6ull_alientek_emmc.h,找到所有如下语句:

panel=TFT43AB

将其改为:

panel=TFT7016

也就是设置panel 为TFT7016,panel 的值要与示例代码33.2.6.4 中的.name 成员变量的值一致。修改完成以后重新编译一遍uboot 并烧写到SD 中启动。
重启以后LCD 驱动一般就会工作正常了,LCD 上回显示NXP 的logo。但是有可能会遇到LCD 并没有工作,还是黑屏,这是什么原因呢?在uboot 命令模式输入“print”来查看环境变量panel 的值,会发现panel 的值要是TFT43AB(或其他的,反正不是TFT7016),如图33.2.6.1
所示:
在这里插入图片描述

图33.2.6.1 panel 的值
这是因为之前有将环境变量保存到EMMC 中,uboot 启动以后会先从EMMC 中读取环境变量,如果EMMC 中没有环境变量的话才会使用mx6ull_alientek_emmc.h 中的默认环境变量。
如果EMMC 中的环境变量panel 不等于TFT7016,那么LCD 显示肯定不正常,我们只需要在uboot 中修改panel 的值为TFT7016 即可,在uboot 的命令模式下输入如下命令:

setenv panel TFT7016
saveenv

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

V2.4 版本以前底板网络驱动修改

注意!正点原子I.MX6U-ALPHA开发板V2.4版本以前的底板使用的网络PHY 为LAN8720,V2.4 及其以后的版本使用的网络PHY 为SR8201F,而且网络PHY 地址有改变,大家一定要看准自己所使用的底板版本号!
如果用的是Mini 板,那么V2.2 及以后版本Mini 开发板请参考V2.4 版本的ALPHA 开发板移植。
查看自己所使用的ALPHA 底板版本号,如果是V2.4 以前版本,那么就看本结!
1、I.MX6U-ALPHA 开发板网络简介
I.MX6UL/ULL 内部有个以太网MAC 外设,也就是ENET,需要外接一个PHY 芯片来实现网络通信功能,也就是内部MAC+外部PHY 芯片的方案。大家可能听过DM9000 这个网络芯片,在一些没有内部MAC 的CPU 中,比如三星的2440,4412 等,就会采用DM9000 来实现联网功能。DM9000 提供了一个类似SRAM 的访问接口,主控CPU 通过这个接口即可与DM9000 进行通信,DM9000 就是一个MAC+PHY 芯片。这个方案就相当于外部MAC+外部PHY,那么I.MX6U 这样的内部MAC+PHY 芯片与DM9000 方案比有什么优势吗?那优势大了去了!首先就是通信效率和速度,一般SOC 内部的MAC 是带有一个专用DMA 的,专门用于处理网络数据包,采用SRAM 来读写DM9000 的速度是压根就没法和内部MAC+外部PHY 芯片的速度比。采用外部DM9000 完全是无奈之举,谁让2440,4412 这些芯片内部没有以太网外设呢,现在又想用有线网络,没有办法只能找个DM9000 的方案。从这里也可以看出,三星的2440、4412 这些芯片设计之初就不是给工业产品用的,他们是给消费类电子使用的,比如手机、平板等,手机或平板要上网,可以通过WIFI 或者4G,我是没有见过哪个手机或者平板上网是要接根网线的。正点原子的I.MX6U-ALPHA 开发板也可以通过WIFI 或者4G 上网,这个是后话了。
I.MX6UL/ULL 有两个网络接口ENET1 和ENET2,正点原子的I.MX6U-ALPHA 开发板提供了这两个网络接口,其中ENET1 和ENET2 都使用LAN8720A 作为PHY 芯片。NXP 官方的I.MX6ULL EVK 开发板使用KSZ8081 这颗PHY 芯片,LAN8720A 相比KSZ8081 具有体积小、外围器件少、价格便宜等优点。直接使用KSZ8081 固然可以,但是我们在实际的产品中不一定会使用KSZ8081,有时候为了降低成本会选择其他的PHY 芯片,这个时候就有个问题:换了PHY 芯片以后网络驱动怎么办?为此,正点原子的I.MX6U-ALPHA 开发板将ENET1 和ENET2的PHY 换成了LAN8720A,这样就可以给大家讲解更换PHY 芯片以后如何调整网络驱动,使网络工作正常。
I.MX6U-ALPHA 开发板ENET1 的网络原理图如图33.2.7.1 所示:
U-Boot 移植_第15张图片
图33.2.7.1 ENET1 原理图
ENET1 的网络PHY 芯片为LAN8720A,通过RMII 接口与I.MX6ULL 相连,正点原子I.MX6U-ALPHA 开发板的ENET1 引脚与NXP 官方的I.MX6ULL EVK 开发板基本一样,唯独复位引脚不同。从图33.2.7.1 可以看出,正点原子I.MX6U-ALPHA 开发板的ENET1 复位引脚ENET1_RST 接到了I.M6ULL 的SNVS_TAMPER7 这个引脚上。
LAN8720A 内部是有寄存器的,I.MX6ULL 会读取LAN8720 内部寄存器来判断当前的物理链接状态、连接速度(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 上连接的LAN8720A器件地址为0X0,所示我们要修改ENET1 网络驱动的话重点就三点:
①、ENET1 复位引脚初始化。
②、LAN8720A 的器件ID。
③、LAN8720 驱动
再来看一下ENET2 的原理图,如图33.2.7.2 所示:
U-Boot 移植_第16张图片
图33.2.7.2 ENET2 原理图
关于ENET2 网络驱动的修改也注意一下三点:
①、ENET2 的复位引脚,从图33.2.7.2 可以看出,ENET2 的复位引脚ENET2_RST 接到了I.MX6ULL 的SNVS_TAMPER8 上。
②、ENET2 所使用的PHY 芯片器件地址,从图33.2.7.2 可以看出,PHY 器件地址为0X1。
③、LAN8720 驱动,ENET1 和ENET2 都使用的LAN8720,所以驱动肯定是一样的。
2、网络PHY 地址修改
首先修改uboot 中的ENET1 和ENET2 的PHY 地址和驱动,打开mx6ull_alientek_emmc.h这个文件,找到如下代码:

示例代码33.2.7.1 网络默认ID 配置参数
325 #ifdef CONFIG_CMD_NET
326 #define CONFIG_CMD_PING
327 #define CONFIG_CMD_DHCP
328 #define CONFIG_CMD_MII
329 #define CONFIG_FEC_MXC
330 #define CONFIG_MII
331 #define CONFIG_FEC_ENET_DEV 1
332
333 #if (CONFIG_FEC_ENET_DEV == 0)
334 #define IMX_FEC_BASE ENET_BASE_ADDR
335 #define CONFIG_FEC_MXC_PHYADDR 0x2
336 #define CONFIG_FEC_XCV_TYPE RMII
337 #elif (CONFIG_FEC_ENET_DEV == 1)
338 #define IMX_FEC_BASE ENET2_BASE_ADDR
339 #define CONFIG_FEC_MXC_PHYADDR 0x1
340 #define CONFIG_FEC_XCV_TYPE RMII
341 #endif
342 #define CONFIG_ETHPRIME "FEC"
343
344 #define CONFIG_PHYLIB
345 #define CONFIG_PHY_MICREL
346 #endif

第331 行的宏CONFIG_FEC_ENET_DEV 用于选择使用哪个网口,默认为1,也就是选择ENET2。第335 行为ENET1 的PHY 地址,默认是0X2,第339 行为ENET2 的PHY 地址,默认为0x1。根据前面的分析可知,正点原子的I.MX6U-ALPHA 开发板ENET1 的PHY 地址为0X0,ENET2 的PHY 地址为0X1,所以需要将第335 行的宏CONFIG_FEC_MXC_PHYADDR改为0x0。
第345 行定了一个宏CONFIG_PHY_MICREL,此宏用于使能uboot 中Micrel 公司的PHY驱动,KSZ8081 这颗PHY 芯片就是Micrel 公司生产的,不过Micrel 已经被Microchip 收购了。
如果要使用LAN8720A,那么就得将CONFIG_PHY_MICREL 改为CONFIG_PHY_SMSC,也就是使能uboot 中的SMSC 公司中的PHY 驱动,因为LAN8720A 就是SMSC 公司生产的。所以示例代码33.2.7.1 有三处要修改:
①、修改ENET1 网络PHY 的地址。
②、修改ENET2 网络PHY 的地址。
③、使能SMSC 公司的PHY 驱动。
修改后的网络PHY 地址参数如下所示:

示例代码33.2.7.2 网络PHY 地址配置参数
325 #ifdef CONFIG_CMD_NET
326 #define CONFIG_CMD_PING
327 #define CONFIG_CMD_DHCP
328 #define CONFIG_CMD_MII
329 #define CONFIG_FEC_MXC
330 #define CONFIG_MII
331 #define CONFIG_FEC_ENET_DEV 1
332
333 #if (CONFIG_FEC_ENET_DEV == 0)
334 #define IMX_FEC_BASE ENET_BASE_ADDR
335 #define CONFIG_FEC_MXC_PHYADDR 0x0
336 #define CONFIG_FEC_XCV_TYPE RMII
337 #elif (CONFIG_FEC_ENET_DEV == 1)
338 #define IMX_FEC_BASE ENET2_BASE_ADDR
339 #define CONFIG_FEC_MXC_PHYADDR 0x1
340 #define CONFIG_FEC_XCV_TYPE RMII
341 #endif
342 #define CONFIG_ETHPRIME "FEC"
343
344 #define CONFIG_PHYLIB
345 #define CONFIG_PHY_SMSC
346 #endif

3、删除uboot 中74LV595 的驱动代码
uboot 中网络PHY 芯片地址修改完成以后就是网络复位引脚的驱动修改了,打开
mx6ull_alientek_emmc.c,找到如下代码:

示例代码33.2.7.3 74LV595 引脚
#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)

示例代码33.2.7.3 中以IOX 开头的宏定义是74LV595 的相关GPIO,因为NXP 官方I.MX6ULL EVK 开发板使用74LV595 来扩展IO,两个网络的复位引脚就是由74LV595 来控制的。正点原子的I.MX6U-ALPHA 开发板并没有使用74LV595,因此我们将示例代码33.2.7.3 中的代码删除掉,替换为如下所示代码:

示例代码33.2.7.4 修改后的网络引脚
#define ENET1_RESET IMX_GPIO_NR(5, 7)
#define ENET2_RESET IMX_GPIO_NR(5, 8)

ENET1 的复位引脚连接到SNVS_TAMPER7 上,对应GPIO5_IO07,ENET2 的复位引脚连接到SNVS_TAMPER8 上,对应GPIO5_IO08。
继续在mx6ull_alientek_emmc.c 中找到如下代码:

示例代码33.2.7.5 74LV595 引脚配置
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),
};

同理,示例代码33.2.7.5 是74LV595 的IO 配置参数结构体,将其删除掉。继续在mx6ull_alientek_emmc.c 中找到函数iox74lv_init,如下所示:

示例代码33.2.7.6 74LV595 初始化函数
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);
};

iox74lv_init 函数是74LV595 的初始化函数,iox74lv_set 函数用于控制74LV595 的IO 输出电平,将这两个函数全部删除掉!
在mx6ull_alientek_emmc.c 中找到board_init 函数,此函数是板子初始化函数,会被board_init_r 调用,board_init 函数内容如下:

示例代码33.2.7.7 board_init 函数
int board_init(void)
{
......
imx_iomux_v3_setup_multiple_pads(iox_pads, ARRAY_SIZE(iox_pads));
iox74lv_init();
......
return 0;
}

board_init 会调用imx_iomux_v3_setup_multiple_pads 和iox74lv_init 这两个函数来初始化
74lv595 的GPIO,将这两行删除掉。至此,mx6ull_alientek_emmc.c 中关于74LV595 芯片的驱动代码都删除掉了,接下来就是添加I.MX6U-ALPHA 开发板两个网络复位引脚了。
4、添加I.MX6U-ALPHA 开发板网络复位引脚驱动
在mx6ull_alientek_emmc.c 中找到如下所示代码:

示例代码33.2.7.8 默认网络IO 结构体数组
640 static iomux_v3_cfg_t const fec1_pads[] = {
641 MX6_PAD_GPIO1_IO06__ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
642 MX6_PAD_GPIO1_IO07__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
......
649 MX6_PAD_ENET1_RX_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
650 MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
651 };
652
653 static iomux_v3_cfg_t const fec2_pads[] = {
654 MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
655 MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
......
664 MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
665 MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
666 };

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

示例代码33.2.7.9 添加网络复位IO 后的结构体数组
640 static iomux_v3_cfg_t const fec1_pads[] = {
641 MX6_PAD_GPIO1_IO06__ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
642 MX6_PAD_GPIO1_IO07__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
......
649 MX6_PAD_ENET1_RX_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
650 MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
651 MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),
652 };
653
654 static iomux_v3_cfg_t const fec2_pads[] = {
655 MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
656 MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
......
665 MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
666 MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
667 MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),
668 };

示例代码33.2.7.9 中,第651 行和667 行分别是ENET1 和ENET2 的复位IO 配置参数。继续在文件mx6ull_alientek_emmc.c 中找到函数setup_iomux_fec,此函数默认代码如下:

示例代码33.2.7.10 setup_iomux_fec 函数默认代码
668 static void setup_iomux_fec(int fec_id)
669 {
670 if (fec_id == 0)
671 imx_iomux_v3_setup_multiple_pads(fec1_pads,
672 ARRAY_SIZE(fec1_pads));
673 else
674 imx_iomux_v3_setup_multiple_pads(fec2_pads,
675 ARRAY_SIZE(fec2_pads));
676 }

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

示例代码33.2.7.11 修改后的setup_iomux_fec 函数
668 static void setup_iomux_fec(int fec_id)
669 {
670 if (fec_id == 0)
671 {
672
673 imx_iomux_v3_setup_multiple_pads(fec1_pads,
674 ARRAY_SIZE(fec1_pads));
675
676 gpio_direction_output(ENET1_RESET, 1);
677 gpio_set_value(ENET1_RESET, 0);
678 mdelay(20);
679 gpio_set_value(ENET1_RESET, 1);
680 }
681 else
682 {
683 imx_iomux_v3_setup_multiple_pads(fec2_pads,
684 ARRAY_SIZE(fec2_pads));
685 gpio_direction_output(ENET2_RESET, 1);
686 gpio_set_value(ENET2_RESET, 0);
687 mdelay(20);
688 gpio_set_value(ENET2_RESET, 1);
689 }
690 }

示例代码33.2.7.11 中第676 行~679 行和第685 行~688 行分别对应ENET1 和ENET2 的复位IO 初始化,将这两个IO 设置为输出并且硬件复位一下LAN8720A,这个硬件复位很重要!否则可能导致uboot 无法识别LAN8720A。
5、修改drivers/net/phy/phy.c 文件中的函数genphy_update_link
大功基本上告成,还差最后一步,uboot 中的LAN8720A 驱动有点问题,打开文件
drivers/net/phy/phy.c,找到函数genphy_update_link,这是个通用PHY 驱动函数,此函数用于更新PHY 的连接状态和速度。使用LAN8720A 的时候需要在此函数中添加一些代码,修改后的函数genphy_update_link 如下所示:

示例代码33.2.7.12 修改后的genphy_update_link 函数
221 int genphy_update_link(struct phy_device *phydev)
222 {
223 unsigned int mii_reg;
224
225 #ifdef CONFIG_PHY_SMSC
226 static int lan8720_flag = 0;
227 int bmcr_reg = 0;
228 if (lan8720_flag == 0) {
229 bmcr_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
230 phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
231 while(phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR) & 0X8000) {
232 udelay(100);
233 }
234 phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr_reg);
235 lan8720_flag = 1;
236 }
237 #endif
238
239 /*
240 * Wait if the link is up, and autonegotiation is in progress
241 * (ie - we're capable and it's not done)
242 */
243 mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
......
291
292 return 0;
293 }

225 行~237 行就是新添加的代码,为条件编译代码段,只有使用SMSC 公司的PHY 这段代码才会执行(目前只测试了LAN8720A,SMSC 公司其他的芯片还未测试)。第229 行读取LAN8720A 的BMCR 寄存器(寄存器地址为0),此寄存器为LAN8720A 的配置寄存器,这里先读取此寄存器的默认值并保存起来。230 行向寄存器BMCR 寄存器写入BMCR_RESET(值为0X8000),因为BMCR 的bit15 是软件复位控制位,因此230 行就是软件复位LAN8720A,复位完成以后此位会自动清零。第231~233 行等待LAN8720A 软件复位完成,也就是判断BMCR的bit15 位是否为1,为1 的话表示还没有复位完成。第234 行重新向BMCR 寄存器写入以前的值,也就是229 行读出的那个值。
至此网络的复位引脚驱动修改完成,重新编译uboot,然后将u-boot.bin 烧写到SD 卡中并启动,uboot 启动信息如图33.2.7.3 所示:
U-Boot 移植_第17张图片
图33.2.7.3 uboot 启动信息
从图33.2.7.3 中可以看到“Net:FEC1”这一行,提示当前使用的FEC1 这个网口,也就是ENET2。在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 //保存环境变量

设置好环境变量以后就可以在uboot 中使用网络了,用网线将I.MX6U-ALPHA 上的ENET2与电脑或者路由器连接起来,保证开发板和电脑在同一个网段内,通过ping 命令来测试一下网络连接,命令如下:

ping 192.168.1.250

结果如图33.2.7.4 所示:
在这里插入图片描述

图33.2.7.4 ping 命令测试
从图33.2.7.4 可以看出,有“host 192.168.1.250 is alive”这句,说明ping 主机成功,说明ENET2 网络工作正常。再来测试一下ENET1 的网络是否正常工作,打开mx6ull_alientek_emmc.h,将CONFIG_FEC_ENET_DEV 改为0,然后重新编译一下uboot 并烧写到SD 卡中重启。重启开发板,uboot 输出信息如图33.2.7.5 所示:
U-Boot 移植_第18张图片
图33.2.7.5 uboot 启动信息
从图33.2.7.5 可以出,有“Net:FEC0”这一行,说明当前使用的FEC0 这个网卡,也就是ENET1,同样的ping 一下主机,结果如图33.2.7.5 所示:
在这里插入图片描述

图33.2.7.6 ping 命令测试
从图33.2.7.6 可以看出,ping 主机也成功,说明ENET1 网络也工作正常,至此,I.MX6U-ALPHA 开发板的两个网络都工作正常了,建议大家将ENET2 设置为uboot 的默认网卡!也就是将宏CONFIG_FEC_ENET_DEV 设置为1。

V2.4 及以后版本底板网络驱动修改

查看自己所使用的ALPHA底板版本号,V2.4及以后版本的底板网络芯片更换为SR8201F,并且网络PHY 地址也有改变。因此如果您使用的底板是V2.4 及以后的版本,请看本小节!
1、I.MX6U-ALPHA 开发板网络简介
I.MX6UL/ULL 内部有个以太网MAC 外设,也就是ENET,需要外接一个PHY 芯片来实现网络通信功能,也就是内部MAC+外部PHY 芯片的方案。大家可能听过DM9000 这个网络芯片,在一些没有内部MAC 的CPU 中,比如三星的2440,4412 等,就会采用DM9000 来实现联网功能。DM9000 提供了一个类似SRAM 的访问接口,主控CPU 通过这个接口即可与DM9000 进行通信,DM9000 就是一个MAC+PHY 芯片。这个方案就相当于外部MAC+外部PHY,那么I.MX6U 这样的内部MAC+PHY 芯片与DM9000 方案比有什么优势吗?那优势大了去了!首先就是通信效率和速度,一般SOC 内部的MAC 是带有一个专用DMA 的,专门用于处理网络数据包,采用SRAM 来读写DM9000 的速度是压根就没法和内部MAC+外部PHY 芯片的速度比。采用外部DM9000 完全是无奈之举,谁让2440,4412 这些芯片内部没有以太网外设呢,现在又想用有线网络,没有办法只能找个DM9000 的方案。从这里也可以看出,三星的2440、4412 这些芯片设计之初就不是给工业产品用的,他们是给消费类电子使用的,比如手机、平板等,手机或平板要上网,可以通过WIFI 或者4G,我是没有见过哪个手机或者平板上网是要接根网线的。正点原子的I.MX6U-ALPHA 开发板也可以通过WIFI 或者4G 上网,这个是后话了。
I.MX6UL/ULL 有两个网络接口ENET1 和ENET2,正点原子的I.MX6U-ALPHA 开发板提供了这两个网络接口,其中ENET1 和ENET2 都使用SR8201F 作为PHY 芯片。NXP 官方的I.MX6ULL EVK 开发板使用KSZ8081 这颗PHY 芯片,SR8201F 相比KSZ8081 具有体积小、外围器件少、价格便宜等优点。直接使用KSZ8081 固然可以,但是我们在实际的产品中不一定会使用KSZ8081,有时候为了降低成本会选择其他的PHY 芯片,这个时候就有个问题:换了PHY芯片以后网络驱动怎么办?为此,正点原子的I.MX6U-ALPHA 开发板将ENET1 和ENET2 的PHY 换成了SR8201F,这样就可以给大家讲解更换PHY 芯片以后如何调整网络驱动,使网络工作正常。
I.MX6U-ALPHA 开发板ENET1 的网络原理图如图33.2.8.1 所示:
U-Boot 移植_第19张图片

图33.2.8.1 ENET1 原理图
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 复位引脚初始化。

②、SR8201F 的器件ID。
③、SR8201F 驱动
再来看一下ENET2 的原理图,如图33.2.8.2 所示:
U-Boot 移植_第20张图片

图33.2.8.2 ENET2 原理图
关于ENET2 网络驱动的修改也注意一下三点:
①、ENET2 的复位引脚,从图33.2.8.2 可以看出,ENET2 的复位引脚ENET2_RST 接到了I.MX6ULL 的SNVS_TAMPER8 上。
②、ENET2 所使用的PHY 芯片器件地址,从图33.2.8.2 可以看出,PHY 器件地址为0X1。
③、SR8201F 驱动,ENET1 和ENET2 都使用的SR8201F,所以驱动肯定是一样的。
2、网络PHY 地址修改
首先修改uboot 中的ENET1 和ENET2 的PHY 地址和驱动,打开mx6ull_alientek_emmc.h这个文件,找到如下代码:

示例代码33.2.8.1 网络默认ID 配置参数
325 #ifdef CONFIG_CMD_NET
326 #define CONFIG_CMD_PING
327 #define CONFIG_CMD_DHCP
328 #define CONFIG_CMD_MII
329 #define CONFIG_FEC_MXC
330 #define CONFIG_MII
331 #define CONFIG_FEC_ENET_DEV 1
332
333 #if (CONFIG_FEC_ENET_DEV == 0)
334 #define IMX_FEC_BASE ENET_BASE_ADDR
335 #define CONFIG_FEC_MXC_PHYADDR 0x2
336 #define CONFIG_FEC_XCV_TYPE RMII
337 #elif (CONFIG_FEC_ENET_DEV == 1)
338 #define IMX_FEC_BASE ENET2_BASE_ADDR
339 #define CONFIG_FEC_MXC_PHYADDR 0x1
340 #define CONFIG_FEC_XCV_TYPE RMII
341 #endif
342 #define CONFIG_ETHPRIME "FEC"
343
344 #define CONFIG_PHYLIB
345 #define CONFIG_PHY_MICREL
346 #endif

第331 行的宏CONFIG_FEC_ENET_DEV 用于选择使用哪个网口,默认为1,也就是选择ENET2。第335 行为ENET1 的PHY 地址,默认是0X2,第339 行为ENET2 的PHY 地址,默认为0x1。根据前面的分析可知,正点原子的I.MX6U-ALPHA 开发板ENET1 的PHY 地址刚好也为0X2,ENET2 的PHY 地址也是0X1,所以可以直接使用默认配置,不需要我们修改。注意,这里和V2.4 版本以前的开发板所使用的LAN8720A 地址不同!
第345 行定了一个宏CONFIG_PHY_MICREL,此宏用于使能uboot 中Micrel 公司的PHY驱动,KSZ8081 这颗PHY 芯片就是Micrel 公司生产的,不过Micrel 已经被Microchip 收购了。
如果要使用SR8201F,那么就得将CONFIG_PHY_MICREL 改为CONFIG_PHY_REALTEK。因为SR8201F 就是Pin to Pin 替换Realtek 的RTL8201F 的,因此可以直接使用Realtek 的相关驱动。所以示例代码33.2.8.1 中理论上有三处要修改:
①、修改ENET1 网络PHY 的地址。
②、修改ENET2 网络PHY 的地址。
③、使能REALTEK 公司的PHY 驱动。
但是,由于我们开发板上SR8201F 的PHY 地址和默认的配置一样,因此只需要是能REALTEK 公司的PHY 驱动,大家如果使用的其他品牌的开发板,那么就要根据实际情况来修改对应的ENET1 和ENET2 网络PHY 地址。
修改后的网络PHY 地址参数如下所示:

示例代码33.2.8.2 网络PHY 地址配置参数
325 #ifdef CONFIG_CMD_NET
326 #define CONFIG_CMD_PING
327 #define CONFIG_CMD_DHCP
328 #define CONFIG_CMD_MII
329 #define CONFIG_FEC_MXC
330 #define CONFIG_MII
331 #define CONFIG_FEC_ENET_DEV 1
332
333 #if (CONFIG_FEC_ENET_DEV == 0)
334 #define IMX_FEC_BASE ENET_BASE_ADDR
335 #define CONFIG_FEC_MXC_PHYADDR 0x2
336 #define CONFIG_FEC_XCV_TYPE RMII
337 #elif (CONFIG_FEC_ENET_DEV == 1)
338 #define IMX_FEC_BASE ENET2_BASE_ADDR
339 #define CONFIG_FEC_MXC_PHYADDR 0x1
340 #define CONFIG_FEC_XCV_TYPE RMII
341 #endif
342 #define CONFIG_ETHPRIME "FEC"
343
344 #define CONFIG_PHYLIB
345 #define CONFIG_PHY_REALTEK
346 #endif

3、删除uboot 中74LV595 的驱动代码
uboot 中网络PHY 芯片地址修改完成以后就是网络复位引脚的驱动修改了,打开mx6ull_alientek_emmc.c,找到如下代码:

示例代码33.2.8.3 74LV595 引脚
#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)

示例代码33.2.8.3 中以IOX 开头的宏定义是74LV595 的相关GPIO,因为NXP 官方I.MX6ULL EVK 开发板使用74LV595 来扩展IO,两个网络的复位引脚就是由74LV595 来控制的。正点原子的I.MX6U-ALPHA 开发板并没有使用74LV595,因此我们将示例代码33.2.8.3中的代码删除掉,替换为如下所示代码:

示例代码33.2.8.4 修改后的网络引脚
#define ENET1_RESET IMX_GPIO_NR(5, 7)
#define ENET2_RESET IMX_GPIO_NR(5, 8)

ENET1 的复位引脚连接到SNVS_TAMPER7 上,对应GPIO5_IO07,ENET2 的复位引脚连接到SNVS_TAMPER8 上,对应GPIO5_IO08。
继续在mx6ull_alientek_emmc.c 中找到如下代码:

示例代码33.2.8.5 74LV595 引脚配置
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),
};

同理,示例代码33.2.8.5 是74LV595 的IO 配置参数结构体,将其删除掉。继续在mx6ull_alientek_emmc.c 中找到函数iox74lv_init,如下所示:

示例代码33.2.8.6 74LV595 初始化函数
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);
};

iox74lv_init 函数是74LV595 的初始化函数,iox74lv_set 函数用于控制74LV595 的IO 输出电平,将这两个函数全部删除掉!
在mx6ull_alientek_emmc.c 中找到board_init 函数,此函数是板子初始化函数,会被board_init_r 调用,board_init 函数内容如下:

示例代码33.2.8.7 board_init 函数
int board_init(void)
{
......
imx_iomux_v3_setup_multiple_pads(iox_pads, ARRAY_SIZE(iox_pads));
iox74lv_init();
......
return 0;
}

board_init 会调用imx_iomux_v3_setup_multiple_pads 和iox74lv_init 这两个函数来初始化74lv595 的GPIO,将这两行删除掉。至此,mx6ull_alientek_emmc.c 中关于74LV595 芯片的驱动代码都删除掉了,接下来就是添加I.MX6U-ALPHA 开发板两个网络复位引脚了。
4、添加I.MX6U-ALPHA 开发板网络复位引脚驱动
在mx6ull_alientek_emmc.c 中找到如下所示代码:

示例代码33.2.8.8 默认网络IO 结构体数组
640 static iomux_v3_cfg_t const fec1_pads[] = {
641 MX6_PAD_GPIO1_IO06__ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
642 MX6_PAD_GPIO1_IO07__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
......
649 MX6_PAD_ENET1_RX_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
650 MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
651 };
652
653 static iomux_v3_cfg_t const fec2_pads[] = {
654 MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
655 MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
......
664 MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
665 MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
666 };

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

示例代码33.2.8.9 添加网络复位IO 后的结构体数组
640 static iomux_v3_cfg_t const fec1_pads[] = {
641 MX6_PAD_GPIO1_IO06__ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
642 MX6_PAD_GPIO1_IO07__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
......
649 MX6_PAD_ENET1_RX_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
650 MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
651 MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),
652 };
653
654 static iomux_v3_cfg_t const fec2_pads[] = {
655 MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
656 MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
......
665 MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
666 MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
667 MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),
668 };

示例代码33.2.8.9 中,第651 行和667 行分别是ENET1 和ENET2 的复位IO 配置参数。继续在文件mx6ull_alientek_emmc.c 中找到函数setup_iomux_fec,此函数默认代码如下:

示例代码33.2.7.10 setup_iomux_fec 函数默认代码
668 static void setup_iomux_fec(int fec_id)
669 {
670 if (fec_id == 0)
671 imx_iomux_v3_setup_multiple_pads(fec1_pads,
672 ARRAY_SIZE(fec1_pads));
673 else
674 imx_iomux_v3_setup_multiple_pads(fec2_pads,
675 ARRAY_SIZE(fec2_pads));
676 }

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

示例代码33.2.8.11 修改后的setup_iomux_fec 函数
668 static void setup_iomux_fec(int fec_id)
669 {
670 if (fec_id == 0)
671 {
672
673 imx_iomux_v3_setup_multiple_pads(fec1_pads,
674 ARRAY_SIZE(fec1_pads));
675
676 gpio_direction_output(ENET1_RESET, 1);
677 gpio_set_value(ENET1_RESET, 0);
678 mdelay(20);
679 gpio_set_value(ENET1_RESET, 1);
680 }
681 else
682 {
683 imx_iomux_v3_setup_multiple_pads(fec2_pads,
684 ARRAY_SIZE(fec2_pads));
685 gpio_direction_output(ENET2_RESET, 1);
686 gpio_set_value(ENET2_RESET, 0);
687 mdelay(20);
688 gpio_set_value(ENET2_RESET, 1);
689 }
689 mdelay(150); /* 复位结束后至少延时150ms才能正常使用*/
690 }

示例代码33.2.8.11 中第676 行~679 行和第685 行~688 行分别对应ENET1 和ENET2 的复位IO 初始化,将这两个IO 设置为输出并且硬件复位一下LAN8720A,这个硬件复位很重要!
第689 行复位结束以后一定要至少延时150ms 才能操作SR8201F,这个在SR8201F 数据手册里面有详细要求的,否则会导致uboot 无法识别SR8201F。
至此网络的复位引脚驱动修改完成,重新编译uboot,然后将u-boot.bin 烧写到SD 卡中并启动,uboot 启动信息如图33.2.8.3 所示:
U-Boot 移植_第21张图片

图33.2.8.3 uboot 启动信息
从图33.2.8.4 中可以看到“Net:FEC1”这一行,提示当前使用的FEC1 这个网口,也就是ENET2。在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 //保存环境变量

设置好环境变量以后就可以在uboot 中使用网络了,用网线将I.MX6U-ALPHA 上的ENET2与电脑或者路由器连接起来,保证开发板和电脑在同一个网段内,通过ping 命令来测试一下网络连接,命令如下:

ping 192.168.1.250

结果如图33.2.8.4 所示:
在这里插入图片描述

图33.2.8.4 ping 命令测试
从图33.2.8.4 可以看出,有“host 192.168.1.250 is alive”这句,说明ping 主机成功,说明ENET2 网络工作正常。再来测试一下ENET1 的网络是否正常工作,打开mx6ull_alientek_emmc.h,将CONFIG_FEC_ENET_DEV 改为0,然后重新编译一下uboot 并烧写到SD 卡中重启。重启开发板,uboot 输出信息如图33.2.8.5 所示:
U-Boot 移植_第22张图片

图33.2.8.5 uboot 启动信息
从图33.2.8.5 可以出,有“Net:FEC0”这一行,说明当前使用的FEC0 这个网卡,也就是ENET1,同样的ping 一下主机,结果如图33.2.8.6 所示:
在这里插入图片描述

图33.2.8.6 ping 命令测试
从图33.2.8.6 可以看出,ping 主机也成功,说明ENET1 网络也工作正常,至此,I.MX6U-ALPHA 开发板的两个网络都工作正常了,建议大家将ENET2 设置为uboot 的默认网卡!也就是将宏CONFIG_FEC_ENET_DEV 设置为1。

其他需要修改的地方

在uboot 启动信息中会有“Board: MX6ULL 14x14 EVK”这一句,也就是说板子名字为“MX6ULL 14x14 EVK”,要将其改为我们所使用的板子名字,比如“MX6ULL ALIENTEK EMMC”或者“MX6ULL ALIENTEK NAND”。打开文件mx6ull_alientek_emmc.c,找到函数checkboard,将其改为如下所示内容:

示例代码33.2.9.1 修改后的checkboard 函数
int checkboard(void)
{
if (is_mx6ull_9x9_evk())
puts("Board: MX6ULL 9x9 EVK\n");
else
puts("Board: MX6ULL ALIENTEK EMMC\n");
return 0;
}

修改完成以后重新编译uboot 并烧写到SD 卡中验证,uboot 启动信息如图33.2.9.1 所示:
U-Boot 移植_第23张图片
图33.2.9.1 uboot 启动信息
从图33.2.9.1 可以看出,Board 变成了“MX6ULL ALIENTEK EMMC”。至此uboot 的驱动部分就修改完成了,uboot 移植也完成了,uboot 的最终目的就是启动Linux 内核,所以需要通过启动Linux 内核来判断uboot 移植是否成功。在启动Linux 内核之前我们先来学习两个重要的环境变量bootcmd 和bootargs。

bootcmd 和bootargs 环境变量

uboot 中有两个非常重要的环境变量bootcmd 和bootargs,接下来看一下这两个环境变量。
bootcmd 和bootagrs 是采用类似shell 脚本语言编写的,里面有很多的变量引用,这些变量其实都是环境变量,有很多是NXP 自己定义的。文件mx6ull_alientek_emmc.h 中的宏
CONFIG_EXTRA_ENV_SETTINGS 保存着这些环境变量的默认值,内容如下:

示例代码33.3.1.1 宏CONFIG_EXTRA_ENV_SETTINGS 默认值
113 #if defined(CONFIG_SYS_BOOT_NAND)
114 #define CONFIG_EXTRA_ENV_SETTINGS \
115 CONFIG_MFG_ENV_SETTINGS \
116 "panel=TFT43AB\0" \
117 "fdt_addr=0x83000000\0" \
118 "fdt_high=0xffffffff\0" \
......
126 "bootz ${loadaddr} - ${fdt_addr}\0"
127
128 #else
129 #define CONFIG_EXTRA_ENV_SETTINGS \
130 CONFIG_MFG_ENV_SETTINGS \
131 "script=boot.scr\0" \
132 "image=zImage\0" \
133 "console=ttymxc0\0" \
134 "fdt_high=0xffffffff\0" \
135 "initrd_high=0xffffffff\0" \
136 "fdt_file=undefined\0" \
......
194 "findfdt="\
195 "if test $fdt_file = undefined; then " \
196 "if test $board_name = EVK && test $board_rev = 9X9; then " \
197 "setenv fdt_file imx6ull-9x9-evk.dtb; fi; " \
198 "if test $board_name = EVK && test $board_rev = 14X14; then " \
199 "setenv fdt_file imx6ull-14x14-evk.dtb; fi; " \
200 "if test $fdt_file = undefined; then " \
201 "echo WARNING: Could not determine dtb to use; fi; " \
202 "fi;\0" \

宏CONFIG_EXTRA_ENV_SETTINGS 是个条件编译语句,使用NAND 和EMMC 的时候宏CONFIG_EXTRA_ENV_SETTINGS 的值是不同的。

环境变量bootcmd

bootcmd 在前面已经说了很多次了,bootcmd 保存着uboot 默认命令,uboot 倒计时结束以后就会执行bootcmd 中的命令。这些命令一般都是用来启动Linux 内核的,比如读取EMMC 或者NAND Flash 中的Linux 内核镜像文件和设备树文件到DRAM 中,然后启动Linux 内核。可以在uboot 启动以后进入命令行设置bootcmd 环境变量的值。如果EMMC 或者NAND 中没有保存bootcmd 的值,那么uboot 就会使用默认的值,板子第一次运行uboot 的时候都会使用默认值来设置bootcmd 环境变量。打开文件include/env_default.h,在此文件中有如下所示内容:

示例代码33.3.1.1 默认环境变量
13 #ifdef DEFAULT_ENV_INSTANCE_EMBEDDED
14 env_t environment __PPCENV__ = {
15 ENV_CRC, /* CRC Sum */
16 #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
17 1, /* Flags: valid */
18 #endif
19 {
20 #elif defined(DEFAULT_ENV_INSTANCE_STATIC)
21 static char default_environment[] = {
22 #else
23 const uchar default_environment[] = {
24 #endif
25 #ifdef CONFIG_ENV_CALLBACK_LIST_DEFAULT
26 ENV_CALLBACK_VAR "=" CONFIG_ENV_CALLBACK_LIST_DEFAULT "\0"
27 #endif
28 #ifdef CONFIG_ENV_FLAGS_LIST_DEFAULT
29 ENV_FLAGS_VAR "=" CONFIG_ENV_FLAGS_LIST_DEFAULT "\0"
30 #endif
31 #ifdef CONFIG_BOOTARGS
32 "bootargs=" CONFIG_BOOTARGS "\0"
33 #endif
34 #ifdef CONFIG_BOOTCOMMAND
35 "bootcmd=" CONFIG_BOOTCOMMAND "\0"
36 #endif
37 #ifdef CONFIG_RAMBOOTCOMMAND
38 "ramboot=" CONFIG_RAMBOOTCOMMAND "\0"
39 #endif
40 #ifdef CONFIG_NFSBOOTCOMMAND
41 "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0"
42 #endif
43 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
44 "bootdelay=" __stringify(CONFIG_BOOTDELAY) "\0"
45 #endif
46 #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
47 "baudrate=" __stringify(CONFIG_BAUDRATE) "\0"
48 #endif
49 #ifdef CONFIG_LOADS_ECHO
50 "loads_echo=" __stringify(CONFIG_LOADS_ECHO) "\0"
51 #endif
52 #ifdef CONFIG_ETHPRIME
53 "ethprime=" CONFIG_ETHPRIME "\0"
54 #endif
55 #ifdef CONFIG_IPADDR
56 "ipaddr=" __stringify(CONFIG_IPADDR) "\0"
57 #endif
58 #ifdef CONFIG_SERVERIP
59 "serverip=" __stringify(CONFIG_SERVERIP) "\0"
60 #endif
61 #ifdef CONFIG_SYS_AUTOLOAD
62 "autoload=" CONFIG_SYS_AUTOLOAD "\0"
63 #endif
64 #ifdef CONFIG_PREBOOT
65 "preboot=" CONFIG_PREBOOT "\0"
66 #endif
67 #ifdef CONFIG_ROOTPATH
68 "rootpath=" CONFIG_ROOTPATH "\0"
69 #endif
70 #ifdef CONFIG_GATEWAYIP
71 "gatewayip=" __stringify(CONFIG_GATEWAYIP) "\0"
72 #endif
73 #ifdef CONFIG_NETMASK
74 "netmask=" __stringify(CONFIG_NETMASK) "\0"
75 #endif
76 #ifdef CONFIG_HOSTNAME
77 "hostname=" __stringify(CONFIG_HOSTNAME) "\0"
78 #endif
79 #ifdef CONFIG_BOOTFILE
80 "bootfile=" CONFIG_BOOTFILE "\0"
81 #endif
82 #ifdef CONFIG_LOADADDR
83 "loadaddr=" __stringify(CONFIG_LOADADDR) "\0"
84 #endif
85 #ifdef CONFIG_CLOCKS_IN_MHZ
86 "clocks_in_mhz=1\0"
87 #endif
88 #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
89 "pcidelay=" __stringify(CONFIG_PCI_BOOTDELAY)"\0"
90 #endif
91 #ifdef CONFIG_ENV_VARS_UBOOT_CONFIG
92 "arch=" CONFIG_SYS_ARCH "\0"
93 "cpu=" CONFIG_SYS_CPU "\0"
94 "board=" CONFIG_SYS_BOARD "\0"
95 "board_name=" CONFIG_SYS_BOARD "\0"
96 #ifdef CONFIG_SYS_VENDOR
97 "vendor=" CONFIG_SYS_VENDOR "\0"
98 #endif
99 #ifdef CONFIG_SYS_SOC
100 "soc=" CONFIG_SYS_SOC "\0"
101 #endif
102 #endif
103 #ifdef CONFIG_EXTRA_ENV_SETTINGS
104 CONFIG_EXTRA_ENV_SETTINGS
105 #endif
106 "\0"
107 #ifdef DEFAULT_ENV_INSTANCE_EMBEDDED
108 }
109 #endif
110 };


第13~23 行,这段代码是个条件编译,由于没有定义
DEFAULT_ENV_INSTANCE_EMBEDDED 和CONFIG_SYS_REDUNDAND_ENVIRONMENT,
因此uchar default_environment[]数组保存环境变量。
在示例代码33.3.1.1 中指定了很多环境变量的默认值,比如bootcmd 的默认值就是
CONFIG_BOOTCOMMAND ,bootargs 的默认值就是CONFIG_BOOTARGS。我们可以在
mx6ull_alientek_emmc.h 文件中通过设置宏CONFIG_BOOTCOMMAND 来设置bootcmd 的默认值,NXP 官方设置的CONFIG_BOOTCOMMAND 值如下:

示例代码33.3.1.3 CONFIG_BOOTCOMMAND 默认值
204 #define CONFIG_BOOTCOMMAND \
205 "run findfdt;" \
206 "mmc dev ${mmcdev};" \
207 "mmc dev ${mmcdev}; if mmc rescan; then " \
208 "if run loadbootscript; then " \
209 "run bootscript; " \
210 "else " \
211 "if run loadimage; then " \
212 "run mmcboot; " \
213 "else run netboot; " \
214 "fi; " \
215 "fi; " \
216 "else run netboot; fi"

看起来很复杂的样子!因为uboot 使用了类似shell 脚本语言的方式来编写的,我们一行一
行来分析。
第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 环境变量如下:

示例代码33.3.1.4 mmcboot 环境变量
154 "mmcboot=echo Booting from mmc ...; " \
155 "run mmcargs; " \
156 "if test ${boot_fdt} = yes || test ${boot_fdt} = try; then " \
157 "if run loadfdt; then " \
158 "bootz ${loadaddr} - ${fdt_addr}; " \
159 "else " \
160 "if test ${boot_fdt} = try; then " \
161 "bootz; " \
162 "else " \
163 "echo WARN: Cannot load the DT; " \
164 "fi; " \
165 "fi; " \
166 "else " \
167 "bootz; " \
168 "fi;\0" \

第154 行,输出信息“Booting from mmc …”。
第155 行,运行环境变量mmcargs,mmcargs 用来设置bootargs,后面分析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-alientek-emmc.dtb;" \
"bootz 0x80800000 - 0x83000000;"

或者可以直接在uboot 中设置bootcmd 的值,这个值就是保存到EMMC 中的,命令如下:
setenv bootcmd ‘mmc dev 1; fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000;’

环境变量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
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 用来设置根文件系统的位置,root=/dev/mmcblk1p2 用于指明根文件系统存放在mmcblk1 设备的分区2 中。EMMC 版本的核心板启动linux 以后会存在/dev/mmcblk0、
/dev/mmcblk1、/dev/mmcblk0p1、/dev/mmcblk0p2、/dev/mmcblk1p1 和/dev/mmcblk1p2 这样的文件,其中/dev/mmcblkx(x=0~n)表示mmc 设备,而/dev/mmcblkxpy(x=0n,y=1n)表示mmc 设备x 的分区y。在I.MX6U-ALPHA 开发板中/dev/mmcblk1 表示EMMC,而/dev/mmcblk1p2 表示EMMC 的分区2。
root 后面有“rootwait rw”,rootwait 表示等待mmc 设备初始化完成以后再挂载,否则的话mmc 设备还没初始化完成就挂载根文件系统会出错的。rw 表示根文件系统是可以读写的,不加rw 的话可能无法在根文件系统中进行写操作,只能进行读操作。
3、rootfstype
此选项一般配置root 一起使用,rootfstype 用于指定根文件系统类型,如果根文件系统为ext 格式的话此选项无所谓。如果根文件系统是yaffs、jffs 或ubifs 的话就需要设置此选项,指定根文件系统的类型。
bootargs 常设置的选项就这三个,后面遇到其他选项的话再讲解。

uboot 启动Linux 测试

uboot 已经移植好了,bootcmd 和bootargs 这两个重要的环境变量也讲解了,接下来就要测试一下uboot 能不能完成它的工作:启动Linux 内核。我们测试两种启动Linux 内核的方法,一种是直接从EMMC 启动,一种是从网络启动。

从EMMC 启动Linux 系统

从EMMC 启动也就是将编译出来的Linux 镜像文件zImage 和设备树文件保存在EMMC中,uboot 从EMMC 中读取这两个文件并启动,这个是我们产品最终的启动方式。但是我们目前还没有讲解如何移植linux 和设备树文件,以及如何将zImage 和设备树文件保存到EMMC中。不过大家拿到手的I.MX6U-ALPHA 开发板(EMMC 版本)已经将zImage 文件和设备树文件烧写到了EMMC 中,所以我们可以直接读取来测试。先检查一下EMMC 的分区1 中有没有zImage 文件和设备树文件,输入命令“ls mmc 1:1”,结果如图33.4.1.1 所示:
U-Boot 移植_第24张图片

图33.4.1.1 EMMC 分区1 文件
从图33.4.1.1 中可以看出,此时EMMC 分区1 中存在zimage 和imx6ull-alientek-emmc.dtb这两个文件,所以我们可以测试新移植的uboot 能不能启动linux 内核。设置bootargs 和bootcmd这两个环境变量,设置如下:

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 内核启动成功的话就会输出如图33.4.1.2 所示的启动信息:
图33.4.1.2 linux 内核启动成功
U-Boot 移植_第25张图片

从网络启动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 目录中,具体方法在30.4.4 小节讲解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

一开始是通过tftp 下载zImage 和imx6ull-alientek-emmc.dtb 这两个文件,过程如下图33.4.2.1所示:
U-Boot 移植_第26张图片
图33.4.2.1 下载过程
下载完成以后就是启动Linux 内核,启动过程如图33.4.2.2 所示:
U-Boot 移植_第27张图片

图33.4.2.2 Linux 启动过程
uboot 移植到此结束,简单总结一下uboot 移植的过程:
①、不管是购买的开发板还是自己做的开发板,基本都是参考半导体厂商的dmeo 板,而半导体厂商会在他们自己的开发板上移植好uboot、linux kernel 和rootfs 等,最终制作好BSP包提供给用户。我们可以在官方提供的BSP 包的基础上添加我们的板子,也就是俗称的移植。
②、我们购买的开发板或者自己做的板子一般都不会原封不动的照抄半导体厂商的demo板,都会根据实际的情况来做修改,既然有修改就必然涉及到uboot 下驱动的移植。
③、一般uboot 中需要解决串口、NAND、EMMC 或SD 卡、网络和LCD 驱动,因为uboot的主要目的就是启动Linux 内核,所以不需要考虑太多的外设驱动。
④、在uboot 中添加自己的板子信息,根据自己板子的实际情况来修改uboot 中的驱动。

你可能感兴趣的:(正点IMX6ULL系统移植,linux)