diff options
| -rw-r--r-- | drivers/mtd/nand/nand_base.c | 7 | ||||
| -rw-r--r-- | drivers/mtd/nand/nand_util.c | 93 | 
2 files changed, 63 insertions, 37 deletions
| diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 7d178468a..276dbfdd1 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 29c42f73b..0f6779048 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; |