一、 方案框图
基于广州创龙TL5728-EasyEVM 开发板,设计了NOR+eMMC启动及升级方案,实现框图如下所示。
-
eMMC 有三大分区,分别是boot分区、rootfs1分区、rootfs2分区(backup),在创龙提供的demo脚本基础上进行了eMMC分区修改。
-
NOR在调试时,遇到了Spansion 和镁光不同型号的NOR出现读写问题。创龙提供了支持报告,可参考。另外自己编写了NOR烧写脚本。
-
高端内存读写操作使用了自定义Uboot命令,myhello命令。
-
分区切换通过修改默认环境变量实现。
-
使能看门狗配合分区切换。
软件版本:
Uboot版本:u-boot-2017.01
Kernel版本:linux-rt-4.9.65
二、 登陆Linux系统后需做的修改
2.1 设备树修改
路径:kernel\Linux-RT-4.9.65\arch\arm\boot\dts\am57xx-evm-common.dtsi
重新调整了NOR Flash的分区。
重新编译设备树文件 tl5728-easy-evm.dtb.
2.2 烧录脚本修改
基于创龙提供的烧录脚本,进行了修改。
1)eMMC烧录脚本
脚本:burn_scripts\mkemmcboot-heat.sh
分区大小如上所示。分了3个区,bank1用于存放 MLO和u-boot.img,bank2用于存放rootfs1,bank3用于存放rootfs2(备份)。
分区完成后,将SD卡分区的文件拷贝至eMMC 分区中。
2)NOR烧录脚本
脚本:burn_scripts\mknorboot-heat.sh
将SD卡中的文件,拷贝至NOR相应的分区中。
MLO - /dev/mtdblock0
u-boot.img - /dev/mtdblock1
image.ub - /dev/mtdblock6
备注:下面章节四有image.ub文件制作过程。
三、 引导启动-MLO和Uboot 修改
3.1 添加对镁光Flash的支持
创龙提供了两种NOR型号的核心板:Spansion 和 镁光。
修改 uboot添加对镁光 flash 的支持,以及不使用 EDMA3 进行拷贝。
另外Uboot初始化镁光NOR后,也会出现读写镜像错误,需修改spi flash读操作函数。
\u-boot-2017.01-g70d59ba-v2.0\drivers\mtd\spi\spi_flash.c
3.2 使能看门狗
参考:
http://e2e.ti.com/support/processors/f/791/t/853613?AM5728-Issues-with-WDT-enabled-in-U-boot
Uboot 2017.01版本u-boot-2017.01-demo\drivers\watchdog 目录下的 Kconfig文件是空的,拷贝了TI 官网SDK 06.02.00.81 版本的u-boot-2019.01 的 Kconfig进行了修改。
下面的Makefile ,有编译 omap_wdt.c的选项。
照猫画虎,修改了对应的 Kconfig 文件。
menu "Watchdog Timer Support"
config WATCHDOG
bool "Enable U-Boot watchdog reset"
help
This option enables U-Boot watchdog support where U-Boot is using
watchdog_reset function to service watchdog device in U-Boot. Enable
this option if you want to service enabled watchdog by U-Boot. Disable
this option if you want U-Boot to start watchdog but never service it.
config HW_WATCHDOG
bool
config WATCHDOG_RESET_DISABLE
bool "Disable reset watchdog"
help
Disable reset watchdog, which can let WATCHDOG_RESET invalid, so
that the watchdog will not be fed in u-boot.
config OMAP_WATCHDOG
bool "TI OMAP watchdog driver"
select HW_WATCHDOG
default y if AM33XX
help
Say Y here to enable the OMAP3+ watchdog driver.
endmenu
添加Kconfig后,需要 make ARCH=arm menuconfig 打开相应的配置。
打开配置后,make ARCH=arm编译,报错如下。
手动在文件 \drivers\watchdog\omap_wdt.c 里定义该宏。
继续编译,仍有报错。
报错找不到结构体 struct wd_timer 的定义,需添加相应头文件。
继续编译,继续报错。重复定义了结构体。
到相应的头文件中进行修改,屏蔽该结构体的定义。
\arch\arm\include\asm\arch-am33xx\cpu.h
继续编译,仍有报错。SPL阶段找不到看门狗初始化等操作函数。
在配置中使能 SPL阶段的看门狗功能。
接下来使能Uboot阶段的看门狗功能。
\board\ti\am57xx\board.c
添加看门狗初始化。
其他修改:通过grep –nr heat 查看
3.3 自定义Uboot命令
在u-boot-2017.01-demo\cmd\目录下添加自己定义的myhello.c。
Makefile添加编译 myhello.c的选项。
Kconfig文件添加myhello编译选项,其上一层为“Memory commands”。
menuconfig查看配置,并将 myhello 勾选上。
进行编译。
说明:
myhello.c 文件中涉及的高端内存地址。
1) 内存地址及数值定义
Count_flag_addr = 0xFFFFF00 , Count_flag_value = 0xBEEF0000 代表启动次数为0,Count_flag_value = 0xBEEF0001 代表启动次数为1;
Media_flag_addr=0xFFFFFF04,Media_flag_value = 0xDEAD0000 代表文件系统分区为eMMC- rootfs1,Media_flag_value = 0xDEAD0001 代表文件系统分区为eMMC- rootfs2,Media_flag_value = 0xDEAD0002 代表文件系统分区为NOR-Ramdisk;
读取到的Media_flag_value数值可以用于判断当前系统所运行的文件系统分区。
2)内存地址读写接口
高端内存的地址映射采用mmap方式,读写接口封装成了so库。
读写API如下:
读操作:unsigned long high_mem_read(char *addr);
写操作:int high_mem_write(char *addr, char *write_val);
3.4 默认环境变量修改
u-boot-2017.01-demo\include\environment\ti\boot.h
/*
* Boot related environment variable definitions on TI boards.
*
* (C) Copyright 2017 Linaro Ltd.
* Sam Protsenko <semen.protsenko@linaro.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __TI_BOOT_H
#define __TI_BOOT_H
#ifndef CONSOLEDEV
#define CONSOLEDEV "ttyO2"
#endif
#ifndef PARTS_DEFAULT
#define PARTS_DEFAULT
#endif
#define DEFAULT_COMMON_BOOT_TI_ARGS \
"console=" CONSOLEDEV ",115200n8\0" \
"fdtfile=undefined\0" \
"board_name=tl5728-easy-evm\0" \
"bootpart=0:2\0" \
"bootdir=/boot\0" \
"bootfile=zImage\0" \
"usbtty=cdc_acm\0" \
"vram=16M\0" \
"partitions=" PARTS_DEFAULT "\0" \
"optargs=\0" \
"idk_pru1_mii=no\0" \
"optargs=omapdrm.displays=0,1\0" \
"dofastboot=0\0" \
"usbdev=0\0" \
"usbroot=/dev/sda2\0" \
"usbrootfstype=ext4 rootwait\0" \
"count_addr=0xFFFFFF00\0" \
"media_addr=0xFFFFFF04\0" \
"norboot=echo norboot\0 " \
"usbloadimage=load usb ${bootpart} ${loadaddr} ${bootdir}/${bootfile}\0" \
"usbloadfdt=load usb ${bootpart} ${fdtaddr} ${bootdir}/${fdtfile};\0" \
"args_usb=setenv bootargs rootfstype=${usbrootfstype} root=${usbroot} rw console=${console} ${optargs};\0" \
"usbboot=echo usb found on device ${usbdev}; " \
"run findfdt; " \
"usb start; " \
"if run usbloadimage; then " \
"run usbloadfdt; " \
"echo Booting from usb${usbdev} ...; " \
"run args_usb; " \
"bootz ${loadaddr} - ${fdtaddr}; " \
"fi;\0" \
"emmc_linux_boot=" \
"echo Trying to boot Linux from eMMC ...; " \
"setenv mmcdev 1; " \
"if myhello ${count_addr} ; then " \
"if myhello ${media_addr} ; then " \
"echo bootpart 1:2;" \
"setenv bootpart 1:2; " \
"else " \
"echo bootpart 1:3;" \
"setenv bootpart 1:3; " \
"fi; " \
"else " \
"if myhello ${media_addr} ; then " \
"mw.l ${media_addr} 0xdead0001 1; " \
"echo bootpart 1:3;" \
"setenv bootpart 1:3; " \
"else " \
"echo Trying to boot Linux from NOR Flash ...; " \
"mw.l ${media_addr} 0xdead0002 1; " \
"run ramdiskboot; " \
"fi; " \
"fi; " \
"setenv mmcroot /dev/mmcblk0p2 rw; " \
"run mmcboot;\0" \
"emmc_android_boot=" \
"echo Trying to boot Android from eMMC ...; " \
"setenv eval_bootargs setenv bootargs $bootargs; " \
"run eval_bootargs; " \
"setenv mmcdev 1; " \
"setenv fdt_part 3; " \
"setenv boot_part 9; " \
"setenv machid fe6; " \
"mmc dev $mmcdev; " \
"mmc rescan; " \
"part start mmc ${mmcdev} ${fdt_part} fdt_start; " \
"part size mmc ${mmcdev} ${fdt_part} fdt_size; " \
"part start mmc ${mmcdev} ${boot_part} boot_start; " \
"part size mmc ${mmcdev} ${boot_part} boot_size; " \
"mmc read ${fdtaddr} ${fdt_start} ${fdt_size}; " \
"mmc read ${loadaddr} ${boot_start} ${boot_size}; " \
"bootm $loadaddr $loadaddr $fdtaddr;\0" \
"uimageaddr=0x9E0000\0" \
"uimagesize=0x1620000\0" \
"uimageloadaddr=0xA0000000\0" \
"ramdiskboot=echo Image is image.ub ...;" \
"echo Filesystem is Ramdisk ...; " \
"sf probe ${spibusno}:0 48000000; " \
"sf read ${uimageloadaddr} ${uimageaddr} ${uimagesize}; " \
"bootm ${uimageloadaddr}\0" \
#ifdef CONFIG_OMAP54XX
#define DEFAULT_FDT_TI_ARGS \
"findfdt="\
"if test $board_name = tl5728-easy-evm; then " \
"setenv fdtfile tl5728-easy-evm.dtb; fi;" \
"if test $board_name = tl5728-idk; then " \
"setenv fdtfile tl5728-idk.dtb; fi;" \
"if test $board_name = tl5728f-evm; then " \
"setenv fdtfile tl5728f-evm.dtb; fi;" \
"if test $board_name = tl5728-idk && test $idk_pru1_mii = yes; then " \
"setenv fdtfile tl5728-idk-pru1-mii.dtb; fi;" \
"if test $board_name = tl570x-evm; then " \
"setenv fdtfile tl570x-evm.dtb; fi;" \
"if test $fdtfile = undefined; then " \
"echo WARNING: Could not determine device tree to use; fi; \0" \
#define CONFIG_BOOTCOMMAND \
"if test ${dofastboot} -eq 1; then " \
"echo Boot fastboot requested, resetting dofastboot ...;" \
"setenv dofastboot 0; saveenv;" \
"echo Booting into fastboot ...; " \
"fastboot " __stringify(CONFIG_FASTBOOT_USB_DEV) "; " \
"fi;" \
"if test ${boot_fit} -eq 1; then " \
"run update_to_fit;" \
"fi;" \
"run findfdt; " \
"run envboot; " \
"run mmcboot;" \
"run emmc_linux_boot; " \
"run emmc_android_boot; " \
""
#endif /* CONFIG_OMAP54XX */
#endif /* __TI_BOOT_H */
四、 image.ub镜像文件制作
its文件配置:
根文件系统:rootfs
制作image.ub步骤:
1)find ./ | cpio -o -H newc | gzip -9 > …/rootfs.cpio.gz
(压缩生成 rootfs.cpio.gz,在根文件系统目录下执行该命令)
2)mkimage -f fitImage-am5728.its image.ub (制作镜像 image.ub)
对于rootfs.cpio.gz格式的文件,通过以下命令解压生成根文件系统。
gzip -d rootfs.cpio.gz (解压 rootfs.cpio.gz 生成 rootfs.cpio文件)
cpio -i -F …/rootfs.cpio (解压 rootfs.cpio 文件生成 cpio 虚拟根文件系统)
另外,Uboot支持FIT镜像加载还需配置menuconfig
五、 实际操作步骤
操作步骤
- 制作SD卡镜像;执行脚本:mksdboot.sh
- 主控板拨码开关拨至从SD卡启动;
- 使用SD卡启动进入系统;执行脚本:mknorboot-fatri.sh,烧写uboot.img和MLO启动引导镜像 NOR Flash;执行脚本:mkemmcboot-fatri.sh,将SD卡镜像固化到eMMC中。
- 主控板拨码开关拨至从NOR启动;
- 进入系统查看当前所处的文件系统分区,可以通过读写高端内存查看。具体情况请对照升级启动流程图。
六、 致谢
此方案的整体设计源于我的师傅 Richard.Li,具体功能实现由我来完成的。感谢我的师傅 Richard.Li在工作中对我的帮助和指导!