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
2276 /* loop over 64 eb batches */
2277 while (len) {
2278 struct erase_info verify_instr = *instr;
2279 int max_eb_count = MB_ERASE_MAX_BLK_COUNT;
2280
2281 verify_instr.addr = addr;
2282 verify_instr.len = 0;
2283
2284 /* do not cross chip boundary */
2285 if (bdry_block) {
2286 int this_block = (addr >> this->erase_shift);
2287
2288 if (this_block < bdry_block) {
2289 max_eb_count = min(max_eb_count,
2290 (bdry_block - this_block));
2291 }
2292 }
2293
2294 eb_count = 0;
2295
2296 while (len > block_size && eb_count < (max_eb_count - 1)) {
2297 this->command(mtd, ONENAND_CMD_MULTIBLOCK_ERASE,
2298 addr, block_size);
2299 onenand_invalidate_bufferram(mtd, addr, block_size);
2300
2301 ret = this->wait(mtd, FL_PREPARING_ERASE);
2302 if (ret) {
2303 printk(KERN_ERR "%s: Failed multiblock erase, "
2304 "block %d\n", __func__,
2305 onenand_block(this, addr));
2306 instr->state = MTD_ERASE_FAILED;
2307 instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
2308 return -EIO;
2309 }
2310
2311 len -= block_size;
2312 addr += block_size;
2313 eb_count++;
2314 }
2315
2316 /* last block of 64-eb series */
2317 cond_resched();
2318 this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
2319 onenand_invalidate_bufferram(mtd, addr, block_size);
2320
2321 ret = this->wait(mtd, FL_ERASING);
2322 /* Check if it is write protected */
2323 if (ret) {
2324 printk(KERN_ERR "%s: Failed erase, block %d\n",
2325 __func__, onenand_block(this, addr));
2326 instr->state = MTD_ERASE_FAILED;
2327 instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
2328 return -EIO;
2329 }
2330
2331 len -= block_size;
2332 addr += block_size;
2333 eb_count++;
2334
2335 /* verify */
2336 verify_instr.len = eb_count * block_size;
2337 if (onenand_multiblock_erase_verify(mtd, &verify_instr)) {
2338 instr->state = verify_instr.state;
2339 instr->fail_addr = verify_instr.fail_addr;
2340 return -EIO;
2341 }
2342
2343 }
2344 return 0;
2345}
2346
2347
2185/** 2348/**
2186 * onenand_block_by_block_erase - [Internal] erase block(s) using regular erase 2349 * onenand_block_by_block_erase - [Internal] erase block(s) using regular erase
2187 * @param mtd MTD device structure 2350 * @param mtd MTD device structure
@@ -2315,7 +2478,13 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
2315 /* Grab the lock and see if the device is available */ 2478 /* Grab the lock and see if the device is available */
2316 onenand_get_device(mtd, FL_ERASING); 2479 onenand_get_device(mtd, FL_ERASING);
2317 2480
2318 ret = onenand_block_by_block_erase(mtd, instr, region, block_size); 2481 if (region || instr->len < MB_ERASE_MIN_BLK_COUNT * block_size) {
2482 /* region is set for Flex-OneNAND (no mb erase) */
2483 ret = onenand_block_by_block_erase(mtd, instr,
2484 region, block_size);
2485 } else {
2486 ret = onenand_multiblock_erase(mtd, instr, block_size);
2487 }
2319 2488
2320 /* Deselect and wake up anyone waiting on the device */ 2489 /* Deselect and wake up anyone waiting on the device */
2321 onenand_release_device(mtd); 2490 onenand_release_device(mtd);
diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h
index f350a4879f75..d0bf422ae374 100644
--- a/include/linux/mtd/flashchip.h
+++ b/include/linux/mtd/flashchip.h
@@ -41,9 +41,11 @@ typedef enum {
41 /* These 2 come from nand_state_t, which has been unified here */ 41 /* These 2 come from nand_state_t, which has been unified here */
42 FL_READING, 42 FL_READING,
43 FL_CACHEDPRG, 43 FL_CACHEDPRG,
44 /* These 2 come from onenand_state_t, which has been unified here */ 44 /* These 4 come from onenand_state_t, which has been unified here */
45 FL_RESETING, 45 FL_RESETING,
46 FL_OTPING, 46 FL_OTPING,
47 FL_PREPARING_ERASE,
48 FL_VERIFYING_ERASE,
47 49
48 FL_UNKNOWN 50 FL_UNKNOWN
49} flstate_t; 51} flstate_t;
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h
index acadbf53a69f..cd6f3b431195 100644
--- a/include/linux/mtd/onenand_regs.h
+++ b/include/linux/mtd/onenand_regs.h
@@ -131,6 +131,8 @@
131#define ONENAND_CMD_LOCK_TIGHT (0x2C) 131#define ONENAND_CMD_LOCK_TIGHT (0x2C)
132#define ONENAND_CMD_UNLOCK_ALL (0x27) 132#define ONENAND_CMD_UNLOCK_ALL (0x27)
133#define ONENAND_CMD_ERASE (0x94) 133#define ONENAND_CMD_ERASE (0x94)
134#define ONENAND_CMD_MULTIBLOCK_ERASE (0x95)
135#define ONENAND_CMD_ERASE_VERIFY (0x71)
134#define ONENAND_CMD_RESET (0xF0) 136#define ONENAND_CMD_RESET (0xF0)
135#define ONENAND_CMD_OTP_ACCESS (0x65) 137#define ONENAND_CMD_OTP_ACCESS (0x65)
136#define ONENAND_CMD_READID (0x90) 138#define ONENAND_CMD_READID (0x90)