aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/onenand
diff options
context:
space:
mode:
authorRichard Purdie <rpurdie@rpsys.net>2008-02-06 05:18:22 -0500
committerDavid Woodhouse <dwmw2@infradead.org>2008-02-07 05:31:23 -0500
commit6c77fd649fab4bea1b44cb534381a22e37650bc3 (patch)
tree757f0048c4422b0872d425f9e8c4a30688001e82 /drivers/mtd/onenand
parent621e4f8e9b208245d1f64eac7e6782b7aa506c21 (diff)
[MTD] onenand: Add panic_write function to the onenand driver
Implement the panic_write function for the onenand driver. This waits for any active command to complete/timeout, performs the write, waits for it to complete and then returns. Signed-off-by: Richard Purdie <rpurdie@rpsys.net> Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'drivers/mtd/onenand')
-rw-r--r--drivers/mtd/onenand/onenand_base.c108
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
1288static 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 */
1312static 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;