Chinaunix首页 | 论坛 | 博客
  • 博客访问: 444450
  • 博文数量: 72
  • 博客积分: 3186
  • 博客等级: 中校
  • 技术积分: 1039
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-07 16:53
文章分类

全部博文(72)

文章存档

2012年(1)

2011年(5)

2010年(10)

2009年(56)

我的朋友

分类: 嵌入式

2009-10-20 14:28:57

对于nand的操作,mtd这一层有好些个参数函数,分别对应不同的nand操作。这些操作函数在之后非常有用。比如在我们对vivi进行bon分区时,用vivi来烧写kernel和fs时,都需要用到这些接口来将数据写入到nand中。下面继续看它的一些函数。

1. 前面已经看过对nand的一个操作:nand_read_ll。这里对mtd重写了一个对nand的读操作,流程上与前面那个是相似的,只不过是处理的内容更多了,程序兼容性更强一些。

/*
 * NAND read

 * 参数: mtd : mtd_info结构

 * from : 读取地址(从nand中的这个位置开始读)

 * len : 读取长度

 * retlen : 记录读了多少个字节的数据

 * buf : 读出数据到buf中

 * 返回值 : 0 读成功 。retlen

 */
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;

    /* 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 根据state来选择读哪个半页。程序在对于页半及起始地址上的判断便利程序的适用性更强。而不必从页始开始读。*/
        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 */
        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;
//这块芯片为8位宽,因而每页宽需为512列。若16位宽的芯片,则只需256列,则它就不用分半页了
        /* 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;    
}




/*
 * NAND read with ECC
 */

static int
nand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
              u_char *buf, u_char *ecc_code)
{
    int j, offset, page;
    struct nand_chip *this = mtd->priv;
    int ret;

    /* Do not allow reads past end of device */
    if ((from + len) > mtd->size) {
        *retlen = 0;
        return -EINVAL;
    }

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

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

    { /* mtd->oobblock == 512 */
        size_t last, next, len2;

        last = from + len;
        for (next = from; from < last; ) {
            page = from >> this->page_shift;
            offset = from & (mtd->oobblock - 1);
            len2 = mtd->oobblock - offset;
            next += len2;
            nand_command(mtd, NAND_CMD_READ0, 0x00, page);

            this->wait_for_ready();

            ret = smc_read_ecc_512(mtd, ecc_code);
            if (ret == -1)
                goto nand_read_ecc_err;

            if (next >= last)
                if ((last & (mtd->oobblock - 1)) != 0)
                    len2 = (last & (mtd->oobblock - 1)) - offset;

            for (j = 0; j < len2; j++)
                buf[(*retlen) + j] = this->data_buf[offset + j];
            *retlen += len2;
            from = next;
        }
    }

    ret = 0;

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

    return ret;

nand_read_ecc_err:
    DEBUG(MTD_DEBUG_LEVEL0,
        __FUNCTION__"(): Failed ECC read, page 0x%08lx\n", page);
    return -EIO;
}


阅读(917) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~