完成上面工作后,u-boot中还没有对2440上Nand Flash的支持,以及u-boot从Nand Flash上启动,这些得我们一步步去实现了。
1、修改配置文件 include/configs/smdk2440.h :
1.1、新增宏 CONFIG_CMD_NAND (大概在95行)
#define CONFIG_CMD_CACHE
#define CONFIG_CMD_DATE
#define CONFIG_CMD_ELF
#define CONFIG_CMD_NAND
#define CONFIG_CMD_ENV1.2、在文件的最后面增加3个宏:
//NAND flash settings
#define NAND_CTL_BASE 0x4E000000 //NAND Flash的地址
#define CFG_MAX_NAND_DEVICE 1 //NAND Flash设备数目为1
#define NAND_MAX_CHIPS 1 //每个NAND设备由1个NADN芯片组成
2、 修改 include/s3c24x0.h 文件,增加S3C2440_NAND数据结构(168行)
/* NAND FLASH (see S3C2440 manual chapter 6) */
typedef struct {
S3C24X0_REG32 NFCONF;
S3C24X0_REG32 NFCONT;
S3C24X0_REG32 NFCMD;
S3C24X0_REG32 NFADDR;
S3C24X0_REG32 NFDATA;
S3C24X0_REG32 NFMECCD0;
S3C24X0_REG32 NFMECCD1;
S3C24X0_REG32 NFSECCD;
S3C24X0_REG32 NFSTAT;
S3C24X0_REG32 NFESTAT0;
S3C24X0_REG32 NFESTAT1;
S3C24X0_REG32 NFMECC0;
S3C24X0_REG32 NFMECC1;
S3C24X0_REG32 NFSECC;
S3C24X0_REG32 NFSBLK;
S3C24X0_REG32 NFEBLK;
} /*__attribute__((__packed__))*/ S3C2440_NAND;
3、u-boot默认是从Nor Flash启动的。修改文件 cpu/arm920t/start.S ,使u-boot可以从Nand Flash启动:
对文件改动较大,仔细对比源代码,主要的修改是将栈的初始化放到前面,用于后面调用C函数的需要;
判断u-boot不是从内存启动后,调用CopyCode2Ram函数,实现把启动代码拷贝到内存
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
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 */
blne cpu_init_crit
#endifstack_setup: //将栈的初始化放到前面
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_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 */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 clear_bssldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
bl CopyCode2Ram // 调用board/smdk2440/boot_Init.c里的CopyCode2Ram函数,把启动代码拷贝到内存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_lldr pc, _start_armboot
_start_armboot: .word start_armboot
4、新建 board/smdk2440/boot_Init.c 文件,实现从NandFlsh读取字节;判断启动方式,并拷贝启动代码到内存。代码如下:
注意:上面这段代码中对Nand进行寻址的部分,这跟具体的Nand Flash的寻址方式有关。根据开发板上的Nand Flash(K9F1208U0C)数据手册得知,片内寻址是采用26位地址形式。从第0位开始分四次通过I/O0-I/O7进行传送,并进行片内寻址。具体含义和结构图如下(相关概念参考Nand数据手册):
#include <common.h>
#include <s3c2440.h>#define BUSY 1
#ifdef NAND_LARGEPAGE
#define NAND_SECTOR_SIZE 2048
#else
#define NAND_SECTOR_SIZE 512
#endif
#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1)/* 供外部调用的函数 */
void nand_init_ll(void);
void nand_read_ll(unsigned char *buf, unsigned long start_addr, int size);/* S3C2440的NAND Flash处理函数 */
static void nand_reset(void);
static void wait_idle(void);
static void nand_select_chip(void);
static void nand_deselect_chip(void);
static void write_cmd(int cmd);
static void write_addr(unsigned int addr);
static unsigned char read_data(void);
/* S3C2440的NAND Flash操作函数 */
/* 复位 */
static void nand_reset(void)
{
nand_select_chip();
write_cmd(0xff); // 复位命令
wait_idle();
nand_deselect_chip();
}/* 等待NAND Flash就绪 */
static void wait_idle(void)
{
int i;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT;while(!(*p & BUSY))
for(i=0; i<10; i++);
}/* 发出片选信号 */
static void nand_select_chip(void)
{
int i;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;s3c2440nand->NFCONT &= ~(1<<1);
for(i=0; i<10; i++);
}/* 取消片选信号 */
static void nand_deselect_chip(void)
{
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
s3c2440nand->NFCONT |= (1<<1);
}/* 发出命令 */
static void write_cmd(int cmd)
{
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD;
*p = cmd;
}/* 发出地址 Nand进行寻址的部分*/
static void write_addr(unsigned int addr)
{
int i;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
#ifndef NAND_LARGEPAGE
*p = addr & 0xff;
for(i=0; i<10; i++);
*p = (addr >> 9) & 0xff;
for(i=0; i<10; i++);
*p = (addr >> 17) & 0xff;
for(i=0; i<10; i++);
*p = (addr >> 25) & 0xff;
for(i=0; i<10; i++);
#else
int col, page;
col = addr & NAND_BLOCK_MASK;
page = addr / NAND_SECTOR_SIZE;
*p = col & 0xff; /* Column Address A0~A7 */
for(i=0; i<10; i++);
*p = (col >> 8) & 0x0f; /* Column Address A8~A11 */
for(i=0; i<10; i++);
*p = page & 0xff; /* Row Address A12~A19 */
for(i=0; i<10; i++);
*p = (page >> 8) & 0xff; /* Row Address A20~A27 */
for(i=0; i<10; i++);
*p = (page >> 16) & 0x03; /* Row Address A28~A29 */
for(i=0; i<10; i++);
#endif
}/* 读取数据 */
static unsigned char read_data(void)
{
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;
return *p;
}/* 初始化NAND Flash */
void nand_init_ll(void)
{
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;#define TACLS 0
#define TWRPH0 3
#define TWRPH1 0
/* 设置时序 */
s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
/* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
s3c2440nand->NFCONT = (1<<4)|(1<<1)|(1<<0);
/* 复位NAND Flash */
nand_reset();
}/* 读函数 */
void nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;
if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {
return ; /* 地址或长度不对齐 */
}
/* 选中芯片 */
nand_select_chip();
for(i=start_addr; i < (start_addr + size);) {
/* 发出READ0命令 */
write_cmd(0);
/* Write Address */
write_addr(i);
#ifdef NAND_LARGEPAGE
write_cmd(0x30);
#endif
wait_idle();
for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {
*buf = read_data();
buf++;
}
}
/* 取消片选信号 */
nand_deselect_chip();
return ;
}int bBootFrmNORFlash(void) /* 取OM0、1的值,判断开发板是从NorFlash还是NandFlash启动 */
{
volatile unsigned int *bwsCON = (volatile unsigned int *)0x48000000;
unsigned int bwsVal;
bwsVal = *bwsCON;
bwsVal &= 0x06;
if (bwsVal==0){
return 0;
}
else {
return 1;
}
}int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size)
{
unsigned int *pdwDest;
unsigned int *pdwSrc;
int i;if (bBootFrmNORFlash())
{
pdwDest = (unsigned int *)buf;
pdwSrc = (unsigned int *)start_addr;
/* 从 NOR Flash启动 */
for (i = 0; i < size / 4; i++)
{
pdwDest[i] = pdwSrc[i];
}
}
else
{
/* 初始化NAND Flash */
nand_init_ll();
/* 从 NAND Flash启动 */
nand_read_ll(buf, start_addr, (size + NAND_BLOCK_MASK)&~(NAND_BLOCK_MASK));
}
return 0;
}
5、在 board/smdk2440/Makefile 中添加 boot_Init.c 的编译选项,使他编译到u-boot中:
COBJS := my2440.o flash.o boot_Init.o
6、还有一个重要的地方要修改,在 cpu/arm920t/u-boot.lds 中,这个u-boot启动连接脚本文件决定了u-boot运行的入口地址,以及各个段的存储位置,这也是链接定位的作用。添加下面两行代码,主要目的是防止编译器把CopyCode2Ram的子函数放到4K之后,否则是无法启动的。如下:
.text :
{
cpu/arm920t/start.o (.text)
board/smdk2440/boot_Init.o (.text)
board/smdk2440/lowlevel_init.o (.text)
*(.text)
}