u-boot移植(七)支持nand启动

来源:互联网 发布:unity3d培训机构那个好 编辑:程序博客网 时间:2024/05/17 21:39
 

1、   从nand启动

添加nandflash启动,网络上有很多资料介绍自动识别从哪种方式启动,原理主要是nand启动时,内存同时映射到0地址和40000000地址,所以通过改变其中一个,去检测另一个是否改变来确定是nor启动,还是nand启动。此程序较简单,需要注意的是如果nand启动完了一定把改变的值复原。代码如下

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    0x0000003C*/

ldr  r0, [r1]

cmp   r0, #0        /*判断0x0000003C所指向的4个字节是否被清0*/

bne relocate    /*没有被清0,就是在Nor Flash中启动,则跳到Nor Flash的重定向代码处relocate*/

/* recovery    */

ldr  r0, =(0xdeadbeef)    /*被清0,就是在Nand Flash中启动*/

ldr  r1, =( (4<<28)|(3<<4)|(3<<2) )    /*恢复0x4000003C指向的4字节的数据0xdeadbeef*

str   r0, [r1]

从nand启动代码,定义u-boot在Nand flash中存放的长度为#define LENGTH_UBOOT 0x100000,可以方便修改u-boot因为裁剪和增添大小的改变而占的长度。 首先检测代码位置,如果在内存中,就设置堆栈,然后跳到c语言执行,否则就进行初始化nandflash,然后搬运代码

#define LENGTH_UBOOT 0x100000

#define NAND_CTL_BASE 0x4E000000

 

#ifdef CONFIG_S3C2440

/* Offset */

#define oNFCONF 0x00

#define oNFCONT 0x04

#define oNFCMD 0x08

#define oNFSTAT 0x20

mov       r1, #NAND_CTL_BASE

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

       str   r2, [r1, #oNFCONF]

       ldr  r2, [r1, #oNFCONF]

       

       ldr  r2, =( (1<<4)|(0<<1)|(1<<0) ) @ 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    @ wait

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      @ setup stack pointer

       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

       tst   r0, #0x0

       beq ok_nand_read

 

bad_nand_read:

loop2:

       b     loop2     @ infinite loop

ok_nand_read:

       @ verify

       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_read_ll函数:

#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

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

 

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();

 

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

              *ptr16 = NFDATA16;

              ptr16++;

       }

 

       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;

}

添加把环境变量设置在nand中

#if !defined(CONFIG_NAND_BOOT)

{

#define CONFIG_ENV_IS_IN_FLASH  1

#define CONFIG_ENV_SIZE         0x10000 /* Total Size of Environment Sector */

}

#if defined(CONFIG_NAND_BOOT)

{

#define CONFIG_ENV_IS_IN_NAND   1

#define CONFIG_ENV_OFFSET       0x100000

#define CONFIG_ENV_SIZE         0x20000

}