[MTD] [OneNAND] main read/write ops support for yaffs2
Now we can use yaffs2 on OneNAND Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: David Woodhouse <dwmw2@infradead.org>
This commit is contained in:
parent
12f77c9eed
commit
d15057b703
1 changed files with 281 additions and 164 deletions
|
@ -763,31 +763,83 @@ static void onenand_release_device(struct mtd_info *mtd)
|
|||
}
|
||||
|
||||
/**
|
||||
* onenand_read - [MTD Interface] Read data from flash
|
||||
* 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;
|
||||
unsigned int i;
|
||||
uint8_t *oob_buf = this->oob_buf;
|
||||
|
||||
free = this->ecclayout->oobfree;
|
||||
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, 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, 0, mtd->oobsize);
|
||||
free = this->ecclayout->oobfree;
|
||||
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, 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;
|
||||
} else if (column == 0)
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_read_ops - [OneNAND Interface] OneNAND read main and/or 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 ops: oob operation description structure
|
||||
*
|
||||
* Read with ecc
|
||||
*/
|
||||
static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
* OneNAND read main and/or out-of-band data
|
||||
*/
|
||||
static int onenand_read_ops(struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
struct mtd_ecc_stats stats;
|
||||
int read = 0, column;
|
||||
int thislen;
|
||||
size_t len = ops->len;
|
||||
size_t ooblen = ops->ooblen;
|
||||
u_char *buf = ops->datbuf;
|
||||
u_char *oobbuf = ops->oobbuf;
|
||||
int read = 0, column, thislen;
|
||||
int oobread = 0, oobcolumn, thisooblen, oobsize;
|
||||
int ret = 0, boundary = 0;
|
||||
int writesize = this->writesize;
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
|
||||
|
||||
if (ops->mode == MTD_OOB_AUTO)
|
||||
oobsize = this->ecclayout->oobavail;
|
||||
else
|
||||
oobsize = mtd->oobsize;
|
||||
|
||||
oobcolumn = from & (mtd->oobsize - 1);
|
||||
|
||||
/* Do not allow reads past end of device */
|
||||
if ((from + len) > mtd->size) {
|
||||
printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n");
|
||||
*retlen = 0;
|
||||
printk(KERN_ERR "onenand_read_ops: Attempt read beyond end of device\n");
|
||||
ops->retlen = 0;
|
||||
ops->oobretlen = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -832,6 +884,21 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
}
|
||||
/* While load is going, read from last bufferRAM */
|
||||
this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
|
||||
|
||||
/* Read oob area if needed */
|
||||
if (oobbuf) {
|
||||
thisooblen = oobsize - oobcolumn;
|
||||
thisooblen = min_t(int, thisooblen, ooblen - oobread);
|
||||
|
||||
if (ops->mode == MTD_OOB_AUTO)
|
||||
onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
|
||||
else
|
||||
this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
|
||||
oobread += thisooblen;
|
||||
oobbuf += thisooblen;
|
||||
oobcolumn = 0;
|
||||
}
|
||||
|
||||
/* See if we are done */
|
||||
read += thislen;
|
||||
if (read == len)
|
||||
|
@ -857,7 +924,8 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
* fs driver will take care of that, because
|
||||
* retlen == desired len and result == -EBADMSG
|
||||
*/
|
||||
*retlen = read;
|
||||
ops->retlen = read;
|
||||
ops->oobretlen = oobread;
|
||||
|
||||
if (mtd->ecc_stats.failed - stats.failed)
|
||||
return -EBADMSG;
|
||||
|
@ -868,56 +936,11 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
unsigned int i;
|
||||
uint8_t *oob_buf = this->oob_buf;
|
||||
|
||||
free = this->ecclayout->oobfree;
|
||||
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, 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, 0, mtd->oobsize);
|
||||
free = this->ecclayout->oobfree;
|
||||
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, 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;
|
||||
} else if (column == 0)
|
||||
break;
|
||||
}
|
||||
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
|
||||
* @param ops: oob operation description structure
|
||||
*
|
||||
* OneNAND read out-of-band data from the spare area
|
||||
*/
|
||||
|
@ -1007,10 +1030,39 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
}
|
||||
|
||||
/**
|
||||
* onenand_read_oob - [MTD Interface] NAND write data and/or out-of-band
|
||||
* onenand_read - [MTD Interface] Read data from flash
|
||||
* @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
|
||||
*
|
||||
* Read with ecc
|
||||
*/
|
||||
static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct mtd_oob_ops ops = {
|
||||
.len = len,
|
||||
.ooblen = 0,
|
||||
.datbuf = buf,
|
||||
.oobbuf = NULL,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = onenand_read_ops(mtd, from, &ops);
|
||||
*retlen = ops.retlen;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_read_oob - [MTD Interface] Read main and/or out-of-band
|
||||
* @param mtd: MTD device structure
|
||||
* @param from: offset to read from
|
||||
* @param ops: oob operation description structure
|
||||
|
||||
* Read main and/or out-of-band
|
||||
*/
|
||||
static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops)
|
||||
|
@ -1024,6 +1076,10 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ops->datbuf)
|
||||
return onenand_read_ops(mtd, from, ops);
|
||||
|
||||
return onenand_do_read_oob(mtd, from, ops);
|
||||
}
|
||||
|
||||
|
@ -1148,7 +1204,6 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
* @param mtd MTD device structure
|
||||
* @param buf the databuffer to verify
|
||||
* @param to offset to read from
|
||||
*
|
||||
*/
|
||||
static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
|
||||
{
|
||||
|
@ -1176,7 +1231,6 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
|
|||
* @param buf the databuffer to verify
|
||||
* @param addr offset to read from
|
||||
* @param len number of bytes to read and compare
|
||||
*
|
||||
*/
|
||||
static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
|
||||
{
|
||||
|
@ -1221,107 +1275,6 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr,
|
|||
|
||||
#define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0)
|
||||
|
||||
/**
|
||||
* onenand_write - [MTD Interface] write buffer to FLASH
|
||||
* @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
|
||||
*
|
||||
* Write with ECC
|
||||
*/
|
||||
static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
int written = 0;
|
||||
int ret = 0;
|
||||
int column, subpage;
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "onenand_write: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
|
||||
|
||||
/* Initialize retlen, in case of early exit */
|
||||
*retlen = 0;
|
||||
|
||||
/* Do not allow writes past end of device */
|
||||
if (unlikely((to + len) > mtd->size)) {
|
||||
printk(KERN_ERR "onenand_write: Attempt write to past end of device\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Reject writes, which are not page aligned */
|
||||
if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {
|
||||
printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
column = to & (mtd->writesize - 1);
|
||||
|
||||
/* Grab the lock and see if the device is available */
|
||||
onenand_get_device(mtd, FL_WRITING);
|
||||
|
||||
/* Loop until all data write */
|
||||
while (written < len) {
|
||||
int thislen = min_t(int, mtd->writesize - column, len - written);
|
||||
u_char *wbuf = (u_char *) buf;
|
||||
|
||||
cond_resched();
|
||||
|
||||
this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
|
||||
|
||||
/* Partial page write */
|
||||
subpage = thislen < mtd->writesize;
|
||||
if (subpage) {
|
||||
memset(this->page_buf, 0xff, mtd->writesize);
|
||||
memcpy(this->page_buf + column, buf, thislen);
|
||||
wbuf = this->page_buf;
|
||||
}
|
||||
|
||||
this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
|
||||
this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize);
|
||||
|
||||
this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
|
||||
|
||||
ret = this->wait(mtd, FL_WRITING);
|
||||
|
||||
/* In partial page write we don't update bufferram */
|
||||
onenand_update_bufferram(mtd, to, !ret && !subpage);
|
||||
if (ONENAND_IS_2PLANE(this)) {
|
||||
ONENAND_SET_BUFFERRAM1(this);
|
||||
onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
printk(KERN_ERR "onenand_write: write filaed %d\n", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Only check verify write turn on */
|
||||
ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "onenand_write: verify failed %d\n", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
written += thislen;
|
||||
|
||||
if (written == len)
|
||||
break;
|
||||
|
||||
column = 0;
|
||||
to += thislen;
|
||||
buf += thislen;
|
||||
}
|
||||
|
||||
/* Deselect and wake up anyone waiting on the device */
|
||||
onenand_release_device(mtd);
|
||||
|
||||
*retlen = written;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_fill_auto_oob - [Internal] oob auto-placement transfer
|
||||
* @param mtd MTD device structure
|
||||
|
@ -1363,6 +1316,139 @@ static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_write_ops - [OneNAND Interface] write main and/or out-of-band
|
||||
* @param mtd MTD device structure
|
||||
* @param to offset to write to
|
||||
* @param ops oob operation description structure
|
||||
*
|
||||
* Write main and/or oob with ECC
|
||||
*/
|
||||
static int onenand_write_ops(struct mtd_info *mtd, loff_t to,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
int written = 0, column, thislen, subpage;
|
||||
int oobwritten = 0, oobcolumn, thisooblen, oobsize;
|
||||
size_t len = ops->len;
|
||||
size_t ooblen = ops->ooblen;
|
||||
const u_char *buf = ops->datbuf;
|
||||
const u_char *oob = ops->oobbuf;
|
||||
u_char *oobbuf;
|
||||
int ret = 0;
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "onenand_write: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
|
||||
|
||||
/* Initialize retlen, in case of early exit */
|
||||
ops->retlen = 0;
|
||||
ops->oobretlen = 0;
|
||||
|
||||
/* Do not allow writes past end of device */
|
||||
if (unlikely((to + len) > mtd->size)) {
|
||||
printk(KERN_ERR "onenand_write: Attempt write to past end of device\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Reject writes, which are not page aligned */
|
||||
if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {
|
||||
printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ops->mode == MTD_OOB_AUTO)
|
||||
oobsize = this->ecclayout->oobavail;
|
||||
else
|
||||
oobsize = mtd->oobsize;
|
||||
|
||||
oobcolumn = to & (mtd->oobsize - 1);
|
||||
|
||||
column = to & (mtd->writesize - 1);
|
||||
|
||||
/* Grab the lock and see if the device is available */
|
||||
onenand_get_device(mtd, FL_WRITING);
|
||||
|
||||
/* Loop until all data write */
|
||||
while (written < len) {
|
||||
u_char *wbuf = (u_char *) buf;
|
||||
|
||||
thislen = min_t(int, mtd->writesize - column, len - written);
|
||||
thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
|
||||
|
||||
cond_resched();
|
||||
|
||||
this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
|
||||
|
||||
/* Partial page write */
|
||||
subpage = thislen < mtd->writesize;
|
||||
if (subpage) {
|
||||
memset(this->page_buf, 0xff, mtd->writesize);
|
||||
memcpy(this->page_buf + column, buf, thislen);
|
||||
wbuf = this->page_buf;
|
||||
}
|
||||
|
||||
this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
|
||||
|
||||
if (oob) {
|
||||
oobbuf = this->oob_buf;
|
||||
|
||||
/* We send data to spare ram with oobsize
|
||||
* to prevent byte access */
|
||||
memset(oobbuf, 0xff, mtd->oobsize);
|
||||
if (ops->mode == MTD_OOB_AUTO)
|
||||
onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
|
||||
else
|
||||
memcpy(oobbuf + oobcolumn, oob, thisooblen);
|
||||
|
||||
oobwritten += thisooblen;
|
||||
oob += thisooblen;
|
||||
oobcolumn = 0;
|
||||
} else
|
||||
oobbuf = (u_char *) ffchars;
|
||||
|
||||
this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
|
||||
|
||||
this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
|
||||
|
||||
ret = this->wait(mtd, FL_WRITING);
|
||||
|
||||
/* In partial page write we don't update bufferram */
|
||||
onenand_update_bufferram(mtd, to, !ret && !subpage);
|
||||
if (ONENAND_IS_2PLANE(this)) {
|
||||
ONENAND_SET_BUFFERRAM1(this);
|
||||
onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
printk(KERN_ERR "onenand_write: write filaed %d\n", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Only check verify write turn on */
|
||||
ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "onenand_write: verify failed %d\n", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
written += thislen;
|
||||
|
||||
if (written == len)
|
||||
break;
|
||||
|
||||
column = 0;
|
||||
to += thislen;
|
||||
buf += thislen;
|
||||
}
|
||||
|
||||
/* Deselect and wake up anyone waiting on the device */
|
||||
onenand_release_device(mtd);
|
||||
|
||||
ops->retlen = written;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* onenand_do_write_oob - [Internal] OneNAND write out-of-band
|
||||
* @param mtd MTD device structure
|
||||
|
@ -1478,6 +1564,33 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_write - [MTD Interface] write buffer to FLASH
|
||||
* @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
|
||||
*
|
||||
* Write with ECC
|
||||
*/
|
||||
static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct mtd_oob_ops ops = {
|
||||
.len = len,
|
||||
.ooblen = 0,
|
||||
.datbuf = (u_char *) buf,
|
||||
.oobbuf = NULL,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = onenand_write_ops(mtd, to, &ops);
|
||||
*retlen = ops.retlen;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band
|
||||
* @param mtd: MTD device structure
|
||||
|
@ -1496,6 +1609,10 @@ static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
|
|||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ops->datbuf)
|
||||
return onenand_write_ops(mtd, to, ops);
|
||||
|
||||
return onenand_do_write_oob(mtd, to, ops);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue