七、支持从NAND启动
1、修改start.S文件
1) 设置好C语言要用的栈
修改arch/arm/cpu/arm920t/start.S,将stack_setup 子程序搬到relocate 子程序之前(在调用C函数之前设置好栈),并修改之前调用设置栈的函数为清除BSS段,这是因为之前的源码程序调用beq stack_setup之后接着执行了清除BSS段的子程序clear_bss,把stack_setup搬移到上面之后,应该确保会执行clear_bss子函数,跟之前的效果一样。修改后的代码如下:
209stack_setup:
210 ldr r0, _TEXT_BASE /* upper128 KiB: relocated uboot */
211 sub r0, r0, #CONFIG_SYS_MALLOC_LEN /*malloc area */
212 sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo */
213 #ifdefCONFIG_USE_IRQ
214 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
215#endif
216 sub sp, r0, #12 /* leave 3words for abort-stack */
217 bic sp, sp, #7 /* 8-bytealignment for ABI compliance */
218
219 #ifndefCONFIG_SKIP_RELOCATE_UBOOT
220relocate: /* relocate U-Boot to RAM */
221 adr r0, _start /* r0<- current position of code */
222 ldr r1, _TEXT_BASE /* test ifwe run from flash or RAM */
223 cmp r0, r1 /* don'treloc during debug */
224 /*beq stack_setup */
225 beq clear_bss
226
227 ldr r2, _armboot_start
228 ldr r3, _bss_start
229 sub r2, r3, r2 /* r2<- size of armboot */
230 add r2, r0, r2 /* r2<- source end address */
2) 修改代码搬移程序
在relocate子程序中屏蔽掉原来的拷贝函数,增加自己用C语言实现的拷贝函数:
219 #ifndefCONFIG_SKIP_RELOCATE_UBOOT
220 relocate: /* relocate U-Boot to RAM */
221 adr r0, _start /* r0<- current position of code */
222 ldr r1, _TEXT_BASE /* test ifwe run from flash or RAM */
223 cmp r0, r1 /* don't reloc during debug */
224 /*beq stack_setup */
225 beq clear_bss
226
227 ldr r2, _armboot_start
228 ldr r3, _bss_start
229 sub r2, r3, r2 /* r2<- size of armboot */
230 #if1
231 bl CopyCode2Ram
232#else
233 add r2, r0, r2 /* r2<- source end address */
234
235 copy_loop:
236 ldmia r0!, {r3-r10} /* copyfrom source address [r0] */
237 stmia r1!, {r3-r10} /* copyto target address [r1] */
238 cmp r0, r2 /* untilsource end addreee [r2] */
239 ble copy_loop
240#endif
241 #endif /* CONFIG_SKIP_RELOCATE_UBOOT */
2、实现代码拷贝函数
在board/samsung/tq2440/目录下增加一个文件nand_start.c,实现Nand的操作:
#include<common.h>
//#include<s3c2410.h>
#include<asm/arch/s3c24x0_cpu.h>
#defineGSTATUS1 (*(volatile unsigned int*)0x560000B0)
#defineBUSY 1
#defineNAND_SECTOR_SIZE 512
#defineNAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1)
#defineNAND_SECTOR_SIZE_LP 2048
#defineNAND_BLOCK_MASK_LP (NAND_SECTOR_SIZE_LP- 1)
charbLARGEBLOCK; //HJ_add 20090807
charb128MB; //HJ_add 20090807
/* 供外部调用的函数 */
voidnand_init_ll(void);
intnand_read_ll(unsigned char *buf, unsigned long start_addr, int size);
intnand_read_ll_lp(unsigned char *buf, unsigned long start_addr, int size);
/* NAND Flash操作的总入口, 它们将调用S3C2410或S3C2440的相应函数 */
static voidnand_reset(void);
static voidwait_idle(void);
static voidnand_select_chip(void);
static voidnand_deselect_chip(void);
static voidwrite_cmd(int cmd);
static voidwrite_addr(unsigned int addr);
static voidwrite_addr_lp(unsigned int addr);
static unsignedchar read_data(void);
intNF_ReadID(void); //HJ_add20090807
/* S3C2440的NAND Flash处理函数 */
static voids3c2440_nand_reset(void);
static voids3c2440_wait_idle(void);
static voids3c2440_nand_select_chip(void);
static voids3c2440_nand_deselect_chip(void);
static voids3c2440_write_cmd(int cmd);
static voids3c2440_write_addr(unsigned int addr);
static voids3c2440_write_addr_lp(unsigned int addr);
static unsignedchar s3c2440_read_data(void);
/* S3C2440的NAND Flash操作函数 */
/* 复位 */
static voids3c2440_nand_reset(void)
{
s3c2440_nand_select_chip();
s3c2440_write_cmd(0xff); // 复位命令
s3c2440_wait_idle();
s3c2440_nand_deselect_chip();
}
/* 等待NAND Flash就绪 */
static voids3c2440_wait_idle(void)
{
int i;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND*)0x4e000000;
volatile unsigned char *p = (volatileunsigned char *)&s3c2440nand->NFSTAT;
while(!(*p & BUSY))
for(i=0; i<10; i++);
}
/* 发出片选信号 */
static voids3c2440_nand_select_chip(void)
{
int i;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND*)0x4e000000;
s3c2440nand->NFCONT &= ~(1<<1);
for(i=0; i<10; i++);
}
/* 取消片选信号 */
static voids3c2440_nand_deselect_chip(void)
{
S3C2440_NAND * s3c2440nand = (S3C2440_NAND*)0x4e000000;
s3c2440nand->NFCONT |= (1<<1);
}
/* 发出命令 */
static voids3c2440_write_cmd(int cmd)
{
S3C2440_NAND * s3c2440nand = (S3C2440_NAND*)0x4e000000;
volatile unsigned char *p = (volatileunsigned char *)&s3c2440nand->NFCMD;
*p = cmd;
}
/* 发出地址 */
static voids3c2440_write_addr(unsigned int addr)
{
int i;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND*)0x4e000000;
volatile unsigned char *p = (volatileunsigned char *)&s3c2440nand->NFADDR;
*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++);
}
/* 发出地址 */
static voids3c2440_write_addr_lp(unsigned int addr)
{
int i;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND*)0x4e000000;
volatile unsigned char *p = (volatileunsigned char *)&s3c2440nand->NFADDR;
int col, page;
col = addr & NAND_BLOCK_MASK_LP;
page = addr / NAND_SECTOR_SIZE_LP;
*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++);
if (b128MB ==0)
*p = (page >> 16) & 0x03; /* Row Address A28~A29 */
for(i=0; i<10; i++);
}
/* 读取数据 */
static unsignedchar s3c2440_read_data(void)
{
S3C2440_NAND * s3c2440nand = (S3C2440_NAND*)0x4e000000;
volatile unsigned char *p = (volatileunsigned char *)&s3c2440nand->NFDATA;
return *p;
}
/* 在第一次使用NAND Flash前,复位一下NAND Flash */
static voidnand_reset(void)
{
s3c2440_nand_reset();
}
static voidwait_idle(void)
{
s3c2440_wait_idle();
}
static voidnand_select_chip(void)
{
int i;
s3c2440_nand_select_chip();
for(i=0; i<10; i++);
}
static voidnand_deselect_chip(void)
{
s3c2440_nand_deselect_chip();
}
static voidwrite_cmd(int cmd)
{
s3c2440_write_cmd(cmd);
}
static voidwrite_addr(unsigned int addr)
{
s3c2440_write_addr(addr);
}
static voidwrite_addr_lp(unsigned int addr)
{
s3c2440_write_addr_lp(addr);
}
static unsignedchar read_data(void)
{
return s3c2440_read_data();
}
/* 初始化NAND Flash */
voidnand_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();
}
#if 1
intNF_ReadID(void)
{
char pMID;
char pDID;
int nBuff;
char n4thcycle;
int i;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatileunsigned char *)&s3c2440nand->NFADDR;
b128MB = 1;
n4thcycle = nBuff = 0;
nand_init_ll();
nand_select_chip();
write_cmd(0x90); // read id command
*p=0x00 & 0xff;
for ( i = 0; i < 100; i++ );
pMID = read_data();
pDID = read_data();
nBuff = read_data();
n4thcycle = read_data();
nand_deselect_chip();
if (pDID >= 0xA0)
{
b128MB = 0;
}
return (pDID);
}
#endif
/* 读函数 */
intnand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;
char dat;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND*)0x4e000000;
volatile unsigned char *p = (volatileunsigned char *)&s3c2440nand->NFADDR;
if ((start_addr & NAND_BLOCK_MASK) ||(size & NAND_BLOCK_MASK))
{
return -1; /* 地址或长度不对齐 */
}
/* 选中芯片 */
nand_select_chip();
for(i=start_addr; i < (start_addr +size);)
{
/* Check BadBlock */
if(1){
/* 发出READ0命令 */
write_cmd(0x50);
*p = 5;
for(j=0; j<10; j++);
*p = (i >> 9) & 0xff;
for(j=0; j<10; j++);
*p = (i >> 17) & 0xff;
for(j=0; j<10; j++);
*p = (i >> 25) & 0xff;
for(j=0; j<10; j++);
wait_idle();
dat = read_data();
write_cmd(0);
/* 取消片选信号 */
nand_deselect_chip();
if(dat != 0xff)
i += 16384; // 1 Block = 512*32= 16384
/* Read Page */
/* 选中芯片 */
nand_select_chip();
}
/* 发出READ0命令 */
write_cmd(0);
/* Write Address */
write_addr(i);
wait_idle();
for(j=0; j < NAND_SECTOR_SIZE; j++,i++)
{
*buf = read_data();
buf++;
}
}
/* 取消片选信号 */
nand_deselect_chip();
return 0;
}
/* 读函数
* Large Page
*/
intnand_read_ll_lp(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;
char dat;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND*)0x4e000000;
volatile unsigned char *p = (volatileunsigned char *)&s3c2440nand->NFADDR;
if ((start_addr & NAND_BLOCK_MASK_LP) ||(size & NAND_BLOCK_MASK_LP))
{
return -1; /* 地址或长度不对齐 */
}
/* 选中芯片 */
nand_select_chip();
for(i=start_addr; i < (start_addr +size);)
{
/* Check BadBlock */
if(1){
int col, page;
col = i & NAND_BLOCK_MASK_LP;
page = i / NAND_SECTOR_SIZE_LP;
/* 发出READ0命令 */
write_cmd(0x00);
*p = 5;
for(j=0; j<10; j++);
*p = 8;
for(j=0; j<10; j++);
*p = page & 0xff; /* Row Address A12~A19 */
for(j=0; j<10; j++);
*p = (page >> 8) & 0xff; /* Row Address A20~A27 */
for(j=0; j<10; j++);
if (b128MB ==0)
*p = (page >> 16) & 0x03; /* Row Address A28~A29 */
for(j=0; j<10; j++);
write_cmd(0x30);
wait_idle();
dat = read_data();
/* 取消片选信号 */
nand_deselect_chip();
if(dat != 0xff)
i += 131072; // 1 Block = 2048*64= 131072
/* Read Page */
/* 选中芯片 */
nand_select_chip();
}
/* 发出READ0命令 */
write_cmd(0);
/* Write Address */
write_addr_lp(i);
write_cmd(0x30);
wait_idle();
for(j=0; j < NAND_SECTOR_SIZE_LP; j++,i++)
{
*buf = read_data();
buf++;
}
}
/* 取消片选信号 */
nand_deselect_chip();
return 0;
}
intbBootFrmNORFlash(void)
{
volatile unsigned int *pdw = (volatileunsigned int *)0;
unsigned int dwVal;
/*
* 无论是从NOR Flash还是从NAND Flash启动,
* 地址0处为指令"b Reset", 机器码为0xEA00000B,
* 对于从NAND Flash启动的情况,其开始4KB的代码会复制到CPU内部4K内存中,
* 对于从NOR Flash启动的情况,NOR Flash的开始地址即为0。
* 对于NOR Flash,必须通过一定的命令序列才能写数据,
* 所以可以根据这点差别来分辨是从NAND Flash还是NOR Flash启动:
* 向地址0写入一个数据,然后读出来,如果没有改变的话就是NOR Flash
*/
dwVal = *pdw;
*pdw = 0x12345678;
if (*pdw != 0x12345678)
{
return 1;
}
else
{
*pdw = dwVal;
return 0;
}
}
intCopyCode2Ram(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];
}
return 0;
}
else
{
/* 初始化NAND Flash */
nand_init_ll();
/* 从 NAND Flash启动 */
if (NF_ReadID() == 0x76 )
nand_read_ll(buf, start_addr, (size +NAND_BLOCK_MASK)&~(NAND_BLOCK_MASK));
else
nand_read_ll_lp(buf, start_addr, (size+ NAND_BLOCK_MASK_LP)&~(NAND_BLOCK_MASK_LP));
return 0;
}
}
3、修改board/samsung/tq2440/config.mk
代码段
25 TEXT_BASE =0x33000000
26 //TEXT_BASE= 0x33F80000
4、修改Makefile
28 COBJS := tq2440.o flash.o nand_start.o
5、修改连接脚本
修改arch/arm/cpu/arm920t/u-boot.lds (由于这个函数是被第一阶段的汇编程序所调用)。
40 .text :
41 {
42 arch/arm/cpu/arm920t/start.o (.text)
43 board/samsung/tq2440/nand_start.o (.text)
44 *(.text)
45 }
6、将之前注释掉的初始化CPU的函数取消注释
修改arch/arm/cpu/arm920t/start.S 第206行:
206 bl cpu_init_crit
它会调用lowlevel_init子函数(载lowlevel_init.S文件定义)进行CPU的初始化,包括总线宽度,时序响应等。
7、最重要的修改Makefile
参见我的另一篇博文,u-boot-2010.06 移植到TQ2440从NAND启动不了以及lowlevel_init运行不过去的解决办法。
编译烧写到Nand Flash,从Nand启动,可以看到启动信息。
八、修改uboot使得支持Linux内核启动
1、修改板子头文件include/configs/tq2440.h
43 /* for tag(s) to transfermessage to kernel */
44 #define CONFIG_SETUP_MEMORY_TAGS1
45 #define CONFIG_CMDLINE_TAG 1
46 #define CONFIG_INITRD_TAG 1
……
123 /*#define CONFIG_BOOTARGS "root=ramfs devfs=mountconsole=ttySA0,9600" */
124 #define CONFIG_BOOTARGS"noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0"
……
131 /*#define CONFIG_BOOTCOMMAND "tftp; bootm" */
132 #define CONFIG_BOOTCOMMAND"nand read 0x32000000 0x200000 0x300000; bootm 0x32000000"
2、修改机器码
由于内核机器吗被改成了168,所以这里要改成跟内核一样,不然内核无法启动。在文件arch/arm/include/asm/mach-types.h中修改之前tq2440.c中赋值过的机器码:
(gd->bd->bi_arch_number = MACH_TYPE_S3C2440;)
375 //#defineMACH_TYPE_S3C2440 362
376 #defineMACH_TYPE_S3C2440 168
3、连接mkimage工具
该工具在生成uImage的时候需要用到,它在编译好uboot之后就在tools文件夹下产生了,把他连接到PATH路径去(注意需要使用完整路径):
[root@arm 16:50 /home/zhang/uboot]# ln -sf/home/zhang/uboot-2010.06/tools/mkimage /usr/local/bin/mkimage
4、编译内核和根文件系统并下载
注意,如果出现文件系统启动之后,隔几秒系统就会自动重启的话,是因为应用层没有喂狗的程序。在文件系统中添加喂狗程序,重新生成文件系统之后就可以了。
Hit any key to stop autoboot: 0
TQ2440 #
TQ2440 # tftp 0x30000000 uImage
……
TQ2440 # bootm 0x30000000
……
这样就会看到内核启动,要是Nand中已经烧录好了根文件系统,就会正常启动。但是掉电之后就没了。
可以通过nand命令将其固化在Nand中,在运行bootm 0x30000000之前:
1) 下载uImage
TQ2440 # tftp0x30000000 uImage
dm9000 i/o: 0x20000000, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 10:23:45:67:89:ab
could not establish link
Using dm9000 device
TFTP from server 192.168.10.96; our IPaddress is 192.168.10.66
Filename 'uImage'.
Load address: 0x30000000
Loading:#################################################################
#################################################################
############################
done
Bytes transferred = 2314780 (23521c hex)
2) 擦除NANDFLASH,擦除地址为0x200000,大小为0x300000 的NAND FLASH:
TQ2440 # nand erase0x200000 0x300000
NAND erase: device 0 offset 0x200000, size0x300000
Erasing at 0x4e0000 -- 100% complete.
OK
3) 写入NANDFLASH,将地址0x30000000 的SDRAM的数据写入到地址0x200000,大小0x300000 的NAND FLASH
TQ2440 # nand write0x30000000 0x200000 0x300000
NAND write: device 0 offset 0x200000, size0x300000
3145728 bytes written: OK
TQ2440 #
重新启动开发板就可以看到正常引导信息。