1)实验平台:正点原子阿尔法Linux开发板
2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434
2)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
3)对正点原子Linux感兴趣的同学可以加群讨论:935446741
4)关注正点原子公众号,获取最新资料更新
第三十三章 U-Boot移植
上一章节我们详细的分析了uboot的启动流程,对uboot有了一个初步的了解。前两章我们都是使用的正点原子提供的uboot,本章我们就来学习如何将NXP官方的uboot移植到正点原子的I.MX6ULL开发板上,学习如何在uboot中添加我们自己的板子。
33.1 NXP官方开发板uboot编译测试
33.1.1 查找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所示,
图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作为默认配置文件。
33.1.2 编译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所示:
图33.1.2.1 编译结果
从图33.1.2.1可以看出,编译成功。我们在编译的时候需要输入ARCH和CORSS_COMPILE这两个变量的值,这样太麻烦了。我们可以直接在顶层Makefile中直接给ARCH和CORSS_COMPILE赋值,修改如图33.1.2.2所示:
图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开发板上呢?试一下不就知道了!
33.1.3 烧写验证与驱动测试
将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所示信息:
图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所示:
图33.1.3.2 emmc设备检查
从图33.1.3.2可以看出当前有两个MMC设备,检查每个MMC设备信息,先检查MMC设备0,输入如下命令:
mmc dev 0
mmc info
结果如图33.1.3.3所示:
图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所示:
图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所示:
图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所示提示:
图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驱动和网络驱动的问题。
33.2 在U-Boot中添加自己的开发板
NXP官方uboot中默认都是NXP自己的开发板,虽说我们可以直接在官方的开发板上直接修改,使uboot可以完整的运行在我们的板子上。但是从学习的角度来讲,这样我们就不能了解到uboot是如何添加新平台的。接下来我们就参考NXP官方的I.MX6ULL EVK开发板,学习如何在uboot中添加我们的开发板或者开发平台。
33.2.1 添加开发板默认配置文件
先在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行做了修改。
33.2.2 添加开发板对应的头文件
在目录include/configs下添加I.MX6ULL-ALPHA开发板对应的头文件,复制include/configs/mx6ullevk.h,并重命名为mx6ull_alientek_emmc.h,命令如下:
cp include/configs/mx6ullevk.h 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所示:
图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所示:
范围 大小 分区
0~63M 64M boot(uboot)
64~79M 16M kernel(linux内核)
80~94M 16M dtb(设备树)
95M 1M misc(杂项)
96M – end 剩余的所有空间 rootfs(根文件系统)
表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这个文件打交道。
33.2.3 添加开发板对应的板级文件夹
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
33.2.4 修改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所示:
图33.2.4.1 修改后的Kconfig文件
到此为止,I.MX6U-ALPHA开发板就已经添加到uboot中了,接下来就是编译这个新添加的开发板。
33.2.5 使用新添加的板子配置编译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所示:
图33.2.5.1 查找结果
编译完成以后就使用imxdownload将新编译出来的u-boot.bin烧写到SD卡中测试,SecureCRT输出结果如图33.2.5.2所示:
图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开发板上是有问题的,需要修改。
33.2.6 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还是没有正常工作的,那就要检查自己哪里有没有改错,或者还有哪里没有修改。
33.2.7 网络驱动修改
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所示:
图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所示:
图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]);elsegpio_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所示:
图33.2.7.3 uboot启动信息
从图33.2.6.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.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所示:
图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。
33.2.8 其他需要修改的地方
在uboot启动信息中会有“Board: MX6ULL 14x14 EVK”这一句,也就是说板子名字为“MX6ULL 14x14 EVK”,要将其改为我们所使用的板子名字,比如“MX6ULL ALIENTEK EMMC”或者“MX6ULL ALIENTEK NAND”。打开文件mx6ull_alientek_emmc.c,找到函数checkboard,将其改为如下所示内容:
示例代码33.2.8.1 修改后的checkboard函数
int checkboard(void)
{if (is_mx6ull_9x9_evk())puts("Board: MX6ULL 9x9 EVK\n");elseputs("Board: MX6ULL ALIENTEK EMMC\n");return 0;
}
修改完成以后重新编译uboot并烧写到SD卡中验证,uboot启动信息如图33.2.8.1所示:
图33.2.8.1 uboot启动信息
从图33.2.8.1可以看出,Board变成了“MX6ULL ALIENTEK EMMC”。至此uboot的驱动部分就修改完成了,uboot移植也完成了,uboot的最终目的就是启动Linux内核,所以需要通过启动Linux内核来判断uboot移植是否成功。在启动Linux内核之前我们先来学习两个重要的环境变量bootcmd和bootargs。
33.3 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的值是不同的。
33.3.1 环境变量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 m m c d e v : {mmcdev}: 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 m m c d e v : {mmcdev}: 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 m m c d e v : {mmcdev}: 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} - KaTeX parse error: Expected 'EOF', got '#' at position 481: …TCOMMAND就可简化为: #̲define CONFIG_B…{console}, b a u d r a t e r o o t = {baudrate} root= baudrateroot={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=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、rootfstype
此选项一般配置root一起使用,rootfstype用于指定根文件系统类型,如果根文件系统为ext格式的话此选项无所谓。如果根文件系统是yaffs、jffs或ubifs的话就需要设置此选项,指定根文件系统的类型。
bootargs常设置的选项就这三个,后面遇到其他选项的话再讲解。
33.4 uboot启动Linux测试
uboot已经移植好了,bootcmd和bootargs这两个重要的环境变量也讲解了,接下来就要测试一下uboot能不能完成它的工作:启动Linux内核。我们测试两种启动Linux内核的方法,一种是直接从EMMC启动,一种是从网络启动。
33.4.1 从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所示:
图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内核启动成功
33.4.2 从网络启动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所示:
图33.4.2.1 下载过程
下载完成以后就是启动Linux内核,启动过程如图33.4.2.2所示:
图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中的驱动。