分类: LINUX
2015-08-27 09:36:18
原文地址:Nand flash uboot 驱动研究 作者:xuxiyao8888
一. 在lib_arm/board.c中调用了函数nand_init()初始化nand flash:
二. nand_flash()定义在drivers/mtd/nand/nand.c中,
在nand_flash()中调用nand_init_chip(), 在nand_init_chip()中会调用两个函数,
一个是board_nand_init(), 用于初始化flash的部分信息, 填充结构体mtd_info
mtd,接着会调用nand_scan(), nand_scan()函数会继续填充结构体mtd, 最后会调用this->scan_bbt
(mtd), 即:nand_default_bbt(mtd),扫描整个flash,
查找坏块,但是找到坏块以后如何标记的目前还不知道,这时初始化完成。
三. 首先我们看一个函数:board_nand_init(nand):
185 int board_nand_init(struct nand_chip *nand)
186 {
187 u_int32_t cfg;
188 u_int8_t tacls, twrph0, twrph1;
189 S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
190
191 DEBUGN("board_nand_init()\n");
192
193 /*
194 * if you have any question, please refer to
195 * /media/study/data/datasheet/cpu/S3C2440A datasheet.pdf
196 * page 7-21 (228/604)
197 * regist:CLKCON addr:0x4C00 000C
198 * description:Clock generator control register.
199 * bit 4:Control HCLK into NAND flash Controller block.
200 * 0 ==> Disable , 1 ==> Enable.
201 */
202 clk_power->CLKCON |= (1 << 4); // enable clock,
203
204 twrph0 = 6; twrph1 = 2; tacls = 0;
// nand flash interface init, you can refer to datasheet.
205 cfg = (tacls<<12)|(twrph0<<8)|(twrph1<<4);
206 NFCONF = cfg;
207 cfg = (1<<6)|(1<<4)|(0<<1)|(1<<0);
208 NFCONT = cfg;
209 /* initialize nand_chip data structure */
210 /* dancy note:
211 * set IO_ADDR_R and IO_ADDR_W to 0x4e000010;
212 * because 0x4E00 0010 is NAND Flash data register.
213 */
214 nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)0x4e000010;
215 /* read_buf and write_buf are default */
216 /* read_byte and write_byte are default */
217 /* hwcontrol always must be implemented */
218 /*函数s3c2410_hwcontrol使用来设置HW的, 如信号CE,也是设置
/*IO_ADDR_W指向的register地址。*/
219 nand->hwcontrol = s3c2410_hwcontrol;
220 nand->dev_ready = s3c2410_dev_ready;
221
222 nand->eccmode = NAND_ECC_SOFT;
//NAND_ECC_SOFT和NAND_ECC_NONE的区别是,NAND_ECC_NONE不会check ECC。
223 //nand->eccmode = NAND_ECC_NONE;
/*这个ECC先去掉,否则你使用nand write命令和nand read会boot 不起内核*/
224 nand->options = 0;
225 DEBUGN("end of nand_init\n");
226
227 return 0;
228 }
看函数:s3c2410_hwcontrol()
80 static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd)
81 {
82 struct nand_chip *chip = mtd->priv;
83
84 DEBUGN("hwcontrol(): 0x%02x: ", cmd);
85
109 switch (cmd) {
110 /* dancy note:
111 * select NAND flash chip by clean pin CS0.
114 */
115 case NAND_CTL_SETNCE:
116 /*
117 * dancy modify:
118 * orig: NFCONT &= ~S3C2440_NFCONT_nCE;
119 */
120 NFCONT &= ~S3C2440_NFCONT_nCE;
121 DEBUGN("NFCONT=0x%08x\n", NFCONT);
122 break;
123 case NAND_CTL_CLRNCE:
124 /*
125 * dancy modify:
126 * orig: NFCONT &= ~S3C2440_NFCONT_nCE;
127 */
128 NFCONT |= S3C2440_NFCONT_nCE;
129 DEBUGN("NFCONT=0x%08x\n", NFCONT);
130 break;
/*下面两个case也算是配置HW,也是为了使用同一个函数this->write_byte也就是*/
/*nand_write_byte函数,使用同一个函数,将写地址指向不同的register,来实现*/
/*同一个函数,当通过hwcontrol配置以后,实现不同的功能。*/
131 case NAND_CTL_SETALE:
/*NF_BASE + S3C2440_ADDR_NALE is NAND Flash memory command value*/
132 chip->IO_ADDR_W = NF_BASE + S3C2440_ADDR_NALE;
133 DEBUGN("SETALE\n");
134 break;
135 case NAND_CTL_SETCLE:
/*NF_BASE + S3C2440_ADDR_NCLE is NAND Flash command set register*/
136 chip->IO_ADDR_W = NF_BASE + S3C2440_ADDR_NCLE;
137 DEBUGN("SETCLE\n");
138 break;
139 default:
/*NF_BASE + 0x10 是data的register--- NAND Flash data register*/
140 chip->IO_ADDR_W = NF_BASE + 0x10; //注意是0x10
141 break;
142 }
143 return ;
144 }
接着研究一下扫描flash的全过程, 会设置也会填充mtd:
如果是small page:
this->cmdfunc = nand_command;
如果是big page:
this->cmdfunc = nand_command_lp;
this->waitfunc = nand_wait;
this->select_chip = nand_select_chip;
this->write_byte = busw ? nand_write_byte16 : nand_write_byte;
//但是使用nand_write_byte
this->read_byte = busw ? nand_read_byte16 : nand_read_byte;
this->write_word = nand_write_word;
this->read_word = nand_read_word;
this->block_bad = nand_block_bad;
this->block_markbad = nand_default_block_markbad;
this->write_buf = busw ? nand_write_buf16 : nand_write_buf;
this->read_buf = busw ? nand_read_buf16 : nand_read_buf;
this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
this->scan_bbt = nand_default_bbt;
接着使用命令NAND_CMD_READID读取flash的ID号,会在现支持的flash列表nand_flash_ids中查找现在的flash, 定义在drivers/mtd/nand/nand_ids.c中:
最后找到的是{"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}。
根据flash的型号设置flash的固有的参数:
最后设置的结果为:
page size :mtd->oobblock = 1024 << (extid & 0x3) = 2K
Redundant Area Size: mtd->oobsize = (8 << (extid & 0x01)) * (mtd->oobblock / 512) = 16*4=64
erase size:mtd->erasesize = (64 * 1024) << (extid & 0x03)=64*1024 << 1 = 128K
busw = 0
this->page_shift = ffs(mtd->oobblock) - 1 = ffs(2048) = 0xb = 11; // how does "ffs" work.
this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1 = ffs(131072) -1 = 0x11 = 17
this->chip_shift = ffs(this->chipsize) - 1 = ffs(134217728)-1 = 0x1b = 27
this->badblockpos = mtd->oobblock > 512 ?
NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS = NAND_LARGE_BADBLOCK_POS
len = mtd->oobsize << (this->phys_erase_shift - this->page_shift)
= 64 << (17 - 11) = 64 << 6 = 4096; this->oob_buf = kmalloc (len, GFP_KERNEL);
led = len = mtd->oobblock + mtd->oobsize = 2048 + 64 = 2112 ;
this->data_buf = kmalloc (len, GFP_KERNEL);
this->autooob = &nand_oob_64;
mtd->oobavail += this->autooob->oobfree[i][1]; // ??????????????
this->eccsize = 256
this->eccbytes = 3;
this->calculate_ecc = nand_calculate_ecc;
this->correct_data = nand_correct_data;
this->eccsteps = mtd->oobblock / 256 = 2048 / 256 = 8;
//作用:需要执行8次来作ECC校验, 每次只能对256byte作校验。
this->pagebuf = -1;
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
mtd->ecctype = MTD_ECC_SW;
mtd->erase = nand_erase;
mtd->point = NULL;
mtd->unpoint = NULL;
mtd->read = nand_read;
mtd->write = nand_write;
mtd->read_ecc = nand_read_ecc;
mtd->write_ecc = nand_write_ecc;
mtd->read_oob = nand_read_oob;
mtd->write_oob = nand_write_oob;
mtd->sync = nand_sync;
mtd->block_isbad = nand_block_isbad;
mtd->block_markbad = nand_block_markbad;
this->erase_cmd = single_erase_cmd;
最后调用:
2730 return this->scan_bbt (mtd); //也就是nand_default_bbt(mtd)
这个函数还没有研究, 以后研究。
在nand_scan函数中会check是small page还是big page的flash:
2450 /* Do not replace user supplied command function ! */
2451 if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
2452 this->cmdfunc = nand_command_lp;
如果是big page,使用nand_command_lp来对flash操作。
1. nand_command
/**
* nand_command - [DEFAULT] Send command to NAND device
* @mtd: MTD device structure
* @command: the command to be sent
* @column: the column address for this command, -1 if none
* @page_addr: the page address for this command, -1 if none
*
* Send command to NAND device. This function is used for small page
* devices (256/512 Bytes per page)
*/
static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
register struct nand_chip *this = mtd->priv;
/* Begin command latch cycle */
this->hwcontrol(mtd, NAND_CTL_SETCLE); //选择写入S3C2410_NFCMD寄存器
/*
* Write out the command to the device.
*/
/* 指令NAND_CMD_SEQIN只有在small page上,而且随机写的时候才可以用到,下面在将write oob的时候还会介绍。*/
if (command == NAND_CMD_SEQIN) {
int readcmd;
if (column >= mtd->oobblock) { //读/写位置超出512,读oob_data
/* OOB area */
column -= mtd->oobblock;
readcmd = NAND_CMD_READOOB;
} else if (column < 256) { //读/写位置在前256,使用read0命令
/* First 256 bytes --> READ0 */
readcmd = NAND_CMD_READ0;
} else { //读/写位置在后256,使用read1命令
column -= 256;
readcmd = NAND_CMD_READ1;
}
this->write_byte(mtd, readcmd); //写入具体命令
}
this->write_byte(mtd, command);
/* Set ALE and clear CLE to start address cycle */
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
if (column != -1 || page_addr != -1) {
/*将chip->IO_ADDR_W 指向 NF_BASE + S3C2440_ADDR_NALE,*/
/*下面的this->write_byte是直接向IO_ADDR_W写值的,这样就实现了同一个函数*/
/*既可以写指令,也可以地址,也可以写数据*/
132 chip->IO_ADDR_W = NF_BASE + S3C2440_ADDR_NALE;*/
this->hwcontrol(mtd, NAND_CTL_SETALE); //选择写入S3C2410_NFADDR寄存器
/* Serially input address */
if (column != -1) {
/* Adjust columns for 16 bit buswidth */
if (this->options & NAND_BUSWIDTH_16)
column >>= 1;
this->write_byte(mtd, column); //写入列地址
}
if (page_addr != -1) { //写入页地址(分三个字节写入)
this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
/* One more address cycle for devices > 32MiB */
if (this->chipsize > (32 << 20))
this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
}
/* Latch in address */
/* 锁存地址 */
this->hwcontrol(mtd, NAND_CTL_CLRALE);
}
/*
* program and erase have their own busy handlers
* status and sequential in needs no delay
*/
switch (command) {
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_SEQIN:
case NAND_CMD_STATUS:
return;
case NAND_CMD_RESET: //复位操作
// 等待nand flash become ready
if (this->dev_ready) //判断nand flash 是否busy(1:ready 0:busy)
break;
udelay(this->chip_delay);
this->hwcontrol(mtd, NAND_CTL_SETCLE);
this->write_byte(mtd, NAND_CMD_STATUS);
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
while ( !(this->read_byte(mtd) & NAND_STATUS_READY));
return;
/* This applies to read commands */
default:
/*
* If we don't have access to the busy pin, we apply the given
* command delay
*/
if (!this->dev_ready) {
udelay (this->chip_delay);//稍作延迟
return;
}
}
/* Apply this short delay always to ensure that we do wait tWB in
* any case on any machine. */
ndelay (100);
nand_wait_ready(mtd);
}
同理nand_command_lp,只是对地址的操作不一样, 机制也有点不一样,下面有区别的时候会介绍到。
共提供了三个函数读取flash, 三个函数功能都不一样, 所有函数的定义在drivers/mtd/nand/nand_base.c中
只是读取, 不做ECC check。
int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen)
{
struct nand_chip *this = mtd->priv;
int page = (int) (from >> this->page_shift); // 要读取的值所在的page, 一个page 2K。
int chip = (int) (from >> this->chip_shift);
int sndcmd = 1;
int cnt = 0;
int pagesize = mtd->oobblock + mtd->oobsize; //一个page的大小, 包括ECC的值。
int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
/* Do not allow reads past end of device */
if ((from + len) > mtd->size) {
MTDDEBUG (MTD_DEBUG_LEVEL0,
"nand_read_raw: Attempt read beyond end of device\n");
return -EINVAL;
}
/* Grab the lock and see if the device is available */
nand_get_device (this, mtd , FL_READING);
this->select_chip (mtd, chip);
/* Add requested oob length */
len += ooblen;
while (len) {
if (sndcmd)
this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask); //nand_command
sndcmd = 0;
this->read_buf (mtd, &buf[cnt], pagesize);
//nand_read_buf(), 读取的长度2K+64byge。
len -= pagesize;
cnt += pagesize;
page++;
if (!this->dev_ready)
udelay (this->chip_delay);
else
while (!this->dev_ready(mtd));
/* Check, if the chip supports auto page increment */
if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
sndcmd = 1;
}
/* Deselect and wake up anyone waiting on the device */
nand_release_device(mtd);
return 0;
}
读取数值后, 会做ECC check, check读取的值是不是正确的。
和函数nand_read_raw的区别是:
1275 case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */
1276 this->read_buf(mtd, data_poi, end); // nand_read_buf
1277 for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc)
1278 this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); //nand_calculate_ecc
第1276行, 只读取2K的大小,然后调用calculate_ecc计算ecc, 放在ecc_calc中:
1308 /* read oobdata */
1309 this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen); //nand_read_buf
然后再读取64byte ecc的值,
1316 for (j = 0; j < oobsel->eccbytes; j++)
1317 ecc_code[j] = oob_data[oob_config[j]];
1319 /* correct data, if neccecary */
1320 for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) {
1321 ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);
check读出的值到底是对的还是错的。
和函数nand_read_ecc一样,使用this->cmdfunc (mtd, NAND_CMD_READOOB,
0x0, page & this->pagemask);读取oob,使用的指令为NAND_CMD_READOOB,
是自己定义的一个指令, 在flash中肯定是没有的, 在nand_command_lp中,
如果使用的是指令NAND_CMD_READOOB,会自动转换为NAND_CMD_READ0,
然后将列地址加上mtd->oobblock也就是一个page的大小2K,不加上oob的空间。
如果是small page 的处理方式有不一样了, 在flash中存在NAND_CMD_READOOB=0x50的指令,直接使用就可以了, 但是列地址就是相对于oob的地址,不是相对于page的起始地址。
654 /* Emulate NAND_CMD_READOOB*/
655 if (command == NAND_CMD_READOOB){
656 column +=mtd->oobblock;
657 command = NAND_CMD_READ0;
658 }
同样也提供了三个函数, 三个函数功能都不一样, 所有函数的定义在drivers/mtd/nand/nand_base.c中
这个函数直接调用nand_write_ecc,只是最后两个参数为NULL, 就是不计算ECC。
return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
这个函数会调用nand_write_page, 对整个page进行操作:
从中
922 switch (eccmode) {
923 /* No ecc, write all */
924 case NAND_ECC_NONE:
925 // printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
926 this->write_buf(mtd, this->data_poi, mtd->oobblock);
927 break;
928
929 /* Software ecc 3/256, write all */
930 case NAND_ECC_SOFT:
931 for (; eccsteps; eccsteps--) {
932 this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
933 for (i = 0; i < 3; i++, eccidx++)
934 oob_buf[oob_config[eccidx]] = ecc_code[i];
935 datidx += this->eccsize;
936 }
937 this->write_buf(mtd, this->data_poi, mtd->oobblock);
938 break;
………………
958 /* Write out OOB data */
959 if (this->options & NAND_HWECC_SYNDROME)
960 this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes);
961 else
962 this->write_buf(mtd, oob_buf, mtd->oobsize);
963
964 /* Send command to actually program the data */
965 this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1);
可以看出来, 不管你选择的是哪种ECC校验方式, 都会向ECC空间中写值,只不过,如果选择NAND_ECC_NONE不会计算ECC的值, 而选择NAND_ECC_SOFT会调用this->calculate_ecc计算。然后写入flash。
写完成以后还会调用nand_verify_pages做verify.
从code上来看,small page 和big page 写oob的方式也是不一样的,这里将的oob是只有oob,没有data:
small page:
在写的时候会check写的地址是什么,如果大于512,会先写入一个NAND_CMD_READOOB=0x50的指令,列地址该为oob的相对地址,
如果大于256会写入一个NAND_CMD_READ1的指令,将列地址改为相对于后半个page的地址,如果小于256,会写一个
NAND_CMD_READ0。
554 if (command == NAND_CMD_SEQIN){
555 intreadcmd;
556
557 if (column >= mtd->oobblock){
558 /* OOB area */
559 column -= mtd->oobblock;
560 readcmd = NAND_CMD_READOOB;
561 } else if (column < 256) { // 当随机写的时候可以用到
562 /* First 256 bytes --> READ0 */
563 readcmd = NAND_CMD_READ0;
564 } else {
565 column -= 256;
566 readcmd = NAND_CMD_READ1;
567 }
568 this->write_byte(mtd, readcmd);
569 }
570 this->write_byte(mtd, command);
而big page就不一样了,直接写,不需要地址转换,地址就是page的起始地址。
1864 this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask);
1865 /* write data */
1866 this->write_buf(mtd, buf, len);
执行过程:nand_erase() ==> nand_erase_nand() ==> nand_block_checkbad() ==> this->erase_cmd(single_erase_cmd)().
nand_block_checkbad() ==> nand_isbad_bbt() :
在看函数nand_isbad_bbt()函数之前,先看一个标记ecc的函数:
static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
struct nand_chip *this = mtd->priv;
u_char buf[2] = {0, 0};
size_t retlen;
int block;
/* Get block number */
block = ((int) ofs) >> this->bbt_erase_shift;
if (this->bbt)
this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
/*
这个暂时不是很好说:内核维护一个标志bad block表,使用2bit来表示1block。
这个表在开机的时候通过扫描nand flash每个block的头两页的oob数据来生成,
发现坏块后至相应的block标志位为非零(有时候至3,但有时候至1,还没搞明白有什么不同)
*/
/* Do we have a flash based bad block table ? */
if (this->options & NAND_USE_FLASH_BBT)//samsun nand flash不属于这种,暂时不去研究,以后同
return nand_update_bbt (mtd, ofs);
/* We write two bytes, so we dont have to mess with 16 bit access */
ofs += mtd->oobsize + (this->badblockpos & ~0x01);//???????????????
return nand_write_oob (mtd, ofs , 2, &retlen, buf);
}
nand_isbad_bbt函数
1024 /**
1025 * nand_isbad_bbt - [NAND Interface] Check if a block is bad
1026 * @mtd: MTD device structure
1027 * @offs: offset in the device
1028 * @allowbbt: allow access to bad block table region
1029 *
1030 */
1031 int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
1032 {
1033 struct nand_chip *this = mtd->priv;
1034 int block;
1035 uint8_t res;
1036
1037 /* Get block number * 2 */
1038 block = (int) (offs >> (this->bbt_erase_shift - 1));
1039 res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
1040
1041 printf("[dancy log|%s:%d].\n",__func__, __LINE__);
1042 MTDDEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: "
1043 "(block %d) 0x%02x\n", (unsigned int)offs, res, block >> 1);
1044
1045 switch ((int)res) {
1046 case 0x00: return 0;
1047 case 0x01: return 1;
1048 case 0x02: return allowbbt ? 0 : 1;
1049 }
1050 return 1;
1051 }
他的计算过程不是很明白, 当不是坏块是res计算值为0, 否则为坏块。
在函数 nand_block_checkbad 中还有一个分支,
531 if (!this->bbt)
532 return this->block_bad(mtd, ofs, getchip); //nand_block_bad
this->bbt是在nand_default_bbt() ==> nand_scan_bbt()中分配空间的,
nand_default_bbt()是在nand_scan的最后调用的,也就是说,如果我把nand_scan()函数的最后return
this->scan_bbt (mtd)注释掉, this->bbt就不会分配空间了,
也就会执行this->block_bad检测该block是不是坏块。
nand_block_bad工作原理:
在函数中,先调用this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos,
page); 命令为NAND_CMD_READOOB, this->badblockpos在init的时候是这样赋值的,
2464 this->badblockpos = mtd->oobblock > 512 ?
2465 NAND_LARGE_BADBLOCK_POS:NAND_SMALL_BADBLOCK_POS;
//NAND_LARGE_BADBLOCK_POS=0, NAND_SMALL_BADBLOCK_POS=5,
//也就是说, 如果是large page, check第2048个byte似乎不是0xff,
//如果是small page会check第517个byte是不是0xff.
459 if (this->read_byte(mtd) != 0xff) //nand_read_byte
460 res = 1;
如果是坏块的话, 就不允许擦除。
调用擦flash。
2128 static void single_erase_cmd (struct mtd_info *mtd, int page)
2129 {
2130 struct nand_chip *this = mtd->priv;
2131 /* Send commands to erase a block */
2132 this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
2133 this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
2134 }
关于nand flash 命令,为了以后看起来方便,我有单独写了一节:
在启动之前从nand flash拷贝到sdram中在common/image.c的nand_read_kernel函数里:
函数调用过程:nand_read()(common/env_nand.c) ==> nand_read_ecc()(drivers/mtd/nand/nand_base.c)中。
函数nand_read_ecc()是会check ecc的,所以,在刚刚开始的时候,如果我在选择NAND_ECC_NONE写flash, 选择NAND_ECC_SOFT读flash, 这时,kernel的数据会一点也读取不出来。