📄 jz4730_nand.c
字号:
if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) { DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); goto out; } datidx += mtd->eccsize; /* Have we a hw generator layout ? */ if (!hweccbytes) continue; if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) { DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); goto out; } oobofs += hweccbytes; } /* check, if we must compare all data or if we just have to * compare the ecc bytes */ if (oobmode) { if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) { DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); goto out; } } else { /* Read always, else autoincrement fails */ this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps); if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) { int ecccnt = oobsel->eccbytes; for (i = 0; i < ecccnt; i++) { int idx = oobsel->eccpos[i]; if (oobdata[idx] != oob_buf[oobofs + idx] ) { DEBUG (MTD_DEBUG_LEVEL0, "%s: Failed ECC write " "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i); goto out; } } } } oobofs += mtd->oobsize - hweccbytes * eccsteps; page++; numpages--; /* 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. * Do this also before returning, so the chip is * ready for the next command. */ if (!this->dev_ready) udelay (this->chip_delay); else nand_wait_ready(mtd); /* All done, return happy */ if (!numpages) return 0; /* Check, if the chip supports auto page increment */ if (!NAND_CANAUTOINCR(this)) this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); } /* * Terminate the read command. We come here in case of an error * So we must issue a reset command. */out: this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1); return res;}#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 */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, NULL, 0xff);} /** * nand_read_ecc - [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 * @oob_buf: filesystem supplied oob data buffer * @oobsel: oob selection structure * * This function simply calls nand_do_read_ecc with flags = 0xff */static int nand_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){ return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff);}/** * 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 * @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; u_char ecc_calc[32]; u_char ecc_code[32]; int eccmode, eccsteps; int *oob_config, datidx; int blockcheck = (1 << (this->phys_erase_shift - this->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; } /* Grab the lock and see if the device is available */ if (flags & NAND_GET_DEVICE) nand_get_device (this, mtd, FL_READING); /* use userspace supplied oobinfo, if zero */ if (oobsel == NULL) oobsel = &mtd->oobinfo; /* Autoplace of oob data ? Use the default placement scheme */ if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) oobsel = this->autooob; eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; oob_config = oobsel->eccpos; /* Select the NAND device */ chipnr = (int)(from >> this->chip_shift); this->select_chip(mtd, chipnr); /* 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; ecc = this->eccsize; eccbytes = this->eccbytes; if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) compareecc = 0; oobreadlen = mtd->oobsize; 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 */ 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 */ 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) oob_data = &this->data_buf[end]; eccsteps = this->eccsteps; switch (eccmode) { case NAND_ECC_NONE: { /* No ECC, Read in a page */ static unsigned long lastwhinge = 0; if ((lastwhinge / HZ) != (jiffies / HZ)) { TRACE (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); for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); 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); /* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */ if (!compareecc) goto readoob; /* Pick the ECC bytes out of the oob data */ 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]); /* 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; } 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: /* Walk through the autoplace chunks */ for (i = 0, j = 0; j < mtd->oobavail; i++) { int from = oobsel->oobfree[i][0]; int num = oobsel->oobfree[i][1]; memcpy(&oob_buf[oob], &oob_data[from], num); j+= num; } oob += mtd->oobavail; 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 */ if (!aligned) { for (j = col; j < end && read < len; j++) buf[read++] = data_poi[j]; this->pagebuf = realpage; } else read += mtd->oobblock; /* 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++; 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. */ 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;}/** * nand_read_oob - [MTD Interface] NAND read out-of-band * @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 * * NAND read out-of-band data from the spare area */static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf){ int i, col, page, chipnr; struct nand_chip *this = mtd->priv; int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); /* Shift to get page */ page = (int)(from >> this->page_shift); chipnr = (int)(from >> this->chip_shift); /* Mask to get column */ col = from & (mtd->oobsize - 1); /* Initialize return length value */ *retlen = 0; /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } /* Grab the lock and see if the device is available */ nand_get_device (this, mtd , FL_READING); /* Select the NAND device */ this->select_chip(mtd, chipnr); /* Send the read command */ this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask); /* * Read the data, if we read more than one page * oob data, let the device transfer the data ! */ i = 0; while (i < len) { int thislen = mtd->oobsize - col; thislen = min_t(int, thislen, len); this->read_buf(mtd, &buf[i], thislen); i += thislen; /* 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); /* Read more ? */ if (i < len) { page++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -