[PATCH] treat OOB as a single chunk of oobavail bytes
Vitaly Wool
vwool at ru.mvista.com
Tue Nov 29 10:02:04 EST 2005
Hi,
the patch below implements treating the OOB data as a chunk of _free_ OOB bytes of mtd->oobavail size.
This is what was announced several times. This patch is a working one (verified with yaffs2 and jffs2), however, it's not completely ready to work with 16bit NAND flashes.
Anyway, I'd like to ask for a permission to commit it to let other people start using it/report the problems/etc. etc.
Of course input of any kind is welcome.
Best regards,
Vitaly
diff -uNr linux-2.6.10.orig/drivers/mtd/nand/nand_base.c linux-2.6.10.nand/drivers/mtd/nand/nand_base.c
--- linux-2.6.10.orig/drivers/mtd/nand/nand_base.c 2005-11-24 16:07:18.000000000 +0300
+++ linux-2.6.10.nand/drivers/mtd/nand/nand_base.c 2005-11-29 16:55:13.634260952 +0300
@@ -117,42 +117,8 @@
#define FFCHARS_SIZE 2048
static u_char ffchars[FFCHARS_SIZE];
-static struct page_layout_item hw3_256_layout[] = {
- { .length = 256, .type = ITEM_TYPE_DATA, },
- { .length = 3, .type = ITEM_TYPE_ECC, },
- { .length = 5, .type = ITEM_TYPE_OOB, },
- { .length = 0, },
-};
-
-static struct page_layout_item hw3_512_layout[] = {
- { .length = 512, .type = ITEM_TYPE_DATA, },
- { .length = 3, .type = ITEM_TYPE_ECC, },
- { .length = 13, .type = ITEM_TYPE_OOB, },
- { .length = 0, },
-};
-
-static struct page_layout_item hw6_512_layout[] = {
- { .length = 512, .type = ITEM_TYPE_DATA, },
- { .length = 6, .type = ITEM_TYPE_ECC, },
- { .length = 10, .type = ITEM_TYPE_OOB, },
- { .length = 0, },
-};
-
-static struct page_layout_item hw8_512_layout[] = {
- { .length = 512, .type = ITEM_TYPE_DATA, },
- { .length = 8, .type = ITEM_TYPE_ECC, },
- { .length = 8, .type = ITEM_TYPE_OOB, },
- { .length = 0, },
-};
-
-static struct page_layout_item hw12_2048_layout[] = {
- { .length = 2048, .type = ITEM_TYPE_DATA, },
- { .length = 12, .type = ITEM_TYPE_ECC, },
- { .length = 52, .type = ITEM_TYPE_OOB, },
- { .length = 0, },
-};
-
-#define HW_AUTOOOB_LAYOUT_SIZE 8 /* should be enough */
+#define HW_AUTOOOB_LAYOUT_SIZE 32 /* should be enough */
+#define SW_ECC_LAYOUT_SIZE 8
/*
* NAND low-level MTD interface functions
@@ -893,11 +859,10 @@
static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page,
u_char *oob_buf, struct nand_oobinfo *oobsel, int cached)
{
- int i, oobidx, status;
+ int j = 0, oobidx = 0, status;
u_char ecc_code[40];
int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
- int *oob_config = oobsel->eccpos;
- int datidx = 0, eccidx = 0, eccsteps = this->eccsteps;
+ int datidx = 0, last_datidx = 0, eccidx = 0;
int eccbytes = 0;
/* FIXME: Enable cached programming */
@@ -914,41 +879,35 @@
this->write_buf(mtd, this->data_poi, mtd->oobblock);
break;
- /* Software ecc 3/256, write all */
- case NAND_ECC_SOFT:
- for (; eccsteps; eccsteps--) {
- this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
- for (i = 0; i < 3; i++, eccidx++)
- oob_buf[oob_config[eccidx]] = ecc_code[i];
- datidx += this->eccsize;
- }
- this->write_buf(mtd, this->data_poi, mtd->oobblock);
- this->write_buf(mtd, oob_buf, mtd->oobsize);
- break;
default:
eccbytes = this->eccbytes;
- for (oobidx = 0; eccsteps; eccsteps--) {
- int j = 0, last_datidx = datidx, last_oobidx;
- for (; this->layout[j].length; j++) {
- switch (this->layout[j].type) {
- case ITEM_TYPE_DATA:
+ for (; this->layout[j].length; j++) {
+ switch (this->layout[j].type) {
+ case ITEM_TYPE_DATA:
+ if (eccmode != NAND_ECC_SOFT)
this->enable_hwecc(mtd, NAND_ECC_WRITE);
- this->write_buf(mtd, &this->data_poi[datidx], this->layout[j].length);
- datidx += this->layout[j].length;
- break;
- case ITEM_TYPE_ECC:
+ this->write_buf(mtd, &this->data_poi[datidx], this->layout[j].length);
+ last_datidx = datidx;
+ datidx += this->layout[j].length;
+ break;
+ case ITEM_TYPE_ECC:
+ if (eccmode != NAND_ECC_SOFT)
this->enable_hwecc(mtd, NAND_ECC_WRITESYN);
- this->calculate_ecc(mtd, &this->data_poi[last_datidx], &ecc_code[eccidx]);
- for (last_oobidx = oobidx; oobidx < last_oobidx + this->layout[j].length; oobidx++, eccidx++)
- oob_buf[oobidx] = ecc_code[eccidx];
- this->write_buf(mtd, ecc_code, this->layout[j].length);
- break;
- case ITEM_TYPE_OOB:
+ this->calculate_ecc(mtd, &this->data_poi[last_datidx], &ecc_code[eccidx]);
+ this->write_buf(mtd, &ecc_code[eccidx], this->layout[j].length);
+ break;
+ case ITEM_TYPE_OOB:
+ if (eccmode != NAND_ECC_SOFT)
this->enable_hwecc(mtd, NAND_ECC_WRITEOOB);
- this->write_buf(mtd, &oob_buf[oobidx], this->layout[j].length);
- oobidx += this->layout[j].length;
- break;
- }
+ this->write_buf(mtd, ffchars, this->layout[j].length);
+ break;
+ case ITEM_TYPE_OOBFREE:
+ if (eccmode != NAND_ECC_SOFT)
+ this->enable_hwecc(mtd, NAND_ECC_WRITEOOB);
+ DEBUG (MTD_DEBUG_LEVEL3, "%s: writing %02x %02x...\n", __FUNCTION__, oob_buf[oobidx], oob_buf[oobidx+1]);
+ this->write_buf(mtd, &oob_buf[oobidx], this->layout[j].length);
+ oobidx += this->layout[j].length;
+ break;
}
}
@@ -1003,7 +962,7 @@
static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages,
u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode)
{
- int i, j, datidx = 0, oobofs = 0, res = -EIO;
+ int j, datidx = 0, oobofs = 0, res = -EIO;
int eccsteps = this->eccsteps;
int hweccbytes;
u_char oobdata[64];
@@ -1014,50 +973,33 @@
this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
for(;;) {
- for (j = 0; j < eccsteps; j++) {
- /* Loop through and verify the data */
- if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) {
- DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
- goto out;
- }
- datidx += mtd->eccsize;
- /* Have we a hw generator layout ? */
- if (!hweccbytes)
- continue;
- if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) {
- DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
- goto out;
- }
- oobofs += hweccbytes;
- }
-
- /* check, if we must compare all data or if we just have to
- * compare the ecc bytes
- */
- if (oobmode) {
- if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) {
- DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
- goto out;
- }
- } else {
- /* Read always, else autoincrement fails */
- this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps);
-
- if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) {
- int ecccnt = oobsel->eccbytes;
-
- for (i = 0; i < ecccnt; i++) {
- int idx = oobsel->eccpos[i];
- if (oobdata[idx] != oob_buf[oobofs + idx] ) {
- DEBUG (MTD_DEBUG_LEVEL0,
- "%s: Failed ECC write "
- "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i);
+ for (j = 0; this->layout[j].length; j++) {
+ switch (this->layout[j].type) {
+ case ITEM_TYPE_DATA:
+ /* Loop through and verify the data */
+ if (this->verify_buf(mtd, &this->data_poi[datidx], this->layout[j].length)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+ goto out;
+ }
+ datidx += this->layout[j].length;
+ break;
+ case ITEM_TYPE_OOBFREE:
+ if (oobmode) {
+ if (this->verify_buf(mtd, &this->oob_buf[oobofs], this->layout[j].length)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
goto out;
}
+ oobofs += this->layout[j].length;
+ break;
}
+ case ITEM_TYPE_ECC:
+ case ITEM_TYPE_OOB:
+ /* Read always, else autoincrement fails */
+ this->read_buf(mtd, oobdata, this->layout[j].length);
}
+
}
- oobofs += mtd->oobsize - hweccbytes * eccsteps;
+
page++;
numpages--;
@@ -1153,13 +1095,13 @@
{
int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
- int read = 0, oob = 0, oobidx, ecc_status = 0, ecc_failed = 0, eccidx;
+ int read = 0, oobidx, ecc_status = 0, ecc_failed = 0, eccidx;
struct nand_chip *this = mtd->priv;
u_char *data_poi, *oob_data = oob_buf;
u_char ecc_calc[32];
u_char ecc_code[32];
int eccmode, eccsteps;
- int *oob_config, datidx;
+ int *oob_config, datidx = 0, last_datidx = 0;
int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
int eccbytes;
int compareecc = 1;
@@ -1240,8 +1182,7 @@
}
/* get oob area, if we have no oob buffer from fs-driver */
- if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE ||
- oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
+ if (!oob_buf)
oob_data = &this->data_buf[end];
eccsteps = this->eccsteps;
@@ -1257,53 +1198,52 @@
break;
}
- case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */
- this->read_buf(mtd, data_poi, end);
- for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc)
- this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
- this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen);
- break;
-
default:
- for (oobidx = 0, datidx = 0, eccidx = 0; eccsteps; eccsteps--) {
- int last_datidx = datidx, last_oobidx = oobidx;
- for (j = 0; this->layout[j].length; j++) {
- switch (this->layout[j].type) {
- case ITEM_TYPE_DATA:
- DEBUG (MTD_DEBUG_LEVEL3, "%s: reading %d bytes of data\n", __FUNCTION__, this->layout[j].length);
+ oobidx = 0;
+ datidx = last_datidx = 0;
+ eccidx = 0;
+ for (j = 0; this->layout[j].length; j++) {
+ switch (this->layout[j].type) {
+ case ITEM_TYPE_DATA:
+ DEBUG (MTD_DEBUG_LEVEL3, "%s: reading %d bytes of data\n", __FUNCTION__, this->layout[j].length);
+ if (eccmode != NAND_ECC_SOFT)
this->enable_hwecc(mtd, NAND_ECC_READ);
- this->read_buf(mtd, &data_poi[datidx], this->layout[j].length);
- datidx += this->layout[j].length;
- break;
-
- case ITEM_TYPE_ECC:
- DEBUG (MTD_DEBUG_LEVEL3, "%s: reading %d ecc bytes\n", __FUNCTION__, this->layout[j].length);
- /* let the particular driver decide whether to read ECC */
+ this->read_buf(mtd, &data_poi[datidx], this->layout[j].length);
+ DEBUG (MTD_DEBUG_LEVEL3, "%s: read %02x %02x...\n", __FUNCTION__, data_poi[datidx], data_poi[datidx+1]);
+ last_datidx = datidx;
+ datidx += this->layout[j].length;
+ break;
+
+ case ITEM_TYPE_ECC:
+ DEBUG (MTD_DEBUG_LEVEL3, "%s: reading %d ecc bytes\n", __FUNCTION__, this->layout[j].length);
+ /* let the particular driver decide whether to read ECC */
+ if (eccmode != NAND_ECC_SOFT)
this->enable_hwecc(mtd, NAND_ECC_READSYN);
- this->read_buf(mtd, &oob_data[oobidx], this->layout[j].length);
- if (!compareecc) {
- /* We calc error correction directly, it checks the hw
- * generator for an error, reads back the syndrome and
- * does the error correction on the fly */
- ecc_status = this->correct_data(mtd, &data_poi[last_datidx], &oob_data[last_oobidx], &ecc_code[eccidx]);
- if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
- DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: "
- "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
- ecc_failed++;
- }
- } else
- this->calculate_ecc(mtd, &data_poi[last_datidx], &ecc_calc[eccidx]);
- oobidx += this->layout[j].length;
- eccidx += this->layout[j].length;
- break;
- case ITEM_TYPE_OOB:
- DEBUG (MTD_DEBUG_LEVEL3, "%s: reading %d free oob bytes\n", __FUNCTION__, this->layout[j].length);
+ this->read_buf(mtd, &ecc_code[eccidx], this->layout[j].length);
+ if (!compareecc) {
+ /* We calc error correction directly, it checks the hw
+ * generator for an error, reads back the syndrome and
+ * does the error correction on the fly */
+ ecc_status = this->correct_data(mtd, &data_poi[last_datidx], &ecc_code[eccidx], &ecc_code[eccidx]);
+ if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
+ DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: "
+ "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
+ ecc_failed++;
+ }
+ } else
+ this->calculate_ecc(mtd, &data_poi[last_datidx], &ecc_calc[eccidx]);
+ eccidx += this->layout[j].length;
+ break;
+ case ITEM_TYPE_OOB:
+ case ITEM_TYPE_OOBFREE:
+ DEBUG (MTD_DEBUG_LEVEL3, "%s: reading %d oob bytes\n", __FUNCTION__, this->layout[j].length);
+ if (eccmode != NAND_ECC_SOFT)
this->enable_hwecc(mtd, NAND_ECC_READOOB);
- this->read_buf(mtd, &oob_data[oobidx], this->layout[j].length);
+ this->read_buf(mtd, &oob_data[oobidx], this->layout[j].length);
+ if (this->layout[j].type == ITEM_TYPE_OOBFREE)
oobidx += this->layout[j].length;
- break;
- }
+ break;
}
}
break;
@@ -1311,11 +1251,7 @@
/* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */
if (!compareecc)
- goto readoob;
-
- /* Pick the ECC bytes out of the oob data */
- for (j = 0; j < oobsel->eccbytes; j++)
- ecc_code[j] = oob_data[oob_config[j]];
+ goto readdata;
/* correct data, if neccecary */
for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) {
@@ -1324,43 +1260,12 @@
/* Get next chunk of ecc bytes */
j += eccbytes;
- /* Check, if we have a fs supplied oob-buffer,
- * This is the legacy mode. Used by YAFFS1
- * Should go away some day
- */
- if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) {
- int *p = (int *)(&oob_data[mtd->oobsize]);
- p[i] = ecc_status;
- }
-
if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
ecc_failed++;
}
}
- readoob:
- /* check, if we have a fs supplied oob-buffer */
- if (oob_buf) {
- /* without autoplace. Legacy mode used by YAFFS1 */
- switch(oobsel->useecc) {
- case MTD_NANDECC_AUTOPLACE:
- case MTD_NANDECC_AUTOPL_USR:
- /* Walk through the autoplace chunks */
- for (i = 0; oobsel->oobfree[i][1]; i++) {
- int from = oobsel->oobfree[i][0];
- int num = oobsel->oobfree[i][1];
- memcpy(&oob_buf[oob], &oob_data[from], num);
- oob += num;
- }
- break;
- case MTD_NANDECC_PLACE:
- /* YAFFS1 legacy mode */
- oob_data += this->eccsteps * sizeof (int);
- default:
- oob_data += mtd->oobsize;
- }
- }
readdata:
/* Partial page read, transfer data into fs buffer */
if (!aligned) {
@@ -1432,7 +1337,6 @@
int read = 0;
struct nand_chip *this = mtd->priv;
u_char *oob_data = oob_buf;
- int eccsteps;
int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
@@ -1463,7 +1367,7 @@
/* Loop until all data read */
while (read < len) {
- if (this->eccmode == NAND_ECC_SOFT || this->eccmode == NAND_ECC_NONE) {
+ if (this->eccmode == NAND_ECC_NONE) {
int thislen = mtd->oobsize - col;
if (sndcmd) {
this->cmdfunc (mtd, NAND_CMD_READOOB, col, page);
@@ -1480,81 +1384,48 @@
sndcmd = 0;
}
- eccsteps = this->eccsteps;
+ for (j = 0; this->layout[j].length; j++) {
+ i = 0;
+ switch (this->layout[j].type) {
+ case ITEM_TYPE_DATA:
+ case ITEM_TYPE_OOB:
+ case ITEM_TYPE_ECC:
+ DEBUG (MTD_DEBUG_LEVEL3, "%s: dummy data read\n", __FUNCTION__);
+ reallen += this->layout[j].length;
+ if (this->options & NAND_BUSWIDTH_16)
+ this->cmdfunc (mtd, NAND_CMD_READ0, reallen & ~1, page);
+ else
+ this->cmdfunc (mtd, NAND_CMD_READ0, reallen, page);
+ break;
- for (; eccsteps; eccsteps--) {
- for (j = 0; this->layout[j].length; j++) {
- i = 0;
- switch (this->layout[j].type) {
- case ITEM_TYPE_DATA:
- DEBUG (MTD_DEBUG_LEVEL3, "%s: dummy data read\n", __FUNCTION__);
- reallen += this->layout[j].length;
+ case ITEM_TYPE_OOBFREE:
+ DEBUG (MTD_DEBUG_LEVEL3, "%s: free oob bytes read\n", __FUNCTION__);
+ i = min_t(int, col, this->layout[j].length);
+ if (i) {
+ reallen += i;
if (this->options & NAND_BUSWIDTH_16)
this->cmdfunc (mtd, NAND_CMD_READ0, reallen & ~1, page);
else
this->cmdfunc (mtd, NAND_CMD_READ0, reallen, page);
- break;
-
- case ITEM_TYPE_ECC:
- DEBUG (MTD_DEBUG_LEVEL3, "%s: ecc bytes read\n", __FUNCTION__);
- i = min_t(int, col, this->layout[j].length);
- if (i) {
- reallen += i;
- if (this->options & NAND_BUSWIDTH_16)
- this->cmdfunc (mtd, NAND_CMD_READ0, reallen & ~1, page);
- else
- this->cmdfunc (mtd, NAND_CMD_READ0, reallen, page);
- }
- col -= i;
- i = min_t(int, len - read, this->layout[j].length - i);
- this->enable_hwecc(mtd, NAND_ECC_READSYN);
- if (i) {
- if (this->options & NAND_BUSWIDTH_16 && reallen & 1) {
- oob_data[0] = cpu_to_le16(this->read_word(mtd)) >> 8;
- oob_data++; i--; reallen++;
- }
-
- this->read_buf(mtd, oob_data, i);
- reallen += i;
- }
- if (oob_buf + len == oob_data + i) {
- read += i;
- goto out;
- }
- break;
- case ITEM_TYPE_OOB:
- DEBUG (MTD_DEBUG_LEVEL3, "%s: free oob bytes read\n", __FUNCTION__);
- i = min_t(int, col, this->layout[j].length);
- if (i) {
- reallen += i;
- if (this->options & NAND_BUSWIDTH_16)
- this->cmdfunc (mtd, NAND_CMD_READ0, reallen & ~1, page);
- else
- this->cmdfunc (mtd, NAND_CMD_READ0, reallen, page);
- }
- col -= i;
+ }
+ col -= i;
+ if (this->eccmode != NAND_ECC_SOFT)
this->enable_hwecc(mtd, NAND_ECC_READOOB);
- i = min_t(int, len - read, this->layout[j].length - i);
- if (i) {
- if (this->options & NAND_BUSWIDTH_16 && reallen & 1) {
- oob_data[0] = cpu_to_le16(this->read_word(mtd)) >> 8;
- oob_data++; i--; reallen++;
- }
-
- this->read_buf(mtd, oob_data, i);
- reallen += i;
+ i = min_t(int, len - read, this->layout[j].length - i);
+ if (i) {
+ if (this->options & NAND_BUSWIDTH_16 && reallen & 1) {
+ oob_data[0] = cpu_to_le16(this->read_word(mtd)) >> 8;
+ oob_data++; i--; reallen++;
}
- if (oob_buf + len == oob_data + i) {
- read += i;
- goto out;
- }
-
- break;
+ this->read_buf(mtd, oob_data, i);
+ reallen += i;
}
read += i;
oob_data += i;
-
+ if (oob_buf + len == oob_data)
+ goto out;
+ break;
}
}
}
@@ -1664,67 +1535,6 @@
return 0;
}
-
-/**
- * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer
- * @mtd: MTD device structure
- * @fsbuf: buffer given by fs driver
- * @oobsel: out of band selection structre
- * @autoplace: 1 = place given buffer into the oob bytes
- * @numpages: number of pages to prepare
- *
- * Return:
- * 1. Filesystem buffer available and autoplacement is off,
- * return filesystem buffer
- * 2. No filesystem buffer or autoplace is off, return internal
- * buffer
- * 3. Filesystem buffer is given and autoplace selected
- * put data from fs buffer into internal buffer and
- * retrun internal buffer
- *
- * Note: The internal buffer is filled with 0xff. This must
- * be done only once, when no autoplacement happens
- * Autoplacement sets the buffer dirty flag, which
- * forces the 0xff fill before using the buffer again.
- *
-*/
-static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel,
- int autoplace, int numpages)
-{
- struct nand_chip *this = mtd->priv;
- int i, len, ofs;
-
- /* Zero copy fs supplied buffer */
- if (fsbuf && !autoplace)
- return fsbuf;
-
- /* Check, if the buffer must be filled with ff again */
- if (this->oobdirty) {
- memset (this->oob_buf, 0xff,
- mtd->oobsize << (this->phys_erase_shift - this->page_shift));
- this->oobdirty = 0;
- }
-
- /* If we have no autoplacement or no fs buffer use the internal one */
- if (!autoplace || !fsbuf)
- return this->oob_buf;
-
- /* Walk through the pages and place the data */
- this->oobdirty = 1;
- ofs = 0;
- while (numpages--) {
- for (i = 0, len = 0; len < mtd->oobavail; i++) {
- int to = ofs + oobsel->oobfree[i][0];
- int num = oobsel->oobfree[i][1];
- memcpy (&this->oob_buf[to], fsbuf, num);
- len += num;
- fsbuf += num;
- }
- ofs += mtd->oobavail;
- }
- return this->oob_buf;
-}
-
#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0
/**
@@ -1817,7 +1627,7 @@
startpage = page;
/* Calc number of pages we can write in one go */
numpages = min (ppblock - (startpage & (ppblock - 1)), totalpages);
- oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages);
+ oobbuf = eccbuf ? eccbuf : ffchars;
bufstart = (u_char *)buf;
/* Loop until all data is written */
@@ -1867,8 +1677,7 @@
numpages = min (totalpages, ppblock);
page &= this->pagemask;
startpage = page;
- oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel,
- autoplace, numpages);
+ oobbuf = eccbuf ? eccbuf : ffchars;
oob = 0;
/* Check, if we cross a chip boundary */
if (!page) {
@@ -1910,6 +1719,7 @@
{
int column, page, status, ret = -EIO, chipnr, eccsteps, fflen, ooblen;
struct nand_chip *this = mtd->priv;
+ int i, j;
DEBUG (MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", __FUNCTION__, (unsigned int) to, (int) len);
@@ -1950,7 +1760,7 @@
if (page == this->pagebuf)
this->pagebuf = -1;
- if (this->eccmode == NAND_ECC_SOFT || this->eccmode == NAND_ECC_NONE) {
+ if (this->eccmode == NAND_ECC_NONE) {
if (NAND_MUST_PAD(this)) {
/* Write out desired data */
this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask);
@@ -1972,41 +1782,43 @@
eccsteps = this->eccsteps;
- for (fflen = 0, ooblen = 0; eccsteps; eccsteps--) {
- int i, j;
- for (j = 0; this->layout[j].length; j++) {
- switch (this->layout[j].type) {
- case ITEM_TYPE_DATA:
+ fflen = 0;
+ ooblen = 0;
+ for (j = 0; this->layout[j].length; j++) {
+ switch (this->layout[j].type) {
+ case ITEM_TYPE_OOB:
+ case ITEM_TYPE_DATA:
+ if (this->eccmode != NAND_ECC_SOFT)
this->enable_hwecc(mtd, NAND_ECC_WRITE);
- this->write_buf(mtd, ffchars, this->layout[j].length);
- fflen += this->layout[j].length;
- break;
+ this->write_buf(mtd, ffchars, this->layout[j].length);
+ fflen += this->layout[j].length;
+ break;
- case ITEM_TYPE_ECC:
+ case ITEM_TYPE_ECC:
+ if (this->eccmode != NAND_ECC_SOFT)
this->enable_hwecc(mtd, NAND_ECC_WRITESYN);
- this->write_buf(mtd, ffchars, this->layout[j].length);
- ooblen += this->layout[j].length;
- break;
+ this->write_buf(mtd, ffchars, this->layout[j].length);
+ fflen += this->layout[j].length;
+ break;
- case ITEM_TYPE_OOB:
+ case ITEM_TYPE_OOBFREE:
+ if (this->eccmode != NAND_ECC_SOFT)
this->enable_hwecc(mtd, NAND_ECC_WRITEOOB);
- i = min_t(int, column, this->layout[j].length);
- if (i)
- this->write_buf(mtd, ffchars, i);
- column -= i;
- fflen += i;
- i = min_t(int, len + column - ooblen, this->layout[j].length - i);
-
- if (i)
- this->write_buf(mtd, &oob_buf[ooblen], i);
- ooblen += i;
- if (ooblen == len) {
- if (NAND_MUST_PAD(this))
- this->write_buf(mtd, ffchars, mtd->oobsize + mtd->oobblock - fflen - ooblen);
- goto finish;
- }
- break;
+ i = min_t(int, column, this->layout[j].length);
+ if (i)
+ this->write_buf(mtd, ffchars, i);
+ column -= i;
+ fflen += i;
+ i = min_t(int, len + column - ooblen, this->layout[j].length - i);
+ if (i)
+ this->write_buf(mtd, &oob_buf[ooblen], i);
+ ooblen += i;
+ if (ooblen == len) {
+ if (NAND_MUST_PAD(this))
+ this->write_buf(mtd, ffchars, mtd->oobsize + mtd->oobblock - fflen - ooblen);
+ goto finish;
}
+ break;
}
}
}
@@ -2026,17 +1838,57 @@
*retlen = len;
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
- if (this->eccmode == NAND_ECC_SOFT | this->eccmode == NAND_ECC_NONE) {
+ if (this->eccmode == NAND_ECC_SOFT || this->eccmode == NAND_ECC_NONE) {
/* Send command to read back the data */
this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask);
- if (this->verify_buf(mtd, buf, len)) {
+ if (this->verify_buf(mtd, oob_buf, len)) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page);
ret = -EIO;
goto out;
}
+ } else {
+ int oobofs;
+ int read_len;
+ int real_ofs = 0;
+
+ column = to & (mtd->oobsize - 1);
+
+ /* Send command to read back the data */
+ this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask);
+ for (j = 0, oobofs = 0; this->layout[j].length; j++) {
+ switch (this->layout[j].type) {
+ case ITEM_TYPE_OOBFREE:
+ i = min_t(int, column, this->layout[j].length);
+ if (i) {
+ real_ofs += i;
+ this->cmdfunc (mtd, NAND_CMD_READ0, real_ofs, page);
+ }
+ column -= i;
+
+ read_len = min_t(int, i, len);
+ if (this->verify_buf(mtd, &oob_buf[oobofs], read_len)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+ ret = -EIO;
+ goto out;
+ }
+ oobofs += read_len;
+ real_ofs += read_len;
+ if (len == oobofs) {
+ ret = 0;
+ goto out;
+ }
+ break;
+ case ITEM_TYPE_ECC:
+ case ITEM_TYPE_OOB:
+ case ITEM_TYPE_DATA:
+ real_ofs += this->layout[j].length;
+ this->cmdfunc (mtd, NAND_CMD_READ0, real_ofs, page & this->pagemask);
+ break;
+ }
+
+ }
}
-#warning "Verify for OOB data in HW ECC case is NOT YET implemented"
#endif
ret = 0;
out:
@@ -2150,7 +2002,7 @@
numpages = (vecs->iov_len - len) >> this->page_shift;
/* Do not cross block boundaries */
numpages = min (ppblock - (startpage & (ppblock - 1)), numpages);
- oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
+ oobbuf = eccbuf ? eccbuf : ffchars;
bufstart = (u_char *)vecs->iov_base;
bufstart += len;
this->data_poi = bufstart;
@@ -2194,7 +2046,7 @@
this->data_poi = this->data_buf;
bufstart = this->data_poi;
numpages = 1;
- oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
+ oobbuf = eccbuf ? eccbuf : ffchars;
ret = nand_write_page (mtd, this, page & this->pagemask,
oobbuf, oobsel, 0);
if (ret)
@@ -2513,13 +2365,66 @@
}
+/**
+ * fill_swecc_layout - [NAND Interface] build the layout for software ECC case
+ * @mtd: MTD device structure
+ * @eccbytes: Number of ECC bytes per page
+ *
+ * Build the page_layout array for NAND page handling for software ECC
+ * handling basing on the number of the ECC bytes per page
+ */
+static int fill_swecc_layout(struct mtd_info *mtd, int eccbytes)
+{
+ struct nand_chip *this = mtd->priv;
+
+ this->layout = kmalloc(SW_ECC_LAYOUT_SIZE * sizeof (struct page_layout_item), GFP_KERNEL);
+
+ if (this->layout == NULL)
+ return -ENOMEM;
+ else
+ this->layout_allocated = 1;
+
+ memset(this->layout, 0, sizeof(struct page_layout_item) * SW_ECC_LAYOUT_SIZE);
+ this->layout[0].type = ITEM_TYPE_DATA;
+ this->layout[0].length = mtd->oobblock;
+
+ if (this->badblockpos == 0) {
+ this->layout[1].type = ITEM_TYPE_OOB;
+ this->layout[1].length = 1;
+ this->layout[2].type = ITEM_TYPE_OOBFREE;
+ this->layout[2].length = mtd->oobsize - 1 - eccbytes * this->eccsteps;
+ this->layout[3].type = ITEM_TYPE_ECC;
+ this->layout[3].length = 3 * this->eccsteps;
+ } else {
+ this->layout[1].type = ITEM_TYPE_OOBFREE;
+ this->layout[1].length = this->badblockpos;
+ this->layout[2].type = ITEM_TYPE_OOB;
+ this->layout[2].length = 1;
+ this->layout[3].type = ITEM_TYPE_OOBFREE;
+ this->layout[3].length = mtd->oobsize - 1 - this->badblockpos - eccbytes * this->eccsteps;
+ this->layout[3].type = ITEM_TYPE_ECC;
+ this->layout[3].length = 3 * this->eccsteps;
+ }
+
+ return 0;
+}
+
+/**
+ * fill_autooob_layout - [NAND Interface] build the layout for hardware ECC case
+ * @mtd: MTD device structure
+ * @eccbytes: Number of ECC bytes per page
+ *
+ * Build the page_layout array for NAND page handling for hardware ECC
+ * handling basing on the nand_oobinfo structure supplied for the chip
+ */
static int fill_autooob_layout(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct nand_oobinfo *oob = this->autooob;
+ int oobfreesize = 0;
int datasize = mtd->oobblock / this->eccsteps;
- int i = 0, res = 0;
- int eccpos = 0, eccbytes = 0, cur = 0;
+ int i = 0, j = 0, res = 0;
+ int eccpos = 0, eccbytes = 0, cur = 0, oobcur = 0;
this->layout = kmalloc(HW_AUTOOOB_LAYOUT_SIZE * sizeof (struct page_layout_item), GFP_KERNEL);
@@ -2529,32 +2434,47 @@
this->layout_allocated = 1;
while (i < HW_AUTOOOB_LAYOUT_SIZE - 1 &&
- cur < (mtd->oobsize + mtd->oobblock) / this->eccsteps - 1) {
- if (cur == 0) {
+ cur < (mtd->oobsize + mtd->oobblock)) {
+ if (cur % ((mtd->oobblock + mtd->oobsize) / this->eccsteps) == 0) {
this->layout[i].type = ITEM_TYPE_DATA;
this->layout[i].length = datasize;
- } else if (oob->eccpos[eccpos] == cur - datasize) {
+ j++;
+ DEBUG (MTD_DEBUG_LEVEL3, "fill_autooob_layout: data type, length %d\n", this->layout[i].length);
+ } else if (oob->oobfree[oobcur][0] == cur - j * datasize) {
+ this->layout[i].type = ITEM_TYPE_OOBFREE;
+ this->layout[i].length = oob->oobfree[oobcur][1];
+ oobfreesize += this->layout[i].length;
+ oobcur++;
+ DEBUG (MTD_DEBUG_LEVEL3, "fill_autooob_layout: oobfree type, length %d\n", this->layout[i].length);
+ } else if (oob->eccpos[eccpos] == cur - j * datasize) {
int eccpos_cur = eccpos;
do {
eccpos++;
eccbytes++;
- } while (eccbytes < oob->eccbytes / this->eccsteps && oob->eccpos[eccpos] == oob->eccpos[eccpos+1] - 1);
+ } while (eccbytes < oob->eccbytes && oob->eccpos[eccpos] == oob->eccpos[eccpos+1] - 1);
eccpos++;
eccbytes++;
this->layout[i].type = ITEM_TYPE_ECC;
this->layout[i].length = eccpos - eccpos_cur;
+ DEBUG (MTD_DEBUG_LEVEL3, "fill_autooob_layout: ecc type, length %d\n", this->layout[i].length);
} else {
+ int len = min_t(int, oob->eccpos[eccpos], mtd->oobsize / this->eccsteps);
+ len = min_t(int, len, oob->oobfree[oobcur][0]);
this->layout[i].type = ITEM_TYPE_OOB;
- if (eccbytes < oob->eccbytes / this->eccsteps)
- this->layout[i].length = datasize - cur + oob->eccpos[eccpos];
- else
- this->layout[i].length = mtd->oobsize / this->eccsteps - (cur - datasize);
+ this->layout[i].length = len;
+ DEBUG (MTD_DEBUG_LEVEL3, "fill_autooob_layout: oob type, length %d\n", this->layout[i].length);
}
cur += this->layout[i].length;
i++;
}
if (cur < (mtd->oobsize + mtd->oobblock) / this->eccsteps - 1)
res = -1;
+ else {
+ /* XXX */
+ oob->oobfree[0][0] = 0;
+ oob->oobfree[0][1] = oobfreesize;
+ oob->oobfree[1][0] = oob->oobfree[1][1] = 0;
+ }
return res;
}
@@ -2911,29 +2831,15 @@
/* We consider only layout allocation performed in nand_base */
this->layout_allocated = 0;
if (!this->layout) {
- if (this->autooob)
+ if (this->eccmode == NAND_ECC_SOFT)
+ fill_swecc_layout(mtd, 3);
+ else if (this->autooob)
fill_autooob_layout(mtd);
- else {
- switch (this->eccmode) {
- case NAND_ECC_HW12_2048:
- this->layout = hw12_2048_layout;
- break;
- case NAND_ECC_HW3_512:
- this->layout = hw3_512_layout;
- break;
- case NAND_ECC_HW6_512:
- this->layout = hw6_512_layout;
- break;
- case NAND_ECC_HW8_512:
- this->layout = hw8_512_layout;
- break;
- case NAND_ECC_HW3_256:
- this->layout = hw3_256_layout;
- break;
- }
- }
+ else
+ printk(KERN_WARNING "Couldn't generate layout. "
+ "Only NAND_ECC_NONE will work\n");
}
-
+
/* Initialize state, waitqueue and spinlock */
this->state = FL_READY;
init_waitqueue_head (&this->wq);
diff -uNr linux-2.6.10.orig/drivers/mtd/nand/nand_ecc.c linux-2.6.10.nand/drivers/mtd/nand/nand_ecc.c
--- linux-2.6.10.orig/drivers/mtd/nand/nand_ecc.c 2005-11-24 15:58:37.000000000 +0300
+++ linux-2.6.10.nand/drivers/mtd/nand/nand_ecc.c 2005-11-25 21:20:19.000000000 +0300
@@ -38,6 +38,7 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
/*
@@ -116,34 +117,40 @@
*/
int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
{
+ struct nand_chip *this = mtd->priv;
u_char idx, reg1, reg2, reg3;
- int j;
+ int i, j;
+ u_char *ecc_calc = ecc_code;
- /* Initialize variables */
- reg1 = reg2 = reg3 = 0;
- ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
+ for (i = 0; i < this->eccsteps; i++) {
+ /* Initialize variables */
+ reg1 = reg2 = reg3 = 0;
+ ecc_calc[0] = ecc_calc[1] = ecc_calc[2] = 0;
- /* Build up column parity */
- for(j = 0; j < 256; j++) {
+ /* Build up column parity */
+ for(j = 0; j < 256; j++) {
- /* Get CP0 - CP5 from table */
- idx = nand_ecc_precalc_table[dat[j]];
- reg1 ^= (idx & 0x3f);
+ /* Get CP0 - CP5 from table */
+ idx = nand_ecc_precalc_table[dat[j]];
+ reg1 ^= (idx & 0x3f);
- /* All bit XOR = 1 ? */
- if (idx & 0x40) {
- reg3 ^= (u_char) j;
- reg2 ^= ~((u_char) j);
+ /* All bit XOR = 1 ? */
+ if (idx & 0x40) {
+ reg3 ^= (u_char) j;
+ reg2 ^= ~((u_char) j);
+ }
}
- }
- /* Create non-inverted ECC code from line parity */
- nand_trans_result(reg2, reg3, ecc_code);
+ /* Create non-inverted ECC code from line parity */
+ nand_trans_result(reg2, reg3, ecc_calc);
- /* Calculate final ECC code */
- ecc_code[0] = ~ecc_code[0];
- ecc_code[1] = ~ecc_code[1];
- ecc_code[2] = ((~reg1) << 2) | 0x03;
+ /* Calculate final ECC code */
+ ecc_calc[0] = ~ecc_calc[0];
+ ecc_calc[1] = ~ecc_calc[1];
+ ecc_calc[2] = ((~reg1) << 2) | 0x03;
+
+ ecc_calc += 3;
+ }
return 0;
}
diff -uNr linux-2.6.10.orig/include/linux/mtd/nand.h linux-2.6.10.nand/include/linux/mtd/nand.h
--- linux-2.6.10.orig/include/linux/mtd/nand.h 2005-11-24 15:59:00.000000000 +0300
+++ linux-2.6.10.nand/include/linux/mtd/nand.h 2005-11-23 17:50:32.000000000 +0300
@@ -171,6 +171,7 @@
enum {
ITEM_TYPE_DATA,
ITEM_TYPE_OOB,
+ ITEM_TYPE_OOBFREE,
ITEM_TYPE_ECC,
} type;
};
More information about the linux-mtd
mailing list