Commit cdc00130 authored by Kyungmin Park's avatar Kyungmin Park Committed by Thomas Gleixner

[PATCH] OneNAND: Simple Bad Block handling support

Based on NAND memory bad block table code
Signed-off-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 52b0eea7
...@@ -3,7 +3,9 @@ ...@@ -3,7 +3,9 @@
# #
# Core functionality. # Core functionality.
obj-$(CONFIG_MTD_ONENAND) += onenand_base.o obj-$(CONFIG_MTD_ONENAND) += onenand.o
# Board specific. # Board specific.
obj-$(CONFIG_MTD_ONENAND_OMAP) += omap-onenand.o obj-$(CONFIG_MTD_ONENAND_OMAP) += omap-onenand.o
onenand-objs = onenand_base.o onenand_bbt.o
...@@ -311,19 +311,21 @@ static int onenand_wait(struct mtd_info *mtd, int state) ...@@ -311,19 +311,21 @@ static int onenand_wait(struct mtd_info *mtd, int state)
ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
if (ctrl & ONENAND_CTRL_ERROR) { if (ctrl & ONENAND_CTRL_ERROR) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x", ctrl); /* It maybe occur at initial bad block */
return -EIO; DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl);
/* Clear other interrupt bits for preventing ECC error */
interrupt &= ONENAND_INT_MASTER;
} }
if (ctrl & ONENAND_CTRL_LOCK) { if (ctrl & ONENAND_CTRL_LOCK) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x", ctrl); DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x\n", ctrl);
return -EIO; return -EACCES;
} }
if (interrupt & ONENAND_INT_READ) { if (interrupt & ONENAND_INT_READ) {
ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
if (ecc & ONENAND_ECC_2BIT_ALL) { if (ecc & ONENAND_ECC_2BIT_ALL) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x", ecc); DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc);
return -EBADMSG; return -EBADMSG;
} }
} }
...@@ -1059,6 +1061,25 @@ static int onenand_writev(struct mtd_info *mtd, const struct kvec *vecs, ...@@ -1059,6 +1061,25 @@ static int onenand_writev(struct mtd_info *mtd, const struct kvec *vecs,
return onenand_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL); return onenand_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL);
} }
/**
* onenand_block_checkbad - [GENERIC] Check if a block is marked bad
* @param mtd MTD device structure
* @param ofs offset from device start
* @param getchip 0, if the chip is already selected
* @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
* calling of the scan function.
*/
static int onenand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt)
{
struct onenand_chip *this = mtd->priv;
struct bbm_info *bbm = this->bbm;
/* Return info from the table */
return bbm->isbad_bbt(mtd, ofs, allowbbt);
}
/** /**
* onenand_erase - [MTD Interface] erase block(s) * onenand_erase - [MTD Interface] erase block(s)
* @param mtd MTD device structure * @param mtd MTD device structure
...@@ -1109,7 +1130,12 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -1109,7 +1130,12 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
while (len) { while (len) {
/* TODO Check badblock */ /* Check if we have a bad block, we do not erase bad blocks */
if (onenand_block_checkbad(mtd, addr, 0, 0)) {
printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%08x\n", (unsigned int) addr);
instr->state = MTD_ERASE_FAILED;
goto erase_exit;
}
this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
...@@ -1161,34 +1187,70 @@ static void onenand_sync(struct mtd_info *mtd) ...@@ -1161,34 +1187,70 @@ static void onenand_sync(struct mtd_info *mtd)
onenand_release_device(mtd); onenand_release_device(mtd);
} }
/** /**
* onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
* @param mtd MTD device structure * @param mtd MTD device structure
* @param ofs offset relative to mtd start * @param ofs offset relative to mtd start
*
* Check whether the block is bad
*/ */
static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
{ {
/* /* Check for invalid offset */
* TODO if (ofs > mtd->size)
* 1. Bad block table (BBT) return -EINVAL;
* -> using NAND BBT to support JFFS2
* 2. Bad block management (BBM) return onenand_block_checkbad(mtd, ofs, 1, 0);
* -> bad block replace scheme }
*
* Currently we do nothing /**
*/ * onenand_default_block_markbad - [DEFAULT] mark a block bad
return 0; * @param mtd MTD device structure
* @param ofs offset from device start
*
* This is the default implementation, which can be overridden by
* a hardware specific driver.
*/
static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
struct onenand_chip *this = mtd->priv;
struct bbm_info *bbm = this->bbm;
u_char buf[2] = {0, 0};
size_t retlen;
int block;
/* Get block number */
block = ((int) ofs) >> bbm->bbt_erase_shift;
if (bbm->bbt)
bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
/* We write two bytes, so we dont have to mess with 16 bit access */
ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
return mtd->write_oob(mtd, ofs , 2, &retlen, buf);
} }
/** /**
* onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
* @param mtd MTD device structure * @param mtd MTD device structure
* @param ofs offset relative to mtd start * @param ofs offset relative to mtd start
*
* Mark the block as bad
*/ */
static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
{ {
/* see above */ struct onenand_chip *this = mtd->priv;
return 0; int ret;
ret = onenand_block_isbad(mtd, ofs);
if (ret) {
/* If it was bad already, return success and do nothing */
if (ret > 0)
return 0;
return ret;
}
return this->block_markbad(mtd, ofs);
} }
/** /**
...@@ -1411,6 +1473,11 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) ...@@ -1411,6 +1473,11 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
if (!this->write_bufferram) if (!this->write_bufferram)
this->write_bufferram = onenand_write_bufferram; this->write_bufferram = onenand_write_bufferram;
if (!this->block_markbad)
this->block_markbad = onenand_default_block_markbad;
if (!this->scan_bbt)
this->scan_bbt = onenand_default_bbt;
if (onenand_probe(mtd)) if (onenand_probe(mtd))
return -ENXIO; return -ENXIO;
...@@ -1472,7 +1539,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) ...@@ -1472,7 +1539,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
/* Unlock whole block */ /* Unlock whole block */
mtd->unlock(mtd, 0x0, this->chipsize); mtd->unlock(mtd, 0x0, this->chipsize);
return 0; return this->scan_bbt(mtd);
} }
/** /**
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/mtd/onenand_regs.h> #include <linux/mtd/onenand_regs.h>
#include <linux/mtd/bbm.h>
#define MAX_BUFFERRAM 2 #define MAX_BUFFERRAM 2
...@@ -67,10 +68,14 @@ struct onenand_bufferram { ...@@ -67,10 +68,14 @@ struct onenand_bufferram {
* @param wait [REPLACEABLE] hardware specific function for wait on ready * @param wait [REPLACEABLE] hardware specific function for wait on ready
* @param read_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area * @param read_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area
* @param write_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area * @param write_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area
* @param read_word [REPLACEABLE] hardware specific function for read register of OneNAND
* @param write_word [REPLACEABLE] hardware specific function for write register of OneNAND
* @param scan_bbt [REPLACEALBE] hardware specific function for scaning Bad block Table
* @param chip_lock [INTERN] spinlock used to protect access to this structure and the chip * @param chip_lock [INTERN] spinlock used to protect access to this structure and the chip
* @param wq [INTERN] wait queue to sleep on if a OneNAND operation is in progress * @param wq [INTERN] wait queue to sleep on if a OneNAND operation is in progress
* @param state [INTERN] the current state of the OneNAND device * @param state [INTERN] the current state of the OneNAND device
* @param autooob [REPLACEABLE] the default (auto)placement scheme * @param autooob [REPLACEABLE] the default (auto)placement scheme
* @param bbm [REPLACEABLE] pointer to Bad Block Management
* @param priv [OPTIONAL] pointer to private chip date * @param priv [OPTIONAL] pointer to private chip date
*/ */
struct onenand_chip { struct onenand_chip {
...@@ -96,6 +101,8 @@ struct onenand_chip { ...@@ -96,6 +101,8 @@ struct onenand_chip {
unsigned short (*read_word)(void __iomem *addr); unsigned short (*read_word)(void __iomem *addr);
void (*write_word)(unsigned short value, void __iomem *addr); void (*write_word)(unsigned short value, void __iomem *addr);
void (*mmcontrol)(struct mtd_info *mtd, int sync_read); void (*mmcontrol)(struct mtd_info *mtd, int sync_read);
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
int (*scan_bbt)(struct mtd_info *mtd);
spinlock_t chip_lock; spinlock_t chip_lock;
wait_queue_head_t wq; wait_queue_head_t wq;
...@@ -103,6 +110,8 @@ struct onenand_chip { ...@@ -103,6 +110,8 @@ struct onenand_chip {
struct nand_oobinfo *autooob; struct nand_oobinfo *autooob;
void *bbm;
void *priv; void *priv;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment