一、介绍
Nand flash K9GAG08U0D (2G Byte)
在u-boot的shell里面执行如下命令: 把 rootfs.yaffs从SD卡的第一个分区读取出来,并写到nand flash中去.
SMDK6401> fatload mmc 0:1 50008000 rootfs.yaffs
SMDK6401> nand erase 600000 $(filesize)
SMDK6401> nand write.yaffs2 50008000 600000 $(filesize)
这儿分析一下最后一条命令:将数据写入到yaffs2分区的过程
二、 过程分析
1.1 u-boot/common/cmd_nand.c中
1.2 在文件driver/nand/nand_utils.c中
1.3 在driver/nand/nand_base.c中
1.4 在driver/nand/nand_base.c中
1.5 在driver/nand/nand_base.c中
1.6 在driver/nand/nand_base.c中
1.7 在cpu/s3c64xx/nand.c中
1.8 在 driver/nand/nand_base.c 中
1.9 上述写过程如下图所示:
1.10 OOB数据如下图所示
Nand flash K9GAG08U0D (2G Byte)
在u-boot的shell里面执行如下命令: 把 rootfs.yaffs从SD卡的第一个分区读取出来,并写到nand flash中去.
SMDK6401> fatload mmc 0:1 50008000 rootfs.yaffs
SMDK6401> nand erase 600000 $(filesize)
SMDK6401> nand write.yaffs2 50008000 600000 $(filesize)
这儿分析一下最后一条命令:将数据写入到yaffs2分区的过程
二、 过程分析
1.1 u-boot/common/cmd_nand.c中
- int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
- {
- if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
- addr = (ulong)simple_strtoul(argv[2], NULL, 16);
- read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
-
- if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0)
- return 1;
-
- s = strchr(cmd, '.');
- if (!read && s != NULL && + (!strcmp(s, ".yaffs2") || !strcmp(s, ".yaffs1")))
- {
- nand_write_options_t opts;
- memset(&opts, 0, sizeof(opts));
- opts.buffer = (u_char*) addr; // addr=0x50008000内存
- opts.length = size; // length是文件长度
- opts.offset = off; // offset 是要写到nand flash的地址0x600000
- opts.pad = 0;
- opts.blockalign = 1;
- opts.quiet = quiet;
- opts.writeoob = 1;
- opts.autoplace = 1;
- ret = nand_write_opts(nand, &opts);
- }
- }
argv[0] argv[1] argv[2] argv[3] argv[4]
nand write.yaffs2 50008000 600000 $(filesize)
addr off size=0x420000
- int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
- {
- ulong mtdoffset = opts->offset; // mtdoffset=nand_flash中的偏移0x600000
- ulong erasesize_blockalign;
- u_char *buffer = opts->buffer; // buffer=(u_char*)0x500080
- imglen = opts->length; // imglen是rootfs.yaffs2这个文件的长度
- while (imglen && (mtdoffset < meminfo->size)) {
-
- //下面这个 while判断要写入的块是不是坏块,如果是坏块继续查找直到找到一个好块
- while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) {
- blockstart = mtdoffset & (~erasesize_blockalign+1);
- offs = blockstart;
- baderaseblock = 0;
-
- do {
- int ret = meminfo->block_isbad(meminfo, offs); //判断是不是块坏
-
- if (ret < 0) {
- printf("Bad block check failedn");
- goto restoreoob;
- }
- if (ret == 1) { //ret=1是坏块
- baderaseblock = 1; //这个地方还要设个标志,直接do_something不就得了?
- if (!opts->quiet)
- printf("rBad block at 0x%lx "
- "in erase block from "
- "0x%x will be skippedn",
- (long) offs,
- blockstart);
- }
-
- if (baderaseblock) {
- mtdoffset = blockstart + erasesize_blockalign; //如果ret=1是坏块,要写入的起始位置指向下一个块
- }
- offs += erasesize_blockalign
- / opts->blockalign;
- } while (offs < blockstart + erasesize_blockalign);
- }
-
- readlen = meminfo->writesize;
- memcpy(data_buf, buffer, readlen); //初始时:buffer=(u_char*)0x50008000
- buffer += readlen; //meminfo->writesize= 4096
-
- if (opts->writeoob) {
- memcpy(oob_buf, buffer, meminfo->oobsize); //buffer=(data+oob)*n, oob紧跟data
- buffer += meminfo->oobsize; //meminfo->oobsize = 128
- oob_ops.mode = MTD_OOB_AUTO;
- oob_ops.len = meminfo->writesize; // 每一次写的大小为writesize=4k
- oob_ops.ooboffs = 0;
- oob_ops.ooblen = meminfo->oobsize;
- oob_ops.oobbuf = (unsigned char *)&oob_buf;
- oob_ops.datbuf = (unsigned char *)&data_buf;
-
- result = meminfo->write_oob(meminfo, mtdoffset, &oob_ops); //如果没有坏块的话: mtdoffset=nand_flash中的偏移0x600000
-
- imglen -= meminfo->oobsize;
- }
- imglen -= readlen; // mtd->writesize=4096
- mtdoffset += meminfo->writesize; // mtdoffset指向下一个page,是page_align
- }
- }
- int nand_scan_tail(struct mtd_info *mtd)
- {
- mtd->write_oob = nand_write_oob; //初始化
- }
- //初始化时,所以 meminfo->write_oob(meminfo, mtdoffset, &oob_ops),就是调用nand_write_oob
- static int nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
- {
- struct nand_chip *chip = mtd->priv;
- nand_get_device(chip, mtd, FL_WRITING);
-
- if (!ops->datbuf) //ops->databuf不为空,要调用下面那个
- ret = nand_do_write_oob(mtd, to, ops);
- else
- ret = nand_do_write_ops(mtd, to, ops); //调用这个,这就是nand flash写的过程,可参考下面的图
-
- }
- /**
- * nand_do_write_ops - [Internal] NAND write with ECC
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operations description structure
- *
- * NAND write with ECC
- */
- static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
- {
- int chipnr, realpage, page, blockmask, column;
- struct nand_chip *chip = mtd->priv;
- uint32_t writelen = ops->len;
- uint8_t *oob = ops->oobbuf;
- uint8_t *buf = ops->datbuf;
- int ret, subpage;
-
- ops->retlen = 0;
-
- column = to & (mtd->writesize - 1);
- subpage = column || (writelen & (mtd->writesize - 1));
-
- if (subpage && oob)
- return -EINVAL;
-
- chipnr = (int)(to >> chip->chip_shift);
- chip->select_chip(mtd, chipnr); //写过程第1步,选中芯片
-
- realpage = (int)(to >> chip->page_shift);
- page = realpage & chip->pagemask;
- blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
-
- /* Invalidate the page cache, when we write to the cached page */
- if (to <= (chip->pagebuf << chip->page_shift) &&
- (chip->pagebuf << chip->page_shift) < (to + ops->len))
- chip->pagebuf = -1;
-
- while(1) {
- int bytes = mtd->writesize;
- int cached = writelen > bytes && page != blockmask;
- uint8_t *wbuf = buf;
-
- if (unlikely(column || writelen < (mtd->writesize - 1))) {
- cached = 0;
- bytes = min_t(int, bytes - column, (int) writelen);
- chip->pagebuf = -1;
- memset(chip->buffers->databuf, 0xff, mtd->writesize);
- memcpy(&chip->buffers->databuf[column], buf, bytes);
- wbuf = chip->buffers->databuf;
- }
-
- if (unlikely(oob))
- oob = nand_fill_oob(chip, oob, ops);
-
- ret = chip->write_page(mtd, chip, wbuf, page, cached, (ops->mode == MTD_OOB_RAW));
- if (ret)
- break;
-
- writelen -= bytes;
-
- if (!writelen)
- break;
-
- column = 0;
- buf += bytes;
- realpage++;
-
- page = realpage & chip->pagemask;
- /* Check, if we cross a chip boundary */
- if (!page) {
- chipnr++;
- chip->select_chip(mtd, -1);
- chip->select_chip(mtd, chipnr);
- }
- }
-
- ops->retlen = ops->len - writelen;
- if (unlikely(oob))
- ops->oobretlen = ops->ooblen;
- return ret;
- }
- static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, struct mtd_oob_ops *ops)
- {
- size_t len = ops->ooblen;
- int i=0;
- switch(ops->mode) {
- case MTD_OOB_AUTO:
- {
- struct nand_oobfree *free = chip->ecc.layout->oobfree;
- uint32_t boffs = 0, woffs = ops->ooboffs;
- size_t bytes = 0;
- // free->length=22, len=128, woffs=0
- for(; free->length && len; free++, len -= bytes)
- { // bytes=22, boffs=2
- bytes = min_t(size_t, len, free->length);
- boffs = free->offset;
-
- memcpy(chip->oob_poi + boffs, oob, bytes); //将rootfs.yaffs2中紧跟main区的22个字节copy到oob_poi[2-24]处
- oob += bytes; //这块数据是yaffs2文件系统的信息
- }
- return oob;
- }
- }
- return NULL;
- }
1.6 在driver/nand/nand_base.c中
- nand_scan_tail
{
if (!chip->write_page)
chip->write_page = nand_write_page;
}
- static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int page, int cached, int raw)
- {
-
- int status;
-
- chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); //写过程第2-3步,发命令0x80,发地址
-
- if (unlikely(raw))
- chip->ecc.write_page_raw(mtd, chip, buf);
- else //exec
- chip->ecc.write_page(mtd, chip, buf);
- cached = 0;
- if (!cached || !(chip->options & NAND_CACHEPRG)) {
- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); //写过程第5步,写命令0x10
- status = chip->waitfunc(mtd, chip); //写过程第6步,等侍结束
-
- if ((status & NAND_STATUS_FAIL) && (chip->errstat))
- status = chip->errstat(mtd, chip, FL_WRITING, status,page);
-
- if (status & NAND_STATUS_FAIL)
- return -EIO;
- }
- return 0;
- }
- board_nand_init()
- {
- #if defined(CONFIG_NAND_BL1_8BIT_ECC) && (defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430))
- nand->ecc.write_page = s3c_nand_write_page_8bit;
- nand->ecc.size = 512;
- nand->ecc.bytes = 13;
- nand->ecc.layout = &s3c_nand_oob_mlc_128_8bit;
- #endif
- }
- void s3c_nand_write_page_8bit(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf)
- {
- int i, eccsize = 512;
- int eccbytes = 13;
- int eccsteps = mtd->writesize / eccsize;
- uint8_t *ecc_calc = chip->buffers->ecccalc;
- uint8_t *p = buf;
- //下面这个for代码,写的不明了,
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { //意思是: 每512字节生成13字节的ECC,4K生成13*(4K/512)=104字节的ECC
- s3c_nand_enable_hwecc_8bit(mtd, NAND_ECC_WRITE); //使能硬件ECC
- chip->write_buf(mtd, p, eccsize); //写过程第4步,写数据
- s3c_nand_calculate_ecc_8bit(mtd, p, &ecc_calc[i]); //每写512字节到main区,就生成13字节的ECC,依次填充到ecc_calc[0-13*8=104]处
- }
-
- for (i = 0; i < eccbytes * (mtd->writesize / eccsize); i++)
- chip->oob_poi[i+24] = ecc_calc[i]; //将硬件生成的ECC值填入oob的[24-128]处, ECC_size=8*13=104, (24+104=128)
-
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); //写过程第4步,写OOB(OOB数据紧随main区数据写入的,单独写OOB是写不了的 )
- }
1.8 在 driver/nand/nand_base.c 中
- static void nand_set_defaults(struct nand_chip *chip, int busw)
- {
- if (!chip->write_buf)
- chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
- }
- static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
- {
- int i;
- struct nand_chip *chip = mtd->priv;
-
- for (i = 0; i < len; i++)
- writeb(buf[i], chip->IO_ADDR_W);
- }
![](https://i-blog.csdnimg.cn/blog_migrate/f75b29cc575ebe80d337bedda1dc5b04.png)
1.10 OOB数据如下图所示
![](https://i-blog.csdnimg.cn/blog_migrate/e25f65d6183623152b316430ca3b4417.png)