📄 nand_base.c
字号:
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; DEBUG(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)) { DEBUG(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)) { DEBUG(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) { sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); len = min(len, readlen); buf = nand_transfer_oob(chip, buf, ops, len); 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); } readlen -= len; if (!readlen) break; /* 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->oobretlen = ops->ooblen; return 0;}/** * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band * @mtd: MTD device structure * @from: offset to read from * @ops: oob operation description structure * * NAND read data and/or out-of-band data */static int nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops){ struct nand_chip *chip = mtd->priv; int ret = -ENOTSUPP; ops->retlen = 0; /* Do not allow reads past end of device */ if (ops->datbuf && (from + ops->len) > mtd->size) { DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " "Attempt read beyond end of device\n"); return -EINVAL; } nand_get_device(chip, mtd, FL_READING); switch(ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: case MTD_OOB_RAW: break; default: goto out; } if (!ops->datbuf) ret = nand_do_read_oob(mtd, from, ops); else ret = nand_do_read_ops(mtd, from, ops); out: nand_release_device(mtd); return ret;}/** * nand_write_page_raw - [Intern] raw page write function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer */static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf){ chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);}/** * nand_write_page_swecc - [REPLACABLE] software ecc based page write function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer */static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf){ int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -