从NAND Flash启动的原理很简单,就是利用S3C2440内部4K大小的SRAM,存储在NAND Flash中的代码不能被执行,而S3C2440在从NAND Flash启动把NAND Flash的前4k代码复制到SRAM中运行,U-boot支持从NAND Flash启动的方法就是利用这前4K代码完成SDRAM的初始化(SDRAM有64M),然后还要完成从U-boot代码从NAND Flash中复制到SDRAM中,然后再跳转到SDRAM中去运行完整的U-boot。
为了便于系统启动的方便,可以在start.S文件中添加代码以识别系统是从NAND Flash启动还是从NOR Flash启动,从S3C2440芯片手册中可以看到
到OM[1:0]都为0时,说明是从NAND Flash启动,01和10都是从NOR Flash启动,OM[1:0]就是寄存器BWSCON的第2位~第1位(DW0)
一、添加NOR Flash启动和NAND Flash启动的识别
修改arch/arm/cpu/arm920t/start.S,首先将217行附近修改为:
- #ifndef CONFIG_SKIP_LOWLEVEL_INIT
- bl cpu_init_crit
- #endif
- #define BWSCON 0x48000000
- ldr r0, =BWSCON
- ldr r0, [r0]
- ands r0, r0, #0x6
- tst r0, #0x0
- bne norflash_boot /*OM[1:0] != 0, 跳转到NOR FLASH 启动处*/
- /*判断uboot是从nand flash启动还是从 nor flash启动*/
在220行附近将:
- #ifndef CONFIG_SKIP_RELOCATE_UBOOT
- relocate:
修改为
- norflash_boot:
- #ifndef CONFIG_SKIP_RELOCATE_UBOOT
- relocate:
二、添加NAND Flash的U-boot代码从NAND FLash到SDRAM搬移的代码
在前面修改的 bne norflash_boot ,227行后添加
- /*****************************nand boot**************************/
- nandflash_boot:
- #define LENGTH_UBOOT 0x40000
- #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))
- str r2,[r1,#oNFCONF]
- ldr r2,[r1,#oNFCONF]
- ldr r2,=((1<<4)|(1<<1)|(1<<0)) @Active low CE control
- str r2,[r1,#oNFCONT]
- ldr r2,[r1,#oNFCONT]
- @ get read to call C functions
- ldr sp,DW_STACK_START @setup stack point
- mov fp,#0 @no previous frame, so fp = 0
- @copy Uboot 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 @ compare 4k code from sram to sdram
- go_next:
- ldr r3, [r0], #4
- ldr r4, [r1], #4
- teq r3, r4
- bne notmatch
- subs r2,r2,#4
- tst r2,#0x0 @do not forget the instruction if have not this command the uboot can't break the loop
- beq stack_setup
- bne go_next
- notmatch:
- loop3:
- b loop3 @infinite loop
- /*****************************nand boot**************************/
上面这部分代码首先初始化了NAND Flash寄存器,然后进行了一个函数调用(这个函数中完成了代码搬移)后面则是对复制出来的数据进行一个简单的校验。在327行附近添加为:
- _start_armboot: .word start_armboot
- #define STACK_BASE 0x33f00000
- #define STACK_SIZE 0x10000
- .align 2
- DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
添加函数的栈调用空间
三、添加C语言从NAND Flash搬移代码部分
首先在board/fl2440目录下新建一个名为nand_read.c的文件,其内容如下:
- /*
- * vivi/s3c2410/nand_read.c: Simple NAND read functions for booting from NAND
- *
- * Copyright (C) 2002 MIZI Research, Inc.
- *
- * Author: Hwang, Chideok <hwang@mizi.com>
- * Date : $Date: 2002/08/14 10:26:47 $
- *
- * $Revision: 1.6 $
- * $Id: param.c,v 1.9 2002/07/11 06:17:20 nandy Exp
- *
- */
- #include <config.h>
- #define __REGb(x) (*(volatile unsigned char *)(x))
- #define __REGi(x) (*(volatile unsigned int *)(x))
- #define NF_BASE 0x4e000000
- /*S3C2440与S3C2440 nandflash控制寄存器地址不同需要进行修改*/
- #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 NFSTAT __REGb(NF_BASE + 0x20)
- #define NAND_CHIP_ENABLE (NFCONT &= ~(1<<1))
- #define NAND_CHIP_DISABLE (NFCONT |= (1<<1))
- #define NAND_CLEAR_RB (NFSTAT |= (1<<2))
- #define NAND_DETECT_RB {while(! (NFSTAT&(1<<2)));}
- #define NAND_ECC_CLEAR (NFCONT |= 0x10)
- /*see data sheet P193*/
- #define BUSY 1
- static inline void wait_idle(void) {
- int i;
- while(!(NFSTAT & BUSY))
- for(i=0; i<10; i++);
- }
- /*根据fl2440的nand flash 作相应修改*/
- #define NAND_SECTOR_SIZE 2048
- #define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1)
- /* low level nand read function */
- int
- nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
- {
- int i, j;
- int pagenum;
- if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {
- return -1; /* invalid alignment */
- }
- /* chip Enable */
- NAND_CHIP_ENABLE;
- for(i = start_addr; i < (start_addr + size);) {
- /* READ0 */
- NAND_CLEAR_RB;
- for(j = 0; j < 10; j++);
- pagenum = i >> 11;
- /* Write Address
- 该步骤详见nand flash(k9f2g08u0a)手册p17
- 参考fl2440开发板nand_lowlevel.c文件中ReadPage函数
- */
- NFCMD = 0x0;
- NFADDR = 0;
- NFADDR = 0;
- NFADDR = pagenum & 0xff;
- NFADDR = (pagenum >> 8) & 0xff;
- NFADDR = (pagenum >> 16) & 0xff;
- /*如果不理解见数据手册nand flashP9*/
- NFCMD = 0x30;
- wait_idle();
- for(j=0; j < NAND_SECTOR_SIZE; j++) {
- *buf = (NFDATA & 0xff);
- buf++;
- }
- i += NAND_SECTOR_SIZE;
- }
- NAND_CHIP_DISABLE;
- /* chip Disable */
- return 0;
- }
开发板的NAND Flash型号为k9f2g08u0a,它的容量是256MByte,NAND Flash的最小读取单位是以页为单位的,即2KByte(12位)每页,共128K页(17位),而地址线是8位的,因此当需要读取一页时,需分周期发送页地址,这款芯片规定前两个地址周期发送页内地址,后面的三周期发送页编号,它的流程如表1:
表1 NAND Flash地址
|
I/O 0 |
I/O 1 |
I/O 2 |
I/O 3 |
I/O 4 |
I/O 5 |
I/O 6 |
I/O 7 |
第一周期 |
A0 |
A1 |
A2 |
A3 |
A4 |
A5 |
A6 |
A7 |
第二周期 |
A8 |
A9 |
A10 |
A11 |
0 |
0 |
0 |
0 |
第三周期 |
A12 |
A13 |
A14 |
A15 |
A16 |
A17 |
A18 |
A19 |
第四周期 |
A20 |
A21 |
A22 |
A23 |
A24 |
A25 |
A26 |
A27 |
第五周期 |
A28 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
我们在读取uboot.bin时,直接按页读取,所以前两周期地址我们设成了全0,后面的3周期实际就是也编号。
修改board/fl2440/Makefile文件,使得nand_read.c能被编译到u-boot.bin中,修改28行为:
- COBJS := fl2440.o nand_read.o flash.o
我们还知道由于我们需要在前4K代码中完成代码的搬移,而搬移的代码集中在start.S和nand_read.c中,而U-boot的编译过程并不能保证nand_read.c能在前4K代码中,所以需要我们手动设置,修改文件arch/arm/cpu/arm920t/u-boot.lds文件40行为:
- .text :
- {
- arch/arm/cpu/arm920t/start.o (.text)
- board/fl2440/lowlevel_init.o (.text)
- board/fl2440/nand_read.o (.text)
- *(.text)
- }
这样,编译器在编译时能保证start.S和nand_read.c编译出的二进制代码在前4K。
修改include/configs/fl2440.h中,添加相关的宏定义以支持NAND Flash的串口操作命令:
- #define CONFIG_CMD_NAND /*****add by yanghao*****/
- <p>#if defined(CONFIG_CMD_NAND)</p><p>#define CONFIG_NAND_S3C2410
- #define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND devices */
- #define NAND_MAX_CHIPS 1
- #define CONFIG_SYS_NAND_BASE 0x4E000000</p>#endif
然后将开发板设置成为从NAND Flash启动,将编译出来的u-boot.bin烧写入NAND Flash,上电这样就完成了从NAND Flash的启动。