Loading drivers/mtd/nand/diskonchip.c +3 −1 Original line number Diff line number Diff line Loading @@ -968,12 +968,14 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsign 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; struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; uint8_t calc_ecc[6]; volatile u_char dummy; int emptymatch = 1; Loading drivers/mtd/nand/nand_base.c +227 −221 Original line number Diff line number Diff line Loading @@ -976,256 +976,224 @@ static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *chip, int p #endif /** * 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 * nand_read_page_swecc - {REPLACABLE] software ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data */ 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 * @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_page_hwecc - {REPLACABLE] hardware ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data * * 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, size_t *retlen, uint8_t *buf, uint8_t *oob_buf, struct nand_oobinfo *oobsel, int flags) static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf) { 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 *chip = mtd->priv; uint8_t *data_poi, *oob_data = oob_buf; uint8_t ecc_calc[32]; uint8_t ecc_code[32]; int eccmode, eccsteps; int *oob_config, datidx; int blockcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; int eccbytes; int compareecc = 1; 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; 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; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); chip->ecc.calculate(mtd, p, &ecc_calc[i]); } chip->read_buf(mtd, chip->oob_buf, mtd->oobsize); /* Grab the lock and see if the device is available */ if (flags & NAND_GET_DEVICE) nand_get_device(chip, mtd, FL_READING); /* Autoplace of oob data ? Use the default placement scheme */ if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) oobsel = chip->autooob; eccmode = oobsel->useecc ? chip->ecc.mode : NAND_ECC_NONE; oob_config = oobsel->eccpos; for (i = 0; i < chip->ecc.total; i++) ecc_code[i] = chip->oob_buf[eccpos[i]]; /* Select the NAND device */ chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); /* First we calculate the starting page */ realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; /* Get raw starting column */ col = from & (mtd->writesize - 1); end = mtd->writesize; ecc = chip->ecc.size; eccbytes = chip->ecc.bytes; if ((eccmode == NAND_ECC_NONE) || (chip->options & NAND_HWECC_SYNDROME)) compareecc = 0; oobreadlen = mtd->oobsize; if (chip->options & NAND_HWECC_SYNDROME) oobreadlen -= oobsel->eccbytes; eccsteps = chip->ecc.steps; p = buf; /* Loop until all data read */ while (read < len) { for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; 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]; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); if (stat == -1) mtd->ecc_stats.failed++; else data_poi = chip->data_buf; mtd->ecc_stats.corrected += stat; } return 0; } /* Check, if we have this page in the buffer /** * 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 * * FIXME: Make it work when we must provide oob data too, * check the usage of data_buf oob field * The hw generator calculates the error syndrome automatically. Therefor * we need a special oob layout and . */ 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 (sndcmd) { chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); sndcmd = 0; } 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; /* 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 = &chip->data_buf[end]; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; eccsteps = chip->ecc.steps; chip->ecc.hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); 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; if (chip->ecc.prepad) { chip->read_buf(mtd, oob, chip->ecc.prepad); oob += chip->ecc.prepad; } case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ 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; chip->ecc.hwctl(mtd, NAND_ECC_READSYN); chip->read_buf(mtd, oob, eccbytes); stat = chip->ecc.correct(mtd, p, oob, NULL); default: for (i = 0, datidx = 0; eccsteps; eccsteps--, i += eccbytes, datidx += ecc) { chip->ecc.hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, &data_poi[datidx], ecc); if (stat == -1) mtd->ecc_stats.failed++; else mtd->ecc_stats.corrected += stat; /* 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 */ chip->ecc.hwctl(mtd, NAND_ECC_READSYN); chip->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 = chip->ecc.correct(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 { chip->ecc.calculate(mtd, &data_poi[datidx], &ecc_calc[i]); oob += eccbytes; if (chip->ecc.postpad) { chip->read_buf(mtd, oob, chip->ecc.postpad); oob += chip->ecc.postpad; } } break; /* Calculate remaining oob bytes */ i = oob - chip->oob_buf; if (i) chip->read_buf(mtd, oob, i); return 0; } /* read oobdata */ chip->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen); /** * nand_do_read - [Internal] 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 * * Internal function. Called with chip held. */ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf) { int chipnr, page, realpage, col, bytes, aligned; struct nand_chip *chip = mtd->priv; struct mtd_ecc_stats stats; int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; int sndcmd = 1; int ret = 0; uint32_t readlen = len; uint8_t *bufpoi; stats = mtd->ecc_stats; /* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */ if (!compareecc) goto readoob; chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); /* Pick the ECC bytes out of the oob data */ for (j = 0; j < oobsel->eccbytes; j++) ecc_code[j] = oob_data[oob_config[j]]; realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; /* correct data, if necessary */ 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]); col = (int)(from & (mtd->writesize - 1)); /* Get next chunk of ecc bytes */ j += eccbytes; while(1) { bytes = min(mtd->writesize - col, readlen); aligned = (bytes == mtd->writesize); /* 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; } /* Is the current page in the buffer ? */ if (realpage != chip->pagebuf) { bufpoi = aligned ? buf : chip->data_buf; 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++; } if (likely(sndcmd)) { chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); sndcmd = 0; } 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; } /* Now read the page into the buffer */ ret = chip->ecc.read_page(mtd, chip, bufpoi); if (ret < 0) break; case MTD_NANDECC_PLACE: /* YAFFS1 legacy mode */ oob_data += chip->ecc.steps * sizeof(int); default: oob_data += mtd->oobsize; } } readdata: /* Partial page read, transfer data into fs buffer */ /* Transfer not aligned data */ if (!aligned) { for (j = col; j < end && read < len; j++) buf[read++] = data_poi[j]; chip->pagebuf = realpage; } else read += mtd->writesize; memcpy(buf, chip->data_buf + col, bytes); } /* 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->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); } } else memcpy(buf, chip->data_buf + col, bytes); if (read == len) buf += bytes; readlen -= bytes; if (!readlen) break; /* For subsequent reads align to page boundary. */ Loading @@ -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, chipnr); } /* Check, if the chip supports auto page increment * or if we have hit a block boundary. */ if (!NAND_CANAUTOINCR(chip) || !(page & blockcheck)) if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) sndcmd = 1; } /* Deselect and wake up anyone waiting on the device */ if (flags & NAND_GET_DEVICE) nand_release_device(mtd); *retlen = len - (size_t) readlen; /* * Return success, if no ECC failures, else -EBADMSG * fs driver will take care of that, because * retlen == desired len and result == -EBADMSG if (ret) return ret; return mtd->ecc_stats.failed - stats.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 */ *retlen = read; return ecc_failed ? -EBADMSG : 0; 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; } /** Loading Loading @@ -2417,6 +2412,10 @@ int nand_scan(struct mtd_info *mtd, int maxchips) */ switch (chip->ecc.mode) { 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: if (!chip->ecc.calculate || !chip->ecc.correct || !chip->ecc.hwctl) { Loading @@ -2424,6 +2423,10 @@ int nand_scan(struct mtd_info *mtd, int maxchips) "Hardware ECC not possible\n"); 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) break; printk(KERN_WARNING "%d byte HW ECC not possible on " Loading @@ -2434,6 +2437,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) case NAND_ECC_SOFT: chip->ecc.calculate = nand_calculate_ecc; chip->ecc.correct = nand_correct_data; chip->ecc.read_page = nand_read_page_swecc; chip->ecc.size = 256; chip->ecc.bytes = 3; break; Loading @@ -2441,6 +2445,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) case NAND_ECC_NONE: printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " "This is not recommended !!\n"); chip->ecc.read_page = nand_read_page_swecc; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; break; Loading @@ -2459,6 +2464,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) printk(KERN_WARNING "Invalid ecc parameters\n"); BUG(); } chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; /* Initialize state */ chip->state = FL_READY; Loading drivers/mtd/nand/rtc_from4.c +37 −25 Original line number Diff line number Diff line Loading @@ -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. * */ 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 rtn, retlen; Loading @@ -455,40 +456,51 @@ static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int s this->cmdfunc(mtd, NAND_CMD_STATUS_CLEAR, -1, -1); if (state == FL_ERASING) { for (i = 0; i < 4; i++) { if (status & 1 << (i + 1)) { this->cmdfunc(mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1); if (!(status & 1 << (i + 1))) continue; this->cmdfunc(mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1); rtn = this->read_byte(mtd); this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1); if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { er_stat |= 1 << (i + 1); /* err_ecc_not_avail */ } } /* err_ecc_not_avail */ if (!(rtn & ERR_STAT_ECC_AVAILABLE)) er_stat |= 1 << (i + 1); } } else if (state == FL_WRITING) { unsigned long corrected = mtd->ecc_stats.corrected; /* single bank write logic */ this->cmdfunc(mtd, NAND_CMD_STATUS_ERROR, -1, -1); rtn = this->read_byte(mtd); this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1); if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { er_stat |= 1 << 1; /* err_ecc_not_avail */ } else { /* err_ecc_not_avail */ er_stat |= 1 << 1; goto out; } len = mtd->writesize; 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 */ 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; if (er_stat == 0) { /* if ECC is available */ Loading include/linux/mtd/nand.h +8 −8 Original line number Diff line number Diff line Loading @@ -483,10 +483,10 @@ 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_default_bbt(struct mtd_info *mtd); 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_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, uint8_t * buf, uint8_t * oob_buf, struct nand_oobinfo *oobsel, int flags); extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int allowbbt); extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, uint8_t * buf); /* * Constants for oob configuration Loading Loading
drivers/mtd/nand/diskonchip.c +3 −1 Original line number Diff line number Diff line Loading @@ -968,12 +968,14 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsign 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; struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; uint8_t calc_ecc[6]; volatile u_char dummy; int emptymatch = 1; Loading
drivers/mtd/nand/nand_base.c +227 −221 Original line number Diff line number Diff line Loading @@ -976,256 +976,224 @@ static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *chip, int p #endif /** * 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 * nand_read_page_swecc - {REPLACABLE] software ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data */ 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 * @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_page_hwecc - {REPLACABLE] hardware ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data * * 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, size_t *retlen, uint8_t *buf, uint8_t *oob_buf, struct nand_oobinfo *oobsel, int flags) static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf) { 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 *chip = mtd->priv; uint8_t *data_poi, *oob_data = oob_buf; uint8_t ecc_calc[32]; uint8_t ecc_code[32]; int eccmode, eccsteps; int *oob_config, datidx; int blockcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; int eccbytes; int compareecc = 1; 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; 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; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); chip->ecc.calculate(mtd, p, &ecc_calc[i]); } chip->read_buf(mtd, chip->oob_buf, mtd->oobsize); /* Grab the lock and see if the device is available */ if (flags & NAND_GET_DEVICE) nand_get_device(chip, mtd, FL_READING); /* Autoplace of oob data ? Use the default placement scheme */ if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) oobsel = chip->autooob; eccmode = oobsel->useecc ? chip->ecc.mode : NAND_ECC_NONE; oob_config = oobsel->eccpos; for (i = 0; i < chip->ecc.total; i++) ecc_code[i] = chip->oob_buf[eccpos[i]]; /* Select the NAND device */ chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); /* First we calculate the starting page */ realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; /* Get raw starting column */ col = from & (mtd->writesize - 1); end = mtd->writesize; ecc = chip->ecc.size; eccbytes = chip->ecc.bytes; if ((eccmode == NAND_ECC_NONE) || (chip->options & NAND_HWECC_SYNDROME)) compareecc = 0; oobreadlen = mtd->oobsize; if (chip->options & NAND_HWECC_SYNDROME) oobreadlen -= oobsel->eccbytes; eccsteps = chip->ecc.steps; p = buf; /* Loop until all data read */ while (read < len) { for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; 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]; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); if (stat == -1) mtd->ecc_stats.failed++; else data_poi = chip->data_buf; mtd->ecc_stats.corrected += stat; } return 0; } /* Check, if we have this page in the buffer /** * 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 * * FIXME: Make it work when we must provide oob data too, * check the usage of data_buf oob field * The hw generator calculates the error syndrome automatically. Therefor * we need a special oob layout and . */ 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 (sndcmd) { chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); sndcmd = 0; } 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; /* 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 = &chip->data_buf[end]; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; eccsteps = chip->ecc.steps; chip->ecc.hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); 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; if (chip->ecc.prepad) { chip->read_buf(mtd, oob, chip->ecc.prepad); oob += chip->ecc.prepad; } case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ 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; chip->ecc.hwctl(mtd, NAND_ECC_READSYN); chip->read_buf(mtd, oob, eccbytes); stat = chip->ecc.correct(mtd, p, oob, NULL); default: for (i = 0, datidx = 0; eccsteps; eccsteps--, i += eccbytes, datidx += ecc) { chip->ecc.hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, &data_poi[datidx], ecc); if (stat == -1) mtd->ecc_stats.failed++; else mtd->ecc_stats.corrected += stat; /* 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 */ chip->ecc.hwctl(mtd, NAND_ECC_READSYN); chip->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 = chip->ecc.correct(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 { chip->ecc.calculate(mtd, &data_poi[datidx], &ecc_calc[i]); oob += eccbytes; if (chip->ecc.postpad) { chip->read_buf(mtd, oob, chip->ecc.postpad); oob += chip->ecc.postpad; } } break; /* Calculate remaining oob bytes */ i = oob - chip->oob_buf; if (i) chip->read_buf(mtd, oob, i); return 0; } /* read oobdata */ chip->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen); /** * nand_do_read - [Internal] 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 * * Internal function. Called with chip held. */ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf) { int chipnr, page, realpage, col, bytes, aligned; struct nand_chip *chip = mtd->priv; struct mtd_ecc_stats stats; int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; int sndcmd = 1; int ret = 0; uint32_t readlen = len; uint8_t *bufpoi; stats = mtd->ecc_stats; /* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */ if (!compareecc) goto readoob; chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); /* Pick the ECC bytes out of the oob data */ for (j = 0; j < oobsel->eccbytes; j++) ecc_code[j] = oob_data[oob_config[j]]; realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; /* correct data, if necessary */ 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]); col = (int)(from & (mtd->writesize - 1)); /* Get next chunk of ecc bytes */ j += eccbytes; while(1) { bytes = min(mtd->writesize - col, readlen); aligned = (bytes == mtd->writesize); /* 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; } /* Is the current page in the buffer ? */ if (realpage != chip->pagebuf) { bufpoi = aligned ? buf : chip->data_buf; 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++; } if (likely(sndcmd)) { chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); sndcmd = 0; } 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; } /* Now read the page into the buffer */ ret = chip->ecc.read_page(mtd, chip, bufpoi); if (ret < 0) break; case MTD_NANDECC_PLACE: /* YAFFS1 legacy mode */ oob_data += chip->ecc.steps * sizeof(int); default: oob_data += mtd->oobsize; } } readdata: /* Partial page read, transfer data into fs buffer */ /* Transfer not aligned data */ if (!aligned) { for (j = col; j < end && read < len; j++) buf[read++] = data_poi[j]; chip->pagebuf = realpage; } else read += mtd->writesize; memcpy(buf, chip->data_buf + col, bytes); } /* 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->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); } } else memcpy(buf, chip->data_buf + col, bytes); if (read == len) buf += bytes; readlen -= bytes; if (!readlen) break; /* For subsequent reads align to page boundary. */ Loading @@ -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, chipnr); } /* Check, if the chip supports auto page increment * or if we have hit a block boundary. */ if (!NAND_CANAUTOINCR(chip) || !(page & blockcheck)) if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) sndcmd = 1; } /* Deselect and wake up anyone waiting on the device */ if (flags & NAND_GET_DEVICE) nand_release_device(mtd); *retlen = len - (size_t) readlen; /* * Return success, if no ECC failures, else -EBADMSG * fs driver will take care of that, because * retlen == desired len and result == -EBADMSG if (ret) return ret; return mtd->ecc_stats.failed - stats.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 */ *retlen = read; return ecc_failed ? -EBADMSG : 0; 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; } /** Loading Loading @@ -2417,6 +2412,10 @@ int nand_scan(struct mtd_info *mtd, int maxchips) */ switch (chip->ecc.mode) { 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: if (!chip->ecc.calculate || !chip->ecc.correct || !chip->ecc.hwctl) { Loading @@ -2424,6 +2423,10 @@ int nand_scan(struct mtd_info *mtd, int maxchips) "Hardware ECC not possible\n"); 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) break; printk(KERN_WARNING "%d byte HW ECC not possible on " Loading @@ -2434,6 +2437,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) case NAND_ECC_SOFT: chip->ecc.calculate = nand_calculate_ecc; chip->ecc.correct = nand_correct_data; chip->ecc.read_page = nand_read_page_swecc; chip->ecc.size = 256; chip->ecc.bytes = 3; break; Loading @@ -2441,6 +2445,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) case NAND_ECC_NONE: printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " "This is not recommended !!\n"); chip->ecc.read_page = nand_read_page_swecc; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; break; Loading @@ -2459,6 +2464,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) printk(KERN_WARNING "Invalid ecc parameters\n"); BUG(); } chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; /* Initialize state */ chip->state = FL_READY; Loading
drivers/mtd/nand/rtc_from4.c +37 −25 Original line number Diff line number Diff line Loading @@ -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. * */ 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 rtn, retlen; Loading @@ -455,40 +456,51 @@ static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int s this->cmdfunc(mtd, NAND_CMD_STATUS_CLEAR, -1, -1); if (state == FL_ERASING) { for (i = 0; i < 4; i++) { if (status & 1 << (i + 1)) { this->cmdfunc(mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1); if (!(status & 1 << (i + 1))) continue; this->cmdfunc(mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1); rtn = this->read_byte(mtd); this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1); if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { er_stat |= 1 << (i + 1); /* err_ecc_not_avail */ } } /* err_ecc_not_avail */ if (!(rtn & ERR_STAT_ECC_AVAILABLE)) er_stat |= 1 << (i + 1); } } else if (state == FL_WRITING) { unsigned long corrected = mtd->ecc_stats.corrected; /* single bank write logic */ this->cmdfunc(mtd, NAND_CMD_STATUS_ERROR, -1, -1); rtn = this->read_byte(mtd); this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1); if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { er_stat |= 1 << 1; /* err_ecc_not_avail */ } else { /* err_ecc_not_avail */ er_stat |= 1 << 1; goto out; } len = mtd->writesize; 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 */ 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; if (er_stat == 0) { /* if ECC is available */ Loading
include/linux/mtd/nand.h +8 −8 Original line number Diff line number Diff line Loading @@ -483,10 +483,10 @@ 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_default_bbt(struct mtd_info *mtd); 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_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, uint8_t * buf, uint8_t * oob_buf, struct nand_oobinfo *oobsel, int flags); extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int allowbbt); extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, uint8_t * buf); /* * Constants for oob configuration Loading