您的位置:首页 > 其它

MTD原始设备与FLASH硬件驱动的对话-续

2014-05-08 08:12 330 查看
MTD原始设备与FLASH硬件驱动的对话-续

上一个贴由下到上的介绍了FLASH硬件驱动是如何与MTD原始设备建立联系的,现在再由上到下的研究一下是如何通过MTD原始设备来访问FLASH硬件驱动的。

首先分析一下如何通过MTD原始设备进而通过FLASH硬件驱动来读取FLASH存储器的数据。

引用自<<Linux系统移植>>一文:

"读Nand Flash:

当对nand flash的设备文件(nand flash在/dev下对应的文件)执行系统调用read(),或在某个文件系统中对该

设备进行读操作时. 会调用struct mtd_info中的read方法,他们缺省调用函数为nand_read(),在

drivers/mtd/nand/nand_base.c中定义.nand_read()调用nand_do_read_ecc(),执行读操作. 在

nand_do_read_ecc()函数中,主要完成如下几项工作:

1. 会调用在nand flash驱动中对struct nand_chip重载的select_chip方法,即

s3c2410_nand_select_chip()选择要操作的MTD芯片.

2. 会调用在struct nand_chip中系统缺省的方法cmdfunc发送读命令到nand flash.

3. 会调用在nand flash驱动中对struct nand_chip重载的read_buf(),即s3c2410_nand_read_buf()

从Nand Flash的控制器的数据寄存器中读出数据.

4. 如果有必要的话,会调用在nand flash驱动中对struct nand_chip重载的

enable_hwecc,correct_data以及calculate_ecc方法,进行数据ECC校验。"

下面研究一下其中的细节:

/**

* nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc

* @mtd: MTD device structure

* @from: offset to read from

* @len: number of bytes to read

* @retlen: pointer to variable to store the number of read bytes

* @buf: the databuffer to put data

*

* This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL

* and flags = 0xff

*/

static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)

{

return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, &mtd->oobinfo, 0xff);

}

注:

以参数oob_buf为NULL,flags为0xff调用nand_do_read_ecc函数。

/**

* nand_do_read_ecc - [MTD Interface] Read data with ECC

* @mtd: MTD device structure

* @from: offset to read from

* @len: number of bytes to read

* @retlen: pointer to variable to store the number of read bytes

* @buf: the databuffer to put data

* @oob_buf: filesystem supplied oob data buffer (can be NULL)

* @oobsel: oob selection structure

* @flags: flag to indicate if nand_get_device/nand_release_device should be preformed

* and how many corrected error bits are acceptable:

* bits 0..7 - number of tolerable errors

* bit 8 - 0 == do not get/release chip, 1 == get/release chip

*

* NAND read with ECC

*/

int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,

size_t * retlen, u_char * buf, u_char * oob_buf,

struct nand_oobinfo *oobsel, int flags)

{

int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;

int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;

struct nand_chip *this = mtd->priv;

u_char *data_poi, *oob_data = oob_buf;//目前oob_data指针为空,以后会去修改它。

u_char ecc_calc[32];//该数组用于存放计算出来的ecc结果

u_char ecc_code[32];//该数组用于存放oob中ecc部分的数据

int eccmode, eccsteps;//eccmode存放ecc的类型(ECC_SOFT);

eccsteps用于记录一个page所需的ecc校验次数(2)。

int *oob_config, datidx;

int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;

int eccbytes;

int compareecc = 1;//是否需要ecc标志(如果设置成ECC_NONE,这个标志将被清0)

int oobreadlen;

DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, 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, "nand_read_ecc: Attempt read beyond end of device\n");

*retlen = 0;

return -EINVAL;

}

/* Grab the lock and see if the device is available */

/* 获取自旋锁,等待设备可用并获取其控制权 */

if (flags & NAND_GET_DEVICE)

nand_get_device (this, mtd, FL_READING);

/* Autoplace of oob data ? Use the default placement scheme */

if (oobsel->useecc == MTD_NANDECC_AUTOPLACE)

oobsel = this->autooob;

/*

* 感觉这一步有点多余,因为nand_scan中已经调用了以下代码:

* memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));

* 把this->autooob的内容拷贝到mtd->oobinfo中了

*/

eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;

oob_config = oobsel->eccpos;//记录ecc在oob数据中的位置

/* Select the NAND device */

chipnr = (int)(from >> this->chip_shift);

this->select_chip(mtd, chipnr);//选择nand flash芯片(在s3c2410 nand flash控制器中为空操作)

/* First we calculate the starting page */

/* 首先,我们计算出开始页码 */

realpage = (int) (from >> this->page_shift);

page = realpage & this->pagemask;

/* Get raw starting column */

/* 其次,我们计算页内偏址 */

col = from & (mtd->oobblock - 1);

end = mtd->oobblock;//页大小(512)

ecc = this->eccsize;//ecc保护下的数据大小(256)

eccbytes = this->eccbytes;//ecc所占的字节数(3)

if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME))

compareecc = 0;//如果设置为关闭ECC或写操作才需要ECC,那把ecc给禁用(现在可是读操作^_^)

oobreadlen = mtd->oobsize;//16

if (this->options & NAND_HWECC_SYNDROME)

oobreadlen -= oobsel->eccbytes;

/* Loop until all data read */

while (read < len) {

int aligned = (!col && (len - read) >= end);

/*

* If the read is not page aligned, we have to read into data buffer

* due to ecc, else we read into return buffer direct

* 如果要读的位置不是页对齐都话,那么只要先把整页读出来,

* 取出所需要读取的数据,然后修改读位置,那么以后的读操作都是页对齐的了。

*/

if (aligned)

data_poi = &buf[read];

else

data_poi = this->data_buf;

/* Check, if we have this page in the buffer

*

* FIXME: Make it work when we must provide oob data too,

* check the usage of data_buf oob field

* 如果我们所需要的数据还存在于缓冲中都话:

* 1 如果读位置页对齐,我们只要把缓冲中的数据直接拷贝到data_poi(buf[read])中即可(因为数据存在与缓存中,所以也无需要考虑ecc问题)

* 2 如果读位置不是页对齐,什么读不要作,让其继续留在缓存(data_buf)中,以后会从data_poi(指向缓存data_buf)中提取所需要的数据。

*/

if (realpage == this->pagebuf && !oob_buf) {

/* aligned read ? */

if (aligned)

memcpy (data_poi, this->data_buf, end);

goto readdata;

}

/* Check, if we must send the read command */

/* 发送读命令,页地址为page,列地址为0x00 */

if (sndcmd) {

this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);

sndcmd = 0;

}

/* get oob area, if we have no oob buffer from fs-driver */

if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE ||

oobsel->useecc == MTD_NANDECC_AUTOPL_USR)

oob_data = &this->data_buf[end];//以上情况,oob_data暂存在data_buf缓存中

eccsteps = this->eccsteps;//2

switch (eccmode) {

case NAND_ECC_NONE: { /* No ECC, Read in a page */

static unsigned long lastwhinge = 0;

if ((lastwhinge / HZ) != (jiffies / HZ)) {

printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n");

lastwhinge = jiffies;

}

this->read_buf(mtd, data_poi, end);

break;

}

case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */

this->read_buf(mtd, data_poi, end);//读取数据到data_poi

for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc)

this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);

/* 计算出读取到data_poi的数据的ecc值,并存放到ecc_calc数组中。

* 因为读都数据有一页大小(512),需要分别对其上半部和下半部分计算一次ecc值,并分开存放到ecc_calc数组相应都位置中。

*/

break;

default:

for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) {

this->enable_hwecc(mtd, NAND_ECC_READ);

this->read_buf(mtd, &data_poi[datidx], ecc);

/* HW ecc with syndrome calculation must read the

* syndrome from flash immidiately after the data */

if (!compareecc) {

/* Some hw ecc generators need to know when the

* syndrome is read from flash */

this->enable_hwecc(mtd, NAND_ECC_READSYN);

this->read_buf(mtd, &oob_data[i], eccbytes);

/* We calc error correction directly, it checks the hw

* generator for an error, reads back the syndrome and

* does the error correction on the fly */

ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]);

if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {

DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: "

"Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);

ecc_failed++;

}

} else {

this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);

}

}

break;

}

/* read oobdata */

this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen);

//读取oob_data存放到oob_data[mtd->oobsize - oobreadlen],在这里是data_buf[end]中

/* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */

/* 跳过ecc检测 */

if (!compareecc)

goto readoob;

/* Pick the ECC bytes out of the oob data */

/* 从刚读出来都oob_data中取出ecc数据(在这里是前三个字节) */

for (j = 0; j < oobsel->eccbytes; j++)

ecc_code[j] = oob_data[oob_config[j]];

/* correct data, if neccecary */

for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) {

ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);

/* 拿前面计算出来都ecc_cal数组都数据与读出来的ecc数据作比较,并尝试修正错误(但不保证能修复,具体看返回值) */

/* Get next chunk of ecc bytes */

j += eccbytes;

/* Check, if we have a fs supplied oob-buffer,

* This is the legacy mode. Used by YAFFS1

* Should go away some day

*/

if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) {

int *p = (int *)(&oob_data[mtd->oobsize]);

p[i] = ecc_status;

}

/* 很不幸,ecc检测发现错误且未能修复,报告错误 */

if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {

DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);

ecc_failed++;

}

}

readoob:

/* check, if we have a fs supplied oob-buffer */

if (oob_buf) {

/* without autoplace. Legacy mode used by YAFFS1 */

switch(oobsel->useecc) {

case MTD_NANDECC_AUTOPLACE:

case MTD_NANDECC_AUTOPL_USR:

/* Walk through the autoplace chunks */

for (i = 0; oobsel->oobfree[i][1]; i++) {

int from = oobsel->oobfree[i][0];

int num = oobsel->oobfree[i][1];

memcpy(&oob_buf[oob], &oob_data[from], num);

oob += num;

}

break;

case MTD_NANDECC_PLACE:

/* YAFFS1 legacy mode */

oob_data += this->eccsteps * sizeof (int);

default:

oob_data += mtd->oobsize;

}

}

readdata:

/* Partial page read, transfer data into fs buffer

* 读位置不是页对齐,从data_poi(data_buf中)提取所需要都数据

*/

if (!aligned) {

for (j = col; j < end && read < len; j++)

buf[read++] = data_poi[j];//read自增

this->pagebuf = realpage;

} else

read += mtd->oobblock;//整页读取,计数值加上整页的数目(512)

/* Apply delay or wait for ready/busy pin

* Do this before the AUTOINCR check, so no problems

* arise if a chip which does auto increment

* is marked as NOAUTOINCR by the board driver.

*/

if (!this->dev_ready)

udelay (this->chip_delay);

else

nand_wait_ready(mtd);

if (read == len)//所需数据读完都情况,退出读循环。

break;

/* For subsequent reads align to page boundary. */

col = 0;//对于读位置不是页对齐都情况,前面已对其进行林相应都处理,现在读位置变得页对齐了。

/* Increment page address */

realpage++;//页地址加1,读取下一页。

page = realpage & this->pagemask;

/* Check, if we cross a chip boundary */

if (!page) {

chipnr++;

this->select_chip(mtd, -1);

this->select_chip(mtd, chipnr);

}

/* Check, if the chip supports auto page increment

* or if we have hit a block boundary.

* 如果芯片支持页自增操作,且未到block boundary(15)的话,不用再发送读命令

*/

if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))

sndcmd = 1;

}

/* Deselect and wake up anyone waiting on the device */

if (flags & NAND_GET_DEVICE)

nand_release_device(mtd);//放弃对设备都控制权,好让其它进程获取并占有它

/*

* Return success, if no ECC failures, else -EBADMSG

* fs driver will take care of that, because

* retlen == desired len and result == -EBADMSG

*/

*retlen = read;

return ecc_failed ? -EBADMSG : 0;

}

好的,接着研究一下如何通过MTD原始设备进而通过FLASH硬件驱动向FLASH存储器写数据。

引用自<<Linux系统移植>>一文:

写Nand Flash

当对nand flash的设备文件(nand flash在/dev下对应的文件)执行系统调用write(),或在某个文件系统中对该设备

进行读操作时, 会调用struct mtd_info中write方法,他们缺省调用函数为nand_write(),这两个函数在

drivers/mtd/nand/nand_base.c中定义. nand_write()调用nand_write_ecc(),执行写操作.在

nand_do_write_ecc()函数中,主要完成如下几项工作:

1. 会调用在nand flash驱动中对struct nand_chip重载的select_chip方法,即

s3c2410_nand_select_chip()选择要操作的MTD芯片.

2. 调用nand_write_page()写一个页.

3. 在nand_write_page()中,会调用在struct nand_chip中系统缺省的方法cmdfunc发送写命令

到nand flash.

4. 在nand_write_page()中,会调用在nand flash驱动中对struct nand_chip重载的

write_buf(),即s3c2410_nand_write_buf()从Nand Flash的控制器的数据寄存器中写入数据.

5. 在nand_write_page()中,会调用在nand flash驱动中对struct nand_chip重载waitfunc方法,

该方法调用系统缺省函数nand_wait(),该方法获取操作状态,并等待nand flash操作完成.等

待操作完成,是调用nand flash驱动中对struct nand_chip中重载的dev_ready方法,即

s3c2410_nand_devready()函数.

下面研究一下其中的细节:

/**

* nand_write - [MTD Interface] compability function for nand_write_ecc

* @mtd: MTD device structure

* @to: offset to write to

* @len: number of bytes to write

* @retlen: pointer to variable to store the number of written bytes

* @buf: the data to write

*

* This function simply calls nand_write_ecc with oob buffer and oobsel = NULL

*

*/

static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)

{

return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));

}

注:

以参数eccbuf、oobsel为NULL,调用nand_write_ecc函数。

/**

* nand_write_ecc - [MTD Interface] NAND write with ECC

* @mtd: MTD device structure

* @to: offset to write to

* @len: number of bytes to write

* @retlen: pointer to variable to store the number of written bytes

* @buf: the data to write

* @eccbuf: filesystem supplied oob data buffer

* @oobsel: oob selection structure

*

* NAND write with ECC

*/

static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,

size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)

{

int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr;

int autoplace = 0, numpages, totalpages;

struct nand_chip *this = mtd->priv;

u_char *oobbuf, *bufstart;

int ppblock = (1 << (this->phys_erase_shift - this->page_shift));//page/block

DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);

/* Initialize retlen, in case of early exit */

*retlen = 0;

/* Do not allow write past end of device */

/* 超越nand flash容量的写操作是不允许的 */

if ((to + len) > mtd->size) {

DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n");

return -EINVAL;

}

/* reject writes, which are not page aligned */

/* 不按页对齐的写操作同样是不允许的 */

if (NOTALIGNED (to) || NOTALIGNED(len)) {

printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");

return -EINVAL;

}

/* Grab the lock and see if the device is available */

/* 获取设备的控制权 */

nand_get_device (this, mtd, FL_WRITING);

/* Calculate chipnr */

/*

* 存在多片flash的情况下,计算出所要写的是哪片flash?

* (当然,像我的板,只用一片nand flash,所以这个操作是不必要的)

*/

chipnr = (int)(to >> this->chip_shift);

/* Select the NAND device */

/* 片选操作 */

this->select_chip(mtd, chipnr);

/* Check, if it is write protected */

/* 如果nand flash写保护,当然不能再写了 */

if (nand_check_wp(mtd))

goto out;

/* if oobsel is NULL, use chip defaults */

if (oobsel == NULL)

oobsel = &mtd->oobinfo;

/* Autoplace of oob data ? Use the default placement scheme */

if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {

oobsel = this->autooob;

autoplace = 1;

}

if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)

autoplace = 1;

/* Setup variables and oob buffer */

totalpages = len >> this->page_shift;//计算所要读取的数据长度共有多少页

page = (int) (to >> this->page_shift);//计算数据所要写到的开始页码

/* Invalidate the page cache, if we write to the cached page */

/* 如果缓存保存的数据在我们要写数据的范围内,把缓存里的数据设置为不可用???? */

if (page <= this->pagebuf && this->pagebuf < (page + totalpages))

this->pagebuf = -1;

/* Set it relative to chip */

page &= this->pagemask;

startpage = page;

/* Calc number of pages we can write in one go */

numpages = min (ppblock - (startpage & (ppblock - 1)), totalpages);//计算出本block中允许被写的页数

oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages);//先不深入研究~_~

bufstart = (u_char *)buf;//获取所要写数据的地址

/* Loop until all data is written */

/* 循环进行写操作 */

while (written < len) {

this->data_poi = (u_char*) &buf[written];//先把所要写的数据缓冲到data_poi下

/* Write one page. If this is the last page to write

* or the last page in this block, then use the

* real pageprogram command, else select cached programming

* if supported by the chip.

* 如果这是所写数据的最后一个页或许这是所写block的最后一个页,调用nand flash的

* pageprogram指令,真正把数据写入nand flash中(nand flash的最小擦除单元为block)

*/

ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0));

if (ret) {

DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret);

goto out;

}

/* Next oob page */

oob += mtd->oobsize;

/* Update written bytes count */

/* 更新写入计数值 */

written += mtd->oobblock;

if (written == len)//写入完毕,退出

goto cmp;

/* Increment page address */

page++;//下一页

/* Have we hit a block boundary ? Then we have to verify and

* if verify is ok, we have to setup the oob buffer for

* the next pages.

* 暂时不是很明白,需要先搞明白nand_prepare_oobbuf函数的作用

*/

if (!(page & (ppblock - 1))){

int ofs;

this->data_poi = bufstart;//怀疑nand_verify_pages用到

ret = nand_verify_pages (mtd, this, startpage,

page - startpage,

oobbuf, oobsel, chipnr, (eccbuf != NULL));//一页写完,检查数据

if (ret) {

DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);

goto out;

}

*retlen = written;

ofs = autoplace ? mtd->oobavail : mtd->oobsize;

if (eccbuf)

eccbuf += (page - startpage) * ofs;

totalpages -= page - startpage;//更新需要写的页数

numpages = min (totalpages, ppblock);//更新可以写的页数

page &= this->pagemask;//更新页码

startpage = page;//更新开始页码

oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel,

autoplace, numpages);

/* Check, if we cross a chip boundary */

if (!page) {

chipnr++;

this->select_chip(mtd, -1);

this->select_chip(mtd, chipnr);

}

}

}

/* Verify the remaining pages */

cmp:

this->data_poi = bufstart;//怀疑nand_verify_pages用到

ret = nand_verify_pages (mtd, this, startpage, totalpages,

oobbuf, oobsel, chipnr, (eccbuf != NULL));

if (!ret)

*retlen = written;

else

DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);

out:

/* Deselect and wake up anyone waiting on the device */

nand_release_device(mtd);//放弃对设备的控制权

return ret;

}

/**

* nand_write_page - [GENERIC] write one page

* @mtd: MTD device structure

* @this: NAND chip structure

* @page: startpage inside the chip, must be called with (page & this->pagemask)

* @oob_buf: out of band data buffer

* @oobsel: out of band selecttion structre

* @cached: 1 = enable cached programming if supported by chip

*

* Nand_page_program function is used for write and writev !

* This function will always program a full page of data

* If you call it with a non page aligned buffer, you're lost :)

*

* Cached programming is not supported yet.

*/

static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page,

u_char *oob_buf, struct nand_oobinfo *oobsel, int cached)

{

int i, status;

u_char ecc_code[32];

int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;

int *oob_config = oobsel->eccpos;

int datidx = 0, eccidx = 0, eccsteps = this->eccsteps;

int eccbytes = 0;

/* FIXME: Enable cached programming */

cached = 0;//在高版本的内核下找到这样的解释:

/*

* Cached progamming disabled for now, Not sure if its worth the

* trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)

*/

/* Send command to begin auto page programming */

/* 发送页编程指令 */

this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);

/* Write out complete page of data, take care of eccmode */

switch (eccmode) {

/* No ecc, write all */

case NAND_ECC_NONE:

printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");

this->write_buf(mtd, this->data_poi, mtd->oobblock);

break;

/* Software ecc 3/256, write all */

case NAND_ECC_SOFT:

for (; eccsteps; eccsteps--) {

this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);//计算出一页的ecc数据

for (i = 0; i < 3; i++, eccidx++)

oob_buf[oob_config[eccidx]] = ecc_code[i];//存放到ecc_code数组中

datidx += this->eccsize;

}

this->write_buf(mtd, this->data_poi, mtd->oobblock);//调用FLASH硬件驱动层进行写操作

break;

default:

eccbytes = this->eccbytes;

for (; eccsteps; eccsteps--) {

/* enable hardware ecc logic for write */

this->enable_hwecc(mtd, NAND_ECC_WRITE);

this->write_buf(mtd, &this->data_poi[datidx], this->eccsize);

this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);

for (i = 0; i < eccbytes; i++, eccidx++)

oob_buf[oob_config[eccidx]] = ecc_code[i];

/* If the hardware ecc provides syndromes then

* the ecc code must be written immidiately after

* the data bytes (words) */

if (this->options & NAND_HWECC_SYNDROME)

this->write_buf(mtd, ecc_code, eccbytes);

datidx += this->eccsize;

}

break;

}

/* Write out OOB data */

if (this->options & NAND_HWECC_SYNDROME)

this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes);

else

this->write_buf(mtd, oob_buf, mtd->oobsize);//写oob data,主要把上面计算的ecc值写进去

/* Send command to actually program the data */

this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1);

if (!cached) {

/* call wait ready function */

status = this->waitfunc (mtd, this, FL_WRITING);//等待写入完成

/* See if operation failed and additional status checks are available */

if ((status & NAND_STATUS_FAIL) && (this->errstat)) {

status = this->errstat(mtd, this, FL_WRITING, status, page);

}

/* See if device thinks it succeeded */

if (status & NAND_STATUS_FAIL) {

DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);

return -EIO;

}

} else {

/* FIXME: Implement cached programming ! */

/* wait until cache is ready*/

// status = this->waitfunc (mtd, this, FL_CACHEDRPG);//cached的写操作暂时没用

}

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: