diff options
author | David Woodhouse <dwmw2@infradead.org> | 2006-11-28 19:03:10 -0500 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2006-11-28 19:03:10 -0500 |
commit | 103e40f6330306753ba11548d53ff25144216236 (patch) | |
tree | daf0e38e82ccc8fabbc314adcddd6ed93602ade9 /drivers | |
parent | 95b93a0cd46682c6d9e8eea803fda510cb6b863a (diff) | |
parent | f4f91ac3c833abbd7181ff2122c6b48a653b4e55 (diff) |
Merge git://git.infradead.org/~kmpark/onenand-mtd-2.6
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/onenand/generic.c | 1 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 188 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_bbt.c | 1 |
3 files changed, 169 insertions, 21 deletions
diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index 53eb5632bdbb..3d44d040a47d 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c | |||
@@ -61,6 +61,7 @@ static int __devinit generic_onenand_probe(struct device *dev) | |||
61 | } | 61 | } |
62 | 62 | ||
63 | info->onenand.mmcontrol = pdata->mmcontrol; | 63 | info->onenand.mmcontrol = pdata->mmcontrol; |
64 | info->onenand.irq = platform_get_irq(pdev, 0); | ||
64 | 65 | ||
65 | info->mtd.name = pdev->dev.bus_id; | 66 | info->mtd.name = pdev->dev.bus_id; |
66 | info->mtd.priv = &info->onenand; | 67 | info->mtd.priv = &info->onenand; |
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 8ed68b28afe3..fc84ddc4987f 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | #include <linux/sched.h> | 15 | #include <linux/sched.h> |
16 | #include <linux/interrupt.h> | ||
16 | #include <linux/jiffies.h> | 17 | #include <linux/jiffies.h> |
17 | #include <linux/mtd/mtd.h> | 18 | #include <linux/mtd/mtd.h> |
18 | #include <linux/mtd/onenand.h> | 19 | #include <linux/mtd/onenand.h> |
@@ -330,15 +331,123 @@ static int onenand_wait(struct mtd_info *mtd, int state) | |||
330 | 331 | ||
331 | if (interrupt & ONENAND_INT_READ) { | 332 | if (interrupt & ONENAND_INT_READ) { |
332 | ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | 333 | ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); |
333 | if (ecc & ONENAND_ECC_2BIT_ALL) { | 334 | if (ecc) { |
334 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); | 335 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); |
335 | return -EBADMSG; | 336 | if (ecc & ONENAND_ECC_2BIT_ALL) |
337 | mtd->ecc_stats.failed++; | ||
338 | else if (ecc & ONENAND_ECC_1BIT_ALL) | ||
339 | mtd->ecc_stats.corrected++; | ||
336 | } | 340 | } |
337 | } | 341 | } |
338 | 342 | ||
339 | return 0; | 343 | return 0; |
340 | } | 344 | } |
341 | 345 | ||
346 | /* | ||
347 | * onenand_interrupt - [DEFAULT] onenand interrupt handler | ||
348 | * @param irq onenand interrupt number | ||
349 | * @param dev_id interrupt data | ||
350 | * | ||
351 | * complete the work | ||
352 | */ | ||
353 | static irqreturn_t onenand_interrupt(int irq, void *data) | ||
354 | { | ||
355 | struct onenand_chip *this = (struct onenand_chip *) data; | ||
356 | |||
357 | /* To handle shared interrupt */ | ||
358 | if (!this->complete.done) | ||
359 | complete(&this->complete); | ||
360 | |||
361 | return IRQ_HANDLED; | ||
362 | } | ||
363 | |||
364 | /* | ||
365 | * onenand_interrupt_wait - [DEFAULT] wait until the command is done | ||
366 | * @param mtd MTD device structure | ||
367 | * @param state state to select the max. timeout value | ||
368 | * | ||
369 | * Wait for command done. | ||
370 | */ | ||
371 | static int onenand_interrupt_wait(struct mtd_info *mtd, int state) | ||
372 | { | ||
373 | struct onenand_chip *this = mtd->priv; | ||
374 | |||
375 | /* To prevent soft lockup */ | ||
376 | touch_softlockup_watchdog(); | ||
377 | |||
378 | wait_for_completion(&this->complete); | ||
379 | |||
380 | return onenand_wait(mtd, state); | ||
381 | } | ||
382 | |||
383 | /* | ||
384 | * onenand_try_interrupt_wait - [DEFAULT] try interrupt wait | ||
385 | * @param mtd MTD device structure | ||
386 | * @param state state to select the max. timeout value | ||
387 | * | ||
388 | * Try interrupt based wait (It is used one-time) | ||
389 | */ | ||
390 | static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state) | ||
391 | { | ||
392 | struct onenand_chip *this = mtd->priv; | ||
393 | unsigned long remain, timeout; | ||
394 | |||
395 | /* We use interrupt wait first */ | ||
396 | this->wait = onenand_interrupt_wait; | ||
397 | |||
398 | /* To prevent soft lockup */ | ||
399 | touch_softlockup_watchdog(); | ||
400 | |||
401 | timeout = msecs_to_jiffies(100); | ||
402 | remain = wait_for_completion_timeout(&this->complete, timeout); | ||
403 | if (!remain) { | ||
404 | printk(KERN_INFO "OneNAND: There's no interrupt. " | ||
405 | "We use the normal wait\n"); | ||
406 | |||
407 | /* Release the irq */ | ||
408 | free_irq(this->irq, this); | ||
409 | |||
410 | this->wait = onenand_wait; | ||
411 | } | ||
412 | |||
413 | return onenand_wait(mtd, state); | ||
414 | } | ||
415 | |||
416 | /* | ||
417 | * onenand_setup_wait - [OneNAND Interface] setup onenand wait method | ||
418 | * @param mtd MTD device structure | ||
419 | * | ||
420 | * There's two method to wait onenand work | ||
421 | * 1. polling - read interrupt status register | ||
422 | * 2. interrupt - use the kernel interrupt method | ||
423 | */ | ||
424 | static void onenand_setup_wait(struct mtd_info *mtd) | ||
425 | { | ||
426 | struct onenand_chip *this = mtd->priv; | ||
427 | int syscfg; | ||
428 | |||
429 | init_completion(&this->complete); | ||
430 | |||
431 | if (this->irq <= 0) { | ||
432 | this->wait = onenand_wait; | ||
433 | return; | ||
434 | } | ||
435 | |||
436 | if (request_irq(this->irq, &onenand_interrupt, | ||
437 | IRQF_SHARED, "onenand", this)) { | ||
438 | /* If we can't get irq, use the normal wait */ | ||
439 | this->wait = onenand_wait; | ||
440 | return; | ||
441 | } | ||
442 | |||
443 | /* Enable interrupt */ | ||
444 | syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); | ||
445 | syscfg |= ONENAND_SYS_CFG1_IOBE; | ||
446 | this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); | ||
447 | |||
448 | this->wait = onenand_try_interrupt_wait; | ||
449 | } | ||
450 | |||
342 | /** | 451 | /** |
343 | * onenand_bufferram_offset - [DEFAULT] BufferRAM offset | 452 | * onenand_bufferram_offset - [DEFAULT] BufferRAM offset |
344 | * @param mtd MTD data structure | 453 | * @param mtd MTD data structure |
@@ -609,6 +718,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
609 | size_t *retlen, u_char *buf) | 718 | size_t *retlen, u_char *buf) |
610 | { | 719 | { |
611 | struct onenand_chip *this = mtd->priv; | 720 | struct onenand_chip *this = mtd->priv; |
721 | struct mtd_ecc_stats stats; | ||
612 | int read = 0, column; | 722 | int read = 0, column; |
613 | int thislen; | 723 | int thislen; |
614 | int ret = 0; | 724 | int ret = 0; |
@@ -627,6 +737,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
627 | 737 | ||
628 | /* TODO handling oob */ | 738 | /* TODO handling oob */ |
629 | 739 | ||
740 | stats = mtd->ecc_stats; | ||
630 | while (read < len) { | 741 | while (read < len) { |
631 | thislen = min_t(int, mtd->writesize, len - read); | 742 | thislen = min_t(int, mtd->writesize, len - read); |
632 | 743 | ||
@@ -668,7 +779,11 @@ out: | |||
668 | * retlen == desired len and result == -EBADMSG | 779 | * retlen == desired len and result == -EBADMSG |
669 | */ | 780 | */ |
670 | *retlen = read; | 781 | *retlen = read; |
671 | return ret; | 782 | |
783 | if (mtd->ecc_stats.failed - stats.failed) | ||
784 | return -EBADMSG; | ||
785 | |||
786 | return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; | ||
672 | } | 787 | } |
673 | 788 | ||
674 | /** | 789 | /** |
@@ -1129,7 +1244,6 @@ static void onenand_sync(struct mtd_info *mtd) | |||
1129 | onenand_release_device(mtd); | 1244 | onenand_release_device(mtd); |
1130 | } | 1245 | } |
1131 | 1246 | ||
1132 | |||
1133 | /** | 1247 | /** |
1134 | * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad | 1248 | * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad |
1135 | * @param mtd MTD device structure | 1249 | * @param mtd MTD device structure |
@@ -1196,32 +1310,38 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
1196 | } | 1310 | } |
1197 | 1311 | ||
1198 | /** | 1312 | /** |
1199 | * onenand_unlock - [MTD Interface] Unlock block(s) | 1313 | * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s) |
1200 | * @param mtd MTD device structure | 1314 | * @param mtd MTD device structure |
1201 | * @param ofs offset relative to mtd start | 1315 | * @param ofs offset relative to mtd start |
1202 | * @param len number of bytes to unlock | 1316 | * @param len number of bytes to lock or unlock |
1203 | * | 1317 | * |
1204 | * Unlock one or more blocks | 1318 | * Lock or unlock one or more blocks |
1205 | */ | 1319 | */ |
1206 | static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | 1320 | static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd) |
1207 | { | 1321 | { |
1208 | struct onenand_chip *this = mtd->priv; | 1322 | struct onenand_chip *this = mtd->priv; |
1209 | int start, end, block, value, status; | 1323 | int start, end, block, value, status; |
1324 | int wp_status_mask; | ||
1210 | 1325 | ||
1211 | start = ofs >> this->erase_shift; | 1326 | start = ofs >> this->erase_shift; |
1212 | end = len >> this->erase_shift; | 1327 | end = len >> this->erase_shift; |
1213 | 1328 | ||
1329 | if (cmd == ONENAND_CMD_LOCK) | ||
1330 | wp_status_mask = ONENAND_WP_LS; | ||
1331 | else | ||
1332 | wp_status_mask = ONENAND_WP_US; | ||
1333 | |||
1214 | /* Continuous lock scheme */ | 1334 | /* Continuous lock scheme */ |
1215 | if (this->options & ONENAND_HAS_CONT_LOCK) { | 1335 | if (this->options & ONENAND_HAS_CONT_LOCK) { |
1216 | /* Set start block address */ | 1336 | /* Set start block address */ |
1217 | this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); | 1337 | this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); |
1218 | /* Set end block address */ | 1338 | /* Set end block address */ |
1219 | this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); | 1339 | this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); |
1220 | /* Write unlock command */ | 1340 | /* Write lock command */ |
1221 | this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); | 1341 | this->command(mtd, cmd, 0, 0); |
1222 | 1342 | ||
1223 | /* There's no return value */ | 1343 | /* There's no return value */ |
1224 | this->wait(mtd, FL_UNLOCKING); | 1344 | this->wait(mtd, FL_LOCKING); |
1225 | 1345 | ||
1226 | /* Sanity check */ | 1346 | /* Sanity check */ |
1227 | while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) | 1347 | while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) |
@@ -1230,7 +1350,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
1230 | 1350 | ||
1231 | /* Check lock status */ | 1351 | /* Check lock status */ |
1232 | status = this->read_word(this->base + ONENAND_REG_WP_STATUS); | 1352 | status = this->read_word(this->base + ONENAND_REG_WP_STATUS); |
1233 | if (!(status & ONENAND_WP_US)) | 1353 | if (!(status & wp_status_mask)) |
1234 | printk(KERN_ERR "wp status = 0x%x\n", status); | 1354 | printk(KERN_ERR "wp status = 0x%x\n", status); |
1235 | 1355 | ||
1236 | return 0; | 1356 | return 0; |
@@ -1246,11 +1366,11 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
1246 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); | 1366 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); |
1247 | /* Set start block address */ | 1367 | /* Set start block address */ |
1248 | this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); | 1368 | this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); |
1249 | /* Write unlock command */ | 1369 | /* Write lock command */ |
1250 | this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); | 1370 | this->command(mtd, cmd, 0, 0); |
1251 | 1371 | ||
1252 | /* There's no return value */ | 1372 | /* There's no return value */ |
1253 | this->wait(mtd, FL_UNLOCKING); | 1373 | this->wait(mtd, FL_LOCKING); |
1254 | 1374 | ||
1255 | /* Sanity check */ | 1375 | /* Sanity check */ |
1256 | while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) | 1376 | while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) |
@@ -1259,7 +1379,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
1259 | 1379 | ||
1260 | /* Check lock status */ | 1380 | /* Check lock status */ |
1261 | status = this->read_word(this->base + ONENAND_REG_WP_STATUS); | 1381 | status = this->read_word(this->base + ONENAND_REG_WP_STATUS); |
1262 | if (!(status & ONENAND_WP_US)) | 1382 | if (!(status & wp_status_mask)) |
1263 | printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); | 1383 | printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); |
1264 | } | 1384 | } |
1265 | 1385 | ||
@@ -1267,6 +1387,32 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
1267 | } | 1387 | } |
1268 | 1388 | ||
1269 | /** | 1389 | /** |
1390 | * onenand_lock - [MTD Interface] Lock block(s) | ||
1391 | * @param mtd MTD device structure | ||
1392 | * @param ofs offset relative to mtd start | ||
1393 | * @param len number of bytes to unlock | ||
1394 | * | ||
1395 | * Lock one or more blocks | ||
1396 | */ | ||
1397 | static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | ||
1398 | { | ||
1399 | return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK); | ||
1400 | } | ||
1401 | |||
1402 | /** | ||
1403 | * onenand_unlock - [MTD Interface] Unlock block(s) | ||
1404 | * @param mtd MTD device structure | ||
1405 | * @param ofs offset relative to mtd start | ||
1406 | * @param len number of bytes to unlock | ||
1407 | * | ||
1408 | * Unlock one or more blocks | ||
1409 | */ | ||
1410 | static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | ||
1411 | { | ||
1412 | return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); | ||
1413 | } | ||
1414 | |||
1415 | /** | ||
1270 | * onenand_check_lock_status - [OneNAND Interface] Check lock status | 1416 | * onenand_check_lock_status - [OneNAND Interface] Check lock status |
1271 | * @param this onenand chip data structure | 1417 | * @param this onenand chip data structure |
1272 | * | 1418 | * |
@@ -1310,7 +1456,7 @@ static int onenand_unlock_all(struct mtd_info *mtd) | |||
1310 | this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); | 1456 | this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); |
1311 | 1457 | ||
1312 | /* There's no return value */ | 1458 | /* There's no return value */ |
1313 | this->wait(mtd, FL_UNLOCKING); | 1459 | this->wait(mtd, FL_LOCKING); |
1314 | 1460 | ||
1315 | /* Sanity check */ | 1461 | /* Sanity check */ |
1316 | while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) | 1462 | while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) |
@@ -1334,7 +1480,7 @@ static int onenand_unlock_all(struct mtd_info *mtd) | |||
1334 | return 0; | 1480 | return 0; |
1335 | } | 1481 | } |
1336 | 1482 | ||
1337 | mtd->unlock(mtd, 0x0, this->chipsize); | 1483 | onenand_unlock(mtd, 0x0, this->chipsize); |
1338 | 1484 | ||
1339 | return 0; | 1485 | return 0; |
1340 | } | 1486 | } |
@@ -1762,7 +1908,7 @@ static int onenand_probe(struct mtd_info *mtd) | |||
1762 | /* Read manufacturer and device IDs from Register */ | 1908 | /* Read manufacturer and device IDs from Register */ |
1763 | maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); | 1909 | maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); |
1764 | dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); | 1910 | dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); |
1765 | ver_id= this->read_word(this->base + ONENAND_REG_VERSION_ID); | 1911 | ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); |
1766 | 1912 | ||
1767 | /* Check OneNAND device */ | 1913 | /* Check OneNAND device */ |
1768 | if (maf_id != bram_maf_id || dev_id != bram_dev_id) | 1914 | if (maf_id != bram_maf_id || dev_id != bram_dev_id) |
@@ -1846,7 +1992,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
1846 | if (!this->command) | 1992 | if (!this->command) |
1847 | this->command = onenand_command; | 1993 | this->command = onenand_command; |
1848 | if (!this->wait) | 1994 | if (!this->wait) |
1849 | this->wait = onenand_wait; | 1995 | onenand_setup_wait(mtd); |
1850 | 1996 | ||
1851 | if (!this->read_bufferram) | 1997 | if (!this->read_bufferram) |
1852 | this->read_bufferram = onenand_read_bufferram; | 1998 | this->read_bufferram = onenand_read_bufferram; |
@@ -1922,7 +2068,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
1922 | mtd->lock_user_prot_reg = onenand_lock_user_prot_reg; | 2068 | mtd->lock_user_prot_reg = onenand_lock_user_prot_reg; |
1923 | #endif | 2069 | #endif |
1924 | mtd->sync = onenand_sync; | 2070 | mtd->sync = onenand_sync; |
1925 | mtd->lock = NULL; | 2071 | mtd->lock = onenand_lock; |
1926 | mtd->unlock = onenand_unlock; | 2072 | mtd->unlock = onenand_unlock; |
1927 | mtd->suspend = onenand_suspend; | 2073 | mtd->suspend = onenand_suspend; |
1928 | mtd->resume = onenand_resume; | 2074 | mtd->resume = onenand_resume; |
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index e3822c1cdb8d..6cceeca40567 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c | |||
@@ -100,6 +100,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
100 | bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); | 100 | bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); |
101 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", | 101 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", |
102 | i >> 1, (unsigned int) from); | 102 | i >> 1, (unsigned int) from); |
103 | mtd->ecc_stats.badblocks++; | ||
103 | break; | 104 | break; |
104 | } | 105 | } |
105 | } | 106 | } |