Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2150657
  • 博文数量: 438
  • 博客积分: 3871
  • 博客等级: 中校
  • 技术积分: 6075
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-10 00:11
个人简介

邮箱: wangcong02345@163.com

文章分类

全部博文(438)

文章存档

2017年(15)

2016年(119)

2015年(91)

2014年(62)

2013年(56)

2012年(79)

2011年(16)

分类: LINUX

2013-06-04 16:53:39

一、介绍
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. int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
  2. {
  3.     if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
  4.         addr = (ulong)simple_strtoul(argv[2], NULL, 16);
  5.         read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */

  6.         if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0)
  7.             return 1;

  8.         s = strchr(cmd, '.');
  9.          if (!read && s != NULL && + (!strcmp(s, ".yaffs2") || !strcmp(s, ".yaffs1")))
  10.          {
  11.             nand_write_options_t opts;
  12.              memset(&opts, 0, sizeof(opts));
  13.              opts.buffer = (u_char*) addr;              // addr=0x50008000内存
  14.              opts.length = size                      // length是文件长度
  15.              opts.offset = off;                         // offset 是要写到nand flash的地址0x600000
  16.              opts.pad = 0;
  17.              opts.blockalign = 1;
  18.              opts.quiet = quiet;
  19.              opts.writeoob = 1;
  20.              opts.autoplace = 1;
  21.              ret = nand_write_opts(nand, &opts);
  22.          }
  23. }

argv[0]    argv[1]      argv[2]    argv[3]   argv[4]
nand     write.yaffs2   50008000   600000   $(filesize)
                         addr       off     size=0x420000

1.2 在文件driver/nand/nand_utils.c中
  1. int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
  2. {
  3.     ulong mtdoffset = opts->offset;                // mtdoffset=nand_flash中的偏移0x600000
  4.     ulong erasesize_blockalign;
  5.     u_char *buffer = opts->buffer;                 // buffer=(u_char*)0x500080
  6.     imglen = opts->length;                         // imglen是rootfs.yaffs2这个文件的长度  
  7.     while (imglen && (mtdoffset < meminfo->size)) {

  8.         //下面这个 while判断要写入的块是不是坏块,如果是坏块继续查找直到找到一个好块
  9.         while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) {
  10.             blockstart = mtdoffset & (~erasesize_blockalign+1);
  11.             offs = blockstart;
  12.             baderaseblock = 0;

  13.             do {
  14.                 int ret = meminfo->block_isbad(meminfo, offs);    //判断是不是块坏

  15.                 if (ret < 0) {
  16.                     printf("Bad block check failedn");
  17.                     goto restoreoob;
  18.                 }
  19.                 if (ret == 1) {                                    //ret=1是坏块
  20.                     baderaseblock = 1;                             //这个地方还要设个标志,直接do_something不就得了?
  21.                     if (!opts->quiet)
  22.                         printf("rBad block at 0x%lx "
  23.                          "in erase block from "
  24.                          "0x%x will be skippedn",
  25.                          (long) offs,
  26.                          blockstart);
  27.                 }

  28.                 if (baderaseblock) {
  29.                     mtdoffset = blockstart + erasesize_blockalign; //如果ret=1是坏块,要写入的起始位置指向下一个块
  30.                 }
  31.                 offs +=     erasesize_blockalign
  32.                     / opts->blockalign;
  33.             } while (offs < blockstart + erasesize_blockalign);
  34.         }

  35.         readlen = meminfo->writesize;       
  36.         memcpy(data_buf, buffer, readlen);                      //初始时:buffer=(u_char*)0x50008000
  37.         buffer += readlen;                                      //meminfo->writesize= 4096

  38.         if (opts->writeoob) {            
  39.             memcpy(oob_buf, buffer, meminfo->oobsize);          //buffer=(data+oob)*n, oob紧跟data
  40.             buffer += meminfo->oobsize;                         //meminfo->oobsize = 128
  41.             oob_ops.mode = MTD_OOB_AUTO;    
  42.             oob_ops.len = meminfo->writesize;                   // 每一次写的大小为writesize=4k
  43.             oob_ops.ooboffs = 0;
  44.             oob_ops.ooblen = meminfo->oobsize;
  45.             oob_ops.oobbuf = (unsigned char *)&oob_buf;
  46.             oob_ops.datbuf = (unsigned char *)&data_buf;

  47.             result = meminfo->write_oob(meminfo, mtdoffset, &oob_ops);    //如果没有坏块的话: mtdoffset=nand_flash中的偏移0x600000
  48.            
  49.             imglen -= meminfo->oobsize;
  50.         }
  51.         imglen -= readlen;                                                // mtd->writesize=4096
  52.         mtdoffset += meminfo->writesize;                                  // mtdoffset指向下一个page,是page_align
  53.     }
  54. }
1.3 在driver/nand/nand_base.c中
  1. int nand_scan_tail(struct mtd_info *mtd)
  2. {
  3.     mtd->write_oob = nand_write_oob;                    //初始化
  4. }
  5. //初始化时,所以 meminfo->write_oob(meminfo, mtdoffset, &oob_ops),就是调用nand_write_oob
  6. static int nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
  7. {
  8.     struct nand_chip *chip = mtd->priv;
  9.     nand_get_device(chip, mtd, FL_WRITING);

  10.     if (!ops->datbuf)                                   //ops->databuf不为空,要调用下面那个
  11.         ret = nand_do_write_oob(mtd, to, ops);
  12.     else
  13.         ret = nand_do_write_ops(mtd, to, ops);         //调用这个,这就是nand flash写的过程,可参考下面的图
  14.    
  15. }
1.4 在driver/nand/nand_base.c中
  1. /**
  2.  * nand_do_write_ops - [Internal] NAND write with ECC
  3.  * @mtd:    MTD device structure
  4.  * @to:        offset to write to
  5.  * @ops:    oob operations description structure
  6.  *
  7.  * NAND write with ECC
  8.  */
  9. static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
  10. {
  11.     int chipnr, realpage, page, blockmask, column;
  12.     struct nand_chip *chip = mtd->priv;
  13.     uint32_t writelen = ops->len;
  14.     uint8_t *oob = ops->oobbuf;
  15.     uint8_t *buf = ops->datbuf;
  16.     int ret, subpage;

  17.     ops->retlen = 0;

  18.     column = to & (mtd->writesize - 1);
  19.     subpage = column || (writelen & (mtd->writesize - 1));

  20.     if (subpage && oob)
  21.         return -EINVAL;

  22.     chipnr = (int)(to >> chip->chip_shift);
  23.     chip->select_chip(mtd, chipnr);                                            //写过程第1步,选中芯片

  24.     realpage = (int)(to >> chip->page_shift);
  25.     page = realpage & chip->pagemask;
  26.     blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;

  27.     /* Invalidate the page cache, when we write to the cached page */
  28.     if (to <= (chip->pagebuf << chip->page_shift) &&
  29.             (chip->pagebuf << chip->page_shift) < (to + ops->len))
  30.         chip->pagebuf = -1;

  31.     while(1) {
  32.         int bytes = mtd->writesize;
  33.         int cached = writelen > bytes && page != blockmask;
  34.         uint8_t *wbuf = buf;

  35.         if (unlikely(column || writelen < (mtd->writesize - 1))) {
  36.             cached = 0;
  37.             bytes = min_t(int, bytes - column, (int) writelen);
  38.             chip->pagebuf = -1;
  39.             memset(chip->buffers->databuf, 0xff, mtd->writesize);
  40.             memcpy(&chip->buffers->databuf[column], buf, bytes);
  41.             wbuf = chip->buffers->databuf;
  42.         }

  43.         if (unlikely(oob))
  44.             oob = nand_fill_oob(chip, oob, ops);

  45.         ret = chip->write_page(mtd, chip, wbuf, page, cached,  (ops->mode == MTD_OOB_RAW));
  46.         if (ret)
  47.             break;

  48.         writelen -= bytes;

  49.         if (!writelen)
  50.             break;

  51.         column = 0;
  52.         buf += bytes;
  53.         realpage++;

  54.         page = realpage & chip->pagemask;
  55.         /* Check, if we cross a chip boundary */
  56.         if (!page) {
  57.             chipnr++;
  58.             chip->select_chip(mtd, -1);
  59.             chip->select_chip(mtd, chipnr);
  60.         }
  61.     }

  62.     ops->retlen = ops->len - writelen;
  63.     if (unlikely(oob))
  64.         ops->oobretlen = ops->ooblen;
  65.     return ret;
  66. }
1.5 在driver/nand/nand_base.c中
  1. static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,    struct mtd_oob_ops *ops)
  2. {
  3.     size_t len = ops->ooblen;
  4.     int i=0;
  5.     switch(ops->mode) {
  6.         case MTD_OOB_AUTO:
  7.         {
  8.          struct nand_oobfree *free = chip->ecc.layout->oobfree;
  9.          uint32_t boffs = 0, woffs = ops->ooboffs;
  10.          size_t bytes = 0;
  11.          // free->length=22, len=128, woffs=0
  12.          for(; free->length && len; free++, len -= bytes) 
  13.          {    // bytes=22, boffs=2
  14.             bytes = min_t(size_t, len, free->length);
  15.             boffs = free->offset;
  16.            
  17.             memcpy(chip->oob_poi + boffs, oob, bytes);                       //将rootfs.yaffs2中紧跟main区的22个字节copy到oob_poi[2-24]    
  18.             oob += bytes;                                                    //这块数据是yaffs2文件系统信息
  19.          }
  20.          return oob;    
  21.         }
  22.      }      
  23.     return NULL;
  24. }

1.6 在driver/nand/nand_base.c中

  1. nand_scan_tail
    {
        if (!chip->write_page)
            chip->write_page = nand_write_page;
    }
  2. static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,  const uint8_t *buf, int page, int cached, int raw)
  3. {

  4.     int status;

  5.     chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);                            //写过程第2-3步,发命令0x80,发地址

  6.     if (unlikely(raw))
  7.        chip->ecc.write_page_raw(mtd, chip, buf);
  8.     else    //exec      
  9.        chip->ecc.write_page(mtd, chip, buf); 
  10.    
  11.    cached = 0;
  12.     if (!cached || !(chip->options & NAND_CACHEPRG)) {
  13.         chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);                           //写过程第5步,写命令0x10
  14.         status = chip->waitfunc(mtd, chip);                                                          //写过程第6步,等侍结束
  15.        
  16.         if ((status & NAND_STATUS_FAIL) && (chip->errstat))
  17.             status = chip->errstat(mtd, chip, FL_WRITING, status,page);

  18.         if (status & NAND_STATUS_FAIL)
  19.             return -EIO;
  20.     }
  21.     return 0;
  22. }
1.7 在cpu/s3c64xx/nand.c中
  1. board_nand_init()
  2. {
  3. #if defined(CONFIG_NAND_BL1_8BIT_ECC) && (defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430))
  4.     nand->ecc.write_page = s3c_nand_write_page_8bit;
  5.     nand->ecc.size = 512;
  6.     nand->ecc.bytes = 13;
  7.     nand->ecc.layout = &s3c_nand_oob_mlc_128_8bit;
  8. #endif
  9. }
  10. void s3c_nand_write_page_8bit(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf)
  11. {
  12.     int i, eccsize = 512;
  13.     int eccbytes = 13;
  14.     int eccsteps = mtd->writesize / eccsize;
  15.     uint8_t *ecc_calc = chip->buffers->ecccalc;
  16.     uint8_t *p = buf;
  17.                                                                                     //下面这个for代码,写的不明了,            
  18.     for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {                //意思是: 每512字节生成13字节的ECC,4K生成13*(4K/512)=104字节的ECC
  19.         s3c_nand_enable_hwecc_8bit(mtd, NAND_ECC_WRITE);                            //使能硬件ECC
  20.         chip->write_buf(mtd, p, eccsize);                                           //写过程第4步,写数据
  21.         s3c_nand_calculate_ecc_8bit(mtd, p, &ecc_calc[i]);                          //每写512字节到main区,就生成13字节的ECC,依次填充到ecc_calc[0-13*8=104]处
  22.     }

  23.     for (i = 0; i < eccbytes * (mtd->writesize / eccsize); i++)
  24.         chip->oob_poi[i+24] = ecc_calc[i];                                      //将硬件生成的ECC值填入oob的[24-128]处, ECC_size=8*13=104, (24+104=128)

  25.     chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);                 //写过程第4步,写OOB(OOB数据紧随main区数据写入的,单独写OOB是写不了的 )            
  26. }

1.8driver/nand/nand_base.c
  1. static void nand_set_defaults(struct nand_chip *chip, int busw)
  2. {
  3.     if (!chip->write_buf)
  4.         chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
  5. }
  6. static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
  7. {
  8.     int i;
  9.     struct nand_chip *chip = mtd->priv;

  10.     for (i = 0; i < len; i++)
  11.         writeb(buf[i], chip->IO_ADDR_W);
  12. }
1.9 上述写过程如下图所示:

1.10 OOB数据如下图所示
阅读(3391) | 评论(0) | 转发(3) |
给主人留下些什么吧!~~