summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/mtd/nand/nand_base.c7
-rw-r--r--drivers/mtd/nand/nand_util.c93
2 files changed, 63 insertions, 37 deletions
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 7d178468ad..276dbfdd1d 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2001,13 +2001,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
if (!writelen)
return 0;
- /* reject writes, which are not page aligned */
- if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
- printk(KERN_NOTICE "nand_write: "
- "Attempt to write not page aligned data\n");
- return -EINVAL;
- }
-
column = to & (mtd->writesize - 1);
subpage = column || (writelen & (mtd->writesize - 1));
diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c
index 29c42f73b1..0f67790488 100644
--- a/drivers/mtd/nand/nand_util.c
+++ b/drivers/mtd/nand/nand_util.c
@@ -28,6 +28,12 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
+ * Copyright 2010 Freescale Semiconductor
+ * The portions of this file whose copyright is held by Freescale and which
+ * are not considered a derived work of GPL v2-only code may be distributed
+ * and/or modified under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
*/
#include <common.h>
@@ -423,36 +429,43 @@ int nand_unlock(struct mtd_info *mtd, ulong start, ulong length)
#endif
/**
- * get_len_incl_bad
+ * check_skip_len
*
- * Check if length including bad blocks fits into device.
+ * Check if there are any bad blocks, and whether length including bad
+ * blocks fits into device
*
* @param nand NAND device
* @param offset offset in flash
* @param length image length
- * @return image length including bad blocks
+ * @return 0 if the image fits and there are no bad blocks
+ * 1 if the image fits, but there are bad blocks
+ * -1 if the image does not fit
*/
-static size_t get_len_incl_bad (nand_info_t *nand, loff_t offset,
- const size_t length)
+static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)
{
- size_t len_incl_bad = 0;
size_t len_excl_bad = 0;
- size_t block_len;
+ int ret = 0;
while (len_excl_bad < length) {
- block_len = nand->erasesize - (offset & (nand->erasesize - 1));
+ size_t block_len, block_off;
+ loff_t block_start;
- if (!nand_block_isbad (nand, offset & ~(nand->erasesize - 1)))
- len_excl_bad += block_len;
+ if (offset >= nand->size)
+ return -1;
- len_incl_bad += block_len;
- offset += block_len;
+ block_start = offset & ~(loff_t)(nand->erasesize - 1);
+ block_off = offset & (nand->erasesize - 1);
+ block_len = nand->erasesize - block_off;
- if (offset >= nand->size)
- break;
+ if (!nand_block_isbad(nand, block_start))
+ len_excl_bad += block_len;
+ else
+ ret = 1;
+
+ offset += block_len;
}
- return len_incl_bad;
+ return ret;
}
/**
@@ -474,29 +487,41 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
{
int rval;
size_t left_to_write = *length;
- size_t len_incl_bad;
u_char *p_buffer = buffer;
+ int need_skip;
- /* Reject writes, which are not page aligned */
- if ((offset & (nand->writesize - 1)) != 0 ||
- (*length & (nand->writesize - 1)) != 0) {
+ /*
+ * nand_write() handles unaligned, partial page writes.
+ *
+ * We allow length to be unaligned, for convenience in
+ * using the $filesize variable.
+ *
+ * However, starting at an unaligned offset makes the
+ * semantics of bad block skipping ambiguous (really,
+ * you should only start a block skipping access at a
+ * partition boundary). So don't try to handle that.
+ */
+ if ((offset & (nand->writesize - 1)) != 0) {
printf ("Attempt to write non page aligned data\n");
+ *length = 0;
return -EINVAL;
}
- len_incl_bad = get_len_incl_bad (nand, offset, *length);
-
- if ((offset + len_incl_bad) > nand->size) {
+ need_skip = check_skip_len(nand, offset, *length);
+ if (need_skip < 0) {
printf ("Attempt to write outside the flash area\n");
+ *length = 0;
return -EINVAL;
}
- if (len_incl_bad == *length) {
+ if (!need_skip) {
rval = nand_write (nand, offset, length, buffer);
- if (rval != 0)
- printf ("NAND write to offset %llx failed %d\n",
- offset, rval);
+ if (rval == 0)
+ return 0;
+ *length = 0;
+ printf ("NAND write to offset %llx failed %d\n",
+ offset, rval);
return rval;
}
@@ -553,20 +578,28 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
{
int rval;
size_t left_to_read = *length;
- size_t len_incl_bad;
u_char *p_buffer = buffer;
+ int need_skip;
- len_incl_bad = get_len_incl_bad (nand, offset, *length);
+ if ((offset & (nand->writesize - 1)) != 0) {
+ printf ("Attempt to read non page aligned data\n");
+ *length = 0;
+ return -EINVAL;
+ }
- if ((offset + len_incl_bad) > nand->size) {
+ need_skip = check_skip_len(nand, offset, *length);
+ if (need_skip < 0) {
printf ("Attempt to read outside the flash area\n");
+ *length = 0;
return -EINVAL;
}
- if (len_incl_bad == *length) {
+ if (!need_skip) {
rval = nand_read (nand, offset, length, buffer);
if (!rval || rval == -EUCLEAN)
return 0;
+
+ *length = 0;
printf ("NAND read from offset %llx failed %d\n",
offset, rval);
return rval;