本文转载自:http://blog.chinaunix.net/uid-28382924-id-3412559.html
s3c6410 NAND flash启动原理网上有很多的资料讨论,这里不做详述,如果对其启动方式不了解的建议还是先阅读相关资料弄清楚。这里要讲的是针对2G MLC类型的NAND的移植方法。
1.修改配置2G MLC NAND相关信息。
1.1修改/include/configs/smdk6410.h
/* NAND chip page size */
#define CONFIG_SYS_NAND_PAGE_SIZE 4096//2048
/* NAND chip block size */
#define CONFIG_SYS_NAND_BLOCK_SIZE (512 * 1024)//128
/* NAND chip page per block count */
#define CONFIG_SYS_NAND_PAGE_COUNT 128//64
1.2修改/drivers/mtd/nand/nand_ids.c
找到这处:{"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS}改为:
{"NAND 2GiB 3,3V 8-bit", 0xD5, 4096, 2048, 512*1024, LP_OPTIONS}
1.3修改/drivers/mtd/nand/nand_base.c
增加:static struct nand_ecclayout nand_oob_218 = {
.eccbytes = 104,
.eccpos = {
24,25,26,27,28,29,30,31,32,33,
34,35,36,37,38,39,40,41,42,43,
44,45,46,47,48,49,50,51,52,53,
54,55,56,57,58,59,60,61,62,63,
64,65,66,67,68,69,70,71,72,73,
74,75,76,77,78,79,80,81,82,83,
84,85,86,87,88,89,90,91,92,93,
94,95,96,97,98,99,100,101,102,103,
104,105,106,107,108,109,110,111,112,113,
114,115,116,117,118,119,120,121,122,123,
124,125,126,127},
.oobfree =
{
{.offset = 2,
.length = 22
}
}
};
还记得我们前面移植启动时有提示NAND:
No oob scheme defined for oobsize 218吗?
在上面这个文件中找到nand_scan_tail()函数,增加:
case 218:
chip->ecc.layout = &nand_oob_218;
break;
经过修改后运行起来测试NAND 的读写看看是否正常:
Hit any key to stop autoboot: 0
SMDK6410 # dnw 50008000
Insert a OTG cable into the connector!
OTG cable Connected!
Now, Waiting for DNW to transmit data
Download Done!! Download Address: 0x50008000, Download Filesize:0x401f0
Checksum is being calculated.
Checksum O.K.
SMDK6410 # md.b 50008000
50008000: 14 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 ................
50008010: 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 ................
50008020: 00 02 e0 57 60 02 e0 57 c0 02 e0 57 20 03 e0 57 ...W`..W...W ..W
50008030: 80 03 e0 57 e0 03 e0 57 40 04 e0 57 78 56 34 12 ...W...W@..WxV4.
SMDK6410 # nand erase 100000 40000
NAND erase: device 0 offset 0x100000, size 0x40000
Erasing at 0x100000 -- 100痮mplete.
OK
SMDK6410 # nand write 50008000 100000 40000
NAND write: device 0 offset 0x100000, size 0x40000
262144 bytes written: OK
SMDK6410 # nand read 58000000 100000 40000
NAND read: device 0 offset 0x100000, size 0x40000
262144 bytes read: OK
SMDK6410 # md.b 58000000
58000000: 14 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 ................
58000010: 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 ................
58000020: 00 02 e0 57 60 02 e0 57 c0 02 e0 57 20 03 e0 57 ...W`..W...W ..W
58000030: 80 03 e0 57 e0 03 e0 57 40 04 e0 57 78 56 34 12 ...W...W@..WxV4.
SMDK6410 #
上面先下载个程序到SDRAM 0x50008000中,然后查看该地址处的内容。再把这个地址开始的0x4000字节写入到NAND 0x100000这个地址处。再读NAND 0x100000的内容到SDRAM 0x58000000,这是第2片SDRAM的开始地址(我们有2片128M的SDRAM)。对比0x50008000和0x58000000的内容我们可以发现结果是相同的,这说明NAND的读写都没有问题。
开始时显示NAND的oob size=218,可能是还没有正确配置NAND的PAGE和BLOCK size,配置后变成128了,如下:
Hit any key to stop autoboot: 0
SMDK6410 # nand info
Device 0: nand0, sector size 512 KiB
Page size 4096 b
OOB size 128 b
Erase size 524288 b
SMDK6410 #
2.添加NAND拷贝程序到SDRAM运行功能。
我们知道,NAND启动时芯片会读NAND 开始的4K字节到iRAM运行,这4K字节里面放的程序主要功能是拷贝存放在NAND中的程序到SDRAM运行,这也是u-boot里面nand_spl这个文件夹里面相关程序的作用。
nand_spl根据u-boot的配置(CONFIG_NAND_SPL=y)生成一个u-boot-spl-16k.bin,这个程序就是放在NAND的第一个page(开始的4K字节)的。而u-boot-2012.10主目录下的u-boot.bin则是用于在SDRAM中运行的。u-boot把u-boot-spl-16k.bin和u-boot.bin拼起来合成一个u-boot-nand.bin这就是通常我们烧到NAND中的程序。但经过实验发现,通过u-boot的NAND write写到NAND的程序却运行不起来?这里要注意两个问题:第一,可能写的不对;第二,可能读得不对。但在上面第一步骤里面我们试过NAND的读写都是正确的。或者还有其他原因?
原来(不知道为什么),在把u-boot-nand.bin写入NAND要这样做才行:
假设通过DNW把文件放在0X50008000这个位置上。
nand erase 0 100000
第一:把50008000写入NAND的0地址上,每次写入大小都是一个page(4K)。
nand write 50008000 0 1000
第二:把50008800写入NAND的1000地址上。
nand write 50008800 1000 1000
第三:把50009000写入NAND的2000地址上。
nand write 50009000 2000 1000
第四:把50009800写入NAND的3000地址上。
nand write 50009800 3000 1000
最后,把剩余的写完:
nand write 5000a000 4000 fe000
这样写入,当然读的时候也不能再是简单的每个page这样读了。
第一:读NAND第3个page放到SDRAM指定要运行的地址,比如0x57e00000
为什么第一第二个page不读?因为第一page放的是u-boot-spl-16k.bin,我们不需要它,我们需要的是u-boot.bin。第二个page由于上面写的方法的原因也没有用了。
第二:读第4个page放在SDRAM的0x57e00800,也就覆盖掉前面2K(这个好像可以没有)。
第三:读剩余的放在0x57e01000开始处。
参考下图:
本来u-boot带有一个nand_boot文件正是实现拷贝到SDRAM功能,所以我们必须修改/nand_spl/nand_boot.c内容如下:
#include <common.h>
#include <nand.h>
#include <asm/io.h>
#include <asm/arch/s3c6410.h>
/*
* address format
* 17 16 9 8 0
* --------------------------------------------
* | block(12bit) | page(5bit) | offset(9bit) |
* --------------------------------------------
*/
#define NAND_DISABLE_CE() (NFCONT_REG |= (1 << 1))
#define NAND_ENABLE_CE() (NFCONT_REG &= ~(1 << 1))
#define NF_TRANSRnB() do { while(!(NFSTAT_REG & (1 << 0))); } while(0)
static void nandll_read_page (uchar *buf, ulong addr, int large_block)
{
int i;
int page_size = 512;
if (large_block==1)
page_size = 2048;
if (large_block==2)
page_size = 4096;
if(large_block==3)
page_size = 8192;
NAND_ENABLE_CE();
NFCMD_REG = NAND_CMD_READ0;
/* Write Address */
NFADDR_REG = 0;
if (large_block)
NFADDR_REG = 0;
NFADDR_REG = (addr) & 0xff;
NFADDR_REG = (addr >> 8) & 0xff;
NFADDR_REG = (addr >> 16) & 0xff;
if (large_block)
NFCMD_REG = NAND_CMD_READSTART;
NF_TRANSRnB();
/* for compatibility(2460). u32 cannot be used. by scsuh */
for(i=0; i < page_size; i ) {
*buf = NFDATA8_REG;
}
NAND_DISABLE_CE();
//return 0;
}
/*
* Read data from NAND.
*/
static void nandll_read_blocks (ulong dst_addr, ulong size, int large_block)
{
uchar *buf = (uchar *)dst_addr;
int i;
uint page_shift = 9;
if (large_block==1)
page_shift = 11;
if(large_block==2)
page_shift = 12;
if(large_block==3)
page_shift =13;
if(large_block == 2)
{
/* Read pages */
for (i = 2; i < 4; i , buf =(1<<(page_shift-1))) {
nandll_read_page(buf, i, large_block);
}
/* Read pages */
for (i = 4; i < (size>>page_shift); i , buf =(1<<page_shift)) {
nandll_read_page(buf, i, large_block);
}
}else if(large_block == 3) //K9GAG08U0E
{
/* Read pages */
for (i = 0; i < 4; i , buf =(1<<(page_shift-2))) {
nandll_read_page(buf, i, large_block);
}
/* Read pages */
for (i = 4; i < (0x40000>>page_shift); i , buf =(1<<page_shift)) {
nandll_read_page(buf, i, large_block);
}
}
else
{
for (i = 0; i < (0x3c000>>page_shift); i , buf =(1<<page_shift)) {
nandll_read_page(buf, i, large_block);
}
}
//return 0;
}
void nand_boot(void)
{
int large_block = 0;
int i;
vu_char id;
__attribute__((noreturn)) void (*uboot)(void);
NAND_ENABLE_CE();
NFCMD_REG=NAND_CMD_RESET;
NF_TRANSRnB();
NFCMD_REG = NAND_CMD_READID;
NFADDR_REG = 0x00;
NF_TRANSRnB();
/* wait for a while */
for (i=0; i<200; i );
int factory = NFDATA8_REG;
id = NFDATA8_REG;
int cellinfo=NFDATA8_REG;
int tmp= NFDATA8_REG;
//int childType=tmp & 0x03; //Page size
int childType=cellinfo; //Page size
if (id > 0x80)
{
large_block = 1;
}
if(id == 0xd5 && childType==0x94 )//K9GAG08U0D
{
large_block = 2;
}
if(id == 0xd5 && childType==0x14 )//K9GAG08U0M
{
large_block = 2;
}
if(id == 0xd5 && childType==0x84 )//K9GAG08U0E
{
large_block = 3;
}
if(id==0xd7)//K9LBG08U0D
{
large_block = 2;
}
if(factory==0x2c && id == 0x48) //MT29F16G08ABACAWP
{
large_block = 2;
}if(factory==0x2c && id == 0x38) //MT29F8G08ABABAWP
{
large_block = 2;
}
/* read NAND Block.
* 128KB ->240KB because of U-Boot size increase. by scsuh
* So, read 0x3c000 bytes not 0x20000(128KB).
*/
nandll_read_blocks(CONFIG_SYS_PHY_UBOOT_BASE, 0x80000, large_block);
uboot = (void *)CONFIG_SYS_PHY_UBOOT_BASE;
(*uboot)();
}
注意nandll_read_blocks()函数里有这段:
if(large_block == 2)
{
/* Read pages */
for (i = 2; i < 4; i , buf =(1<<(page_shift-1))) {
nandll_read_page(buf, i, large_block);
}
i的值一开始等于2的意思是跳过NAND开始第一个page放的u-boot-spl-16k.bin。
3.制作用于烧录到NAND的u-boot-nand.bin文件。
如果我们编译的u-boot-spl-16k小于4k,那么我们需要自己把它补充到4K再放到u-boot.bin的开头,合成一个u-boot-nand.bin。嫌hex手工修改麻烦的话,也可以在建立一个文件,内容如下:
#!/bin/sh
rm -f u-boot-hand.bin
cat nand_spl/u-boot-spl-16k.bin >> temp
cat zero-16k.bin >> temp
split -b 4k temp
mv xaa u-boot-4k.bin
cat u-boot-4k.bin >> u-boot-hand.bin
cat u-boot.bin >> u-boot-hand.bin
rm -f temp
rm -f u-boot-4k.bin
rm -f xa*
rm -f /mnt/hgfs/share/u-boot.bin
rm -f /mnt/hgfs/share/u-boot-nand.bin
cp u-boot.bin /mnt/hgfs/share/u-boot.bin
cp u-boot-hand.bin /mnt/hgfs/share/u-boot-nand.bin
root@ubuntu:/home/my/u-boot-2012.10# ./rc
这时在当前目录下就有一个可以烧录的u-boot-hand.bin.