*/
#include <common.h>
-#include <linux/compat.h>
+#include <linux/mtd/compat.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h>
#include <asm/errno.h>
#include <malloc.h>
+extern void *memcpy32(void *dst, const void *src, int len);
+
/* It should access 16-bit instead of 8-bit */
static void *memcpy_16(void *dst, const void *src, unsigned int len)
{
short *d = dst;
const short *s = src;
+ if (len >= 32 && (len & (32 - 1)) == 0)
+ return memcpy32(dst, src, len);
+
len >>= 1;
while (len-- > 0)
*d++ = *s++;
70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
102, 103, 104, 105
- },
- .oobfree = {
+ },
+ .oobfree = {
{2, 4}, {18, 4}, {34, 4}, {50, 4},
{66, 4}, {82, 4}, {98, 4}, {114, 4}
}
.oobfree = { {2, 3}, {14, 2}, {18, 3}, {30, 2} }
};
-/*
- * Warning! This array is used with the memcpy_16() function, thus
- * it must be aligned to 2 bytes. GCC can make this array unaligned
- * as the array is made of unsigned char, which memcpy16() doesn't
- * like and will cause unaligned access.
- */
-static const unsigned char __aligned(2) ffchars[] = {
+static const unsigned char ffchars[] __attribute__((aligned(4))) = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
size_t len)
{
struct onenand_chip *this = mtd->priv;
- int value;
- int block, page;
-
- /* Now we use page size operation */
- int sectors = 0, count = 0;
+ int value, block, page;
/* Address translation */
switch (cmd) {
default:
block = onenand_block(this, addr);
- page = (int) (addr
- - onenand_addr(this, block)) >> this->page_shift;
+ if (FLEXONENAND(this))
+ page = (int) (addr - onenand_addr(this, block)) >>
+ this->page_shift;
+ else
+ page = (int) (addr >> this->page_shift);
page &= this->page_mask;
break;
}
this->base + ONENAND_REG_START_ADDRESS2);
if (ONENAND_IS_4KB_PAGE(this))
+ /* It is always BufferRAM0 */
ONENAND_SET_BUFFERRAM0(this);
else
/* Switch to the next data buffer */
}
if (page != -1) {
+ /* Now we use page size operation */
+ int sectors = 0, count = 0;
int dataram;
switch (cmd) {
case FLEXONENAND_CMD_RECOVER_LSB:
case ONENAND_CMD_READ:
case ONENAND_CMD_READOOB:
+ case ONENAND_CMD_SUPERLOAD:
if (ONENAND_IS_4KB_PAGE(this))
+ /* It is always BufferRAM0 */
dataram = ONENAND_SET_BUFFERRAM0(this);
else
dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
-
break;
default:
{
int ecc, i;
- if (!FLEXONENAND(this))
+ if (!ONENAND_IS_4KB_PAGE(this))
return this->read_word(this->base + ONENAND_REG_ECC_STATUS);
for (i = 0; i < 4; i++) {
return -EIO;
}
-
return 0;
}
}
/**
- * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode
- * @param mtd MTD data structure
- * @param area BufferRAM area
- * @param buffer the databuffer to put/get data
- * @param offset offset to read from or write to
- * @param count number of bytes to read/write
- *
- * Read the BufferRAM area with Sync. Burst Mode
- */
-static int onenand_sync_read_bufferram(struct mtd_info *mtd, loff_t addr, int area,
- unsigned char *buffer, int offset,
- size_t count)
-{
- struct onenand_chip *this = mtd->priv;
- void __iomem *bufferram;
-
- bufferram = this->base + area;
- bufferram += onenand_bufferram_offset(mtd, area);
-
- this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ);
-
- memcpy_16(buffer, bufferram + offset, count);
-
- this->mmcontrol(mtd, 0);
-
- return 0;
-}
-
-/**
* onenand_write_bufferram - [OneNAND Interface] Write the bufferram area
* @param mtd MTD data structure
* @param area BufferRAM area
}
/**
- * onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer
+ * 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
uint8_t *oob_buf = this->oob_buf;
free = this->ecclayout->oobfree;
- for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length;
- i++, free++) {
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
if (readcol >= lastgap)
readcol += free->offset - lastgap;
if (readend >= lastgap)
}
this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
free = this->ecclayout->oobfree;
- for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length;
- i++, free++) {
+ 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);
return status;
/* check if we failed due to uncorrectable error */
- if (!mtd_is_eccerr(status) && status != ONENAND_BBT_READ_ECC_ERROR)
+ if (status != -EBADMSG && status != ONENAND_BBT_READ_ECC_ERROR)
return status;
/* check if address lies in MLC region */
*
* OneNAND read main and/or out-of-band data
*/
-static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
+static int onenand_normal_read_ops_nolock(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
struct onenand_chip *this = mtd->priv;
MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
- if (ops->mode == MTD_OPS_AUTO_OOB)
+ if (ops->mode == MTD_OOB_AUTO)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
/* Do first load to bufferRAM */
if (read < len) {
if (!onenand_check_bufferram(mtd, from)) {
- this->main_buf = buf;
this->command(mtd, ONENAND_CMD_READ, from, writesize);
ret = this->wait(mtd, FL_READING);
if (unlikely(ret))
while (!ret) {
/* If there is more to load then start next load */
from += thislen;
- if (!ONENAND_IS_4KB_PAGE(this) && read + thislen < len) {
- this->main_buf = buf + thislen;
+
+ if (ONENAND_IS_4KB_PAGE(this))
+ goto skip_read_while_load;
+
+ if (read + thislen < len) {
this->command(mtd, ONENAND_CMD_READ, from, writesize);
/*
* Chip boundary handling in DDP
boundary = 0;
ONENAND_SET_PREV_BUFFERRAM(this);
}
-
+skip_read_while_load:
/* While load is going, read from last bufferRAM */
this->read_bufferram(mtd, from - thislen, ONENAND_DATARAM, buf, column, thislen);
thisooblen = oobsize - oobcolumn;
thisooblen = min_t(int, thisooblen, ooblen - oobread);
- if (ops->mode == MTD_OPS_AUTO_OOB)
+ if (ops->mode == MTD_OOB_AUTO)
onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
else
this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
oobcolumn = 0;
}
- if (ONENAND_IS_4KB_PAGE(this) && (read + thislen < len)) {
- this->command(mtd, ONENAND_CMD_READ, from, writesize);
- ret = this->wait(mtd, FL_READING);
- if (unlikely(ret))
- ret = onenand_recover_lsb(mtd, from, ret);
- onenand_update_bufferram(mtd, from, !ret);
- if (mtd_is_eccerr(ret))
- ret = 0;
- }
-
/* See if we are done */
read += thislen;
if (read == len)
/* Set up for next read from bufferRAM */
if (unlikely(boundary))
this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
- if (!ONENAND_IS_4KB_PAGE(this))
+ if (ONENAND_IS_4KB_PAGE(this))
+ this->command(mtd, ONENAND_CMD_READ, from, writesize);
+ else
ONENAND_SET_NEXT_BUFFERRAM(this);
buf += thislen;
thislen = min_t(int, writesize, len - read);
column = 0;
- if (!ONENAND_IS_4KB_PAGE(this)) {
+ if (ONENAND_IS_4KB_PAGE(this)) {
/* Now wait for load */
ret = this->wait(mtd, FL_READING);
onenand_update_bufferram(mtd, from, !ret);
- if (mtd_is_eccerr(ret))
+ if (ret == -EBADMSG)
ret = 0;
}
}
if (mtd->ecc_stats.failed - stats.failed)
return -EBADMSG;
- /* return max bitflips per ecc step; ONENANDs correct 1 bit only */
- return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
+ return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+}
+
+static int onenand_superload_read_ops_nolock(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct mtd_ecc_stats stats;
+ 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;
+
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: 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_ops_nolock: Attempt read beyond end of device\n");
+ ops->retlen = 0;
+ ops->oobretlen = 0;
+ return -EINVAL;
+ }
+
+ stats = mtd->ecc_stats;
+
+ this->command(mtd, ONENAND_CMD_READ, from, writesize);
+ ret = this->wait(mtd, FL_READING);
+ if (unlikely(ret))
+ ret = onenand_recover_lsb(mtd, from, ret);
+ onenand_update_bufferram(mtd, from, !ret);
+ if (ret == -EBADMSG)
+ ret = 0;
+
+ thislen = min_t(int, writesize, len - read);
+ column = from & (writesize - 1);
+ if (column + thislen > writesize)
+ thislen = writesize - column;
+
+ while (!ret) {
+ /* If there is more to load then start next load */
+ from += thislen;
+
+ if (read + thislen < len) {
+ this->command(mtd, ONENAND_CMD_SUPERLOAD, from, writesize);
+ /*
+ * Chip boundary handling in DDP
+ * Now we issued chip 1 read and pointed chip 1
+ * bufferam so we have to point chip 0 bufferam.
+ */
+ if (ONENAND_IS_DDP(this) &&
+ unlikely(from == (this->chipsize >> 1))) {
+ this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2);
+ boundary = 1;
+ } else
+ boundary = 0;
+ }
+ /* While load is going, read from last bufferRAM */
+ this->read_bufferram(mtd, from - thislen, 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, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
+ oobread += thisooblen;
+ oobbuf += thisooblen;
+ oobcolumn = 0;
+ } else {
+ /* It should be read at least 4 bytes
+ * to the end of the spare area
+ */
+ this->read_bufferram(mtd, 0, ONENAND_SPARERAM, this->oob_buf, 0, mtd->oobsize);
+ }
+
+ /* See if we are done */
+ read += thislen;
+ if (read == len)
+ break;
+ /* Set up for next read from bufferRAM */
+ if (unlikely(boundary))
+ this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
+
+ buf += thislen;
+ thislen = min_t(int, writesize, len - read);
+ column = 0;
+
+ /* Now wait for load */
+ ret = this->wait(mtd, FL_READING);
+ onenand_update_bufferram(mtd, from, !ret);
+ if (ret == -EBADMSG)
+ ret = 0;
+ }
+
+ /*
+ * Return success, if no ECC failures, else -EBADMSG
+ * fs driver will take care of that, because
+ * retlen == desired len and result == -EBADMSG
+ */
+ ops->retlen = read;
+ ops->oobretlen = oobread;
+
+ if (ret)
+ return ret;
+
+ if (mtd->ecc_stats.failed - stats.failed)
+ return -EBADMSG;
+
+ return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
}
/**
+ * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
+ * @param mtd MTD device structure
+ * @param from offset to read from
+ * @param ops oob operation description structure
+ *
+ * OneNAND read main and/or out-of-band data
+ */
+static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct onenand_chip *this = mtd->priv;
+
+ if (ONENAND_IS_4KB_PAGE(this) && ONENAND_IS_SYNC_MODE(this))
+ return onenand_superload_read_ops_nolock(mtd, from, ops);
+
+ return onenand_normal_read_ops_nolock(mtd, from, ops);
+}
+
+
+/**
* onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band
* @param mtd MTD device structure
* @param from offset to read from
struct mtd_ecc_stats stats;
int read = 0, thislen, column, oobsize;
size_t len = ops->ooblen;
- unsigned int mode = ops->mode;
+ mtd_oob_mode_t mode = ops->mode;
u_char *buf = ops->oobbuf;
int ret = 0, readcmd;
/* Initialize return length value */
ops->oobretlen = 0;
- if (mode == MTD_OPS_AUTO_OOB)
+ if (mode == MTD_OOB_AUTO)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
stats = mtd->ecc_stats;
- readcmd = ONENAND_IS_4KB_PAGE(this) ?
- ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+ readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
while (read < len) {
thislen = oobsize - column;
thislen = min_t(int, thislen, len);
- this->spare_buf = buf;
this->command(mtd, readcmd, from, mtd->oobsize);
onenand_update_bufferram(mtd, from, 0);
break;
}
- if (mode == MTD_OPS_AUTO_OOB)
+ if (mode == MTD_OOB_AUTO)
onenand_transfer_auto_oob(mtd, buf, column, thislen);
else
this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
*
* This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL
*/
-int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
+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 = {
*
* OneNAND main and/or out-of-band
*/
-int onenand_read_oob(struct mtd_info *mtd, loff_t from,
+static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
int ret;
switch (ops->mode) {
- case MTD_OPS_PLACE_OOB:
- case MTD_OPS_AUTO_OOB:
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
break;
- case MTD_OPS_RAW:
+ case MTD_OOB_RAW:
/* Not implemented yet */
default:
return -EINVAL;
MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len);
- readcmd = ONENAND_IS_4KB_PAGE(this) ?
- ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+ readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
/* Initialize return value */
ops->oobretlen = 0;
thislen = mtd->oobsize - column;
thislen = min_t(int, thislen, len);
- this->spare_buf = buf;
this->command(mtd, readcmd, from, mtd->oobsize);
onenand_update_bufferram(mtd, from, 0);
return ret;
}
-
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
/**
* onenand_verify_oob - [GENERIC] verify the oob contents after a write
u_char *oob_buf = this->oob_buf;
int status, i, readcmd;
- readcmd = ONENAND_IS_4KB_PAGE(this) ?
- ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+ readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
this->command(mtd, readcmd, to, mtd->oobsize);
onenand_update_bufferram(mtd, to, 0);
static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
{
struct onenand_chip *this = mtd->priv;
- void __iomem *dataram;
int ret = 0;
int thislen, column;
onenand_update_bufferram(mtd, addr, 1);
- dataram = this->base + ONENAND_DATARAM;
- dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM);
+ this->read_bufferram(mtd, 0, ONENAND_DATARAM, this->verify_buf, 0, mtd->writesize);
- if (memcmp(buf, dataram + column, thislen))
+ if (memcmp(buf, this->verify_buf, thislen))
return -EBADMSG;
len -= thislen;
#define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0)
/**
- * onenand_fill_auto_oob - [INTERN] oob auto-placement transfer
+ * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
* @param mtd MTD device structure
* @param oob_buf oob buffer
* @param buf source address
unsigned int i;
free = this->ecclayout->oobfree;
- for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length;
- i++, free++) {
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
if (writecol >= lastgap)
writecol += free->offset - lastgap;
if (writeend >= lastgap)
lastgap = free->offset + free->length;
}
free = this->ecclayout->oobfree;
- for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length;
- i++, free++) {
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
int free_end = free->offset + free->length;
if (free->offset < writeend && free_end > writecol) {
int st = max_t(int,free->offset,writecol);
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_ops_nolock: Attempt write to past end of device\n");
+ return -EINVAL;
+ }
+
/* Reject writes, which are not page aligned */
if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n");
return -EINVAL;
}
- if (ops->mode == MTD_OPS_AUTO_OOB)
+ if (ops->mode == MTD_OOB_AUTO)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
/* We send data to spare ram with oobsize
* * to prevent byte access */
memset(oobbuf, 0xff, mtd->oobsize);
- if (ops->mode == MTD_OPS_AUTO_OOB)
+ if (ops->mode == MTD_OOB_AUTO)
onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
else
memcpy(oobbuf + oobcolumn, oob, thisooblen);
} else
oobbuf = (u_char *) ffchars;
- this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
+ this->write_bufferram(mtd, to, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
}
/**
- * onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band
+ * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
* @param mtd MTD device structure
* @param to offset to write to
* @param len number of bytes to write
u_char *oobbuf;
size_t len = ops->ooblen;
const u_char *buf = ops->oobbuf;
- unsigned int mode = ops->mode;
+ mtd_oob_mode_t mode = ops->mode;
to += ops->ooboffs;
/* Initialize retlen, in case of early exit */
ops->oobretlen = 0;
- if (mode == MTD_OPS_AUTO_OOB)
+ if (mode == MTD_OOB_AUTO)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
oobbuf = this->oob_buf;
- oobcmd = ONENAND_IS_4KB_PAGE(this) ?
- ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB;
+ oobcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB;
/* Loop until all data write */
while (written < len) {
/* We send data to spare ram with oobsize
* to prevent byte access */
memset(oobbuf, 0xff, mtd->oobsize);
- if (mode == MTD_OPS_AUTO_OOB)
+ if (mode == MTD_OOB_AUTO)
onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);
else
memcpy(oobbuf + column, buf, thislen);
*
* Write with ECC
*/
-int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
+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 = {
.ooblen = 0,
.datbuf = (u_char *) buf,
.oobbuf = NULL,
+ .mode = MTD_OOB_AUTO,
};
int ret;
int ret;
switch (ops->mode) {
- case MTD_OPS_PLACE_OOB:
- case MTD_OPS_AUTO_OOB:
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
break;
- case MTD_OPS_RAW:
+ case MTD_OOB_RAW:
/* Not implemented yet */
default:
return -EINVAL;
*
* Erase one ore more blocks
*/
-int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
+static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct onenand_chip *this = mtd->priv;
unsigned int block_size;
MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n",
(unsigned int) addr, len);
+ /* Do not allow erase past end of device */
+ if (unlikely((len + addr) > mtd->size)) {
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:"
+ "Erase past end of device\n");
+ return -EINVAL;
+ }
+
if (FLEXONENAND(this)) {
/* Find the eraseregion of this address */
i = flexonenand_region(mtd, addr);
return -EINVAL;
}
+ instr->fail_addr = 0xffffffff;
+
/* Grab the lock and see if the device is available */
onenand_get_device(mtd, FL_ERASING);
onenand_block(this, addr));
instr->state = MTD_ERASE_FAILED;
instr->fail_addr = addr;
-
- goto erase_exit;
}
len -= block_size;
*
* Sync is actually a wait for chip ready function
*/
-void onenand_sync(struct mtd_info *mtd)
+static void onenand_sync(struct mtd_info *mtd)
{
MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_sync: called\n");
*
* Check whether the block is bad
*/
-int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
+static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
{
int ret;
return -EINVAL;
onenand_get_device(mtd, FL_READING);
- ret = onenand_block_isbad_nolock(mtd,ofs, 0);
+ ret = onenand_block_isbad_nolock(mtd, ofs, 0);
onenand_release_device(mtd);
return ret;
}
struct bbm_info *bbm = this->bbm;
u_char buf[2] = {0, 0};
struct mtd_oob_ops ops = {
- .mode = MTD_OPS_PLACE_OOB,
+ .mode = MTD_OOB_PLACE,
.ooblen = 2,
.oobbuf = buf,
.ooboffs = 0,
*
* Mark the block as bad
*/
-int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
+ struct onenand_chip *this = mtd->priv;
int ret;
ret = onenand_block_isbad(mtd, ofs);
return ret;
}
- ret = mtd_block_markbad(mtd, ofs);
+ ret = this->block_markbad(mtd, ofs);
return ret;
}
{
struct onenand_chip *this = mtd->priv;
int start, end, block, value, status;
+ int wp_status_mask;
start = onenand_block(this, ofs);
end = onenand_block(this, ofs + len);
+ if (cmd == ONENAND_CMD_LOCK)
+ wp_status_mask = ONENAND_WP_LS;
+ else
+ wp_status_mask = ONENAND_WP_US;
+
/* Continuous lock scheme */
if (this->options & ONENAND_HAS_CONT_LOCK) {
/* Set start block address */
/* Set end block address */
this->write_word(end - 1,
this->base + ONENAND_REG_END_BLOCK_ADDRESS);
- /* Write unlock command */
+ /* Write lock command */
this->command(mtd, cmd, 0, 0);
/* There's no return value */
- this->wait(mtd, FL_UNLOCKING);
+ this->wait(mtd, FL_LOCKING);
/* Sanity check */
while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
/* Check lock status */
status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
- if (!(status & ONENAND_WP_US))
+ if (!(status & wp_status_mask))
printk(KERN_ERR "wp status = 0x%x\n", status);
return 0;
/* Set start block address */
this->write_word(block,
this->base + ONENAND_REG_START_BLOCK_ADDRESS);
- /* Write unlock command */
- this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
+ /* Write lock command */
+ this->command(mtd, cmd, 0, 0);
/* There's no return value */
- this->wait(mtd, FL_UNLOCKING);
+ this->wait(mtd, FL_LOCKING);
/* Sanity check */
while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
/* Check lock status */
status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
- if (!(status & ONENAND_WP_US))
+ if (!(status & wp_status_mask))
printk(KERN_ERR "block = %d, wp status = 0x%x\n",
block, status);
}
onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
}
+/**
+ * onenand_lock_tight - [OneNAND Interface] Lock-tight block(s)
+ * @param mtd MTD device structure
+ * @param ofs offset relative to mtd start
+ * @param len number of bytes to lock-tight
+ *
+ * Lock-tight one or more blocks
+ */
+static int onenand_lock_tight(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ int ret;
+
+ onenand_get_device(mtd, FL_LOCKING);
+ ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK_TIGHT);
+ onenand_release_device(mtd);
+ return ret;
+}
+
+/**
+ * onenand_block_islock - [MTD Interface] Check whether the block at the given offset is locked
+ * @param mtd MTD device structure
+ * @param ofs offset relative to mtd start
+ *
+ * Check whether the block is bad
+ */
+static int onenand_block_islock(struct mtd_info *mtd, loff_t ofs)
+{
+ struct onenand_chip *this = mtd->priv;
+ int block, value, status;
+
+ /* Check for invalid offset */
+ if (ofs > mtd->size)
+ return -EINVAL;
+
+ onenand_get_device(mtd, FL_READING);
+
+ block = onenand_block(this, ofs);
+ /* Set block address */
+ value = onenand_block_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
+ /* Select DataRAM for DDP */
+ value = onenand_bufferram_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
+ /* Set start block address */
+ this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
+
+ /* Check lock status */
+ status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
+
+ onenand_release_device(mtd);
+
+ return status;
+}
/**
* onenand_check_features - Check and set OneNAND features
{
struct onenand_chip *this = mtd->priv;
unsigned int density, process;
+ unsigned int syscfg;
/* Lock scheme depends on density and process */
density = onenand_get_density(this->device_id);
this->options |= ONENAND_HAS_UNLOCK_ALL;
}
+ syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
+ if (syscfg & ONENAND_SYS_CFG1_SYNC_READ) {
+ printk(KERN_DEBUG "Sync Burst Mode\n");
+ this->options |= ONENAND_SYNC_MODE;
+ }
+
+#ifdef ONENAND_LINUX
if (this->options & ONENAND_HAS_CONT_LOCK)
printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");
if (this->options & ONENAND_HAS_UNLOCK_ALL)
printk(KERN_DEBUG "Chip support all block unlock\n");
if (this->options & ONENAND_HAS_2PLANE)
printk(KERN_DEBUG "Chip has 2 plane\n");
+#endif
if (this->options & ONENAND_HAS_4KB_PAGE)
printk(KERN_DEBUG "Chip has 4KiB pagesize\n");
-
}
/**
static int onenand_check_maf(int manuf)
{
int size = ARRAY_SIZE(onenand_manuf_ids);
- int i;
-#ifdef ONENAND_DEBUG
char *name;
-#endif
+ int i;
for (i = 0; i < size; i++)
if (manuf == onenand_manuf_ids[i].id)
break;
-#ifdef ONENAND_DEBUG
if (i < size)
name = onenand_manuf_ids[i].name;
else
name = "Unknown";
+#ifdef ONENAND_DEBUG
printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf);
#endif
{
struct onenand_chip *this = mtd->priv;
unsigned int die, bdry;
- int syscfg, locked;
+ int ret, syscfg, locked;
/* Disable ECC */
syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
this->wait(mtd, FL_SYNCING);
this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
- this->wait(mtd, FL_READING);
+ ret = this->wait(mtd, FL_READING);
bdry = this->read_word(this->base + ONENAND_DATARAM);
if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3)
this->boundary[die] = bdry & FLEXONENAND_PI_MASK;
this->command(mtd, ONENAND_CMD_RESET, 0, 0);
- this->wait(mtd, FL_RESETING);
+ ret = this->wait(mtd, FL_RESETING);
printk(KERN_INFO "Die %d boundary: %d%s\n", die,
this->boundary[die], locked ? "(Locked)" : "(Unlocked)");
int i, ret;
int block;
struct mtd_oob_ops ops = {
- .mode = MTD_OPS_PLACE_OOB,
+ .mode = MTD_OOB_PLACE,
.ooboffs = 0,
.ooblen = mtd->oobsize,
.datbuf = NULL,
}
/**
- * onenand_chip_probe - [OneNAND Interface] Probe the OneNAND chip
+ * onenand_chip_probe - [OneNAND Interface] Probe the OneNAND device
* @param mtd MTD device structure
*
* OneNAND detection method:
syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
/* Clear Sync. Burst Read mode to read BootRAM */
- this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ),
- this->base + ONENAND_REG_SYS_CFG1);
+ this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1);
/* Send the command for reading device ID from BootRAM */
this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
* onenand_probe - [OneNAND Interface] Probe the OneNAND device
* @param mtd MTD device structure
*
- * OneNAND detection method:
- * Compare the the values from command with ones from register
+ * Setup required OneNAND internal data structures
*/
-int onenand_probe(struct mtd_info *mtd)
+static int onenand_probe(struct mtd_info *mtd)
{
struct onenand_chip *this = mtd->priv;
- int dev_id, ver_id;
+ int maf_id, dev_id, ver_id;
int density;
int ret;
if (ret)
return ret;
- /* Read device IDs from Register */
+ /* Read manufacturer and device IDs from Register */
+ maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY);
mtd->size = this->chipsize;
mtd->flags = MTD_CAP_NANDFLASH;
- mtd->_erase = onenand_erase;
- mtd->_read = onenand_read;
- mtd->_write = onenand_write;
- mtd->_read_oob = onenand_read_oob;
- mtd->_write_oob = onenand_write_oob;
- mtd->_sync = onenand_sync;
- mtd->_block_isbad = onenand_block_isbad;
- mtd->_block_markbad = onenand_block_markbad;
+ mtd->erase = onenand_erase;
+ mtd->read = onenand_read;
+ mtd->write = onenand_write;
+ mtd->read_oob = onenand_read_oob;
+ mtd->write_oob = onenand_write_oob;
+ mtd->sync = onenand_sync;
+ mtd->block_isbad = onenand_block_isbad;
+ mtd->block_markbad = onenand_block_markbad;
return 0;
}
this->wait = onenand_wait;
if (!this->bbt_wait)
this->bbt_wait = onenand_bbt_wait;
+ if (!this->unlock_all)
+ this->unlock_all = onenand_unlock_all;
+ if (!this->lock_tight)
+ this->lock_tight = onenand_lock_tight;
+ if (!this->block_islock)
+ this->block_islock = onenand_block_islock;
+
+ if (!this->chip_probe)
+ this->chip_probe = onenand_chip_probe;
if (!this->read_bufferram)
this->read_bufferram = onenand_read_bufferram;
if (!this->write_bufferram)
this->write_bufferram = onenand_write_bufferram;
- if (!this->chip_probe)
- this->chip_probe = onenand_chip_probe;
-
if (!this->block_markbad)
this->block_markbad = onenand_default_block_markbad;
if (!this->scan_bbt)
if (onenand_probe(mtd))
return -ENXIO;
- /* Set Sync. Burst Read after probing */
- if (this->mmcontrol) {
- printk(KERN_INFO "OneNAND Sync. Burst Read support\n");
- this->read_bufferram = onenand_sync_read_bufferram;
- }
-
/* Allocate buffers, if necessary */
if (!this->page_buf) {
this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL);
printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n");
return -ENOMEM;
}
+#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
+ this->verify_buf = kzalloc(mtd->writesize, GFP_KERNEL);
+ if (!this->verify_buf) {
+ kfree(this->page_buf);
+ return -ENOMEM;
+ }
+#endif
this->options |= ONENAND_PAGEBUF_ALLOC;
}
if (!this->oob_buf) {
* the out of band area
*/
this->ecclayout->oobavail = 0;
-
- for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE &&
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
this->ecclayout->oobfree[i].length; i++)
this->ecclayout->oobavail +=
this->ecclayout->oobfree[i].length;
mtd->ecclayout = this->ecclayout;
/* Unlock whole block */
- onenand_unlock_all(mtd);
+ this->unlock_all(mtd);
return this->scan_bbt(mtd);
}