[PATCH] OneNAND: Simple Bad Block handling support
Based on NAND memory bad block table code Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
parent
52b0eea73d
commit
cdc001305d
|
@ -3,7 +3,9 @@
|
|||
#
|
||||
|
||||
# Core functionality.
|
||||
obj-$(CONFIG_MTD_ONENAND) += onenand_base.o
|
||||
obj-$(CONFIG_MTD_ONENAND) += onenand.o
|
||||
|
||||
# Board specific.
|
||||
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)
|
|||
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", ctrl);
|
||||
return -EIO;
|
||||
/* It maybe occur at initial bad block */
|
||||
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) {
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x", ctrl);
|
||||
return -EIO;
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x\n", ctrl);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (interrupt & ONENAND_INT_READ) {
|
||||
ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
* @param mtd MTD device structure
|
||||
|
@ -1109,7 +1130,12 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
|
|||
|
||||
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);
|
||||
|
||||
|
@ -1161,34 +1187,70 @@ static void onenand_sync(struct mtd_info *mtd)
|
|||
onenand_release_device(mtd);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
|
||||
* @param mtd MTD device structure
|
||||
* @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)
|
||||
{
|
||||
/*
|
||||
* TODO
|
||||
* 1. Bad block table (BBT)
|
||||
* -> using NAND BBT to support JFFS2
|
||||
* 2. Bad block management (BBM)
|
||||
* -> bad block replace scheme
|
||||
*
|
||||
* Currently we do nothing
|
||||
*/
|
||||
return 0;
|
||||
/* Check for invalid offset */
|
||||
if (ofs > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
return onenand_block_checkbad(mtd, ofs, 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_default_block_markbad - [DEFAULT] mark a block bad
|
||||
* @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
|
||||
* @param mtd MTD device structure
|
||||
* @param ofs offset relative to mtd start
|
||||
*
|
||||
* Mark the block as bad
|
||||
*/
|
||||
static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
{
|
||||
/* see above */
|
||||
return 0;
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
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)
|
|||
if (!this->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))
|
||||
return -ENXIO;
|
||||
|
||||
|
@ -1472,7 +1539,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
|
|||
/* Unlock whole block */
|
||||
mtd->unlock(mtd, 0x0, this->chipsize);
|
||||
|
||||
return 0;
|
||||
return this->scan_bbt(mtd);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mtd/onenand_regs.h>
|
||||
#include <linux/mtd/bbm.h>
|
||||
|
||||
#define MAX_BUFFERRAM 2
|
||||
|
||||
|
@ -67,10 +68,14 @@ struct onenand_bufferram {
|
|||
* @param wait [REPLACEABLE] hardware specific function for wait on ready
|
||||
* @param read_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 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 autooob [REPLACEABLE] the default (auto)placement scheme
|
||||
* @param bbm [REPLACEABLE] pointer to Bad Block Management
|
||||
* @param priv [OPTIONAL] pointer to private chip date
|
||||
*/
|
||||
struct onenand_chip {
|
||||
|
@ -96,6 +101,8 @@ struct onenand_chip {
|
|||
unsigned short (*read_word)(void __iomem *addr);
|
||||
void (*write_word)(unsigned short value, void __iomem *addr);
|
||||
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;
|
||||
wait_queue_head_t wq;
|
||||
|
@ -103,6 +110,8 @@ struct onenand_chip {
|
|||
|
||||
struct nand_oobinfo *autooob;
|
||||
|
||||
void *bbm;
|
||||
|
||||
void *priv;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue