diff options
Diffstat (limited to 'drivers/mtd/onenand/onenand_base.c')
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 77 |
1 files changed, 51 insertions, 26 deletions
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 7f1cb6e5dccb..9e14a26ca4e8 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
@@ -4,6 +4,11 @@ | |||
4 | * Copyright (C) 2005-2007 Samsung Electronics | 4 | * Copyright (C) 2005-2007 Samsung Electronics |
5 | * Kyungmin Park <kyungmin.park@samsung.com> | 5 | * Kyungmin Park <kyungmin.park@samsung.com> |
6 | * | 6 | * |
7 | * Credits: | ||
8 | * Adrian Hunter <ext-adrian.hunter@nokia.com>: | ||
9 | * auto-placement support, read-while load support, various fixes | ||
10 | * Copyright (C) Nokia Corporation, 2007 | ||
11 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | 12 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 13 | * it under the terms of the GNU General Public License version 2 as |
9 | * published by the Free Software Foundation. | 14 | * published by the Free Software Foundation. |
@@ -831,7 +836,7 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col | |||
831 | int readcol = column; | 836 | int readcol = column; |
832 | int readend = column + thislen; | 837 | int readend = column + thislen; |
833 | int lastgap = 0; | 838 | int lastgap = 0; |
834 | uint8_t *oob_buf = this->page_buf + mtd->writesize; | 839 | uint8_t *oob_buf = this->oob_buf; |
835 | 840 | ||
836 | for (free = this->ecclayout->oobfree; free->length; ++free) { | 841 | for (free = this->ecclayout->oobfree; free->length; ++free) { |
837 | if (readcol >= lastgap) | 842 | if (readcol >= lastgap) |
@@ -849,7 +854,8 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col | |||
849 | int n = ed - st; | 854 | int n = ed - st; |
850 | memcpy(buf, oob_buf + st, n); | 855 | memcpy(buf, oob_buf + st, n); |
851 | buf += n; | 856 | buf += n; |
852 | } | 857 | } else |
858 | break; | ||
853 | } | 859 | } |
854 | return 0; | 860 | return 0; |
855 | } | 861 | } |
@@ -947,9 +953,9 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
947 | 953 | ||
948 | /** | 954 | /** |
949 | * onenand_read_oob - [MTD Interface] NAND write data and/or out-of-band | 955 | * onenand_read_oob - [MTD Interface] NAND write data and/or out-of-band |
950 | * @mtd: MTD device structure | 956 | * @param mtd: MTD device structure |
951 | * @from: offset to read from | 957 | * @param from: offset to read from |
952 | * @ops: oob operation description structure | 958 | * @param ops: oob operation description structure |
953 | */ | 959 | */ |
954 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, | 960 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, |
955 | struct mtd_oob_ops *ops) | 961 | struct mtd_oob_ops *ops) |
@@ -1017,7 +1023,7 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state) | |||
1017 | * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan | 1023 | * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan |
1018 | * @param mtd MTD device structure | 1024 | * @param mtd MTD device structure |
1019 | * @param from offset to read from | 1025 | * @param from offset to read from |
1020 | * @param @ops oob operation description structure | 1026 | * @param ops oob operation description structure |
1021 | * | 1027 | * |
1022 | * OneNAND read out-of-band data from the spare area for bbt scan | 1028 | * OneNAND read out-of-band data from the spare area for bbt scan |
1023 | */ | 1029 | */ |
@@ -1093,7 +1099,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | |||
1093 | static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) | 1099 | static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) |
1094 | { | 1100 | { |
1095 | struct onenand_chip *this = mtd->priv; | 1101 | struct onenand_chip *this = mtd->priv; |
1096 | char *readp = this->page_buf + mtd->writesize; | 1102 | char oobbuf[64]; |
1097 | int status, i; | 1103 | int status, i; |
1098 | 1104 | ||
1099 | this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); | 1105 | this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); |
@@ -1102,9 +1108,9 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to | |||
1102 | if (status) | 1108 | if (status) |
1103 | return status; | 1109 | return status; |
1104 | 1110 | ||
1105 | this->read_bufferram(mtd, ONENAND_SPARERAM, readp, 0, mtd->oobsize); | 1111 | this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); |
1106 | for(i = 0; i < mtd->oobsize; i++) | 1112 | for (i = 0; i < mtd->oobsize; i++) |
1107 | if (buf[i] != 0xFF && buf[i] != readp[i]) | 1113 | if (buf[i] != 0xFF && buf[i] != oobbuf[i]) |
1108 | return -EBADMSG; | 1114 | return -EBADMSG; |
1109 | 1115 | ||
1110 | return 0; | 1116 | return 0; |
@@ -1290,7 +1296,8 @@ static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, | |||
1290 | int n = ed - st; | 1296 | int n = ed - st; |
1291 | memcpy(oob_buf + st, buf, n); | 1297 | memcpy(oob_buf + st, buf, n); |
1292 | buf += n; | 1298 | buf += n; |
1293 | } | 1299 | } else |
1300 | break; | ||
1294 | } | 1301 | } |
1295 | return 0; | 1302 | return 0; |
1296 | } | 1303 | } |
@@ -1312,6 +1319,7 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1312 | struct onenand_chip *this = mtd->priv; | 1319 | struct onenand_chip *this = mtd->priv; |
1313 | int column, ret = 0, oobsize; | 1320 | int column, ret = 0, oobsize; |
1314 | int written = 0; | 1321 | int written = 0; |
1322 | u_char *oobbuf; | ||
1315 | 1323 | ||
1316 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); | 1324 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); |
1317 | 1325 | ||
@@ -1331,7 +1339,7 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1331 | } | 1339 | } |
1332 | 1340 | ||
1333 | /* For compatibility with NAND: Do not allow write past end of page */ | 1341 | /* For compatibility with NAND: Do not allow write past end of page */ |
1334 | if (column + len > oobsize) { | 1342 | if (unlikely(column + len > oobsize)) { |
1335 | printk(KERN_ERR "onenand_write_oob: " | 1343 | printk(KERN_ERR "onenand_write_oob: " |
1336 | "Attempt to write past end of page\n"); | 1344 | "Attempt to write past end of page\n"); |
1337 | return -EINVAL; | 1345 | return -EINVAL; |
@@ -1348,6 +1356,8 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1348 | /* Grab the lock and see if the device is available */ | 1356 | /* Grab the lock and see if the device is available */ |
1349 | onenand_get_device(mtd, FL_WRITING); | 1357 | onenand_get_device(mtd, FL_WRITING); |
1350 | 1358 | ||
1359 | oobbuf = this->oob_buf; | ||
1360 | |||
1351 | /* Loop until all data write */ | 1361 | /* Loop until all data write */ |
1352 | while (written < len) { | 1362 | while (written < len) { |
1353 | int thislen = min_t(int, oobsize, len - written); | 1363 | int thislen = min_t(int, oobsize, len - written); |
@@ -1358,12 +1368,12 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1358 | 1368 | ||
1359 | /* We send data to spare ram with oobsize | 1369 | /* We send data to spare ram with oobsize |
1360 | * to prevent byte access */ | 1370 | * to prevent byte access */ |
1361 | memset(this->page_buf, 0xff, mtd->oobsize); | 1371 | memset(oobbuf, 0xff, mtd->oobsize); |
1362 | if (mode == MTD_OOB_AUTO) | 1372 | if (mode == MTD_OOB_AUTO) |
1363 | onenand_fill_auto_oob(mtd, this->page_buf, buf, column, thislen); | 1373 | onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen); |
1364 | else | 1374 | else |
1365 | memcpy(this->page_buf + column, buf, thislen); | 1375 | memcpy(oobbuf + column, buf, thislen); |
1366 | this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); | 1376 | this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); |
1367 | 1377 | ||
1368 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); | 1378 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); |
1369 | 1379 | ||
@@ -1375,7 +1385,7 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1375 | break; | 1385 | break; |
1376 | } | 1386 | } |
1377 | 1387 | ||
1378 | ret = onenand_verify_oob(mtd, this->page_buf, to); | 1388 | ret = onenand_verify_oob(mtd, oobbuf, to); |
1379 | if (ret) { | 1389 | if (ret) { |
1380 | printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret); | 1390 | printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret); |
1381 | break; | 1391 | break; |
@@ -1400,9 +1410,9 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1400 | 1410 | ||
1401 | /** | 1411 | /** |
1402 | * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band | 1412 | * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band |
1403 | * @mtd: MTD device structure | 1413 | * @param mtd: MTD device structure |
1404 | * @from: offset to read from | 1414 | * @param to: offset to write |
1405 | * @ops: oob operation description structure | 1415 | * @param ops: oob operation description structure |
1406 | */ | 1416 | */ |
1407 | static int onenand_write_oob(struct mtd_info *mtd, loff_t to, | 1417 | static int onenand_write_oob(struct mtd_info *mtd, loff_t to, |
1408 | struct mtd_oob_ops *ops) | 1418 | struct mtd_oob_ops *ops) |
@@ -1616,6 +1626,7 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
1616 | * @param mtd MTD device structure | 1626 | * @param mtd MTD device structure |
1617 | * @param ofs offset relative to mtd start | 1627 | * @param ofs offset relative to mtd start |
1618 | * @param len number of bytes to lock or unlock | 1628 | * @param len number of bytes to lock or unlock |
1629 | * @param cmd lock or unlock command | ||
1619 | * | 1630 | * |
1620 | * Lock or unlock one or more blocks | 1631 | * Lock or unlock one or more blocks |
1621 | */ | 1632 | */ |
@@ -2117,10 +2128,11 @@ static void onenand_check_features(struct mtd_info *mtd) | |||
2117 | } | 2128 | } |
2118 | 2129 | ||
2119 | /** | 2130 | /** |
2120 | * onenand_print_device_info - Print device ID | 2131 | * onenand_print_device_info - Print device & version ID |
2121 | * @param device device ID | 2132 | * @param device device ID |
2133 | * @param version version ID | ||
2122 | * | 2134 | * |
2123 | * Print device ID | 2135 | * Print device & version ID |
2124 | */ | 2136 | */ |
2125 | static void onenand_print_device_info(int device, int version) | 2137 | static void onenand_print_device_info(int device, int version) |
2126 | { | 2138 | { |
@@ -2320,15 +2332,25 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
2320 | 2332 | ||
2321 | /* Allocate buffers, if necessary */ | 2333 | /* Allocate buffers, if necessary */ |
2322 | if (!this->page_buf) { | 2334 | if (!this->page_buf) { |
2323 | size_t len; | 2335 | this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL); |
2324 | len = mtd->writesize + mtd->oobsize; | ||
2325 | this->page_buf = kmalloc(len, GFP_KERNEL); | ||
2326 | if (!this->page_buf) { | 2336 | if (!this->page_buf) { |
2327 | printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n"); | 2337 | printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n"); |
2328 | return -ENOMEM; | 2338 | return -ENOMEM; |
2329 | } | 2339 | } |
2330 | this->options |= ONENAND_PAGEBUF_ALLOC; | 2340 | this->options |= ONENAND_PAGEBUF_ALLOC; |
2331 | } | 2341 | } |
2342 | if (!this->oob_buf) { | ||
2343 | this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL); | ||
2344 | if (!this->oob_buf) { | ||
2345 | printk(KERN_ERR "onenand_scan(): Can't allocate oob_buf\n"); | ||
2346 | if (this->options & ONENAND_PAGEBUF_ALLOC) { | ||
2347 | this->options &= ~ONENAND_PAGEBUF_ALLOC; | ||
2348 | kfree(this->page_buf); | ||
2349 | } | ||
2350 | return -ENOMEM; | ||
2351 | } | ||
2352 | this->options |= ONENAND_OOBBUF_ALLOC; | ||
2353 | } | ||
2332 | 2354 | ||
2333 | this->state = FL_READY; | 2355 | this->state = FL_READY; |
2334 | init_waitqueue_head(&this->wq); | 2356 | init_waitqueue_head(&this->wq); |
@@ -2367,6 +2389,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
2367 | for (i = 0; this->ecclayout->oobfree[i].length; i++) | 2389 | for (i = 0; this->ecclayout->oobfree[i].length; i++) |
2368 | this->ecclayout->oobavail += | 2390 | this->ecclayout->oobavail += |
2369 | this->ecclayout->oobfree[i].length; | 2391 | this->ecclayout->oobfree[i].length; |
2392 | mtd->oobavail = this->ecclayout->oobavail; | ||
2370 | 2393 | ||
2371 | mtd->ecclayout = this->ecclayout; | 2394 | mtd->ecclayout = this->ecclayout; |
2372 | 2395 | ||
@@ -2424,9 +2447,11 @@ void onenand_release(struct mtd_info *mtd) | |||
2424 | kfree(bbm->bbt); | 2447 | kfree(bbm->bbt); |
2425 | kfree(this->bbm); | 2448 | kfree(this->bbm); |
2426 | } | 2449 | } |
2427 | /* Buffer allocated by onenand_scan */ | 2450 | /* Buffers allocated by onenand_scan */ |
2428 | if (this->options & ONENAND_PAGEBUF_ALLOC) | 2451 | if (this->options & ONENAND_PAGEBUF_ALLOC) |
2429 | kfree(this->page_buf); | 2452 | kfree(this->page_buf); |
2453 | if (this->options & ONENAND_OOBBUF_ALLOC) | ||
2454 | kfree(this->oob_buf); | ||
2430 | } | 2455 | } |
2431 | 2456 | ||
2432 | EXPORT_SYMBOL_GPL(onenand_scan); | 2457 | EXPORT_SYMBOL_GPL(onenand_scan); |