diff options
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index b281b116aaeb..8d7d21be1541 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
20 | #include <linux/sched.h> | 20 | #include <linux/sched.h> |
21 | #include <linux/delay.h> | ||
21 | #include <linux/interrupt.h> | 22 | #include <linux/interrupt.h> |
22 | #include <linux/jiffies.h> | 23 | #include <linux/jiffies.h> |
23 | #include <linux/mtd/mtd.h> | 24 | #include <linux/mtd/mtd.h> |
@@ -1284,6 +1285,112 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, | |||
1284 | 1285 | ||
1285 | #define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0) | 1286 | #define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0) |
1286 | 1287 | ||
1288 | static void onenand_panic_wait(struct mtd_info *mtd) | ||
1289 | { | ||
1290 | struct onenand_chip *this = mtd->priv; | ||
1291 | unsigned int interrupt; | ||
1292 | int i; | ||
1293 | |||
1294 | for (i = 0; i < 2000; i++) { | ||
1295 | interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); | ||
1296 | if (interrupt & ONENAND_INT_MASTER) | ||
1297 | break; | ||
1298 | udelay(10); | ||
1299 | } | ||
1300 | } | ||
1301 | |||
1302 | /** | ||
1303 | * onenand_panic_write - [MTD Interface] write buffer to FLASH in a panic context | ||
1304 | * @param mtd MTD device structure | ||
1305 | * @param to offset to write to | ||
1306 | * @param len number of bytes to write | ||
1307 | * @param retlen pointer to variable to store the number of written bytes | ||
1308 | * @param buf the data to write | ||
1309 | * | ||
1310 | * Write with ECC | ||
1311 | */ | ||
1312 | static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
1313 | size_t *retlen, const u_char *buf) | ||
1314 | { | ||
1315 | struct onenand_chip *this = mtd->priv; | ||
1316 | int column, subpage; | ||
1317 | int written = 0; | ||
1318 | int ret = 0; | ||
1319 | |||
1320 | if (this->state == FL_PM_SUSPENDED) | ||
1321 | return -EBUSY; | ||
1322 | |||
1323 | /* Wait for any existing operation to clear */ | ||
1324 | onenand_panic_wait(mtd); | ||
1325 | |||
1326 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_panic_write: to = 0x%08x, len = %i\n", | ||
1327 | (unsigned int) to, (int) len); | ||
1328 | |||
1329 | /* Initialize retlen, in case of early exit */ | ||
1330 | *retlen = 0; | ||
1331 | |||
1332 | /* Do not allow writes past end of device */ | ||
1333 | if (unlikely((to + len) > mtd->size)) { | ||
1334 | printk(KERN_ERR "onenand_panic_write: Attempt write to past end of device\n"); | ||
1335 | return -EINVAL; | ||
1336 | } | ||
1337 | |||
1338 | /* Reject writes, which are not page aligned */ | ||
1339 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { | ||
1340 | printk(KERN_ERR "onenand_panic_write: Attempt to write not page aligned data\n"); | ||
1341 | return -EINVAL; | ||
1342 | } | ||
1343 | |||
1344 | column = to & (mtd->writesize - 1); | ||
1345 | |||
1346 | /* Loop until all data write */ | ||
1347 | while (written < len) { | ||
1348 | int thislen = min_t(int, mtd->writesize - column, len - written); | ||
1349 | u_char *wbuf = (u_char *) buf; | ||
1350 | |||
1351 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); | ||
1352 | |||
1353 | /* Partial page write */ | ||
1354 | subpage = thislen < mtd->writesize; | ||
1355 | if (subpage) { | ||
1356 | memset(this->page_buf, 0xff, mtd->writesize); | ||
1357 | memcpy(this->page_buf + column, buf, thislen); | ||
1358 | wbuf = this->page_buf; | ||
1359 | } | ||
1360 | |||
1361 | this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); | ||
1362 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); | ||
1363 | |||
1364 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); | ||
1365 | |||
1366 | onenand_panic_wait(mtd); | ||
1367 | |||
1368 | /* In partial page write we don't update bufferram */ | ||
1369 | onenand_update_bufferram(mtd, to, !ret && !subpage); | ||
1370 | if (ONENAND_IS_2PLANE(this)) { | ||
1371 | ONENAND_SET_BUFFERRAM1(this); | ||
1372 | onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage); | ||
1373 | } | ||
1374 | |||
1375 | if (ret) { | ||
1376 | printk(KERN_ERR "onenand_panic_write: write failed %d\n", ret); | ||
1377 | break; | ||
1378 | } | ||
1379 | |||
1380 | written += thislen; | ||
1381 | |||
1382 | if (written == len) | ||
1383 | break; | ||
1384 | |||
1385 | column = 0; | ||
1386 | to += thislen; | ||
1387 | buf += thislen; | ||
1388 | } | ||
1389 | |||
1390 | *retlen = written; | ||
1391 | return ret; | ||
1392 | } | ||
1393 | |||
1287 | /** | 1394 | /** |
1288 | * onenand_fill_auto_oob - [Internal] oob auto-placement transfer | 1395 | * onenand_fill_auto_oob - [Internal] oob auto-placement transfer |
1289 | * @param mtd MTD device structure | 1396 | * @param mtd MTD device structure |
@@ -2673,6 +2780,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
2673 | mtd->write = onenand_write; | 2780 | mtd->write = onenand_write; |
2674 | mtd->read_oob = onenand_read_oob; | 2781 | mtd->read_oob = onenand_read_oob; |
2675 | mtd->write_oob = onenand_write_oob; | 2782 | mtd->write_oob = onenand_write_oob; |
2783 | mtd->panic_write = onenand_panic_write; | ||
2676 | #ifdef CONFIG_MTD_ONENAND_OTP | 2784 | #ifdef CONFIG_MTD_ONENAND_OTP |
2677 | mtd->get_fact_prot_info = onenand_get_fact_prot_info; | 2785 | mtd->get_fact_prot_info = onenand_get_fact_prot_info; |
2678 | mtd->read_fact_prot_reg = onenand_read_fact_prot_reg; | 2786 | mtd->read_fact_prot_reg = onenand_read_fact_prot_reg; |