为什么要使能SPL功能呢?
因为根据S3C6410_Internal_ROM_Booting.pdf
文档中的启动流程描述,从Nand启动时,系统上电三星固化好的BL0会自动加载Nand中前8K到iROM; 因为u-boot的大小大于8K,BL0无法将所有的u-boot加载到iROM,u-boot为了解决此类设备的问题,引入了SPL。 由SPL (BL1)负责将u-boot (BL2)拷贝到SDRAM,DDR等,然后跳转到BL2中执行。
SPL相关基础 参考之前的学习笔记:
u-boot SPL学习笔记
SPL相关配置 在arch/arm/mach-s3c6410/Kconfig
中添加SPPORT_SPL
表示支持SPL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #在smdk6410_defconfig中添加 CONFIG_SPL_LOGLEVEL=7 CONFIG_SPL=y CONFIG_SPL_NAND_BOOT=y CONFIG_SPL_LDSCRIPT="board/$(BOARDDIR)/u-boot-spl.lds" CONFIG_SPL_BOARD_INIT=y #因为IROM 8K,编译时检查SPL文件大小 CONFIG_SPL_MAX_SIZE=8192 CONFIG_SPL_TEXT_BASE=0x0C000000 CONFIG_SPL_STACK=0x0C004080 CONFIG_SYS_SPL_MALLOC_START=0x0C003000 CONFIG_SYS_SPL_MALLOC_SIZE=4096 CONFIG_SPL_NAND_SUPPORT=y ## SPL debug #CONFIG_SPL_SERIAL_SUPPORT=y ## support printf() CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y CONFIG_SPL_PRINTF=y #在include/configs/smdk6410.h中定义 /* * BL1 16K (every 2K of one page, 4page * 2K=8K; 4pages=4*4K=16K) */ #define CONFIG_SPL_NAND_RAW_ONLY #define CONFIG_SYS_NAND_U_BOOT_OFFS 4 /*pages, 4*4096=16K*/ #define CONFIG_SYS_NAND_U_BOOT_DST CONFIG_SYS_TEXT_BASE #define CONFIG_SYS_NAND_U_BOOT_SIZE (512*1024) #define CONFIG_SYS_MONITOR_LEN (512*1024)
SPL启动流程 流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #根据u-boot-spl.lds链接脚本,入口为_start _start # vectors.S reset # arch/cpu/arm1176/start.S lowlevel_init # board/samsung/smdk6410/lowlevel_init.S #LED灯,时钟,串口,nand控制器,sdram _main #arch/arm/lib/crt0.S #CONFIG_SPL_STACK 堆栈使用SRAM #分配16字节对齐的栈空间,返回栈指针 board_init_f_alloc_reserve() #common/init/board_init.c #初始化GD board_init_f_init_reserve() #common/init/board_init.c #重载board_init_f(),完成板子必要的操作,必须正常返回,不能直接调用board_init_r() board_init_f() #relocate GD结构,如果定义CONFIG_SPL_STACK_R #对于一些board_init_r()运行需要更多栈空间的情况,可以定义,然后进行relocate,从而使用SDRAM栈空间 spl_relocate_stack_gd() #清除BSS #调用board_init_r mov r0, r9 /* gd_t */ ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */ ldr pc, =board_init_r board_init_r() #common/spl/spl.c spl_set_bd() #common/spl/spl.c spl_init() #common/spl/spl.c spl_common_init() #启动流程记录,主要用于log bootstage_init() #include/bootstage.h #of_control dts相关初始化(可选,未启用) #dm相关初始化(可选,未启用) #定时器初始化,10ms timer_init() #arch/arm/mach-s3c6410/timer.c #是否初始化板子其他硬件(可选未启用),通过定义CONFIG_BOARD_INIT启用;需要自己实现 spl_board_init() #启动次数计数(未使用) bootcount_inc() #配置启动顺序,这里选择的是:BOOT_DEVICE_NAND board_boot_order() spl_boot_device() #board/samsung/smdk6410.c #从nand启动 boot_from_devices() #common/spl/spl.c #spl_ll_find_loader()从`.u_boot_list`段中查找boot入口, spl_ll_find_loader() #拷贝BL2到SDRAM spl_load_image() #common/spl/spl_nand.c nand_init() #board/samsung/smdk6410/smdk6410.c nand_spl_load_image() #board/samsung/smdk6410/smdk6410.c spl_set_header_raw_uboot() #common/spl/spl.c spl_image->size = CONFIG_SYS_MONITOR_LEN spl_image->entry_point = CONFIG_SYS_UBOOT_START; spl_image->load_addr = CONFIG_SYS_TEXT_BASE; nand_deselect() #board/samsung/smdk6410.c #跳转前的准备 spl_board_prepare_for_boot() #跳转到u-boot(BL2) jump_to_image_no_args()
gd在哪里定义的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #ifdef __clang__ #else #ifdef CONFIG_ARM64 #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("x18" ) #else #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r9" ) #endif #endif
需要自己实现哪些函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 int timer_init (void ) { S3C64XX_TIMERS *const timers = S3C64XX_GetBase_TIMERS(); timers->TCFG0 = 0x0f00 ; if (timer_load_val == 0 ) { timer_load_val = get_PCLK() / (2 * 16 * 100 ); } lastdec = timers->TCNTB4 = timer_load_val; timers->TCON = (timers->TCON & ~0x00700000 ) | TCON_4_AUTO | TCON_4_UPDATE; timers->TCON = (timers->TCON & ~0x00700000 ) | TCON_4_AUTO | COUNT_4_ON; timestamp = 0 ; return (0 ); } void nand_init (void ) { } void nand_deselect (void ) { } void board_init_f (ulong dummy) { }
nand_spl_load_image()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 #include <common.h> #include <nand.h> #include <asm/arch/hardware.h> #include <asm/arch/nand-regs.h> #include <asm/arch/bootnand.h> #include <asm/io.h> static int nandll_read_page (uchar *buf, ulong addr, int large_block) { uchar *pdest_sdram_addr = (uchar *)buf; int i; int page_size = 512 ; uchar temp = 0 ; if (large_block == 1 ) page_size = 2048 ; if (large_block == 2 ) page_size = 4096 ; if (large_block == 3 ) page_size = 8192 ; NAND_ENABLE_CE(); NFCMD_REG = NAND_CMD_READ0; NFADDR_REG = 0 ; if (large_block) NFADDR_REG = 0 ; NFADDR_REG = (addr)&0xff ; NFADDR_REG = (addr >> 8 ) & 0xff ; NFADDR_REG = (addr >> 16 ) & 0xff ; if (large_block) NFCMD_REG = NAND_CMD_READSTART; NF_TRANSRnB(); for (i = 0 ; i < page_size; i++) { temp = NFDATA8_REG; *pdest_sdram_addr++ = temp; } NAND_DISABLE_CE(); return 0 ; } static int nandll_read_blocks (uint32_t offs, unsigned int size, void *buff, int large_block) { int i; uchar *buf = (uchar *)buff; uint page_shift = 9 ; if (large_block == 1 ) page_shift = 11 ; if (large_block == 2 ) page_shift = 12 ; if (large_block == 2 ) { for (i = (0 + offs); i < (offs + (size >> page_shift)); i++, buf += (1 << page_shift)) { nandll_read_page(buf, i, large_block); } } else { for (i = (0 + offs); i < (offs + (size >> page_shift)); i++, buf += (1 << page_shift)) { nandll_read_page(buf, i, large_block); } } return 0 ; } int nand_spl_load_image (uint32_t offs, unsigned int size, void *buf) { int large_block = 0 ; int i; vu_char id; NAND_ENABLE_CE(); NFCMD_REG = NAND_CMD_RESET; NF_TRANSRnB(); NFCMD_REG = NAND_CMD_READID; NFADDR_REG = 0x00 ; NF_TRANSRnB(); for (i = 0 ; i < 200 ; i++) ; int factory = NFDATA8_REG; id = NFDATA8_REG; int cellinfo = NFDATA8_REG; int tmp = NFDATA8_REG; int childType = cellinfo; if (id > 0x80 ) { large_block = 1 ; } if (id == 0xd5 && childType == 0x94 ) { large_block = 2 ; } if (id == 0xd5 && childType == 0x14 ) { large_block = 2 ; } if (id == 0xd5 && childType == 0x84 ) { large_block = 3 ; } if (id == 0xd7 ) { large_block = 2 ; } if (factory == 0x2c && id == 0x48 ) { large_block = 2 ; } if (factory == 0x2c && id == 0x38 ) { large_block = 2 ; } nandll_read_blocks(offs, size, buf, large_block); NAND_DISABLE_CE(); return 0 ; }
有关于nand flash的地址问题,这里使用的是MT29F8G08ABABAWP ,datasheet关于地址的描述如下:
LUN or Die结构图:
列地址(column addr) –页内地址CA0-CA11 12bit
行地址(row addr) – 页地址PA0-PA6 7bit, 块地址BA7-BA17 11bit
时序图:
如何跳转到BL2的 通过jump_to_image_no_args()
函数,通过函数指针,转换城汇编就是通过ldr pc, =entry_point
:
1 2 3 4 5 6 7 8 9 10 11 12 __weak void __noreturn jump_to_image_no_args (struct spl_image_info *spl_image) { typedef void __noreturn (*image_entry_noargs_t )(void ); image_entry_noargs_t image_entry = (image_entry_noargs_t )spl_image->entry_point; debug("image entry point: 0x%lX\n" , spl_image->entry_point); image_entry(); }
SPL & u-boot合并成一个文件 因为u-boot烧写到nand flash中,是通过飞凌提供的uboot1.1.6源码编译的sd卡启动版本u-boot;
所以按照sd卡中的u-boot烧写流程,需要将SPL(BL1)
和u-boot(BL2)
合并成一个文件。
根据三星固化的BL0程序,上电时只读取每个page的前2K,一共读取4page合并成8K,所以BL2的偏移地址为8192 。
合并shell脚本run_combine.sh, 位于u-boot源码的根目录:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 # !/bin/bash SPL=./build/spl/u-boot-spl.bin UBOOT=./build/u-boot.bin TMP_FILL=./build/tmp_ff.bin COMBINE=./u-boot.bin # caculate spl file size SPL_SIZE=`ls -l $SPL | awk '{print $5}' | tr -d '\n'` BL1_SIZE=8192 #20K BL1_FILL_SIZE=`expr $BL1_SIZE - $SPL_SIZE` echo "----spl size=$SPL_SIZE byte, need fill $BL1_FILL_SIZE byte." echo "----create $TMP_FILL...." rm -rf $TMP_FILL tr '\000' '\377' < /dev/zero | dd of=$TMP_FILL bs=1 count=$BL1_FILL_SIZE echo "----combine spl & u-boot...." rm -rf $COMBINE cat $SPL $TMP_FILL $UBOOT > $COMBINE echo "----Done."
参考
关于Nand Flash行地址和列地址的计算