* [PATCH] [MTD] OneNAND: Add support for auto-placement of out-of-band data
@ 2007-01-26 15:13 Adrian Hunter
2007-01-26 15:44 ` Adrian Hunter
0 siblings, 1 reply; 15+ messages in thread
From: Adrian Hunter @ 2007-01-26 15:13 UTC (permalink / raw)
To: linux-mtd
Enable the use of oob operation mode MTD_OOB_AUTO with OneNAND.
Note that MTD_OOB_RAW is still not supported.
Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
---
drivers/mtd/onenand/onenand_base.c | 144 ++++++++++++++++++++++++++++++++---
1 files changed, 131 insertions(+), 13 deletions(-)
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 65acb85..3d1a725 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -787,17 +787,61 @@ static int onenand_read(struct mtd_info
}
/**
+ * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param buf destination address
+ * @param column oob offset to read from
+ * @param thislen oob length to read
+ */
+static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
+ int *thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int readcol = column;
+ int readend = column + *thislen;
+ int lastgap = 0, len = 0;
+ uint8_t *oob_buf = this->page_buf + mtd->writesize;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (readcol >= lastgap)
+ readcol += free->offset - lastgap;
+ if (readend >= lastgap)
+ readend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ if (readend > mtd->oobsize)
+ readend = mtd->oobsize;
+ this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf + readcol,
+ readcol, readend - readcol);
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < readend && free_end > readcol) {
+ int st = max_t(int,free->offset,readcol);
+ int ed = min_t(int,free_end,readend);
+ int n = ed - st;
+ memcpy(buf, oob_buf + st, n);
+ len += n;
+ buf += n;
+ }
+ }
+ *thislen = len;
+ return 0;
+}
+
+/**
* onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band
* @param mtd MTD device structure
* @param from offset to read from
* @param len number of bytes to read
* @param retlen pointer to variable to store the number of read bytes
* @param buf the databuffer to put data
+ * @param mode operation mode
*
* OneNAND read out-of-band data from the spare area
*/
int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
int read = 0, thislen, column;
@@ -832,7 +876,10 @@ int onenand_do_read_oob(struct mtd_info
ret = this->wait(mtd, FL_READING);
/* First copy data and check return value for ECC handling */
- this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_transfer_auto_oob(mtd, buf, column, &thislen);
+ else
+ this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret);
@@ -871,10 +918,18 @@ out:
static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
@@ -1060,17 +1115,58 @@ static int onenand_write(struct mtd_info
}
/**
+ * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param oob_buf oob buffer
+ * @param buf source address
+ * @param column oob offset to write to
+ * @param thislen oob length to write
+ */
+static void onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
+ const u_char *buf, int column, int *thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int writecol = column;
+ int writeend = column + *thislen;
+ int lastgap = 0, len = 0;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (writecol >= lastgap)
+ writecol += free->offset - lastgap;
+ if (writeend >= lastgap)
+ writeend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ if (writeend > mtd->oobsize)
+ writeend = mtd->oobsize;
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < writeend && free_end > writecol) {
+ int st = max_t(int,free->offset,writecol);
+ int ed = min_t(int,free_end,writeend);
+ int n = ed - st;
+ memcpy(oob_buf + st, buf, n);
+ len += n;
+ buf += n;
+ }
+ }
+ *thislen = len;
+}
+
+/**
* onenand_do_write_oob - [Internal] OneNAND write out-of-band
* @param mtd MTD device structure
* @param to offset to write to
* @param len number of bytes to write
* @param retlen pointer to variable to store the number of written bytes
* @param buf the data to write
+ * @param mode operation mode
*
* OneNAND write out-of-band
*/
static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
+ size_t *retlen, const u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
int column, ret = 0;
@@ -1103,7 +1199,10 @@ static int onenand_do_write_oob(struct m
/* We send data to spare ram with oobsize
* to prevent byte access */
memset(this->page_buf, 0xff, mtd->oobsize);
- memcpy(this->page_buf + column, buf, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_fill_auto_oob(mtd, this->page_buf, buf, column, &thislen);
+ else
+ memcpy(this->page_buf + column, buf, thislen);
this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize);
this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
@@ -1116,7 +1215,7 @@ static int onenand_do_write_oob(struct m
goto out;
}
- ret = onenand_verify_oob(mtd, buf, to, thislen);
+ ret = onenand_verify_oob(mtd, this->page_buf, to, thislen);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret);
goto out;
@@ -1149,10 +1248,18 @@ out:
static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
/**
@@ -1318,7 +1425,7 @@ static int onenand_default_block_markbad
/* We write two bytes, so we dont have to mess with 16 bit access */
ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
- return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf);
+ return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE);
}
/**
@@ -1612,7 +1719,7 @@ static int do_otp_lock(struct mtd_info *
this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
this->wait(mtd, FL_OTPING);
- ret = onenand_do_write_oob(mtd, from, len, retlen, buf);
+ ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE);
/* Exit OTP access mode */
this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -2020,6 +2127,7 @@ static void onenand_resume(struct mtd_in
*/
int onenand_scan(struct mtd_info *mtd, int maxchips)
{
+ int i;
struct onenand_chip *this = mtd->priv;
if (!this->read_word)
@@ -2091,6 +2199,16 @@ int onenand_scan(struct mtd_info *mtd, i
}
this->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+ /*
+ * The number of bytes available for a client to place data into
+ * the out of band area
+ */
+ this->ecclayout->oobavail = 0;
+ for (i = 0; this->ecclayout->oobfree[i].length; i++)
+ this->ecclayout->oobavail +=
+ this->ecclayout->oobfree[i].length;
+
mtd->ecclayout = this->ecclayout;
/* Fill in remaining MTD driver data */
--
1.4.3
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH] [MTD] OneNAND: Add support for auto-placement of out-of-band data
2007-01-26 15:13 [PATCH] [MTD] OneNAND: Add support for auto-placement of out-of-band data Adrian Hunter
@ 2007-01-26 15:44 ` Adrian Hunter
2007-01-29 6:02 ` [PATCH] [MTD] OneNAND: Add support for auto-placement ofout-of-band data Kyungmin Park
0 siblings, 1 reply; 15+ messages in thread
From: Adrian Hunter @ 2007-01-26 15:44 UTC (permalink / raw)
To: linux-mtd
Enable the use of oob operation mode MTD_OOB_AUTO with OneNAND.
Note that MTD_OOB_RAW is still not supported.
Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
---
drivers/mtd/onenand/onenand_base.c | 144 ++++++++++++++++++++++++++++++++---
drivers/mtd/onenand/onenand_bbt.c | 4 +-
2 files changed, 133 insertions(+), 15 deletions(-)
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 65acb85..3d1a725 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -787,17 +787,61 @@ static int onenand_read(struct mtd_info
}
/**
+ * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param buf destination address
+ * @param column oob offset to read from
+ * @param thislen oob length to read
+ */
+static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
+ int *thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int readcol = column;
+ int readend = column + *thislen;
+ int lastgap = 0, len = 0;
+ uint8_t *oob_buf = this->page_buf + mtd->writesize;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (readcol >= lastgap)
+ readcol += free->offset - lastgap;
+ if (readend >= lastgap)
+ readend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ if (readend > mtd->oobsize)
+ readend = mtd->oobsize;
+ this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf + readcol,
+ readcol, readend - readcol);
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < readend && free_end > readcol) {
+ int st = max_t(int,free->offset,readcol);
+ int ed = min_t(int,free_end,readend);
+ int n = ed - st;
+ memcpy(buf, oob_buf + st, n);
+ len += n;
+ buf += n;
+ }
+ }
+ *thislen = len;
+ return 0;
+}
+
+/**
* onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band
* @param mtd MTD device structure
* @param from offset to read from
* @param len number of bytes to read
* @param retlen pointer to variable to store the number of read bytes
* @param buf the databuffer to put data
+ * @param mode operation mode
*
* OneNAND read out-of-band data from the spare area
*/
int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
int read = 0, thislen, column;
@@ -832,7 +876,10 @@ int onenand_do_read_oob(struct mtd_info
ret = this->wait(mtd, FL_READING);
/* First copy data and check return value for ECC handling */
- this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_transfer_auto_oob(mtd, buf, column, &thislen);
+ else
+ this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret);
@@ -871,10 +918,18 @@ out:
static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
@@ -1060,17 +1115,58 @@ static int onenand_write(struct mtd_info
}
/**
+ * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param oob_buf oob buffer
+ * @param buf source address
+ * @param column oob offset to write to
+ * @param thislen oob length to write
+ */
+static void onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
+ const u_char *buf, int column, int *thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int writecol = column;
+ int writeend = column + *thislen;
+ int lastgap = 0, len = 0;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (writecol >= lastgap)
+ writecol += free->offset - lastgap;
+ if (writeend >= lastgap)
+ writeend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ if (writeend > mtd->oobsize)
+ writeend = mtd->oobsize;
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < writeend && free_end > writecol) {
+ int st = max_t(int,free->offset,writecol);
+ int ed = min_t(int,free_end,writeend);
+ int n = ed - st;
+ memcpy(oob_buf + st, buf, n);
+ len += n;
+ buf += n;
+ }
+ }
+ *thislen = len;
+}
+
+/**
* onenand_do_write_oob - [Internal] OneNAND write out-of-band
* @param mtd MTD device structure
* @param to offset to write to
* @param len number of bytes to write
* @param retlen pointer to variable to store the number of written bytes
* @param buf the data to write
+ * @param mode operation mode
*
* OneNAND write out-of-band
*/
static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
+ size_t *retlen, const u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
int column, ret = 0;
@@ -1103,7 +1199,10 @@ static int onenand_do_write_oob(struct m
/* We send data to spare ram with oobsize
* to prevent byte access */
memset(this->page_buf, 0xff, mtd->oobsize);
- memcpy(this->page_buf + column, buf, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_fill_auto_oob(mtd, this->page_buf, buf, column, &thislen);
+ else
+ memcpy(this->page_buf + column, buf, thislen);
this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize);
this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
@@ -1116,7 +1215,7 @@ static int onenand_do_write_oob(struct m
goto out;
}
- ret = onenand_verify_oob(mtd, buf, to, thislen);
+ ret = onenand_verify_oob(mtd, this->page_buf, to, thislen);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret);
goto out;
@@ -1149,10 +1248,18 @@ out:
static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
/**
@@ -1318,7 +1425,7 @@ static int onenand_default_block_markbad
/* We write two bytes, so we dont have to mess with 16 bit access */
ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
- return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf);
+ return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE);
}
/**
@@ -1612,7 +1719,7 @@ static int do_otp_lock(struct mtd_info *
this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
this->wait(mtd, FL_OTPING);
- ret = onenand_do_write_oob(mtd, from, len, retlen, buf);
+ ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE);
/* Exit OTP access mode */
this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -2020,6 +2127,7 @@ static void onenand_resume(struct mtd_in
*/
int onenand_scan(struct mtd_info *mtd, int maxchips)
{
+ int i;
struct onenand_chip *this = mtd->priv;
if (!this->read_word)
@@ -2091,6 +2199,16 @@ int onenand_scan(struct mtd_info *mtd, i
}
this->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+ /*
+ * The number of bytes available for a client to place data into
+ * the out of band area
+ */
+ this->ecclayout->oobavail = 0;
+ for (i = 0; this->ecclayout->oobfree[i].length; i++)
+ this->ecclayout->oobavail +=
+ this->ecclayout->oobfree[i].length;
+
mtd->ecclayout = this->ecclayout;
/* Fill in remaining MTD driver data */
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
index aa46b7f..acea9a1 100644
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -18,7 +18,7 @@ #include <linux/mtd/onenand.h>
#include <linux/mtd/compatmac.h>
extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf);
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode);
/**
* check_short_pattern - [GENERIC] check if a pattern is in the buffer
@@ -91,7 +91,7 @@ static int create_bbt(struct mtd_info *m
/* No need to read pages fully,
* just read required OOB bytes */
ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs,
- readlen, &retlen, &buf[0]);
+ readlen, &retlen, &buf[0], MTD_OOB_PLACE);
/* If it is a initial bad block, just ignore it */
if (ret && !(ret & ONENAND_CTRL_LOAD))
--
1.4.3
^ permalink raw reply related [flat|nested] 15+ messages in thread
* RE: [PATCH] [MTD] OneNAND: Add support for auto-placement ofout-of-band data
2007-01-26 15:44 ` Adrian Hunter
@ 2007-01-29 6:02 ` Kyungmin Park
2007-01-29 8:13 ` Adrian Hunter
0 siblings, 1 reply; 15+ messages in thread
From: Kyungmin Park @ 2007-01-29 6:02 UTC (permalink / raw)
To: 'Adrian Hunter', linux-mtd
Hi Adiran
>
> Enable the use of oob operation mode MTD_OOB_AUTO with OneNAND.
> Note that MTD_OOB_RAW is still not supported.
>
It's cool. however how can we test this one?
Just modify JFFS2 oob mode from OOB_PLACE to OOB_AUTO?
Thank you,
Kyungmin Park
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] [MTD] OneNAND: Add support for auto-placement ofout-of-band data
2007-01-29 6:02 ` [PATCH] [MTD] OneNAND: Add support for auto-placement ofout-of-band data Kyungmin Park
@ 2007-01-29 8:13 ` Adrian Hunter
2007-01-29 15:36 ` Adrian Hunter
2007-01-29 15:37 ` [PATCH] [MTD] OneNAND: Add support for auto-placement ofout-of-band data Adrian Hunter
0 siblings, 2 replies; 15+ messages in thread
From: Adrian Hunter @ 2007-01-29 8:13 UTC (permalink / raw)
To: kmpark; +Cc: linux-mtd
ext Kyungmin Park wrote:
> It's cool. however how can we test this one?
I have a test module, that I will post.
However I just noticed lots of bugs so please wait for a corrected version of the patch.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] [MTD] OneNAND: Add support for auto-placement ofout-of-band data
2007-01-29 8:13 ` Adrian Hunter
@ 2007-01-29 15:36 ` Adrian Hunter
2007-01-30 6:08 ` [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-band data Kyungmin Park
2007-01-29 15:37 ` [PATCH] [MTD] OneNAND: Add support for auto-placement ofout-of-band data Adrian Hunter
1 sibling, 1 reply; 15+ messages in thread
From: Adrian Hunter @ 2007-01-29 15:36 UTC (permalink / raw)
To: linux-mtd
ext Adrian Hunter wrote:
> However I just noticed lots of bugs so please wait for a corrected version of the patch.
Corrected patch below
>From 9a1a24325059ba00a64faeabac8a1f0da9febe6f Mon Sep 17 00:00:00 2001
From: Adrian Hunter <ext-adrian.hunter@nokia.com>
Date: Mon, 29 Jan 2007 17:28:54 +0200
Subject: [MTD] OneNAND: Add support for auto-placement of out-of-band data
Enable the use of oob operation mode MTD_OOB_AUTO with OneNAND.
Note that MTD_OOB_RAW is still not supported.
Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
---
drivers/mtd/onenand/onenand_base.c | 211 ++++++++++++++++++++++++++++++------
drivers/mtd/onenand/onenand_bbt.c | 4 +-
2 files changed, 178 insertions(+), 37 deletions(-)
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 65acb85..24f1954 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -787,20 +787,65 @@ static int onenand_read(struct mtd_info
}
/**
+ * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param buf destination address
+ * @param column oob offset to read from
+ * @param thislen oob length to read
+ */
+static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
+ int *thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int readcol = column;
+ int readend = column + *thislen;
+ int lastgap = 0, len = 0;
+ uint8_t *oob_buf = this->page_buf + mtd->writesize;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (readcol >= lastgap)
+ readcol += free->offset - lastgap;
+ if (readend >= lastgap)
+ readend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ if (readend > mtd->oobsize)
+ readend = mtd->oobsize;
+ if (readcol < readend)
+ this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf + readcol,
+ readcol, readend - readcol);
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < readend && free_end > readcol) {
+ int st = max_t(int,free->offset,readcol);
+ int ed = min_t(int,free_end,readend);
+ int n = ed - st;
+ memcpy(buf, oob_buf + st, n);
+ len += n;
+ buf += n;
+ }
+ }
+ *thislen = len;
+ return 0;
+}
+
+/**
* onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band
* @param mtd MTD device structure
* @param from offset to read from
* @param len number of bytes to read
* @param retlen pointer to variable to store the number of read bytes
* @param buf the databuffer to put data
+ * @param mode operation mode
*
* OneNAND read out-of-band data from the spare area
*/
int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
- int read = 0, thislen, column;
+ int read = 0, thislen, column, oobsize;
int ret = 0;
DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
@@ -808,21 +853,33 @@ int onenand_do_read_oob(struct mtd_info
/* Initialize return length value */
*retlen = 0;
+ if (mode == MTD_OOB_AUTO)
+ oobsize = this->ecclayout->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = from & (mtd->oobsize - 1);
+
+ if (unlikely(column >= oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to start read outside oob\n");
+ return -EINVAL;
+ }
+
/* Do not allow reads past end of device */
- if (unlikely((from + len) > mtd->size)) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n");
+ if (unlikely(from >= mtd->size ||
+ len > ((mtd->size >> this->page_shift) -
+ (from >> this->page_shift)) * oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to read beyond end of device\n");
return -EINVAL;
}
/* Grab the lock and see if the device is available */
onenand_get_device(mtd, FL_READING);
- column = from & (mtd->oobsize - 1);
-
while (read < len) {
cond_resched();
- thislen = mtd->oobsize - column;
+ thislen = oobsize - column;
thislen = min_t(int, thislen, len);
this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
@@ -832,7 +889,10 @@ int onenand_do_read_oob(struct mtd_info
ret = this->wait(mtd, FL_READING);
/* First copy data and check return value for ECC handling */
- this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_transfer_auto_oob(mtd, buf, column, &thislen);
+ else
+ this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret);
@@ -871,10 +931,18 @@ out:
static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
@@ -883,14 +951,12 @@ #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
* @param mtd MTD device structure
* @param buf the databuffer to verify
* @param to offset to read from
- * @param len number of bytes to read and compare
*
*/
-static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to, int len)
+static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
{
struct onenand_chip *this = mtd->priv;
- char *readp = this->page_buf;
- int column = to & (mtd->oobsize - 1);
+ char *readp = this->page_buf + mtd->writesize;
int status, i;
this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
@@ -899,9 +965,8 @@ static int onenand_verify_oob(struct mtd
if (status)
return status;
- this->read_bufferram(mtd, ONENAND_SPARERAM, readp, column, len);
-
- for(i = 0; i < len; i++)
+ this->read_bufferram(mtd, ONENAND_SPARERAM, readp, 0, mtd->oobsize);
+ for(i = 0; i < mtd->oobsize; i++)
if (buf[i] != 0xFF && buf[i] != readp[i])
return -EBADMSG;
@@ -1060,20 +1125,61 @@ static int onenand_write(struct mtd_info
}
/**
+ * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param oob_buf oob buffer
+ * @param buf source address
+ * @param column oob offset to write to
+ * @param thislen oob length to write
+ */
+static void onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
+ const u_char *buf, int column, int *thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int writecol = column;
+ int writeend = column + *thislen;
+ int lastgap = 0, len = 0;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (writecol >= lastgap)
+ writecol += free->offset - lastgap;
+ if (writeend >= lastgap)
+ writeend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ if (writeend > mtd->oobsize)
+ writeend = mtd->oobsize;
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < writeend && free_end > writecol) {
+ int st = max_t(int,free->offset,writecol);
+ int ed = min_t(int,free_end,writeend);
+ int n = ed - st;
+ memcpy(oob_buf + st, buf, n);
+ len += n;
+ buf += n;
+ }
+ }
+ *thislen = len;
+}
+
+/**
* onenand_do_write_oob - [Internal] OneNAND write out-of-band
* @param mtd MTD device structure
* @param to offset to write to
* @param len number of bytes to write
* @param retlen pointer to variable to store the number of written bytes
* @param buf the data to write
+ * @param mode operation mode
*
* OneNAND write out-of-band
*/
static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
+ size_t *retlen, const u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
- int column, ret = 0;
+ int column, ret = 0, oobsize;
int written = 0;
DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
@@ -1081,9 +1187,23 @@ static int onenand_do_write_oob(struct m
/* Initialize retlen, in case of early exit */
*retlen = 0;
- /* Do not allow writes past end of device */
- if (unlikely((to + len) > mtd->size)) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n");
+ if (mode == MTD_OOB_AUTO)
+ oobsize = this->ecclayout->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = to & (mtd->oobsize - 1);
+
+ if (unlikely(column >= oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to start write outside oob\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow reads past end of device */
+ if (unlikely(to >= mtd->size ||
+ len > ((mtd->size >> this->page_shift) -
+ (to >> this->page_shift)) * oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to write past end of device\n");
return -EINVAL;
}
@@ -1092,18 +1212,19 @@ static int onenand_do_write_oob(struct m
/* Loop until all data write */
while (written < len) {
- int thislen = min_t(int, mtd->oobsize, len - written);
+ int thislen = min_t(int, oobsize, len - written);
cond_resched();
- column = to & (mtd->oobsize - 1);
-
this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
/* We send data to spare ram with oobsize
* to prevent byte access */
memset(this->page_buf, 0xff, mtd->oobsize);
- memcpy(this->page_buf + column, buf, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_fill_auto_oob(mtd, this->page_buf, buf, column, &thislen);
+ else
+ memcpy(this->page_buf + column, buf, thislen);
this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize);
this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
@@ -1112,11 +1233,11 @@ static int onenand_do_write_oob(struct m
ret = this->wait(mtd, FL_WRITING);
if (ret) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write filaed %d\n", ret);
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write failed %d\n", ret);
goto out;
}
- ret = onenand_verify_oob(mtd, buf, to, thislen);
+ ret = onenand_verify_oob(mtd, this->page_buf, to);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret);
goto out;
@@ -1127,8 +1248,9 @@ static int onenand_do_write_oob(struct m
if (written == len)
break;
- to += thislen;
+ to += mtd->writesize;
buf += thislen;
+ column = 0;
}
out:
@@ -1149,10 +1271,18 @@ out:
static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
/**
@@ -1318,7 +1448,7 @@ static int onenand_default_block_markbad
/* We write two bytes, so we dont have to mess with 16 bit access */
ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
- return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf);
+ return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE);
}
/**
@@ -1612,7 +1742,7 @@ static int do_otp_lock(struct mtd_info *
this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
this->wait(mtd, FL_OTPING);
- ret = onenand_do_write_oob(mtd, from, len, retlen, buf);
+ ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE);
/* Exit OTP access mode */
this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -2020,6 +2150,7 @@ static void onenand_resume(struct mtd_in
*/
int onenand_scan(struct mtd_info *mtd, int maxchips)
{
+ int i;
struct onenand_chip *this = mtd->priv;
if (!this->read_word)
@@ -2091,6 +2222,16 @@ int onenand_scan(struct mtd_info *mtd, i
}
this->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+ /*
+ * The number of bytes available for a client to place data into
+ * the out of band area
+ */
+ this->ecclayout->oobavail = 0;
+ for (i = 0; this->ecclayout->oobfree[i].length; i++)
+ this->ecclayout->oobavail +=
+ this->ecclayout->oobfree[i].length;
+
mtd->ecclayout = this->ecclayout;
/* Fill in remaining MTD driver data */
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
index aa46b7f..acea9a1 100644
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -18,7 +18,7 @@ #include <linux/mtd/onenand.h>
#include <linux/mtd/compatmac.h>
extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf);
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode);
/**
* check_short_pattern - [GENERIC] check if a pattern is in the buffer
@@ -91,7 +91,7 @@ static int create_bbt(struct mtd_info *m
/* No need to read pages fully,
* just read required OOB bytes */
ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs,
- readlen, &retlen, &buf[0]);
+ readlen, &retlen, &buf[0], MTD_OOB_PLACE);
/* If it is a initial bad block, just ignore it */
if (ret && !(ret & ONENAND_CTRL_LOAD))
--
1.4.3
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH] [MTD] OneNAND: Add support for auto-placement ofout-of-band data
2007-01-29 8:13 ` Adrian Hunter
2007-01-29 15:36 ` Adrian Hunter
@ 2007-01-29 15:37 ` Adrian Hunter
1 sibling, 0 replies; 15+ messages in thread
From: Adrian Hunter @ 2007-01-29 15:37 UTC (permalink / raw)
To: linux-mtd
ext Adrian Hunter wrote:
> ext Kyungmin Park wrote:
>> It's cool. however how can we test this one?
>
> I have a test module, that I will post.
Here it is
/*
* oobtest.c
*
* Test oob read and write on MTD device.
*
* Copyright (C) 2005-2006 Nokia Corporation
*
* Authors: Artem Bityutskiy <artem.bityutskiy@nokia.com>
* Adrian Hunter <ext-adrian.hunter@nokia.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; see the file COPYING. If not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kobject.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/mtd/mtd.h>
#include <linux/sched.h>
#include <linux/jiffies.h>
#define PRINT_PREF KERN_CRIT "oobtest: "
/* Uncomment this if you have old MTD sources */
/* #define writesize oobblock */
static int dev = 4;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
static struct mtd_info *mtd;
static unsigned char *readbuf = 0;
static unsigned char *writebuf = 0;
static int pgsize;
static int bufsize;
static int ebcnt;
static int pgcnt;
static int errcnt = 0;
static int use_offset;
static int use_len;
static int use_len_max;
static int vary_offset;
static unsigned long next = 1;
static int simple_rand(void)
{
next = next * 1103515245 + 12345;
return ((unsigned) (next / 65536) % 32768);
}
static void simple_srand(unsigned long seed)
{
next = seed;
}
static inline void set_random_data(unsigned char *buf,size_t len)
{
size_t i;
for (i = 0; i < len; ++i)
buf[i] = simple_rand();
}
static inline int erase_eraseblock(int ebnum)
{
int err;
struct erase_info ei;
loff_t addr = ebnum * mtd->erasesize;
memset(&ei, 0, sizeof(struct erase_info));
ei.mtd = mtd;
ei.addr = addr;
ei.len = mtd->erasesize;
err = mtd->erase(mtd, &ei);
if (unlikely(err)) {
printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum);
return err;
}
if (unlikely(ei.state == MTD_ERASE_FAILED)) {
printk(PRINT_PREF "some erase error occurred at EB %d\n", ebnum);
return -EIO;
}
return 0;
}
static void do_vary_offset(void)
{
use_len -= 1;
if (use_len < 1) {
use_offset += 1;
if (use_offset >= use_len_max)
use_offset = 0;
use_len = use_len_max - use_offset;
}
}
static inline int write_eraseblock(int ebnum)
{
int i;
struct mtd_oob_ops ops;
int err = 0;
loff_t addr = ebnum * mtd->erasesize;
for (i = 0; i < pgcnt; ++i, addr += pgsize) {
set_random_data(writebuf, use_len);
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = use_len;
ops.oobretlen = 0;
ops.ooboffs = use_offset;
ops.datbuf = 0;
ops.oobbuf = writebuf;
err = mtd->write_oob(mtd, addr, &ops);
if (unlikely(err || ops.oobretlen != use_len)) {
printk(PRINT_PREF "error: writeoob failed at 0x%08x\n", (unsigned) addr);
printk(PRINT_PREF "error: use_len = %d use_offset = %d\n", use_len, use_offset);
errcnt += 1;
return err ? err : -1;
}
if (vary_offset)
do_vary_offset();
}
return err;
}
static inline int write_eraseblock_in_one_go(int ebnum)
{
struct mtd_oob_ops ops;
int err = 0;
loff_t addr = ebnum * mtd->erasesize;
size_t len = mtd->ecclayout->oobavail * pgcnt;
set_random_data(writebuf, len);
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = len;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
ops.oobbuf = writebuf;
err = mtd->write_oob(mtd, addr, &ops);
if (unlikely(err || ops.oobretlen != len)) {
printk(PRINT_PREF "error: writeoob failed at 0x%08x\n", (unsigned) addr);
errcnt += 1;
return err ? err : -1;
}
return err;
}
static inline int verify_eraseblock(int ebnum)
{
int i;
struct mtd_oob_ops ops;
int err = 0;
loff_t addr = ebnum * mtd->erasesize;
for (i = 0; i < pgcnt; ++i, addr += pgsize) {
set_random_data(writebuf, use_len);
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = use_len;
ops.oobretlen = 0;
ops.ooboffs = use_offset;
ops.datbuf = 0;
ops.oobbuf = readbuf;
err = mtd->read_oob(mtd, addr, &ops);
if (unlikely(err || ops.oobretlen != use_len)) {
printk(PRINT_PREF "error: readoob failed at 0x%08x\n", (unsigned) addr);
errcnt += 1;
return err ? err : -1;
}
if (unlikely(memcmp(readbuf, writebuf, use_len))) {
printk(PRINT_PREF "error: verify failed at 0x%08x\n", (unsigned) addr);
errcnt += 1;
if (errcnt > 1000) {
printk(PRINT_PREF "error: too many errors\n");
return -1;
}
}
if (vary_offset)
do_vary_offset();
}
return err;
}
static inline int verify_eraseblock_in_one_go(int ebnum)
{
struct mtd_oob_ops ops;
int err = 0;
loff_t addr = ebnum * mtd->erasesize;
size_t len = mtd->ecclayout->oobavail * pgcnt;
set_random_data(writebuf, len);
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = len;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
ops.oobbuf = readbuf;
err = mtd->read_oob(mtd, addr, &ops);
if (unlikely(err || ops.oobretlen != len)) {
printk(PRINT_PREF "error: readoob failed at 0x%08x\n", (unsigned) addr);
errcnt += 1;
return err ? err : -1;
}
if (unlikely(memcmp(readbuf, writebuf, len))) {
printk(PRINT_PREF "error: verify failed at 0x%08x\n", (unsigned) addr);
errcnt += 1;
if (errcnt > 1000) {
printk(PRINT_PREF "error: too many errors\n");
return -1;
}
}
return err;
}
static int __init oobtest_init(void)
{
int err = 0;
u_int32_t i;
struct mtd_oob_ops ops;
loff_t addr = 0;
printk("\n");
printk("=========================================================="
"===============================\n");
printk("oobtest: dev = %d\n", dev);
mtd = get_mtd_device(NULL, dev);
if (IS_ERR(mtd)) {
err = PTR_ERR(mtd);
printk(PRINT_PREF "error: Cannot get MTD device\n");
return err;
}
if (mtd->writesize == 1) {
printk(PRINT_PREF "warning: this test was written for NAND."
"Assume page size is 512 bytes.\n");
pgsize = 512;
} else
pgsize = mtd->writesize;
printk(PRINT_PREF "oob available per page = %u\n",(unsigned) mtd->ecclayout->oobavail);
err = -ENOMEM;
bufsize = mtd->erasesize;
readbuf = kmalloc(bufsize, GFP_KERNEL);
if (!readbuf) {
printk(PRINT_PREF "error: cannot allocate memory\n");
goto out;
}
writebuf = kmalloc(bufsize, GFP_KERNEL);
if (!writebuf) {
printk(PRINT_PREF "error: cannot allocate memory\n");
goto out;
}
ebcnt = mtd->size / mtd->erasesize;
pgcnt = mtd->erasesize / pgsize;
use_offset = 0;
use_len = mtd->ecclayout->oobavail;
use_len_max = mtd->ecclayout->oobavail;
vary_offset = 0;
/* First test: write all oob, read it back and verify */
printk(PRINT_PREF "Test 1 of 5\n");
/* Erase all eraseblocks */
printk(PRINT_PREF "erasing\n");
for (i = 0; i < ebcnt; ++i) {
err = erase_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "erased %u\n", i);
cond_resched();
}
printk(PRINT_PREF "erased %u\n", i);
/* Write all eraseblocks */
simple_srand(1);
printk(PRINT_PREF "writing\n");
for (i = 0; i < ebcnt; ++i) {
err = write_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "written %u\n", i);
cond_resched();
}
printk(PRINT_PREF "written %u\n", i);
/* Check all eraseblocks */
simple_srand(1);
printk(PRINT_PREF "verifying\n");
for (i = 0; i < ebcnt; ++i) {
err = verify_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "verified %u\n", i);
cond_resched();
}
printk(PRINT_PREF "verified %u\n", i);
/* Second test: write all oob, a block at a time, read it back and verify */
printk(PRINT_PREF "Test 2 of 5\n");
/* Erase all eraseblocks */
printk(PRINT_PREF "erasing\n");
for (i = 0; i < ebcnt; ++i) {
err = erase_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "erased %u\n", i);
cond_resched();
}
printk(PRINT_PREF "erased %u\n", i);
/* Write all eraseblocks */
simple_srand(3);
printk(PRINT_PREF "writing\n");
for (i = 0; i < ebcnt; ++i) {
err = write_eraseblock_in_one_go(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "written %u\n", i);
cond_resched();
}
printk(PRINT_PREF "written %u\n", i);
/* Check all eraseblocks */
simple_srand(3);
printk(PRINT_PREF "verifying\n");
for (i = 0; i < ebcnt; ++i) {
err = verify_eraseblock_in_one_go(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "verified %u\n", i);
cond_resched();
}
printk(PRINT_PREF "verified %u\n", i);
/* Third test: write oob at varying offsets and lengths,
read it back and verify */
printk(PRINT_PREF "Test 3 of 5\n");
/* Erase all eraseblocks */
printk(PRINT_PREF "erasing\n");
for (i = 0; i < ebcnt; ++i) {
err = erase_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "erased %u\n", i);
cond_resched();
}
printk(PRINT_PREF "erased %u\n", i);
/* Write all eraseblocks */
use_offset = 0;
use_len = mtd->ecclayout->oobavail;
use_len_max = mtd->ecclayout->oobavail;
vary_offset = 1;
simple_srand(5);
printk(PRINT_PREF "writing\n");
for (i = 0; i < ebcnt; ++i) {
err = write_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "written %u\n", i);
cond_resched();
}
printk(PRINT_PREF "written %u\n", i);
/* Check all eraseblocks */
use_offset = 0;
use_len = mtd->ecclayout->oobavail;
use_len_max = mtd->ecclayout->oobavail;
vary_offset = 1;
simple_srand(5);
printk(PRINT_PREF "verifying\n");
for (i = 0; i < ebcnt; ++i) {
err = verify_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "verified %u\n", i);
cond_resched();
}
printk(PRINT_PREF "verified %u\n", i);
use_offset = 0;
use_len = mtd->ecclayout->oobavail;
use_len_max = mtd->ecclayout->oobavail;
vary_offset = 0;
/* Fourth test: try to write off end of device */
printk(PRINT_PREF "Test 4 of 5\n");
/* Erase all eraseblocks */
printk(PRINT_PREF "erasing\n");
for (i = 0; i < ebcnt; ++i) {
err = erase_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "erased %u\n", i);
cond_resched();
}
printk(PRINT_PREF "erased %u\n", i);
/* Attempt to write off end of oob */
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = 1;
ops.oobretlen = 0;
ops.ooboffs = mtd->ecclayout->oobavail;
ops.datbuf = 0;
ops.oobbuf = writebuf;
printk(PRINT_PREF "Attempting to start write past end of oob\n");
printk(PRINT_PREF "An error is expected...\n");
err = mtd->write_oob(mtd, 0, &ops);
if (unlikely(err)) {
printk(PRINT_PREF "Error occurred as expected\n");
err = 0;
} else {
printk(PRINT_PREF "error: started write past end of oob\n");
errcnt += 1;
}
/* Attempt to read off end of oob */
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = 1;
ops.oobretlen = 0;
ops.ooboffs = mtd->ecclayout->oobavail;
ops.datbuf = 0;
ops.oobbuf = readbuf;
printk(PRINT_PREF "Attempting to start read past end of oob\n");
printk(PRINT_PREF "An error is expected...\n");
err = mtd->read_oob(mtd, 0, &ops);
if (unlikely(err)) {
printk(PRINT_PREF "Error occurred as expected\n");
err = 0;
} else {
printk(PRINT_PREF "error: started read past end of oob\n");
errcnt += 1;
}
/* Write oob across chip boundary */
simple_srand(7);
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail * 2;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
ops.oobbuf = writebuf;
err = mtd->write_oob(mtd, addr, &ops);
if (unlikely(err))
goto out;
/* Read oob across chip boundary */
simple_srand(7);
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail * 2;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
ops.oobbuf = readbuf;
err = mtd->read_oob(mtd, addr, &ops);
if (unlikely(err))
goto out;
/* Attempt to write off end of device */
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail + 1;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
ops.oobbuf = writebuf;
printk(PRINT_PREF "Attempting to write past end of device\n");
printk(PRINT_PREF "An error is expected...\n");
err = mtd->write_oob(mtd, mtd->size - mtd->writesize, &ops);
if (unlikely(err)) {
printk(PRINT_PREF "Error occurred as expected\n");
err = 0;
} else {
printk(PRINT_PREF "error: wrote past end of device\n");
errcnt += 1;
}
/* Attempt to read off end of device */
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail + 1;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
ops.oobbuf = readbuf;
printk(PRINT_PREF "Attempting to read past end of device\n");
printk(PRINT_PREF "An error is expected...\n");
err = mtd->read_oob(mtd, mtd->size - mtd->writesize, &ops);
if (unlikely(err)) {
printk(PRINT_PREF "Error occurred as expected\n");
err = 0;
} else {
printk(PRINT_PREF "error: read past end of device\n");
errcnt += 1;
}
/* Fifth test: write / read across block boundaries */
printk(PRINT_PREF "Test 5 of 5\n");
/* Erase all eraseblocks */
printk(PRINT_PREF "erasing\n");
for (i = 0; i < ebcnt; ++i) {
err = erase_eraseblock(i);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "erased %u\n", i);
cond_resched();
}
printk(PRINT_PREF "erased %u\n", i);
/* Write all eraseblocks */
simple_srand(11);
printk(PRINT_PREF "writing\n");
for (i = 0; i < ebcnt - 1; ++i) {
set_random_data(writebuf, mtd->ecclayout->oobavail * 2);
addr = (i + 1) * mtd->erasesize - mtd->writesize;
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail * 2;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
ops.oobbuf = writebuf;
err = mtd->write_oob(mtd, addr, &ops);
if (unlikely(err))
goto out;
if (i % 256 == 0)
printk(PRINT_PREF "written %u\n", i);
cond_resched();
}
printk(PRINT_PREF "written %u\n", i);
/* Check all eraseblocks */
simple_srand(11);
printk(PRINT_PREF "verifying\n");
for (i = 0; i < ebcnt - 1; ++i) {
set_random_data(writebuf, mtd->ecclayout->oobavail * 2);
addr = (i + 1) * mtd->erasesize - mtd->writesize;
ops.mode = MTD_OOB_AUTO;
ops.len = 0;
ops.retlen = 0;
ops.ooblen = mtd->ecclayout->oobavail * 2;
ops.oobretlen = 0;
ops.ooboffs = 0;
ops.datbuf = 0;
ops.oobbuf = readbuf;
err = mtd->read_oob(mtd, addr, &ops);
if (unlikely(err))
goto out;
if (unlikely(memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2))) {
printk(PRINT_PREF "error: verify failed at 0x%08x\n", (unsigned) addr);
errcnt += 1;
if (errcnt > 1000) {
printk(PRINT_PREF "error: too many errors\n");
goto out;
}
}
if (i % 256 == 0)
printk(PRINT_PREF "verified %u\n", i);
cond_resched();
}
printk(PRINT_PREF "verified %u\n", i);
printk(PRINT_PREF "oobtest finished with %d errors\n", errcnt);
out:
kfree(writebuf);
kfree(readbuf);
put_mtd_device(mtd);
if (err)
printk(PRINT_PREF "error %d occurred\n", err);
printk("=========================================================="
"===============================\n");
return -1;
}
module_init(oobtest_init);
static void __exit oobtest_exit(void)
{
return;
}
module_exit(oobtest_exit);
MODULE_DESCRIPTION("Page test module");
MODULE_AUTHOR("Artem Bityutskiy, Adrian Hunter");
MODULE_LICENSE("GPL");
^ permalink raw reply [flat|nested] 15+ messages in thread
* RE: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-band data
2007-01-29 15:36 ` Adrian Hunter
@ 2007-01-30 6:08 ` Kyungmin Park
2007-01-31 8:45 ` Adrian Hunter
2007-01-31 15:42 ` Adrian Hunter
0 siblings, 2 replies; 15+ messages in thread
From: Kyungmin Park @ 2007-01-30 6:08 UTC (permalink / raw)
To: 'Adrian Hunter', linux-mtd
Hi,
Now I tested it and it works well,
It looks good except some minor error condition handling and type mismatch.
(see below)
Another interest things are nand is not passed in this test program. :)
/ # cat /proc/mtd
dev: size erasesize name
mtd6: 00800000 00002000 "NAND simulator partition"
/ # insmod /mtd-utils/oobtest.ko dev=6
Using /mtd-utils/oobtest.ko
============================================================================
====
oobtest: dev = 6
oobtest: oob available per page = 8
oobtest: Test 1 of 5
oobtest: erasing
oobtest: erased 0
oobtest: erased 256
oobtest: erased 512
oobtest: erased 768
oobtest: erased 1024
oobtest: writing
oobtest: written 0
oobtest: written 256
oobtest: written 512
oobtest: written 768
oobtest: written 1024
oobtest: verifying
oobtest: verified 0
oobtest: verified 256
oobtest: verified 512
oobtest: verified 768
oobtest: verified 1024
oobtest: Test 2 of 5
oobtest: erasing
oobtest: erased 0
oobtest: erased 256
oobtest: erased 512
oobtest: erased 768
oobtest: erased 1024
oobtest: writing
oobtest: error: writeoob failed at 0x00000000
oobtest: error -22 occurred
============================================================================
====
Thank you,
Kyungmin Park
> @@ -787,20 +787,65 @@ static int onenand_read(struct mtd_info }
>
> /**
> + * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
> + * @param mtd MTD device structure
> + * @param buf destination address
> + * @param column oob offset to read from
> + * @param thislen oob length to read
> + */
> +static int onenand_transfer_auto_oob(struct mtd_info *mtd,
> uint8_t *buf, int column,
> + int *thislen)
> +{
> + struct onenand_chip *this = mtd->priv;
> + struct nand_oobfree *free;
> + int readcol = column;
> + int readend = column + *thislen;
> + int lastgap = 0, len = 0;
> + uint8_t *oob_buf = this->page_buf + mtd->writesize;
> +
> + for (free = this->ecclayout->oobfree; free->length; ++free) {
> + if (readcol >= lastgap)
> + readcol += free->offset - lastgap;
> + if (readend >= lastgap)
> + readend += free->offset - lastgap;
> + lastgap = free->offset + free->length;
> + }
> + if (readend > mtd->oobsize)
> + readend = mtd->oobsize;
When this condition occurs?
> + if (readcol < readend)
> + this->read_bufferram(mtd, ONENAND_SPARERAM,
> oob_buf + readcol,
> + readcol, readend - readcol);
ditto, what happend If readcol is grater than readend ?
> + for (free = this->ecclayout->oobfree; free->length; ++free) {
> + int free_end = free->offset + free->length;
> + if (free->offset < readend && free_end > readcol) {
> + int st = max_t(int,free->offset,readcol);
> + int ed = min_t(int,free_end,readend);
> + int n = ed - st;
> + memcpy(buf, oob_buf + st, n);
> + len += n;
> + buf += n;
> + }
> + }
> + *thislen = len;
Why do we re-assign thislen value. In test program. it never changed.
> + return 0;
> +}
> +
> static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
> struct mtd_oob_ops *ops)
> {
> - BUG_ON(ops->mode != MTD_OOB_PLACE);
> -
> + switch (ops->mode)
> + {
=> swich (ops->mode) {
> + case MTD_OOB_PLACE:
> + case MTD_OOB_AUTO:
> + break;
> + case MTD_OOB_RAW:
> + return -EINVAL; /* Not implemented yet */
> + default:
> + return -EINVAL;
> + }
> return onenand_do_read_oob(mtd, from + ops->ooboffs,
> ops->ooblen,
> - &ops->oobretlen, ops->oobbuf);
> + &ops->oobretlen,
> ops->oobbuf, ops->mode);
> }
>
> +static void onenand_fill_auto_oob(struct mtd_info *mtd,
> u_char *oob_buf,
> + const u_char *buf, int
> column, int *thislen) {
Use int return type for compatability.
> + struct onenand_chip *this = mtd->priv;
> + struct nand_oobfree *free;
> + int writecol = column;
> + int writeend = column + *thislen;
> + int lastgap = 0, len = 0;
> +
> + for (free = this->ecclayout->oobfree; free->length; ++free) {
> + if (writecol >= lastgap)
> + writecol += free->offset - lastgap;
> + if (writeend >= lastgap)
> + writeend += free->offset - lastgap;
> + lastgap = free->offset + free->length;
> + }
> + if (writeend > mtd->oobsize)
> + writeend = mtd->oobsize;
It is also don't occurs if we need it use 'unlikely' macro
> + for (free = this->ecclayout->oobfree; free->length; ++free) {
> + int free_end = free->offset + free->length;
> + if (free->offset < writeend && free_end > writecol) {
> + int st = max_t(int,free->offset,writecol);
> + int ed = min_t(int,free_end,writeend);
> + int n = ed - st;
> + memcpy(oob_buf + st, buf, n);
> + len += n;
> + buf += n;
> + }
> + }
> + *thislen = len;
add return 0;
> +}
> +
> +/**
> * onenand_do_write_oob - [Internal] OneNAND write out-of-band
> * @param mtd MTD device structure
> * @param to offset to write to
> * @param len number of bytes to write
> * @param retlen pointer to variable to store the number
> of written bytes
> * @param buf the data to write
> + * @param mode operation mode
> *
> * OneNAND write out-of-band
> */
> static int onenand_do_write_oob(struct mtd_info *mtd, loff_t
> to, size_t len,
> - size_t *retlen, const u_char *buf)
> + size_t *retlen, const u_char
> *buf, mtd_oob_mode_t mode)
> {
> struct onenand_chip *this = mtd->priv;
> - int column, ret = 0;
> + int column, ret = 0, oobsize;
> int written = 0;
>
> DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to =
> 0x%08x, len = %i\n", (unsigned int) to, (int) len); @@
> -1081,9 +1187,23 @@ static int onenand_do_write_oob(struct m
> /* Initialize retlen, in case of early exit */
> *retlen = 0;
>
> - /* Do not allow writes past end of device */
> - if (unlikely((to + len) > mtd->size)) {
> - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob:
> Attempt write to past end of device\n");
> + if (mode == MTD_OOB_AUTO)
> + oobsize = this->ecclayout->oobavail;
> + else
> + oobsize = mtd->oobsize;
> +
> + column = to & (mtd->oobsize - 1);
> +
> + if (unlikely(column >= oobsize)) {
> + DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob:
> Attempted to start write outside oob\n");
> + return -EINVAL;
> + }
> +
> + /* Do not allow reads past end of device */
> + if (unlikely(to >= mtd->size ||
> + len > ((mtd->size >> this->page_shift) -
> + (to >> this->page_shift)) * oobsize)) {
> + DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob:
> Attempted to write past
> +end of device\n");
> return -EINVAL;
> }
>
> @@ -1092,18 +1212,19 @@ static int onenand_do_write_oob(struct m
>
> /* Loop until all data write */
> while (written < len) {
> - int thislen = min_t(int, mtd->oobsize, len - written);
> + int thislen = min_t(int, oobsize, len - written);
>
> cond_resched();
>
> - column = to & (mtd->oobsize - 1);
> -
> this->command(mtd, ONENAND_CMD_BUFFERRAM, to,
> mtd->oobsize);
>
> /* We send data to spare ram with oobsize
> * to prevent byte access */
> memset(this->page_buf, 0xff, mtd->oobsize);
> - memcpy(this->page_buf + column, buf, thislen);
> + if (mode == MTD_OOB_AUTO)
> + onenand_fill_auto_oob(mtd,
> this->page_buf, buf, column, &thislen);
> + else
> + memcpy(this->page_buf + column, buf, thislen);
> this->write_bufferram(mtd, ONENAND_SPARERAM,
> this->page_buf, 0, mtd->oobsize);
>
> this->command(mtd, ONENAND_CMD_PROGOOB, to,
> mtd->oobsize); @@ -1112,11 +1233,11 @@ static int
> onenand_do_write_oob(struct m
>
> ret = this->wait(mtd, FL_WRITING);
> if (ret) {
> - DEBUG(MTD_DEBUG_LEVEL0,
> "onenand_write_oob: write filaed %d\n", ret);
> + DEBUG(MTD_DEBUG_LEVEL0,
> "onenand_write_oob: write failed %d\n",
> +ret);
> goto out;
> }
>
> - ret = onenand_verify_oob(mtd, buf, to, thislen);
> + ret = onenand_verify_oob(mtd, this->page_buf, to);
> if (ret) {
> DEBUG(MTD_DEBUG_LEVEL0,
> "onenand_write_oob: verify failed %d\n", ret);
> goto out;
> @@ -1127,8 +1248,9 @@ static int onenand_do_write_oob(struct m
> if (written == len)
> break;
>
> - to += thislen;
> + to += mtd->writesize;
> buf += thislen;
> + column = 0;
> }
>
> out:
> @@ -1149,10 +1271,18 @@ out:
> static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
> struct mtd_oob_ops *ops)
> {
> - BUG_ON(ops->mode != MTD_OOB_PLACE);
> -
> + switch (ops->mode)
> + {
> + case MTD_OOB_PLACE:
> + case MTD_OOB_AUTO:
> + break;
> + case MTD_OOB_RAW:
> + return -EINVAL; /* Not implemented yet */
> + default:
> + return -EINVAL;
> + }
> return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
> - &ops->oobretlen, ops->oobbuf);
> + &ops->oobretlen,
> ops->oobbuf, ops->mode);
> }
>
> /**
> @@ -1318,7 +1448,7 @@ static int onenand_default_block_markbad
>
> /* We write two bytes, so we dont have to mess with
> 16 bit access */
> ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
> - return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf);
> + return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf,
> + MTD_OOB_PLACE);
> }
>
> /**
> @@ -1612,7 +1742,7 @@ static int do_otp_lock(struct mtd_info *
> this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
> this->wait(mtd, FL_OTPING);
>
> - ret = onenand_do_write_oob(mtd, from, len, retlen, buf);
> + ret = onenand_do_write_oob(mtd, from, len, retlen, buf,
> +MTD_OOB_PLACE);
>
> /* Exit OTP access mode */
> this->command(mtd, ONENAND_CMD_RESET, 0, 0); @@ -2020,6
> +2150,7 @@ static void onenand_resume(struct mtd_in
> */
> int onenand_scan(struct mtd_info *mtd, int maxchips) {
> + int i;
> struct onenand_chip *this = mtd->priv;
>
> if (!this->read_word)
> @@ -2091,6 +2222,16 @@ int onenand_scan(struct mtd_info *mtd, i
> }
>
> this->subpagesize = mtd->writesize >> mtd->subpage_sft;
> +
> + /*
> + * The number of bytes available for a client to place data into
> + * the out of band area
> + */
> + this->ecclayout->oobavail = 0;
> + for (i = 0; this->ecclayout->oobfree[i].length; i++)
> + this->ecclayout->oobavail +=
> + this->ecclayout->oobfree[i].length;
> +
> mtd->ecclayout = this->ecclayout;
>
> /* Fill in remaining MTD driver data */ diff --git
> a/drivers/mtd/onenand/onenand_bbt.c
> b/drivers/mtd/onenand/onenand_bbt.c
> index aa46b7f..acea9a1 100644
> --- a/drivers/mtd/onenand/onenand_bbt.c
> +++ b/drivers/mtd/onenand/onenand_bbt.c
> @@ -18,7 +18,7 @@ #include <linux/mtd/onenand.h> #include
> <linux/mtd/compatmac.h>
>
> extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t
> from, size_t len,
> - size_t *retlen, u_char *buf);
> + size_t *retlen, u_char *buf,
> mtd_oob_mode_t mode);
>
> /**
> * check_short_pattern - [GENERIC] check if a pattern is in
> the buffer @@ -91,7 +91,7 @@ static int create_bbt(struct mtd_info *m
> /* No need to read pages fully,
> * just read required OOB bytes */
> ret = onenand_do_read_oob(mtd, from + j
> * mtd->writesize + bd->offs,
> - readlen,
> &retlen, &buf[0]);
> + readlen,
> &retlen, &buf[0], MTD_OOB_PLACE);
>
> /* If it is a initial bad block, just
> ignore it */
> if (ret && !(ret & ONENAND_CTRL_LOAD))
> --
> 1.4.3
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-band data
2007-01-30 6:08 ` [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-band data Kyungmin Park
@ 2007-01-31 8:45 ` Adrian Hunter
2007-01-31 15:42 ` Adrian Hunter
1 sibling, 0 replies; 15+ messages in thread
From: Adrian Hunter @ 2007-01-31 8:45 UTC (permalink / raw)
To: linux-mtd
ext Kyungmin Park wrote:
> It looks good except some minor error condition handling and type mismatch.
Yes the original version of the patch did not have enough validation. When I
added the validation, those conditions could not happen anymore, so I have
now taken them out.
I have made the changes. The new patch is below.
> Another interest things are nand is not passed in this test program. :)
Test 2 does unaligned writes and reads of odd sizes. Presumably nand won't
accept that.
>From b0a39f5eb3151fad2605f96b8032828586162bfd Mon Sep 17 00:00:00 2001
From: Adrian Hunter <ext-adrian.hunter@nokia.com>
Date: Wed, 31 Jan 2007 10:24:43 +0200
Subject: [MTD] OneNAND: Add support for auto-placement of out-of-band data
Enable the use of oob operation mode MTD_OOB_AUTO with OneNAND.
Note that MTD_OOB_RAW is still not supported.
Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
---
drivers/mtd/onenand/onenand_base.c | 204 +++++++++++++++++++++++++++++------
drivers/mtd/onenand/onenand_bbt.c | 4 +-
2 files changed, 171 insertions(+), 37 deletions(-)
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 65acb85..a04ddcb 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -787,20 +787,60 @@ static int onenand_read(struct mtd_info
}
/**
+ * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param buf destination address
+ * @param column oob offset to read from
+ * @param thislen oob length to read
+ */
+static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
+ int thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int readcol = column;
+ int readend = column + thislen;
+ int lastgap = 0;
+ uint8_t *oob_buf = this->page_buf + mtd->writesize;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (readcol >= lastgap)
+ readcol += free->offset - lastgap;
+ if (readend >= lastgap)
+ readend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf + readcol,
+ readcol, readend - readcol);
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < readend && free_end > readcol) {
+ int st = max_t(int,free->offset,readcol);
+ int ed = min_t(int,free_end,readend);
+ int n = ed - st;
+ memcpy(buf, oob_buf + st, n);
+ buf += n;
+ }
+ }
+ return 0;
+}
+
+/**
* onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band
* @param mtd MTD device structure
* @param from offset to read from
* @param len number of bytes to read
* @param retlen pointer to variable to store the number of read bytes
* @param buf the databuffer to put data
+ * @param mode operation mode
*
* OneNAND read out-of-band data from the spare area
*/
int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
- int read = 0, thislen, column;
+ int read = 0, thislen, column, oobsize;
int ret = 0;
DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
@@ -808,21 +848,33 @@ int onenand_do_read_oob(struct mtd_info
/* Initialize return length value */
*retlen = 0;
+ if (mode == MTD_OOB_AUTO)
+ oobsize = this->ecclayout->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = from & (mtd->oobsize - 1);
+
+ if (unlikely(column >= oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to start read outside oob\n");
+ return -EINVAL;
+ }
+
/* Do not allow reads past end of device */
- if (unlikely((from + len) > mtd->size)) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n");
+ if (unlikely(from >= mtd->size ||
+ len > ((mtd->size >> this->page_shift) -
+ (from >> this->page_shift)) * oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to read beyond end of device\n");
return -EINVAL;
}
/* Grab the lock and see if the device is available */
onenand_get_device(mtd, FL_READING);
- column = from & (mtd->oobsize - 1);
-
while (read < len) {
cond_resched();
- thislen = mtd->oobsize - column;
+ thislen = oobsize - column;
thislen = min_t(int, thislen, len);
this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
@@ -832,7 +884,10 @@ int onenand_do_read_oob(struct mtd_info
ret = this->wait(mtd, FL_READING);
/* First copy data and check return value for ECC handling */
- this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_transfer_auto_oob(mtd, buf, column, thislen);
+ else
+ this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret);
@@ -871,10 +926,18 @@ out:
static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
@@ -883,14 +946,12 @@ #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
* @param mtd MTD device structure
* @param buf the databuffer to verify
* @param to offset to read from
- * @param len number of bytes to read and compare
*
*/
-static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to, int len)
+static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
{
struct onenand_chip *this = mtd->priv;
- char *readp = this->page_buf;
- int column = to & (mtd->oobsize - 1);
+ char *readp = this->page_buf + mtd->writesize;
int status, i;
this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
@@ -899,9 +960,8 @@ static int onenand_verify_oob(struct mtd
if (status)
return status;
- this->read_bufferram(mtd, ONENAND_SPARERAM, readp, column, len);
-
- for(i = 0; i < len; i++)
+ this->read_bufferram(mtd, ONENAND_SPARERAM, readp, 0, mtd->oobsize);
+ for(i = 0; i < mtd->oobsize; i++)
if (buf[i] != 0xFF && buf[i] != readp[i])
return -EBADMSG;
@@ -1060,20 +1120,59 @@ static int onenand_write(struct mtd_info
}
/**
+ * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param oob_buf oob buffer
+ * @param buf source address
+ * @param column oob offset to write to
+ * @param thislen oob length to write
+ */
+static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
+ const u_char *buf, int column, int thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int writecol = column;
+ int writeend = column + thislen;
+ int lastgap = 0;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (writecol >= lastgap)
+ writecol += free->offset - lastgap;
+ if (writeend >= lastgap)
+ writeend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ writeend = mtd->oobsize;
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < writeend && free_end > writecol) {
+ int st = max_t(int,free->offset,writecol);
+ int ed = min_t(int,free_end,writeend);
+ int n = ed - st;
+ memcpy(oob_buf + st, buf, n);
+ buf += n;
+ }
+ }
+ return 0;
+}
+
+/**
* onenand_do_write_oob - [Internal] OneNAND write out-of-band
* @param mtd MTD device structure
* @param to offset to write to
* @param len number of bytes to write
* @param retlen pointer to variable to store the number of written bytes
* @param buf the data to write
+ * @param mode operation mode
*
* OneNAND write out-of-band
*/
static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
+ size_t *retlen, const u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
- int column, ret = 0;
+ int column, ret = 0, oobsize;
int written = 0;
DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
@@ -1081,9 +1180,23 @@ static int onenand_do_write_oob(struct m
/* Initialize retlen, in case of early exit */
*retlen = 0;
- /* Do not allow writes past end of device */
- if (unlikely((to + len) > mtd->size)) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n");
+ if (mode == MTD_OOB_AUTO)
+ oobsize = this->ecclayout->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = to & (mtd->oobsize - 1);
+
+ if (unlikely(column >= oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to start write outside oob\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow reads past end of device */
+ if (unlikely(to >= mtd->size ||
+ len > ((mtd->size >> this->page_shift) -
+ (to >> this->page_shift)) * oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to write past end of device\n");
return -EINVAL;
}
@@ -1092,18 +1205,19 @@ static int onenand_do_write_oob(struct m
/* Loop until all data write */
while (written < len) {
- int thislen = min_t(int, mtd->oobsize, len - written);
+ int thislen = min_t(int, oobsize, len - written);
cond_resched();
- column = to & (mtd->oobsize - 1);
-
this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
/* We send data to spare ram with oobsize
* to prevent byte access */
memset(this->page_buf, 0xff, mtd->oobsize);
- memcpy(this->page_buf + column, buf, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_fill_auto_oob(mtd, this->page_buf, buf, column, thislen);
+ else
+ memcpy(this->page_buf + column, buf, thislen);
this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize);
this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
@@ -1112,11 +1226,11 @@ static int onenand_do_write_oob(struct m
ret = this->wait(mtd, FL_WRITING);
if (ret) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write filaed %d\n", ret);
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write failed %d\n", ret);
goto out;
}
- ret = onenand_verify_oob(mtd, buf, to, thislen);
+ ret = onenand_verify_oob(mtd, this->page_buf, to);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret);
goto out;
@@ -1127,8 +1241,9 @@ static int onenand_do_write_oob(struct m
if (written == len)
break;
- to += thislen;
+ to += mtd->writesize;
buf += thislen;
+ column = 0;
}
out:
@@ -1149,10 +1264,18 @@ out:
static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
/**
@@ -1318,7 +1441,7 @@ static int onenand_default_block_markbad
/* We write two bytes, so we dont have to mess with 16 bit access */
ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
- return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf);
+ return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE);
}
/**
@@ -1612,7 +1735,7 @@ static int do_otp_lock(struct mtd_info *
this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
this->wait(mtd, FL_OTPING);
- ret = onenand_do_write_oob(mtd, from, len, retlen, buf);
+ ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE);
/* Exit OTP access mode */
this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -2020,6 +2143,7 @@ static void onenand_resume(struct mtd_in
*/
int onenand_scan(struct mtd_info *mtd, int maxchips)
{
+ int i;
struct onenand_chip *this = mtd->priv;
if (!this->read_word)
@@ -2091,6 +2215,16 @@ int onenand_scan(struct mtd_info *mtd, i
}
this->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+ /*
+ * The number of bytes available for a client to place data into
+ * the out of band area
+ */
+ this->ecclayout->oobavail = 0;
+ for (i = 0; this->ecclayout->oobfree[i].length; i++)
+ this->ecclayout->oobavail +=
+ this->ecclayout->oobfree[i].length;
+
mtd->ecclayout = this->ecclayout;
/* Fill in remaining MTD driver data */
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
index aa46b7f..acea9a1 100644
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -18,7 +18,7 @@ #include <linux/mtd/onenand.h>
#include <linux/mtd/compatmac.h>
extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf);
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode);
/**
* check_short_pattern - [GENERIC] check if a pattern is in the buffer
@@ -91,7 +91,7 @@ static int create_bbt(struct mtd_info *m
/* No need to read pages fully,
* just read required OOB bytes */
ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs,
- readlen, &retlen, &buf[0]);
+ readlen, &retlen, &buf[0], MTD_OOB_PLACE);
/* If it is a initial bad block, just ignore it */
if (ret && !(ret & ONENAND_CTRL_LOAD))
--
1.4.3
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-band data
2007-01-30 6:08 ` [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-band data Kyungmin Park
2007-01-31 8:45 ` Adrian Hunter
@ 2007-01-31 15:42 ` Adrian Hunter
2007-02-01 2:26 ` [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata Kyungmin Park
2007-02-01 3:13 ` [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata Kyungmin Park
1 sibling, 2 replies; 15+ messages in thread
From: Adrian Hunter @ 2007-01-31 15:42 UTC (permalink / raw)
To: linux-mtd
Just noticed another bug. Here is better version.
>From f93c480e9ccd6a72abbc3fed1ab71dc4244b0229 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <ext-adrian.hunter@nokia.com>
Date: Wed, 31 Jan 2007 17:19:28 +0200
Subject: [MTD] OneNAND: Add support for auto-placement of out-of-band data
Enable the use of oob operation mode MTD_OOB_AUTO with OneNAND.
Note that MTD_OOB_RAW is still not supported.
Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
---
drivers/mtd/onenand/onenand_base.c | 204 +++++++++++++++++++++++++++++------
drivers/mtd/onenand/onenand_bbt.c | 4 +-
2 files changed, 171 insertions(+), 37 deletions(-)
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index daf2989..67efbc7 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -787,20 +787,60 @@ static int onenand_read(struct mtd_info
}
/**
+ * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param buf destination address
+ * @param column oob offset to read from
+ * @param thislen oob length to read
+ */
+static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
+ int thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int readcol = column;
+ int readend = column + thislen;
+ int lastgap = 0;
+ uint8_t *oob_buf = this->page_buf + mtd->writesize;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (readcol >= lastgap)
+ readcol += free->offset - lastgap;
+ if (readend >= lastgap)
+ readend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf + readcol,
+ readcol, readend - readcol);
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < readend && free_end > readcol) {
+ int st = max_t(int,free->offset,readcol);
+ int ed = min_t(int,free_end,readend);
+ int n = ed - st;
+ memcpy(buf, oob_buf + st, n);
+ buf += n;
+ }
+ }
+ return 0;
+}
+
+/**
* onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band
* @param mtd MTD device structure
* @param from offset to read from
* @param len number of bytes to read
* @param retlen pointer to variable to store the number of read bytes
* @param buf the databuffer to put data
+ * @param mode operation mode
*
* OneNAND read out-of-band data from the spare area
*/
int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
- int read = 0, thislen, column;
+ int read = 0, thislen, column, oobsize;
int ret = 0;
DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
@@ -808,21 +848,33 @@ int onenand_do_read_oob(struct mtd_info
/* Initialize return length value */
*retlen = 0;
+ if (mode == MTD_OOB_AUTO)
+ oobsize = this->ecclayout->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = from & (mtd->oobsize - 1);
+
+ if (unlikely(column >= oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to start read outside oob\n");
+ return -EINVAL;
+ }
+
/* Do not allow reads past end of device */
- if (unlikely((from + len) > mtd->size)) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n");
+ if (unlikely(from >= mtd->size ||
+ column + len > ((mtd->size >> this->page_shift) -
+ (from >> this->page_shift)) * oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to read beyond end of device\n");
return -EINVAL;
}
/* Grab the lock and see if the device is available */
onenand_get_device(mtd, FL_READING);
- column = from & (mtd->oobsize - 1);
-
while (read < len) {
cond_resched();
- thislen = mtd->oobsize - column;
+ thislen = oobsize - column;
thislen = min_t(int, thislen, len);
this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
@@ -832,7 +884,10 @@ int onenand_do_read_oob(struct mtd_info
ret = this->wait(mtd, FL_READING);
/* First copy data and check return value for ECC handling */
- this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_transfer_auto_oob(mtd, buf, column, thislen);
+ else
+ this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret);
@@ -871,10 +926,18 @@ out:
static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
@@ -883,14 +946,12 @@ #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
* @param mtd MTD device structure
* @param buf the databuffer to verify
* @param to offset to read from
- * @param len number of bytes to read and compare
*
*/
-static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to, int len)
+static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
{
struct onenand_chip *this = mtd->priv;
- char *readp = this->page_buf;
- int column = to & (mtd->oobsize - 1);
+ char *readp = this->page_buf + mtd->writesize;
int status, i;
this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
@@ -899,9 +960,8 @@ static int onenand_verify_oob(struct mtd
if (status)
return status;
- this->read_bufferram(mtd, ONENAND_SPARERAM, readp, column, len);
-
- for(i = 0; i < len; i++)
+ this->read_bufferram(mtd, ONENAND_SPARERAM, readp, 0, mtd->oobsize);
+ for(i = 0; i < mtd->oobsize; i++)
if (buf[i] != 0xFF && buf[i] != readp[i])
return -EBADMSG;
@@ -1060,20 +1120,59 @@ static int onenand_write(struct mtd_info
}
/**
+ * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param oob_buf oob buffer
+ * @param buf source address
+ * @param column oob offset to write to
+ * @param thislen oob length to write
+ */
+static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
+ const u_char *buf, int column, int thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct nand_oobfree *free;
+ int writecol = column;
+ int writeend = column + thislen;
+ int lastgap = 0;
+
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ if (writecol >= lastgap)
+ writecol += free->offset - lastgap;
+ if (writeend >= lastgap)
+ writeend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ writeend = mtd->oobsize;
+ for (free = this->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < writeend && free_end > writecol) {
+ int st = max_t(int,free->offset,writecol);
+ int ed = min_t(int,free_end,writeend);
+ int n = ed - st;
+ memcpy(oob_buf + st, buf, n);
+ buf += n;
+ }
+ }
+ return 0;
+}
+
+/**
* onenand_do_write_oob - [Internal] OneNAND write out-of-band
* @param mtd MTD device structure
* @param to offset to write to
* @param len number of bytes to write
* @param retlen pointer to variable to store the number of written bytes
* @param buf the data to write
+ * @param mode operation mode
*
* OneNAND write out-of-band
*/
static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
+ size_t *retlen, const u_char *buf, mtd_oob_mode_t mode)
{
struct onenand_chip *this = mtd->priv;
- int column, ret = 0;
+ int column, ret = 0, oobsize;
int written = 0;
DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
@@ -1081,9 +1180,23 @@ static int onenand_do_write_oob(struct m
/* Initialize retlen, in case of early exit */
*retlen = 0;
- /* Do not allow writes past end of device */
- if (unlikely((to + len) > mtd->size)) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n");
+ if (mode == MTD_OOB_AUTO)
+ oobsize = this->ecclayout->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = to & (mtd->oobsize - 1);
+
+ if (unlikely(column >= oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to start write outside oob\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow reads past end of device */
+ if (unlikely(to >= mtd->size ||
+ column + len > ((mtd->size >> this->page_shift) -
+ (to >> this->page_shift)) * oobsize)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to write past end of device\n");
return -EINVAL;
}
@@ -1092,18 +1205,19 @@ static int onenand_do_write_oob(struct m
/* Loop until all data write */
while (written < len) {
- int thislen = min_t(int, mtd->oobsize, len - written);
+ int thislen = min_t(int, oobsize, len - written);
cond_resched();
- column = to & (mtd->oobsize - 1);
-
this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
/* We send data to spare ram with oobsize
* to prevent byte access */
memset(this->page_buf, 0xff, mtd->oobsize);
- memcpy(this->page_buf + column, buf, thislen);
+ if (mode == MTD_OOB_AUTO)
+ onenand_fill_auto_oob(mtd, this->page_buf, buf, column, thislen);
+ else
+ memcpy(this->page_buf + column, buf, thislen);
this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize);
this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
@@ -1112,11 +1226,11 @@ static int onenand_do_write_oob(struct m
ret = this->wait(mtd, FL_WRITING);
if (ret) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write filaed %d\n", ret);
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write failed %d\n", ret);
goto out;
}
- ret = onenand_verify_oob(mtd, buf, to, thislen);
+ ret = onenand_verify_oob(mtd, this->page_buf, to);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret);
goto out;
@@ -1127,8 +1241,9 @@ static int onenand_do_write_oob(struct m
if (written == len)
break;
- to += thislen;
+ to += mtd->writesize;
buf += thislen;
+ column = 0;
}
out:
@@ -1149,10 +1264,18 @@ out:
static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
- BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+ switch (ops->mode)
+ {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ return -EINVAL; /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
- &ops->oobretlen, ops->oobbuf);
+ &ops->oobretlen, ops->oobbuf, ops->mode);
}
/**
@@ -1318,7 +1441,7 @@ static int onenand_default_block_markbad
/* We write two bytes, so we dont have to mess with 16 bit access */
ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
- return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf);
+ return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE);
}
/**
@@ -1612,7 +1735,7 @@ static int do_otp_lock(struct mtd_info *
this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
this->wait(mtd, FL_OTPING);
- ret = onenand_do_write_oob(mtd, from, len, retlen, buf);
+ ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE);
/* Exit OTP access mode */
this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -2019,6 +2142,7 @@ static void onenand_resume(struct mtd_in
*/
int onenand_scan(struct mtd_info *mtd, int maxchips)
{
+ int i;
struct onenand_chip *this = mtd->priv;
if (!this->read_word)
@@ -2090,6 +2214,16 @@ int onenand_scan(struct mtd_info *mtd, i
}
this->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+ /*
+ * The number of bytes available for a client to place data into
+ * the out of band area
+ */
+ this->ecclayout->oobavail = 0;
+ for (i = 0; this->ecclayout->oobfree[i].length; i++)
+ this->ecclayout->oobavail +=
+ this->ecclayout->oobfree[i].length;
+
mtd->ecclayout = this->ecclayout;
/* Fill in remaining MTD driver data */
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
index aa46b7f..acea9a1 100644
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -18,7 +18,7 @@ #include <linux/mtd/onenand.h>
#include <linux/mtd/compatmac.h>
extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf);
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode);
/**
* check_short_pattern - [GENERIC] check if a pattern is in the buffer
@@ -91,7 +91,7 @@ static int create_bbt(struct mtd_info *m
/* No need to read pages fully,
* just read required OOB bytes */
ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs,
- readlen, &retlen, &buf[0]);
+ readlen, &retlen, &buf[0], MTD_OOB_PLACE);
/* If it is a initial bad block, just ignore it */
if (ret && !(ret & ONENAND_CTRL_LOAD))
--
1.4.3
^ permalink raw reply related [flat|nested] 15+ messages in thread
* RE: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata
2007-01-31 15:42 ` Adrian Hunter
@ 2007-02-01 2:26 ` Kyungmin Park
2007-02-01 7:43 ` Adrian Hunter
2007-02-01 3:13 ` [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata Kyungmin Park
1 sibling, 1 reply; 15+ messages in thread
From: Kyungmin Park @ 2007-02-01 2:26 UTC (permalink / raw)
To: 'Adrian Hunter', linux-mtd
I'll push it.
But with JFFS2 patch, there's one bug (see below)
I'l fix it and commit it.
Thank you,
Kyungmin Park
> +static int onenand_fill_auto_oob(struct mtd_info *mtd,
> u_char *oob_buf,
> + const u_char *buf, int
> column, int thislen) {
> + struct onenand_chip *this = mtd->priv;
> + struct nand_oobfree *free;
> + int writecol = column;
> + int writeend = column + thislen;
> + int lastgap = 0;
> +
> + for (free = this->ecclayout->oobfree; free->length; ++free) {
> + if (writecol >= lastgap)
> + writecol += free->offset - lastgap;
> + if (writeend >= lastgap)
> + writeend += free->offset - lastgap;
> + lastgap = free->offset + free->length;
> + }
> + writeend = mtd->oobsize;
We don't re-assign the 'writeend'. It cause the block is not clean. Since
JFFS2 send only 12 bytes but onenand writes until oobsize, actually 20
bytes with zero.
So this statement will be removed.
> + for (free = this->ecclayout->oobfree; free->length; ++free) {
> + int free_end = free->offset + free->length;
> + if (free->offset < writeend && free_end > writecol) {
> + int st = max_t(int,free->offset,writecol);
> + int ed = min_t(int,free_end,writeend);
> + int n = ed - st;
> + memcpy(oob_buf + st, buf, n);
> + buf += n;
> + }
> + }
> + return 0;
> -----Original Message-----
> From: linux-mtd-bounces@lists.infradead.org
> [mailto:linux-mtd-bounces@lists.infradead.org] On Behalf Of
> Adrian Hunter
> Sent: Thursday, February 01, 2007 12:42 AM
> To: linux-mtd@lists.infradead.org
> Subject: Re: [PATCH] [MTD] OneNAND: Add support for
> auto-placementofout-of-banddata
>
> Just noticed another bug. Here is better version.
>
>
> >From f93c480e9ccd6a72abbc3fed1ab71dc4244b0229 Mon Sep 17
> 00:00:00 2001
> From: Adrian Hunter <ext-adrian.hunter@nokia.com>
> Date: Wed, 31 Jan 2007 17:19:28 +0200
> Subject: [MTD] OneNAND: Add support for auto-placement of
> out-of-band data
>
> Enable the use of oob operation mode MTD_OOB_AUTO with OneNAND.
> Note that MTD_OOB_RAW is still not supported.
>
> Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
> ---
^ permalink raw reply [flat|nested] 15+ messages in thread
* RE: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata
2007-01-31 15:42 ` Adrian Hunter
2007-02-01 2:26 ` [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata Kyungmin Park
@ 2007-02-01 3:13 ` Kyungmin Park
1 sibling, 0 replies; 15+ messages in thread
From: Kyungmin Park @ 2007-02-01 3:13 UTC (permalink / raw)
To: 'Adrian Hunter', linux-mtd
> /**
> + * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
> + * @param mtd MTD device structure
> + * @param buf destination address
> + * @param column oob offset to read from
> + * @param thislen oob length to read
> + */
> +static int onenand_transfer_auto_oob(struct mtd_info *mtd,
> uint8_t *buf, int column,
> + int thislen)
> +{
> + struct onenand_chip *this = mtd->priv;
> + struct nand_oobfree *free;
> + int readcol = column;
> + int readend = column + thislen;
> + int lastgap = 0;
> + uint8_t *oob_buf = this->page_buf + mtd->writesize;
> +
> + for (free = this->ecclayout->oobfree; free->length; ++free) {
> + if (readcol >= lastgap)
> + readcol += free->offset - lastgap;
> + if (readend >= lastgap)
> + readend += free->offset - lastgap;
> + lastgap = free->offset + free->length;
> + }
> + this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf + readcol,
> + readcol, readend - readcol);
How about to read full oobsize? since 32 bytes memcpy is optimized.
Now there are two cases.
case 1: readcol = 2, readend = 36, so read length is 34
case 2: readcol = 2, readend = 64, so read length is 62
Buf if we change to colume = 0, length = 64. we only do 2 loop in memcpy.
this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
The contents of buffer are handled correctly (see below)
> + for (free = this->ecclayout->oobfree; free->length; ++free) {
> + int free_end = free->offset + free->length;
> + if (free->offset < readend && free_end > readcol) {
> + int st = max_t(int,free->offset,readcol);
> + int ed = min_t(int,free_end,readend);
> + int n = ed - st;
> + memcpy(buf, oob_buf + st, n);
> + buf += n;
> + }
> + }
> + return 0;
> +}
> +
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata
2007-02-01 2:26 ` [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata Kyungmin Park
@ 2007-02-01 7:43 ` Adrian Hunter
2007-02-01 8:02 ` Adrian Hunter
0 siblings, 1 reply; 15+ messages in thread
From: Adrian Hunter @ 2007-02-01 7:43 UTC (permalink / raw)
To: linux-mtd
ext Kyungmin Park wrote:
>> + writeend = mtd->oobsize;
>
> We don't re-assign the 'writeend'. It cause the block is not clean. Since
> JFFS2 send only 12 bytes but onenand writes until oobsize, actually 20
> bytes with zero.
> So this statement will be removed.
Yes, that was a typo. The line should have been deleted when the previous
line was deleted.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata
2007-02-01 7:43 ` Adrian Hunter
@ 2007-02-01 8:02 ` Adrian Hunter
2007-02-01 8:50 ` [PATCH] [MTD] OneNAND: Add support forauto-placementofout-of-banddata Kyungmin Park
0 siblings, 1 reply; 15+ messages in thread
From: Adrian Hunter @ 2007-02-01 8:02 UTC (permalink / raw)
To: linux-mtd
ext Adrian Hunter wrote:
> ext Kyungmin Park wrote:
>>> + writeend = mtd->oobsize;
>> We don't re-assign the 'writeend'. It cause the block is not clean. Since
>> JFFS2 send only 12 bytes but onenand writes until oobsize, actually 20
>> bytes with zero.
>> So this statement will be removed.
>
> Yes, that was a typo. The line should have been deleted when the previous
> line was deleted.
The statement still seems to be in the onenand tree. Here is a patch to remove it.
>From 397419987d744ed971c74cfabae2eeac705f2fe0 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <ext-adrian.hunter@nokia.com>
Date: Thu, 1 Feb 2007 09:59:41 +0200
Subject: [MTD] OneNAND: Remove line of code that was meant to be deleted
Iterations of the patch to add oob auto-placement support to
OneNAND left a line of code that was meant to have been deleted.
Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
---
drivers/mtd/onenand/onenand_base.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 67efbc7..9ec28bb 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -1143,7 +1143,6 @@ static int onenand_fill_auto_oob(struct
writeend += free->offset - lastgap;
lastgap = free->offset + free->length;
}
- writeend = mtd->oobsize;
for (free = this->ecclayout->oobfree; free->length; ++free) {
int free_end = free->offset + free->length;
if (free->offset < writeend && free_end > writecol) {
--
1.4.3
^ permalink raw reply related [flat|nested] 15+ messages in thread
* RE: [PATCH] [MTD] OneNAND: Add support forauto-placementofout-of-banddata
2007-02-01 8:02 ` Adrian Hunter
@ 2007-02-01 8:50 ` Kyungmin Park
2007-02-01 9:39 ` Adrian Hunter
0 siblings, 1 reply; 15+ messages in thread
From: Kyungmin Park @ 2007-02-01 8:50 UTC (permalink / raw)
To: 'Adrian Hunter', linux-mtd
How about the second mail about onenand_transfer_auto_oob
I will commit it both
Thank you,
Kyungmin Park
>
>
> >From 397419987d744ed971c74cfabae2eeac705f2fe0 Mon Sep 17
> 00:00:00 2001
> From: Adrian Hunter <ext-adrian.hunter@nokia.com>
> Date: Thu, 1 Feb 2007 09:59:41 +0200
> Subject: [MTD] OneNAND: Remove line of code that was meant to
> be deleted
>
> Iterations of the patch to add oob auto-placement support to
> OneNAND left a line of code that was meant to have been deleted.
>
> Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
> ---
> drivers/mtd/onenand/onenand_base.c | 1 -
> 1 files changed, 0 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mtd/onenand/onenand_base.c
> b/drivers/mtd/onenand/onenand_base.c
> index 67efbc7..9ec28bb 100644
> --- a/drivers/mtd/onenand/onenand_base.c
> +++ b/drivers/mtd/onenand/onenand_base.c
> @@ -1143,7 +1143,6 @@ static int onenand_fill_auto_oob(struct
> writeend += free->offset - lastgap;
> lastgap = free->offset + free->length;
> }
> - writeend = mtd->oobsize;
> for (free = this->ecclayout->oobfree; free->length; ++free) {
> int free_end = free->offset + free->length;
> if (free->offset < writeend && free_end > writecol) {
> --
> 1.4.3
>
>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] [MTD] OneNAND: Add support forauto-placementofout-of-banddata
2007-02-01 8:50 ` [PATCH] [MTD] OneNAND: Add support forauto-placementofout-of-banddata Kyungmin Park
@ 2007-02-01 9:39 ` Adrian Hunter
0 siblings, 0 replies; 15+ messages in thread
From: Adrian Hunter @ 2007-02-01 9:39 UTC (permalink / raw)
To: linux-mtd
ext Kyungmin Park wrote:
> How about the second mail about onenand_transfer_auto_oob
Sorry, I thought you had done it already.
I will leave it to you.
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2007-02-01 9:42 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-01-26 15:13 [PATCH] [MTD] OneNAND: Add support for auto-placement of out-of-band data Adrian Hunter
2007-01-26 15:44 ` Adrian Hunter
2007-01-29 6:02 ` [PATCH] [MTD] OneNAND: Add support for auto-placement ofout-of-band data Kyungmin Park
2007-01-29 8:13 ` Adrian Hunter
2007-01-29 15:36 ` Adrian Hunter
2007-01-30 6:08 ` [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-band data Kyungmin Park
2007-01-31 8:45 ` Adrian Hunter
2007-01-31 15:42 ` Adrian Hunter
2007-02-01 2:26 ` [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata Kyungmin Park
2007-02-01 7:43 ` Adrian Hunter
2007-02-01 8:02 ` Adrian Hunter
2007-02-01 8:50 ` [PATCH] [MTD] OneNAND: Add support forauto-placementofout-of-banddata Kyungmin Park
2007-02-01 9:39 ` Adrian Hunter
2007-02-01 3:13 ` [PATCH] [MTD] OneNAND: Add support for auto-placementofout-of-banddata Kyungmin Park
2007-01-29 15:37 ` [PATCH] [MTD] OneNAND: Add support for auto-placement ofout-of-band data Adrian Hunter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox