*/
#include <common.h>
-
-#ifdef CONFIG_CMD_ONENAND
-
#include <linux/mtd/compat.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h>
#include <asm/io.h>
#include <asm/errno.h>
+#include <malloc.h>
/* It should access 16-bit instead of 8-bit */
static inline void *memcpy_16(void *dst, const void *src, unsigned int len)
ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
if (ctrl & ONENAND_CTRL_ERROR) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_wait: controller error = 0x%04x\n", ctrl);
+ MTDDEBUG (MTD_DEBUG_LEVEL0,
+ "onenand_wait: controller error = 0x%04x\n", ctrl);
return -EAGAIN;
}
if (ctrl & ONENAND_CTRL_LOCK) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_wait: it's locked error = 0x%04x\n", ctrl);
+ MTDDEBUG (MTD_DEBUG_LEVEL0,
+ "onenand_wait: it's locked error = 0x%04x\n", ctrl);
return -EIO;
}
if (interrupt & ONENAND_INT_READ) {
ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
if (ecc & ONENAND_ECC_2BIT_ALL) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_wait: ECC error = 0x%04x\n", ecc);
+ MTDDEBUG (MTD_DEBUG_LEVEL0,
+ "onenand_wait: ECC error = 0x%04x\n", ecc);
return -EBADMSG;
}
}
if (ONENAND_CURRENT_BUFFERRAM(this)) {
if (area == ONENAND_DATARAM)
- return mtd->oobblock;
+ return mtd->writesize;
if (area == ONENAND_SPARERAM)
return mtd->oobsize;
}
}
/**
+ * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information
+ * @param mtd MTD data structure
+ * @param addr start address to invalidate
+ * @param len length to invalidate
+ *
+ * Invalidate BufferRAM information
+ */
+static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr,
+ unsigned int len)
+{
+ struct onenand_chip *this = mtd->priv;
+ int i;
+ loff_t end_addr = addr + len;
+
+ /* Invalidate BufferRAM */
+ for (i = 0; i < MAX_BUFFERRAM; i++) {
+ loff_t buf_addr = this->bufferram[i].block << this->erase_shift;
+
+ if (buf_addr >= addr && buf_addr < end_addr)
+ this->bufferram[i].valid = 0;
+ }
+}
+
+/**
* onenand_get_device - [GENERIC] Get chip for selected access
* @param mtd MTD device structure
* @param new_state the state which is requested
int thislen;
int ret = 0;
- DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ecc: from = 0x%08x, len = %i\n",
- (unsigned int)from, (int)len);
+ MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_read_ecc: "
+ "from = 0x%08x, len = %i\n",
+ (unsigned int)from, (int)len);
/* Do not allow reads past end of device */
if ((from + len) > mtd->size) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_read_ecc: Attempt read beyond end of device\n");
+ MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_read_ecc: "
+ "Attempt read beyond end of device\n");
*retlen = 0;
return -EINVAL;
}
onenand_get_device(mtd, FL_READING);
while (read < len) {
- thislen = min_t(int, mtd->oobblock, len - read);
+ thislen = min_t(int, mtd->writesize, len - read);
- column = from & (mtd->oobblock - 1);
- if (column + thislen > mtd->oobblock)
- thislen = mtd->oobblock - column;
+ column = from & (mtd->writesize - 1);
+ if (column + thislen > mtd->writesize)
+ thislen = mtd->writesize - column;
if (!onenand_check_bufferram(mtd, from)) {
this->command(mtd, ONENAND_CMD_READ, from,
- mtd->oobblock);
+ mtd->writesize);
ret = this->wait(mtd, FL_READING);
/* First copy data and check return value for ECC handling */
onenand_update_bufferram(mtd, from, 1);
break;
if (ret) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_read_ecc: read failed = %d\n", ret);
+ MTDDEBUG (MTD_DEBUG_LEVEL0,
+ "onenand_read_ecc: read failed = %d\n", ret);
break;
}
int read = 0, thislen, column;
int ret = 0;
- DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n",
- (unsigned int)from, (int)len);
+ MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_read_oob: "
+ "from = 0x%08x, len = %i\n",
+ (unsigned int)from, (int)len);
/* Initialize return length value */
*retlen = 0;
/* 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");
+ MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_read_oob: "
+ "Attempt read beyond end of device\n");
return -EINVAL;
}
break;
if (ret) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_read_oob: read failed = %d\n", ret);
+ MTDDEBUG (MTD_DEBUG_LEVEL0,
+ "onenand_read_oob: read failed = %d\n", ret);
break;
}
/* Read more? */
if (read < len) {
/* Page size */
- from += mtd->oobblock;
+ from += mtd->writesize;
column = 0;
}
}
* onenand_verify_page - [GENERIC] verify the chip contents after a write
* @param mtd MTD device structure
* @param buf the databuffer to verify
- * @param block block address
- * @param page page address
*
* Check DataRAM area directly
*/
static int onenand_verify_page(struct mtd_info *mtd, u_char * buf,
- loff_t addr, int block, int page)
+ loff_t addr)
{
struct onenand_chip *this = mtd->priv;
void __iomem *dataram0, *dataram1;
int ret = 0;
- this->command(mtd, ONENAND_CMD_READ, addr, mtd->oobblock);
+ this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize);
ret = this->wait(mtd, FL_READING);
if (ret)
/* Check, if the two dataram areas are same */
dataram0 = this->base + ONENAND_DATARAM;
- dataram1 = dataram0 + mtd->oobblock;
+ dataram1 = dataram0 + mtd->writesize;
- if (memcmp(dataram0, dataram1, mtd->oobblock))
+ if (memcmp(dataram0, dataram1, mtd->writesize))
return -EBADMSG;
return 0;
#define onenand_verify_page(...) (0)
#endif
-#define NOTALIGNED(x) ((x & (mtd->oobblock - 1)) != 0)
+#define NOTALIGNED(x) ((x & (mtd->writesize - 1)) != 0)
/**
* onenand_write_ecc - [MTD Interface] OneNAND write with ECC
int written = 0;
int ret = 0;
- DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ecc: to = 0x%08x, len = %i\n",
- (unsigned int)to, (int)len);
+ MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_write_ecc: "
+ "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)) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_write_ecc: Attempt write to past end of device\n");
+ MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_write_ecc: "
+ "Attempt write to past end of device\n");
return -EINVAL;
}
/* Reject writes, which are not page aligned */
if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_write_ecc: Attempt to write not page aligned data\n");
+ MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_write_ecc: "
+ "Attempt to write not page aligned data\n");
return -EINVAL;
}
/* Loop until all data write */
while (written < len) {
- int thislen = min_t(int, mtd->oobblock, len - written);
+ int thislen = min_t(int, mtd->writesize, len - written);
- this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobblock);
+ this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->writesize);
this->write_bufferram(mtd, ONENAND_DATARAM, buf, 0, thislen);
this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0,
mtd->oobsize);
- this->command(mtd, ONENAND_CMD_PROG, to, mtd->oobblock);
+ this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
onenand_update_bufferram(mtd, to, 1);
ret = this->wait(mtd, FL_WRITING);
if (ret) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_write_ecc: write filaed %d\n", ret);
+ MTDDEBUG (MTD_DEBUG_LEVEL0,
+ "onenand_write_ecc: write filaed %d\n", ret);
break;
}
written += thislen;
/* Only check verify write turn on */
- ret = onenand_verify_page(mtd, (u_char *) buf, to, block, page);
+ ret = onenand_verify_page(mtd, (u_char *) buf, to);
if (ret) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_write_ecc: verify failed %d\n", ret);
+ MTDDEBUG (MTD_DEBUG_LEVEL0,
+ "onenand_write_ecc: verify failed %d\n", ret);
break;
}
int column, status;
int written = 0;
- DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n",
- (unsigned int)to, (int)len);
+ MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_write_oob: "
+ "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)) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_write_oob: Attempt write to past end of device\n");
+ MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_write_oob: "
+ "Attempt write to past end of device\n");
return -EINVAL;
}
}
/**
+ * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad
+ * @param mtd MTD device structure
+ * @param ofs offset from device start
+ * @param allowbbt 1, if its allowed to access the bbt area
+ *
+ * Check, if the block is bad, Either by reading the bad block table or
+ * calling of the scan function.
+ */
+static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct bbm_info *bbm = this->bbm;
+
+ /* Return info from the table */
+ return bbm->isbad_bbt(mtd, ofs, allowbbt);
+}
+
+
+/**
* onenand_erase - [MTD Interface] erase block(s)
* @param mtd MTD device structure
* @param instr erase instruction
int len;
int ret = 0;
- DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n",
- (unsigned int)instr->addr, (unsigned int)instr->len);
+ MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n",
+ (unsigned int)instr->addr, (unsigned int)instr->len);
block_size = (1 << this->erase_shift);
/* Start address must align on block boundary */
if (unlikely(instr->addr & (block_size - 1))) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n");
+ MTDDEBUG (MTD_DEBUG_LEVEL0,
+ "onenand_erase: Unaligned address\n");
return -EINVAL;
}
/* Length must align on block boundary */
if (unlikely(instr->len & (block_size - 1))) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_erase: Length not block aligned\n");
+ MTDDEBUG (MTD_DEBUG_LEVEL0,
+ "onenand_erase: Length not block aligned\n");
return -EINVAL;
}
/* Do not allow erase past end of device */
if (unlikely((instr->len + instr->addr) > mtd->size)) {
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_erase: Erase past end of device\n");
+ MTDDEBUG (MTD_DEBUG_LEVEL0,
+ "onenand_erase: Erase past end of device\n");
return -EINVAL;
}
this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
+ onenand_invalidate_bufferram(mtd, addr, block_size);
+
ret = this->wait(mtd, FL_ERASING);
/* Check, if it is write protected */
if (ret) {
if (ret == -EPERM)
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_erase: Device is write protected!!!\n");
+ MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: "
+ "Device is write protected!!!\n");
else
- DEBUG(MTD_DEBUG_LEVEL0,
- "onenand_erase: Failed erase, block %d\n",
- (unsigned)(addr >> this->erase_shift));
+ MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: "
+ "Failed erase, block %d\n",
+ (unsigned)(addr >> this->erase_shift));
instr->state = MTD_ERASE_FAILED;
instr->fail_addr = addr;
goto erase_exit;
*/
void onenand_sync(struct mtd_info *mtd)
{
- DEBUG(MTD_DEBUG_LEVEL3, "onenand_sync: called\n");
+ MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_sync: called\n");
/* Grab the lock and see if the device is available */
onenand_get_device(mtd, FL_SYNCING);
* onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
* @param mtd MTD device structure
* @param ofs offset relative to mtd start
+ *
+ * Check whether the block is bad
*/
int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
{
- /*
- * TODO
- * 1. Bad block table (BBT)
- * -> using NAND BBT to support JFFS2
- * 2. Bad block management (BBM)
- * -> bad block replace scheme
- *
- * Currently we do nothing
- */
- return 0;
+ int ret;
+
+ /* Check for invalid offset */
+ if (ofs > mtd->size)
+ return -EINVAL;
+
+ onenand_get_device(mtd, FL_READING);
+ ret = onenand_block_isbad_nolock(mtd,ofs, 0);
+ onenand_release_device(mtd);
+ return ret;
}
/**
* onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
* @param mtd MTD device structure
* @param ofs offset relative to mtd start
+ *
+ * Mark the block as bad
*/
int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
- /* see above */
- return 0;
+ struct onenand_chip *this = mtd->priv;
+ int ret;
+
+ ret = onenand_block_isbad(mtd, ofs);
+ if (ret) {
+ /* If it was bad already, return success and do nothing */
+ if (ret > 0)
+ return 0;
+ return ret;
+ }
+
+ ret = this->block_markbad(mtd, ofs);
+ return ret;
}
/**
*
* Print device ID
*/
-void onenand_print_device_info(int device, int verbose)
+char * onenand_print_device_info(int device)
{
int vcc, demuxed, ddp, density;
-
- if (!verbose)
- return;
+ char *dev_info = malloc(80);
vcc = device & ONENAND_DEVICE_VCC_MASK;
demuxed = device & ONENAND_DEVICE_IS_DEMUX;
ddp = device & ONENAND_DEVICE_IS_DDP;
density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
- printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",
+ sprintf(dev_info, "%sOneNAND%s %dMB %sV 16-bit (0x%02x)",
demuxed ? "" : "Muxed ",
ddp ? "(DDP)" : "",
(16 << density), vcc ? "2.65/3.3" : "1.8", device);
+
+ return dev_info;
}
static const struct onenand_manufacturers onenand_manuf_ids[] = {
/* Reset OneNAND to read default register values */
this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
- {
- int i;
- for (i = 0; i < 10000; i++) ;
- }
+ /* Wait reset */
+ this->wait(mtd, FL_RESETING);
/* Read manufacturer and device IDs from Register */
maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
}
/* Flash device information */
- onenand_print_device_info(dev_id, 0);
+ mtd->name = onenand_print_device_info(dev_id);
this->device_id = dev_id;
density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
/* OneNAND page size & block size */
/* The data buffer size is equal to page size */
- mtd->oobblock =
+ mtd->writesize =
this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
- mtd->oobsize = mtd->oobblock >> 5;
+ mtd->oobsize = mtd->writesize >> 5;
/* Pagers per block is always 64 in OneNAND */
- mtd->erasesize = mtd->oobblock << 6;
+ mtd->erasesize = mtd->writesize << 6;
this->erase_shift = ffs(mtd->erasesize) - 1;
- this->page_shift = ffs(mtd->oobblock) - 1;
+ this->page_shift = ffs(mtd->writesize) - 1;
this->ppb_shift = (this->erase_shift - this->page_shift);
- this->page_mask = (mtd->erasesize / mtd->oobblock) - 1;
+ this->page_mask = (mtd->erasesize / mtd->writesize) - 1;
/* REVIST: Multichip handling */
this->options |= ONENAND_CONT_LOCK;
}
+ 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;
+
return 0;
}
void onenand_release(struct mtd_info *mtd)
{
}
-
-#endif /* CONFIG_CMD_ONENAND */