📄 onenand_base.c
字号:
return ONENAND_BBT_READ_FATAL_ERROR; } /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_READING); column = from & (mtd->oobsize - 1); while (read < len) { thislen = mtd->oobsize - column; thislen = min_t(int, thislen, len); this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); onenand_update_bufferram(mtd, from, 0); ret = onenand_bbt_wait(mtd, FL_READING); if (ret) break; this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); read += thislen; if (read == len) break; buf += thislen; /* Read more? */ if (read < len) { /* Update Page size */ from += this->writesize; column = 0; } } /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); ops->oobretlen = read; return ret;}#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE/** * onenand_verify_oob - [GENERIC] verify the oob contents after a write * @param mtd MTD device structure * @param buf the databuffer to verify * @param to offset to read from */static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to){ struct onenand_chip *this = mtd->priv; u_char *oob_buf = this->oob_buf; int status, i; this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); status = this->wait(mtd, FL_READING); if (status) return status; this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); for (i = 0; i < mtd->oobsize; i++) if (buf[i] != 0xFF && buf[i] != oob_buf[i]) return -EBADMSG; return 0;}/** * onenand_verify - [GENERIC] verify the chip contents after a write * @param mtd MTD device structure * @param buf the databuffer to verify * @param addr offset to read from * @param len number of bytes to read and compare */static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len){ struct onenand_chip *this = mtd->priv; void __iomem *dataram; int ret = 0; int thislen, column; while (len != 0) { thislen = min_t(int, this->writesize, len); column = addr & (this->writesize - 1); if (column + thislen > this->writesize) thislen = this->writesize - column; this->command(mtd, ONENAND_CMD_READ, addr, this->writesize); onenand_update_bufferram(mtd, addr, 0); ret = this->wait(mtd, FL_READING); if (ret) return ret; onenand_update_bufferram(mtd, addr, 1); dataram = this->base + ONENAND_DATARAM; dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); if (memcmp(buf, dataram + column, thislen)) return -EBADMSG; len -= thislen; buf += thislen; addr += thislen; } return 0;}#else#define onenand_verify(...) (0)#define onenand_verify_oob(...) (0)#endif#define NOTALIGNED(x) ((x & (mtd->writesize - 1)) != 0)/** * onenand_fill_auto_oob - [Internal] oob auto-placement transfer * @param mtd MTD device structure * @param oob_buf oob buffer * @param buf source address * @param column oob offset to write to * @param thislen oob length to write */static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, const u_char *buf, int column, int thislen){ struct onenand_chip *this = mtd->priv; struct nand_oobfree *free; int writecol = column; int writeend = column + thislen; int lastgap = 0; unsigned int i; free = this->ecclayout->oobfree; for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { if (writecol >= lastgap) writecol += free->offset - lastgap; if (writeend >= lastgap) writeend += free->offset - lastgap; lastgap = free->offset + free->length; } free = this->ecclayout->oobfree; for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { int free_end = free->offset + free->length; if (free->offset < writeend && free_end > writecol) { int st = max_t(int,free->offset,writecol); int ed = min_t(int,free_end,writeend); int n = ed - st; memcpy(oob_buf + st, buf, n); buf += n; } else if (column == 0) break; } return 0;}/** * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band * @param mtd MTD device structure * @param to offset to write to * @param ops oob operation description structure * * Write main and/or oob with ECC */static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops){ struct onenand_chip *this = mtd->priv; int written = 0, column, thislen, subpage; int oobwritten = 0, oobcolumn, thisooblen, oobsize; size_t len = ops->len; size_t ooblen = ops->ooblen; const u_char *buf = ops->datbuf; const u_char *oob = ops->oobbuf; u_char *oobbuf; int ret = 0; MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ ops->retlen = 0; ops->oobretlen = 0; /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n"); return -EINVAL; } /* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); return -EINVAL; } if (ops->mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; else oobsize = mtd->oobsize; oobcolumn = to & (mtd->oobsize - 1); column = to & (mtd->writesize - 1); /* Loop until all data write */ while (written < len) { u_char *wbuf = (u_char *) buf; thislen = min_t(int, mtd->writesize - column, len - written); thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten); this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); /* Partial page write */ subpage = thislen < mtd->writesize; if (subpage) { memset(this->page_buf, 0xff, mtd->writesize); memcpy(this->page_buf + column, buf, thislen); wbuf = this->page_buf; } this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); if (oob) { oobbuf = this->oob_buf; /* We send data to spare ram with oobsize * * to prevent byte access */ memset(oobbuf, 0xff, mtd->oobsize); if (ops->mode == MTD_OOB_AUTO) onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen); else memcpy(oobbuf + oobcolumn, oob, thisooblen); oobwritten += thisooblen; oob += thisooblen; oobcolumn = 0; } else oobbuf = (u_char *) ffchars; this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); ret = this->wait(mtd, FL_WRITING); /* In partial page write we don't update bufferram */ onenand_update_bufferram(mtd, to, !ret && !subpage); if (ONENAND_IS_2PLANE(this)) { ONENAND_SET_BUFFERRAM1(this); onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage); } if (ret) { printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); break; } /* Only check verify write turn on */ ret = onenand_verify(mtd, buf, to, thislen); if (ret) { printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); break; } written += thislen; if (written == len) break; column = 0; to += thislen; buf += thislen; } ops->retlen = written; return ret;}/** * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write * @param retlen pointer to variable to store the number of written bytes * @param buf the data to write * @param mode operation mode * * OneNAND write out-of-band */static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops){ struct onenand_chip *this = mtd->priv; int column, ret = 0, oobsize; int written = 0; u_char *oobbuf; size_t len = ops->ooblen; const u_char *buf = ops->oobbuf; mtd_oob_mode_t mode = ops->mode; to += ops->ooboffs; MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ ops->oobretlen = 0; if (mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; else oobsize = mtd->oobsize; column = to & (mtd->oobsize - 1); if (unlikely(column >= oobsize)) { printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n"); return -EINVAL; } /* For compatibility with NAND: Do not allow write past end of page */ if (unlikely(column + len > oobsize)) { printk(KERN_ERR "onenand_write_oob_nolock: " "Attempt to write past end of page\n"); return -EINVAL; } /* Do not allow reads past end of device */ if (unlikely(to >= mtd->size || column + len > ((mtd->size >> this->page_shift) - (to >> this->page_shift)) * oobsize)) { printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n"); return -EINVAL; } oobbuf = this->oob_buf; /* Loop until all data write */ while (written < len) { int thislen = min_t(int, oobsize, len - written); this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); /* We send data to spare ram with oobsize * to prevent byte access */ memset(oobbuf, 0xff, mtd->oobsize); if (mode == MTD_OOB_AUTO) onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen); else memcpy(oobbuf + column, buf, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); if (ONENAND_IS_2PLANE(this)) { ONENAND_SET_BUFFERRAM1(this); onenand_update_bufferram(mtd, to + this->writesize, 0); } ret = this->wait(mtd, FL_WRITING); if (ret) { printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret); break; } ret = onenand_verify_oob(mtd, oobbuf, to); if (ret) { printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret); break; } written += thislen; if (written == len) break; to += mtd->writesize; buf += thislen; column = 0; } ops->oobretlen = written; return ret;}/** * onenand_write - [MTD Interface] compability function for onenand_write_ecc * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write * @param retlen pointer to variable to store the number of written bytes * @param buf the data to write * * Write with ECC */int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf){ struct mtd_oob_ops ops = { .len = len, .ooblen = 0, .datbuf = (u_char *) buf, .oobbuf = NULL, }; int ret; onenand_get_device(mtd, FL_WRITING); ret = onenand_write_ops_nolock(mtd, to, &ops); onenand_release_device(mtd); *retlen = ops.retlen; return ret;}/** * onenand_write_oob - [MTD Interface] OneNAND write out-of-band * @param mtd MTD device structure * @param to offset to write to * @param ops oob operation description structure * * OneNAND write main and/or out-of-band */int onenand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops){ int ret; switch (ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: break; case MTD_OOB_RAW: /* Not implemented yet */ default: return -EINVAL; } onenand_get_device(mtd, FL_WRITING); if (ops->datbuf) ret = onenand_write_ops_nolock(mtd, to, ops); else ret = onenand_write_oob_nolock(mtd, to, ops); onenand_release_device(mtd); return ret;}/** * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad * @param mtd MTD device structure * @param ofs offset from device start * @param allowbbt 1, if its allowed to access the bbt area * * Check, if the block is bad, Either by reading the bad block table or
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -