Uboot启动分析之stage1-Nand-Flash启动部分详解
根据Uboot的Stage1程序,在关闭watchdog 和 初始化时钟后就会判断从Norflash 还是NandFlash启动.
2440的Memory map 如下图
NorFlash的启动比较简单,直接判定当前执行位置,如果不在RAM中就执行relocate,将UBoot代码copy到RAM中;从NandFlash则不同,由于CPU会首先将NandFlash的前4K自动copy到Boot Internal SRAM中,而且只有4K大小,程序必须在这4K中将后面的代码copy到RAM中,并跳转到RAM中运行。
首先介绍NandFlash启动整体流程,如下图:
其中整理了一个流程图,分为3个存储器:
1 Boot Internal SRAM , 接在BANK0,起始地址为0x0
2 RAM , 接在BANK6,起始地址为0x3000 0000
3 NAND FLASH,为单独寻址
红字为流程序号:
2. 从Boot Internal SRAM的0x0地址处开始执行指令
3. 将Uboot从Flash拷贝到RAM中
4. 执行ldr pc, _start_armboot
下面详细分析NandFlash的代码( Uboot版本为U-boot-2009.11_tekkaman )
1 start.SNandFlash部分分析 cpu/arm920t/start.S
前面的代码略去,从判断启动方式开始
/***************** CHECK_CODE_POSITION ******************************************/
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup
/***************** CHECK_CODE_POSITION ******************************************/
/***************** CHECK_BOOT_FLASH ******************************************/
/*tekkaman采用的是在0x4000_003c写数据的方法检测启动方式具体见(http://blog.chinaunix.net/space.php?uid=20543672&do=blog&id=94363)*/
ldr r1, =( (4<<28)|(3<<4)|(3<<2) ) /* address of Internal SRAM 0x4000003C*/
mov r0, #0 /* r0 = 0 */
str r0, [r1]
mov r1, #0x3c /* address of men 0x0000003C*/
ldr r0, [r1]
cmp r0, #0
bne relocate @跳转至NorFlash启动
/* recovery */
ldr r0, =(0xdeadbeef)
ldr r1, =( (4<<28)|(3<<4)|(3<<2) )
str r0, [r1]
/***************** CHECK_BOOT_FLASH end**************************************/
/***************** NAND_BOOT *************************************************/
#define LENGTH_UBOOT 0x60000
#define NAND_CTL_BASE 0x4E000000
#ifdef CONFIG_S3C2440 //具体参见2440手册
#define oNFCONF 0x00
#define oNFCONT 0x04
#define oNFCMD 0x08
#define oNFSTAT 0x20
@ reset NAND
mov r1, #NAND_CTL_BASE
ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )
@CLE & ALE =3, TWRPH0= 7,TWRPH1 =7, BusWitdth= 8bit
str r2, [r1, #oNFCONF]
ldr r2, [r1, #oNFCONF]
ldr r2, =( (1<<4)|(0<<1)|(1<<0) )
@initialize ECC deconder/encoder;Enable chip select;NAND flash controller endable,
@Active low CE Control
str r2, [r1, #oNFCONT]
ldr r2, [r1, #oNFCONT]
ldr r2, =(0x6) @ RnB Clear
str r2, [r1, #oNFSTAT]
ldr r2, [r1, #oNFSTAT]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
mov r3, #0 @ 循环等待
nand1:
add r3, r3, #0x1
cmp r3, #0xa
blt nand1
nand2:
ldr r2, [r1, #oNFSTAT] @ wait ready,查询
tst r2, #0x4
beq nand2
ldr r2, [r1, #oNFCONT]
orr r2, r2, #0x2 @ Flash Memory Chip Disable
str r2, [r1, #oNFCONT]
@ get read to call C functions (for nand_read())
ldr sp, DW_STACK_START @为跳转C函数准备堆栈
mov fp, #0 @ no previous frame, so fp=0
@ copy U-Boot to RAM
ldr r0, =TEXT_BASE
mov r1, #0x0
mov r2, #LENGTH_UBOOT
bl nand_read_ll @ 跳转到C函数, r0,r1,r2为传递给函数的参数,完成代码的copy,函数分析见后
tst r0, #0x0 @函数返回参数在r0中
beq ok_nand_read
bad_nand_read:
loop2:
b loop2 @ infinite loop
ok_nand_read:
@ verify比较Boot internal SRAM中的数据与copy的前4K是否相同
mov r0, #0
ldr r1, =TEXT_BASE
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch
subs r2, r2, #4
beq stack_setup
bne go_next
notmatch:
loop3:
b loop3 @ infinite loop
#endif
/***************** NAND_BOOT *************************************************/
/***************** NOR_BOOT *************************************************/
relocate: 省略。。。 /* relocate U-Boot to RAM */
/***************** NOR_BOOT *************************************************/
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
。。。。。。
2 nand_read_ll 函数分析
U-boot-2009.11_tekkaman/board/tekkamanninja/mini2440/nand_read.c
/* low level nand read function */
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;
unsigned short nand_id;
struct boot_nand_t nand;
nand_select();/* chip Enable */
nand_clear_RnB();
for (i = 0; i < 10; i++);
//读取FlashID,自动识别不同型号的Flash
nand_id = nand_read_id();
if (0) { /* dirty little hack to detect if nand id is misread */
unsigned short * nid = (unsigned short *)0x31fffff0;
*nid = nand_id;
}
if (nand_id == 0xec76 || /* Samsung K91208 */
nand_id == 0xad76 ) { /*Hynix HY27US08121A*/
nand.page_size = 512;
nand.block_size = 16 * 1024;
nand.bad_block_offset = 5;
// nand.size = 0x4000000;
} else if (nand_id == 0xecf1 || /* Samsung K9F1G08U0B */
nand_id == 0xecda || /* Samsung K9F2G08U0B */
nand_id == 0xecd3 ) { /* Samsung K9K8G08 */
nand.page_size = 2048;
nand.block_size = 128 * 1024; //K9F1G08U0B为128M,应该是64*1024
nand.bad_block_offset = nand.page_size;
// nand.size = 0x8000000;
} else {
return -1; // hang
}
if ((start_addr & (nand.block_size-1)) || (size & ((nand.block_size-1))))
return -1; /* invalid alignment */
for (i=start_addr; i < (start_addr + size);) {
#ifdef CONFIG_S3C2410_NAND_SKIP_BAD
if (i & (nand.block_size-1)== 0) {
if (is_bad_block(&nand, i) ||
is_bad_block(&nand, i + nand.page_size)) {
/* Bad block */
i += nand.block_size;
size += nand.block_size;
continue;
}
}
#endif
j = nand_read_page_ll(&nand, buf, i);//读取一页的数据
i += j;
buf += j;
}
nand_deselect();/* chip Disable */
return 0;
}
static int nand_read_page_ll(struct boot_nand_t * nand, unsigned char *buf, unsigned long addr)
{
unsigned short *ptr16 = (unsigned short *)buf;
unsigned int i, page_num;
nand_clear_RnB();
NFCMD = NAND_CMD_READ0;//读取命令 参看flash手册
if (nand->page_size == 512) {
/* Write Address */
NFADDR = addr & 0xff;
NFADDR = (addr >> 9) & 0xff;
NFADDR = (addr >> 17) & 0xff;
NFADDR = (addr >> 25) & 0xff;
/*上面的地址操作与K91208手册上的时序一致*/
} else if (nand->page_size == 2048) {
page_num = addr >> 11; /* addr / 2048 除以页大小*/
/* Write Address */
NFADDR = 0;
NFADDR = 0;
NFADDR = page_num & 0xff;
NFADDR = (page_num >> 8) & 0xff;
NFADDR = (page_num >> 16) & 0xff;
NFCMD = NAND_CMD_READSTART;
/*上面的地址操作与K9F1G08U0B手册上的时序不一致,可能是为了与K9F2G08共用,
* K9F1G08U0B 只需要4个周期
* K9F2G08U0B需要 5 个,下图为K9F1G08U0B的手册截图*/
} else {
return -1;
}
nand_wait();
#elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
for (i = 0; i < (nand->page_size>>1); i++) {
*ptr16 = NFDATA16;
ptr16++;
}
#endif
return nand->page_size;
}