summaryrefslogtreecommitdiffstats
path: root/commands/nand.c
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2009-08-03 15:30:03 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2009-08-13 10:43:06 +0200
commit7d27f77b6ea0378b4a1cac0c6560661861a0b8ab (patch)
tree0f08f864d26b17dbab84aca324f38342cc9fe33b /commands/nand.c
parente9dc699882b65ecd84567c00757705a7ff663c8b (diff)
downloadbarebox-7d27f77b6ea0378b4a1cac0c6560661861a0b8ab.tar.gz
barebox-7d27f77b6ea0378b4a1cac0c6560661861a0b8ab.tar.xz
nand bb: Fix writing of nand page unaligned data
We used to write the data in nand_bb_write directly to the NAND flash. If we do not write a whole NAND page at once, this resulted in multiple writes of one page which corrupted the ecc data. Fix this by collecting 4096 bytes of data before actually writing the data to the flash. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'commands/nand.c')
-rw-r--r--commands/nand.c67
1 files changed, 53 insertions, 14 deletions
diff --git a/commands/nand.c b/commands/nand.c
index f7406fde4a..cbf68b05c1 100644
--- a/commands/nand.c
+++ b/commands/nand.c
@@ -36,6 +36,7 @@ struct nand_bb {
char *devname;
char *name;
int open;
+ int needs_write;
struct mtd_info_user info;
@@ -43,6 +44,7 @@ struct nand_bb {
size_t size;
int fd;
off_t offset;
+ void *writebuf;
struct cdev cdev;
};
@@ -80,36 +82,67 @@ static ssize_t nand_bb_read(struct cdev *cdev, void *buf, size_t count,
return bytes;
}
-static ssize_t nand_bb_write(struct cdev *cdev, const void *buf, size_t count,
- unsigned long offset, ulong flags)
-{
- struct nand_bb *bb = cdev->priv;
- int ret, bytes = 0, now;
+/* Must be a multiple of the largest NAND page size */
+#define BB_WRITEBUF_SIZE 4096
- debug("%s offset: 0x%08x count: 0x%08x\n", __func__, offset, count);
+static int nand_bb_write_buf(struct nand_bb *bb, size_t count)
+{
+ int ret, now;
+ void *buf = bb->writebuf;
+ int cur_ofs = bb->offset & ~(BB_WRITEBUF_SIZE - 1);
- while(count) {
- ret = ioctl(bb->fd, MEMGETBADBLOCK, (void *)bb->offset);
+ while (count) {
+ ret = ioctl(bb->fd, MEMGETBADBLOCK, (void *)cur_ofs);
if (ret < 0)
return ret;
if (ret) {
- debug("skipping bad block at 0x%08x\n", bb->offset);
+ debug("skipping bad block at 0x%08x\n", cur_ofs);
bb->offset += bb->info.erasesize;
+ cur_ofs += bb->info.erasesize;
+ continue;
}
- now = min(count, (size_t)(bb->info.erasesize -
- (bb->offset % bb->info.erasesize)));
- lseek(bb->fd, offset, SEEK_SET);
+ now = min(count, (size_t)(bb->info.erasesize));
+ lseek(bb->fd, cur_ofs, SEEK_SET);
ret = write(bb->fd, buf, now);
if (ret < 0)
return ret;
buf += now;
count -= now;
- bb->offset += now;
- bytes += now;
+ cur_ofs += now;
};
+ return 0;
+}
+
+static ssize_t nand_bb_write(struct cdev *cdev, const void *buf, size_t count,
+ unsigned long offset, ulong flags)
+{
+ struct nand_bb *bb = cdev->priv;
+ int bytes = count, now, wroffs, ret;
+
+ debug("%s offset: 0x%08x count: 0x%08x\n", __func__, offset, count);
+
+ while (count) {
+ wroffs = bb->offset % BB_WRITEBUF_SIZE;
+ now = min((int)count, BB_WRITEBUF_SIZE - wroffs);
+ memcpy(bb->writebuf + wroffs, buf, now);
+
+ if (wroffs + now == BB_WRITEBUF_SIZE) {
+ bb->needs_write = 0;
+ ret = nand_bb_write_buf(bb, BB_WRITEBUF_SIZE);
+ if (ret)
+ return ret;
+ } else {
+ bb->needs_write = 1;
+ }
+
+ bb->offset += now;
+ count -= now;
+ buf += now;
+ }
+
return bytes;
}
@@ -134,6 +167,8 @@ static int nand_bb_open(struct cdev *cdev, struct filep *f)
bb->open = 1;
bb->offset = 0;
+ bb->needs_write = 0;
+ bb->writebuf = xmalloc(BB_WRITEBUF_SIZE);
return 0;
}
@@ -142,7 +177,11 @@ static int nand_bb_close(struct cdev *cdev, struct filep *f)
{
struct nand_bb *bb = cdev->priv;
+ if (bb->needs_write)
+ nand_bb_write_buf(bb, bb->offset % BB_WRITEBUF_SIZE);
+
bb->open = 0;
+ free(bb->writebuf);
return 0;
}