aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/onenand
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw2@infradead.org>2006-11-28 19:03:10 -0500
committerDavid Woodhouse <dwmw2@infradead.org>2006-11-28 19:03:10 -0500
commit103e40f6330306753ba11548d53ff25144216236 (patch)
treedaf0e38e82ccc8fabbc314adcddd6ed93602ade9 /drivers/mtd/onenand
parent95b93a0cd46682c6d9e8eea803fda510cb6b863a (diff)
parentf4f91ac3c833abbd7181ff2122c6b48a653b4e55 (diff)
Merge git://git.infradead.org/~kmpark/onenand-mtd-2.6
Diffstat (limited to 'drivers/mtd/onenand')
-rw-r--r--drivers/mtd/onenand/generic.c1
-rw-r--r--drivers/mtd/onenand/onenand_base.c188
-rw-r--r--drivers/mtd/onenand/onenand_bbt.c1
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 */
353static 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 */
371static 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 */
390static 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 */
424static 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 */
1206static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) 1320static 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 */
1397static 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 */
1410static 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 }