[MTD] OneNAND: Error message printing and bad block scan erros
Provide the bad block scan with its own read function so that important error
messages that are not from the the bad block scan, can always be printed.
Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 3d6f880..f690c19 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -304,16 +304,16 @@
ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
if (ctrl & ONENAND_CTRL_ERROR) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl);
+ printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl);
if (ctrl & ONENAND_CTRL_LOCK)
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error.\n");
+ printk(KERN_ERR "onenand_wait: it's locked error.\n");
return ctrl;
}
if (interrupt & ONENAND_INT_READ) {
int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
if (ecc) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc);
+ printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc);
if (ecc & ONENAND_ECC_2BIT_ALL) {
mtd->ecc_stats.failed++;
return ecc;
@@ -703,7 +703,7 @@
/* Do not allow reads past end of device */
if ((from + len) > mtd->size) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: Attempt read beyond end of device\n");
+ printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n");
*retlen = 0;
return -EINVAL;
}
@@ -834,7 +834,7 @@
*
* OneNAND read out-of-band data from the spare area
*/
-int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
+static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
@@ -854,7 +854,7 @@
column = from & (mtd->oobsize - 1);
if (unlikely(column >= oobsize)) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to start read outside oob\n");
+ printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n");
return -EINVAL;
}
@@ -862,7 +862,7 @@
if (unlikely(from >= mtd->size ||
column + len > ((mtd->size >> this->page_shift) -
(from >> this->page_shift)) * oobsize)) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to read beyond end of device\n");
+ printk(KERN_ERR "onenand_read_oob: Attempted to read beyond end of device\n");
return -EINVAL;
}
@@ -888,7 +888,7 @@
this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
if (ret) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret);
+ printk(KERN_ERR "onenand_read_oob: read failed = 0x%x\n", ret);
break;
}
@@ -936,6 +936,121 @@
&ops->oobretlen, ops->oobbuf, ops->mode);
}
+/**
+ * onenand_bbt_wait - [DEFAULT] wait until the command is done
+ * @param mtd MTD device structure
+ * @param state state to select the max. timeout value
+ *
+ * Wait for command done.
+ */
+static int onenand_bbt_wait(struct mtd_info *mtd, int state)
+{
+ struct onenand_chip *this = mtd->priv;
+ unsigned long timeout;
+ unsigned int interrupt;
+ unsigned int ctrl;
+
+ /* The 20 msec is enough */
+ timeout = jiffies + msecs_to_jiffies(20);
+ while (time_before(jiffies, timeout)) {
+ interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
+ if (interrupt & ONENAND_INT_MASTER)
+ break;
+ }
+ /* To get correct interrupt status in timeout case */
+ interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
+ ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+
+ if (ctrl & ONENAND_CTRL_ERROR) {
+ printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl);
+ /* Initial bad block case */
+ if (ctrl & ONENAND_CTRL_LOAD)
+ return ONENAND_BBT_READ_ERROR;
+ return ONENAND_BBT_READ_FATAL_ERROR;
+ }
+
+ if (interrupt & ONENAND_INT_READ) {
+ int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
+ if (ecc & ONENAND_ECC_2BIT_ALL)
+ return ONENAND_BBT_READ_ERROR;
+ } else {
+ printk(KERN_ERR "onenand_bbt_wait: read timeout!"
+ "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);
+ return ONENAND_BBT_READ_FATAL_ERROR;
+ }
+
+ return 0;
+}
+
+/**
+ * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan
+ * @param mtd MTD device structure
+ * @param from offset to read from
+ * @param @ops oob operation description structure
+ *
+ * OneNAND read out-of-band data from the spare area for bbt scan
+ */
+int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct onenand_chip *this = mtd->priv;
+ int read = 0, thislen, column;
+ int ret = 0;
+ size_t len = ops->ooblen;
+ u_char *buf = ops->oobbuf;
+
+ DEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, len);
+
+ /* Initialize return value */
+ ops->oobretlen = 0;
+
+ /* Do not allow reads past end of device */
+ if (unlikely((from + len) > mtd->size)) {
+ printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n");
+ 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) {
+ cond_resched();
+
+ 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 += mtd->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
@@ -1040,13 +1155,13 @@
/* Do not allow writes past end of device */
if (unlikely((to + len) > mtd->size)) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt write to past end of device\n");
+ printk(KERN_ERR "onenand_write: Attempt write to past end of device\n");
return -EINVAL;
}
/* Reject writes, which are not page aligned */
if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt to write not page aligned data\n");
+ printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n");
return -EINVAL;
}
@@ -1083,14 +1198,14 @@
onenand_update_bufferram(mtd, to, !ret && !subpage);
if (ret) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret);
+ printk(KERN_ERR "onenand_write: write filaed %d\n", ret);
break;
}
/* Only check verify write turn on */
ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen);
if (ret) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret);
+ printk(KERN_ERR "onenand_write: verify failed %d\n", ret);
break;
}
@@ -1180,13 +1295,13 @@
column = to & (mtd->oobsize - 1);
if (unlikely(column >= oobsize)) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to start write outside oob\n");
+ printk(KERN_ERR "onenand_write_oob: Attempted to start write outside oob\n");
return -EINVAL;
}
/* For compatibility with NAND: Do not allow write past end of page */
if (column + len > oobsize) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: "
+ printk(KERN_ERR "onenand_write_oob: "
"Attempt to write past end of page\n");
return -EINVAL;
}
@@ -1195,7 +1310,7 @@
if (unlikely(to >= mtd->size ||
column + len > ((mtd->size >> this->page_shift) -
(to >> this->page_shift)) * oobsize)) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to write past end of device\n");
+ printk(KERN_ERR "onenand_write_oob: Attempted to write past end of device\n");
return -EINVAL;
}
@@ -1225,13 +1340,13 @@
ret = this->wait(mtd, FL_WRITING);
if (ret) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write failed %d\n", ret);
+ printk(KERN_ERR "onenand_write_oob: write failed %d\n", ret);
break;
}
ret = onenand_verify_oob(mtd, this->page_buf, to);
if (ret) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret);
+ printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret);
break;
}
@@ -1314,19 +1429,19 @@
/* Start address must align on block boundary */
if (unlikely(instr->addr & (block_size - 1))) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n");
+ printk(KERN_ERR "onenand_erase: Unaligned address\n");
return -EINVAL;
}
/* Length must align on block boundary */
if (unlikely(instr->len & (block_size - 1))) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Length not block aligned\n");
+ printk(KERN_ERR "onenand_erase: Length not block aligned\n");
return -EINVAL;
}
/* Do not allow erase past end of device */
if (unlikely((instr->len + instr->addr) > mtd->size)) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Erase past end of device\n");
+ printk(KERN_ERR "onenand_erase: Erase past end of device\n");
return -EINVAL;
}
@@ -1356,7 +1471,7 @@
ret = this->wait(mtd, FL_ERASING);
/* Check, if it is write protected */
if (ret) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift));
+ printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift));
instr->state = MTD_ERASE_FAILED;
instr->fail_addr = addr;
goto erase_exit;
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
index acea9a1..aecdd50 100644
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -17,8 +17,8 @@
#include <linux/mtd/onenand.h>
#include <linux/mtd/compatmac.h>
-extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf, mtd_oob_mode_t mode);
+extern int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops);
/**
* check_short_pattern - [GENERIC] check if a pattern is in the buffer
@@ -65,6 +65,7 @@
int startblock;
loff_t from;
size_t readlen, ooblen;
+ struct mtd_oob_ops ops;
printk(KERN_INFO "Scanning device for bad blocks\n");
@@ -82,22 +83,24 @@
startblock = 0;
from = 0;
+ ops.mode = MTD_OOB_PLACE;
+ ops.ooblen = readlen;
+ ops.oobbuf = buf;
+ ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
+
for (i = startblock; i < numblocks; ) {
int ret;
for (j = 0; j < len; j++) {
- size_t retlen;
-
/* No need to read pages fully,
* just read required OOB bytes */
- ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs,
- readlen, &retlen, &buf[0], MTD_OOB_PLACE);
+ ret = onenand_bbt_read_oob(mtd, from + j * mtd->writesize + bd->offs, &ops);
/* If it is a initial bad block, just ignore it */
- if (ret && !(ret & ONENAND_CTRL_LOAD))
- return ret;
+ if (ret == ONENAND_BBT_READ_FATAL_ERROR)
+ return -EIO;
- if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) {
+ if (ret || check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) {
bbm->bbt[i >> 3] |= 0x03 << (i & 0x6);
printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
i >> 1, (unsigned int) from);