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; |
