diff options
Diffstat (limited to 'drivers/mtd/nand/nand_util.c')
-rw-r--r-- | drivers/mtd/nand/nand_util.c | 93 |
1 files changed, 63 insertions, 30 deletions
diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 29c42f7..0f67790 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; |