uboot之nand flash相关(3)

310阅读 0评论2016-10-06 诺亚方舟破土巴郎
分类:LINUX

然后我们分析一下nand flash的读写等函数。
既然是命令那自然要看到U_BOOT_CMD宏,这个宏分析的很多就不分析了。在cmd_nand.c文件中。nand的命令执行函数是do_nand。当然我们没有定义CFG_NAND_LEGACY,要看这个分支。do_nand函数也没有什么好分析的,摘取几个命令的处理分析下。
1。nand bad命令 
列出函数调用次序先,
do_nand
nand_block_isbad//include/nand.h
nand_block_isbad//在Nand_base.c中, info->block_isbad函数指针指向
nand_block_checkbad//在Nand_base.c
nand_block_bad() //在Nand_base.c中,nand_chip,this->block_bad函数指针指向,
nand_isbad_bbt //在Nand_bbt.c中
如下。

点击(此处)折叠或打开

  1. nand = &nand_info[nand_curr_device];
  2. if (strcmp(cmd, "bad") == 0) {
  3.         printf("\nDevice %d bad blocks:\n", nand_curr_device);
  4.         for (off = 0; off < nand->size; off += nand->erasesize)    //按块循环
  5.             if (nand_block_isbad(nand, off))    //(1)
  6.                 printf(" %08x\n", off);
  7.         return 0;
  8.     }
这个函数的定义在include/nand.h中,它调用nand_info[]变量中的block_isbad函数指针指向的函数;这个指针在初始化时已经被分配,这里是Nand_base.c文件中的nand_block_isbad函数。这里有个小问题,那有两个都被编译的nand_block_isbad函数的定义,那到底调用的是哪个呢。答案是nand.h中的,因为Nand_base.c中的是被定义成static的函数,只能在本文件中使用。

点击(此处)折叠或打开

  1. static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs)
  2. {
  3.     /* Check for invalid offset */
  4.     if (ofs > mtd->size)
  5.         return -EINVAL;

  6.     return nand_block_checkbad (mtd, ofs, 1, 0);
  7. }
这个函数又会调用nand_block_checkbad 函数

点击(此处)折叠或打开

  1. static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt)
  2. {
  3.     struct nand_chip *this = mtd->priv;

  4.     if (!this->bbt)    //如果nand_chip结构体变量中的bbt(坏块标记表)表指针是空的
  5.         return this->block_bad(mtd, ofs, getchip);

  6.     /* Return info from the table */
  7.     return nand_isbad_bbt (mtd, ofs, allowbbt);
  8. }
block_bad函数指针被指向nand_block_bad,分析它,
此函数将从芯片读取坏块标记

点击(此处)折叠或打开

  1. static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
  2. {
  3.     int page, chipnr, res = 0;
  4.     struct nand_chip *this = mtd->priv;
  5.     u16 bad;

  6.     page = (int)(ofs >> this->page_shift) & this->pagemask;            //(1)

  7.     if (getchip) {            //选中芯片
  8.         chipnr = (int)(ofs >> this->chip_shift);

  9.         /* Grab the lock and see if the device is available */
  10.         nand_get_device (this, mtd, FL_READING);

  11.         /* Select the NAND device */
  12.         this->select_chip(mtd, chipnr);
  13.     }

  14.     if (this->options & NAND_BUSWIDTH_16) {
  15.         this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page);
  16.         bad = cpu_to_le16(this->read_word(mtd));
  17.         if (this->badblockpos & 0x1)
  18.             bad >>= 1;
  19.         if ((bad & 0xFF) != 0xff)
  20.             res = 1;
  21.     } else {
  22.         this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page);    //(2)
  23.         if (this->read_byte(mtd) != 0xff)        //(3)
  24.             res = 1;
  25.     }

  26.     if (getchip) {
  27.         /* Deselect and wake up anyone waiting on the device */
  28.         nand_release_device(mtd);
  29.     }

  30.     return res;
  31. }
(1)从偏移地址获取页号。page_shift是page页位数(就是一页的大小的数值用二进制表示最高位的序号)。将偏移地址右移页位数,则低位就是页的号码,有相当于除页大小。然后在与上pagemask,就是页大小(主要是将高位置0,其实这里与不与感觉都无所谓,高位本来就是0)
(2)主要就是这一句,cmdfunc()函数,发送读取oob区命令。this->badblockpos在nand_scan函数中设置了大页0,小页5。
(3)读出的位是否是0xff,如果不是就是坏块。
...................................
再看下如果有bbt表,nand_block_checkbad函数将调用nand_isbad_bbt。bbt表在初始化时scan_bbt函数已经建立。所以nand bad命令在这个uboot中都是通过查bbt表完成的。

点击(此处)折叠或打开

  1. int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
  2. {
  3.     struct nand_chip *this = mtd->priv;
  4.     int block;
  5.     uint8_t    res;

  6.     /* Get block number * 2 */
  7.     block = (int) (offs >> (this->bbt_erase_shift - 1));        //(1)
  8.     res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;    //(2)

  9.     DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
  10.         (unsigned int)offs, res, block >> 1);

  11.     switch ((int)res) {
  12.     case 0x00:    return 0;
  13.     case 0x01:    return 1;
  14.     case 0x02:    return allowbbt ? 0 : 1;
  15.     }
  16.     return 1;
  17. }
nand bad命令处理暂时分析到这里
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

分析nand read 命令:
nand read命令的调用顺序为:
do_nand //cmd_nand.c
nand_read_opts ///driver/mtd/nand/nand_util.c
nand_read //nand_base.c ,meminfo->read指针指向
nand_read_ecc //nand_base.c
sep4020_nand_read_buf//cpu/sep4020/nand_flash.c

这里的代码大多照搬了内核的mtd层代码,而仅仅对于uboot不需要这么复杂,一些操作觉得不合理,有很多无用又费周折的操作。
do_nand函数中read相关部分:

点击(此处)折叠或打开

  1. if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
  2.         int read;

  3.         if (argc < 4)
  4.             goto usage;

  5.         addr = (ulong)simple_strtoul(argv[2], NULL, 16);

  6.         read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
  7.         printf("\nNAND %s: ", read ? "read" : "write");
  8.         if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0)
  9.             return 1;

  10.         s = strchr(cmd, '.');
  11.         if (s != NULL &&
  12.          (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) {
  13.             if (read) {
  14.                 /* read */
  15.                 nand_read_options_t opts;
  16.                 memset(&opts, 0, sizeof(opts));
  17.                 opts.buffer    = (u_char*) addr;    //addr是内存地址,nand读出来的数据最终将存入这里
  18.                 opts.length    = size;    //读取的大小
  19.                 opts.offset    = off;    //flash地址
  20.                 opts.quiet = quiet;
  21.                 ret = nand_read_opts(nand, &opts);    //读数据操作,opts将保存必要的信息。
  22.             } else {
  23.                 /* write */
  24.                 nand_write_options_t opts;
  25.                 memset(&opts, 0, sizeof(opts));
  26.                 opts.buffer    = (u_char*) addr;
  27.                 opts.length    = size;
  28.                 opts.offset    = off;
  29.                 /* opts.forcejffs2 = 1; */
  30.                 opts.pad    = 1;
  31.                 opts.blockalign = 1;
  32.                 opts.quiet = quiet;
  33.                 ret = nand_write_opts(nand, &opts);
  34.             }
  35.         } else if (s != NULL && !strcmp(s, ".yaffs")) {
  36.             if (read) {
  37.                 /* read */
  38.                 nand_read_options_t opts;
  39.                 memset(&opts, 0, sizeof(opts));
  40.                 opts.buffer    = (u_char*) addr;
  41.                 opts.length    = size;
  42.                 opts.offset    = off;
  43.                 opts.quiet = quiet;
  44.                 ret = nand_read_opts(nand, &opts);
  45.             } else {
  46.                 /* write */
  47.                 nand_write_options_t opts;
  48.                 memset(&opts, 0, sizeof(opts));
  49.                 opts.buffer    = (u_char*) addr;    
  50.                 opts.length    = size;
  51.                 opts.offset    = off;
  52.                 /* opts.forceyaffs = 1; */
  53.                 opts.noecc = 1;
  54.                 opts.writeoob = 1;    
  55.                 opts.blockalign = 1;
  56.                 opts.quiet = quiet;
  57.                 opts.skipfirstblk = 1;
  58.                 ret = nand_write_opts(nand, &opts);
  59.             }


  60.         } else if (s != NULL && !strcmp(s, ".oob")) {
  61.             /* read out-of-band data */
  62.             if (read)
  63.                 ret = nand->read_oob(nand, off, size, &size,
  64.                          (u_char *) addr);
  65.             else
  66.                 ret = nand->write_oob(nand, off, size, &size,
  67.                          (u_char *) addr);
  68.         } else {
  69.             if (read)
  70.                 ret = nand_read(nand, off, &size, (u_char *)addr);
  71.             else
  72.                 ret = nand_write(nand, off, &size, (u_char *)addr);
  73.         }

  74.         printf(" %d bytes %s: %s\n", size,
  75.          read ? "read" : "written", ret ? "ERROR" : "OK");

  76.         return ret == 0 ? 0 : 1;
  77.     }
nand_read_opts在/driver/mtd/nand/nand_util.c,参照代码中原本的英文注释,代码量大也就不做详细分析了

点击(此处)折叠或打开

  1. /**
  2.  * nand_read_opts: - read image from NAND flash with support for various options
  3.  *
  4.  * @param meminfo    NAND device to erase
  5.  * @param opts        read options (@see struct nand_read_options)
  6.  * @return        0 in case of success
  7.  *
  8.  */
  9. int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)
  10. {
  11.     int imglen = opts->length;
  12.     int pagelen;
  13.     int baderaseblock;
  14.     int blockstart = -1;
  15.     int percent_complete = -1;
  16.     loff_t offs;
  17.     size_t readlen;
  18.     ulong mtdoffset = opts->offset;
  19.     u_char *buffer = opts->buffer;
  20.     int result;

  21.     /* make sure device page sizes are valid */
  22.     if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512)
  23.      && !(meminfo->oobsize == 8 && meminfo->oobblock == 256)
  24.      && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) {
  25.         printf("Unknown flash (not normal NAND)\n");
  26.         return -1;
  27.     }

  28.     pagelen = meminfo->oobblock
  29.         + ((opts->readoob != 0) ? meminfo->oobsize : 0);

  30.     /* check, if length is not larger than device */
  31.     if (((imglen / pagelen) * meminfo->oobblock)
  32.      > (meminfo->size - opts->offset)) {
  33.         printf("Image %d bytes, NAND page %d bytes, "
  34.          "OOB area %u bytes, device size %u bytes\n",
  35.          imglen, pagelen, meminfo->oobblock, meminfo->size);
  36.         printf("Input block is larger than device\n");
  37.         return -1;
  38.     }

  39.     if (!opts->quiet)
  40.         printf("\n");

  41.     /* get data from input and write to the device */
  42.     while (imglen && (mtdoffset < meminfo->size)) {

  43.         WATCHDOG_RESET ();

  44.         /*
  45.          * new eraseblock, check for bad block(s). Stay in the
  46.          * loop to be sure if the offset changes because of
  47.          * a bad block, that the next block that will be
  48.          * written to is also checked. Thus avoiding errors if
  49.          * the block(s) after the skipped block(s) is also bad
  50.          * (number of blocks depending on the blockalign
  51.          */
  52.         while (blockstart != (mtdoffset & (~meminfo->erasesize+1))) {
  53.             blockstart = mtdoffset & (~meminfo->erasesize+1);
  54.             offs = blockstart;
  55.             baderaseblock = 0;

  56.             /* check all the blocks in an erase block for
  57.              * bad blocks */
  58.             do {
  59.                 int ret = meminfo->block_isbad(meminfo, offs);

  60.                 if (ret < 0) {
  61.                     printf("Bad block check failed\n");
  62.                     return -1;
  63.                 }
  64.                 if (ret == 1) {
  65.                     baderaseblock = 1;
  66.                     if (!opts->quiet)
  67.                         printf("\rBad block at 0x%lx "
  68.                          "in erase block from "
  69.                          "0x%x will be skipped\n",
  70.                          (long) offs,
  71.                          blockstart);
  72.                 }

  73.                 if (baderaseblock) {
  74.                     mtdoffset = blockstart
  75.                         + meminfo->erasesize;
  76.                 }
  77.                 offs +=     meminfo->erasesize;

  78.             } while (offs < blockstart + meminfo->erasesize);
  79.         }


  80.         /* read page data to memory buffer */
  81.         result = meminfo->read(meminfo,            //读2048字节(不包含oob的一页)
  82.                  mtdoffset,                //nand flash地址
  83.                  meminfo->oobblock,    //页大小(2048),即需要读取的字节数
  84.                  &readlen,
  85.                  (unsigned char *) &data_buf);

  86.         if (result != 0) {
  87.             printf("reading NAND page at offset 0x%lx failed\n",
  88.              mtdoffset);
  89.             return -1;
  90.         }

  91.         if (imglen < readlen) {
  92.             readlen = imglen;
  93.         }

  94.         memcpy(buffer, data_buf, readlen);
  95.         buffer += readlen;
  96.         imglen -= readlen;
  97.         
  98. //上面是读页有效数据(2048),这里读oob数据。
  99.         if (opts->readoob) {                
  100.             result = meminfo->read_oob(meminfo,
  101.                          mtdoffset,
  102.                          meminfo->oobsize,
  103.                          &readlen,
  104.                          (unsigned char *)
  105.                          &oob_buf);

  106.             if (result != 0) {
  107.                 printf("\nMTD readoob failure: %d\n",
  108.                  result);
  109.                 return -1;
  110.             }


  111.             if (imglen < readlen) {
  112.                 readlen = imglen;
  113.             }

  114.             memcpy(buffer, oob_buf, readlen);

  115.             buffer += readlen;
  116.             imglen -= readlen;
  117.         }

  118.         if (!opts->quiet) {
  119.             unsigned long long n = (unsigned long long)
  120.                  (opts->length-imglen) * 100;
  121.             int percent;

  122.             do_div(n, opts->length);
  123.             percent = (int)n;

  124.             /* output progress message only at whole percent
  125.              * steps to reduce the number of messages printed
  126.              * on (slow) serial consoles
  127.              */
  128.             if (percent != percent_complete) {
  129.             if (!opts->quiet)
  130.                 printf("\rReading data from 0x%x "
  131.                  "-- %3d%% complete.",
  132.                  mtdoffset, percent);
  133.                 percent_complete = percent;
  134.             }
  135.         }

  136.         mtdoffset += meminfo->oobblock;
  137.     }

  138.     if (!opts->quiet)
  139.         printf("\n");

  140.     if (imglen > 0) {
  141.         printf("Could not read entire image due to bad blocks\n");
  142.         return -1;
  143.     }

  144.     /* return happy */
  145.     return 0;
  146. }
上面meminfo->read指向的函数是,nand_read在nand_base.c文件中。

点击(此处)折叠或打开

  1. static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
  2. {
  3.     return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL);
  4. }
nand_read_ecc函数在nand_base.c中,函数如下

点击(此处)折叠或打开

  1. /**
  2.  * nand_read_ecc - [MTD Interface] Read data with ECC
  3.  * @mtd:    MTD device structure
  4.  * @from:    offset to read from
  5.  * @len:    number of bytes to read
  6.  * @retlen:    pointer to variable to store the number of read bytes
  7.  * @buf:    the databuffer to put data
  8.  * @oob_buf:    filesystem supplied oob data buffer
  9.  * @oobsel:    oob selection structure
  10.  *
  11.  * NAND read with ECC
  12.  */
  13. static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
  14.              size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
  15. {
  16.     int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
  17.     int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
  18.     struct nand_chip *this = mtd->priv;
  19.     u_char *data_poi, *oob_data = oob_buf;
  20.     u_char ecc_calc[32];
  21.     u_char ecc_code[32];
  22.     int eccmode, eccsteps;
  23.     unsigned *oob_config;
  24.     int    datidx;
  25.     int    blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
  26.     int    eccbytes;
  27.     int    compareecc = 1;
  28.     int    oobreadlen;


  29.     DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);

  30.     /* Do not allow reads past end of device */
  31.     if ((from + len) > mtd->size) {
  32.         DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n");
  33.         *retlen = 0;
  34.         return -EINVAL;
  35.     }

  36.     /* Grab the lock and see if the device is available */
  37.     nand_get_device (this, mtd ,FL_READING);

  38.     /* use userspace supplied oobinfo, if zero */
  39.     if (oobsel == NULL)
  40.         oobsel = &mtd->oobinfo;

  41.     /* Autoplace of oob data ? Use the default placement scheme */
  42.     if (oobsel->useecc == MTD_NANDECC_AUTOPLACE)
  43.         oobsel = this->autooob;

  44.     eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
  45.     oob_config = oobsel->eccpos;

  46.     /* Select the NAND device */
  47.     chipnr = (int)(from >> this->chip_shift);
  48.     this->select_chip(mtd, chipnr);

  49.     /* First we calculate the starting page */
  50.     realpage = (int) (from >> this->page_shift);
  51.     page = realpage & this->pagemask;

  52.     /* Get raw starting column */
  53.     col = from & (mtd->oobblock - 1);

  54.     end = mtd->oobblock;
  55.     ecc = this->eccsize;
  56.     eccbytes = this->eccbytes;

  57.     if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME))
  58.         compareecc = 0;

  59.     oobreadlen = mtd->oobsize;
  60.     if (this->options & NAND_HWECC_SYNDROME)
  61.         oobreadlen -= oobsel->eccbytes;

  62.     /* Loop until all data read */
  63.     while (read < len) {

  64.         int aligned = (!col && (len - read) >= end);
  65.         /*
  66.          * If the read is not page aligned, we have to read into data buffer
  67.          * due to ecc, else we read into return buffer direct
  68.          */
  69.         if (aligned)
  70.             data_poi = &buf[read];
  71.         else
  72.             data_poi = this->data_buf;

  73.         /* Check, if we have this page in the buffer
  74.          *
  75.          * FIXME: Make it work when we must provide oob data too,
  76.          * check the usage of data_buf oob field
  77.          */
  78.         if (realpage == this->pagebuf && !oob_buf) {
  79.             /* aligned read ? */
  80.             if (aligned)
  81.                 memcpy (data_poi, this->data_buf, end);
  82.             goto readdata;
  83.         }

  84.         /* Check, if we must send the read command */
  85.         if (sndcmd) { //板级读命令发送,其实这里主要设置了nandflash的地址。
  86.             this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
  87.             sndcmd = 0;
  88.         }

  89.         /* get oob area, if we have no oob buffer from fs-driver */
  90.         if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE ||
  91.             oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
  92.             oob_data = &this->data_buf[end];

  93.         eccsteps = this->eccsteps;

  94.         switch (eccmode) {
  95.         case NAND_ECC_NONE: {    /* No ECC, Read in a page */
  96. /* XXX U-BOOT XXX */
  97. #if 0
  98.             static unsigned long lastwhinge = 0;
  99.             if ((lastwhinge / HZ) != (jiffies / HZ)) {
  100.                 printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n");
  101.                 lastwhinge = jiffies;
  102.             }
  103. #else
  104.             puts("Reading data from NAND FLASH without ECC is not recommended\n");
  105. #endif
  106.             this->read_buf(mtd, data_poi, end);
  107.             break;
  108.         }

  109.         case NAND_ECC_SOFT:    /* Software ECC 3/256: Read in a page + oob data */
  110.             this->read_buf(mtd, data_poi, end);    //读取数据
  111.             for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc)
  112.                 this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
  113.             break;

  114.         default:
  115.             for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) {
  116.                 this->enable_hwecc(mtd, NAND_ECC_READ);
  117.                 this->read_buf(mtd, &data_poi[datidx], ecc);

  118.                 /* HW ecc with syndrome calculation must read the
  119.                  * syndrome from flash immidiately after the data */
  120.                 if (!compareecc) {
  121.                     /* Some hw ecc generators need to know when the
  122.                      * syndrome is read from flash */
  123.                     this->enable_hwecc(mtd, NAND_ECC_READSYN);
  124.                     this->read_buf(mtd, &oob_data[i], eccbytes);
  125.                     /* We calc error correction directly, it checks the hw
  126.                      * generator for an error, reads back the syndrome and
  127.                      * does the error correction on the fly */
  128.                     if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) {
  129.                         DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: "
  130.                             "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
  131.                         ecc_failed++;
  132.                     }
  133.                 } else {
  134.                     this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
  135.                 }
  136.             }
  137.             break;
  138.         }

  139.         /* read oobdata */
  140.         this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen);

  141.         /* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */
  142.         if (!compareecc)
  143.             goto readoob;

  144.         /* Pick the ECC bytes out of the oob data */
  145.         for (j = 0; j < oobsel->eccbytes; j++)
  146.             ecc_code[j] = oob_data[oob_config[j]];

  147.         /* correct data, if neccecary */
  148.         for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) {
  149.             ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);

  150.             /* Get next chunk of ecc bytes */
  151.             j += eccbytes;

  152.             /* Check, if we have a fs supplied oob-buffer,
  153.              * This is the legacy mode. Used by YAFFS1
  154.              * Should go away some day
  155.              */
  156.             if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) {
  157.                 int *p = (int *)(&oob_data[mtd->oobsize]);
  158.                 p[i] = ecc_status;
  159.             }

  160.             if (ecc_status == -1) {
  161.                 DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
  162.                 ecc_failed++;
  163.             }
  164.         }

  165.     readoob:
  166.         /* check, if we have a fs supplied oob-buffer */
  167.         if (oob_buf) {
  168.             /* without autoplace. Legacy mode used by YAFFS1 */
  169.             switch(oobsel->useecc) {
  170.             case MTD_NANDECC_AUTOPLACE:
  171.             case MTD_NANDECC_AUTOPL_USR:
  172.                 /* Walk through the autoplace chunks */
  173.                 for (i = 0, j = 0; j < mtd->oobavail; i++) {
  174.                     int from = oobsel->oobfree[i][0];
  175.                     int num = oobsel->oobfree[i][1];
  176.                     memcpy(&oob_buf[oob+j], &oob_data[from], num);
  177.                     j+= num;
  178.                 }
  179.                 oob += mtd->oobavail;
  180.                 break;
  181.             case MTD_NANDECC_PLACE:
  182.                 /* YAFFS1 legacy mode */
  183.                 oob_data += this->eccsteps * sizeof (int);
  184.             default:
  185.                 oob_data += mtd->oobsize;
  186.             }
  187.         }
  188.     readdata:
  189.         /* Partial page read, transfer data into fs buffer */
  190.         if (!aligned) {
  191.             for (j = col; j < end && read < len; j++)
  192.                 buf[read++] = data_poi[j];
  193.             this->pagebuf = realpage;
  194.         } else
  195.             read += mtd->oobblock;

  196.         /* Apply delay or wait for ready/busy pin
  197.          * Do this before the AUTOINCR check, so no problems
  198.          * arise if a chip which does auto increment
  199.          * is marked as NOAUTOINCR by the board driver.
  200.         */
  201.         if (!this->dev_ready)
  202.             udelay (this->chip_delay);
  203.         else
  204.             while (!this->dev_ready(mtd));

  205.         if (read == len)
  206.             break;

  207.         /* For subsequent reads align to page boundary. */
  208.         col = 0;
  209.         /* Increment page address */
  210.         realpage++;

  211.         page = realpage & this->pagemask;
  212.         /* Check, if we cross a chip boundary */
  213.         if (!page) {
  214.             chipnr++;
  215.             this->select_chip(mtd, -1);
  216.             this->select_chip(mtd, chipnr);
  217.         }
  218.         /* Check, if the chip supports auto page increment
  219.          * or if we have hit a block boundary.
  220.         */
  221.         if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
  222.             sndcmd = 1;
  223.     }

  224.     /* Deselect and wake up anyone waiting on the device */
  225.     nand_release_device(mtd);

  226.     /*
  227.      * Return success, if no ECC failures, else -EBADMSG
  228.      * fs driver will take care of that, because
  229.      * retlen == desired len and result == -EBADMSG
  230.      */
  231.     *retlen = read;
  232.     return ecc_failed ? -EBADMSG : 0;
  233. }

this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page)函数指针指向的函数为sep4020_nand_command函数

点击(此处)折叠或打开

  1. static void sep4020_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
  2. {
  3.     register struct nand_chip *this = mtd->priv;

  4.     if(command == NAND_CMD_READOOB) //(1)
  5.     {
  6.         column += mtd->oobblock;
  7.         command = NAND_CMD_READ0;
  8.     }
  9.     //column是坏块在oob中的位置,加上oobblock(就是页大小pagesiz,不知道为什么起这个名字oobblock),这样就是
  10.     //地址中的列地址。command命令赋值NAND_CMD_READ0(0),读命令。

  11.     this->hwcontrol(mtd, NAND_CTL_SETCLE);
  12.     //命令引脚使能

  13.     switch(command)
  14.     {
  15.         case NAND_CMD_READ0:
  16.             *(volatile unsigned long*)EMI_NAND_COM_RAW = 0x40003000;
  17.                 //这个寄存器[7:0]命令的第一个字节00 [15:8]是命令的第二个字节30 .
  18.                 //最高位是使能位(暂不开启),30位是字节表示1字节还是2字节命令。4=0100
  19.             break;
  20.         case NAND_CMD_SEQIN:
  21.             *(volatile unsigned long*)EMI_NAND_COM_RAW = 0x40001080;
  22.             // 80,10 写flash
  23.             break;
  24.         default:
  25.             this->write_byte(mtd,command);
  26.             break;
  27.     }
  28.     this->hwcontrol(mtd,NAND_CTL_CLRCLE);
  29.  //命令引脚无效
  30.     if (command == NAND_CMD_READID)
  31.     {
  32.         EMI_NAND_COM |= 0x80000000; //使能EMI_NAND_COM
  33.         this->hwcontrol(mtd, NAND_READ_ID); 
  34.         return;
  35.     }
  36.     
  37.     if (command == NAND_CMD_STATUS)
  38.     {
  39.         EMI_NAND_COM |= 0x80000000; //使能EMI_NAND_COM
  40.         this->hwcontrol(mtd, NAND_READ_STATUS);
  41.     }
  42.     
  43.     if (command == NAND_CMD_RESET)
  44.     {
  45.         EMI_NAND_COM |= 0x80000000;
  46.         this->hwcontrol(mtd, NAND_CTL_CLRALE);
  47.     }
  48.     /* Set ALE and clear CLE to start address cycle */

  49.     if (column != -|| page_addr != -1) {
  50.         this->hwcontrol(mtd, NAND_CTL_SETALE); //这里这个函数其实没什么用。 
  51.         EMI_NAND_ADDR1 = page_addr<<16; //page_addr是页号。128M,2Kflash一共就64K页
  52.         EMI_NAND_ADDR2 = page_addr>>16; //对于一共总数64K的页,这个值等于0
  53.         this->hwcontrol(mtd, NAND_CTL_CLRALE);
  54.     }
  55.     //

  56. }
分析sep4020_hwcontrol函数。此函数之所以存在,应该是为了和MCU通过引脚直接控制或其他MCU的nand flash的代码结构保持兼容,此处此函数的主要作用是将IO_ADDR_W替换成对应的寄存器地址

点击(此处)折叠或打开

  1. static void sep4020_hwcontrol(struct mtd_info *mtd, int cmd)
  2. {    
  3.     struct nand_chip *this = mtd->priv;
  4.     
  5.     switch (cmd) {
  6.     case NAND_CTL_SETNCE:        
  7.     case NAND_CTL_CLRNCE:
  8.         break;
  9.     //对于nCE位的操作都不予理睬
  10.     
  11.     case NAND_CTL_SETCLE:
  12.         this->IO_ADDR_W = (void __iomem *) EMI_NAND_COM_RAW;
  13.         break;
  14.     //IO_ADDR_W是nand flash的数据寄存器地址。是_iomem类型变量(这是个空的宏定义,
  15.     //但这样可以让人很容易知道这是个寄存器变量。),这里的作用是将EMI_NAND_COM_RAW即nand flash
  16.     //内存的地址赋值给IO_ADDR_W,这样后面的操作,在使用IO_ADDR_W时就是使用EMI_NAND_COM_RAW。
  17.     
  18.     case NAND_CTL_SETALE:
  19.         this->IO_ADDR_W = (void __iomem *) EMI_NAND_ADDR1_RAW;
  20.         break;
  21.     
  22.     case NAND_READ_ID:
  23.         this->IO_ADDR_R = (void __iomem *) EMI_NAND_ID_RAW;
  24.         break;
  25.     
  26.     case NAND_READ_STATUS:
  27.         this->IO_ADDR_R = (void __iomem *) EMI_NAND_STA_RAW;
  28.         break;
  29.         
  30.         /* NAND_CTL_CLRCLE: */
  31.         /* NAND_CTL_CLRALE: */
  32.     default:
  33.         this->IO_ADDR_W = (void __iomem *) EMI_NAND_DATA_RAW;
  34.         this->IO_ADDR_R = (void __iomem *) EMI_NAND_DATA_RAW;
  35.         //在一些命令使能和地址使能后,将IO_ADDR_W还原成EMI_NAND_DATA_RAW nand flash数据寄存器地址
  36.         break;
  37.     }
  38. }
this->read_buf(mtd, data_poi, end);read_buf指向的函数为sep4020_nand_read_buf,

点击(此处)折叠或打开

  1. static void sep4020_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
  2. {
  3.     int i;
  4.     struct nand_chip *this = mtd->priv;
  5.     //配置DMAC用于nand的传输
  6.         DMAC_C0CONTROL = ((2112>>2)<<14) + (1<<13) + (2<<9) +(2<<6) + (3<<3) + 3;
  7.         DMAC_C0SRCADDR = EMI_NAND_DATA_RAW ;
  8.          DMAC_C0DESTADD = vaddr;                    //vaddr在board_nand_init函数中,使用malloc分配的一块2112大的内存空间
  9.          DMAC_C0CONFIGURATION = 0x31d ;
  10.          EMI_NAND_COM        = 0xc0003000;    //nand命令控制器,00 30读命令,且最高位使能nand控制器,开始读数据。
  11.     while(1)
  12.         {
  13.             if ((EMI_NAND_IDLE & 0x01) != 0)
  14.             break;
  15.         }
  16.     if(len == 2048 || len == 2112)    //如果要读取的长度是1页或包含oob的1页。则从vaddr开始复制len长度的数据
  17.     {
  18.         memcpy(buf,vaddr,len);    
  19.     }
  20.     else if(len == 64)    //如果读取的长度是64,则是要只读取oob区域,则从vaddr+2048地址处开始复制。
  21.     {    
  22.         memcpy(buf,vaddr+2048,len);    
  23.     }
  24. }
这个函数使能了nand flash控制器,将nandflash中对于的一页数据读出,并将适当的数据复制给了参数传来的buf。

nand read命令大致就是这样一个流程。本来想只是写写uboot关于nand的处理,和这个sep4020 nand控制器的特点。没想到这个版本的uboot就是nand驱动和内核的差不多,代码量太多。可能也是自己不熟悉这块,陆陆续续写了几天,感觉写的效率很低,写的想吐。于是草草结尾。之后看看其他版本的uboot的nand相关,不知道还是不是这样了。


上一篇:uboot之nand flash相关(2)
下一篇:裸机实验之存储控制器