diff options
| author | Stefan Roese <sr@denx.de> | 2008-08-13 06:47:12 +0200 | 
|---|---|---|
| committer | Stefan Roese <sr@denx.de> | 2008-08-13 06:47:12 +0200 | 
| commit | 5a7ddf4e1fb9347f783eb1473c30187d7a22bd81 (patch) | |
| tree | 5e30fc06d7bbd5b382b1a7b89f57cd81a5246f32 /drivers/mtd/nand/nand_util.c | |
| parent | 9939ffd5fbf1f5aff4d8172531d4fc33797c62c8 (diff) | |
| parent | 8641ff266ae6638da201747c239fd39ba34c4958 (diff) | |
| download | olio-uboot-2014.01-5a7ddf4e1fb9347f783eb1473c30187d7a22bd81.tar.xz olio-uboot-2014.01-5a7ddf4e1fb9347f783eb1473c30187d7a22bd81.zip | |
Merge branch 'master' of /home/stefan/git/u-boot/u-boot
Diffstat (limited to 'drivers/mtd/nand/nand_util.c')
| -rw-r--r-- | drivers/mtd/nand/nand_util.c | 723 | 
1 files changed, 227 insertions, 496 deletions
| diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 828cc338a..02fe914db 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -39,6 +39,9 @@  #include <malloc.h>  #include <div64.h> + +#include <asm/errno.h> +#include <linux/mtd/mtd.h>  #include <nand.h>  #include <jffs2/jffs2.h> @@ -69,71 +72,33 @@ static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip)  int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)  {  	struct jffs2_unknown_node cleanmarker; -	int clmpos = 0; -	int clmlen = 8;  	erase_info_t erase;  	ulong erase_length; -	int isNAND;  	int bbtest = 1;  	int result;  	int percent_complete = -1;  	int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL;  	const char *mtd_device = meminfo->name; +	struct mtd_oob_ops oob_opts; +	struct nand_chip *chip = meminfo->priv; +	uint8_t buf[64]; +	memset(buf, 0, sizeof(buf));  	memset(&erase, 0, sizeof(erase)); +	memset(&oob_opts, 0, sizeof(oob_opts));  	erase.mtd = meminfo;  	erase.len  = meminfo->erasesize;  	erase.addr = opts->offset;  	erase_length = opts->length; -	isNAND = meminfo->type == MTD_NANDFLASH ? 1 : 0; - -	if (opts->jffs2) { -		cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); -		cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); -		if (isNAND) { -			struct nand_oobinfo *oobinfo = &meminfo->oobinfo; - -			/* check for autoplacement */ -			if (oobinfo->useecc == MTD_NANDECC_AUTOPLACE) { -				/* get the position of the free bytes */ -				if (!oobinfo->oobfree[0][1]) { -					printf(" Eeep. Autoplacement selected " -					       "and no empty space in oob\n"); -					return -1; -				} -				clmpos = oobinfo->oobfree[0][0]; -				clmlen = oobinfo->oobfree[0][1]; -				if (clmlen > 8) -					clmlen = 8; -			} else { -				/* legacy mode */ -				switch (meminfo->oobsize) { -				case 8: -					clmpos = 6; -					clmlen = 2; -					break; -				case 16: -					clmpos = 8; -					clmlen = 8; -					break; -				case 64: -					clmpos = 16; -					clmlen = 8; -					break; -				} -			} -			cleanmarker.totlen = cpu_to_je32(8); -		} else { -			cleanmarker.totlen = -				cpu_to_je32(sizeof(struct jffs2_unknown_node)); -		} -		cleanmarker.hdr_crc =  cpu_to_je32( -			crc32_no_comp(0, (unsigned char *) &cleanmarker, -				      sizeof(struct jffs2_unknown_node) - 4)); -	} +	cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); +	cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); +	cleanmarker.totlen = cpu_to_je32(8); +	cleanmarker.hdr_crc = cpu_to_je32( +	crc32_no_comp(0, (unsigned char *) &cleanmarker, +	sizeof(struct jffs2_unknown_node) - 4));  	/* scrub option allows to erase badblock. To prevent internal  	 * check from erase() method, set block check method to dummy @@ -194,25 +159,21 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)  		/* format for JFFS2 ? */  		if (opts->jffs2) { -			/* write cleanmarker */ -			if (isNAND) { -				size_t written; -				result = meminfo->write_oob(meminfo, -							    erase.addr + clmpos, -							    clmlen, -							    &written, -							    (unsigned char *) -							    &cleanmarker); -				if (result != 0) { -					printf("\n%s: MTD writeoob failure: %d\n", -					       mtd_device, result); -					continue; -				} -			} else { -				printf("\n%s: this erase routine only supports" -				       " NAND devices!\n", -				       mtd_device); +			chip->ops.len = chip->ops.ooblen = 64; +			chip->ops.datbuf = NULL; +			chip->ops.oobbuf = buf; +			chip->ops.ooboffs = chip->badblockpos & ~0x01; + +			result = meminfo->write_oob(meminfo, +							erase.addr + meminfo->oobsize, +							&chip->ops); +			if (result != 0) { +				printf("\n%s: MTD writeoob failure: %d\n", +				mtd_device, result); +				continue;  			} +			else +				printf("%s: MTD writeoob at 0x%08x\n",mtd_device, erase.addr + meminfo->oobsize );  		}  		if (!opts->quiet) { @@ -232,11 +193,11 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)  				percent_complete = percent;  				printf("\rErasing at 0x%x -- %3d%% complete.", -				       erase.addr, percent); +				erase.addr, percent);  				if (opts->jffs2 && result == 0) -					printf(" Cleanmarker written at 0x%x.", -					       erase.addr); +				printf(" Cleanmarker written at 0x%x.", +				erase.addr);  			}  		}  	} @@ -253,6 +214,9 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)  	return 0;  } +/* XXX U-BOOT XXX */ +#if 0 +  #define MAX_PAGE_SIZE	2048  #define MAX_OOB_SIZE	64 @@ -263,443 +227,29 @@ static unsigned char data_buf[MAX_PAGE_SIZE];  static unsigned char oob_buf[MAX_OOB_SIZE];  /* OOB layouts to pass into the kernel as default */ -static struct nand_oobinfo none_oobinfo = { +static struct nand_ecclayout none_ecclayout = {  	.useecc = MTD_NANDECC_OFF,  }; -static struct nand_oobinfo jffs2_oobinfo = { +static struct nand_ecclayout jffs2_ecclayout = {  	.useecc = MTD_NANDECC_PLACE,  	.eccbytes = 6,  	.eccpos = { 0, 1, 2, 3, 6, 7 }  }; -static struct nand_oobinfo yaffs_oobinfo = { +static struct nand_ecclayout yaffs_ecclayout = {  	.useecc = MTD_NANDECC_PLACE,  	.eccbytes = 6,  	.eccpos = { 8, 9, 10, 13, 14, 15}  }; -static struct nand_oobinfo autoplace_oobinfo = { +static struct nand_ecclayout autoplace_ecclayout = {  	.useecc = MTD_NANDECC_AUTOPLACE  }; +#endif -/** - * nand_write_opts: - write image to NAND flash with support for various options - * - * @param meminfo	NAND device to erase - * @param opts		write options (@see nand_write_options) - * @return		0 in case of success - * - * This code is ported from nandwrite.c from Linux mtd utils by - * Steven J. Hill and Thomas Gleixner. - */ -int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) -{ -	int imglen = 0; -	int pagelen; -	int baderaseblock; -	int blockstart = -1; -	loff_t offs; -	int readlen; -	int oobinfochanged = 0; -	int percent_complete = -1; -	struct nand_oobinfo old_oobinfo; -	ulong mtdoffset = opts->offset; -	ulong erasesize_blockalign; -	u_char *buffer = opts->buffer; -	size_t written; -	int result; - -	if (opts->pad && opts->writeoob) { -		printf("Can't pad when oob data is present.\n"); -		return -1; -	} - -	/* set erasesize to specified number of blocks - to match -	 * jffs2 (virtual) block size */ -	if (opts->blockalign == 0) { -		erasesize_blockalign = meminfo->erasesize; -	} else { -		erasesize_blockalign = meminfo->erasesize * opts->blockalign; -	} - -	/* make sure device page sizes are valid */ -	if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) -	    && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) -	    && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { -		printf("Unknown flash (not normal NAND)\n"); -		return -1; -	} - -	/* read the current oob info */ -	memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo)); - -	/* write without ecc? */ -	if (opts->noecc) { -		memcpy(&meminfo->oobinfo, &none_oobinfo, -		       sizeof(meminfo->oobinfo)); -		oobinfochanged = 1; -	} - -	/* autoplace ECC? */ -	if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { - -		memcpy(&meminfo->oobinfo, &autoplace_oobinfo, -		       sizeof(meminfo->oobinfo)); -		oobinfochanged = 1; -	} - -	/* force OOB layout for jffs2 or yaffs? */ -	if (opts->forcejffs2 || opts->forceyaffs) { -		struct nand_oobinfo *oobsel = -			opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; - -		if (meminfo->oobsize == 8) { -			if (opts->forceyaffs) { -				printf("YAFSS cannot operate on " -				       "256 Byte page size\n"); -				goto restoreoob; -			} -			/* Adjust number of ecc bytes */ -			jffs2_oobinfo.eccbytes = 3; -		} - -		memcpy(&meminfo->oobinfo, oobsel, sizeof(meminfo->oobinfo)); -	} - -	/* get image length */ -	imglen = opts->length; -	pagelen = meminfo->oobblock -		+ ((opts->writeoob != 0) ? meminfo->oobsize : 0); - -	/* check, if file is pagealigned */ -	if ((!opts->pad) && ((imglen % pagelen) != 0)) { -		printf("Input block length is not page aligned\n"); -		goto restoreoob; -	} - -	/* check, if length fits into device */ -	if (((imglen / pagelen) * meminfo->oobblock) -	     > (meminfo->size - opts->offset)) { -		printf("Image %d bytes, NAND page %d bytes, " -		       "OOB area %u bytes, device size %u bytes\n", -		       imglen, pagelen, meminfo->oobblock, meminfo->size); -		printf("Input block does not fit into device\n"); -		goto restoreoob; -	} - -	if (!opts->quiet) -		printf("\n"); - -	/* get data from input and write to the device */ -	while (imglen && (mtdoffset < meminfo->size)) { - -		WATCHDOG_RESET (); - -		/* -		 * new eraseblock, check for bad block(s). Stay in the -		 * loop to be sure if the offset changes because of -		 * a bad block, that the next block that will be -		 * written to is also checked. Thus avoiding errors if -		 * the block(s) after the skipped block(s) is also bad -		 * (number of blocks depending on the blockalign -		 */ -		while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) { -			blockstart = mtdoffset & (~erasesize_blockalign+1); -			offs = blockstart; -			baderaseblock = 0; - -			/* check all the blocks in an erase block for -			 * bad blocks */ -			do { -				int ret = meminfo->block_isbad(meminfo, offs); - -				if (ret < 0) { -					printf("Bad block check failed\n"); -					goto restoreoob; -				} -				if (ret == 1) { -					baderaseblock = 1; -					if (!opts->quiet) -						printf("\rBad block at 0x%lx " -						       "in erase block from " -						       "0x%x will be skipped\n", -						       (long) offs, -						       blockstart); -				} - -				if (baderaseblock) { -					mtdoffset = blockstart -						+ erasesize_blockalign; -				} -				offs +=	 erasesize_blockalign -					/ opts->blockalign; -			} while (offs < blockstart + erasesize_blockalign); -		} - -		readlen = meminfo->oobblock; -		if (opts->pad && (imglen < readlen)) { -			readlen = imglen; -			memset(data_buf + readlen, 0xff, -			       meminfo->oobblock - readlen); -		} - -		/* read page data from input memory buffer */ -		memcpy(data_buf, buffer, readlen); -		buffer += readlen; - -		if (opts->writeoob) { -			/* read OOB data from input memory block, exit -			 * on failure */ -			memcpy(oob_buf, buffer, meminfo->oobsize); -			buffer += meminfo->oobsize; - -			/* write OOB data first, as ecc will be placed -			 * in there*/ -			result = meminfo->write_oob(meminfo, -						    mtdoffset, -						    meminfo->oobsize, -						    &written, -						    (unsigned char *) -						    &oob_buf); - -			if (result != 0) { -				printf("\nMTD writeoob failure: %d\n", -				       result); -				goto restoreoob; -			} -			imglen -= meminfo->oobsize; -		} - -		/* write out the page data */ -		result = meminfo->write(meminfo, -					mtdoffset, -					meminfo->oobblock, -					&written, -					(unsigned char *) &data_buf); - -		if (result != 0) { -			printf("writing NAND page at offset 0x%lx failed\n", -			       mtdoffset); -			goto restoreoob; -		} -		imglen -= readlen; - -		if (!opts->quiet) { -			unsigned long long n = (unsigned long long) -				 (opts->length-imglen) * 100; -			int percent; - -			do_div(n, opts->length); -			percent = (int)n; - -			/* output progress message only at whole percent -			 * steps to reduce the number of messages printed -			 * on (slow) serial consoles -			 */ -			if (percent != percent_complete) { -				printf("\rWriting data at 0x%lx " -				       "-- %3d%% complete.", -				       mtdoffset, percent); -				percent_complete = percent; -			} -		} - -		mtdoffset += meminfo->oobblock; -	} - -	if (!opts->quiet) -		printf("\n"); - -restoreoob: -	if (oobinfochanged) { -		memcpy(&meminfo->oobinfo, &old_oobinfo, -		       sizeof(meminfo->oobinfo)); -	} - -	if (imglen > 0) { -		printf("Data did not fit into device, due to bad blocks\n"); -		return -1; -	} - -	/* return happy */ -	return 0; -} - -/** - * nand_read_opts: - read image from NAND flash with support for various options - * - * @param meminfo	NAND device to erase - * @param opts		read options (@see struct nand_read_options) - * @return		0 in case of success - * - */ -int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) -{ -	int imglen = opts->length; -	int pagelen; -	int baderaseblock; -	int blockstart = -1; -	int percent_complete = -1; -	loff_t offs; -	size_t readlen; -	ulong mtdoffset = opts->offset; -	u_char *buffer = opts->buffer; -	int result; - -	/* make sure device page sizes are valid */ -	if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) -	    && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) -	    && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { -		printf("Unknown flash (not normal NAND)\n"); -		return -1; -	} - -	pagelen = meminfo->oobblock -		+ ((opts->readoob != 0) ? meminfo->oobsize : 0); - -	/* check, if length is not larger than device */ -	if (((imglen / pagelen) * meminfo->oobblock) -	     > (meminfo->size - opts->offset)) { -		printf("Image %d bytes, NAND page %d bytes, " -		       "OOB area %u bytes, device size %u bytes\n", -		       imglen, pagelen, meminfo->oobblock, meminfo->size); -		printf("Input block is larger than device\n"); -		return -1; -	} - -	if (!opts->quiet) -		printf("\n"); - -	/* get data from input and write to the device */ -	while (imglen && (mtdoffset < meminfo->size)) { - -		WATCHDOG_RESET (); - -		/* -		 * new eraseblock, check for bad block(s). Stay in the -		 * loop to be sure if the offset changes because of -		 * a bad block, that the next block that will be -		 * written to is also checked. Thus avoiding errors if -		 * the block(s) after the skipped block(s) is also bad -		 * (number of blocks depending on the blockalign -		 */ -		while (blockstart != (mtdoffset & (~meminfo->erasesize+1))) { -			blockstart = mtdoffset & (~meminfo->erasesize+1); -			offs = blockstart; -			baderaseblock = 0; - -			/* check all the blocks in an erase block for -			 * bad blocks */ -			do { -				int ret = meminfo->block_isbad(meminfo, offs); - -				if (ret < 0) { -					printf("Bad block check failed\n"); -					return -1; -				} -				if (ret == 1) { -					baderaseblock = 1; -					if (!opts->quiet) -						printf("\rBad block at 0x%lx " -						       "in erase block from " -						       "0x%x will be skipped\n", -						       (long) offs, -						       blockstart); -				} - -				if (baderaseblock) { -					mtdoffset = blockstart -						+ meminfo->erasesize; -				} -				offs +=	 meminfo->erasesize; - -			} while (offs < blockstart + meminfo->erasesize); -		} - - -		/* read page data to memory buffer */ -		result = meminfo->read(meminfo, -				       mtdoffset, -				       meminfo->oobblock, -				       &readlen, -				       (unsigned char *) &data_buf); - -		if (result != 0) { -			printf("reading NAND page at offset 0x%lx failed\n", -			       mtdoffset); -			return -1; -		} - -		if (imglen < readlen) { -			readlen = imglen; -		} - -		memcpy(buffer, data_buf, readlen); -		buffer += readlen; -		imglen -= readlen; - -		if (opts->readoob) { -			result = meminfo->read_oob(meminfo, -						   mtdoffset, -						   meminfo->oobsize, -						   &readlen, -						   (unsigned char *) -						   &oob_buf); - -			if (result != 0) { -				printf("\nMTD readoob failure: %d\n", -				       result); -				return -1; -			} - - -			if (imglen < readlen) { -				readlen = imglen; -			} - -			memcpy(buffer, oob_buf, readlen); - -			buffer += readlen; -			imglen -= readlen; -		} - -		if (!opts->quiet) { -			unsigned long long n = (unsigned long long) -				 (opts->length-imglen) * 100; -			int percent; - -			do_div(n, opts->length); -			percent = (int)n; - -			/* output progress message only at whole percent -			 * steps to reduce the number of messages printed -			 * on (slow) serial consoles -			 */ -			if (percent != percent_complete) { -			if (!opts->quiet) -				printf("\rReading data from 0x%lx " -				       "-- %3d%% complete.", -				       mtdoffset, percent); -				percent_complete = percent; -			} -		} - -		mtdoffset += meminfo->oobblock; -	} - -	if (!opts->quiet) -		printf("\n"); - -	if (imglen > 0) { -		printf("Could not read entire image due to bad blocks\n"); -		return -1; -	} - -	/* return happy */ -	return 0; -} - +/* XXX U-BOOT XXX */ +#if 0  /******************************************************************************   * Support for locking / unlocking operations of some NAND devices   *****************************************************************************/ @@ -784,7 +334,7 @@ int nand_get_lock_status(nand_info_t *meminfo, ulong offset)  	this->select_chip(meminfo, chipnr); -	if ((offset & (meminfo->oobblock - 1)) != 0) { +	if ((offset & (meminfo->writesize - 1)) != 0) {  		printf ("nand_get_lock_status: "  			"Start address must be beginning of "  			"nand page!\n"); @@ -813,7 +363,7 @@ int nand_get_lock_status(nand_info_t *meminfo, ulong offset)   * @param meminfo	nand mtd instance   * @param start		start byte address   * @param length	number of bytes to unlock (must be a multiple of - *			page size nand->oobblock) + *			page size nand->writesize)   *   * @return		0 on success, -1 in case of error   */ @@ -839,14 +389,14 @@ int nand_unlock(nand_info_t *meminfo, ulong start, ulong length)  		goto out;  	} -	if ((start & (meminfo->oobblock - 1)) != 0) { +	if ((start & (meminfo->writesize - 1)) != 0) {  		printf ("nand_unlock: Start address must be beginning of "  			"nand page!\n");  		ret = -1;  		goto out;  	} -	if (length == 0 || (length & (meminfo->oobblock - 1)) != 0) { +	if (length == 0 || (length & (meminfo->writesize - 1)) != 0) {  		printf ("nand_unlock: Length must be a multiple of nand page "  			"size!\n");  		ret = -1; @@ -875,5 +425,186 @@ int nand_unlock(nand_info_t *meminfo, ulong start, ulong length)  	this->select_chip(meminfo, -1);  	return ret;  } -  #endif + +/** + * get_len_incl_bad + * + * Check if 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 + */ +static size_t get_len_incl_bad (nand_info_t *nand, size_t offset, +                                const size_t length) +{ +	size_t len_incl_bad = 0; +	size_t len_excl_bad = 0; +	size_t block_len; + +	while (len_excl_bad < length) { +		block_len = nand->erasesize - (offset & (nand->erasesize - 1)); + +		if (!nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) +			len_excl_bad += block_len; + +		len_incl_bad += block_len; +		offset       += block_len; + +		if ((offset + len_incl_bad) >= nand->size) +			break; +	} + +	return len_incl_bad; +} + +/** + * nand_write_skip_bad: + * + * Write image to NAND flash. + * Blocks that are marked bad are skipped and the is written to the next + * block instead as long as the image is short enough to fit even after + * skipping the bad blocks. + * + * @param nand  	NAND device + * @param offset	offset in flash + * @param length	buffer length + * @param buf           buffer to read from + * @return		0 in case of success + */ +int nand_write_skip_bad(nand_info_t *nand, size_t offset, size_t *length, +                        u_char *buffer) +{ +	int rval; +	size_t left_to_write = *length; +	size_t len_incl_bad; +	u_char *p_buffer = buffer; + +	/* Reject writes, which are not page aligned */ +	if ((offset & (nand->writesize - 1)) != 0 || +	    (*length & (nand->writesize - 1)) != 0) { +		printf ("Attempt to write non page aligned data\n"); +		return -EINVAL; +	} + +	len_incl_bad = get_len_incl_bad (nand, offset, *length); + +	if ((offset + len_incl_bad) >= nand->size) { +		printf ("Attempt to write outside the flash area\n"); +		return -EINVAL; +	} + +	if (len_incl_bad == *length) { +		rval = nand_write (nand, offset, length, buffer); +		if (rval != 0) { +			printf ("NAND write to offset %x failed %d\n", +			        offset, rval); +			return rval; +		} +	} + +	while (left_to_write > 0) { +		size_t block_offset = offset & (nand->erasesize - 1); +		size_t write_size; + +		if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) { +			printf ("Skip bad block 0x%08x\n", +				offset & ~(nand->erasesize - 1)); +			offset += nand->erasesize - block_offset; +			continue; +		} + +		if (left_to_write < (nand->erasesize - block_offset)) +			write_size = left_to_write; +		else +			write_size = nand->erasesize - block_offset; + +		rval = nand_write (nand, offset, &write_size, p_buffer); +		if (rval != 0) { +			printf ("NAND write to offset %x failed %d\n", +			        offset, rval); +			*length -= left_to_write; +			return rval; +		} + +		left_to_write -= write_size; +		offset        += write_size; +		p_buffer      += write_size; +	} + +	return 0; +} + +/** + * nand_read_skip_bad: + * + * Read image from NAND flash. + * Blocks that are marked bad are skipped and the next block is readen + * instead as long as the image is short enough to fit even after skipping the + * bad blocks. + * + * @param nand NAND device + * @param offset offset in flash + * @param length buffer length, on return holds remaining bytes to read + * @param buffer buffer to write to + * @return 0 in case of success + */ +int nand_read_skip_bad(nand_info_t *nand, size_t offset, size_t *length, +		       u_char *buffer) +{ +	int rval; +	size_t left_to_read = *length; +	size_t len_incl_bad; +	u_char *p_buffer = buffer; + +	len_incl_bad = get_len_incl_bad (nand, offset, *length); + +	if ((offset + len_incl_bad) >= nand->size) { +		printf ("Attempt to read outside the flash area\n"); +		return -EINVAL; +	} + +	if (len_incl_bad == *length) { +		rval = nand_read (nand, offset, length, buffer); +		if (rval != 0) { +			printf ("NAND read from offset %x failed %d\n", +			        offset, rval); +			return rval; +		} +	} + +	while (left_to_read > 0) { +		size_t block_offset = offset & (nand->erasesize - 1); +		size_t read_length; + +		if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) { +			printf ("Skipping bad block 0x%08x\n", +				offset & ~(nand->erasesize - 1)); +			offset += nand->erasesize - block_offset; +			continue; +		} + +		if (left_to_read < (nand->erasesize - block_offset)) +			read_length = left_to_read; +		else +			read_length = nand->erasesize - block_offset; + +		rval = nand_read (nand, offset, &read_length, p_buffer); +		if (rval != 0) { +			printf ("NAND read from offset %x failed %d\n", +			        offset, rval); +			*length -= left_to_read; +			return rval; +		} + +		left_to_read -= read_length; +		offset       += read_length; +		p_buffer     += read_length; +	} + +	return 0; +} + +#endif /* defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) */ |