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 [email protected].
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 [email protected].
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.