From 71ebb2166f129824ad3b82ba3372d104b98fb3a6 Mon Sep 17 00:00:00 2001 From: Mika Korhonen Date: Mon, 15 Jun 2009 09:48:36 +0300 Subject: [PATCH] MTD OneNAND: multiblock erase support Add support for multiblock erase command. OneNAND is capable of simultaneous erase of up to 64 eraseblocks. In case this kernel option is enabled the erase requests for regions of size 2..64 eraseblocks are performed using multiblock erase. Signed-off-by: Mika Korhonen --- drivers/mtd/onenand/Kconfig | 9 ++++ drivers/mtd/onenand/omap2.c | 6 +++ drivers/mtd/onenand/onenand_base.c | 84 ++++++++++++++++++++++++++++++++++++ include/linux/mtd/onenand.h | 2 + include/linux/mtd/onenand_regs.h | 2 + 5 files changed, 103 insertions(+), 0 deletions(-) diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 79fa79e..f7037c3 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -64,6 +64,15 @@ config MTD_ONENAND_2X_PROGRAM And more recent chips +config MTD_ONENAND_MULTIBLOCK_ERASE + bool "OneNAND multiblock erase support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + Support for multiblock erase command. OneNAND is capable of + simultaneous erase of up to 64 eraseblocks. If this option is + enabled the erase requests for regions of size 2..64 eraseblocks + are performed using multiblock erase. + config MTD_ONENAND_SIM tristate "OneNAND simulator support" depends on MTD_PARTITIONS diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index df26db8..cc2b48b 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -255,6 +255,12 @@ retry: return -EIO; } +#ifdef CONFIG_MTD_ONENAND_MULTIBLOCK_ERASE + /* multiblock erase command: ONGO | ERASE (0x8800) */ + if (state == FL_PREPARING_ERASE && !(ctrl & 0x769F)) { + return 0; + } +#endif if (ctrl & 0xFE9F) wait_warn("unexpected controller status", state, ctrl, intr); diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 864327e..1525fd7 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -31,9 +31,15 @@ #include +/* Multiblock erase if number of blocks to erase is 2..64 */ +#define MB_ERASE_MIN_BLK_COUNT 2 +#define MB_ERASE_MAX_BLK_COUNT 64 + + /* Default Flex-OneNAND boundary and lock respectively */ static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 }; + /** * onenand_oob_128 - oob info for Flex-Onenand with 4KB page * For now, we expose only 64 out of 80 ecc bytes @@ -330,6 +336,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le break; case ONENAND_CMD_ERASE: + case ONENAND_CMD_MULTIBLOCK_ERASE: + case ONENAND_CMD_ERASE_VERIFY: case ONENAND_CMD_BUFFERRAM: case ONENAND_CMD_OTP_ACCESS: block = onenand_block(this, addr); @@ -2147,6 +2155,9 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) int ret = 0, i; struct mtd_erase_region_info *region = NULL; loff_t region_end = 0; +#ifdef CONFIG_MTD_ONENAND_MULTIBLOCK_ERASE + int do_mb_erase = 0; +#endif DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len); @@ -2193,6 +2204,52 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) onenand_get_device(mtd, FL_ERASING); /* Loop throught the pages */ +#ifdef CONFIG_MTD_ONENAND_MULTIBLOCK_ERASE + + instr->state = MTD_ERASING; + if (!FLEXONENAND(this) && + (len >= MB_ERASE_MIN_BLK_COUNT * block_size)) { + if (len <= MB_ERASE_MAX_BLK_COUNT * block_size) { + do_mb_erase = 1; + } else { + /* OneNAND would seem capable of handling more than + 64 blocks (how much more?) with multiblock erase + command. However, 64 is the max for simultaneous + erase */ + printk(KERN_WARNING "onenand_erase: performance warning: " + "erase over %d blocks\n", MB_ERASE_MAX_BLK_COUNT); + do_mb_erase = 0; + } + } + + while (do_mb_erase && len > block_size) { + /* Check if we have a bad block, we do not erase bad blocks */ + if (onenand_block_isbad_nolock(mtd, addr, 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_MULTIBLOCK_ERASE, addr, block_size); + + onenand_invalidate_bufferram(mtd, addr, block_size); + + ret = this->wait(mtd, FL_PREPARING_ERASE); + /* Check, if it is write protected */ + if (ret) { + printk(KERN_ERR "onenand_erase: Failed multiblock erase, " + "block %d\n", (unsigned) (addr >> this->erase_shift)); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = addr; + goto erase_exit; + } + len -= block_size; + addr += block_size; + } +#endif /* CONFIG_MTD_ONENAND_MULTIBLOCK_ERASE */ + + /* single block erase, also called for the last block of multiblock erase */ instr->state = MTD_ERASING; while (len) { @@ -2239,6 +2296,33 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) } + +#ifdef CONFIG_MTD_ONENAND_MULTIBLOCK_ERASE + + /* multiblock erase verify */ + if (do_mb_erase) { + /* Loop through the blocks */ + len = instr->len; + addr = instr->addr; + + while (len) { + this->command(mtd, ONENAND_CMD_ERASE_VERIFY, addr, block_size); + ret = this->wait(mtd, FL_VERIFYING_ERASE); + if (ret) { + printk(KERN_ERR "onenand_erase: Failed verify, " + "block %d\n", (unsigned) (addr >> this->erase_shift)); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = addr; + goto erase_exit; + } + + len -= block_size; + addr += block_size; + } + } + +#endif /* CONFIG_MTD_ONENAND_MULTIBLOCK_ERASE */ + instr->state = MTD_ERASE_DONE; erase_exit: diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 8ed8733..42384f3 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -39,6 +39,8 @@ typedef enum { FL_RESETING, FL_OTPING, FL_PM_SUSPENDED, + FL_PREPARING_ERASE, + FL_VERIFYING_ERASE } onenand_state_t; /** diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index 86a6bbe..bd346e5 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -131,6 +131,8 @@ #define ONENAND_CMD_LOCK_TIGHT (0x2C) #define ONENAND_CMD_UNLOCK_ALL (0x27) #define ONENAND_CMD_ERASE (0x94) +#define ONENAND_CMD_MULTIBLOCK_ERASE (0x95) +#define ONENAND_CMD_ERASE_VERIFY (0x71) #define ONENAND_CMD_RESET (0xF0) #define ONENAND_CMD_OTP_ACCESS (0x65) #define ONENAND_CMD_READID (0x90) -- 1.5.6.5