mtd: add support for bad blocks in NAND flash
NAND flash is very likely to contain bad blocks. Currently, mtd and therefore sysupgrade fails when it encounters a single bad block, potentially leaving an unbootable system. This patch allows the mtd utility to skip bad blocks in NAND flash and complete sysupgrade successfully. Patch by: Matthew Redfearn <matt.redfearn@nxp.com> Signed-off-by: Felix Fietkau <nbd@openwrt.org> SVN-Revision: 40021
This commit is contained in:
		@@ -59,6 +59,15 @@ static void pad(int size)
 | 
			
		||||
	}
 | 
			
		||||
	ofs = ofs % erasesize;
 | 
			
		||||
	if (ofs == 0) {
 | 
			
		||||
		while (mtd_block_is_bad(outfd, mtdofs) && (mtdofs < mtdsize)) {
 | 
			
		||||
			if (!quiet)
 | 
			
		||||
				fprintf(stderr, "\nSkipping bad block at 0x%08x   ", mtdofs);
 | 
			
		||||
 | 
			
		||||
			mtdofs += erasesize;
 | 
			
		||||
 | 
			
		||||
			/* Move the file pointer along over the bad block. */
 | 
			
		||||
			lseek(outfd, erasesize, SEEK_CUR);
 | 
			
		||||
		}
 | 
			
		||||
		mtd_erase_block(outfd, mtdofs);
 | 
			
		||||
		write(outfd, buf, erasesize);
 | 
			
		||||
		mtdofs += erasesize;
 | 
			
		||||
 
 | 
			
		||||
@@ -58,6 +58,7 @@ int no_erase;
 | 
			
		||||
int mtdsize = 0;
 | 
			
		||||
int erasesize = 0;
 | 
			
		||||
int jffs2_skip_bytes=0;
 | 
			
		||||
int mtdtype = 0;
 | 
			
		||||
 | 
			
		||||
int mtd_open(const char *mtd, bool block)
 | 
			
		||||
{
 | 
			
		||||
@@ -103,10 +104,28 @@ int mtd_check_open(const char *mtd)
 | 
			
		||||
	}
 | 
			
		||||
	mtdsize = mtdInfo.size;
 | 
			
		||||
	erasesize = mtdInfo.erasesize;
 | 
			
		||||
	mtdtype = mtdInfo.type;
 | 
			
		||||
 | 
			
		||||
	return fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mtd_block_is_bad(int fd, int offset)
 | 
			
		||||
{
 | 
			
		||||
	int r = 0;
 | 
			
		||||
	loff_t o = offset;
 | 
			
		||||
 | 
			
		||||
	if (mtdtype == MTD_NANDFLASH)
 | 
			
		||||
	{
 | 
			
		||||
		r = ioctl(fd, MEMGETBADBLOCK, &o);
 | 
			
		||||
		if (r < 0)
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "Failed to get erase block status\n");
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mtd_erase_block(int fd, int offset)
 | 
			
		||||
{
 | 
			
		||||
	struct erase_info_user mtdEraseInfo;
 | 
			
		||||
@@ -236,10 +255,14 @@ mtd_erase(const char *mtd)
 | 
			
		||||
	for (mtdEraseInfo.start = 0;
 | 
			
		||||
		 mtdEraseInfo.start < mtdsize;
 | 
			
		||||
		 mtdEraseInfo.start += erasesize) {
 | 
			
		||||
 | 
			
		||||
		ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
 | 
			
		||||
		if(ioctl(fd, MEMERASE, &mtdEraseInfo))
 | 
			
		||||
			fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start);
 | 
			
		||||
		if (mtd_block_is_bad(fd, mtdEraseInfo.start)) {
 | 
			
		||||
			if (!quiet)
 | 
			
		||||
				fprintf(stderr, "\nSkipping bad block at 0x%x   ", mtdEraseInfo.start);
 | 
			
		||||
		} else {
 | 
			
		||||
			ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
 | 
			
		||||
			if(ioctl(fd, MEMERASE, &mtdEraseInfo))
 | 
			
		||||
				fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	close(fd);
 | 
			
		||||
@@ -324,6 +347,7 @@ mtd_write(int imagefd, const char *mtd, char *fis_layout, size_t part_offset)
 | 
			
		||||
	ssize_t skip = 0;
 | 
			
		||||
	uint32_t offset = 0;
 | 
			
		||||
	int jffs2_replaced = 0;
 | 
			
		||||
	int skip_bad_blocks = 0;
 | 
			
		||||
 | 
			
		||||
#ifdef FIS_SUPPORT
 | 
			
		||||
	static struct fis_part new_parts[MAX_ARGS];
 | 
			
		||||
@@ -429,6 +453,12 @@ resume:
 | 
			
		||||
		if (buflen == 0)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (buflen < erasesize) {
 | 
			
		||||
			/* Pad block to eraseblock size */
 | 
			
		||||
			memset(&buf[buflen], 0xff, erasesize - buflen);
 | 
			
		||||
			buflen = erasesize;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (skip > 0) {
 | 
			
		||||
			skip -= buflen;
 | 
			
		||||
			buflen = 0;
 | 
			
		||||
@@ -466,10 +496,21 @@ resume:
 | 
			
		||||
		/* need to erase the next block before writing data to it */
 | 
			
		||||
		if(!no_erase)
 | 
			
		||||
		{
 | 
			
		||||
			while (w + buflen > e) {
 | 
			
		||||
			while (w + buflen > e - skip_bad_blocks) {
 | 
			
		||||
				if (!quiet)
 | 
			
		||||
					fprintf(stderr, "\b\b\b[e]");
 | 
			
		||||
 | 
			
		||||
				if (mtd_block_is_bad(fd, e)) {
 | 
			
		||||
					if (!quiet)
 | 
			
		||||
						fprintf(stderr, "\nSkipping bad block at 0x%08x   ", e);
 | 
			
		||||
 | 
			
		||||
					skip_bad_blocks += erasesize;
 | 
			
		||||
					e += erasesize;
 | 
			
		||||
 | 
			
		||||
					// Move the file pointer along over the bad block.
 | 
			
		||||
					lseek(fd, erasesize, SEEK_CUR);
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (mtd_erase_block(fd, e) < 0) {
 | 
			
		||||
					if (next) {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ extern int erasesize;
 | 
			
		||||
 | 
			
		||||
extern int mtd_open(const char *mtd, bool block);
 | 
			
		||||
extern int mtd_check_open(const char *mtd);
 | 
			
		||||
extern int mtd_block_is_bad(int fd, int offset);
 | 
			
		||||
extern int mtd_erase_block(int fd, int offset);
 | 
			
		||||
extern int mtd_write_buffer(int fd, const char *buf, int offset, int length);
 | 
			
		||||
extern int mtd_write_jffs2(const char *mtd, const char *filename, const char *dir);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user