Uboot启动分析之stage1-Nand-Flash启动部分详解

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,为单独寻址

红字为流程序号:

  1. 首先将NAND FLASH中的前0x1000字节内容拷贝到Boot Internal SRAM中

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;

}

你可能感兴趣的:(Uboot启动分析之stage1-Nand-Flash启动部分详解)