6、是u-boot支持从NandFlash启动
先看S2C2440A的datasheet第五章 Memory Controller中的那个映射图,也就是那个Figure 5-1 S3C2440A Memory Map after Reset,从理论上来讲,对于0x40000000以后的内存,只有在Nor boot的时候才存在;而在 Nand boot的时候它被映射到了0x00000000,在0x40000000以后不存在内存。如果我们在启动的时候,将一些特定的数据写入0x40000000 - 0x40001000之间,那么按照手册上的说法,如果回读的结果和写入的一致说明是Nor boot,否则就是Nand boot。
这种方法理论上是可以的,但是实际上,那个图是错的!真正的映射图如下:
从上图可以看出,无论是Nor启动还是Nand启动,这4K的内部SRAM都被映射到了0x40000000,而在Nand boot的时候,这块内存同时被映射到了0x00000000,那么一开始提出的办法就不可行了,而且在Nand boot的时候,写入0x40000000 - 0x40001000还会破坏自身的程序。
可以这样解决,在启动的时候,用程序将0x4000000 - 0x40001000中的某些位置清零,如果回读0x00000000 - 0x00001000中的相应位置为零,说明是Nand boot,如果是原来的数据(一定要选非零的位置)就是Nor boot。判断完如果是Nand boot,还要恢复被改动的数据,再进入自拷贝阶段。
只要检测的位置合理,这个方法是可行的。现在的关键是选什么位置的数据最好呢?可以选在start.S文件开头,全局中断向量之后的:
.balignl 16,0xdeadbeef
选这个数据作为检测位置的理由如下:
(1)他是非零数,而且数据是确定的:0xdeadbeef;
(2)他的位置是固定的:0x0000003c(0x4000003c);
(3)他在检测程序之前,不会影响程序的向下运行;
(4)他不属于程序,他是一个程序中的魔数(Magic Number),用魔数来检测也比较合理。
检测步骤如下:
在启动的时候,将0x4000003c位置开始的四个字节清零,然后读取0x0000003c位置开始的四个字节。如果回读的结果为零,说明是Nand boot,否则就是Nor boot(为了保险还可以判断是否为0xdeadbeef,不是的话就说明有未知错误,死循环!)。但是最后有一点很重要:如果是Nand boot,必须要复原清零的数据。原因是:在nand boot过后,会核对内部SRAM中的4K程序,和从Nand中拷贝到SDRAM的前4K程序是否一致,如果不一致会进入死循环。
现在就动手修改u-boot源码,使其支持Nor 和 Nand双启动,主要是是修改start.S这个文件,代码如下:
- #ifndef CONFIG_SKIP_RELOCATE_UBOOT
- 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 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
- /* recovery */
- ldr r0, =(0xdeadbeef)
- ldr r1, =( (4<<28)|(3<<4)|(3<<2) )
- str r0, [r1]
-
- #define LENGTH_UBOOT 0x60000
- #define NAND_CTL_BASE 0x4E000000
- #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) )
- 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
- relocate: /* relocate U-Boot to RAM */
- ldr r1, =(0xdeadbeef)
- cmp r0, r1
- bne loop3
- 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 */
- /* Set up the stack */
- stack_setup:
- ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
- sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */
- sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo */
- #ifdef CONFIG_USE_IRQ
- sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
- #endif
- sub sp, r0, #12 /* leave 3 words for abort-stack */
- clear_bss:
- ldr r0, _bss_start /* find start of bss segment */
- ldr r1, _bss_end /* stop here */
- mov r2, #0x00000000 /* clear */
- clbss_l:str r2, [r0] /* clear loop... */
- add r0, r0, #4
- cmp r0, r1
- ble clbss_l
- ldr pc, _start_armboot
- _start_armboot: .word start_armboot
- #define STACK_BASE 0x33f00000
- #define STACK_SIZE 0x10000
- .align 2
- DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
另外,还需在board/samsung/smdk2440/目录下新建一个nand_read.c文件,用来实现上面汇编中要调用的nand_read_ll函数,代码如下:
- #include
- #include
- #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;
- };
- 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; /* 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) {
- 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; /* 2048 */
- 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;
- }
- 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 (nand_id == 0xec76 || /* Samsung K91208 */
- nand_id == 0xad76 ) { /*Hynix HY27US08121A*/
- nand.page_size = 512;
- nand.block_size = 16 * 1024;
- nand.bad_block_offset = 5;
- } 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;
- } else {
- return -1;
- }
- 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/smdk2440/Makefile中添加nand_read.c的编译选项,使他编译到u-boot中,如下:
COBJS := smdk2440.o flash.o nand_read.o |
还有一个重要的地方要修改,在cpu/arm920t/u-boot.lds中,这个u-boot启动连接脚本文件决定了u-boot运行的入口地址,以及各个段的存储位置,这也是链接定位的作用。添加下面两行代码的主要目的是防止编译器把我们自己添加的用于nand boot的子函数放到4K之后,否则是无法启动的。如下:
.text : { cpu/arm920t/start.o (.text) board/samsung/smdk2440/lowlevel_init.o (.text) board/samsung/smdk2440/nand_read.o (.text) *(.text) } |
最后编译u-boot,生成u-boot.bin文件。然后将u-boot.bin下载到开发板的Nand Flash中,把开发板调到Nand启动档,打开电源就从Nand Flash启动了,启动结果图如下:
阅读(974) | 评论(0) | 转发(0) |