aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Norris <computersforpeace@gmail.com>2011-08-30 21:45:37 -0400
committerArtem Bityutskiy <artem.bityutskiy@intel.com>2011-09-11 08:02:18 -0400
commit9ce244b3fb416ce6600e05612ac46b9692dcc638 (patch)
tree34b9b77f38ce65f8efe9f9f40e71efd22bc43507
parent10a2bcae99267b28e058b089fda30de7397b69f5 (diff)
mtd: support writing OOB without ECC
This fixes issues with `nandwrite -n -o' and the MEMWRITEOOB[64] ioctls on hardware that writes ECC when writing OOB. The problem arises as follows: `nandwrite -n' can write page data to flash without applying ECC, but when used with the `-o' option, ECC is applied (incorrectly), contrary to the `--noecc' option. I found that this is the case because my hardware computes and writes ECC data to flash upon either OOB write or page write. Thus, to support a proper "no ECC" write, my driver must know when we're performing a raw OOB write vs. a normal ECC OOB write. However, MTD does not pass any raw mode information to the write_oob functions. This patch addresses the problems by: 1) Passing MTD_OOB_RAW down to lower layers, instead of just defaulting to MTD_OOB_PLACE 2) Handling MTD_OOB_RAW within the NAND layer's `nand_do_write_oob' 3) Adding a new (replaceable) function pointer in struct ecc_ctrl; this function should support writing OOB without ECC data. Current hardware often can use the same OOB write function when writing either with or without ECC This was tested with nandsim as well as on actual SLC NAND. Signed-off-by: Brian Norris <computersforpeace@gmail.com> Cc: Jim Quinlan <jim2101024@gmail.com> Signed-off-by: Artem Bityutskiy <artem.bityutskiy@intel.com>
-rw-r--r--drivers/mtd/mtdchar.c3
-rw-r--r--drivers/mtd/nand/nand_base.c10
-rw-r--r--include/linux/mtd/nand.h3
3 files changed, 14 insertions, 2 deletions
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index b20625475132..bcb7f05fd27b 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -391,6 +391,7 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,
391 uint64_t start, uint32_t length, void __user *ptr, 391 uint64_t start, uint32_t length, void __user *ptr,
392 uint32_t __user *retp) 392 uint32_t __user *retp)
393{ 393{
394 struct mtd_file_info *mfi = file->private_data;
394 struct mtd_oob_ops ops; 395 struct mtd_oob_ops ops;
395 uint32_t retlen; 396 uint32_t retlen;
396 int ret = 0; 397 int ret = 0;
@@ -412,7 +413,7 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,
412 ops.ooblen = length; 413 ops.ooblen = length;
413 ops.ooboffs = start & (mtd->writesize - 1); 414 ops.ooboffs = start & (mtd->writesize - 1);
414 ops.datbuf = NULL; 415 ops.datbuf = NULL;
415 ops.mode = MTD_OOB_PLACE; 416 ops.mode = (mfi->mode == MTD_MODE_RAW) ? MTD_OOB_RAW : MTD_OOB_PLACE;
416 417
417 if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) 418 if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
418 return -EINVAL; 419 return -EINVAL;
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 7f2691f94322..b61a7c7bd097 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2404,7 +2404,11 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
2404 chip->pagebuf = -1; 2404 chip->pagebuf = -1;
2405 2405
2406 nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops); 2406 nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops);
2407 status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); 2407
2408 if (ops->mode == MTD_OOB_RAW)
2409 status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask);
2410 else
2411 status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
2408 2412
2409 if (status) 2413 if (status)
2410 return status; 2414 return status;
@@ -3380,6 +3384,10 @@ int nand_scan_tail(struct mtd_info *mtd)
3380 BUG(); 3384 BUG();
3381 } 3385 }
3382 3386
3387 /* For many systems, the standard OOB write also works for raw */
3388 if (!chip->ecc.write_oob_raw)
3389 chip->ecc.write_oob_raw = chip->ecc.write_oob;
3390
3383 /* 3391 /*
3384 * The number of bytes available for a client to place data into 3392 * The number of bytes available for a client to place data into
3385 * the out of band area. 3393 * the out of band area.
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 85fef68a379d..5f3fdd9877b7 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -340,6 +340,7 @@ struct nand_hw_control {
340 * @read_subpage: function to read parts of the page covered by ECC. 340 * @read_subpage: function to read parts of the page covered by ECC.
341 * @write_page: function to write a page according to the ECC generator 341 * @write_page: function to write a page according to the ECC generator
342 * requirements. 342 * requirements.
343 * @write_oob_raw: function to write chip OOB data without ECC
343 * @read_oob: function to read chip OOB data 344 * @read_oob: function to read chip OOB data
344 * @write_oob: function to write chip OOB data 345 * @write_oob: function to write chip OOB data
345 */ 346 */
@@ -368,6 +369,8 @@ struct nand_ecc_ctrl {
368 uint32_t offs, uint32_t len, uint8_t *buf); 369 uint32_t offs, uint32_t len, uint8_t *buf);
369 void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, 370 void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
370 const uint8_t *buf); 371 const uint8_t *buf);
372 int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
373 int page);
371 int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page, 374 int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page,
372 int sndcmd); 375 int sndcmd);
373 int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip, 376 int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip,