zoukankan      html  css  js  c++  java
  • u-boot_2010.6 nandflash驱动彻底分析

        2017年11月13日15:37:34   

        最近公司大裁员,闹的人心惶惶,不管怎么样,武装好自己才是硬道理,坚持学习,学会那些还没学会的。

        今天虚拟机突然打不开了,吓了我一跳,因为代码都还没备份,一定得养成备份代码的习惯!

        好了,下面开始进入正题吧,nandflash驱动彻底分析

        底层驱动移植完后,执行nand 命令:nand read 0x30000000 0 0x2000

        nand read 的命令格式是 nand read addr off | partition size ,总结起来就是 读到哪去? 从哪读? 读多大?

        返回的结果是:

            NAND read: device 0 offset 0x0, size 0x2000

            file is nand_util.c,fun is nand_read_skip_bad,line is 599,NAND read from offset 2000 failed -74

            0 bytes read: ERROR

        读失败,给出读失败错误码 -74

        要分析失败原因,先分析函数执行流程

        执行nand read 命令后,其实是执行了nand_read_skip_bad(nand, off, &size,(u_char *)addr);

        跳过坏块读函数的参数简单明了,从哪读,读到哪去,读多少,以及一个公共句柄(包含nand的信息,例如有多少个块,块大小等)

     1 int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
     2                u_char *buffer)
     3 {
     4     int rval;
     5     size_t left_to_read = *length;
     6     size_t len_incl_bad;
     7     u_char *p_buffer = buffer;
     8 
     9     len_incl_bad = get_len_incl_bad (nand, offset, *length); /* 函数分析见2017.11.14笔记(往下翻) */
    10 
    11     if ((offset + len_incl_bad) > nand->size) {
    12         printf ("Attempt to read outside the flash area
    ");
    13         return -EINVAL;
    14     }
    15 
    16     if (len_incl_bad == *length) {
    17         rval = nand_read (nand, offset, length, buffer);
    18         if (!rval || rval == -EUCLEAN)
    19             return 0;
    20         printf ("NAND read from offset %llx failed %d
    ",
    21             offset, rval);
    22         return rval;
    23     }
    24 
    25     while (left_to_read > 0) {
    26         size_t block_offset = offset & (nand->erasesize - 1);
    27         size_t read_length;
    28 
    29         WATCHDOG_RESET ();
    30 
    31         if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
    32             printf ("Skipping bad block 0x%08llx
    ",
    33                 offset & ~(nand->erasesize - 1));
    34             offset += nand->erasesize - block_offset;
    35             continue;
    36         }
    37 
    38         if (left_to_read < (nand->erasesize - block_offset))
    39             read_length = left_to_read;
    40         else
    41             read_length = nand->erasesize - block_offset;
    42         /* read_length 最大不会超过 nand->erasesize (128K) */
    43         /* nand_read 函数是一个按块读函数 */
    44         rval = nand_read (nand, offset, &read_length, p_buffer);
    45         if (rval && rval != -EUCLEAN) {
    46             printf ("NAND read from offset %llx failed %d
    ",
    47                 offset, rval);
    48             *length -= left_to_read;
    49             return rval;
    50         }
    51 
    52         left_to_read -= read_length;
    53         offset       += read_length;
    54         p_buffer     += read_length;
    55     }
    56 
    57     return 0;
    58 }

         先看get_len_incl_bad

     1 static size_t get_len_incl_bad (nand_info_t *nand, loff_t offset,
     2                 const size_t length)
     3 {
     4     size_t len_incl_bad = 0;
     5     size_t len_excl_bad = 0;
     6     size_t block_len;
     7 
     8     while (len_excl_bad < length) {
     9         block_len = nand->erasesize - (offset & (nand->erasesize - 1));
    10 
    11         if (!nand_block_isbad (nand, offset & ~(nand->erasesize - 1)))
    12             len_excl_bad += block_len;
    13 
    14         len_incl_bad += block_len;
    15         offset       += block_len;
    16 
    17         if (offset >= nand->size)
    18             break;
    19     }
    20 
    21     return len_incl_bad;
    22 }

        想要看懂这个函数,先要理解offset的构成

       

        2017年11月14日10:00:40

        每天进步一点点

        nand->erasesize = 128K = 0x20000

        nand->erasesize - 1 = 0x1FFFF

        offset & (nand->erasesize - 1) 保留低17位,清高位,因为nand的特性,所以是按块来统计长度

        block_len = nand->erasesize - offset & (nand->erasesize - 1) 作用是统计当前块要读的长度

        然后判断当前块是不是坏块,如果不是坏块,则让len_excl_bad += block_len,判断是否循环的标准是len_excl_bad < length

        不论当前块是不是坏块,都让len_incl_bad += block_len,len_incl_bad 得到的是包含坏块的长度

        总结get_len_incl_bad函数的作用为:

        ①如果偏移offset是从整块开始的,返回的结果是块的整数倍(不用看length,比如length是0x20001,则读两块)

        ②如果偏移offset不是从整块开始的,返回的结果是块的整数倍+第一块要读的长度

        总之,返回的结果是按块补齐的

        再继续分析nand_read_skip_bad

    1 if (len_incl_bad == *length) {
    2         rval = nand_read (nand, offset, length, buffer);
    3         if (!rval || rval == -EUCLEAN)
    4             return 0;
    5         
    6         return rval;
    7     }

        什么情况下,len_incl_bad == *length ?
        要读的内容里,没有坏块,且长度最后按块对齐(例如,从0x400开始读,读的长度为0x40000-0x400)

        如果不满足,则进入while循环读,在while里,按块读,先判断当前块是不是坏块,如果是则跳过,不是则读

        下面分析nand_read函数

       

     1 /* 1.从哪读 2.读多少 3.读到哪去 */
     2 static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
     3              size_t *retlen, uint8_t *buf)
     4 {
     5     struct nand_chip *chip = mtd->priv;
     6     int ret;
     7 
     8     /* Do not allow reads past end of device */
     9     if ((from + len) > mtd->size)
    10         return -EINVAL;
    11     if (!len)
    12         return 0;
    13     /* 选中芯片 */
    14     nand_get_device(chip, mtd, FL_READING);
    15 
    16     chip->ops.len = len;
    17     chip->ops.datbuf = buf;
    18     chip->ops.oobbuf = NULL;
    19 
    20     ret = nand_do_read_ops(mtd, from, &chip->ops);
    21 
    22     *retlen = chip->ops.retlen;
    23     /* 取消选中芯片 */
    24     nand_release_device(mtd);
    25 
    26     return ret;
    27 }

        其实真正的读函数是nand_do_read_ops,从哪去,读多少,读到哪去都被装载在chip->ops结构体中

        再看nand_do_read_ops函数

      1 /**
      2  * nand_do_read_ops - [Internal] Read data with ECC
      3  *
      4  * @mtd:    MTD device structure
      5  * @from:    offset to read from
      6  * @ops:    oob ops structure
      7  *
      8  * Internal function. Called with chip held.
      9  */
     10 static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
     11                 struct mtd_oob_ops *ops)
     12 {
     13     int chipnr, page, realpage, col, bytes, aligned;
     14     struct nand_chip *chip = mtd->priv;
     15     struct mtd_ecc_stats stats;
     16     int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
     17     int sndcmd = 1;
     18     int ret = 0;
     19     uint32_t readlen = ops->len;//128K 0x20000 根据输入的长度决定
     20     uint32_t oobreadlen = ops->ooblen; // oobreadlen = 0
     21     uint8_t *bufpoi, *oob, *buf;
     22 
     23     stats = mtd->ecc_stats;
     24 
     25     chipnr = (int)(from >> chip->chip_shift);
     26     chip->select_chip(mtd, chipnr);
     27     /* realpage 总页地址(高17位)  */
     28     realpage = (int)(from >> chip->page_shift);//pageshift is 11 
     29     page = realpage & chip->pagemask;//pagemask = 1ffff
     30     /* col 低11位 */
     31     col = (int)(from & (mtd->writesize - 1));//writesize = 2048, 2047 = 0x7ff
     32     /* 得到页地址和列地址 */
     33     buf = ops->datbuf;
     34     oob = ops->oobbuf;
     35 
     36     while(1) {
     37         bytes = min(mtd->writesize - col, readlen); //128K 0x20000 根据输入的长度决定
     38         aligned = (bytes == mtd->writesize);        //aligned = 1;
     39 
     40         /* Is the current page in the buffer ? */
     41         if (realpage != chip->pagebuf || oob) {
     42             bufpoi = aligned ? buf : chip->buffers->databuf;
     43             /* 对齐与不对齐读到的缓冲是不一样的 */
     44             if (likely(sndcmd)) {
     45                 chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
     46                 sndcmd = 0;
     47             }
     48 
     49             /* Now read the page into the buffer */
     50             if (unlikely(ops->mode == MTD_OOB_RAW))
     51                 ret = chip->ecc.read_page_raw(mtd, chip,//nand_read_page_raw
     52                         bufpoi, page);//从哪里读,读到哪里去,读多少
     53             else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
     54                 ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);
     55                 /* nand_read_subpage */
     56             else
     57                 ret = chip->ecc.read_page(mtd, chip, bufpoi,//整页读 bufpoi = buf
     58                         page);  //nand_read_page_swecc
     59             if (ret < 0)
     60                 break;
     61 
     62             /* Transfer not aligned data */
     63             if (!aligned) {
     64                 if (!NAND_SUBPAGE_READ(chip) && !oob)
     65                     chip->pagebuf = realpage;
     66                 memcpy(buf, chip->buffers->databuf + col, bytes);
     67             }
     68 
     69             buf += bytes;
     70 
     71             if (unlikely(oob)) {
     72                 /* Raw mode does data:oob:data:oob */
     73                 if (ops->mode != MTD_OOB_RAW) {
     74                     int toread = min(oobreadlen,
     75                         chip->ecc.layout->oobavail);
     76                     if (toread) {
     77                         oob = nand_transfer_oob(chip,
     78                             oob, ops, toread);
     79                         oobreadlen -= toread;
     80                     }
     81                 } else
     82                     buf = nand_transfer_oob(chip,
     83                         buf, ops, mtd->oobsize);
     84             }
     85 
     86             if (!(chip->options & NAND_NO_READRDY)) {
     87                 /*
     88                  * Apply delay or wait for ready/busy pin. Do
     89                  * this before the AUTOINCR check, so no
     90                  * problems arise if a chip which does auto
     91                  * increment is marked as NOAUTOINCR by the
     92                  * board driver.
     93                  */
     94                 if (!chip->dev_ready)
     95                     udelay(chip->chip_delay);
     96                 else
     97                     nand_wait_ready(mtd);
     98             }
     99         } else {
    100             memcpy(buf, chip->buffers->databuf + col, bytes);
    101             buf += bytes;
    102         }
    103 
    104         readlen -= bytes;
    105 
    106         if (!readlen)
    107             break;
    108 
    109         /* For subsequent reads align to page boundary. */
    110         col = 0;
    111         /* Increment page address */
    112         realpage++;
    113 
    114         page = realpage & chip->pagemask;
    115         /* Check, if we cross a chip boundary */
    116         if (!page) {
    117             chipnr++;
    118             chip->select_chip(mtd, -1);
    119             chip->select_chip(mtd, chipnr);
    120         }
    121 
    122         /* Check, if the chip supports auto page increment
    123          * or if we have hit a block boundary.
    124          */
    125         if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
    126             sndcmd = 1;
    127     }
    128 
    129     ops->retlen = ops->len - (size_t) readlen;
    130     if (oob)
    131         ops->oobretlen = ops->ooblen - oobreadlen;
    132 
    133     if (ret)
    134         return ret;
    135 
    136     if (mtd->ecc_stats.failed - stats.failed)
    137         return -EBADMSG;
    138 
    139     return  mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
    140 }


       

  • 相关阅读:
    Python的递归深度问题
    Python之多进程
    Python之多线程
    Git的基本操作
    ref与out区别
    Numpy基本操作
    面向对象中有哪些双下线方法及应用场景
    上下文管理
    Local与LocalStack
    基于列表实现栈
  • 原文地址:https://www.cnblogs.com/zhang2318/p/7552128.html
Copyright © 2011-2022 走看看