uboot驱动初始化
nand flash初始化
一. 在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,只是对地址的操作不一样, 机制也有点不一样,下面有区别的时候会介绍到。
uboot 读nand flash
共提供了三个函数读取flash, 三个函数功能都不一样, 所有函数的定义在drivers/mtd/nand/nand_base.c中
nand_read_raw
只是读取, 不做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; }
nand_read_ecc
读取数值后, 会做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_oob
和函数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 }
uboot 写nand flash
同样也提供了三个函数, 三个函数功能都不一样, 所有函数的定义在drivers/mtd/nand/nand_base.c中
nand_write
这个函数直接调用nand_write_ecc,只是最后两个参数为NULL, 就是不计算ECC。
return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
nand_write_ecc
这个函数会调用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.
nand_write_oob
从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);
uboot 擦nand flash
执行过程: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 }
uboot nand flash子命令详探
关于nand flash 命令,为了以后看起来方便,我有单独写了一节:Nand flash uboot 命令详解
启动linux
在启动之前从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的数据会一点也读取不出来。
linux ecc校验
关于linux的nand flash驱动研究,请看……
|