aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMika Korhonen <ext-mika.2.korhonen@nokia.com>2009-10-23 01:50:43 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2009-11-30 04:43:18 -0500
commit72073027ee95d059eb5a064da4a978efab36d4ab (patch)
treea7ee5f24092602d7fda6c34db6781c3de2d415d8
parent73885aeaca046a21183db598c2da46529e46fdab (diff)
mtd: OneNAND: multiblock erase support
Add support for multiblock erase command. OneNANDs (excluding Flex-OneNAND) are capable of simultaneous erase of up to 64 eraseblocks which is much faster. This changes the erase requests for regions covering multiple eraseblocks to be performed using multiblock erase. Signed-off-by: Mika Korhonen <ext-mika.2.korhonen@nokia.com> Reviewed-by: Adrian Hunter <adrian.hunter@nokia.com> Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r--drivers/mtd/onenand/omap2.c22
-rw-r--r--drivers/mtd/onenand/onenand_base.c173
-rw-r--r--include/linux/mtd/flashchip.h4
-rw-r--r--include/linux/mtd/onenand_regs.h2
4 files changed, 194 insertions, 7 deletions
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c
index 0108ed42e877..2dafd0949be5 100644
--- a/drivers/mtd/onenand/omap2.c
+++ b/drivers/mtd/onenand/omap2.c
@@ -112,10 +112,24 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state)
112 unsigned long timeout; 112 unsigned long timeout;
113 u32 syscfg; 113 u32 syscfg;
114 114
115 if (state == FL_RESETING) { 115 if (state == FL_RESETING || state == FL_PREPARING_ERASE ||
116 int i; 116 state == FL_VERIFYING_ERASE) {
117 int i = 21;
118 unsigned int intr_flags = ONENAND_INT_MASTER;
119
120 switch (state) {
121 case FL_RESETING:
122 intr_flags |= ONENAND_INT_RESET;
123 break;
124 case FL_PREPARING_ERASE:
125 intr_flags |= ONENAND_INT_ERASE;
126 break;
127 case FL_VERIFYING_ERASE:
128 i = 101;
129 break;
130 }
117 131
118 for (i = 0; i < 20; i++) { 132 while (--i) {
119 udelay(1); 133 udelay(1);
120 intr = read_reg(c, ONENAND_REG_INTERRUPT); 134 intr = read_reg(c, ONENAND_REG_INTERRUPT);
121 if (intr & ONENAND_INT_MASTER) 135 if (intr & ONENAND_INT_MASTER)
@@ -126,7 +140,7 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state)
126 wait_err("controller error", state, ctrl, intr); 140 wait_err("controller error", state, ctrl, intr);
127 return -EIO; 141 return -EIO;
128 } 142 }
129 if (!(intr & ONENAND_INT_RESET)) { 143 if ((intr & intr_flags) != intr_flags) {
130 wait_err("timeout", state, ctrl, intr); 144 wait_err("timeout", state, ctrl, intr);
131 return -EIO; 145 return -EIO;
132 } 146 }
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 894ebadc3e69..266f471901e5 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -34,6 +34,13 @@
34 34
35#include <asm/io.h> 35#include <asm/io.h>
36 36
37/*
38 * Multiblock erase if number of blocks to erase is 2 or more.
39 * Maximum number of blocks for simultaneous erase is 64.
40 */
41#define MB_ERASE_MIN_BLK_COUNT 2
42#define MB_ERASE_MAX_BLK_COUNT 64
43
37/* Default Flex-OneNAND boundary and lock respectively */ 44/* Default Flex-OneNAND boundary and lock respectively */
38static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 }; 45static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 };
39 46
@@ -353,6 +360,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
353 break; 360 break;
354 361
355 case ONENAND_CMD_ERASE: 362 case ONENAND_CMD_ERASE:
363 case ONENAND_CMD_MULTIBLOCK_ERASE:
364 case ONENAND_CMD_ERASE_VERIFY:
356 case ONENAND_CMD_BUFFERRAM: 365 case ONENAND_CMD_BUFFERRAM:
357 case ONENAND_CMD_OTP_ACCESS: 366 case ONENAND_CMD_OTP_ACCESS:
358 block = onenand_block(this, addr); 367 block = onenand_block(this, addr);
@@ -497,7 +506,7 @@ static int onenand_wait(struct mtd_info *mtd, int state)
497 if (interrupt & flags) 506 if (interrupt & flags)
498 break; 507 break;
499 508
500 if (state != FL_READING) 509 if (state != FL_READING && state != FL_PREPARING_ERASE)
501 cond_resched(); 510 cond_resched();
502 } 511 }
503 /* To get correct interrupt status in timeout case */ 512 /* To get correct interrupt status in timeout case */
@@ -530,6 +539,18 @@ static int onenand_wait(struct mtd_info *mtd, int state)
530 return -EIO; 539 return -EIO;
531 } 540 }
532 541
542 if (state == FL_PREPARING_ERASE && !(interrupt & ONENAND_INT_ERASE)) {
543 printk(KERN_ERR "%s: mb erase timeout! ctrl=0x%04x intr=0x%04x\n",
544 __func__, ctrl, interrupt);
545 return -EIO;
546 }
547
548 if (!(interrupt & ONENAND_INT_MASTER)) {
549 printk(KERN_ERR "%s: timeout! ctrl=0x%04x intr=0x%04x\n",
550 __func__, ctrl, interrupt);
551 return -EIO;
552 }
553
533 /* If there's controller error, it's a real error */ 554 /* If there's controller error, it's a real error */
534 if (ctrl & ONENAND_CTRL_ERROR) { 555 if (ctrl & ONENAND_CTRL_ERROR) {
535 printk(KERN_ERR "%s: controller error = 0x%04x\n", 556 printk(KERN_ERR "%s: controller error = 0x%04x\n",
@@ -2182,6 +2203,148 @@ static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allo
2182 return bbm->isbad_bbt(mtd, ofs, allowbbt); 2203 return bbm->isbad_bbt(mtd, ofs, allowbbt);
2183} 2204}
2184 2205
2206
2207static int onenand_multiblock_erase_verify(struct mtd_info *mtd,
2208 struct erase_info *instr)
2209{
2210 struct onenand_chip *this = mtd->priv;
2211 loff_t addr = instr->addr;
2212 int len = instr->len;
2213 unsigned int block_size = (1 << this->erase_shift);
2214 int ret = 0;
2215
2216 while (len) {
2217 this->command(mtd, ONENAND_CMD_ERASE_VERIFY, addr, block_size);
2218 ret = this->wait(mtd, FL_VERIFYING_ERASE);
2219 if (ret) {
2220 printk(KERN_ERR "%s: Failed verify, block %d\n",
2221 __func__, onenand_block(this, addr));
2222 instr->state = MTD_ERASE_FAILED;
2223 instr->fail_addr = addr;
2224 return -1;
2225 }
2226 len -= block_size;
2227 addr += block_size;
2228 }
2229 return 0;
2230}
2231
2232/**
2233 * onenand_multiblock_erase - [Internal] erase block(s) using multiblock erase
2234 * @param mtd MTD device structure
2235 * @param instr erase instruction
2236 * @param region erase region
2237 *
2238 * Erase one or more blocks up to 64 block at a time
2239 */
2240static int onenand_multiblock_erase(struct mtd_info *mtd,
2241 struct erase_info *instr,
2242 unsigned int block_size)
2243{
2244 struct onenand_chip *this = mtd->priv;
2245 loff_t addr = instr->addr;
2246 int len = instr->len;
2247 int eb_count = 0;
2248 int ret = 0;
2249 int bdry_block = 0;
2250
2251 instr->state = MTD_ERASING;
2252
2253 if (ONENAND_IS_DDP(this)) {
2254 loff_t bdry_addr = this->chipsize >> 1;
2255 if (addr < bdry_addr && (addr + len) > bdry_addr)
2256 bdry_block = bdry_addr >> this->erase_shift;
2257 }
2258
2259 /* Pre-check bbs */
2260 while (len) {
2261 /* Check if we have a bad block, we do not erase bad blocks */
2262 if (onenand_block_isbad_nolock(mtd, addr, 0)) {
2263 printk(KERN_WARNING "%s: attempt to erase a bad block "
2264 "at addr 0x%012llx\n",
2265 __func__, (unsigned long long) addr);
2266 instr->state = MTD_ERASE_FAILED;
2267 return -EIO;
2268 }
2269 len -= block_size;
2270 addr += block_size;
2271 }
2272
2273 len = instr->len;
2274 addr = instr->addr;
2275