📄 nand_base.c
字号:
/** * 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 * * Not for syndrome calculating ecc controllers which need a special oob layout */static int nand_read_page_hwecc(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 *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->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_poi, mtd->oobsize); for (i = 0; i < chip->ecc.total; i++) ecc_code[i] = chip->oob_poi[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_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 handling. */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_poi; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; chip->ecc.hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); if (chip->ecc.prepad) { chip->read_buf(mtd, oob, chip->ecc.prepad); oob += chip->ecc.prepad; } chip->ecc.hwctl(mtd, NAND_ECC_READSYN); chip->read_buf(mtd, oob, eccbytes); stat = chip->ecc.correct(mtd, p, oob, NULL); if (stat < 0) mtd->ecc_stats.failed++; else mtd->ecc_stats.corrected += stat; oob += eccbytes; if (chip->ecc.postpad) { chip->read_buf(mtd, oob, chip->ecc.postpad); oob += chip->ecc.postpad; } } /* Calculate remaining oob bytes */ i = mtd->oobsize - (oob - chip->oob_poi); if (i) chip->read_buf(mtd, oob, i); return 0;}/** * nand_transfer_oob - [Internal] Transfer oob to client buffer * @chip: nand chip structure * @oob: oob destination address * @ops: oob ops structure * @len: size of oob to transfer */static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, struct mtd_oob_ops *ops, size_t len){ switch(ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_RAW: memcpy(oob, chip->oob_poi + ops->ooboffs, len); return oob + len; case MTD_OOB_AUTO: { struct nand_oobfree *free = chip->ecc.layout->oobfree; uint32_t boffs = 0, roffs = ops->ooboffs; size_t bytes = 0; for(; free->length && len; free++, len -= bytes) { /* Read request not from offset 0 ? */ if (unlikely(roffs)) { if (roffs >= free->length) { roffs -= free->length; continue; } boffs = free->offset + roffs; bytes = min_t(size_t, len, (free->length - roffs)); roffs = 0; } else { bytes = min_t(size_t, len, free->length); boffs = free->offset; } memcpy(oob, chip->oob_poi + boffs, bytes); oob += bytes; } return oob; } default: BUG(); } return NULL;}/** * nand_do_read_ops - [Internal] Read data with ECC * * @mtd: MTD device structure * @from: offset to read from * @ops: oob ops structure * * Internal function. Called with chip held. */static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops){ 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 = ops->len; uint32_t oobreadlen = ops->ooblen; uint8_t *bufpoi, *oob, *buf; stats = mtd->ecc_stats; chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; col = (int)(from & (mtd->writesize - 1)); buf = ops->datbuf; oob = ops->oobbuf; while(1) { bytes = min(mtd->writesize - col, readlen); aligned = (bytes == mtd->writesize); /* Is the current page in the buffer ? */ if (realpage != chip->pagebuf || oob) { bufpoi = aligned ? buf : chip->buffers->databuf; if (likely(sndcmd)) { chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); sndcmd = 0; } /* Now read the page into the buffer */ if (unlikely(ops->mode == MTD_OOB_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); else ret = chip->ecc.read_page(mtd, chip, bufpoi); if (ret < 0) break; /* Transfer not aligned data */ if (!aligned) { if (!NAND_SUBPAGE_READ(chip) && !oob) chip->pagebuf = realpage; memcpy(buf, chip->buffers->databuf + col, bytes); } buf += bytes; if (unlikely(oob)) { /* Raw mode does data:oob:data:oob */ if (ops->mode != MTD_OOB_RAW) { int toread = min(oobreadlen, chip->ecc.layout->oobavail); if (toread) { oob = nand_transfer_oob(chip, oob, ops, toread); oobreadlen -= toread; } } else buf = nand_transfer_oob(chip, buf, ops, mtd->oobsize); } 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->buffers->databuf + col, bytes); buf += bytes; } readlen -= bytes; if (!readlen) break; /* For subsequent reads align to page boundary. */ col = 0; /* Increment page address */ realpage++; page = realpage & chip->pagemask; /* Check, if we cross a chip boundary */ if (!page) { chipnr++; 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 & blkcheck)) sndcmd = 1; } ops->retlen = ops->len - (size_t) readlen; if (oob) ops->oobretlen = ops->ooblen - oobreadlen; if (ret) return ret; if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 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){ struct nand_chip *chip = mtd->priv; int ret; /* Do not allow reads past end of device */ if ((from + len) > mtd->size) return -EINVAL; if (!len) return 0; nand_get_device(chip, mtd, FL_READING); chip->ops.len = len; chip->ops.datbuf = buf; chip->ops.oobbuf = NULL; ret = nand_do_read_ops(mtd, from, &chip->ops); *retlen = chip->ops.retlen; nand_release_device(mtd); return ret;}/** * nand_read_oob_std - [REPLACABLE] the most common OOB data read function * @mtd: mtd info structure * @chip: nand chip info structure * @page: page number to read * @sndcmd: flag whether to issue read command or not */static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd){ if (sndcmd) { chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); sndcmd = 0; } chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); return sndcmd;}/** * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC * with syndromes * @mtd: mtd info structure * @chip: nand chip info structure * @page: page number to read * @sndcmd: flag whether to issue read command or not */static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd){ uint8_t *buf = chip->oob_poi; int length = mtd->oobsize; int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; int eccsize = chip->ecc.size; uint8_t *bufpoi = buf; int i, toread, sndrnd = 0, pos; chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); for (i = 0; i < chip->ecc.steps; i++) { if (sndrnd) { pos = eccsize + i * (eccsize + chunk); if (mtd->writesize > 512) chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1); else chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page); } else sndrnd = 1; toread = min_t(int, length, chunk); chip->read_buf(mtd, bufpoi, toread); bufpoi += toread; length -= toread; } if (length > 0) chip->read_buf(mtd, bufpoi, length); return 1;}/** * nand_write_oob_std - [REPLACABLE] the most common OOB data write function * @mtd: mtd info structure * @chip: nand chip info structure * @page: page number to write */static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page){ int status = 0; const uint8_t *buf = chip->oob_poi; int length = mtd->oobsize; chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); chip->write_buf(mtd, buf, length); /* Send command to program the OOB data */ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); return status & NAND_STATUS_FAIL ? -EIO : 0;}/** * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC * with syndrome - only for large page flash ! * @mtd: mtd info structure * @chip: nand chip info structure * @page: page number to write */static int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page){ int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; int eccsize = chip->ecc.size, length = mtd->oobsize; int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps; const uint8_t *bufpoi = chip->oob_poi; /* * data-ecc-data-ecc ... ecc-oob * or * data-pad-ecc-pad-data-pad .... ecc-pad-oob */ if (!chip->ecc.prepad && !chip->ecc.postpad) { pos = steps * (eccsize + chunk); steps = 0; } else pos = eccsize; chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); for (i = 0; i < steps; i++) { if (sndcmd) { if (mtd->writesize <= 512) { uint32_t fill = 0xFFFFFFFF; len = eccsize; while (len > 0) { int num = min_t(int, len, 4); chip->write_buf(mtd, (uint8_t *)&fill, num); len -= num; } } else { pos = eccsize + i * (eccsize + chunk); chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1); } } else sndcmd = 1; len = min_t(int, length, chunk); chip->write_buf(mtd, bufpoi, len); bufpoi += len; length -= len; } if (length > 0) chip->write_buf(mtd, bufpoi, length); chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); return status & NAND_STATUS_FAIL ? -EIO : 0;}/** * nand_do_read_oob - [Intern] NAND read out-of-band * @mtd: MTD device structure * @from: offset to read from * @ops: oob operations description structure * * NAND read out-of-band data from the spare area */static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops){ int page, realpage, chipnr, sndcmd = 1; struct nand_chip *chip = mtd->priv; int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; int readlen = ops->ooblen; int len; uint8_t *buf = ops->oobbuf; MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", (unsigned long long)from, readlen); if (ops->mode == MTD_OOB_AUTO) len = chip->ecc.layout->oobavail; else len = mtd->oobsize; if (unlikely(ops->ooboffs >= len)) { MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " "Attempt to start read outside oob\n"); return -EINVAL; } /* Do not allow reads past end of device */ if (unlikely(from >= mtd->size || ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - (from >> chip->page_shift)) * len)) { MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " "Attempt read beyond end of device\n"); return -EINVAL; } chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); /* Shift to get page */ realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; while(1) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -