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>

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@40021 3c298f89-4303-0410-b956-a3cf2f4a3e73
master
Felix Fietkau 2014-03-26 10:50:09 +00:00
parent 58ca40c218
commit d7a9700570
3 changed files with 56 additions and 5 deletions

View File

@ -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;

View File

@ -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) {

View File

@ -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);