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:
parent
0555ebf66d
commit
608f4fe3b0
3 changed files with 56 additions and 5 deletions
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue