|||
(9)复制U-Boot第二阶段代码到RAM
cpu/arm920t/start.S原来的代码是只支持从NOR Flash启动的,经过修改现在U-Boot在NOR Flash和NAND Flash上都能启动了,实现的思路是这样的:
bl bBootFrmNORFlash /* 判断U-Boot是在NAND Flash还是NOR Flash启动 */
cmp r0, #0 /* r0存放bBootFrmNORFlash函数返回值,若返回0表示NAND Flash启动,否则表示在NOR Flash启动 */
beq nand_boot /* 跳转到NAND Flash启动代码 */
/* NOR Flash启动的代码 */
b stack_setup /* 跳过NAND Flash启动的代码 */
nand_boot:
/* NAND Flash启动的代码 */
stack_setup:
/* 其他代码 */
其中bBootFrmNORFlash函数作用是判断U-Boot是在NAND Flash启动还是NOR Flash启动,若在NOR Flash启动则返回1,否则返回0。根据ATPCS规则,函数返回值会被存放在r0寄存器中,因此调用bBootFrmNORFlash函数后根据r0的值就可以判断U-Boot在NAND Flash启动还是NOR Flash启动。bBootFrmNORFlash函数在board/samsung/mini2440/nand_read.c中定义如下:
int bBootFrmNORFlash(void)
{
volatile unsigned int *pdw = (volatile unsigned int *)0;
unsigned int dwVal;
dwVal = *pdw; /* 先记录下原来的数据 */
*pdw = 0x12345678;
if (*pdw != 0x12345678) /* 写入失败,说明是在NOR Flash启动 */
{
return 1;
}
else /* 写入成功,说明是在NAND Flash启动 */
{
*pdw = dwVal; /* 恢复原来的数据 */
return 0;
}
}
无论是从NOR Flash还是从NAND Flash启动,地址0处为U-Boot的第一条指令“ b start_code”。
对于从NAND Flash启动的情况,其开始4KB的代码会被自动复制到CPU内部4K内存中,因此可以通过直接赋值的方法来修改。
对于从NOR Flash启动的情况,NOR Flash的开始地址即为0,必须通过一定的命令序列才能向NOR Flash中写数据,所以可以根据这点差别来分辨是从NAND Flash还是NOR Flash启动:向地址0写入一个数据,然后读出来,如果发现写入失败的就是NOR Flash,否则就是NAND Flash。
下面来分析NOR Flash启动部分代码:
208 adr r0, _start /* r0 <- current position of code */
209 ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
/* 判断U-Boot是否是下载到RAM中运行,若是,则不用 再复制到RAM中了,这种情况通常在调试U-Boot时才发生 */
210 cmp r0, r1 /*_start等于_TEXT_BASE说明是下载到RAM中运行 */
211 beq stack_setup
212 /* 以下直到nand_boot标号前都是NOR Flash启动的代码 */
213 ldr r2, _armboot_start
214 ldr r3, _bss_start
215 sub r2, r3, r2 /* r2 <- size of armboot */
216 add r2, r0, r2 /* r2 <- source end address */
217 /* 搬运U-Boot自身到RAM中*/
218 copy_loop:
219 ldmia r0!, {r3-r10} /* 从地址为[r0]的NOR Flash中读入8个字的数据 */
220 stmia r1!, {r3-r10} /* 将r3至r10寄存器的数据复制给地址为[r1]的内存 */
221 cmp r0, r2 /* until source end addreee [r2] */
222 ble copy_loop
223 b stack_setup /* 跳过NAND Flash启动的代码 */
下面再来分析NAND Flash启动部分代码:
nand_boot:
mov r1, #NAND_CTL_BASE
ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )
str r2, [r1, #oNFCONF] /* 设置NFCONF寄存器 */
/* 设置NFCONT,初始化ECC编/解码器,禁止NAND Flash片选 */
ldr r2, =( (1<<4)|(0<<1)|(1<<0) )
str r2, [r1, #oNFCONT]
ldr r2, =(0x6) /* 设置NFSTAT */
str r2, [r1, #oNFSTAT]
/* 复位命令,第一次使用NAND Flash前复位 */
mov r2, #0xff
strb r2, [r1, #oNFCMD]
mov r3, #0
/* 为调用C函数nand_read_ll准备堆栈 */
ldr sp, DW_STACK_START
mov fp, #0
/* 下面先设置r0至r2,然后调用nand_read_ll函数将U-Boot读入RAM */
ldr r0, =TEXT_BASE /* 目的地址:U-Boot在RAM的开始地址 */
mov r1, #0x0 /* 源地址:U-Boot在NAND Flash中的开始地址 */
mov r2, #0x30000 /* 复制的大小,必须比u-boot.bin文件大,并且必须是NAND Flash块大小的整数倍,这里设置为0x30000(192KB) */
bl nand_read_ll /* 跳转到nand_read_ll函数,开始复制U-Boot到RAM */
tst r0, #0x0 /* 检查返回值是否正确 */
beq stack_setup
bad_nand_read:
loop2: b loop2 //infinite loop
.align 2
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
其中NAND_CTL_BASE,oNFCONF等在include/configs/mini2440.h中定义如下:
#define NAND_CTL_BASE 0x4E000000 // NAND Flash控制寄存器基址
#define STACK_BASE 0x33F00000 //base address of stack
#define STACK_SIZE 0x8000 //size of stack
#define oNFCONF 0x00 /* NFCONF相对于NAND_CTL_BASE偏移地址 */
#define oNFCONT 0x04 /* NFCONT相对于NAND_CTL_BASE偏移地址*/
#define oNFADDR 0x0c /* NFADDR相对于NAND_CTL_BASE偏移地址*/
#define oNFDATA 0x10 /* NFDATA相对于NAND_CTL_BASE偏移地址*/
#define oNFCMD 0x08 /* NFCMD相对于NAND_CTL_BASE偏移地址*/
#define oNFSTAT 0x20 /* NFSTAT相对于NAND_CTL_BASE偏移地址*/
#define oNFECC 0x2c /* NFECC相对于NAND_CTL_BASE偏移地址*/
NAND Flash各个控制寄存器的设置在S3C2440的数据手册有详细说明,这里就不介绍了。
代码中nand_read_ll函数的作用是在NAND Flash中搬运U-Boot到RAM,该函数在board/samsung/mini2440/nand_read.c中定义。
NAND Flash根据page大小可分为2种: 512B/page和2048B/page的。这两种NAND Flash的读操作是不同的。因此就需要U-Boot识别到NAND Flash的类型,然后采用相应的读操作,也就是说nand_read_ll函数要能自动适应两种NAND Flash。
参考S3C2440的数据手册可以知道:根据NFCONF寄存器的Bit3(AdvFlash (Read only))和Bit2 (PageSize (Read only))可以判断NAND Flash的类型。Bit2、Bit3与NAND Flash的block类型的关系如下表所示:
表 2.4 NFCONF的Bit3、Bit2与NAND Flash的关系
Bit2 Bit3 | 0 | 1 |
0 | 256 B/page | 512 B/page |
1 | 1024 B/page | 2048 B/page |
由于的NAND Flash只有512B/page和2048 B/page这两种,因此根据NFCONF寄存器的Bit3即可区分这两种NAND Flash了。
完整代码见board/samsung/mini2440/nand_read.c中的nand_read_ll函数,这里给出伪代码:
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
//根据NFCONF寄存器的Bit3来区分2种NAND Flash
if( NFCONF & 0x8 ) /* Bit是1,表示是2KB/page的NAND Flash */
{
////////////////////////////////////
读取2K block 的NAND Flash
////////////////////////////////////
}
else /* Bit是0,表示是512B/page的NAND Flash */
{
/////////////////////////////////////
读取512B block 的NAND Flash
/////////////////////////////////////
}
return 0;