[PATCH 08/12] nand: factor out nand write support
Sascha Hauer
s.hauer at pengutronix.de
Mon Apr 4 10:46:40 EDT 2011
In order to make it optional later.
Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
drivers/mtd/nand/Makefile | 2 +-
drivers/mtd/nand/nand.h | 17 +
drivers/mtd/nand/nand_base.c | 718 +---------------------------------------
drivers/mtd/nand/nand_write.c | 746 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 766 insertions(+), 717 deletions(-)
create mode 100644 drivers/mtd/nand/nand_write.c
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 89e18d9..05f0485 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -1,6 +1,6 @@
# Generic NAND options
-obj-$(CONFIG_NAND) += nand.o nand_ecc.o nand_hwecc.o nand_swecc.o nand_hwecc_syndrome.o
+obj-$(CONFIG_NAND) += nand.o nand_ecc.o nand_hwecc.o nand_swecc.o nand_hwecc_syndrome.o nand_write.o
obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
obj-$(CONFIG_NAND) += nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/nand.h b/drivers/mtd/nand/nand.h
index db1a2a5..24cc5a2 100644
--- a/drivers/mtd/nand/nand.h
+++ b/drivers/mtd/nand/nand.h
@@ -7,6 +7,23 @@ int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
int page, int sndcmd);
int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
int page);
+int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs);
+int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
+ int allowbbt);
+int nand_block_isbad(struct mtd_info *mtd, loff_t offs);
+void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len);
+void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len);
+void single_erase_cmd(struct mtd_info *mtd, int page);
+void multi_erase_cmd(struct mtd_info *mtd, int page);
+void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf);
+int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int page, int cached, int raw);
+int nand_erase(struct mtd_info *mtd, struct erase_info *instr);
+int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const uint8_t *buf);
+int nand_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops);
void nand_init_ecc_hw(struct nand_chip *chip);
void nand_init_ecc_soft(struct nand_chip *chip);
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 7ffafe1..f2150b8 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -77,9 +77,6 @@ static struct nand_ecclayout nand_oob_64 = {
.length = 38}}
};
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops);
-
#define DEFINE_LED_TRIGGER(x)
#define DEFINE_LED_TRIGGER_GLOBAL(x)
#define led_trigger_register_simple(x, y) do {} while(0)
@@ -153,23 +150,6 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr)
}
/**
- * nand_write_buf - [DEFAULT] write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- *
- * Default write function for 8bit buswith
- */
-static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
- int i;
- struct nand_chip *chip = mtd->priv;
-
- for (i = 0; i < len; i++)
- writeb(buf[i], chip->IO_ADDR_W);
-}
-
-/**
* nand_read_buf - [DEFAULT] read chip data into buffer
* @mtd: MTD device structure
* @buf: buffer to store date
@@ -206,26 +186,6 @@ static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
}
/**
- * nand_write_buf16 - [DEFAULT] write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- *
- * Default write function for 16bit buswith
- */
-static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
- int i;
- struct nand_chip *chip = mtd->priv;
- u16 *p = (u16 *) buf;
- len >>= 1;
-
- for (i = 0; i < len; i++)
- writew(p[i], chip->IO_ADDR_W);
-
-}
-
-/**
* nand_read_buf16 - [DEFAULT] read chip data into buffer
* @mtd: MTD device structure
* @buf: buffer to store date
@@ -307,61 +267,6 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
}
/**
- * nand_default_block_markbad - [DEFAULT] mark a block bad
- * @mtd: MTD device structure
- * @ofs: offset from device start
- *
- * This is the default implementation, which can be overridden by
- * a hardware specific driver.
-*/
-static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
-{
- struct nand_chip *chip = mtd->priv;
- uint8_t buf[2] = { 0, 0 };
- int block, ret;
-
- /* Get block number */
- block = (int)(ofs >> chip->bbt_erase_shift);
- if (chip->bbt)
- chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
-
- /* Do we have a flash based bad block table ? */
- if (chip->options & NAND_USE_FLASH_BBT)
- ret = nand_update_bbt(mtd, ofs);
- else {
- /* We write two bytes, so we dont have to mess with 16 bit
- * access
- */
- ofs += mtd->oobsize;
- chip->ops.len = chip->ops.ooblen = 2;
- chip->ops.datbuf = NULL;
- chip->ops.oobbuf = buf;
- chip->ops.ooboffs = chip->badblockpos & ~0x01;
-
- ret = nand_do_write_oob(mtd, ofs, &chip->ops);
- }
- if (!ret)
- mtd->ecc_stats.badblocks++;
-
- return ret;
-}
-
-/**
- * nand_check_wp - [GENERIC] check if the chip is write protected
- * @mtd: MTD device structure
- * Check, if the device is write protected
- *
- * The function expects, that the device is already selected
- */
-static int nand_check_wp(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd->priv;
- /* Check the WP bit */
- chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
- return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
-}
-
-/**
* nand_block_checkbad - [GENERIC] Check if a block is marked bad
* @mtd: MTD device structure
* @ofs: offset from device start
@@ -371,7 +276,7 @@ static int nand_check_wp(struct mtd_info *mtd)
* Check, if the block is bad. Either by reading the bad block table or
* calling of the scan function.
*/
-static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
+int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
int allowbbt)
{
struct nand_chip *chip = mtd->priv;
@@ -921,29 +826,6 @@ int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * 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
- */
-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_do_read_oob - [Intern] NAND read out-of-band
* @mtd: MTD device structure
* @from: offset to read from
@@ -1077,608 +959,12 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
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 - [REPLACEABLE] write one page
- * @mtd: MTD device structure
- * @chip: NAND chip descriptor
- * @buf: the data to write
- * @page: page number to write
- * @cached: cached programming
- * @raw: use _raw version of write_page
- */
-static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int page, int cached, int raw)
-{
- int status;
-
- chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
-
- if (unlikely(raw))
- chip->ecc.write_page_raw(mtd, chip, buf);
- else
- chip->ecc.write_page(mtd, chip, buf);
-
- /*
- * Cached progamming disabled for now, Not sure if its worth the
- * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
- */
- cached = 0;
-
- if (!cached || !(chip->options & NAND_CACHEPRG)) {
-
- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->waitfunc(mtd, chip);
- /*
- * See if operation failed and additional status checks are
- * available
- */
- if ((status & NAND_STATUS_FAIL) && (chip->errstat))
- status = chip->errstat(mtd, chip, FL_WRITING, status,
- page);
-
- if (status & NAND_STATUS_FAIL) {
- return -EIO;
- }
- } else {
- chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
- status = chip->waitfunc(mtd, chip);
- }
-
-#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
- /* Send command to read back the data */
- chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
-
- if (chip->verify_buf(mtd, buf, mtd->writesize))
- return -EIO;
-#endif
- return 0;
-}
-
-/**
- * nand_fill_oob - [Internal] Transfer client buffer to oob
- * @chip: nand chip structure
- * @oob: oob data buffer
- * @ops: oob ops structure
- */
-static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
- struct mtd_oob_ops *ops)
-{
- size_t len = ops->ooblen;
-
- switch(ops->mode) {
-
- case MTD_OOB_PLACE:
- case MTD_OOB_RAW:
- memcpy(chip->oob_poi + ops->ooboffs, oob, len);
- return oob + len;
-
- case MTD_OOB_AUTO: {
- struct nand_oobfree *free = chip->ecc.layout->oobfree;
- uint32_t boffs = 0, woffs = ops->ooboffs;
- size_t bytes = 0;
-
- for(; free->length && len; free++, len -= bytes) {
- /* Write request not from offset 0 ? */
- if (unlikely(woffs)) {
- if (woffs >= free->length) {
- woffs -= free->length;
- continue;
- }
- boffs = free->offset + woffs;
- bytes = min_t(size_t, len,
- (free->length - woffs));
- woffs = 0;
- } else {
- bytes = min_t(size_t, len, free->length);
- boffs = free->offset;
- }
- memcpy(chip->oob_poi + boffs, oob, bytes);
- oob += bytes;
- }
- return oob;
- }
- default:
- BUG();
- }
- return NULL;
-}
-
-#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0
-
-/**
- * nand_do_write_ops - [Internal] NAND write with ECC
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operations description structure
- *
- * NAND write with ECC
- */
-static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops)
-{
- int chipnr, realpage, page, blockmask, column;
- struct nand_chip *chip = mtd->priv;
- uint32_t writelen = ops->len;
- uint8_t *oob = ops->oobbuf;
- uint8_t *buf = ops->datbuf;
- int ret, subpage;
-
- ops->retlen = 0;
- if (!writelen)
- return 0;
-
- /* reject writes, which are not page aligned */
- if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
- printk(KERN_NOTICE "nand_write: "
- "Attempt to write not page aligned data\n");
- return -EINVAL;
- }
-
- column = to & (mtd->writesize - 1);
- subpage = column || (writelen & (mtd->writesize - 1));
-
- if (subpage && oob)
- return -EINVAL;
-
- chipnr = (int)(to >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
-
- /* Check, if it is write protected */
- if (nand_check_wp(mtd)) {
- return -EIO;
- }
-
- realpage = (int)(to >> chip->page_shift);
- page = realpage & chip->pagemask;
- blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
-
- /* Invalidate the page cache, when we write to the cached page */
- if (to <= (chip->pagebuf << chip->page_shift) &&
- (chip->pagebuf << chip->page_shift) < (to + ops->len))
- chip->pagebuf = -1;
-
- /* If we're not given explicit OOB data, let it be 0xFF */
- if (likely(!oob))
- memset(chip->oob_poi, 0xff, mtd->oobsize);
-
- while(1) {
- int bytes = mtd->writesize;
- int cached = writelen > bytes && page != blockmask;
- uint8_t *wbuf = buf;
-
- /* Partial page write ? */
- if (unlikely(column || writelen < (mtd->writesize - 1))) {
- cached = 0;
- bytes = min_t(int, bytes - column, (int) writelen);
- chip->pagebuf = -1;
- memset(chip->buffers->databuf, 0xff, mtd->writesize);
- memcpy(&chip->buffers->databuf[column], buf, bytes);
- wbuf = chip->buffers->databuf;
- }
-
- if (unlikely(oob))
- oob = nand_fill_oob(chip, oob, ops);
-
- ret = chip->write_page(mtd, chip, wbuf, page, cached,
- (ops->mode == MTD_OOB_RAW));
- if (ret)
- break;
-
- writelen -= bytes;
- if (!writelen)
- break;
-
- column = 0;
- buf += bytes;
- 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);
- }
- }
-
- ops->retlen = ops->len - writelen;
- if (unlikely(oob))
- ops->oobretlen = ops->ooblen;
- return ret;
-}
-
-/**
- * nand_write - [MTD Interface] NAND write with ECC
- * @mtd: MTD device structure
- * @to: offset to write to
- * @len: number of bytes to write
- * @retlen: pointer to variable to store the number of written bytes
- * @buf: the data to write
- *
- * NAND write with ECC
- */
-static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const uint8_t *buf)
-{
- struct nand_chip *chip = mtd->priv;
- int ret;
-
- /* Do not allow reads past end of device */
- if ((to + len) > mtd->size)
- return -EINVAL;
- if (!len)
- return 0;
-
- chip->ops.len = len;
- chip->ops.datbuf = (uint8_t *)buf;
- chip->ops.oobbuf = NULL;
-
- ret = nand_do_write_ops(mtd, to, &chip->ops);
-
- *retlen = chip->ops.retlen;
-
- return ret;
-}
-
-/**
- * nand_do_write_oob - [MTD Interface] NAND write out-of-band
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operation description structure
- *
- * NAND write out-of-band
- */
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops)
-{
- int chipnr, page, status, len;
- struct nand_chip *chip = mtd->priv;
-
- MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
- (unsigned int)to, (int)ops->ooblen);
-
- if (ops->mode == MTD_OOB_AUTO)
- len = chip->ecc.layout->oobavail;
- else
- len = mtd->oobsize;
-
- /* Do not allow write past end of page */
- if ((ops->ooboffs + ops->ooblen) > len) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
- "Attempt to write past end of page\n");
- return -EINVAL;
- }
-
- if (unlikely(ops->ooboffs >= len)) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
- "Attempt to start write outside oob\n");
- return -EINVAL;
- }
-
- /* Do not allow reads past end of device */
- if (unlikely(to >= mtd->size ||
- ops->ooboffs + ops->ooblen >
- ((mtd->size >> chip->page_shift) -
- (to >> chip->page_shift)) * len)) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
- "Attempt write beyond end of device\n");
- return -EINVAL;
- }
-
- chipnr = (int)(to >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
-
- /* Shift to get page */
- page = (int)(to >> chip->page_shift);
-
- /*
- * Reset the chip. Some chips (like the Toshiba TC5832DC found in one
- * of my DiskOnChip 2000 test units) will clear the whole data page too
- * if we don't do this. I have no clue why, but I seem to have 'fixed'
- * it in the doc2000 driver in August 1999. dwmw2.
- */
- chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
-
- /* Check, if it is write protected */
- if (nand_check_wp(mtd))
- return -EROFS;
-
- /* Invalidate the page cache, if we write to the cached page */
- if (page == chip->pagebuf)
- chip->pagebuf = -1;
-
- memset(chip->oob_poi, 0xff, mtd->oobsize);
- nand_fill_oob(chip, ops->oobbuf, ops);
- status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
- memset(chip->oob_poi, 0xff, mtd->oobsize);
-
- if (status)
- return status;
-
- ops->oobretlen = ops->ooblen;
-
- return 0;
-}
-
-/**
- * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operation description structure
- */
-static int nand_write_oob(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops)
-{
- int ret = -ENOSYS;
-
- ops->retlen = 0;
-
- /* Do not allow writes past end of device */
- if (ops->datbuf && (to + ops->len) > mtd->size) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
- "Attempt read beyond end of device\n");
- return -EINVAL;
- }
-
- 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_write_oob(mtd, to, ops);
- else
- ret = nand_do_write_ops(mtd, to, ops);
-
- out:
- return ret;
-}
-
-/**
- * single_erease_cmd - [GENERIC] NAND standard block erase command function
- * @mtd: MTD device structure
- * @page: the page address of the block which will be erased
- *
- * Standard erase command for NAND chips
- */
-static void single_erase_cmd(struct mtd_info *mtd, int page)
-{
- struct nand_chip *chip = mtd->priv;
- /* Send commands to erase a block */
- chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
- chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
-}
-
-/**
- * multi_erease_cmd - [GENERIC] AND specific block erase command function
- * @mtd: MTD device structure
- * @page: the page address of the block which will be erased
- *
- * AND multi block erase command function
- * Erase 4 consecutive blocks
- */
-static void multi_erase_cmd(struct mtd_info *mtd, int page)
-{
- struct nand_chip *chip = mtd->priv;
- /* Send commands to erase a block */
- chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
- chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
- chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
- chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
- chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
-}
-
-/**
- * nand_erase - [MTD Interface] erase block(s)
- * @mtd: MTD device structure
- * @instr: erase instruction
- *
- * Erase one ore more blocks
- */
-static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
-{
- return nand_erase_nand(mtd, instr, 0);
-}
-
-#define BBT_PAGE_MASK 0xffffff3f
-/**
- * nand_erase_nand - [Internal] erase block(s)
- * @mtd: MTD device structure
- * @instr: erase instruction
- * @allowbbt: allow erasing the bbt area
- *
- * Erase one ore more blocks
- */
-int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
- int allowbbt)
-{
- int page, len, status, pages_per_block, ret, chipnr;
- struct nand_chip *chip = mtd->priv;
- int rewrite_bbt[NAND_MAX_CHIPS]={0};
- unsigned int bbt_masked_page = 0xffffffff;
-
- MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n",
- (unsigned int)instr->addr, (unsigned int)instr->len);
-
- /* Start address must align on block boundary */
- if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
- return -EINVAL;
- }
-
- /* Length must align on block boundary */
- if (instr->len & ((1 << chip->phys_erase_shift) - 1)) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
- "Length not block aligned\n");
- return -EINVAL;
- }
-
- /* Do not allow erase past end of device */
- if ((instr->len + instr->addr) > mtd->size) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
- "Erase past end of device\n");
- return -EINVAL;
- }
-
- instr->fail_addr = 0xffffffff;
-
- /* Shift to get first page */
- page = (int)(instr->addr >> chip->page_shift);
- chipnr = (int)(instr->addr >> chip->chip_shift);
-
- /* Calculate pages in each block */
- pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
-
- /* Select the NAND device */
- chip->select_chip(mtd, chipnr);
-
- /* Check, if it is write protected */
- if (nand_check_wp(mtd)) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
- "Device is write protected!!!\n");
- instr->state = MTD_ERASE_FAILED;
- goto erase_exit;
- }
-
- /*
- * If BBT requires refresh, set the BBT page mask to see if the BBT
- * should be rewritten. Otherwise the mask is set to 0xffffffff which
- * can not be matched. This is also done when the bbt is actually
- * erased to avoid recusrsive updates
- */
- if (chip->options & BBT_AUTO_REFRESH && !allowbbt)
- bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
-
- /* Loop through the pages */
- len = instr->len;
-
- instr->state = MTD_ERASING;
-
- while (len) {
- /*
- * heck if we have a bad block, we do not erase bad blocks !
- */
- if (nand_block_checkbad(mtd, ((loff_t) page) <<
- chip->page_shift, 0, allowbbt)) {
- printk(KERN_WARNING "nand_erase: attempt to erase a "
- "bad block at page 0x%08x\n", page);
- instr->state = MTD_ERASE_FAILED;
- goto erase_exit;
- }
-
- /*
- * Invalidate the page cache, if we erase the block which
- * contains the current cached page
- */
- if (page <= chip->pagebuf && chip->pagebuf <
- (page + pages_per_block))
- chip->pagebuf = -1;
-
- chip->erase_cmd(mtd, page & chip->pagemask);
-
- status = chip->waitfunc(mtd, chip);
-
- /*
- * See if operation failed and additional status checks are
- * available
- */
- if ((status & NAND_STATUS_FAIL) && (chip->errstat))
- status = chip->errstat(mtd, chip, FL_ERASING,
- status, page);
-
- /* See if block erase succeeded */
- if (status & NAND_STATUS_FAIL) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
- "Failed erase, page 0x%08x\n", page);
- instr->state = MTD_ERASE_FAILED;
- instr->fail_addr = (page << chip->page_shift);
- goto erase_exit;
- }
-
- /*
- * If BBT requires refresh, set the BBT rewrite flag to the
- * page being erased
- */
- if (bbt_masked_page != 0xffffffff &&
- (page & BBT_PAGE_MASK) == bbt_masked_page)
- rewrite_bbt[chipnr] = (page << chip->page_shift);
-
- /* Increment page address and decrement length */
- len -= (1 << chip->phys_erase_shift);
- page += pages_per_block;
-
- /* Check, if we cross a chip boundary */
- if (len && !(page & chip->pagemask)) {
- chipnr++;
- chip->select_chip(mtd, -1);
- chip->select_chip(mtd, chipnr);
-
- /*
- * If BBT requires refresh and BBT-PERCHIP, set the BBT
- * page mask to see if this BBT should be rewritten
- */
- if (bbt_masked_page != 0xffffffff &&
- (chip->bbt_td->options & NAND_BBT_PERCHIP))
- bbt_masked_page = chip->bbt_td->pages[chipnr] &
- BBT_PAGE_MASK;
- }
- }
- instr->state = MTD_ERASE_DONE;
-
- erase_exit:
-
- ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
-
- /* Do call back function */
- if (!ret)
- mtd_erase_callback(instr);
-
- /*
- * If BBT requires refresh and erase was successful, rewrite any
- * selected bad block tables
- */
- if (bbt_masked_page == 0xffffffff || ret)
- return ret;
-
- for (chipnr = 0; chipnr < chip->numchips; chipnr++) {
- if (!rewrite_bbt[chipnr])
- continue;
- /* update the BBT for chip */
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt "
- "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr],
- chip->bbt_td->pages[chipnr]);
- nand_update_bbt(mtd, rewrite_bbt[chipnr]);
- }
-
- /* Return more or less happy */
- return ret;
-}
-
/**
* nand_block_isbad - [MTD Interface] Check if block at offset is bad
* @mtd: MTD device structure
* @offs: offset relative to mtd start
*/
-static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
+int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
{
/* Check for invalid offset */
if (offs > mtd->size)
diff --git a/drivers/mtd/nand/nand_write.c b/drivers/mtd/nand/nand_write.c
new file mode 100644
index 0000000..89dc47b
--- /dev/null
+++ b/drivers/mtd/nand/nand_write.c
@@ -0,0 +1,746 @@
+#include <common.h>
+#include <errno.h>
+#include <clock.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/err.h>
+#include <linux/mtd/nand_ecc.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <module.h>
+
+#include "nand.h"
+
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops);
+
+/**
+ * nand_write_buf - [DEFAULT] write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ *
+ * Default write function for 8bit buswith
+ */
+void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *chip = mtd->priv;
+
+ for (i = 0; i < len; i++)
+ writeb(buf[i], chip->IO_ADDR_W);
+}
+
+/**
+ * nand_write_buf16 - [DEFAULT] write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ *
+ * Default write function for 16bit buswith
+ */
+void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *chip = mtd->priv;
+ u16 *p = (u16 *) buf;
+ len >>= 1;
+
+ for (i = 0; i < len; i++)
+ writew(p[i], chip->IO_ADDR_W);
+
+}
+
+/**
+ * nand_default_block_markbad - [DEFAULT] mark a block bad
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ *
+ * This is the default implementation, which can be overridden by
+ * a hardware specific driver.
+ */
+int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd->priv;
+ uint8_t buf[2] = { 0, 0 };
+ int block, ret;
+
+ /* Get block number */
+ block = (int)(ofs >> chip->bbt_erase_shift);
+ if (chip->bbt)
+ chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+ /* Do we have a flash based bad block table ? */
+ if (chip->options & NAND_USE_FLASH_BBT)
+ ret = nand_update_bbt(mtd, ofs);
+ else {
+ /* We write two bytes, so we dont have to mess with 16 bit
+ * access
+ */
+ ofs += mtd->oobsize;
+ chip->ops.len = chip->ops.ooblen = 2;
+ chip->ops.datbuf = NULL;
+ chip->ops.oobbuf = buf;
+ chip->ops.ooboffs = chip->badblockpos & ~0x01;
+
+ ret = nand_do_write_oob(mtd, ofs, &chip->ops);
+ }
+ if (!ret)
+ mtd->ecc_stats.badblocks++;
+
+ return ret;
+}
+
+/**
+ * nand_check_wp - [GENERIC] check if the chip is write protected
+ * @mtd: MTD device structure
+ * Check, if the device is write protected
+ *
+ * The function expects, that the device is already selected
+ */
+static int nand_check_wp(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ /* Check the WP bit */
+ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+ return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 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
+ */
+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_page_raw - [Intern] raw page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ */
+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 - [REPLACEABLE] write one page
+ * @mtd: MTD device structure
+ * @chip: NAND chip descriptor
+ * @buf: the data to write
+ * @page: page number to write
+ * @cached: cached programming
+ * @raw: use _raw version of write_page
+ */
+int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int page, int cached, int raw)
+{
+ int status;
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+
+ if (unlikely(raw))
+ chip->ecc.write_page_raw(mtd, chip, buf);
+ else
+ chip->ecc.write_page(mtd, chip, buf);
+
+ /*
+ * Cached progamming disabled for now, Not sure if its worth the
+ * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
+ */
+ cached = 0;
+
+ if (!cached || !(chip->options & NAND_CACHEPRG)) {
+
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+ /*
+ * See if operation failed and additional status checks are
+ * available
+ */
+ if ((status & NAND_STATUS_FAIL) && (chip->errstat))
+ status = chip->errstat(mtd, chip, FL_WRITING, status,
+ page);
+
+ if (status & NAND_STATUS_FAIL) {
+ return -EIO;
+ }
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+ }
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+ /* Send command to read back the data */
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+ if (chip->verify_buf(mtd, buf, mtd->writesize))
+ return -EIO;
+#endif
+ return 0;
+}
+
+/**
+ * nand_fill_oob - [Internal] Transfer client buffer to oob
+ * @chip: nand chip structure
+ * @oob: oob data buffer
+ * @ops: oob ops structure
+ */
+static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
+ struct mtd_oob_ops *ops)
+{
+ size_t len = ops->ooblen;
+
+ switch(ops->mode) {
+
+ case MTD_OOB_PLACE:
+ case MTD_OOB_RAW:
+ memcpy(chip->oob_poi + ops->ooboffs, oob, len);
+ return oob + len;
+
+ case MTD_OOB_AUTO: {
+ struct nand_oobfree *free = chip->ecc.layout->oobfree;
+ uint32_t boffs = 0, woffs = ops->ooboffs;
+ size_t bytes = 0;
+
+ for(; free->length && len; free++, len -= bytes) {
+ /* Write request not from offset 0 ? */
+ if (unlikely(woffs)) {
+ if (woffs >= free->length) {
+ woffs -= free->length;
+ continue;
+ }
+ boffs = free->offset + woffs;
+ bytes = min_t(size_t, len,
+ (free->length - woffs));
+ woffs = 0;
+ } else {
+ bytes = min_t(size_t, len, free->length);
+ boffs = free->offset;
+ }
+ memcpy(chip->oob_poi + boffs, oob, bytes);
+ oob += bytes;
+ }
+ return oob;
+ }
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0
+
+/**
+ * nand_do_write_ops - [Internal] NAND write with ECC
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operations description structure
+ *
+ * NAND write with ECC
+ */
+int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int chipnr, realpage, page, blockmask, column;
+ struct nand_chip *chip = mtd->priv;
+ uint32_t writelen = ops->len;
+ uint8_t *oob = ops->oobbuf;
+ uint8_t *buf = ops->datbuf;
+ int ret, subpage;
+
+ ops->retlen = 0;
+ if (!writelen)
+ return 0;
+
+ /* reject writes, which are not page aligned */
+ if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
+ printk(KERN_NOTICE "nand_write: "
+ "Attempt to write not page aligned data\n");
+ return -EINVAL;
+ }
+
+ column = to & (mtd->writesize - 1);
+ subpage = column || (writelen & (mtd->writesize - 1));
+
+ if (subpage && oob)
+ return -EINVAL;
+
+ chipnr = (int)(to >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ /* Check, if it is write protected */
+ if (nand_check_wp(mtd)) {
+ return -EIO;
+ }
+
+ realpage = (int)(to >> chip->page_shift);
+ page = realpage & chip->pagemask;
+ blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+
+ /* Invalidate the page cache, when we write to the cached page */
+ if (to <= (chip->pagebuf << chip->page_shift) &&
+ (chip->pagebuf << chip->page_shift) < (to + ops->len))
+ chip->pagebuf = -1;
+
+ /* If we're not given explicit OOB data, let it be 0xFF */
+ if (likely(!oob))
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+
+ while(1) {
+ int bytes = mtd->writesize;
+ int cached = writelen > bytes && page != blockmask;
+ uint8_t *wbuf = buf;
+
+ /* Partial page write ? */
+ if (unlikely(column || writelen < (mtd->writesize - 1))) {
+ cached = 0;
+ bytes = min_t(int, bytes - column, (int) writelen);
+ chip->pagebuf = -1;
+ memset(chip->buffers->databuf, 0xff, mtd->writesize);
+ memcpy(&chip->buffers->databuf[column], buf, bytes);
+ wbuf = chip->buffers->databuf;
+ }
+
+ if (unlikely(oob))
+ oob = nand_fill_oob(chip, oob, ops);
+
+ ret = chip->write_page(mtd, chip, wbuf, page, cached,
+ (ops->mode == MTD_OOB_RAW));
+ if (ret)
+ break;
+
+ writelen -= bytes;
+ if (!writelen)
+ break;
+
+ column = 0;
+ buf += bytes;
+ 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);
+ }
+ }
+
+ ops->retlen = ops->len - writelen;
+ if (unlikely(oob))
+ ops->oobretlen = ops->ooblen;
+ return ret;
+}
+
+/**
+ * nand_write - [MTD Interface] NAND write with ECC
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @len: number of bytes to write
+ * @retlen: pointer to variable to store the number of written bytes
+ * @buf: the data to write
+ *
+ * NAND write with ECC
+ */
+int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const uint8_t *buf)
+{
+ struct nand_chip *chip = mtd->priv;
+ int ret;
+
+ /* Do not allow reads past end of device */
+ if ((to + len) > mtd->size)
+ return -EINVAL;
+ if (!len)
+ return 0;
+
+ chip->ops.len = len;
+ chip->ops.datbuf = (uint8_t *)buf;
+ chip->ops.oobbuf = NULL;
+
+ ret = nand_do_write_ops(mtd, to, &chip->ops);
+
+ *retlen = chip->ops.retlen;
+
+ return ret;
+}
+
+/**
+ * nand_do_write_oob - [MTD Interface] NAND write out-of-band
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ *
+ * NAND write out-of-band
+ */
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int chipnr, page, status, len;
+ struct nand_chip *chip = mtd->priv;
+
+ MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
+ (unsigned int)to, (int)ops->ooblen);
+
+ if (ops->mode == MTD_OOB_AUTO)
+ len = chip->ecc.layout->oobavail;
+ else
+ len = mtd->oobsize;
+
+ /* Do not allow write past end of page */
+ if ((ops->ooboffs + ops->ooblen) > len) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
+ "Attempt to write past end of page\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(ops->ooboffs >= len)) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+ "Attempt to start write outside oob\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow reads past end of device */
+ if (unlikely(to >= mtd->size ||
+ ops->ooboffs + ops->ooblen >
+ ((mtd->size >> chip->page_shift) -
+ (to >> chip->page_shift)) * len)) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+ "Attempt write beyond end of device\n");
+ return -EINVAL;
+ }
+
+ chipnr = (int)(to >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ /* Shift to get page */
+ page = (int)(to >> chip->page_shift);
+
+ /*
+ * Reset the chip. Some chips (like the Toshiba TC5832DC found in one
+ * of my DiskOnChip 2000 test units) will clear the whole data page too
+ * if we don't do this. I have no clue why, but I seem to have 'fixed'
+ * it in the doc2000 driver in August 1999. dwmw2.
+ */
+ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+ /* Check, if it is write protected */
+ if (nand_check_wp(mtd))
+ return -EROFS;
+
+ /* Invalidate the page cache, if we write to the cached page */
+ if (page == chip->pagebuf)
+ chip->pagebuf = -1;
+
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+ nand_fill_oob(chip, ops->oobbuf, ops);
+ status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+
+ if (status)
+ return status;
+
+ ops->oobretlen = ops->ooblen;
+
+ return 0;
+}
+
+/**
+ * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ */
+int nand_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int ret = -ENOSYS;
+
+ ops->retlen = 0;
+
+ /* Do not allow writes past end of device */
+ if (ops->datbuf && (to + ops->len) > mtd->size) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+ "Attempt read beyond end of device\n");
+ return -EINVAL;
+ }
+
+ 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_write_oob(mtd, to, ops);
+ else
+ ret = nand_do_write_ops(mtd, to, ops);
+
+ out:
+ return ret;
+}
+
+/**
+ * single_erease_cmd - [GENERIC] NAND standard block erase command function
+ * @mtd: MTD device structure
+ * @page: the page address of the block which will be erased
+ *
+ * Standard erase command for NAND chips
+ */
+void single_erase_cmd(struct mtd_info *mtd, int page)
+{
+ struct nand_chip *chip = mtd->priv;
+ /* Send commands to erase a block */
+ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
+}
+
+/**
+ * multi_erease_cmd - [GENERIC] AND specific block erase command function
+ * @mtd: MTD device structure
+ * @page: the page address of the block which will be erased
+ *
+ * AND multi block erase command function
+ * Erase 4 consecutive blocks
+ */
+void multi_erase_cmd(struct mtd_info *mtd, int page)
+{
+ struct nand_chip *chip = mtd->priv;
+ /* Send commands to erase a block */
+ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
+}
+
+/**
+ * nand_erase - [MTD Interface] erase block(s)
+ * @mtd: MTD device structure
+ * @instr: erase instruction
+ *
+ * Erase one ore more blocks
+ */
+int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ return nand_erase_nand(mtd, instr, 0);
+}
+
+#define BBT_PAGE_MASK 0xffffff3f
+/**
+ * nand_erase_nand - [Internal] erase block(s)
+ * @mtd: MTD device structure
+ * @instr: erase instruction
+ * @allowbbt: allow erasing the bbt area
+ *
+ * Erase one ore more blocks
+ */
+int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
+ int allowbbt)
+{
+ int page, len, status, pages_per_block, ret, chipnr;
+ struct nand_chip *chip = mtd->priv;
+ int rewrite_bbt[NAND_MAX_CHIPS]={0};
+ unsigned int bbt_masked_page = 0xffffffff;
+
+ MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n",
+ (unsigned int)instr->addr, (unsigned int)instr->len);
+
+ /* Start address must align on block boundary */
+ if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
+ return -EINVAL;
+ }
+
+ /* Length must align on block boundary */
+ if (instr->len & ((1 << chip->phys_erase_shift) - 1)) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
+ "Length not block aligned\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow erase past end of device */
+ if ((instr->len + instr->addr) > mtd->size) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
+ "Erase past end of device\n");
+ return -EINVAL;
+ }
+
+ instr->fail_addr = 0xffffffff;
+
+ /* Shift to get first page */
+ page = (int)(instr->addr >> chip->page_shift);
+ chipnr = (int)(instr->addr >> chip->chip_shift);
+
+ /* Calculate pages in each block */
+ pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
+
+ /* Select the NAND device */
+ chip->select_chip(mtd, chipnr);
+
+ /* Check, if it is write protected */
+ if (nand_check_wp(mtd)) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
+ "Device is write protected!!!\n");
+ instr->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+ }
+
+ /*
+ * If BBT requires refresh, set the BBT page mask to see if the BBT
+ * should be rewritten. Otherwise the mask is set to 0xffffffff which
+ * can not be matched. This is also done when the bbt is actually
+ * erased to avoid recusrsive updates
+ */
+ if (chip->options & BBT_AUTO_REFRESH && !allowbbt)
+ bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
+
+ /* Loop through the pages */
+ len = instr->len;
+
+ instr->state = MTD_ERASING;
+
+ while (len) {
+ /*
+ * heck if we have a bad block, we do not erase bad blocks !
+ */
+ if (nand_block_checkbad(mtd, ((loff_t) page) <<
+ chip->page_shift, 0, allowbbt)) {
+ printk(KERN_WARNING "nand_erase: attempt to erase a "
+ "bad block at page 0x%08x\n", page);
+ instr->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+ }
+
+ /*
+ * Invalidate the page cache, if we erase the block which
+ * contains the current cached page
+ */
+ if (page <= chip->pagebuf && chip->pagebuf <
+ (page + pages_per_block))
+ chip->pagebuf = -1;
+
+ chip->erase_cmd(mtd, page & chip->pagemask);
+
+ status = chip->waitfunc(mtd, chip);
+
+ /*
+ * See if operation failed and additional status checks are
+ * available
+ */
+ if ((status & NAND_STATUS_FAIL) && (chip->errstat))
+ status = chip->errstat(mtd, chip, FL_ERASING,
+ status, page);
+
+ /* See if block erase succeeded */
+ if (status & NAND_STATUS_FAIL) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
+ "Failed erase, page 0x%08x\n", page);
+ instr->state = MTD_ERASE_FAILED;
+ instr->fail_addr = (page << chip->page_shift);
+ goto erase_exit;
+ }
+
+ /*
+ * If BBT requires refresh, set the BBT rewrite flag to the
+ * page being erased
+ */
+ if (bbt_masked_page != 0xffffffff &&
+ (page & BBT_PAGE_MASK) == bbt_masked_page)
+ rewrite_bbt[chipnr] = (page << chip->page_shift);
+
+ /* Increment page address and decrement length */
+ len -= (1 << chip->phys_erase_shift);
+ page += pages_per_block;
+
+ /* Check, if we cross a chip boundary */
+ if (len && !(page & chip->pagemask)) {
+ chipnr++;
+ chip->select_chip(mtd, -1);
+ chip->select_chip(mtd, chipnr);
+
+ /*
+ * If BBT requires refresh and BBT-PERCHIP, set the BBT
+ * page mask to see if this BBT should be rewritten
+ */
+ if (bbt_masked_page != 0xffffffff &&
+ (chip->bbt_td->options & NAND_BBT_PERCHIP))
+ bbt_masked_page = chip->bbt_td->pages[chipnr] &
+ BBT_PAGE_MASK;
+ }
+ }
+ instr->state = MTD_ERASE_DONE;
+
+ erase_exit:
+
+ ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
+
+ /* Do call back function */
+ if (!ret)
+ mtd_erase_callback(instr);
+
+ /*
+ * If BBT requires refresh and erase was successful, rewrite any
+ * selected bad block tables
+ */
+ if (bbt_masked_page == 0xffffffff || ret)
+ return ret;
+
+ for (chipnr = 0; chipnr < chip->numchips; chipnr++) {
+ if (!rewrite_bbt[chipnr])
+ continue;
+ /* update the BBT for chip */
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt "
+ "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr],
+ chip->bbt_td->pages[chipnr]);
+ nand_update_bbt(mtd, rewrite_bbt[chipnr]);
+ }
+
+ /* Return more or less happy */
+ return ret;
+}
+
+/**
+ * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
+ * @mtd: MTD device structure
+ * @ofs: offset relative to mtd start
+ */
+int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd->priv;
+ int ret;
+
+ if ((ret = nand_block_isbad(mtd, ofs))) {
+ /* If it was bad already, return success and do nothing. */
+ if (ret > 0)
+ return 0;
+ return ret;
+ }
+
+ return chip->block_markbad(mtd, ofs);
+}
--
1.7.2.3
More information about the barebox
mailing list