uboot之nand flash相关(3)

来源:互联网 发布:中国环境监测实时数据 编辑:程序博客网 时间:2024/06/05 04:09
然后我们分析一下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. /* Checkfor 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 lockand see if the deviceis 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. /* Deselectand 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 incase 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 isnot 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, checkfor bad block(s). Stayin the
  46. * loop to be sure if the offset changes because of
  47. * a bad block, that thenext block that will be
  48. * written tois also checked. Thus avoiding errorsif
  49. * the block(s) after the skipped block(s)is also bad
  50. * (number of blocks dependingon 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 blocksin an erase blockfor
  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 datato 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. //上面是读页有效数据(2048),这里读oob数据。
  98. if (opts->readoob){
  99. result = meminfo->read_oob(meminfo,
  100. mtdoffset,
  101. meminfo->oobsize,
  102. &readlen,
  103. (unsigned char *)
  104. &oob_buf);

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


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

  113. memcpy(buffer, oob_buf, readlen);

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

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

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

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

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

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

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

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

点击(此处)折叠或打开

  1. static int nand_read (struct mtd_info *mtd, loff_t from, size_tlen, 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: offsetto read from
  5. * @len: number of bytesto read
  6. * @retlen: pointerto variable to store the number of read bytes
  7. * @buf: the databufferto 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_tlen,
  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 lockand see if the deviceis 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 readis not page aligned, we haveto 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, Readin 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: Readin 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 needto 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 calcerror correction directly, it checks the hw
  126. * generator for anerror, reads back the syndromeand
  127. * does the error correctionon 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 delayor 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. /* Deselectand 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 == desiredlen 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. if (command== NAND_CMD_STATUS)
  37. {
  38. EMI_NAND_COM |= 0x80000000;//使能EMI_NAND_COM
  39. this->hwcontrol(mtd, NAND_READ_STATUS);
  40. }

  41. if (command== NAND_CMD_RESET)
  42. {
  43. EMI_NAND_COM |= 0x80000000;
  44. this->hwcontrol(mtd, NAND_CTL_CLRALE);
  45. }
  46. /* Set ALE and clear CLE to start address cycle */

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

  54. }
分析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. switch (cmd){
  5. case NAND_CTL_SETNCE:
  6. case NAND_CTL_CLRNCE:
  7. break;
  8. //对于nCE位的操作都不予理睬

  9. case NAND_CTL_SETCLE:
  10. this->IO_ADDR_W= (void __iomem*) EMI_NAND_COM_RAW;
  11. break;
  12. //IO_ADDR_W是nand flash的数据寄存器地址。是_iomem类型变量(这是个空的宏定义,
  13. //但这样可以让人很容易知道这是个寄存器变量。),这里的作用是将EMI_NAND_COM_RAW即nand flash
  14. //内存的地址赋值给IO_ADDR_W,这样后面的操作,在使用IO_ADDR_W时就是使用EMI_NAND_COM_RAW。

  15. case NAND_CTL_SETALE:
  16. this->IO_ADDR_W= (void __iomem*) EMI_NAND_ADDR1_RAW;
  17. break;

  18. case NAND_READ_ID:
  19. this->IO_ADDR_R= (void __iomem*) EMI_NAND_ID_RAW;
  20. break;

  21. case NAND_READ_STATUS:
  22. this->IO_ADDR_R= (void __iomem*) EMI_NAND_STA_RAW;
  23. break;

  24. /* NAND_CTL_CLRCLE:*/
  25. /* NAND_CTL_CLRALE:*/
  26. default:
  27. this->IO_ADDR_W= (void __iomem*) EMI_NAND_DATA_RAW;
  28. this->IO_ADDR_R= (void __iomem*) EMI_NAND_DATA_RAW;
  29. //在一些命令使能和地址使能后,将IO_ADDR_W还原成EMI_NAND_DATA_RAW nand flash数据寄存器地址
  30. break;
  31. }
  32. }
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相关,不知道还是不是这样了。
原创粉丝点击