为什么要使能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:

image-20220326225836128
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
//arch/arm/include/asm/global_data.h

#ifdef __clang__ //如果使用__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") //32位arm,使用r9来存放gd首地址
#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
/*
* timer_init(): arm/arch/mach-s3c6410/timer.c
*/

int timer_init(void)
{
S3C64XX_TIMERS *const timers = S3C64XX_GetBase_TIMERS();

/* use PWM Timer 4 because it has no output */
/* prescaler for Timer 4 is 16 */
timers->TCFG0 = 0x0f00;
if (timer_load_val == 0) {
/*
* for 10 ms clock period @ PCLK with 4 bit divider = 1/2
* (default) and prescaler = 16. Should be 10390
* @33.25MHz and 15625 @ 50 MHz
*/
timer_load_val = get_PCLK() / (2 * 16 * 100);
}

/* load value for 10 ms timeout */
lastdec = timers->TCNTB4 = timer_load_val;
/* auto load, manual update of Timer 4 */
timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | TCON_4_UPDATE;
/* auto load, start Timer 4 */
timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | COUNT_4_ON;
timestamp = 0;

return (0);
}

/*
* nand_init(): board/samsung/smdk6410/smdk6410.c
*/
void nand_init(void)
{
//do nothing, cause has init in lowlevel_init.S
}

/*
* nand_deselect(): board/samsung/smdk6410/smdk6410.c
*/
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
//board/samsung/smdk6410/bl1_nand_copy.c

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

/*
* address format
* 1 0 7 0|7 6 0| 3 0|7 0
* --------------------------------------------
* | block(11bit) | page(7bit) | offset(12bit) |
* --------------------------------------------
*/

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;

/* Write Address */

/*页内地址11bit: 0,读一整页所以从0开始*/
NFADDR_REG = 0;
if (large_block)
NFADDR_REG = 0;

/*页地址0-bit6, 块地址0:bit7:*/
NFADDR_REG = (addr)&0xff;

/*块地址1-8: */
NFADDR_REG = (addr >> 8) & 0xff;

/*块地址9-10*/
NFADDR_REG = (addr >> 16) & 0xff;

if (large_block)
NFCMD_REG = NAND_CMD_READSTART;

NF_TRANSRnB();

/* for compatibility(2460). u32 cannot be used. by scsuh */
for (i = 0; i < page_size; i++)
{
temp = NFDATA8_REG;
*pdest_sdram_addr++ = temp;
/*
if(i<32 && addr == 4) {
asm_print_hex(temp);
asm_puts(" ");
}
*/
}
NAND_DISABLE_CE();
return 0;
}


// #define NF8_ReadPage_Adv(a,b,c) (((int(*)(uint32, uint32, uint8*))(*((uint32 *)0x0C004004)))(a,b,c))

/*
* Read data from NAND.
*/
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;

/* Read pages */
if (large_block == 2)
page_shift = 12;

if (large_block == 2)
{
/* Read pages,仅读取BL2 */
for (i = (0 + offs); i < (offs + (size >> page_shift)); i++, buf += (1 << page_shift))
{
nandll_read_page(buf, i, large_block);
}
//asm_puts("nandll_read_blocks Done\n");
}
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();

/* wait for a while */
for (i = 0; i < 200; i++)
;

int factory = NFDATA8_REG;
id = NFDATA8_REG;

int cellinfo = NFDATA8_REG;
int tmp = NFDATA8_REG;

//int childType=tmp & 0x03; //Page size
int childType = cellinfo; //Page size

if (id > 0x80)
{
large_block = 1;
}

if (id == 0xd5 && childType == 0x94) //K9GAG08U0D
{
large_block = 2;
}
if (id == 0xd5 && childType == 0x14) //K9GAG08U0M
{
large_block = 2;
}
if (id == 0xd5 && childType == 0x84) //K9GAG08U0E
{
large_block = 3;
}
if (id == 0xd7) //K9LBG08U0D
{
large_block = 2;
}
if (factory == 0x2c && id == 0x48) //MT29F16G08ABACAWP
{
large_block = 2;
}
if (factory == 0x2c && id == 0x38) //MT29F8G08ABABAWP
{
large_block = 2;
//asm_puts("large_block=2\n");
}

nandll_read_blocks(offs, size, buf, large_block);

NAND_DISABLE_CE();

return 0;
}

有关于nand flash的地址问题,这里使用的是MT29F8G08ABABAWP,datasheet关于地址的描述如下:

LUN or Die结构图:

LUN or Die结构图

列地址(column addr) –页内地址CA0-CA11 12bit

行地址(row addr)– 页地址PA0-PA6 7bit, 块地址BA7-BA17 11bit

时序图:

Page Read时序图

如何跳转到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);
//asm_puts("will jump to u-boot...\n");
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行地址和列地址的计算