* [PATCH v2 00/16] DocG3 fixes and write support
@ 2011-11-10 8:05 Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 01/16] mtd/docg3: fix debug log verbosity Robert Jarzmik
` (16 more replies)
0 siblings, 17 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
This patchset is aimed at :
- fixing 5 glitches in the driver
- add several new functions
The added functions are :
- add block erase capability
- add write page capability
- add powerdown and powerup
- add multiple floor support
- add ECC support
- add docg3 modes support
- add protection support (DPS areas)
The ECC part should especially be reviewed by Ivan and Mike,
as this is their work (patch "add ECC correction code").
Since V1, Ivan's and Mike's comments have been taken into
account, and nandtest/nanddump/nandwrite testing has been
done, with and without bitflipped data.
Moreover, 3 new patches have been added:
- fix reading oob+data without correction
This was triggered by the bitflipped pages tests, and
fixes cases where we want to read corrupted data.
- add docg3 modes support
This actually fixes the read part, as we now read in
"normal" mode by default rather than in "reliable"
mode. This is mandatory for nandtest to work, because of
the "even/odd pages logical AND".
- add protection support
This is an evolution to be able to access the whole chip
in protected chips, as they are in smartphones.
Happy review.
--
Robert
Robert Jarzmik (16):
mtd/docg3: fix debug log verbosity
mtd/docg3: fix tracing of IO in writeb
mtd/docg3: fix protection areas reading
mtd/docg3: fix BCH registers
mtd/docg3: fix reading oob+data without correction
mtd/docg3: add multiple floor support
mtd/docg3: add OOB layout to mtdinfo
mtd/docg3: add registers for erasing and writing
mtd/docg3: add OOB buffer to device structure
mtd/docg3: add write functions
mtd/docg3: add erase functions
mtd/docg3: map erase and write functions
mtd/docg3: add ECC correction code
mtd/docg3: add suspend and resume
mtd/docg3: add fast mode
mtd/docg3: add protection areas sysfs access
drivers/mtd/devices/Kconfig | 9 +
drivers/mtd/devices/docg3.c | 1451 ++++++++++++++++++++++++++++++++++++-------
drivers/mtd/devices/docg3.h | 64 ++-
3 files changed, 1299 insertions(+), 225 deletions(-)
--
1.7.5.4
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH v2 01/16] mtd/docg3: fix debug log verbosity
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 02/16] mtd/docg3: fix tracing of IO in writeb Robert Jarzmik
` (15 subsequent siblings)
16 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
Change the NOP debug log verbosity to very verbose to
unburden log analysis.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
drivers/mtd/devices/docg3.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index bdcf5df..6f5233d 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -143,7 +143,7 @@ static void doc_delay(struct docg3 *docg3, int nbNOPs)
{
int i;
- doc_dbg("NOP x %d\n", nbNOPs);
+ doc_vdbg("NOP x %d\n", nbNOPs);
for (i = 0; i < nbNOPs; i++)
doc_writeb(docg3, 0, DOC_NOP);
}
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 02/16] mtd/docg3: fix tracing of IO in writeb
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 01/16] mtd/docg3: fix debug log verbosity Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 03/16] mtd/docg3: fix protection areas reading Robert Jarzmik
` (14 subsequent siblings)
16 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
Writeb was incorrectly traced as a 16 bits write, instead of
a 8 bits write. Fix it by tracing the correct width.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
drivers/mtd/devices/docg3.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 6f5233d..8942a75 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -82,7 +82,7 @@ static inline u16 doc_readw(struct docg3 *docg3, u16 reg)
static inline void doc_writeb(struct docg3 *docg3, u8 val, u16 reg)
{
writeb(val, docg3->base + reg);
- trace_docg3_io(1, 16, reg, val);
+ trace_docg3_io(1, 8, reg, val);
}
static inline void doc_writew(struct docg3 *docg3, u16 val, u16 reg)
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 03/16] mtd/docg3: fix protection areas reading
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 01/16] mtd/docg3: fix debug log verbosity Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 02/16] mtd/docg3: fix tracing of IO in writeb Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 04/16] mtd/docg3: fix BCH registers Robert Jarzmik
` (13 subsequent siblings)
16 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
The protection areas boundaries were on 16bit registers, not
8bit. This is consistent with block numbers, which can
extend up to 4096 on bigger chips (and is 2048 on the
docg3).
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
drivers/mtd/devices/docg3.c | 16 +++++++++-------
1 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 8942a75..5834e6e 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -852,13 +852,15 @@ static int dbg_protection_show(struct seq_file *s, void *p)
{
struct docg3 *docg3 = (struct docg3 *)s->private;
int pos = 0;
- int protect = doc_register_readb(docg3, DOC_PROTECTION);
- int dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS);
- int dps0_low = doc_register_readb(docg3, DOC_DPS0_ADDRLOW);
- int dps0_high = doc_register_readb(docg3, DOC_DPS0_ADDRHIGH);
- int dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS);
- int dps1_low = doc_register_readb(docg3, DOC_DPS1_ADDRLOW);
- int dps1_high = doc_register_readb(docg3, DOC_DPS1_ADDRHIGH);
+ int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high;
+
+ protect = doc_register_readb(docg3, DOC_PROTECTION);
+ dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS);
+ dps0_low = doc_register_readw(docg3, DOC_DPS0_ADDRLOW);
+ dps0_high = doc_register_readw(docg3, DOC_DPS0_ADDRHIGH);
+ dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS);
+ dps1_low = doc_register_readw(docg3, DOC_DPS1_ADDRLOW);
+ dps1_high = doc_register_readw(docg3, DOC_DPS1_ADDRHIGH);
pos += seq_printf(s, "Protection = 0x%02x (",
protect);
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 04/16] mtd/docg3: fix BCH registers
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
` (2 preceding siblings ...)
2011-11-10 8:05 ` [PATCH v2 03/16] mtd/docg3: fix protection areas reading Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-12 19:40 ` Mike Dunn
2011-11-10 8:05 ` [PATCH v2 05/16] mtd/docg3: fix reading oob+data without correction Robert Jarzmik
` (12 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
BCH registers are contiguous, not on every byte. Fix the
register definitions.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
drivers/mtd/devices/docg3.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h
index 0d407be..62af5aa 100644
--- a/drivers/mtd/devices/docg3.h
+++ b/drivers/mtd/devices/docg3.h
@@ -105,7 +105,7 @@
#define DOC_ECCCONF1 0x1042
#define DOC_ECCPRESET 0x1044
#define DOC_HAMMINGPARITY 0x1046
-#define DOC_BCH_SYNDROM(idx) (0x1048 + (idx << 1))
+#define DOC_BCH_SYNDROM(idx) (0x1048 + (idx << 0))
#define DOC_PROTECTION 0x1056
#define DOC_DPS0_ADDRLOW 0x1060
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 05/16] mtd/docg3: fix reading oob+data without correction
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
` (3 preceding siblings ...)
2011-11-10 8:05 ` [PATCH v2 04/16] mtd/docg3: fix BCH registers Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 06/16] mtd/docg3: add multiple floor support Robert Jarzmik
` (11 subsequent siblings)
16 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
Fix the docg3 reads to be able to cope with all possible
data buffer / oob buffer / file mode combinations from
docg3_read_oob().
This especially ensures that raw reads do not use ECC
corrections, and AUTOOOB and PLACEOOB do use ECC
correction.
The approach is to empty docg3_read() and make it a wrapper
to docg3_read_oob(). As docg3_read_oob() handles all the
funny cases (no data buffer but oob buffer, data buffer but
no oob buffer, ...), docg3_read() is just a special use of
docg3_read_oob().
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
drivers/mtd/devices/docg3.c | 194 +++++++++++++++++++++----------------------
1 files changed, 95 insertions(+), 99 deletions(-)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 5834e6e..1c2f54d 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -196,8 +196,8 @@ static int doc_reset_seq(struct docg3 *docg3)
/**
* doc_read_data_area - Read data from data area
* @docg3: the device
- * @buf: the buffer to fill in
- * @len: the lenght to read
+ * @buf: the buffer to fill in (might be NULL is dummy reads)
+ * @len: the length to read
* @first: first time read, DOC_READADDRESS should be set
*
* Reads bytes from flash data. Handles the single byte / even bytes reads.
@@ -218,8 +218,10 @@ static void doc_read_data_area(struct docg3 *docg3, void *buf, int len,
dst16 = buf;
for (i = 0; i < len4; i += 2) {
data16 = doc_readw(docg3, DOC_IOSPACE_DATA);
- *dst16 = data16;
- dst16++;
+ if (dst16) {
+ *dst16 = data16;
+ dst16++;
+ }
}
if (cdr) {
@@ -229,8 +231,10 @@ static void doc_read_data_area(struct docg3 *docg3, void *buf, int len,
dst8 = (u8 *)dst16;
for (i = 0; i < cdr; i++) {
data8 = doc_readb(docg3, DOC_IOSPACE_DATA);
- *dst8 = data8;
- dst8++;
+ if (dst8) {
+ *dst8 = data8;
+ dst8++;
+ }
}
}
}
@@ -542,96 +546,109 @@ static void calc_block_sector(loff_t from, int *block0, int *block1, int *page,
}
/**
- * doc_read - Read bytes from flash
+ * doc_read_oob - Read out of band bytes from flash
* @mtd: the device
* @from: the offset from first block and first page, in bytes, aligned on page
* size
- * @len: the number of bytes to read (must be a multiple of 4)
- * @retlen: the number of bytes actually read
- * @buf: the filled in buffer
+ * @ops: the mtd oob structure
*
- * Reads flash memory pages. This function does not read the OOB chunk, but only
- * the page data.
+ * Reads flash memory OOB area of pages.
*
* Returns 0 if read successfull, of -EIO, -EINVAL if an error occured
*/
-static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
+static int doc_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
{
struct docg3 *docg3 = mtd->priv;
- int block0, block1, page, readlen, ret, ofs = 0;
- int syn[DOC_ECC_BCH_SIZE], eccconf1;
- u8 oob[DOC_LAYOUT_OOB_SIZE];
+ int block0, block1, page, ret, ofs = 0;
+ u8 *oobbuf = ops->oobbuf;
+ u8 *buf = ops->datbuf;
+ size_t len, ooblen, nbdata, nboob;
+ u8 calc_ecc[DOC_ECC_BCH_SIZE], eccconf1;
+
+ if (buf)
+ len = ops->len;
+ else
+ len = 0;
+ if (oobbuf)
+ ooblen = ops->ooblen;
+ else
+ ooblen = 0;
+
+ if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB)
+ oobbuf += ops->ooboffs;
+
+ doc_dbg("doc_read_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n",
+ from, ops->mode, buf, len, oobbuf, ooblen);
+ if ((len % DOC_LAYOUT_PAGE_SIZE) || (ooblen % DOC_LAYOUT_OOB_SIZE) ||
+ (from % DOC_LAYOUT_PAGE_SIZE))
+ return -EINVAL;
ret = -EINVAL;
- doc_dbg("doc_read(from=%lld, len=%zu, buf=%p)\n", from, len, buf);
- if (from % DOC_LAYOUT_PAGE_SIZE)
- goto err;
- if (len % 4)
- goto err;
- calc_block_sector(from, &block0, &block1, &page, &ofs);
+ calc_block_sector(from + len, &block0, &block1, &page, &ofs);
if (block1 > docg3->max_block)
goto err;
- *retlen = 0;
+ ops->oobretlen = 0;
+ ops->retlen = 0;
ret = 0;
- readlen = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE);
- while (!ret && len > 0) {
- readlen = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE);
+ while (!ret && (len > 0 || ooblen > 0)) {
+ calc_block_sector(from, &block0, &block1, &page, &ofs);
+ nbdata = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE);
+ nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE);
ret = doc_read_page_prepare(docg3, block0, block1, page, ofs);
if (ret < 0)
goto err;
ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_COVERED_BYTES);
if (ret < 0)
goto err_in_read;
- ret = doc_read_page_getbytes(docg3, readlen, buf, 1);
- if (ret < readlen)
+ ret = doc_read_page_getbytes(docg3, nbdata, buf, 1);
+ if (ret < nbdata)
goto err_in_read;
- ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE,
- oob, 0);
- if (ret < DOC_LAYOUT_OOB_SIZE)
+ doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE - nbdata,
+ NULL, 0);
+ ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0);
+ if (ret < nboob)
goto err_in_read;
+ doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob,
+ NULL, 0);
- *retlen += readlen;
- buf += readlen;
- len -= readlen;
-
- ofs ^= DOC_LAYOUT_PAGE_OOB_SIZE;
- if (ofs == 0)
- page += 2;
- if (page > DOC_ADDR_PAGE_MASK) {
- page = 0;
- block0 += 2;
- block1 += 2;
- }
-
- /*
- * There should be a BCH bitstream fixing algorithm here ...
- * By now, a page read failure is triggered by BCH error
- */
- doc_get_hw_bch_syndroms(docg3, syn);
+ doc_get_hw_bch_syndroms(docg3, calc_ecc);
eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1);
- doc_dbg("OOB - INFO: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
- oob[0], oob[1], oob[2], oob[3], oob[4],
- oob[5], oob[6]);
- doc_dbg("OOB - HAMMING: %02x\n", oob[7]);
- doc_dbg("OOB - BCH_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
- oob[8], oob[9], oob[10], oob[11], oob[12],
- oob[13], oob[14]);
- doc_dbg("OOB - UNUSED: %02x\n", oob[15]);
+ if (nboob >= DOC_LAYOUT_OOB_SIZE) {
+ doc_dbg("OOB - INFO: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ oobbuf[0], oobbuf[1], oobbuf[2], oobbuf[3],
+ oobbuf[4], oobbuf[5], oobbuf[6]);
+ doc_dbg("OOB - HAMMING: %02x\n", oobbuf[7]);
+ doc_dbg("OOB - BCH_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ oobbuf[8], oobbuf[9], oobbuf[10], oobbuf[11],
+ oobbuf[12], oobbuf[13], oobbuf[14]);
+ doc_dbg("OOB - UNUSED: %02x\n", oobbuf[15]);
+ }
doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1);
- doc_dbg("ECC BCH syndrom: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
- syn[0], syn[1], syn[2], syn[3], syn[4], syn[5], syn[6]);
+ doc_dbg("ECC CALC_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ calc_ecc[0], calc_ecc[1], calc_ecc[2],
+ calc_ecc[3], calc_ecc[4], calc_ecc[5],
+ calc_ecc[6]);
ret = -EBADMSG;
if (block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) {
- if (eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR)
+ if ((eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) &&
+ (eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN))
goto err_in_read;
if (is_prot_seq_error(docg3))
goto err_in_read;
}
+
doc_read_page_finish(docg3);
+ ops->retlen += nbdata;
+ ops->oobretlen += nboob;
+ buf += nbdata;
+ oobbuf += nboob;
+ len -= nbdata;
+ ooblen -= nboob;
+ from += DOC_LAYOUT_PAGE_SIZE;
}
return 0;
@@ -642,54 +659,33 @@ err:
}
/**
- * doc_read_oob - Read out of band bytes from flash
+ * doc_read - Read bytes from flash
* @mtd: the device
* @from: the offset from first block and first page, in bytes, aligned on page
* size
- * @ops: the mtd oob structure
+ * @len: the number of bytes to read (must be a multiple of 4)
+ * @retlen: the number of bytes actually read
+ * @buf: the filled in buffer
*
- * Reads flash memory OOB area of pages.
+ * Reads flash memory pages. This function does not read the OOB chunk, but only
+ * the page data.
*
* Returns 0 if read successfull, of -EIO, -EINVAL if an error occured
*/
-static int doc_read_oob(struct mtd_info *mtd, loff_t from,
- struct mtd_oob_ops *ops)
+static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
{
- struct docg3 *docg3 = mtd->priv;
- int block0, block1, page, ofs, ret;
- u8 *buf = ops->oobbuf;
- size_t len = ops->ooblen;
+ struct mtd_oob_ops ops;
+ size_t ret;
- doc_dbg("doc_read_oob(from=%lld, buf=%p, len=%zu)\n", from, buf, len);
- if (len != DOC_LAYOUT_OOB_SIZE)
- return -EINVAL;
+ memset(&ops, 0, sizeof(ops));
+ ops.datbuf = buf;
+ ops.len = len;
+ ops.mode = MTD_OPS_AUTO_OOB;
- switch (ops->mode) {
- case MTD_OPS_PLACE_OOB:
- buf += ops->ooboffs;
- break;
- default:
- break;
- }
-
- calc_block_sector(from, &block0, &block1, &page, &ofs);
- if (block1 > docg3->max_block)
- return -EINVAL;
-
- ret = doc_read_page_prepare(docg3, block0, block1, page,
- ofs + DOC_LAYOUT_PAGE_SIZE);
- if (!ret)
- ret = doc_read_page_ecc_init(docg3, DOC_LAYOUT_OOB_SIZE);
- if (!ret)
- ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE,
- buf, 1);
- doc_read_page_finish(docg3);
-
- if (ret > 0)
- ops->oobretlen = ret;
- else
- ops->oobretlen = 0;
- return (ret > 0) ? 0 : ret;
+ ret = doc_read_oob(mtd, from, &ops);
+ *retlen = ops.retlen;
+ return ret;
}
static int doc_reload_bbt(struct docg3 *docg3)
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 06/16] mtd/docg3: add multiple floor support
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
` (4 preceding siblings ...)
2011-11-10 8:05 ` [PATCH v2 05/16] mtd/docg3: fix reading oob+data without correction Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo Robert Jarzmik
` (10 subsequent siblings)
16 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
Add support for multiple floors, ie. cascaded docg3
chips. There might be 4 docg3 chips cascaded, sharing the
same address space, and providing up to 4 times the storage
capacity of a unique chip.
Each floor will be seen as an independant mtd device.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
drivers/mtd/devices/docg3.c | 181 +++++++++++++++++++++++++++++-------------
drivers/mtd/devices/docg3.h | 1 +
2 files changed, 126 insertions(+), 56 deletions(-)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 1c2f54d..6eca7f6 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -948,7 +948,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
switch (chip_id) {
case DOC_CHIPID_G3:
- mtd->name = "DiskOnChip G3";
+ mtd->name = kasprintf(GFP_KERNEL, "DiskOnChip G3 floor %d",
+ docg3->device_id);
docg3->max_block = 2047;
break;
}
@@ -975,22 +976,24 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
}
/**
- * doc_probe - Probe the IO space for a DiskOnChip G3 chip
- * @pdev: platform device
+ * doc_probe_device - Check if a device is available
+ * @base: the io space where the device is probed
+ * @floor: the floor of the probed device
+ * @dev: the device
*
- * Probes for a G3 chip at the specified IO space in the platform data
- * ressources.
+ * Checks whether a device at the specified IO range, and floor is available.
*
- * Returns 0 on success, -ENOMEM, -ENXIO on error
+ * Returns a mtd_info struct if there is a device, ENODEV if none found, ENOMEM
+ * if a memory allocation failed. If floor 0 is checked, a reset of the ASIC is
+ * launched.
*/
-static int __init docg3_probe(struct platform_device *pdev)
+static struct mtd_info *doc_probe_device(void __iomem *base, int floor,
+ struct device *dev)
{
- struct device *dev = &pdev->dev;
- struct docg3 *docg3;
- struct mtd_info *mtd;
- struct resource *ress;
int ret, bbt_nbpages;
u16 chip_id, chip_id_inv;
+ struct docg3 *docg3;
+ struct mtd_info *mtd;
ret = -ENOMEM;
docg3 = kzalloc(sizeof(struct docg3), GFP_KERNEL);
@@ -1000,69 +1003,132 @@ static int __init docg3_probe(struct platform_device *pdev)
if (!mtd)
goto nomem2;
mtd->priv = docg3;
+ bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1,
+ 8 * DOC_LAYOUT_PAGE_SIZE);
+ docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL);
+ if (!docg3->bbt)
+ goto nomem3;
- ret = -ENXIO;
- ress = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!ress) {
- dev_err(dev, "No I/O memory resource defined\n");
- goto noress;
- }
- docg3->base = ioremap(ress->start, DOC_IOSPACE_SIZE);
-
- docg3->dev = &pdev->dev;
- docg3->device_id = 0;
+ docg3->dev = dev;
+ docg3->device_id = floor;
+ docg3->base = base;
doc_set_device_id(docg3, docg3->device_id);
- doc_set_asic_mode(docg3, DOC_ASICMODE_RESET);
+ if (!floor)
+ doc_set_asic_mode(docg3, DOC_ASICMODE_RESET);
doc_set_asic_mode(docg3, DOC_ASICMODE_NORMAL);
chip_id = doc_register_readw(docg3, DOC_CHIPID);
chip_id_inv = doc_register_readw(docg3, DOC_CHIPID_INV);
- ret = -ENODEV;
+ ret = 0;
if (chip_id != (u16)(~chip_id_inv)) {
- doc_info("No device found at IO addr %p\n",
- (void *)ress->start);
- goto nochipfound;
+ goto nomem3;
}
switch (chip_id) {
case DOC_CHIPID_G3:
- doc_info("Found a G3 DiskOnChip at addr %p\n",
- (void *)ress->start);
+ doc_info("Found a G3 DiskOnChip at addr %p, floor %d\n",
+ base, floor);
break;
default:
doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id);
- goto nochipfound;
+ goto nomem3;
}
doc_set_driver_info(chip_id, mtd);
- platform_set_drvdata(pdev, mtd);
- ret = -ENOMEM;
- bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1,
- 8 * DOC_LAYOUT_PAGE_SIZE);
- docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL);
- if (!docg3->bbt)
- goto nochipfound;
doc_reload_bbt(docg3);
+ return mtd;
- ret = mtd_device_parse_register(mtd, part_probes,
- NULL, NULL, 0);
- if (ret)
- goto register_error;
-
- doc_dbg_register(docg3);
- return 0;
-
-register_error:
- kfree(docg3->bbt);
-nochipfound:
- iounmap(docg3->base);
-noress:
+nomem3:
kfree(mtd);
nomem2:
kfree(docg3);
nomem1:
+ return ERR_PTR(ret);
+}
+
+/**
+ * doc_release_device - Release a docg3 floor
+ * @mtd: the device
+ */
+static void doc_release_device(struct mtd_info *mtd)
+{
+ struct docg3 *docg3 = mtd->priv;
+
+ mtd_device_unregister(mtd);
+ kfree(docg3->bbt);
+ kfree(docg3);
+ kfree(mtd->name);
+ kfree(mtd);
+}
+
+/**
+ * doc_probe - Probe the IO space for a DiskOnChip G3 chip
+ * @pdev: platform device
+ *
+ * Probes for a G3 chip at the specified IO space in the platform data
+ * ressources. The floor 0 must be available.
+ *
+ * Returns 0 on success, -ENOMEM, -ENXIO on error
+ */
+static int __init docg3_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mtd_info *mtd;
+ struct resource *ress;
+ void __iomem *base;
+ int ret, floor, found = 0;
+ struct mtd_info **docg3_floors;
+
+ ret = -ENXIO;
+ ress = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!ress) {
+ dev_err(dev, "No I/O memory resource defined\n");
+ goto noress;
+ }
+ base = ioremap(ress->start, DOC_IOSPACE_SIZE);
+
+ ret = -ENOMEM;
+ docg3_floors = kzalloc(sizeof(*docg3_floors) * DOC_MAX_NBFLOORS,
+ GFP_KERNEL);
+ if (!docg3_floors)
+ goto nomem;
+
+ ret = 0;
+ for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) {
+ mtd = doc_probe_device(base, floor, dev);
+ if (floor == 0 && !mtd)
+ goto notfound;
+ if (!IS_ERR_OR_NULL(mtd))
+ ret = mtd_device_parse_register(mtd, part_probes,
+ NULL, NULL, 0);
+ else
+ ret = PTR_ERR(mtd);
+ docg3_floors[floor] = mtd;
+ if (ret)
+ goto err_probe;
+ if (mtd)
+ found++;
+ }
+
+ if (!found)
+ goto notfound;
+
+ platform_set_drvdata(pdev, docg3_floors);
+ doc_dbg_register(docg3_floors[0]->priv);
+ return 0;
+
+notfound:
+ ret = -ENODEV;
+ dev_info(dev, "No supported DiskOnChip found\n");
+err_probe:
+ for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)
+ if (docg3_floors[floor])
+ doc_release_device(docg3_floors[floor]);
+nomem:
+ iounmap(base);
+noress:
return ret;
}
@@ -1074,15 +1140,18 @@ nomem1:
*/
static int __exit docg3_release(struct platform_device *pdev)
{
- struct mtd_info *mtd = platform_get_drvdata(pdev);
- struct docg3 *docg3 = mtd->priv;
+ struct mtd_info **docg3_floors = platform_get_drvdata(pdev);
+ struct docg3 *docg3 = docg3_floors[0]->priv;
+ void __iomem *base = docg3->base;
+ int floor;
doc_dbg_unregister(docg3);
- mtd_device_unregister(mtd);
- iounmap(docg3->base);
- kfree(docg3->bbt);
- kfree(docg3);
- kfree(mtd);
+ for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)
+ if (docg3_floors[floor])
+ doc_release_device(docg3_floors[floor]);
+
+ kfree(docg3_floors);
+ iounmap(base);
return 0;
}
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h
index 62af5aa..75df3a1 100644
--- a/drivers/mtd/devices/docg3.h
+++ b/drivers/mtd/devices/docg3.h
@@ -80,6 +80,7 @@
#define DOC_CHIPID_G3 0x200
#define DOC_ERASE_MARK 0xaa
+#define DOC_MAX_NBFLOORS 4
/*
* Flash registers
*/
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
` (5 preceding siblings ...)
2011-11-10 8:05 ` [PATCH v2 06/16] mtd/docg3: add multiple floor support Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-12 19:39 ` Mike Dunn
2011-11-10 8:05 ` [PATCH v2 08/16] mtd/docg3: add registers for erasing and writing Robert Jarzmik
` (9 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
Add OOB layout description for docg3, so that userspace can
use this information to setup the data for write_oob().
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
Since V1: added ECC oobavail field for OOB auto placement
---
drivers/mtd/devices/docg3.c | 15 +++++++++++++++
1 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 6eca7f6..27c4fea 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -63,6 +63,20 @@
*
*/
+/**
+ * struct docg3_oobinfo - DiskOnChip G3 OOB layout
+ * @eccbytes: 8 bytes are used (1 for Hamming ECC, 7 for BCH ECC)
+ * @eccpos: ecc positions (byte 7 is Hamming ECC, byte 8-14 are BCH ECC)
+ * @oobfree: free pageinfo bytes (byte 0 until byte 6, byte 15
+ * @oobavail: 8 available bytes remaining after ECC toll
+ */
+static struct nand_ecclayout docg3_oobinfo = {
+ .eccbytes = 8,
+ .eccpos = {7, 8, 9, 10, 11, 12, 13, 14},
+ .oobfree = {{0, 7}, {15, 1} },
+ .oobavail = 8,
+};
+
static inline u8 doc_readb(struct docg3 *docg3, u16 reg)
{
u8 val = readb(docg3->base + reg);
@@ -973,6 +987,7 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
mtd->write_oob = NULL;
mtd->sync = NULL;
mtd->block_isbad = doc_block_isbad;
+ mtd->ecclayout = &docg3_oobinfo;
}
/**
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 08/16] mtd/docg3: add registers for erasing and writing
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
` (6 preceding siblings ...)
2011-11-10 8:05 ` [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 09/16] mtd/docg3: add OOB buffer to device structure Robert Jarzmik
` (8 subsequent siblings)
16 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
Add the required registers and commands to erase and write
flash pages / blocks.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
drivers/mtd/devices/docg3.h | 14 +++++++++++++-
1 files changed, 13 insertions(+), 1 deletions(-)
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h
index 75df3a1..e9967ab 100644
--- a/drivers/mtd/devices/docg3.h
+++ b/drivers/mtd/devices/docg3.h
@@ -130,6 +130,8 @@
#define DOC_SEQ_SET_PLANE1 0x0e
#define DOC_SEQ_SET_PLANE2 0x10
#define DOC_SEQ_PAGE_SETUP 0x1d
+#define DOC_SEQ_ERASE 0x27
+#define DOC_SEQ_PLANES_STATUS 0x31
/*
* Flash commands
@@ -144,7 +146,10 @@
#define DOC_CMD_PROG_BLOCK_ADDR 0x60
#define DOC_CMD_PROG_CYCLE1 0x80
#define DOC_CMD_PROG_CYCLE2 0x10
+#define DOC_CMD_PROG_CYCLE3 0x11
#define DOC_CMD_ERASECYCLE2 0xd0
+#define DOC_CMD_READ_STATUS 0x70
+#define DOC_CMD_PLANES_STATUS 0x71
#define DOC_CMD_RELIABLE_MODE 0x22
#define DOC_CMD_FAST_MODE 0xa2
@@ -186,7 +191,7 @@
*/
#define DOC_ECCCONF1_BCH_SYNDROM_ERR 0x80
#define DOC_ECCCONF1_UNKOWN1 0x40
-#define DOC_ECCCONF1_UNKOWN2 0x20
+#define DOC_ECCCONF1_PAGE_IS_WRITTEN 0x20
#define DOC_ECCCONF1_UNKOWN3 0x10
#define DOC_ECCCONF1_HAMMING_BITS_MASK 0x0f
@@ -224,6 +229,13 @@
#define DOC_READADDR_ONE_BYTE 0x4000
#define DOC_READADDR_ADDR_MASK 0x1fff
+/*
+ * Status of erase and write operation
+ */
+#define DOC_PLANES_STATUS_FAIL 0x01
+#define DOC_PLANES_STATUS_PLANE0_KO 0x02
+#define DOC_PLANES_STATUS_PLANE1_KO 0x04
+
/**
* struct docg3 - DiskOnChip driver private data
* @dev: the device currently under control
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 09/16] mtd/docg3: add OOB buffer to device structure
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
` (7 preceding siblings ...)
2011-11-10 8:05 ` [PATCH v2 08/16] mtd/docg3: add registers for erasing and writing Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 10/16] mtd/docg3: add write functions Robert Jarzmik
` (7 subsequent siblings)
16 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
Add OOB buffer area to store the OOB data until the actual
page is written, so that it can be completed by hardware ECC
generator.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
drivers/mtd/devices/docg3.h | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h
index e9967ab..397e461 100644
--- a/drivers/mtd/devices/docg3.h
+++ b/drivers/mtd/devices/docg3.h
@@ -243,6 +243,11 @@
* @device_id: number of the cascaded DoCG3 device (0, 1, 2 or 3)
* @if_cfg: if true, reads are on 16bits, else reads are on 8bits
* @bbt: bad block table cache
+ * @oob_write_ofs: offset of the MTD where this OOB should belong (ie. in next
+ * page_write)
+ * @oob_autoecc: if 1, use only bytes 0-7, 15, and fill the others with HW ECC
+ * if 0, use all the 16 bytes.
+ * @oob_write_buf: prepared OOB for next page_write
* @debugfs_root: debugfs root node
*/
struct docg3 {
@@ -252,6 +257,9 @@ struct docg3 {
unsigned int if_cfg:1;
int max_block;
u8 *bbt;
+ loff_t oob_write_ofs;
+ int oob_autoecc;
+ u8 oob_write_buf[DOC_LAYOUT_OOB_SIZE];
struct dentry *debugfs_root;
};
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 10/16] mtd/docg3: add write functions
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
` (8 preceding siblings ...)
2011-11-10 8:05 ` [PATCH v2 09/16] mtd/docg3: add OOB buffer to device structure Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 11/16] mtd/docg3: add erase functions Robert Jarzmik
` (6 subsequent siblings)
16 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
Add write capability to the docg3 driver. The writes are
possible on a single page (512 bytes + 16 bytes), even if
that page is split on 2 physical pages on 2 blocks (each on
one plane).
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
Since V1: fixed noecc write versus autoecc write,
reshuffled the code to have doc_write be a wrapper
to doc_write_oob(),
add oob (raw, auto, place) placement
---
drivers/mtd/devices/docg3.c | 553 ++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 541 insertions(+), 12 deletions(-)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 27c4fea..fd4d47b 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -254,6 +254,40 @@ static void doc_read_data_area(struct docg3 *docg3, void *buf, int len,
}
/**
+ * doc_write_data_area - Write data into data area
+ * @docg3: the device
+ * @buf: the buffer to get input bytes from
+ * @len: the length to write
+ *
+ * Writes bytes into flash data. Handles the single byte / even bytes writes.
+ */
+static void doc_write_data_area(struct docg3 *docg3, const void *buf, int len)
+{
+ int i, cdr, len4;
+ u16 *src16;
+ u8 *src8;
+
+ doc_dbg("doc_write_data_area(buf=%p, len=%d)\n", buf, len);
+ cdr = len & 0x3;
+ len4 = len - cdr;
+
+ doc_writew(docg3, DOC_IOSPACE_DATA, DOC_READADDRESS);
+ src16 = (u16 *)buf;
+ for (i = 0; i < len4; i += 2) {
+ doc_writew(docg3, *src16, DOC_IOSPACE_DATA);
+ src16++;
+ }
+
+ src8 = (u8 *)src16;
+ for (i = 0; i < cdr; i++) {
+ doc_writew(docg3, DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE,
+ DOC_READADDRESS);
+ doc_writeb(docg3, *src8, DOC_IOSPACE_DATA);
+ src8++;
+ }
+}
+
+/**
* doc_set_data_mode - Sets the flash to reliable data mode
* @docg3: the device
*
@@ -343,6 +377,37 @@ static int doc_set_extra_page_mode(struct docg3 *docg3)
}
/**
+ * doc_setup_addr_sector - Setup blocks/page/ofs address for one plane
+ * @docg3: the device
+ * @sector: the sector
+ */
+static void doc_setup_addr_sector(struct docg3 *docg3, int sector)
+{
+ doc_delay(docg3, 1);
+ doc_flash_address(docg3, sector & 0xff);
+ doc_flash_address(docg3, (sector >> 8) & 0xff);
+ doc_flash_address(docg3, (sector >> 16) & 0xff);
+ doc_delay(docg3, 1);
+}
+
+/**
+ * doc_setup_writeaddr_sector - Setup blocks/page/ofs address for one plane
+ * @docg3: the device
+ * @sector: the sector
+ * @ofs: the offset in the page, between 0 and (512 + 16 + 512)
+ */
+static void doc_setup_writeaddr_sector(struct docg3 *docg3, int sector, int ofs)
+{
+ ofs = ofs >> 2;
+ doc_delay(docg3, 1);
+ doc_flash_address(docg3, ofs & 0xff);
+ doc_flash_address(docg3, sector & 0xff);
+ doc_flash_address(docg3, (sector >> 8) & 0xff);
+ doc_flash_address(docg3, (sector >> 16) & 0xff);
+ doc_delay(docg3, 1);
+}
+
+/**
* doc_seek - Set both flash planes to the specified block, page for reading
* @docg3: the device
* @block0: the first plane block index
@@ -378,27 +443,73 @@ static int doc_read_seek(struct docg3 *docg3, int block0, int block1, int page,
if (ret)
goto out;
- sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK);
doc_flash_sequence(docg3, DOC_SEQ_READ);
+ sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK);
doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR);
- doc_delay(docg3, 1);
- doc_flash_address(docg3, sector & 0xff);
- doc_flash_address(docg3, (sector >> 8) & 0xff);
- doc_flash_address(docg3, (sector >> 16) & 0xff);
- doc_delay(docg3, 1);
+ doc_setup_addr_sector(docg3, sector);
sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK);
doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR);
+ doc_setup_addr_sector(docg3, sector);
doc_delay(docg3, 1);
- doc_flash_address(docg3, sector & 0xff);
- doc_flash_address(docg3, (sector >> 8) & 0xff);
- doc_flash_address(docg3, (sector >> 16) & 0xff);
+
+out:
+ return ret;
+}
+
+/**
+ * doc_write_seek - Set both flash planes to the specified block, page for writing
+ * @docg3: the device
+ * @block0: the first plane block index
+ * @block1: the second plane block index
+ * @page: the page index within the block
+ * @ofs: offset in page to write
+ *
+ * Programs the flash even and odd planes to the specific block and page.
+ * Alternatively, programs the flash to the wear area of the specified page.
+ */
+static int doc_write_seek(struct docg3 *docg3, int block0, int block1, int page,
+ int ofs)
+{
+ int ret = 0, sector;
+
+ doc_dbg("doc_write_seek(blocks=(%d,%d), page=%d, ofs=%d)\n",
+ block0, block1, page, ofs);
+
+ doc_set_reliable_mode(docg3);
+
+ if (ofs < 2 * DOC_LAYOUT_PAGE_SIZE) {
+ doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE1);
+ doc_flash_command(docg3, DOC_CMD_READ_PLANE1);
+ doc_delay(docg3, 2);
+ } else {
+ doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE2);
+ doc_flash_command(docg3, DOC_CMD_READ_PLANE2);
+ doc_delay(docg3, 2);
+ }
+
+ doc_flash_sequence(docg3, DOC_SEQ_PAGE_SETUP);
+ doc_flash_command(docg3, DOC_CMD_PROG_CYCLE1);
+
+ sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK);
+ doc_setup_writeaddr_sector(docg3, sector, ofs);
+
+ doc_flash_command(docg3, DOC_CMD_PROG_CYCLE3);
doc_delay(docg3, 2);
+ ret = doc_wait_ready(docg3);
+ if (ret)
+ goto out;
+
+ doc_flash_command(docg3, DOC_CMD_PROG_CYCLE1);
+ sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK);
+ doc_setup_writeaddr_sector(docg3, sector, ofs);
+ doc_delay(docg3, 1);
out:
return ret;
}
+
/**
* doc_read_page_ecc_init - Initialize hardware ECC engine
* @docg3: the device
@@ -421,6 +532,58 @@ static int doc_read_page_ecc_init(struct docg3 *docg3, int len)
}
/**
+ * doc_write_page_ecc_init - Initialize hardware BCH ECC engine
+ * @docg3: the device
+ * @len: the number of bytes covered by the ECC (BCH covered)
+ *
+ * The function does initialize the hardware ECC engine to compute the Hamming
+ * ECC (on 1 byte) and the BCH Syndroms (on 7 bytes).
+ *
+ * Return 0 if succeeded, -EIO on error
+ */
+static int doc_write_page_ecc_init(struct docg3 *docg3, int len)
+{
+ doc_writew(docg3, !DOC_ECCCONF0_READ_MODE
+ | DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE
+ | (len & DOC_ECCCONF0_DATA_BYTES_MASK),
+ DOC_ECCCONF0);
+ doc_delay(docg3, 4);
+ doc_register_readb(docg3, DOC_FLASHCONTROL);
+ return doc_wait_ready(docg3);
+}
+
+/**
+ * doc_ecc_disable - Disable Hamming and BCH ECC hardware calculator
+ * @docg3: the device
+ *
+ * Disables the hardware ECC generator and checker, for unchecked reads (as when
+ * reading OOB only or write status byte).
+ */
+static void doc_ecc_disable(struct docg3 *docg3)
+{
+ doc_writew(docg3, DOC_ECCCONF0_READ_MODE, DOC_ECCCONF0);
+ doc_delay(docg3, 4);
+}
+
+/**
+ * doc_hamming_ecc_init - Initialize hardware Hamming ECC engine
+ * @docg3: the device
+ * @nb_bytes: the number of bytes covered by the ECC (Hamming covered)
+ *
+ * This function programs the ECC hardware to compute the hamming code on the
+ * last provided N bytes to the hardware generator.
+ */
+static void doc_hamming_ecc_init(struct docg3 *docg3, int nb_bytes)
+{
+ u8 ecc_conf1;
+
+ ecc_conf1 = doc_register_readb(docg3, DOC_ECCCONF1);
+ ecc_conf1 &= ~DOC_ECCCONF1_HAMMING_BITS_MASK;
+ ecc_conf1 |= (nb_bytes & DOC_ECCCONF1_HAMMING_BITS_MASK);
+ doc_writeb(docg3, ecc_conf1, DOC_ECCCONF1);
+}
+
+/**
* doc_read_page_prepare - Prepares reading data from a flash page
* @docg3: the device
* @block0: the first plane block index on flash memory
@@ -506,11 +669,25 @@ static int doc_read_page_getbytes(struct docg3 *docg3, int len, u_char *buf,
}
/**
+ * doc_write_page_putbytes - Writes bytes into a prepared page
+ * @docg3: the device
+ * @len: the number of bytes to be written
+ * @buf: the buffer of input bytes
+ *
+ */
+static void doc_write_page_putbytes(struct docg3 *docg3, int len,
+ const u_char *buf)
+{
+ doc_write_data_area(docg3, buf, len);
+ doc_delay(docg3, 2);
+}
+
+/**
* doc_get_hw_bch_syndroms - Get hardware calculated BCH syndroms
* @docg3: the device
* @syns: the array of 7 integers where the syndroms will be stored
*/
-static void doc_get_hw_bch_syndroms(struct docg3 *docg3, int *syns)
+static void doc_get_hw_bch_syndroms(struct docg3 *docg3, u8 *syns)
{
int i;
@@ -519,6 +696,16 @@ static void doc_get_hw_bch_syndroms(struct docg3 *docg3, int *syns)
}
/**
+ * doc_page_finish - Ends reading/writing of a flash page
+ * @docg3: the device
+ */
+static void doc_page_finish(struct docg3 *docg3)
+{
+ doc_writeb(docg3, 0, DOC_DATAEND);
+ doc_delay(docg3, 2);
+}
+
+/**
* doc_read_page_finish - Ends reading of a flash page
* @docg3: the device
*
@@ -528,8 +715,7 @@ static void doc_get_hw_bch_syndroms(struct docg3 *docg3, int *syns)
*/
static void doc_read_page_finish(struct docg3 *docg3)
{
- doc_writeb(docg3, 0, DOC_DATAEND);
- doc_delay(docg3, 2);
+ doc_page_finish(docg3);
doc_set_device_id(docg3, 0);
}
@@ -791,6 +977,348 @@ static int doc_get_erase_count(struct docg3 *docg3, loff_t from)
return max(plane1_erase_count, plane2_erase_count);
}
+/**
+ * doc_get_op_status - get erase/write operation status
+ * @docg3: the device
+ *
+ * Queries the status from the chip, and returns it
+ *
+ * Returns the status (bits DOC_PLANES_STATUS_*)
+ */
+static int doc_get_op_status(struct docg3 *docg3)
+{
+ u8 status;
+
+ doc_flash_sequence(docg3, DOC_SEQ_PLANES_STATUS);
+ doc_flash_command(docg3, DOC_CMD_PLANES_STATUS);
+ doc_delay(docg3, 5);
+
+ doc_ecc_disable(docg3);
+ doc_read_data_area(docg3, &status, 1, 1);
+ return status;
+}
+
+/**
+ * doc_write_erase_wait_status - wait for write or erase completion
+ * @docg3: the device
+ *
+ * Wait for the chip to be ready again after erase or write operation, and check
+ * erase/write status.
+ *
+ * Returns 0 if erase successfull, -EIO if erase/write issue, -ETIMEOUT if
+ * timeout
+ */
+static int doc_write_erase_wait_status(struct docg3 *docg3)
+{
+ int status, ret = 0;
+
+ if (!doc_is_ready(docg3))
+ usleep_range(3000, 3000);
+ if (!doc_is_ready(docg3)) {
+ doc_dbg("Timeout reached and the chip is still not ready\n");
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ status = doc_get_op_status(docg3);
+ if (status & DOC_PLANES_STATUS_FAIL) {
+ doc_dbg("Erase/Write failed on (a) plane(s), status = %x\n",
+ status);
+ ret = -EIO;
+ }
+
+out:
+ doc_page_finish(docg3);
+ return ret;
+}
+
+/**
+ * doc_write_page - Write a single page to the chip
+ * @docg3: the device
+ * @to: the offset from first block and first page, in bytes, aligned on page
+ * size
+ * @buf: buffer to get bytes from
+ * @oob: buffer to get out of band bytes from (can be NULL if no OOB should be
+ * written)
+ * @autoecc: if 0, all 16 bytes from OOB are taken, regardless of HW Hamming or
+ * BCH computations. If 1, only bytes 0-7 and byte 15 are taken,
+ * remaining ones are filled with hardware Hamming and BCH
+ * computations. Its value is not meaningfull is oob == NULL.
+ *
+ * Write one full page (ie. 1 page split on two planes), of 512 bytes, with the
+ * OOB data. The OOB ECC is automatically computed by the hardware Hamming and
+ * BCH generator if autoecc is not null.
+ *
+ * Returns 0 if write successful, -EIO if write error, -EAGAIN if timeout
+ */
+static int doc_write_page(struct docg3 *docg3, loff_t to, const u_char *buf,
+ const u_char *oob, int autoecc)
+{
+ int block0, block1, page, ret, ofs = 0;
+ u8 syn[DOC_ECC_BCH_SIZE], hamming;
+
+ doc_dbg("doc_write_page(to=%lld)\n", to);
+ calc_block_sector(to, &block0, &block1, &page, &ofs);
+
+ doc_set_device_id(docg3, docg3->device_id);
+ ret = doc_reset_seq(docg3);
+ if (ret)
+ goto err;
+
+ /* Program the flash address block and page */
+ ret = doc_write_seek(docg3, block0, block1, page, ofs);
+ if (ret)
+ goto err;
+
+ doc_write_page_ecc_init(docg3, DOC_ECC_BCH_COVERED_BYTES);
+ doc_delay(docg3, 2);
+ doc_write_page_putbytes(docg3, DOC_LAYOUT_PAGE_SIZE, buf);
+
+ if (oob && autoecc) {
+ doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ, oob);
+ doc_delay(docg3, 2);
+ oob += DOC_LAYOUT_OOB_UNUSED_OFS;
+
+ hamming = doc_register_readb(docg3, DOC_HAMMINGPARITY);
+ doc_delay(docg3, 2);
+ doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_HAMMING_SZ,
+ &hamming);
+ doc_delay(docg3, 2);
+
+ doc_get_hw_bch_syndroms(docg3, syn);
+ doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_BCH_SZ, syn);
+ doc_delay(docg3, 2);
+
+ doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_UNUSED_SZ, oob);
+ }
+ if (oob && !autoecc)
+ doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_SIZE, oob);
+
+ doc_delay(docg3, 2);
+ doc_page_finish(docg3);
+ doc_delay(docg3, 2);
+ doc_flash_command(docg3, DOC_CMD_PROG_CYCLE2);
+ doc_delay(docg3, 2);
+
+ /*
+ * The wait status will perform another doc_page_finish() call, but that
+ * seems to please the docg3, so leave it.
+ */
+ ret = doc_write_erase_wait_status(docg3);
+ return ret;
+err:
+ doc_read_page_finish(docg3);
+ return ret;
+}
+
+/**
+ * doc_guess_autoecc - Guess autoecc mode from mbd_oob_ops
+ * @ops: the oob operations
+ *
+ * Returns 0 or 1 if success, -EINVAL if invalid oob mode
+ */
+static int doc_guess_autoecc(struct mtd_oob_ops *ops)
+{
+ int autoecc;
+
+ switch (ops->mode) {
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ autoecc = 1;
+ break;
+ case MTD_OPS_RAW:
+ autoecc = 0;
+ break;
+ default:
+ autoecc = -EINVAL;
+ }
+ return autoecc;
+}
+
+/**
+ * doc_fill_autooob - Fill a 16 bytes OOB from 8 non-ECC bytes
+ * @dst: the target 16 bytes OOB buffer
+ * @oobsrc: the source 8 bytes non-ECC OOB buffer
+ *
+ */
+static void doc_fill_autooob(u8 *dst, u8 *oobsrc)
+{
+ memcpy(dst, oobsrc, DOC_LAYOUT_OOB_PAGEINFO_SZ);
+ dst[DOC_LAYOUT_OOB_UNUSED_OFS] = oobsrc[DOC_LAYOUT_OOB_PAGEINFO_SZ];
+}
+
+/**
+ * doc_backup_oob - Backup OOB into docg3 structure
+ * @docg3: the device
+ * @to: the page offset in the chip
+ * @ops: the OOB size and buffer
+ *
+ * As the docg3 should write a page with its OOB in one pass, and some userland
+ * applications do write_oob() to setup the OOB and then write(), store the OOB
+ * into a temporary storage. This is very dangerous, as 2 concurrent
+ * applications could store an OOB, and then write their pages (which will
+ * result into one having its OOB corrupted).
+ *
+ * The only reliable way would be for userland to call doc_write_oob() with both
+ * the page data _and_ the OOB area.
+ *
+ * Returns 0 if success, -EINVAL if ops content invalid
+ */
+static int doc_backup_oob(struct docg3 *docg3, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int ooblen = ops->ooblen, autoecc;
+
+ if (ooblen != DOC_LAYOUT_OOB_SIZE)
+ return -EINVAL;
+ autoecc = doc_guess_autoecc(ops);
+ if (autoecc < 0)
+ return autoecc;
+
+ docg3->oob_write_ofs = to;
+ docg3->oob_autoecc = autoecc;
+ if (ops->mode == MTD_OPS_AUTO_OOB) {
+ doc_fill_autooob(docg3->oob_write_buf, ops->oobbuf);
+ ops->oobretlen = 8;
+ } else {
+ memcpy(docg3->oob_write_buf, ops->oobbuf, DOC_LAYOUT_OOB_SIZE);
+ ops->oobretlen = DOC_LAYOUT_OOB_SIZE;
+ }
+ return 0;
+}
+
+/**
+ * doc_write_oob - Write out of band bytes to flash
+ * @mtd: the device
+ * @ofs: the offset from first block and first page, in bytes, aligned on page
+ * size
+ * @ops: the mtd oob structure
+ *
+ * Either write OOB data into a temporary buffer, for the subsequent write
+ * page. The provided OOB should be 16 bytes long. If a data buffer is provided
+ * as well, issue the page write.
+ * Or provide data without OOB, and then a all zeroed OOB will be used (ECC will
+ * still be filled in if asked for).
+ *
+ * Returns 0 is successfull, EINVAL if length is not 14 bytes
+ */
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
+ struct mtd_oob_ops *ops)
+{
+ struct docg3 *docg3 = mtd->priv;
+ int block0, block1, page, ret, pofs = 0, autoecc, oobdelta;
+ u8 *oobbuf = ops->oobbuf;
+ u8 *buf = ops->datbuf;
+ size_t len, ooblen;
+ u8 oob[DOC_LAYOUT_OOB_SIZE];
+
+ if (buf)
+ len = ops->len;
+ else
+ len = 0;
+ if (oobbuf)
+ ooblen = ops->ooblen;
+ else
+ ooblen = 0;
+
+ if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB)
+ oobbuf += ops->ooboffs;
+
+ doc_dbg("doc_write_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n",
+ ofs, ops->mode, buf, len, oobbuf, ooblen);
+ switch (ops->mode) {
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_RAW:
+ oobdelta = mtd->oobsize;
+ break;
+ case MTD_OPS_AUTO_OOB:
+ oobdelta = mtd->ecclayout->oobavail;
+ break;
+ default:
+ oobdelta = 0;
+ }
+ if ((len % DOC_LAYOUT_PAGE_SIZE) || (ooblen % oobdelta) ||
+ (ofs % DOC_LAYOUT_PAGE_SIZE))
+ return -EINVAL;
+ if (len && ooblen &&
+ (len / DOC_LAYOUT_PAGE_SIZE) != (ooblen / oobdelta))
+ return -EINVAL;
+
+ ret = -EINVAL;
+ calc_block_sector(ofs + len, &block0, &block1, &page, &pofs);
+ if (block1 > docg3->max_block)
+ goto err;
+
+ ops->oobretlen = 0;
+ ops->retlen = 0;
+ ret = 0;
+ if (len == 0 && ooblen == 0)
+ return -EINVAL;
+ if (len == 0 && ooblen > 0)
+ return doc_backup_oob(docg3, ofs, ops);
+
+ autoecc = doc_guess_autoecc(ops);
+ if (autoecc < 0)
+ return autoecc;
+
+ while (!ret && len > 0) {
+ memset(oob, 0, sizeof(oob));
+ if (ofs == docg3->oob_write_ofs)
+ memcpy(oob, docg3->oob_write_buf, DOC_LAYOUT_OOB_SIZE);
+ else if (ooblen > 0 && ops->mode == MTD_OPS_AUTO_OOB)
+ doc_fill_autooob(oob, oobbuf);
+ else if (ooblen > 0)
+ memcpy(oob, oobbuf, DOC_LAYOUT_OOB_SIZE);
+ ret = doc_write_page(docg3, ofs, buf, oob, autoecc);
+
+ ofs += DOC_LAYOUT_PAGE_SIZE;
+ len -= DOC_LAYOUT_PAGE_SIZE;
+ buf += DOC_LAYOUT_PAGE_SIZE;
+ if (ooblen) {
+ oobbuf += oobdelta;
+ ooblen -= oobdelta;
+ ops->oobretlen += oobdelta;
+ }
+ ops->retlen += DOC_LAYOUT_PAGE_SIZE;
+ }
+err:
+ doc_set_device_id(docg3, 0);
+ return ret;
+}
+
+/**
+ * doc_write - Write a buffer to the chip
+ * @mtd: the device
+ * @to: the offset from first block and first page, in bytes, aligned on page
+ * size
+ * @len: the number of bytes to write (must be a full page size, ie. 512)
+ * @retlen: the number of bytes actually written (0 or 512)
+ * @buf: the buffer to get bytes from
+ *
+ * Writes data to the chip.
+ *
+ * Returns 0 if write successful, -EIO if write error
+ */
+static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct docg3 *docg3 = mtd->priv;
+ int ret;
+ struct mtd_oob_ops ops;
+
+ doc_dbg("doc_write(to=%lld, len=%zu)\n", to, len);
+ ops.datbuf = (char *)buf;
+ ops.len = len;
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ops.oobbuf = NULL;
+ ops.ooblen = 0;
+ ops.ooboffs = 0;
+
+ ret = doc_write_oob(mtd, to, &ops);
+ *retlen = ops.retlen;
+ return ret;
+}
+
/*
* Debug sysfs entries
*/
@@ -1052,6 +1580,7 @@ static struct mtd_info *doc_probe_device(void __iomem *base, int floor,
doc_set_driver_info(chip_id, mtd);
+ doc_hamming_ecc_init(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ);
doc_reload_bbt(docg3);
return mtd;
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 11/16] mtd/docg3: add erase functions
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
` (9 preceding siblings ...)
2011-11-10 8:05 ` [PATCH v2 10/16] mtd/docg3: add write functions Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 12/16] mtd/docg3: map erase and write functions Robert Jarzmik
` (5 subsequent siblings)
16 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
Add erase capability to the docg3 driver. The erase block is
made of 2 physical blocks, as both share all 64 pages. That
makes an erase block of at least 64 kBytes.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
drivers/mtd/devices/docg3.c | 90 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 90 insertions(+), 0 deletions(-)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index fd4d47b..3087b15 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -1033,6 +1033,96 @@ out:
}
/**
+ * doc_erase_block - Erase a couple of blocks
+ * @docg3: the device
+ * @block0: the first block to erase (leftmost plane)
+ * @block1: the second block to erase (rightmost plane)
+ *
+ * Erase both blocks, and return operation status
+ *
+ * Returns 0 if erase successful, -EIO if erase issue, -ETIMEOUT if chip not
+ * ready for too long
+ */
+static int doc_erase_block(struct docg3 *docg3, int block0, int block1)
+{
+ int ret, sector;
+
+ doc_dbg("doc_erase_block(blocks=(%d,%d))\n", block0, block1);
+ ret = doc_reset_seq(docg3);
+ if (ret)
+ return -EIO;
+
+ doc_set_reliable_mode(docg3);
+ doc_flash_sequence(docg3, DOC_SEQ_ERASE);
+
+ sector = block0 << DOC_ADDR_BLOCK_SHIFT;
+ doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR);
+ doc_setup_addr_sector(docg3, sector);
+ sector = block1 << DOC_ADDR_BLOCK_SHIFT;
+ doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR);
+ doc_setup_addr_sector(docg3, sector);
+ doc_delay(docg3, 1);
+
+ doc_flash_command(docg3, DOC_CMD_ERASECYCLE2);
+ doc_delay(docg3, 2);
+
+ if (is_prot_seq_error(docg3)) {
+ doc_err("Erase blocks %d,%d error\n", block0, block1);
+ return -EIO;
+ }
+
+ return doc_write_erase_wait_status(docg3);
+}
+
+/**
+ * doc_erase - Erase a portion of the chip
+ * @mtd: the device
+ * @info: the erase info
+ *
+ * Erase a bunch of contiguous blocks, by pairs, as a "mtd" page of 1024 is
+ * split into 2 pages of 512 bytes on 2 contiguous blocks.
+ *
+ * Returns 0 if erase successful, -EINVAL if adressing error, -EIO if erase
+ * issue
+ */
+static int doc_erase(struct mtd_info *mtd, struct erase_info *info)
+{
+ struct docg3 *docg3 = mtd->priv;
+ uint64_t len;
+ int block0, block1, page, ret, ofs = 0;
+
+ doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len);
+ doc_set_device_id(docg3, docg3->device_id);
+
+ info->state = MTD_ERASE_PENDING;
+ calc_block_sector(info->addr + info->len,
+ &block0, &block1, &page, &ofs);
+ ret = -EINVAL;
+ if (block1 > docg3->max_block || page || ofs)
+ goto reset_err;
+
+ ret = 0;
+ calc_block_sector(info->addr, &block0, &block1, &page, &ofs);
+ doc_set_reliable_mode(docg3);
+ for (len = info->len; !ret && len > 0; len -= mtd->erasesize) {
+ info->state = MTD_ERASING;
+ ret = doc_erase_block(docg3, block0, block1);
+ block0 += 2;
+ block1 += 2;
+ }
+
+ if (ret)
+ goto reset_err;
+
+ info->state = MTD_ERASE_DONE;
+ return 0;
+
+reset_err:
+ info->state = MTD_ERASE_FAILED;
+ return ret;
+}
+
+/**
* doc_write_page - Write a single page to the chip
* @docg3: the device
* @to: the offset from first block and first page, in bytes, aligned on page
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 12/16] mtd/docg3: map erase and write functions
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
` (10 preceding siblings ...)
2011-11-10 8:05 ` [PATCH v2 11/16] mtd/docg3: add erase functions Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 13/16] mtd/docg3: add ECC correction code Robert Jarzmik
` (4 subsequent siblings)
16 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
Map the developped write and erase functions into the mtd
structure.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
drivers/mtd/devices/docg3.c | 14 ++++----------
1 files changed, 4 insertions(+), 10 deletions(-)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 3087b15..1f949ea 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -41,8 +41,6 @@
*
* As no specification is available from M-Systems/Sandisk, this drivers lacks
* several functions available on the chip, as :
- * - block erase
- * - page write
* - IPL write
* - ECC fixing (lack of BCH algorith understanding)
* - powerdown / powerup
@@ -1586,23 +1584,19 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
break;
}
mtd->type = MTD_NANDFLASH;
- /*
- * Once write methods are added, the correct flags will be set.
- * mtd->flags = MTD_CAP_NANDFLASH;
- */
- mtd->flags = MTD_CAP_ROM;
+ mtd->flags = MTD_CAP_NANDFLASH;
mtd->size = (docg3->max_block + 1) * DOC_LAYOUT_BLOCK_SIZE;
mtd->erasesize = DOC_LAYOUT_BLOCK_SIZE * DOC_LAYOUT_NBPLANES;
mtd->writesize = DOC_LAYOUT_PAGE_SIZE;
mtd->oobsize = DOC_LAYOUT_OOB_SIZE;
mtd->owner = THIS_MODULE;
- mtd->erase = NULL;
+ mtd->erase = doc_erase;
mtd->point = NULL;
mtd->unpoint = NULL;
mtd->read = doc_read;
- mtd->write = NULL;
+ mtd->write = doc_write;
mtd->read_oob = doc_read_oob;
- mtd->write_oob = NULL;
+ mtd->write_oob = doc_write_oob;
mtd->sync = NULL;
mtd->block_isbad = doc_block_isbad;
mtd->ecclayout = &docg3_oobinfo;
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 13/16] mtd/docg3: add ECC correction code
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
` (11 preceding siblings ...)
2011-11-10 8:05 ` [PATCH v2 12/16] mtd/docg3: map erase and write functions Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-12 19:49 ` Mike Dunn
2011-11-10 8:05 ` [PATCH v2 14/16] mtd/docg3: add suspend and resume Robert Jarzmik
` (3 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
Credit for discovering the BCH algorith parameters, and bit
reversing algorithm is to be give to Mike Dunn and Ivan
Djelic.
The BCH correction code relied upon the BCH library, where
all data and ECC is bit-reversed. The BCH library works
correctly when each input byte is bit-reversed, and
accordingly ECC output is also bit-reversed.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
Since V1: integrate reviews from Ivan Djelic and Mike Dunn.
---
drivers/mtd/devices/Kconfig | 9 +++
drivers/mtd/devices/docg3.c | 116 ++++++++++++++++++++++++++++++++++--------
drivers/mtd/devices/docg3.h | 11 ++++-
3 files changed, 113 insertions(+), 23 deletions(-)
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 6d91a1f..b016e2b 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -251,6 +251,8 @@ config MTD_DOC2001PLUS
config MTD_DOCG3
tristate "M-Systems Disk-On-Chip G3"
+ select BCH
+ select BCH_CONST_PARAMS
---help---
This provides an MTD device driver for the M-Systems DiskOnChip
G3 devices.
@@ -259,6 +261,13 @@ config MTD_DOCG3
M-Systems and now Sandisk. The support is very experimental,
and doesn't give access to any write operations.
+if MTD_DOCG3
+config BCH_CONST_M
+ default 14
+config BCH_CONST_T
+ default 4
+endif
+
config MTD_DOCPROBE
tristate
select MTD_DOCECC
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 1f949ea..26cc179 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -29,6 +29,9 @@
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
+#include <linux/bitmap.h>
+#include <linux/bitrev.h>
+#include <linux/bch.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
@@ -42,7 +45,6 @@
* As no specification is available from M-Systems/Sandisk, this drivers lacks
* several functions available on the chip, as :
* - IPL write
- * - ECC fixing (lack of BCH algorith understanding)
* - powerdown / powerup
*
* The bus data width (8bits versus 16bits) is not handled (if_cfg flag), and
@@ -51,8 +53,7 @@
* DocG3 relies on 2 ECC algorithms, which are handled in hardware :
* - a 1 byte Hamming code stored in the OOB for each page
* - a 7 bytes BCH code stored in the OOB for each page
- * The BCH part is only used for check purpose, no correction is available as
- * some information is missing. What is known is that :
+ * The BCH ECC is :
* - BCH is in GF(2^14)
* - BCH is over data of 520 bytes (512 page + 7 page_info bytes
* + 1 hamming byte)
@@ -75,6 +76,11 @@ static struct nand_ecclayout docg3_oobinfo = {
.oobavail = 8,
};
+/**
+ * struct docg3_bch - BCH engine
+ */
+static struct bch_control *docg3_bch;
+
static inline u8 doc_readb(struct docg3 *docg3, u16 reg)
{
u8 val = readb(docg3->base + reg);
@@ -582,6 +588,54 @@ static void doc_hamming_ecc_init(struct docg3 *docg3, int nb_bytes)
}
/**
+ * doc_correct_data - Fix if need be read data from flash
+ * @docg3: the device
+ * @buf: the buffer of read data (512 + 7 + 1 bytes)
+ * @hwecc: the hardware calculated ECC.
+ * It's in fact recv_ecc ^ calc_ecc, where recv_ecc was read from OOB
+ * area data, and calc_ecc the ECC calculated by the hardware generator.
+ *
+ * Checks if the received data matches the ECC, and if an error is detected,
+ * tries to fix the bit flips (at most 4) in the buffer buf. As the docg3
+ * understands the (data, ecc, syndroms) in an inverted order in comparison to
+ * the BCH library, the function reverses the order of bits (ie. bit7 and bit0,
+ * bit6 and bit 1, ...) for all ECC data.
+ *
+ * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch
+ * algorithm is used to decode this. However the hw operates on page
+ * data in a bit order that is the reverse of that of the bch alg,
+ * requiring that the bits be reversed on the result. Thanks to Ivan
+ * Djelic for his analysis.
+ *
+ * Returns number of fixed bits (0, 1, 2, 3, 4) or -EBADMSG if too many bit
+ * errors were detected and cannot be fixed.
+ */
+static int doc_ecc_bch_fix_data(struct docg3 *docg3, void *buf, u8 *hwecc)
+{
+ u8 ecc[DOC_ECC_BCH_SIZE];
+ int errorpos[DOC_ECC_BCH_T], i, numerrs;
+
+ for (i = 0; i < DOC_ECC_BCH_SIZE; i++)
+ ecc[i] = bitrev8(hwecc[i]);
+ numerrs = decode_bch(docg3_bch, NULL, DOC_ECC_BCH_COVERED_BYTES,
+ NULL, ecc, NULL, errorpos);
+ BUG_ON(numerrs == -EINVAL);
+ if (numerrs < 0)
+ goto out;
+
+ for (i = 0; i < numerrs; i++)
+ errorpos[i] = (errorpos[i] & ~7) | (7 - (errorpos[i] & 7));
+ for (i = 0; i < numerrs; i++)
+ if (errorpos[i] < DOC_ECC_BCH_COVERED_BYTES*8)
+ /* error is located in data, correct it */
+ change_bit(errorpos[i], buf);
+out:
+ doc_dbg("doc_ecc_bch_fix_data: flipped %d bits\n", numerrs);
+ return numerrs;
+}
+
+
+/**
* doc_read_page_prepare - Prepares reading data from a flash page
* @docg3: the device
* @block0: the first plane block index on flash memory
@@ -762,7 +816,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
u8 *oobbuf = ops->oobbuf;
u8 *buf = ops->datbuf;
size_t len, ooblen, nbdata, nboob;
- u8 calc_ecc[DOC_ECC_BCH_SIZE], eccconf1;
+ u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1;
if (buf)
len = ops->len;
@@ -797,7 +851,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
ret = doc_read_page_prepare(docg3, block0, block1, page, ofs);
if (ret < 0)
goto err;
- ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_COVERED_BYTES);
+ ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES);
if (ret < 0)
goto err_in_read;
ret = doc_read_page_getbytes(docg3, nbdata, buf, 1);
@@ -811,7 +865,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob,
NULL, 0);
- doc_get_hw_bch_syndroms(docg3, calc_ecc);
+ doc_get_hw_bch_syndroms(docg3, hwecc);
eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1);
if (nboob >= DOC_LAYOUT_OOB_SIZE) {
@@ -825,18 +879,28 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
doc_dbg("OOB - UNUSED: %02x\n", oobbuf[15]);
}
doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1);
- doc_dbg("ECC CALC_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
- calc_ecc[0], calc_ecc[1], calc_ecc[2],
- calc_ecc[3], calc_ecc[4], calc_ecc[5],
- calc_ecc[6]);
-
- ret = -EBADMSG;
- if (block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) {
- if ((eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) &&
- (eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN))
- goto err_in_read;
- if (is_prot_seq_error(docg3))
- goto err_in_read;
+ doc_dbg("ECC HW_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ hwecc[0], hwecc[1], hwecc[2], hwecc[3], hwecc[4],
+ hwecc[5], hwecc[6]);
+
+ ret = -EIO;
+ if (is_prot_seq_error(docg3))
+ goto err_in_read;
+ ret = 0;
+ if ((block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) &&
+ (eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) &&
+ (eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN) &&
+ (ops->mode != MTD_OPS_RAW) &&
+ (nbdata == DOC_LAYOUT_PAGE_SIZE)) {
+ ret = doc_ecc_bch_fix_data(docg3, buf, hwecc);
+ if (ret < 0) {
+ mtd->ecc_stats.failed++;
+ ret = -EBADMSG;
+ }
+ if (ret > 0) {
+ mtd->ecc_stats.corrected += ret;
+ ret = -EUCLEAN;
+ }
}
doc_read_page_finish(docg3);
@@ -849,7 +913,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
from += DOC_LAYOUT_PAGE_SIZE;
}
- return 0;
+ return ret;
err_in_read:
doc_read_page_finish(docg3);
err:
@@ -1158,7 +1222,7 @@ static int doc_write_page(struct docg3 *docg3, loff_t to, const u_char *buf,
if (ret)
goto err;
- doc_write_page_ecc_init(docg3, DOC_ECC_BCH_COVERED_BYTES);
+ doc_write_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES);
doc_delay(docg3, 2);
doc_write_page_putbytes(docg3, DOC_LAYOUT_PAGE_SIZE, buf);
@@ -1721,7 +1785,11 @@ static int __init docg3_probe(struct platform_device *pdev)
docg3_floors = kzalloc(sizeof(*docg3_floors) * DOC_MAX_NBFLOORS,
GFP_KERNEL);
if (!docg3_floors)
- goto nomem;
+ goto nomem1;
+ docg3_bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T,
+ DOC_ECC_BCH_PRIMPOLY);
+ if (!docg3_bch)
+ goto nomem2;
ret = 0;
for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) {
@@ -1751,10 +1819,13 @@ notfound:
ret = -ENODEV;
dev_info(dev, "No supported DiskOnChip found\n");
err_probe:
+ free_bch(docg3_bch);
for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)
if (docg3_floors[floor])
doc_release_device(docg3_floors[floor]);
-nomem:
+nomem2:
+ kfree(docg3_floors);
+nomem1:
iounmap(base);
noress:
return ret;
@@ -1779,6 +1850,7 @@ static int __exit docg3_release(struct platform_device *pdev)
doc_release_device(docg3_floors[floor]);
kfree(docg3_floors);
+ free_bch(docg3_bch);
iounmap(base);
return 0;
}
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h
index 397e461..33db727 100644
--- a/drivers/mtd/devices/docg3.h
+++ b/drivers/mtd/devices/docg3.h
@@ -51,10 +51,19 @@
#define DOC_LAYOUT_WEAR_OFFSET (DOC_LAYOUT_PAGE_OOB_SIZE * 2)
#define DOC_LAYOUT_BLOCK_SIZE \
(DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_PAGE_SIZE)
+
+/*
+ * ECC related constants
+ */
+#define DOC_ECC_BCH_M 14
+#define DOC_ECC_BCH_T 4
+#define DOC_ECC_BCH_PRIMPOLY 0x4443
#define DOC_ECC_BCH_SIZE 7
#define DOC_ECC_BCH_COVERED_BYTES \
(DOC_LAYOUT_PAGE_SIZE + DOC_LAYOUT_OOB_PAGEINFO_SZ + \
- DOC_LAYOUT_OOB_HAMMING_SZ + DOC_LAYOUT_OOB_BCH_SZ)
+ DOC_LAYOUT_OOB_HAMMING_SZ)
+#define DOC_ECC_BCH_TOTAL_BYTES \
+ (DOC_ECC_BCH_COVERED_BYTES + DOC_LAYOUT_OOB_BCH_SZ)
/*
* Blocks distribution
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 14/16] mtd/docg3: add suspend and resume
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
` (12 preceding siblings ...)
2011-11-10 8:05 ` [PATCH v2 13/16] mtd/docg3: add ECC correction code Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 15/16] mtd/docg3: add fast mode Robert Jarzmik
` (2 subsequent siblings)
16 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
Add functions to powerdown and powerup from suspend, in
order to save power.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
drivers/mtd/devices/docg3.c | 75 ++++++++++++++++++++++++++++++++++++++++++-
drivers/mtd/devices/docg3.h | 6 +++
2 files changed, 80 insertions(+), 1 deletions(-)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 26cc179..d94c759 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -45,7 +45,6 @@
* As no specification is available from M-Systems/Sandisk, this drivers lacks
* several functions available on the chip, as :
* - IPL write
- * - powerdown / powerup
*
* The bus data width (8bits versus 16bits) is not handled (if_cfg flag), and
* the driver assumes a 16bits data bus.
@@ -1756,6 +1755,78 @@ static void doc_release_device(struct mtd_info *mtd)
}
/**
+ * docg3_resume - Awakens docg3 floor
+ * @pdev: platfrom device
+ *
+ * Returns 0 (always successfull)
+ */
+static int docg3_resume(struct platform_device *pdev)
+{
+ int i;
+ struct mtd_info **docg3_floors, *mtd;
+ struct docg3 *docg3;
+
+ docg3_floors = platform_get_drvdata(pdev);
+ mtd = docg3_floors[0];
+ docg3 = mtd->priv;
+
+ doc_dbg("docg3_resume()\n");
+ for (i = 0; i < 12; i++)
+ doc_readb(docg3, DOC_IOSPACE_IPL);
+ return 0;
+}
+
+/**
+ * docg3_suspend - Put in low power mode the docg3 floor
+ * @pdev: platform device
+ * @state: power state
+ *
+ * Shuts off most of docg3 circuitery to lower power consumption.
+ *
+ * Returns 0 if suspend succeeded, -EIO if chip refused suspend
+ */
+static int docg3_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int floor, i;
+ struct mtd_info **docg3_floors, *mtd;
+ struct docg3 *docg3;
+ u8 ctrl, pwr_down;
+
+ docg3_floors = platform_get_drvdata(pdev);
+ for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) {
+ mtd = docg3_floors[floor];
+ if (!mtd)
+ continue;
+ docg3 = mtd->priv;
+
+ doc_writeb(docg3, floor, DOC_DEVICESELECT);
+ ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
+ ctrl &= ~DOC_CTRL_VIOLATION & ~DOC_CTRL_CE;
+ doc_writeb(docg3, ctrl, DOC_FLASHCONTROL);
+
+ for (i = 0; i < 10; i++) {
+ usleep_range(3000, 4000);
+ pwr_down = doc_register_readb(docg3, DOC_POWERMODE);
+ if (pwr_down & DOC_POWERDOWN_READY)
+ break;
+ }
+ if (pwr_down & DOC_POWERDOWN_READY) {
+ doc_dbg("docg3_suspend(): floor %d powerdown ok\n",
+ floor);
+ } else {
+ doc_err("docg3_suspend(): floor %d powerdown failed\n",
+ floor);
+ return -EIO;
+ }
+ }
+
+ mtd = docg3_floors[0];
+ docg3 = mtd->priv;
+ doc_set_asic_mode(docg3, DOC_ASICMODE_POWERDOWN);
+ return 0;
+}
+
+/**
* doc_probe - Probe the IO space for a DiskOnChip G3 chip
* @pdev: platform device
*
@@ -1860,6 +1931,8 @@ static struct platform_driver g3_driver = {
.name = "docg3",
.owner = THIS_MODULE,
},
+ .suspend = docg3_suspend,
+ .resume = docg3_resume,
.remove = __exit_p(docg3_release),
};
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h
index 33db727..cd70b18 100644
--- a/drivers/mtd/devices/docg3.h
+++ b/drivers/mtd/devices/docg3.h
@@ -127,6 +127,7 @@
#define DOC_ASICMODECONFIRM 0x1072
#define DOC_CHIPID_INV 0x1074
+#define DOC_POWERMODE 0x107c
/*
* Flash sequences
@@ -239,6 +240,11 @@
#define DOC_READADDR_ADDR_MASK 0x1fff
/*
+ * Flash register : DOC_POWERMODE
+ */
+#define DOC_POWERDOWN_READY 0x80
+
+/*
* Status of erase and write operation
*/
#define DOC_PLANES_STATUS_FAIL 0x01
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 15/16] mtd/docg3: add fast mode
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
` (13 preceding siblings ...)
2011-11-10 8:05 ` [PATCH v2 14/16] mtd/docg3: add suspend and resume Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 16/16] mtd/docg3: add protection areas sysfs access Robert Jarzmik
2011-11-12 20:02 ` [PATCH v2 00/16] DocG3 fixes and write support Mike Dunn
16 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
Docg3 chips can work in 3 modes : normal MLC mode, fast
mode and reliable mode. Normally, as docg3 is a MLC chip, it
should be configured to work in normal mode.
In both normal mode, each page is distinct. This
means that writing to page 12 of blocks 14,15 writes only to
that page, and reading from page 12 of blocks 14,15 reads
only from that page.
In reliable and fast modes, pages are coupled by pairs, and
are clones one of each other. This means that the available
capacity of the chip is halved. Pages are coupled in each
block, and page of index 2*n contains the same data as page
2*n+1 of the same block.
In fast mode, the reads occur a bit faster, but are a bit
less reliable that in normal mode.
When reading from page 2*n, the chip reads bytes from both
page 2*n and page 2*n+1, makes a logical and for each byte,
and returns the result. As programming a page means
"clearing bits", even if a bit was not cleared on one page
because the flash is worn out, the other page has the bit
cleared, and the result of the "AND" gives a correct result.
When writing to page 2*n, the chip writes data to both page
2*n and page 2*n+1.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
drivers/mtd/devices/docg3.c | 86 +++++++++++++++++++++++++++++++++---------
drivers/mtd/devices/docg3.h | 9 ++++-
2 files changed, 75 insertions(+), 20 deletions(-)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index d94c759..35df377 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -61,6 +61,11 @@
*
*/
+static unsigned int reliable_mode = 0;
+module_param(reliable_mode, uint, 0);
+MODULE_PARM_DESC(reliable_mode, "Set the docg3 mode (0=normal MLC, 1=fast, "
+ "2=reliable) : MLC normal operations are in normal mode");
+
/**
* struct docg3_oobinfo - DiskOnChip G3 OOB layout
* @eccbytes: 8 bytes are used (1 for Hamming ECC, 7 for BCH ECC)
@@ -291,19 +296,41 @@ static void doc_write_data_area(struct docg3 *docg3, const void *buf, int len)
}
/**
- * doc_set_data_mode - Sets the flash to reliable data mode
+ * doc_set_data_mode - Sets the flash to normal or reliable data mode
* @docg3: the device
*
* The reliable data mode is a bit slower than the fast mode, but less errors
* occur. Entering the reliable mode cannot be done without entering the fast
* mode first.
+ *
+ * In reliable mode, pages 2*n and 2*n+1 are clones. Writing to page 0 of blocks
+ * (4,5) make the hardware write also to page 1 of blocks blocks(4,5). Reading
+ * from page 0 of blocks (4,5) or from page 1 of blocks (4,5) gives the same
+ * result, which is a logical and between bytes from page 0 and page 1 (which is
+ * consistent with the fact that writing to a page is _clearing_ bits of that
+ * page).
*/
static void doc_set_reliable_mode(struct docg3 *docg3)
{
- doc_dbg("doc_set_reliable_mode()\n");
- doc_flash_sequence(docg3, DOC_SEQ_SET_MODE);
- doc_flash_command(docg3, DOC_CMD_FAST_MODE);
- doc_flash_command(docg3, DOC_CMD_RELIABLE_MODE);
+ static char *strmode[] = { "normal", "fast", "reliable", "invalid" };
+
+ doc_dbg("doc_set_reliable_mode(%s)\n", strmode[docg3->reliable]);
+ switch (docg3->reliable) {
+ case 0:
+ break;
+ case 1:
+ doc_flash_sequence(docg3, DOC_SEQ_SET_FASTMODE);
+ doc_flash_command(docg3, DOC_CMD_FAST_MODE);
+ break;
+ case 2:
+ doc_flash_sequence(docg3, DOC_SEQ_SET_RELIABLEMODE);
+ doc_flash_command(docg3, DOC_CMD_FAST_MODE);
+ doc_flash_command(docg3, DOC_CMD_RELIABLE_MODE);
+ break;
+ default:
+ doc_err("doc_set_reliable_mode(): invalid mode\n");
+ break;
+ }
doc_delay(docg3, 2);
}
@@ -778,18 +805,29 @@ static void doc_read_page_finish(struct docg3 *docg3)
* @block1: second plane block index calculated
* @page: page calculated
* @ofs: offset in page
+ * @reliable: 0 if docg3 in normal mode, 1 if docg3 in fast mode, 2 if docg3 in
+ * reliable mode.
+ *
+ * The calculation is based on the reliable/normal mode. In normal mode, the 64
+ * pages of a block are available. In reliable mode, as pages 2*n and 2*n+1 are
+ * clones, only 32 pages per block are available.
*/
static void calc_block_sector(loff_t from, int *block0, int *block1, int *page,
- int *ofs)
+ int *ofs, int reliable)
{
- uint sector;
+ uint sector, pages_biblock;
+
+ pages_biblock = DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES;
+ if (reliable == 1 || reliable == 2)
+ pages_biblock /= 2;
sector = from / DOC_LAYOUT_PAGE_SIZE;
- *block0 = sector / (DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES)
- * DOC_LAYOUT_NBPLANES;
+ *block0 = sector / pages_biblock * DOC_LAYOUT_NBPLANES;
*block1 = *block0 + 1;
- *page = sector % (DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES);
+ *page = sector % pages_biblock;
*page /= DOC_LAYOUT_NBPLANES;
+ if (reliable == 1 || reliable == 2)
+ *page *= 2;
if (sector % 2)
*ofs = DOC_LAYOUT_PAGE_OOB_SIZE;
else
@@ -836,7 +874,8 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
return -EINVAL;
ret = -EINVAL;
- calc_block_sector(from + len, &block0, &block1, &page, &ofs);
+ calc_block_sector(from + len, &block0, &block1, &page, &ofs,
+ docg3->reliable);
if (block1 > docg3->max_block)
goto err;
@@ -844,7 +883,8 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
ops->retlen = 0;
ret = 0;
while (!ret && (len > 0 || ooblen > 0)) {
- calc_block_sector(from, &block0, &block1, &page, &ofs);
+ calc_block_sector(from, &block0, &block1, &page, &ofs,
+ docg3->reliable);
nbdata = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE);
nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE);
ret = doc_read_page_prepare(docg3, block0, block1, page, ofs);
@@ -983,7 +1023,8 @@ static int doc_block_isbad(struct mtd_info *mtd, loff_t from)
struct docg3 *docg3 = mtd->priv;
int block0, block1, page, ofs, is_good;
- calc_block_sector(from, &block0, &block1, &page, &ofs);
+ calc_block_sector(from, &block0, &block1, &page, &ofs,
+ docg3->reliable);
doc_dbg("doc_block_isbad(from=%lld) => block=(%d,%d), page=%d, ofs=%d\n",
from, block0, block1, page, ofs);
@@ -1015,7 +1056,7 @@ static int doc_get_erase_count(struct docg3 *docg3, loff_t from)
doc_dbg("doc_get_erase_count(from=%lld, buf=%p)\n", from, buf);
if (from % DOC_LAYOUT_PAGE_SIZE)
return -EINVAL;
- calc_block_sector(from, &block0, &block1, &page, &ofs);
+ calc_block_sector(from, &block0, &block1, &page, &ofs, docg3->reliable);
if (block1 > docg3->max_block)
return -EINVAL;
@@ -1156,14 +1197,15 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info)
doc_set_device_id(docg3, docg3->device_id);
info->state = MTD_ERASE_PENDING;
- calc_block_sector(info->addr + info->len,
- &block0, &block1, &page, &ofs);
+ calc_block_sector(info->addr + info->len, &block0, &block1, &page,
+ &ofs, docg3->reliable);
ret = -EINVAL;
if (block1 > docg3->max_block || page || ofs)
goto reset_err;
ret = 0;
- calc_block_sector(info->addr, &block0, &block1, &page, &ofs);
+ calc_block_sector(info->addr, &block0, &block1, &page, &ofs,
+ docg3->reliable);
doc_set_reliable_mode(docg3);
for (len = info->len; !ret && len > 0; len -= mtd->erasesize) {
info->state = MTD_ERASING;
@@ -1209,7 +1251,7 @@ static int doc_write_page(struct docg3 *docg3, loff_t to, const u_char *buf,
u8 syn[DOC_ECC_BCH_SIZE], hamming;
doc_dbg("doc_write_page(to=%lld)\n", to);
- calc_block_sector(to, &block0, &block1, &page, &ofs);
+ calc_block_sector(to, &block0, &block1, &page, &ofs, docg3->reliable);
doc_set_device_id(docg3, docg3->device_id);
ret = doc_reset_seq(docg3);
@@ -1396,7 +1438,8 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
return -EINVAL;
ret = -EINVAL;
- calc_block_sector(ofs + len, &block0, &block1, &page, &pofs);
+ calc_block_sector(ofs + len, &block0, &block1, &page, &pofs,
+ docg3->reliable);
if (block1 > docg3->max_block)
goto err;
@@ -1638,6 +1681,7 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
cfg = doc_register_readb(docg3, DOC_CONFIGURATION);
docg3->if_cfg = (cfg & DOC_CONF_IF_CFG ? 1 : 0);
+ docg3->reliable = reliable_mode;
switch (chip_id) {
case DOC_CHIPID_G3:
@@ -1649,7 +1693,11 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
mtd->size = (docg3->max_block + 1) * DOC_LAYOUT_BLOCK_SIZE;
+ if (docg3->reliable == 2)
+ mtd->size /= 2;
mtd->erasesize = DOC_LAYOUT_BLOCK_SIZE * DOC_LAYOUT_NBPLANES;
+ if (docg3->reliable == 2)
+ mtd->erasesize /= 2;
mtd->writesize = DOC_LAYOUT_PAGE_SIZE;
mtd->oobsize = DOC_LAYOUT_OOB_SIZE;
mtd->owner = THIS_MODULE;
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h
index cd70b18..07182f9 100644
--- a/drivers/mtd/devices/docg3.h
+++ b/drivers/mtd/devices/docg3.h
@@ -135,7 +135,8 @@
*/
#define DOC_SEQ_RESET 0x00
#define DOC_SEQ_PAGE_SIZE_532 0x03
-#define DOC_SEQ_SET_MODE 0x09
+#define DOC_SEQ_SET_FASTMODE 0x05
+#define DOC_SEQ_SET_RELIABLEMODE 0x09
#define DOC_SEQ_READ 0x12
#define DOC_SEQ_SET_PLANE1 0x0e
#define DOC_SEQ_SET_PLANE2 0x10
@@ -257,6 +258,11 @@
* @base: mapped IO space
* @device_id: number of the cascaded DoCG3 device (0, 1, 2 or 3)
* @if_cfg: if true, reads are on 16bits, else reads are on 8bits
+
+ * @reliable: if 0, docg3 in normal mode, if 1 docg3 in fast mode, if 2 in
+ * reliable mode
+ * Fast mode implies more errors than normal mode.
+ * Reliable mode implies that page 2*n and 2*n+1 are clones.
* @bbt: bad block table cache
* @oob_write_ofs: offset of the MTD where this OOB should belong (ie. in next
* page_write)
@@ -270,6 +276,7 @@ struct docg3 {
void __iomem *base;
unsigned int device_id:4;
unsigned int if_cfg:1;
+ unsigned int reliable:2;
int max_block;
u8 *bbt;
loff_t oob_write_ofs;
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v2 16/16] mtd/docg3: add protection areas sysfs access
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
` (14 preceding siblings ...)
2011-11-10 8:05 ` [PATCH v2 15/16] mtd/docg3: add fast mode Robert Jarzmik
@ 2011-11-10 8:05 ` Robert Jarzmik
2011-11-12 20:02 ` [PATCH v2 00/16] DocG3 fixes and write support Mike Dunn
16 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-10 8:05 UTC (permalink / raw)
To: dwmw2, dedekind1, mikedunn; +Cc: Robert Jarzmik, linux-mtd, linux-kernel
As each docg3 chip has 2 protection areas (DPS0 and DPS1),
and because theses areas can prevent user access to the chip
data, add for each floor the sysfs entries which insert the
protection key into the right DPS.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
drivers/mtd/devices/docg3.c | 121 +++++++++++++++++++++++++++++++++++++++++++
drivers/mtd/devices/docg3.h | 13 +++++
2 files changed, 134 insertions(+), 0 deletions(-)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 35df377..d7df311 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -1513,6 +1513,123 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
return ret;
}
+static struct docg3 *sysfs_dev2docg3(struct device *dev,
+ struct device_attribute *attr)
+{
+ int floor;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mtd_info **docg3_floors = platform_get_drvdata(pdev);
+
+ floor = attr->attr.name[1] - '0';
+ if (floor < 0 || floor >= DOC_MAX_NBFLOORS)
+ return NULL;
+ else
+ return docg3_floors[floor]->priv;
+}
+
+static ssize_t dps0_is_key_locked(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct docg3 *docg3 = sysfs_dev2docg3(dev, attr);
+ int dps0;
+
+ doc_set_device_id(docg3, docg3->device_id);
+ dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS);
+ doc_set_device_id(docg3, 0);
+
+ return sprintf(buf, "%d\n", !(dps0 & DOC_DPS_KEY_OK));
+}
+
+static ssize_t dps1_is_key_locked(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct docg3 *docg3 = sysfs_dev2docg3(dev, attr);
+ int dps1;
+
+ doc_set_device_id(docg3, docg3->device_id);
+ dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS);
+ doc_set_device_id(docg3, 0);
+
+ return sprintf(buf, "%d\n", !(dps1 & DOC_DPS_KEY_OK));
+}
+
+static ssize_t dps0_insert_key(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct docg3 *docg3 = sysfs_dev2docg3(dev, attr);
+ int i;
+
+ if (count != DOC_LAYOUT_DPS_KEY_LENGTH)
+ return -EINVAL;
+
+ doc_set_device_id(docg3, docg3->device_id);
+ for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++)
+ doc_writeb(docg3, buf[i], DOC_DPS0_KEY);
+ doc_set_device_id(docg3, 0);
+ return count;
+}
+
+static ssize_t dps1_insert_key(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct docg3 *docg3 = sysfs_dev2docg3(dev, attr);
+ int i;
+
+ if (count != DOC_LAYOUT_DPS_KEY_LENGTH)
+ return -EINVAL;
+
+ doc_set_device_id(docg3, docg3->device_id);
+ for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++)
+ doc_writeb(docg3, buf[i], DOC_DPS1_KEY);
+ doc_set_device_id(docg3, 0);
+ return count;
+}
+
+#define FLOOR_SYSFS(id) { \
+ __ATTR(f##id##_dps0_is_keylocked, S_IRUGO, dps0_is_key_locked, NULL), \
+ __ATTR(f##id##_dps1_is_keylocked, S_IRUGO, dps1_is_key_locked, NULL), \
+ __ATTR(f##id##_dps0_protection_key, S_IWUGO, NULL, dps0_insert_key), \
+ __ATTR(f##id##_dps1_protection_key, S_IWUGO, NULL, dps1_insert_key), \
+}
+
+static struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = {
+ FLOOR_SYSFS(0), FLOOR_SYSFS(1), FLOOR_SYSFS(2), FLOOR_SYSFS(3)
+};
+
+static int doc_register_sysfs(struct platform_device *pdev,
+ struct mtd_info **floors)
+{
+ int ret = 0, floor, i = 0;
+ struct device *dev = &pdev->dev;
+
+ for (floor = 0; !ret && floor < DOC_MAX_NBFLOORS && floors[floor];
+ floor++)
+ for (i = 0; !ret && i < 4; i++)
+ ret = device_create_file(dev, &doc_sys_attrs[floor][i]);
+ if (!ret)
+ return 0;
+ do {
+ while (--i >= 0)
+ device_remove_file(dev, &doc_sys_attrs[floor][i]);
+ i = 4;
+ } while (--floor >= 0);
+ return ret;
+}
+
+static void doc_unregister_sysfs(struct platform_device *pdev,
+ struct mtd_info **floors)
+{
+ struct device *dev = &pdev->dev;
+ int floor, i;
+
+ for (floor = 0; floor < DOC_MAX_NBFLOORS && floors[floor];
+ floor++)
+ for (i = 0; i < 4; i++)
+ device_remove_file(dev, &doc_sys_attrs[floor][i]);
+}
+
/*
* Debug sysfs entries
*/
@@ -1927,6 +2044,9 @@ static int __init docg3_probe(struct platform_device *pdev)
found++;
}
+ ret = doc_register_sysfs(pdev, docg3_floors);
+ if (ret)
+ goto err_probe;
if (!found)
goto notfound;
@@ -1963,6 +2083,7 @@ static int __exit docg3_release(struct platform_device *pdev)
void __iomem *base = docg3->base;
int floor;
+ doc_unregister_sysfs(pdev, docg3_floors);
doc_dbg_unregister(docg3);
for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)
if (docg3_floors[floor])
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h
index 07182f9..a349915 100644
--- a/drivers/mtd/devices/docg3.h
+++ b/drivers/mtd/devices/docg3.h
@@ -118,6 +118,8 @@
#define DOC_BCH_SYNDROM(idx) (0x1048 + (idx << 0))
#define DOC_PROTECTION 0x1056
+#define DOC_DPS0_KEY 0x105c
+#define DOC_DPS1_KEY 0x105e
#define DOC_DPS0_ADDRLOW 0x1060
#define DOC_DPS0_ADDRHIGH 0x1062
#define DOC_DPS1_ADDRLOW 0x1064
@@ -252,6 +254,17 @@
#define DOC_PLANES_STATUS_PLANE0_KO 0x02
#define DOC_PLANES_STATUS_PLANE1_KO 0x04
+/*
+ * DPS key management
+ *
+ * Each floor of docg3 has 2 protection areas: DPS0 and DPS1. These areas span
+ * across block boundaries, and define whether these blocks can be read or
+ * written.
+ * The definition is dynamically stored in page 0 of blocks (2,3) for DPS0, and
+ * page 0 of blocks (4,5) for DPS1.
+ */
+#define DOC_LAYOUT_DPS_KEY_LENGTH 8
+
/**
* struct docg3 - DiskOnChip driver private data
* @dev: the device currently under control
--
1.7.5.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* Re: [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo
2011-11-10 8:05 ` [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo Robert Jarzmik
@ 2011-11-12 19:39 ` Mike Dunn
2011-11-13 10:18 ` Robert Jarzmik
0 siblings, 1 reply; 35+ messages in thread
From: Mike Dunn @ 2011-11-12 19:39 UTC (permalink / raw)
To: Robert Jarzmik; +Cc: linux-mtd, dwmw2, linux-kernel, dedekind1
On 11/10/2011 12:05 AM, Robert Jarzmik wrote:
> +static struct nand_ecclayout docg3_oobinfo = {
> + .eccbytes = 8,
> + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14},
> + .oobfree = {{0, 7}, {15, 1} },
> + .oobavail = 8,
> +};
> +
Just FYI, per Ivan's suggestion, I changed this to use the last oob byte as a
"page programmed" flag for the purpose of detecting bit flips when reading a
blank page. Maybe something to keep in mind. You can have a look at the latest
G4 driver patch to see exactly how I use it.
Mike
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 04/16] mtd/docg3: fix BCH registers
2011-11-10 8:05 ` [PATCH v2 04/16] mtd/docg3: fix BCH registers Robert Jarzmik
@ 2011-11-12 19:40 ` Mike Dunn
2011-11-13 10:20 ` Robert Jarzmik
0 siblings, 1 reply; 35+ messages in thread
From: Mike Dunn @ 2011-11-12 19:40 UTC (permalink / raw)
To: Robert Jarzmik; +Cc: linux-mtd, dwmw2, linux-kernel, dedekind1
On 11/10/2011 12:05 AM, Robert Jarzmik wrote:
> -#define DOC_BCH_SYNDROM(idx) (0x1048 + (idx << 1))
> +#define DOC_BCH_SYNDROM(idx) (0x1048 + (idx << 0))
Minor nit: why the 0, and not just (0x1048 + idx) ?
Mike
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 13/16] mtd/docg3: add ECC correction code
2011-11-10 8:05 ` [PATCH v2 13/16] mtd/docg3: add ECC correction code Robert Jarzmik
@ 2011-11-12 19:49 ` Mike Dunn
2011-11-13 10:35 ` Robert Jarzmik
0 siblings, 1 reply; 35+ messages in thread
From: Mike Dunn @ 2011-11-12 19:49 UTC (permalink / raw)
To: Robert Jarzmik; +Cc: linux-mtd, dwmw2, linux-kernel, dedekind1
On 11/10/2011 12:05 AM, Robert Jarzmik wrote:
>
> +if MTD_DOCG3
> +config BCH_CONST_M
> + default 14
> +config BCH_CONST_T
> + default 4
> +endif
It might be better to let the user set this in the kernel config. Doing it here
precludes the use of the algorithm by any other module that needs to use it with
different parameters.
>
> /**
> + * doc_correct_data - Fix if need be read data from flash
> + * @docg3: the device
> + * @buf: the buffer of read data (512 + 7 + 1 bytes)
> + * @hwecc: the hardware calculated ECC.
> + * It's in fact recv_ecc ^ calc_ecc, where recv_ecc was read from OOB
> + * area data, and calc_ecc the ECC calculated by the hardware generator.
> + *
> + * Checks if the received data matches the ECC, and if an error is detected,
> + * tries to fix the bit flips (at most 4) in the buffer buf. As the docg3
> + * understands the (data, ecc, syndroms) in an inverted order in comparison to
> + * the BCH library, the function reverses the order of bits (ie. bit7 and bit0,
> + * bit6 and bit 1, ...) for all ECC data.
> + *
> + * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch
> + * algorithm is used to decode this. However the hw operates on page
> + * data in a bit order that is the reverse of that of the bch alg,
> + * requiring that the bits be reversed on the result. Thanks to Ivan
> + * Djelic for his analysis.
> + *
> + * Returns number of fixed bits (0, 1, 2, 3, 4) or -EBADMSG if too many bit
> + * errors were detected and cannot be fixed.
> + */
> +static int doc_ecc_bch_fix_data(struct docg3 *docg3, void *buf, u8 *hwecc)
Nit: function name in comment is inconsistent with its actual name.
> +{
> + u8 ecc[DOC_ECC_BCH_SIZE];
> + int errorpos[DOC_ECC_BCH_T], i, numerrs;
> +
> + for (i = 0; i < DOC_ECC_BCH_SIZE; i++)
> + ecc[i] = bitrev8(hwecc[i]);
> + numerrs = decode_bch(docg3_bch, NULL, DOC_ECC_BCH_COVERED_BYTES,
> + NULL, ecc, NULL, errorpos);
> + BUG_ON(numerrs == -EINVAL);
> + if (numerrs < 0)
> + goto out;
> +
> + for (i = 0; i < numerrs; i++)
> + errorpos[i] = (errorpos[i] & ~7) | (7 - (errorpos[i] & 7));
There's that unexplained cryptic step again :-)
> + for (i = 0; i < numerrs; i++)
> + if (errorpos[i] < DOC_ECC_BCH_COVERED_BYTES*8)
> + /* error is located in data, correct it */
> + change_bit(errorpos[i], buf);
> +out:
> + doc_dbg("doc_ecc_bch_fix_data: flipped %d bits\n", numerrs);
> + return numerrs;
> +}
Where do you check for reads of a blank page? When an erased page is read,
uncorrectible ecc errors will occur (at least). You can compare the bytes read
from the hw ecc to those generated when a bank page is read to determine if the
page is indeed blank. At Ivan's suggestion, I went a step further and used oob
byte 15 as a "programmed flag", which is used to determine if a page has been
written or not. This is then used as a secondary check for a blank page read,
which will avoid the situation where a blank page is read but not detected
because a genuine bit flip occurred when reading a blank page. In that case the
hw ecc will not generate the usual blank page values. (You can have a look at
correct_data() in the latest G4 driver patch to see what I'm talking about.)
> +
> +
> +/**
> * doc_read_page_prepare - Prepares reading data from a flash page
> * @docg3: the device
> * @block0: the first plane block index on flash memory
> @@ -762,7 +816,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
> u8 *oobbuf = ops->oobbuf;
> u8 *buf = ops->datbuf;
> size_t len, ooblen, nbdata, nboob;
> - u8 calc_ecc[DOC_ECC_BCH_SIZE], eccconf1;
> + u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1;
>
> if (buf)
> len = ops->len;
> @@ -797,7 +851,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
> ret = doc_read_page_prepare(docg3, block0, block1, page, ofs);
> if (ret < 0)
> goto err;
> - ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_COVERED_BYTES);
> + ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES);
Not specifically related to this patch, but... are you sure you want to
initialize the ecc on every read? I'm sure it's not necessary; you can just
leave it on; maybe turn it off if doing raw reads. I know this is the case for
both the P3 and G4 when running under PalmOS / TrueFFS library. I notice that
this function has delays and polls the status register in between calls to
cpu_relax(), so the performance hit is probably not insignificant, especiallu
when done for every 512 byte page.
> if (ret < 0)
> goto err_in_read;
> ret = doc_read_page_getbytes(docg3, nbdata, buf, 1);
> @@ -811,7 +865,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
> doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob,
> NULL, 0);
>
> - doc_get_hw_bch_syndroms(docg3, calc_ecc);
> + doc_get_hw_bch_syndroms(docg3, hwecc);
Another nit (also not specifically related to this patch): bad name for this
function. The ecc being read is not the BCH syndrome, as we now know. This is
a pet peeve of mine; M-sys abused that word by misapplying it to the byts read
from the ecc hw, which confused the hell out of me as I tried to understand what
the hw was generating.
Otherwise, looks correct. And if it's passing nandtest we know it works!
Mike
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 00/16] DocG3 fixes and write support
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
` (15 preceding siblings ...)
2011-11-10 8:05 ` [PATCH v2 16/16] mtd/docg3: add protection areas sysfs access Robert Jarzmik
@ 2011-11-12 20:02 ` Mike Dunn
2011-11-13 10:41 ` Robert Jarzmik
16 siblings, 1 reply; 35+ messages in thread
From: Mike Dunn @ 2011-11-12 20:02 UTC (permalink / raw)
To: Robert Jarzmik; +Cc: linux-mtd, dwmw2, linux-kernel, dedekind1
On 11/10/2011 12:05 AM, Robert Jarzmik wrote:
> nandtest/nanddump/nandwrite testing has been
> done, with and without bitflipped data.
Yay!
> Moreover, 3 new patches have been added:
> - fix reading oob+data without correction
> This was triggered by the bitflipped pages tests, and
> fixes cases where we want to read corrupted data.
> - add docg3 modes support
> This actually fixes the read part, as we now read in
> "normal" mode by default rather than in "reliable"
> mode. This is mandatory for nandtest to work, because of
> the "even/odd pages logical AND".
> - add protection support
> This is an evolution to be able to access the whole chip
> in protected chips, as they are in smartphones.
How did you figure out these operating modes and the protected part stuff? From
reverse-engineering a disassembled binary? I'm impressed. You don't get that
level of detail from just monitoring cpu accesses to the device during normal
operation.
BTW, I'm coming around to your thinking that the nand interface is not
appropriate for these chips. Even though they are nand, the lack of any
standard nand interface means the nand base does not do much for you except
obfuscate. Memory based bbt maintenance is handled in nand base, maybe a couple
other minor things. I hope I change my mind; otherwise I'll have to rework the
G4 driver :-(
This would also eliminate my main argument for combining G3/G4 drivers: to keep
the nand interface code consistent. That and more device differences seen in
your latest patches argue against combining. I still strongly suspect that the
G3 is very similiar to the P3 in my Treo 650. At some point down the road I'll
test it out on the P3. Device capacity might be the only difference between the
two devices. If so, the G3 driver might even work on the P3 right out of the box.
Thanks,
Mike
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo
2011-11-12 19:39 ` Mike Dunn
@ 2011-11-13 10:18 ` Robert Jarzmik
2011-11-13 12:53 ` Artem Bityutskiy
2011-11-14 0:58 ` [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo Mike Dunn
0 siblings, 2 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-13 10:18 UTC (permalink / raw)
To: Mike Dunn; +Cc: linux-mtd, dwmw2, linux-kernel, dedekind1
Mike Dunn <mikedunn@newsguy.com> writes:
> On 11/10/2011 12:05 AM, Robert Jarzmik wrote:
>> +static struct nand_ecclayout docg3_oobinfo = {
>> + .eccbytes = 8,
>> + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14},
>> + .oobfree = {{0, 7}, {15, 1} },
>> + .oobavail = 8,
>> +};
>> +
I took a different approach. I check an internal docg3 register to see if the
page was written. Or I could had have checked the Hamming code, as I don't think
it can be 0xff whatever the pagesize 7 bytes values.
The reason behind is that the Hamming code is Ham(64, 57), ie. Ham(2^6,
2^6-6-1). The means the 6 bits are enough to cover all codewords possibilities,
and 0xff is not one of them.
So unless a bitflip in Hamming code, 0xff in it means blank page. And I think
the ECC engine is even smarter, with the ECCCONF1_PAGE_IS_WRITTEN.
>
> Just FYI, per Ivan's suggestion, I changed this to use the last oob byte as a
> "page programmed" flag for the purpose of detecting bit flips when reading a
> blank page. Maybe something to keep in mind. You can have a look at the latest
> G4 driver patch to see exactly how I use it.
I personally think this should be provided by the MTD API. A function
is_page_blank(ofs) could tell if the page was written or not. Now if the
function is NULL, the upper layer (UBIFS, ...) could decide _by itself_ to
assign a free OOB byte to that meaning. But IMHO it's not the drivers duty to
take these decisions to restrict the OOB, let it be done at an upper level.
Cheers.
--
Robert
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 04/16] mtd/docg3: fix BCH registers
2011-11-12 19:40 ` Mike Dunn
@ 2011-11-13 10:20 ` Robert Jarzmik
0 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-13 10:20 UTC (permalink / raw)
To: Mike Dunn; +Cc: linux-mtd, dwmw2, linux-kernel, dedekind1
Mike Dunn <mikedunn@newsguy.com> writes:
> On 11/10/2011 12:05 AM, Robert Jarzmik wrote:
>> -#define DOC_BCH_SYNDROM(idx) (0x1048 + (idx << 1))
>> +#define DOC_BCH_SYNDROM(idx) (0x1048 + (idx << 0))
>
>
> Minor nit: why the 0, and not just (0x1048 + idx) ?
I'll fix that, old idea I had that address bus did not stand odd adresses, and I
couldn't convince myself the "<< 0" was right, so left it to have back quicky
"<< 1". But I was wrong :)
Cheers.
--
Robert
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 13/16] mtd/docg3: add ECC correction code
2011-11-12 19:49 ` Mike Dunn
@ 2011-11-13 10:35 ` Robert Jarzmik
2011-11-14 2:13 ` Mike Dunn
0 siblings, 1 reply; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-13 10:35 UTC (permalink / raw)
To: Mike Dunn; +Cc: linux-mtd, dwmw2, linux-kernel, dedekind1
Mike Dunn <mikedunn@newsguy.com> writes:
> On 11/10/2011 12:05 AM, Robert Jarzmik wrote:
>>
>> +if MTD_DOCG3
>> +config BCH_CONST_M
>> + default 14
>> +config BCH_CONST_T
>> + default 4
>> +endif
>
>
> It might be better to let the user set this in the kernel config. Doing it here
> precludes the use of the algorithm by any other module that needs to use it with
> different parameters.
You're right.
I'll shift that to mioa701 board code, where I'm sure no other BCH is necessary.
>> +static int doc_ecc_bch_fix_data(struct docg3 *docg3, void *buf, u8 *hwecc)
> Nit: function name in comment is inconsistent with its actual name.
Good catch. Will fix.
>> + for (i = 0; i < numerrs; i++)
>> + if (errorpos[i] < DOC_ECC_BCH_COVERED_BYTES*8)
>> + /* error is located in data, correct it */
>> + change_bit(errorpos[i], buf);
>> +out:
>> + doc_dbg("doc_ecc_bch_fix_data: flipped %d bits\n", numerrs);
>> + return numerrs;
>> +}
>
>
> Where do you check for reads of a blank page?
On stack frame above. Look at doc_read_oob():
if ((block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) &&
(eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) &&
(eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN) &&
\---> this is the key
(ops->mode != MTD_OPS_RAW) &&
(nbdata == DOC_LAYOUT_PAGE_SIZE)) {
ret = doc_ecc_bch_fix_data(docg3, buf, hwecc);
Here you see that I'll make the error correction only if the page was written
before. If it's blank, I continue reading without attempting ECC correction.
> Not specifically related to this patch, but... are you sure you want to
> initialize the ecc on every read? I'm sure it's not necessary; you can just
> leave it on; maybe turn it off if doing raw reads. I know this is the case for
> both the P3 and G4 when running under PalmOS / TrueFFS library. I notice that
> this function has delays and polls the status register in between calls to
> cpu_relax(), so the performance hit is probably not insignificant, especiallu
> when done for every 512 byte page.
Well, that's some info.
And yes, it adds some latency.
Now for the necessity, I'm not fully convinced. I know that the ECC register is
set up differently for reads and writes (that's the
DOC_ECCCONF0_READ_MODE). When doc_read_oob() is called, I don't know if the
previous action was a read or a write ...
What I could do to improve performance would be to store in the docg3 private
data if last action was a read or a write, and perform the doc_*_page_ecc_init()
only if action changes. What do you think ?
> Another nit (also not specifically related to this patch): bad name for this
> function. The ecc being read is not the BCH syndrome, as we now know. This is
> a pet peeve of mine; M-sys abused that word by misapplying it to the byts read
> from the ecc hw, which confused the hell out of me as I tried to understand what
> the hw was generating.
Yes, I'll change the name to hw_ecc or something like that.
Thanks for the review.
--
Robert
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 00/16] DocG3 fixes and write support
2011-11-12 20:02 ` [PATCH v2 00/16] DocG3 fixes and write support Mike Dunn
@ 2011-11-13 10:41 ` Robert Jarzmik
0 siblings, 0 replies; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-13 10:41 UTC (permalink / raw)
To: Mike Dunn; +Cc: linux-mtd, dwmw2, linux-kernel, dedekind1
Mike Dunn <mikedunn@newsguy.com> writes:
> How did you figure out these operating modes and the protected part stuff? From
> reverse-engineering a disassembled binary? I'm impressed. You don't get that
> level of detail from just monitoring cpu accesses to the device during normal
> operation.
Retro-engineering and the nandwrite tests.
You see nandtest did not work. I could see why, and made some additionnal
tests. The result was that writting to page 0 and page 1 gave back the AND of
these both pages.
And from there, I checked what was the difference between DOCG3 IPL and SPL, and
landed on the "mode" register.
> BTW, I'm coming around to your thinking that the nand interface is not
> appropriate for these chips. Even though they are nand, the lack of any
> standard nand interface means the nand base does not do much for you except
> obfuscate. Memory based bbt maintenance is handled in nand base, maybe a couple
> other minor things. I hope I change my mind; otherwise I'll have to rework the
> G4 driver :-(
I don't know for G4, but I'm more convinced for G3 as well, as NAND interface
provides some state machine in the chip (where the last seek occured, ...).
> I still strongly suspect that the G3 is very similiar to the P3 in my Treo
> 650. At some point down the road I'll test it out on the P3. Device capacity
> might be the only difference between the two devices. If so, the G3 driver
> might even work on the P3 right out of the box.
And if not right out of the box, I'll bet on :
- adding a DOC_CHIPID_P3 ...
- and if the chip is bigger, amend doc_setup_addr_sector(0 and
doc_setup_writeaddr_sector() to input another byte of address (ie. add a line
with doc_flash_address(docg3, (sector >> 24) & 0xff).
Cheers.
--
Robert
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo
2011-11-13 10:18 ` Robert Jarzmik
@ 2011-11-13 12:53 ` Artem Bityutskiy
2011-11-13 13:03 ` David Woodhouse
2011-11-14 0:58 ` [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo Mike Dunn
1 sibling, 1 reply; 35+ messages in thread
From: Artem Bityutskiy @ 2011-11-13 12:53 UTC (permalink / raw)
To: Robert Jarzmik; +Cc: dwmw2, Mike Dunn, linux-kernel, linux-mtd
On Sun, 2011-11-13 at 11:18 +0100, Robert Jarzmik wrote:
> I personally think this should be provided by the MTD API. A function
> is_page_blank(ofs) could tell if the page was written or not. Now if the
> function is NULL, the upper layer (UBIFS, ...) could decide _by itself_ to
> assign a free OOB byte to that meaning. But IMHO it's not the drivers duty to
> take these decisions to restrict the OOB, let it be done at an upper level.
Probably it is a good idea to introduce an mtd_is_page_blank. But it
should either work for all flashes or not introduced at all. I do not
think upper layers should use OOB at all. And this interface should also
work for NOR flash. Probably it should just fall-back to comparing the
data with 0xFF if the driver cannot offer anything special.
Artem.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo
2011-11-13 12:53 ` Artem Bityutskiy
@ 2011-11-13 13:03 ` David Woodhouse
2011-11-13 13:35 ` Artem Bityutskiy
0 siblings, 1 reply; 35+ messages in thread
From: David Woodhouse @ 2011-11-13 13:03 UTC (permalink / raw)
To: dedekind1; +Cc: Mike Dunn, Robert Jarzmik, linux-kernel, linux-mtd
[-- Attachment #1: Type: text/plain, Size: 1331 bytes --]
On Sun, 2011-11-13 at 14:53 +0200, Artem Bityutskiy wrote:
> On Sun, 2011-11-13 at 11:18 +0100, Robert Jarzmik wrote:
> > I personally think this should be provided by the MTD API. A function
> > is_page_blank(ofs) could tell if the page was written or not. Now if the
> > function is NULL, the upper layer (UBIFS, ...) could decide _by itself_ to
> > assign a free OOB byte to that meaning. But IMHO it's not the drivers duty to
> > take these decisions to restrict the OOB, let it be done at an upper level.
>
> Probably it is a good idea to introduce an mtd_is_page_blank. But it
> should either work for all flashes or not introduced at all. I do not
> think upper layers should use OOB at all. And this interface should also
> work for NOR flash. Probably it should just fall-back to comparing the
> data with 0xFF if the driver cannot offer anything special.
We *tried* comparing with 0xFF in JFFS2 and found that it wasn't good
enough (it could be *mostly* erased before a powerfail but not
completely, so as soon as you start to program it you get a lot of
bitflips). Hence the cleanmarkers.
The only way you can treat a page as erased is if you *know* it was
successfully erased. So I'd be reluctant to introduce a function that
encourages people to draw inferences they shouldn't.
--
dwmw2
[-- Attachment #2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 5818 bytes --]
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo
2011-11-13 13:03 ` David Woodhouse
@ 2011-11-13 13:35 ` Artem Bityutskiy
2011-11-13 16:38 ` Robert Jarzmik
0 siblings, 1 reply; 35+ messages in thread
From: Artem Bityutskiy @ 2011-11-13 13:35 UTC (permalink / raw)
To: David Woodhouse; +Cc: Mike Dunn, Robert Jarzmik, linux-kernel, linux-mtd
On Sun, 2011-11-13 at 13:03 +0000, David Woodhouse wrote:
> On Sun, 2011-11-13 at 14:53 +0200, Artem Bityutskiy wrote:
> > On Sun, 2011-11-13 at 11:18 +0100, Robert Jarzmik wrote:
> > > I personally think this should be provided by the MTD API. A function
> > > is_page_blank(ofs) could tell if the page was written or not. Now if the
> > > function is NULL, the upper layer (UBIFS, ...) could decide _by itself_ to
> > > assign a free OOB byte to that meaning. But IMHO it's not the drivers duty to
> > > take these decisions to restrict the OOB, let it be done at an upper level.
> >
> > Probably it is a good idea to introduce an mtd_is_page_blank. But it
> > should either work for all flashes or not introduced at all. I do not
> > think upper layers should use OOB at all. And this interface should also
> > work for NOR flash. Probably it should just fall-back to comparing the
> > data with 0xFF if the driver cannot offer anything special.
>
> We *tried* comparing with 0xFF in JFFS2 and found that it wasn't good
> enough (it could be *mostly* erased before a powerfail but not
> completely, so as soon as you start to program it you get a lot of
> bitflips). Hence the cleanmarkers.
>
> The only way you can treat a page as erased is if you *know* it was
> successfully erased. So I'd be reluctant to introduce a function that
> encourages people to draw inferences they shouldn't.
That's a bit different. Nowadays no one tries to compare to 0xFFs to
check if an eraseblock is erased or not :-)
This is about the situation when you have an eraseblock with data, and
you want to find where it ends, e.g., you want to find the first blank
NAND page. Both JFFS2 and UBIFS search for 0xFFs. It worked for many
years, but modern NANDs have bit-flips even in empty space, so reading a
never written NAND page may return mostly 0xFFs, but with few bits set
to 0. Modern NANDs have strong ECC which can handle 4 bit errors and
above. According to Ivan, manufacturers say it is expected and OK.
No make SW like JFFS2 and UBIFS work, modern drivers need to
either correct bit-flips on blank pages automatically, or be able to
quickly distinguish between blank and non-blank pages and memset the
buffer with 0xFFs for the former case.
What Robert says is that we probably need an is_page_blank() and let the
driver implement it optimally, or make MTD fall-back to 0xFFs
comparison.
This is my understanding.
Artem.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo
2011-11-13 13:35 ` Artem Bityutskiy
@ 2011-11-13 16:38 ` Robert Jarzmik
2011-11-13 19:55 ` Mike Dunn
0 siblings, 1 reply; 35+ messages in thread
From: Robert Jarzmik @ 2011-11-13 16:38 UTC (permalink / raw)
To: dedekind1, David Woodhouse
Cc: linux-mtd, Ivan Djelic, Mike Dunn, linux-kernel
Artem Bityutskiy <dedekind1@gmail.com> writes:
> What Robert says is that we probably need an is_page_blank() and let the
> driver implement it optimally, or make MTD fall-back to 0xFFs
> comparison.
>
> This is my understanding.
And that's exactly my point.
And while we're discussing MTD API, I'd like to add another thing I was thinking
of, from a conversation Mike and Ivan had.
They discussed how UBIFS is "intolerant" to bitflips, and marks a block as
"unusable" if one bitflip occured, even if the ECC can fix much more.
What I was thinking is that the MTD oob information which exposes how many ECC
are available should expose as well how many bitflips can be fixed (for example
4 bitflips can be fixed, 5 bitflips can be detected). Then, the read_oob()
function could return back 0 if read was successful, -Exxxx on error, or a
positive number N if N bitflips were fixed.
With this information, upper level could decide from (read_oob() return and
ecc.fixable_bitflips) if a block should be marked as unusable (worn out) or not.
I'd like some feedback on this idea as well.
Cheers.
--
Robert
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo
2011-11-13 16:38 ` Robert Jarzmik
@ 2011-11-13 19:55 ` Mike Dunn
2011-11-13 20:27 ` Artem Bityutskiy
0 siblings, 1 reply; 35+ messages in thread
From: Mike Dunn @ 2011-11-13 19:55 UTC (permalink / raw)
To: Robert Jarzmik
Cc: linux-mtd, Ivan Djelic, David Woodhouse, linux-kernel, dedekind1
On 11/13/2011 08:38 AM, Robert Jarzmik wrote:
>
> And while we're discussing MTD API, I'd like to add another thing I was thinking
> of, from a conversation Mike and Ivan had.
>
> They discussed how UBIFS is "intolerant" to bitflips, and marks a block as
> "unusable" if one bitflip occured, even if the ECC can fix much more.
>
> What I was thinking is that the MTD oob information which exposes how many ECC
> are available should expose as well how many bitflips can be fixed (for example
> 4 bitflips can be fixed, 5 bitflips can be detected).
You're referring to struct nand_ecclayout? I wouldn't think that would be the
appropriate place; it just describes the layout, not operational details.
> Then, the read_oob()
> function could return back 0 if read was successful, -Exxxx on error, or a
> positive number N if N bitflips were fixed.
> With this information, upper level could decide from (read_oob() return and
> ecc.fixable_bitflips) if a block should be marked as unusable (worn out) or not.
Something along these lines was suggested by Artem a few days ago:
http://lists.infradead.org/pipermail/linux-mtd/2011-November/038376.html
I'm looking into implementing this. Currently the drivers return -EUCLEAN from
read() and read_oob() if *any* bit error corrections were made, and this is the
information used by ubi to determine whether to scrub, and also whether to mark
a block as "bad" after running the PEB torture test. To summarize Artem's
suggestion in my own words... The drivers expose the ecc strength to the mtd
subsystem (as Robert also suggests). Mtd assigns a "scrublevel" to the device,
settable by the user through sysfs, with ecc strength as the default. Read
operations no longer go directly to the driver (as they currently do for
unpartitioned devices) but are handled in mtd. The driver returns the corrected
error count to mtd, which makes the determination of whether to return -EUCLEAN
or 0, based on the number of corrected errors and the scrublevel.
An objection might be that mtd should not be setting policy. It's also a fairly
sizeable modification. The alternative would be to implement a mechanism to
return the corrected error count to the higher layer (e.g., ubi) for each read
operation. This would be even more work, requiring modifications to mtd and ubi.
I'd like to work to resolve this either way, as ubi and ubifs are the killer
apps for these new drivers.
Thanks,
Mike
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo
2011-11-13 19:55 ` Mike Dunn
@ 2011-11-13 20:27 ` Artem Bityutskiy
2011-11-14 18:08 ` Proposed change to mtd read functions (Was Re: [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo) Mike Dunn
0 siblings, 1 reply; 35+ messages in thread
From: Artem Bityutskiy @ 2011-11-13 20:27 UTC (permalink / raw)
To: Mike Dunn
Cc: David Woodhouse, Ivan Djelic, Robert Jarzmik, linux-kernel,
linux-mtd
On Sun, 2011-11-13 at 11:55 -0800, Mike Dunn wrote:
> An objection might be that mtd should not be setting policy. It's also a fairly
> sizeable modification. The alternative would be to implement a mechanism to
> return the corrected error count to the higher layer (e.g., ubi) for each read
> operation. This would be even more work, requiring modifications to mtd and ubi.
Yeah, probably just returning the ECC correction count is cleaner
design. Probably we can add another argument to the mtd read function
and if the return code is -EUCLEAN (correctable bit-flips happened), it
would contain the highest ECC correction count encountered while reading
this region of the flash. So the SW which does not care, will not
require any changes.
I am not sure if you'll need to mtd interfaces from mtd->func(...) to
mtd_func(mtd, ...) for this or not, though.
Artem.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo
2011-11-13 10:18 ` Robert Jarzmik
2011-11-13 12:53 ` Artem Bityutskiy
@ 2011-11-14 0:58 ` Mike Dunn
1 sibling, 0 replies; 35+ messages in thread
From: Mike Dunn @ 2011-11-14 0:58 UTC (permalink / raw)
To: Robert Jarzmik; +Cc: linux-mtd, dwmw2, linux-kernel, dedekind1
On 11/13/2011 02:18 AM, Robert Jarzmik wrote:
> I took a different approach. I check an internal docg3 register to see if the
> page was written. Or I could had have checked the Hamming code, as I don't think
> it can be 0xff whatever the pagesize 7 bytes values.
>
> The reason behind is that the Hamming code is Ham(64, 57), ie. Ham(2^6,
> 2^6-6-1). The means the 6 bits are enough to cover all codewords possibilities,
> and 0xff is not one of them.
>
> So unless a bitflip in Hamming code, 0xff in it means blank page.
Clever!
> And I think
> the ECC engine is even smarter, with the ECCCONF1_PAGE_IS_WRITTEN.
I forgot about this. If the hardware can indeed tell you, I guess this is the
best way. Sounds like I'll be updating my blank page detection.
Mike
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v2 13/16] mtd/docg3: add ECC correction code
2011-11-13 10:35 ` Robert Jarzmik
@ 2011-11-14 2:13 ` Mike Dunn
0 siblings, 0 replies; 35+ messages in thread
From: Mike Dunn @ 2011-11-14 2:13 UTC (permalink / raw)
To: Robert Jarzmik; +Cc: dwmw2, linux-mtd, linux-kernel, dedekind1
On 11/13/2011 02:35 AM, Robert Jarzmik wrote:
> Mike Dunn <mikedunn@newsguy.com> writes:
>
>>
>> Where do you check for reads of a blank page?
> On stack frame above. Look at doc_read_oob():
> if ((block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) &&
> (eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) &&
> (eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN) &&
> \---> this is the key
> (ops->mode != MTD_OPS_RAW) &&
> (nbdata == DOC_LAYOUT_PAGE_SIZE)) {
> ret = doc_ecc_bch_fix_data(docg3, buf, hwecc);
>
> Here you see that I'll make the error correction only if the page was written
> before. If it's blank, I continue reading without attempting ECC correction.
G4 probably has this capability too. If so, I'll be improving my blank page
detection. I *really* have to go through your driver in its entirety to see
what other insights you have. Currently getting pulled in multiple directions.
>> Not specifically related to this patch, but... are you sure you want to
>> initialize the ecc on every read? I'm sure it's not necessary; you can just
>> leave it on; maybe turn it off if doing raw reads. I know this is the case for
>> both the P3 and G4 when running under PalmOS / TrueFFS library. I notice that
>> this function has delays and polls the status register in between calls to
>> cpu_relax(), so the performance hit is probably not insignificant, especiallu
>> when done for every 512 byte page.
> Well, that's some info.
> And yes, it adds some latency.
> Now for the necessity, I'm not fully convinced. I know that the ECC register is
> set up differently for reads and writes (that's the
> DOC_ECCCONF0_READ_MODE). When doc_read_oob() is called, I don't know if the
> previous action was a read or a write ...
>
> What I could do to improve performance would be to store in the docg3 private
> data if last action was a read or a write, and perform the doc_*_page_ecc_init()
> only if action changes. What do you think ?
>
Never mind. Sorry. I was thinking DOC_ECCCONF1, which I write to during
initialization to (I believe) enable ecc. Anyway, you have a better handle on
the internals of the chip than I do. I'm still looking forward to comparing
your code to my old P3 test code (which mostly just parrots what I observed
during operation). Until then I'll keep my opinions on register sequences, etc,
to myself!'
Good job!
Mike
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: Proposed change to mtd read functions (Was Re: [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo)
2011-11-14 18:08 ` Proposed change to mtd read functions (Was Re: [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo) Mike Dunn
@ 2011-11-14 17:38 ` Artem Bityutskiy
0 siblings, 0 replies; 35+ messages in thread
From: Artem Bityutskiy @ 2011-11-14 17:38 UTC (permalink / raw)
To: Mike Dunn
Cc: Robert Jarzmik, Ivan Djelic, David Woodhouse, linux-kernel,
linux-mtd
[-- Attachment #1: Type: text/plain, Size: 1637 bytes --]
On Mon, 2011-11-14 at 10:08 -0800, Mike Dunn wrote:
> This would be better than the cumulative error count over the entire block,
> because the highest count on any one page is more significant, I think.
Yeah, although in the previous proposal I also assumed something like
that, not "cumulative".
Just a side note - take my suggestions with a grain of salt - I do not
actively work on MTD any longer so may mislead you :-)
> > So the SW which does not care, will not
> > require any changes.
> >
> > I am not sure if you'll need to mtd interfaces from mtd->func(...) to
> > mtd_func(mtd, ...) for this or not, though.
>
>
> I don't (yet) see why I would need to.
>
> Just adding the argument to mtd->read(), mtd->read_oob(), would be a simple
> change, but large in scope, affecting all users of the mtd interface. Any
> advice on how to proceed?
Add the argument without implementing its support, amend all users and
make them compile.
> Should it be one big patchset, with individual
> patches for changes to mtd, nand, one_nand, mtdchar, each driver, ... ?
> If it
> is not all merged at once, the build will be broken for the unpatched
> components. Or is that acceptable, and the patches can be submitted piecemeal,
> starting with, say, mtd, nand, nandsim, mtdram, mtdchar? Or should we
> temporarily create a branch from l2-mtd until we're satisfiled that this is all
> stable?
We can create a branch regardless, if you find this useful.
I guess one big patch should be OK. If it causes issues we can later
think how to split it.
--
Best Regards,
Artem Bityutskiy
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply [flat|nested] 35+ messages in thread
* Proposed change to mtd read functions (Was Re: [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo)
2011-11-13 20:27 ` Artem Bityutskiy
@ 2011-11-14 18:08 ` Mike Dunn
2011-11-14 17:38 ` Artem Bityutskiy
0 siblings, 1 reply; 35+ messages in thread
From: Mike Dunn @ 2011-11-14 18:08 UTC (permalink / raw)
To: dedekind1
Cc: Robert Jarzmik, Ivan Djelic, David Woodhouse, linux-kernel,
linux-mtd
On 11/13/2011 12:27 PM, Artem Bityutskiy wrote:
> On Sun, 2011-11-13 at 11:55 -0800, Mike Dunn wrote:
>> An objection might be that mtd should not be setting policy. It's also a fairly
>> sizeable modification. The alternative would be to implement a mechanism to
>> return the corrected error count to the higher layer (e.g., ubi) for each read
>> operation. This would be even more work, requiring modifications to mtd and ubi.
> Yeah, probably just returning the ECC correction count is cleaner
> design. Probably we can add another argument to the mtd read function
> and if the return code is -EUCLEAN (correctable bit-flips happened), it
> would contain the highest ECC correction count encountered while reading
> this region of the flash.
This would be better than the cumulative error count over the entire block,
because the highest count on any one page is more significant, I think.
> So the SW which does not care, will not
> require any changes.
>
> I am not sure if you'll need to mtd interfaces from mtd->func(...) to
> mtd_func(mtd, ...) for this or not, though.
I don't (yet) see why I would need to.
Just adding the argument to mtd->read(), mtd->read_oob(), would be a simple
change, but large in scope, affecting all users of the mtd interface. Any
advice on how to proceed? Should it be one big patchset, with individual
patches for changes to mtd, nand, one_nand, mtdchar, each driver, ... ? If it
is not all merged at once, the build will be broken for the unpatched
components. Or is that acceptable, and the patches can be submitted piecemeal,
starting with, say, mtd, nand, nandsim, mtdram, mtdchar? Or should we
temporarily create a branch from l2-mtd until we're satisfiled that this is all
stable?
Thanks,
Mike
> Artem.
>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
>
>
^ permalink raw reply [flat|nested] 35+ messages in thread
end of thread, other threads:[~2011-11-14 17:38 UTC | newest]
Thread overview: 35+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-11-10 8:05 [PATCH v2 00/16] DocG3 fixes and write support Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 01/16] mtd/docg3: fix debug log verbosity Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 02/16] mtd/docg3: fix tracing of IO in writeb Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 03/16] mtd/docg3: fix protection areas reading Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 04/16] mtd/docg3: fix BCH registers Robert Jarzmik
2011-11-12 19:40 ` Mike Dunn
2011-11-13 10:20 ` Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 05/16] mtd/docg3: fix reading oob+data without correction Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 06/16] mtd/docg3: add multiple floor support Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo Robert Jarzmik
2011-11-12 19:39 ` Mike Dunn
2011-11-13 10:18 ` Robert Jarzmik
2011-11-13 12:53 ` Artem Bityutskiy
2011-11-13 13:03 ` David Woodhouse
2011-11-13 13:35 ` Artem Bityutskiy
2011-11-13 16:38 ` Robert Jarzmik
2011-11-13 19:55 ` Mike Dunn
2011-11-13 20:27 ` Artem Bityutskiy
2011-11-14 18:08 ` Proposed change to mtd read functions (Was Re: [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo) Mike Dunn
2011-11-14 17:38 ` Artem Bityutskiy
2011-11-14 0:58 ` [PATCH v2 07/16] mtd/docg3: add OOB layout to mtdinfo Mike Dunn
2011-11-10 8:05 ` [PATCH v2 08/16] mtd/docg3: add registers for erasing and writing Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 09/16] mtd/docg3: add OOB buffer to device structure Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 10/16] mtd/docg3: add write functions Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 11/16] mtd/docg3: add erase functions Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 12/16] mtd/docg3: map erase and write functions Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 13/16] mtd/docg3: add ECC correction code Robert Jarzmik
2011-11-12 19:49 ` Mike Dunn
2011-11-13 10:35 ` Robert Jarzmik
2011-11-14 2:13 ` Mike Dunn
2011-11-10 8:05 ` [PATCH v2 14/16] mtd/docg3: add suspend and resume Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 15/16] mtd/docg3: add fast mode Robert Jarzmik
2011-11-10 8:05 ` [PATCH v2 16/16] mtd/docg3: add protection areas sysfs access Robert Jarzmik
2011-11-12 20:02 ` [PATCH v2 00/16] DocG3 fixes and write support Mike Dunn
2011-11-13 10:41 ` Robert Jarzmik
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).