u-boot 的NAND启动方式

来源:互联网 发布:eclipse导入jre源码 编辑:程序博客网 时间:2024/05/26 19:20
   

u-boot-2010-06在mini2440上的移植

注意,这些代码有个问题就是要把!define (CONFIG_S3C2440)变成!defined (CONFIG_S3C2440)

4.1 支持u-boot从Nand flash启动

目前u-boot中还没有对2440上Nand Flash的支持,也就是说要想u-boot从Nand Flash上启动得自己去实现了。

首先,在include/configs/mini2440.h头文件中定义Nand要用到的宏和寄存器,如下:

#gedit include/configs/my2440.h  //在文件末尾加入以下Nand Flash相关定义

/*

 * Nand flash register and envionment variables

 */

#define CONFIG_S3C2440_NAND_BOOT  1

#define NAND_CTL_BASE  0x4E000000  //Nand Flash配置寄存器基地址,查2440手册可得知

#define bINT_CTL(Nb)   __REG(INT_CTL_BASE+(Nb))

#define UBOOT_RAM_BASE  0x33f80000

#define STACK_BASE  0x33F00000     //定义堆栈的地址

#define STACK_SIZE  0x8000         //堆栈的长度大小

 

其次,修改cpu/arm920t/start.S这个文件,使u-boot从Nand Flash启动,在上一节中提过,u-boot默认是从Nor Flash启动的。修改部分如下:

#gedit cpu/arm920t/start.S

/*注意:在上一篇Nor Flash启动中,我们为了把u-boot用supervivi下载到内存中运行而屏蔽掉这段有关CPU初始化的代码。而现在我们要把u-boot下载到Nand Flash中,从Nand Flash启动,所以现在要恢复这段代码。*/

 

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

    bl cpu_init_crit

#endif

 

#if 0 //屏蔽掉u-boot中的从Nor Flash启动部分

#ifndef CONFIG_SKIP_RELOCATE_UBOOT

relocate:               /* relocate U-Boot to RAM */

    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

 

    ldr r2, _armboot_start

    ldr r3, _bss_start

    sub r2, r3, r2      /* r2 <- size of armboot */

    add r2, r0, r2      /* r2 <- source end address */

 

copy_loop:

    ldmia r0!, {r3-r10}   /* copy from source address [r0] */

    stmia r1!, {r3-r10}   /* copy to   target address [r1] */

    cmp r0, r2          /* until source end addreee [r2] */

    ble copy_loop

#endif /* CONFIG_SKIP_RELOCATE_UBOOT */

#endif

 

 

//下面添加2440中u-boot从Nand Flash启动

 

#ifdef CONFIG_S3C2440_NAND_BOOT

 

#define  oNFCONF  0x00

#define  oNFCONT  0x04

#define  oNFCMD   0x08

#define  oNFSTAT   0x20

#define  LENGTH_UBOOT  0x60000

 

    mov r1, #NAND_CTL_BASE   //复位Nand Flash

    ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )

    str r2, [r1, #oNFCONF]   //设置配置寄存器的初始值,参考s3c2440手册

    ldr r2, [r1, #oNFCONF]

 

    ldr r2, =( (1<<4)|(0<<1)|(1<<0) )

    str r2, [r1, #oNFCONT]   //设置控制寄存器

    ldr r2, [r1, #oNFCONT]

 

    ldr r2, =(0x6)           //RnB Clear

    str r2, [r1, #oNFSTAT]

ldr r2, [r1, #oNFSTAT]

 

    mov r2, #0xff            //复位command

    strb r2, [r1, #oNFCMD]

    mov r3, #0               //等待

nand1:

    add r3, r3, #0x1

    cmp r3, #0xa

    blt nand1

 

nand2:

    ldr r2, [r1, #oNFSTAT]   //等待就绪

    tst r2, #0x4

    beq nand2

 

    ldr r2, [r1, #oNFCONT]

    orr r2, r2, #0x2         //取消片选

    str r2, [r1, #oNFCONT]

 

    //get read to call C functions (for nand_read())

    ldr sp, DW_STACK_START   //为C代码准备堆栈,DW_STACK_START定义在下面

    mov fp, #0              

 

    //copy U-Boot to RAM

    ldr r0, =TEXT_BASE//传递给C代码的第一个参数:u-boot在RAM中的起始地址

    mov r1, #0x0      //传递给C代码的第二个参数:Nand Flash的起始地址

    mov r2, # LENGTH_UBOOT //传递给C代码的第三个参数:u-boot的长度大小(128k)

    bl nand_read_ll   //此处调用C代码中读Nand的函数,现在还没有要自己编写实现

    tst r0, #0x0

    beq ok_nand_read

 

bad_nand_read:

    loop2: b loop2    //infinite loop

 

ok_nand_read: //检查搬移后的数据,如果前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                      //CONFIG_S3C2440_NAND_BOOT

 

_start_armboot: .word start_armboot //在这一句的下面加上DW_STACK_START的定义

 

.align 2

DW_STACK_START: .word STACK_BASE+STACK_SIZE-4

再次,在board/samsung/mini2440/目录下新建一个nand_read.c文件,在该文件中来实现上面汇编中要调用的nand_read_ll函数,代码如下:

#gedit board/samsung/mini2440/nand_read.c  //新建一个nand_read.c文件,记得保存

#include <common.h>

#include <linux/mtd/nand.h>

 

#define __REGb(x) (*(volatile unsigned char *)(x))

#define __REGw(x) (*(volatile unsigned short *)(x))

#define __REGi(x) (*(volatile unsigned int *)(x))

 

#define NF_BASE 0x4e000000

#if defined(CONFIG_S3C2410) && !define (CONFIG_S3C2440)

#define NFCONF __REGi(NF_BASE + 0x0)

#define NFCMD  __REGb(NF_BASE + 0x4)

#define NFADDR __REGb(NF_BASE + 0x8)

#define NFDATA __REGb(NF_BASE + 0xc)

#define NFSTAT __REGb(NF_BASE + 0x10)

#define NFSTAT_BUSY 1

#define nand_select() (NFCONF &= ~0x800)

#define nand_deselect() (NFCONF |= 0x800)

#define nand_clear_RnB() do {} while (0)

#elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)

#define NFCONF   __REGi(NF_BASE + 0x0)

#define NFCONT   __REGi(NF_BASE + 0x4)

#define NFCMD    __REGb(NF_BASE + 0x8)

#define NFADDR   __REGb(NF_BASE + 0xc)

#define NFDATA   __REGb(NF_BASE + 0x10)

#define NFDATA16 __REGw(NF_BASE + 0x10)

#define NFSTAT   __REGb(NF_BASE + 0x20)

#define NFSTAT_BUSY 1

#define nand_select()    (NFCONT &= ~(1 << 1))

#define nand_deselect()  (NFCONT |= (1 << 1))

#define nand_clear_RnB() (NFSTAT |= (1 << 2))

#endif

 

static inline void nand_wait(void)

{

       int i;

       while (!(NFSTAT & NFSTAT_BUSY))

            for (i=0; i<10; i++);

}

 

struct boot_nand_t {

       int page_size;

       int block_size;

       int bad_block_offset;

       // unsigned long size;

};

 

static int is_bad_block(struct boot_nand_t * nand, unsigned long i)

{

       unsigned char data;

       unsigned long page_num;

       nand_clear_RnB();

       if (nand->page_size == 512) {

              NFCMD = NAND_CMD_READOOB; /* 0x50 */

              NFADDR = nand->bad_block_offset & 0xf;

              NFADDR = (i >> 9) & 0xff;

              NFADDR = (i >> 17) & 0xff;

              NFADDR = (i >> 25) & 0xff;

       } else if (nand->page_size == 2048) {

              page_num = i >> 11; /* addr / 2048 */

              NFCMD = NAND_CMD_READ0;

              NFADDR = nand->bad_block_offset & 0xff;

              NFADDR = (nand->bad_block_offset >> 8) & 0xff;

              NFADDR = page_num & 0xff;

              NFADDR = (page_num >> 8) & 0xff;

              NFADDR = (page_num >> 16) & 0xff;

              NFCMD = NAND_CMD_READSTART;

       } else {

              return -1;

       }

       nand_wait();

       data = (NFDATA & 0xff);

       if (data != 0xff)

               return 1;

       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;

       if (nand->page_size == 512) {

              /* Write Address */

              NFADDR = addr & 0xff;

              NFADDR = (addr >> 9) & 0xff;

              NFADDR = (addr >> 17) & 0xff;

              NFADDR = (addr >> 25) & 0xff;

              } 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;

              } else {

                     return -1;

              }

              nand_wait();

#if defined(CONFIG_S3C2410)&& !define (CONFIG_S3C2440)

              for (i = 0; i < nand->page_size; i++) {

              *buf = (NFDATA & 0xff);

              buf++;

                }

 

#elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)

 

       for (i = 0; i < (nand->page_size>>1); i++) {

              *ptr16 = NFDATA16;

              ptr16++;

       }

#endif

       return nand->page_size;

}

 

static unsigned short nand_read_id()

{

       unsigned short res = 0;

       NFCMD = NAND_CMD_READID;

       NFADDR = 0;

       res = NFDATA;

       res = (res << 8) | NFDATA;

       return res;

}

 

extern unsigned int dynpart_size[];

 

/* 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;

       /* chip Enable */

       nand_select();

       nand_clear_RnB();

       for (i = 0; i < 10; i++)

       ;

       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;

              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;

       }

       /* chip Disable */

       nand_deselect();

       return 0;

}

然后,在board/samsung/mini2440/Makefile中添加nand_read.c的编译选项,使他编译到u-boot中,如下:

COBJS    := mini2440.o flash.o nand_read.o

还有一个重要的地方要修改,在cpu/arm920t/u-boot.lds中,这个u-boot启动连接脚本文件决定了u-boot运行的入口地址,以及各个段的存储位置,这也是链接定位的作用。添加下面两行代码的主要目的是防止编译器把我们自己添加的用于nandboot的子函数放到4K之后,否则是无法启动的。如下:

.text :

{

    cpu/arm920t/start.o    (.text)

    board/samsung/mini2440/lowlevel_init.o (.text)

    board/samsung/mini440/nand_read.o (.text)

    *(.text)

}

最后编译u-boot,生成u-boot.bin文件。然后先将mini2440开发板调到Nor启动档,利用supervivi的a命令将u-boot.bin下载到开发板的Nand Flash中,再把开发板调到Nand启动档,打开电源就从Nand Flash启动了。