Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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
+86 −19
Original line number Diff line number Diff line
@@ -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
	/* 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
 *
	 * Currently we do nothing
 * This is the default implementation, which can be overridden by
 * a hardware specific driver.
 */
	return 0;
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 */
	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);
}

/**
+9 −0
Original line number Diff line number Diff line
@@ -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;
};