nand util: read/write: accept unaligned length
authorScott Wood <scottwood@freescale.com>
Fri, 30 Jul 2010 21:11:41 +0000 (16:11 -0500)
committerScott Wood <scottwood@freescale.com>
Mon, 11 Oct 2010 20:09:23 +0000 (15:09 -0500)
The underlying code in nand_base.c already supports non-page-aligned reads
and writes, but the block-skipping wrapper code did not.

With block skipping, an unaligned start address is not useful since you
really want to be starting at the beginning of a partition -- or at least
that's where you want to start checking for blocks to skip, but we don't
(yet) support that.  So we still require the start address to be aligned.

An unaligned length, though, is useful for passing $filesize to the
read/write command, and handling it does not complicate block skipping.

Signed-off-by: Scott Wood <scottwood@freescale.com>
Tested-by: Ben Gardiner <bengardiner@nanometrics.ca>
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_util.c

index 7d178468adbe4fa0954b1d5f932a35c1f0deafdf..276dbfdd1d07b030a0802506b18641cd07e08564 100644 (file)
@@ -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));
 
index 29c42f73b1cc4c3c2131a332e68763937ed49585..0f67790488bcdfdeaf43d6f94b388de41beb3c82 100644 (file)
  * 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;