Commit f5bbdacc authored by Thomas Gleixner's avatar Thomas Gleixner Committed by David Woodhouse

[MTD] NAND Modularize read function

Split the core of the read function out and implement
seperate handling functions for software and hardware
ECC.
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 9577f44a
...@@ -968,12 +968,14 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsign ...@@ -968,12 +968,14 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsign
return 0; return 0;
} }
static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *isnull)
{ {
int i, ret = 0; int i, ret = 0;
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
uint8_t calc_ecc[6];
volatile u_char dummy; volatile u_char dummy;
int emptymatch = 1; int emptymatch = 1;
......
...@@ -976,256 +976,224 @@ static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *chip, int p ...@@ -976,256 +976,224 @@ static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *chip, int p
#endif #endif
/** /**
* nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc * nand_read_page_swecc - {REPLACABLE] software ecc based page read function
* @mtd: MTD device structure * @mtd: mtd info structure
* @from: offset to read from * @chip: nand chip info structure
* @len: number of bytes to read * @buf: buffer to store read data
* @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, uint8_t *buf) static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf)
{ {
return nand_do_read_ecc(mtd, from, len, retlen, buf, NULL, &mtd->oobinfo, 0xff); int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
uint8_t *ecc_calc = chip->oob_buf + mtd->oobsize;
uint8_t *ecc_code = ecc_calc + mtd->oobsize;
int *eccpos = chip->autooob->eccpos;
chip->read_buf(mtd, buf, mtd->writesize);
chip->read_buf(mtd, chip->oob_buf, mtd->oobsize);
if (chip->ecc.mode == NAND_ECC_NONE)
return 0;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_buf[eccpos[i]];
eccsteps = chip->ecc.steps;
p = buf;
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int stat;
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
if (stat == -1)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}
return 0;
} }
/** /**
* nand_do_read_ecc - [MTD Interface] Read data with ECC * nand_read_page_hwecc - {REPLACABLE] hardware ecc based page read function
* @mtd: MTD device structure * @mtd: mtd info structure
* @from: offset to read from * @chip: nand chip info structure
* @len: number of bytes to read * @buf: buffer to store read data
* @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 * Not for syndrome calculating ecc controllers which need a special oob layout
*/ */
int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
size_t *retlen, uint8_t *buf, uint8_t *oob_buf, struct nand_oobinfo *oobsel, int flags) uint8_t *buf)
{ {
int i, eccsize = chip->ecc.size;
int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1; int eccbytes = chip->ecc.bytes;
int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; int eccsteps = chip->ecc.steps;
struct nand_chip *chip = mtd->priv; uint8_t *p = buf;
uint8_t *data_poi, *oob_data = oob_buf; uint8_t *ecc_calc = chip->oob_buf + mtd->oobsize;
uint8_t ecc_calc[32]; uint8_t *ecc_code = ecc_calc + mtd->oobsize;
uint8_t ecc_code[32]; int *eccpos = chip->autooob->eccpos;
int eccmode, eccsteps;
int *oob_config, datidx; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int blockcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; chip->ecc.hwctl(mtd, NAND_ECC_READ);
int eccbytes; chip->read_buf(mtd, p, eccsize);
int compareecc = 1; chip->ecc.calculate(mtd, p, &ecc_calc[i]);
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;
} }
chip->read_buf(mtd, chip->oob_buf, mtd->oobsize);
/* Grab the lock and see if the device is available */ for (i = 0; i < chip->ecc.total; i++)
if (flags & NAND_GET_DEVICE) ecc_code[i] = chip->oob_buf[eccpos[i]];
nand_get_device(chip, mtd, FL_READING);
/* Autoplace of oob data ? Use the default placement scheme */ eccsteps = chip->ecc.steps;
if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) p = buf;
oobsel = chip->autooob;
eccmode = oobsel->useecc ? chip->ecc.mode : NAND_ECC_NONE; for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
oob_config = oobsel->eccpos; int stat;
/* Select the NAND device */ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
chipnr = (int)(from >> chip->chip_shift); if (stat == -1)
chip->select_chip(mtd, chipnr); mtd->ecc_stats.failed++;
else
/* First we calculate the starting page */ mtd->ecc_stats.corrected += stat;
realpage = (int)(from >> chip->page_shift); }
page = realpage & chip->pagemask; return 0;
}
/* Get raw starting column */ /**
col = from & (mtd->writesize - 1); * nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
*
* The hw generator calculates the error syndrome automatically. Therefor
* we need a special oob layout and .
*/
static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
uint8_t *oob = chip->oob_buf;
end = mtd->writesize; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
ecc = chip->ecc.size; int stat;
eccbytes = chip->ecc.bytes;
if ((eccmode == NAND_ECC_NONE) || (chip->options & NAND_HWECC_SYNDROME)) chip->ecc.hwctl(mtd, NAND_ECC_READ);
compareecc = 0; chip->read_buf(mtd, p, eccsize);
oobreadlen = mtd->oobsize; if (chip->ecc.prepad) {
if (chip->options & NAND_HWECC_SYNDROME) chip->read_buf(mtd, oob, chip->ecc.prepad);
oobreadlen -= oobsel->eccbytes; oob += chip->ecc.prepad;
}
/* Loop until all data read */ chip->ecc.hwctl(mtd, NAND_ECC_READSYN);
while (read < len) { chip->read_buf(mtd, oob, eccbytes);
stat = chip->ecc.correct(mtd, p, oob, NULL);
int aligned = (!col && (len - read) >= end); if (stat == -1)
/* mtd->ecc_stats.failed++;
* 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 else
data_poi = chip->data_buf; mtd->ecc_stats.corrected += stat;
/* Check, if we have this page in the buffer oob += eccbytes;
*
* FIXME: Make it work when we must provide oob data too,
* check the usage of data_buf oob field
*/
if (realpage == chip->pagebuf && !oob_buf) {
/* aligned read ? */
if (aligned)
memcpy(data_poi, chip->data_buf, end);
goto readdata;
}
/* Check, if we must send the read command */ if (chip->ecc.postpad) {
if (sndcmd) { chip->read_buf(mtd, oob, chip->ecc.postpad);
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); oob += chip->ecc.postpad;
sndcmd = 0;
} }
}
/* get oob area, if we have no oob buffer from fs-driver */ /* Calculate remaining oob bytes */
if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE || i = oob - chip->oob_buf;
oobsel->useecc == MTD_NANDECC_AUTOPL_USR) if (i)
oob_data = &chip->data_buf[end]; chip->read_buf(mtd, oob, i);
eccsteps = chip->ecc.steps;
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;
}
chip->read_buf(mtd, data_poi, end);
break;
}
case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ return 0;
chip->read_buf(mtd, data_poi, end); }
for (i = 0, datidx = 0; eccsteps; eccsteps--, i += 3, datidx += ecc)
chip->ecc.calculate(mtd, &data_poi[datidx], &ecc_calc[i]);
break;
default: /**
for (i = 0, datidx = 0; eccsteps; eccsteps--, i += eccbytes, datidx += ecc) { * nand_do_read - [Internal] Read data with ECC
chip->ecc.hwctl(mtd, NAND_ECC_READ); *
chip->read_buf(mtd, &data_poi[datidx], ecc); * @mtd: MTD device structure
* @from: offset to read from
/* HW ecc with syndrome calculation must read the * @len: number of bytes to read
* syndrome from flash immidiately after the data */ * @retlen: pointer to variable to store the number of read bytes
if (!compareecc) { * @buf: the databuffer to put data
/* Some hw ecc generators need to know when the *
* syndrome is read from flash */ * Internal function. Called with chip held.
chip->ecc.hwctl(mtd, NAND_ECC_READSYN); */
chip->read_buf(mtd, &oob_data[i], eccbytes); int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
/* We calc error correction directly, it checks the hw size_t *retlen, uint8_t *buf)
* generator for an error, reads back the syndrome and {
* does the error correction on the fly */ int chipnr, page, realpage, col, bytes, aligned;
ecc_status = chip->ecc.correct(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]); struct nand_chip *chip = mtd->priv;
if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { struct mtd_ecc_stats stats;
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_ecc: " int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
"Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); int sndcmd = 1;
ecc_failed++; int ret = 0;
} uint32_t readlen = len;
} else { uint8_t *bufpoi;
chip->ecc.calculate(mtd, &data_poi[datidx], &ecc_calc[i]);
}
}
break;
}
/* read oobdata */ stats = mtd->ecc_stats;
chip->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen);
/* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */ chipnr = (int)(from >> chip->chip_shift);
if (!compareecc) chip->select_chip(mtd, chipnr);
goto readoob;
/* Pick the ECC bytes out of the oob data */ realpage = (int)(from >> chip->page_shift);
for (j = 0; j < oobsel->eccbytes; j++) page = realpage & chip->pagemask;
ecc_code[j] = oob_data[oob_config[j]];
/* correct data, if necessary */ col = (int)(from & (mtd->writesize - 1));
for (i = 0, j = 0, datidx = 0; i < chip->ecc.steps; i++, datidx += ecc) {
ecc_status = chip->ecc.correct(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);
/* Get next chunk of ecc bytes */ while(1) {
j += eccbytes; bytes = min(mtd->writesize - col, readlen);
aligned = (bytes == mtd->writesize);
/* Check, if we have a fs supplied oob-buffer, /* Is the current page in the buffer ? */
* This is the legacy mode. Used by YAFFS1 if (realpage != chip->pagebuf) {
* Should go away some day bufpoi = aligned ? buf : chip->data_buf;
*/
if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) {
int *p = (int *)(&oob_data[mtd->oobsize]);
p[i] = ecc_status;
}
if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { if (likely(sndcmd)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
ecc_failed++; sndcmd = 0;
} }
}
readoob: /* Now read the page into the buffer */
/* check, if we have a fs supplied oob-buffer */ ret = chip->ecc.read_page(mtd, chip, bufpoi);
if (oob_buf) { if (ret < 0)
/* 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; break;
case MTD_NANDECC_PLACE:
/* YAFFS1 legacy mode */ /* Transfer not aligned data */
oob_data += chip->ecc.steps * sizeof(int); if (!aligned) {
default: chip->pagebuf = realpage;
oob_data += mtd->oobsize; memcpy(buf, chip->data_buf + col, bytes);
}
if (!(chip->options & NAND_NO_READRDY)) {
/*
* 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 (!chip->dev_ready)
udelay(chip->chip_delay);
else
nand_wait_ready(mtd);
} }
}
readdata:
/* Partial page read, transfer data into fs buffer */
if (!aligned) {
for (j = col; j < end && read < len; j++)
buf[read++] = data_poi[j];
chip->pagebuf = realpage;
} else } else
read += mtd->writesize; memcpy(buf, chip->data_buf + col, bytes);
/* Apply delay or wait for ready/busy pin buf += bytes;
* Do this before the AUTOINCR check, so no problems readlen -= bytes;
* arise if a chip which does auto increment
* is marked as NOAUTOINCR by the board driver.
*/
if (!chip->dev_ready)
udelay(chip->chip_delay);
else
nand_wait_ready(mtd);
if (read == len) if (!readlen)
break; break;
/* For subsequent reads align to page boundary. */ /* For subsequent reads align to page boundary. */
...@@ -1240,24 +1208,51 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -1240,24 +1208,51 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
chip->select_chip(mtd, -1); chip->select_chip(mtd, -1);
chip->select_chip(mtd, chipnr); chip->select_chip(mtd, chipnr);
} }
/* Check, if the chip supports auto page increment /* Check, if the chip supports auto page increment
* or if we have hit a block boundary. * or if we have hit a block boundary.
*/ */
if (!NAND_CANAUTOINCR(chip) || !(page & blockcheck)) if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
sndcmd = 1; sndcmd = 1;
} }
/* Deselect and wake up anyone waiting on the device */ *retlen = len - (size_t) readlen;
if (flags & NAND_GET_DEVICE)
nand_release_device(mtd);
/* if (ret)
* Return success, if no ECC failures, else -EBADMSG return ret;
* fs driver will take care of that, because
* retlen == desired len and result == -EBADMSG return mtd->ecc_stats.failed - stats.failed ? -EBADMSG : 0;
*/ }
*retlen = read;
return ecc_failed ? -EBADMSG : 0; /**
* 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
*
* Get hold of the chip and call nand_do_read
*/
static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, uint8_t *buf)
{
int ret;
*retlen = 0;
/* Do not allow reads past end of device */
if ((from + len) > mtd->size)
return -EINVAL;
if (!len)
return 0;
nand_get_device(mtd->priv, mtd, FL_READING);
ret = nand_do_read(mtd, from, len, retlen, buf);
nand_release_device(mtd);
return ret;
} }
/** /**
...@@ -2417,6 +2412,10 @@ int nand_scan(struct mtd_info *mtd, int maxchips) ...@@ -2417,6 +2412,10 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
*/ */
switch (chip->ecc.mode) { switch (chip->ecc.mode) {
case NAND_ECC_HW: case NAND_ECC_HW:
/* Use standard hwecc read page function ? */
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_hwecc;
case NAND_ECC_HW_SYNDROME: case NAND_ECC_HW_SYNDROME:
if (!chip->ecc.calculate || !chip->ecc.correct || if (!chip->ecc.calculate || !chip->ecc.correct ||
!chip->ecc.hwctl) { !chip->ecc.hwctl) {
...@@ -2424,6 +2423,10 @@ int nand_scan(struct mtd_info *mtd, int maxchips) ...@@ -2424,6 +2423,10 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
"Hardware ECC not possible\n"); "Hardware ECC not possible\n");
BUG(); BUG();
} }
/* Use standard syndrome read page function ? */
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_syndrome;
if (mtd->writesize >= chip->ecc.size) if (mtd->writesize >= chip->ecc.size)
break; break;
printk(KERN_WARNING "%d byte HW ECC not possible on " printk(KERN_WARNING "%d byte HW ECC not possible on "
...@@ -2434,6 +2437,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) ...@@ -2434,6 +2437,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
case NAND_ECC_SOFT: case NAND_ECC_SOFT:
chip->ecc.calculate = nand_calculate_ecc; chip->ecc.calculate = nand_calculate_ecc;
chip->ecc.correct = nand_correct_data; chip->ecc.correct = nand_correct_data;
chip->ecc.read_page = nand_read_page_swecc;
chip->ecc.size = 256; chip->ecc.size = 256;
chip->ecc.bytes = 3; chip->ecc.bytes = 3;
break; break;
...@@ -2441,6 +2445,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) ...@@ -2441,6 +2445,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
case NAND_ECC_NONE: case NAND_ECC_NONE:
printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
"This is not recommended !!\n"); "This is not recommended !!\n");
chip->ecc.read_page = nand_read_page_swecc;
chip->ecc.size = mtd->writesize; chip->ecc.size = mtd->writesize;
chip->ecc.bytes = 0; chip->ecc.bytes = 0;
break; break;
...@@ -2459,6 +2464,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) ...@@ -2459,6 +2464,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
printk(KERN_WARNING "Invalid ecc parameters\n"); printk(KERN_WARNING "Invalid ecc parameters\n");
BUG(); BUG();
} }
chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
/* Initialize state */ /* Initialize state */
chip->state = FL_READY; chip->state = FL_READY;
......
...@@ -444,7 +444,8 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha ...@@ -444,7 +444,8 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha
* note: see pages 34..37 of data sheet for details. * note: see pages 34..37 of data sheet for details.
* *
*/ */
static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page) static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this,
int state, int status, int page)
{ {
int er_stat = 0; int er_stat = 0;
int rtn, retlen; int rtn, retlen;
...@@ -455,39 +456,50 @@ static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int s ...@@ -455,39 +456,50 @@ static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int s
this->cmdfunc(mtd, NAND_CMD_STATUS_CLEAR, -1, -1); this->cmdfunc(mtd, NAND_CMD_STATUS_CLEAR, -1, -1);
if (state == FL_ERASING) { if (state == FL_ERASING) {
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
if (status & 1 << (i + 1)) { if (!(status & 1 << (i + 1)))
this->cmdfunc(mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1); continue;
rtn = this->read_byte(mtd); this->cmdfunc(mtd, (NAND_CMD_STATUS_ERROR + i + 1),
this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1); -1, -1);
if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { rtn = this->read_byte(mtd);
er_stat |= 1 << (i + 1); /* err_ecc_not_avail */ this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1);
}
} /* err_ecc_not_avail */
if (!(rtn & ERR_STAT_ECC_AVAILABLE))
er_stat |= 1 << (i + 1);
} }
} else if (state == FL_WRITING) { } else if (state == FL_WRITING) {
unsigned long corrected = mtd->ecc_stats.corrected;
/* single bank write logic */ /* single bank write logic */
this->cmdfunc(mtd, NAND_CMD_STATUS_ERROR, -1, -1); this->cmdfunc(mtd, NAND_CMD_STATUS_ERROR, -1, -1);
rtn = this->read_byte(mtd); rtn = this->read_byte(mtd);
this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1); this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1);
if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { if (!(rtn & ERR_STAT_ECC_AVAILABLE)) {
er_stat |= 1 << 1; /* err_ecc_not_avail */ /* err_ecc_not_avail */
} else { er_stat |= 1 << 1;
len = mtd->writesize; goto out;
buf = kmalloc(len, GFP_KERNEL);
if (!buf) {
printk(KERN_ERR "rtc_from4_errstat: Out of memory!\n");
er_stat = 1; /* if we can't check, assume failed */
} else {
/* recovery read */
/* page read */
rtn = nand_do_read_ecc(mtd, page, len, &retlen, buf, NULL, this->autooob, 1);
if (rtn) { /* if read failed or > 1-bit error corrected */
er_stat |= 1 << 1; /* ECC read failed */
}
kfree(buf);
}
} }
len = mtd->writesize;
buf = kmalloc(len, GFP_KERNEL);
if (!buf) {
printk(KERN_ERR "rtc_from4_errstat: Out of memory!\n");
er_stat = 1;
goto out;
}
/* recovery read */
rtn = nand_do_read(mtd, page, len, &retlen, buf);
/* if read failed or > 1-bit error corrected */
if (rtn || (mtd->ecc_stats.corrected - corrected) > 1) {
er_stat |= 1 << 1;
kfree(buf);
} }
rtn = status; rtn = status;
......
...@@ -479,14 +479,14 @@ struct nand_bbt_descr { ...@@ -479,14 +479,14 @@ struct nand_bbt_descr {
/* The maximum number of blocks to scan for a bbt */ /* The maximum number of blocks to scan for a bbt */
#define NAND_BBT_SCAN_MAXBLOCKS 4 #define NAND_BBT_SCAN_MAXBLOCKS 4
extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd); extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs); extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs);
extern int nand_default_bbt (struct mtd_info *mtd); extern int nand_default_bbt(struct mtd_info *mtd);
extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt); extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);
extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt); extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, int allowbbt);
size_t * retlen, uint8_t * buf, uint8_t * oob_buf, extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
struct nand_oobinfo *oobsel, int flags); size_t * retlen, uint8_t * buf);
/* /*
* Constants for oob configuration * Constants for oob configuration
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment