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:
Felix Fietkau 2014-03-26 10:50:09 +00:00
parent 0555ebf66d
commit 608f4fe3b0
3 changed files with 56 additions and 5 deletions

View file

@ -59,6 +59,15 @@ static void pad(int size)
} }
ofs = ofs % erasesize; ofs = ofs % erasesize;
if (ofs == 0) { 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); mtd_erase_block(outfd, mtdofs);
write(outfd, buf, erasesize); write(outfd, buf, erasesize);
mtdofs += erasesize; mtdofs += erasesize;

View file

@ -58,6 +58,7 @@ int no_erase;
int mtdsize = 0; int mtdsize = 0;
int erasesize = 0; int erasesize = 0;
int jffs2_skip_bytes=0; int jffs2_skip_bytes=0;
int mtdtype = 0;
int mtd_open(const char *mtd, bool block) int mtd_open(const char *mtd, bool block)
{ {
@ -103,10 +104,28 @@ int mtd_check_open(const char *mtd)
} }
mtdsize = mtdInfo.size; mtdsize = mtdInfo.size;
erasesize = mtdInfo.erasesize; erasesize = mtdInfo.erasesize;
mtdtype = mtdInfo.type;
return fd; 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) int mtd_erase_block(int fd, int offset)
{ {
struct erase_info_user mtdEraseInfo; struct erase_info_user mtdEraseInfo;
@ -236,10 +255,14 @@ mtd_erase(const char *mtd)
for (mtdEraseInfo.start = 0; for (mtdEraseInfo.start = 0;
mtdEraseInfo.start < mtdsize; mtdEraseInfo.start < mtdsize;
mtdEraseInfo.start += erasesize) { mtdEraseInfo.start += erasesize) {
if (mtd_block_is_bad(fd, mtdEraseInfo.start)) {
ioctl(fd, MEMUNLOCK, &mtdEraseInfo); if (!quiet)
if(ioctl(fd, MEMERASE, &mtdEraseInfo)) fprintf(stderr, "\nSkipping bad block at 0x%x ", mtdEraseInfo.start);
fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, 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); close(fd);
@ -324,6 +347,7 @@ mtd_write(int imagefd, const char *mtd, char *fis_layout, size_t part_offset)
ssize_t skip = 0; ssize_t skip = 0;
uint32_t offset = 0; uint32_t offset = 0;
int jffs2_replaced = 0; int jffs2_replaced = 0;
int skip_bad_blocks = 0;
#ifdef FIS_SUPPORT #ifdef FIS_SUPPORT
static struct fis_part new_parts[MAX_ARGS]; static struct fis_part new_parts[MAX_ARGS];
@ -429,6 +453,12 @@ resume:
if (buflen == 0) if (buflen == 0)
break; break;
if (buflen < erasesize) {
/* Pad block to eraseblock size */
memset(&buf[buflen], 0xff, erasesize - buflen);
buflen = erasesize;
}
if (skip > 0) { if (skip > 0) {
skip -= buflen; skip -= buflen;
buflen = 0; buflen = 0;
@ -466,10 +496,21 @@ resume:
/* need to erase the next block before writing data to it */ /* need to erase the next block before writing data to it */
if(!no_erase) if(!no_erase)
{ {
while (w + buflen > e) { while (w + buflen > e - skip_bad_blocks) {
if (!quiet) if (!quiet)
fprintf(stderr, "\b\b\b[e]"); 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 (mtd_erase_block(fd, e) < 0) {
if (next) { if (next) {

View file

@ -15,6 +15,7 @@ extern int erasesize;
extern int mtd_open(const char *mtd, bool block); extern int mtd_open(const char *mtd, bool block);
extern int mtd_check_open(const char *mtd); 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_erase_block(int fd, int offset);
extern int mtd_write_buffer(int fd, const char *buf, int offset, int length); 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); extern int mtd_write_jffs2(const char *mtd, const char *filename, const char *dir);