Chinaunix首页 | 论坛 | 博客
  • 博客访问: 852679
  • 博文数量: 190
  • 博客积分: 7021
  • 博客等级: 少将
  • 技术积分: 1752
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-17 19:26
文章分类

全部博文(190)

文章存档

2014年(9)

2011年(32)

2010年(149)

我的朋友

分类: LINUX

2010-05-18 14:43:23

第二个函数:
static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
在分析这个函数之前,我们先来看看nand flash中读操作的流程图

一、NAND flash的物理组成
NAND Flash 的数据是以bit的方式保存在memory cell,一般来说,一个cell 中只能存储一个bit。这些cell 以8个或者16个为单位,连成bit line,形成所谓的byte(x8)/word(x16),这就是NAND Device的位宽。这些Line会再组成Page,(NAND Flash 有多种结构,我使用的NAND Flash 是K9F1208,下面内容针对三星的K9F1208U0M),每页528Bytes(512byte(Main Area)+16byte(Spare Area)),每32个page形成一个Block(32*528B)。具体一片flash上有多少个Block视需要所定。我所使用的三星 k9f1208U0M具有4096个block,故总容量为4096*(32*528B)=66MB,但是其中的2MB是用来保存ECC校验码等额外数据 的,故实际中可使用的为64MB。
NAND flash以页为单位读写数据,而以块为单位擦除数据。按照这样的组织方式可以形成所谓的三类地址:
Column Address:Starting Address of the Register. 翻成中文为列地址,地址的低8位
Page Address :页地址
Block Address :块地址
对于NAND Flash来讲,地址和命令只能在I/O[7:0]上传递,数据宽度是8位。

二、NAND Flash地址的表示
512byte 需要9bit来表示,对于528byte系列的NAND,这512byte被分成1st half Page Register和2nd half Page Register,各自的访问由地址指针命令来选择,A[7:0]就是所谓的column address(列地址),在进行擦除操作时不需要它,why?因为以块为单位擦除。32个page需要5bit来表示,占用A[13:9],即该 page在块内的相对地址。A8这一位地址被用来设置512byte的1st half page还是2nd half page,0表示1st,1表示2nd。Block的地址是由A14以上的bit来表示。
例如64MB(512Mb)的NAND flash(实际中由于存在spare area,故都大于这个值),共4096block,因此,需要12个bit来表示,即A[25:14],如果是128MB(1Gbit) 的528byte/page的NAND Flash,则block address用A[26:14]表示。而page address就是blcok address|page address in block NAND Flash 的地址表示为:
Block Address|Page Address in block|halfpage pointer|Column Address 地址传送顺序是Column Address,Page Address,Block Address
由于地址只能在I/O[7:0]上传递,因此,必须采用移位的方式进行。 例如,对于512Mbit x8的NAND flash,地址范围是0~0x3FF_FFFF,只要是这个范围内的数值表示的地址都是有效的。 以NAND_ADDR 为例:
第1 步是传递column address,就是NAND_ADDR[7:0],不需移位即可传递到I/O[7:0]上,而halfpage pointer即A8 是由操作指令决定的,即指令决定在哪个halfpage 上进行读
写,而真正的A8 的值是不需程序员关心的。
第2 步就是将NAND_ADDR 右移9位,将NAND_ADDR[16:9]传到I/O[7:0]上;
第3 步将NAND_ADDR[24:17]放到I/O上;
第4步需要将NAND_ADDR[25]放到I/O上;
因此,整个地址传递过程需要4 步才能完成,即4-step addressing。 如果NAND Flash 的容量是32MB(256Mbit)以下,那么,block adress最高位只到bit24,因此寻址只需要3步。
下 面,就x16 的NAND flash 器件稍微进行一下说明。 由于一个page 的main area 的容量为256word,仍相当于512byte。但是,这个时候没有所谓的1st halfpage 和2nd halfpage 之分了,所以,bit8就变得没有意义了,也就是这个时候 A8 完全不用管,地址传递仍然和x8 器件相同。除了,这一点之外,x16 的NAND使用方法和 x8 的使用方法完全相同。

在了解了上面这些基本知识以后,我们来看nand_read()的源代码


/*
 * NAND read
 */

static int
nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
    int j, col, page, state;
    struct nand_chip *this = mtd->priv;

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

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

    /* First we calculate the starting page */
    page = from >> this->page_shift;
//获取低8位的地址,也就是列地址
    /* Get raw starting column */
    col = from & (mtd->oobblock - 1);

    /* State machine for devices having pages larger than 256 bytes */
    state = (col < mtd->eccsize) ? 0 : 1;

    /* Calculate column address within ECC block context */
    col = (col >= mtd->eccsize) ? (col - mtd->eccsize) : col;

    /* Initialize return value */
    *retlen = 0;

    /* Select the NAND device */
    nand_select();

    /* Loop until all data read */
    while (*retlen < len) {
        /* Send the read command */
        if (!state)
            nand_command(mtd, NAND_CMD_READ0, col, page);
        else

            nand_command(mtd, NAND_CMD_READ1, col, page);


        this->wait_for_ready();

        /* Read the data directly into the return buffer */

       //假设从第一半页的23开始读取,一共读取49个
        if ((*retlen + (mtd->eccsize - col)) >= len) {
            while (*retlen < len)
                buf[(*retlen)++] = this->read_data();
            /* We're done */
            continue;
        } else {
            for (j = col; j < mtd->eccsize; j++)
                buf[(*retlen)++] = this->read_data();
        }

        /*
         * If the amount of data to be read is greater than
         * (256 - col), then all subsequent reads will take
         * place on page or half-page (in the case of 512 byte
         * page devices) aligned boundaries and the column
         * address will be zero. Setting the column address to
         * zero after the first read allows us to simplify
         * the reading of data and the if/else statements above.
         */

        if (col)
            col = 0x00;

        /* Increment page address */
        if ((mtd->oobblock == 256) || state)
            page++;

        /* Toggle state machine */
        if (mtd->oobblock == 512)
            state = state ? 0 : 1;
    }

    /* De-select the NAND device */
    nand_deselect();

    /* Return happy */
    return 0;
}

分析以上代码也就是首先得到页的位置,然后的到低8为的地址,然后我们要判断低八位的地址的大小,从而决定是读取第一半页(first halfpage)还是第二半页(second halfpage),因为读取的位置不一定,输入的命令就不一样,因此定义一个变量为state,当state = 1时,读取的是第二个半页,反之则是第一个半页。
在这个时候,问题就出来了,按我的理解就是nand flash不是一页一页的读取的,而是半页半页的读取,这个问题暂时还没解决!请知道告诉我,不胜感激!chenming8605@gmail.com



看完了nand flash的读取,现在我们来看看nand flash的写入:
static int nand_write(struct mtd_info, loff_t to, size_t len, size_t *retlen, const u_char *buf);
看函数原型之前我们同样来看看nand flash的写数据的流程图,如下:
从上面的流程图可以看出:开始-->写命令(0x80)-->写地址-->写数据-->写命令(0x10)-->读寄存器的状态(i/o=1?等于1则表示设备可用,!=1则表示设备正忙)-->(Register bit[0]成功位,0表示成功,1表示失败)--->检查数据的正确性(这里其实就是将写入的数据读出,然后于原始的数据对比,如果不相同则表示写失败)
现在我们再来看源代码,就会很清晰:

/*
 * NAND write
 */

static int
nand_write(struct mtd_info *mtd, loff_t to, size_t len,
           size_t *retlen, const u_char *buf)
{
    int i, page, col, cnt, status;
    struct nand_chip *this = mtd->priv;

    DEBUG(MTD_DEBUG_LEVEL3,
        __FUNCTION__"(): to = 0x%08x, len = %i\n", (unsigned int)to,
        (int) len);
    //不可越界
    /* Do not allow write past end of page */
    if ((to + len) > mtd->size) {
        DEBUG(MTD_DEBUG_LEVEL0,
            __FUNCTION__"(): Attempted write past end of device\n");
        return -EINVAL;
    }
    //找到所写的页的位置
    /* Shift to get page */
    page = ((int)to) >> this->page_shift;
    //获取低八位的值,(页中的地址)
    /* Get the starting column */
    col = to & (mtd->oobblock - 1);

    /* Initialize return length value */
    *retlen = 0;

    /* Select the NAND device */
    nand_select();
    //读取寄存器的状态,查看该地址是不是被保护
    /* Check the WP bit */
    nand_command(mtd, NAND_CMD_STATUS, -1, -1);

    this->wait_for_ready();
    //如果被保护,则直接跳转到nand_write_exit()函数
    if (!(this->read_data() & SMC_STAT_NOT_WP)) {
        DEBUG(MTD_DEBUG_LEVEL0,
            __FUNCTION__"(): Device is write protected!!!\n");
        i = -EPERM;
        goto nand_write_exit;
    }

    /* Loop until all data is written */
    while (*retlen < len) {
        /* Write data into buffer */

        //将数据写入到this->data_buf中,并按相应位保存,因为我们采用的是512字节的         // 页面,因此数据不可超过512。


        /****************************************************

        *首先将我们需要写入的数据放在struct nand_chip结构的数据还存取中

        *然后将不需要的数据全部写入0xff,在完成了数据的第一步缓存以后将数据依次写

        *nand flash中的区域

        /******************************************************
        if ((col + len) >= mtd->oobblock)
            for (i = col, cnt = 0; i < mtd->oobblock; i++, cnt++)
                this->data_buf[i] = buf[(*retlen + cnt)];
        else
            for (i = col, cnt = 0; cnt < (len - *retlen); i++, cnt++)
                this->data_buf[i] = buf[(*retlen + cnt)];
        /* Write ones for partial page programming */
        for (i = mtd->oobblock; i < (mtd->oobblock + mtd->oobsize); i++)
            this->data_buf[i] = 0xff;

        /* Write pre-padding bytes into buffer */
        for (i = 0; i < col; i++)
            this->data_buf[i] = 0xff;

        /* Write post-padding bytes into buffer */
        if ((col + (len - *retlen)) < mtd->oobblock) {
            for (i = (col + cnt); i < mtd->oobblock; i++)
                this->data_buf[i] = 0xff;
        }

        /* Send command to begin auto page programming */
        nand_command(mtd, NAND_CMD_SEQIN, 0x00, page);

        /* Write out complete page of data */
        this->hwcontrol(NAND_CTL_DAT_OUT);   //无效
        for (i = 0; i < (mtd->oobblock + mtd->oobsize); i++)
            this->write_data(this->data_buf[i]);
        this->hwcontrol(NAND_CTL_DAT_IN);   //无效

        /* Send command to actually program the data */

        //写入命令0x10,参照上面的流程图,也就是第5步
        nand_command(mtd, NAND_CMD_PAGEPROG, -1, -1);

        this->wait_for_ready();

        /*
         * Wait for program operation to complete. This could
         * take up to 3000us (3ms) on some devices. so we try
         * and exit as quickly as possible.

         * 上面的解释很清楚,其实就是一个延时,等待写完成
         */

        status = 0;
        for (i = 0; i < 24; i++) {
            /* Delay for 125us */
            udelay(125);

            /* Check for status */
            nand_command(mtd, NAND_CMD_STATUS, -1, -1);
            status = (int)this->read_data();
            if (status & SMC_STAT_READY)
                break;
        }

        /* See if device thinks it succeeded */

        //检查状态寄存器的bit[0]1则说明有错误,0为成功
        if (status & SMC_STAT_WRITE_ERR) {
            DEBUG(MTD_DEBUG_LEVEL0,
                __FUNCTION__"(): Failed write, page 0x%08x, " \
                "%6i bytes were sucesful\n", page, *retlen);
            i = -EIO;
            goto nand_write_exit;
        }

#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
        /*
         * The NAND device assumes that it is always writing to
         * a cleanly erased page. Hence, it performs its internal
         * write verification only on bits that transitioned from
         * 1 to 0. The device does NOT verify the whole page on
         * a byte by byte basis. It is possible that the page was
         * not completely erased or the page is becoming unusable
         * due to wear. The read with ECC would catch the error
         * later when the ECC page check fails, but we would rather
         * catch it early in the page write stage. Better to write
         * no data than invalid data.
         */


        /* Send command to read back the page */
        if (col < mtd->eccsize)
            nand_command(mtd, NAND_CMD_READ0, col, page);
        else
            nand_command(mtd, NAND_CMD_READ1, col - 256, page);

        this->wait_for_ready();

        /* Loop through and verify the data */
        for (i = col; i < cnt; i++) {
            if (this->data_buf[i] != this->read_data()) {
                DEBUG(MTD_DEBUG_LEVEL0,
                    __FUNCTION__"(): Failed write verify, " \
                    "page 0x%08x, %6i bytes were succesful\n",
                    page, *retlen);
                i = -EIO;
                goto nand_write_exit;
            }
        }
#endif

        /*
         * If we are writing a large amount of data and/or it
         * crosses page or half-page boundaries, we set the
         * the column to zero. It simplifies the program logic.
         */

        if (col)
            col = 0x00;

        /* Update written bytes count */
        *retlen += cnt;

        /* Increment page address */
        page++;

    }

    /* Return happy */
    *retlen = len;
    i = 0;

nand_write_exit:
    /* De-select the NAND device */
    nand_deselect();

    return i;
}

现在看这个代码是不是有豁然开朗的感觉,主要的对于nand flash对应的擦除,读和写函数已经分析完,今天写这些东西花了不下6个小时的时间,还有一些函数,明天继续分析!(敬请留意
阅读(1938) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~