轉自:luofuchong
http://www.cnitblog.com/luofuchong/archive/2007/09/04/32939.html
上一個貼由下到上的介紹了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;
}