diff options
-rw-r--r-- | drivers/mtd/onenand/omap2.c | 22 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 173 | ||||
-rw-r--r-- | include/linux/mtd/flashchip.h | 4 | ||||
-rw-r--r-- | include/linux/mtd/onenand_regs.h | 2 |
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 */ |
38 | static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 }; | 45 | static 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 | |||
2207 | static 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 | */ | ||
2240 | static 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) |