*/
#include <common.h>
+#include <log.h>
+#include <watchdog.h>
+#include <dm/devres.h>
+#include <linux/bitops.h>
#include <linux/compat.h>
#include <linux/mtd/mtd.h>
+#include "linux/mtd/flashchip.h"
#include <linux/mtd/onenand.h>
#include <asm/io.h>
-#include <asm/errno.h>
+#include <linux/errno.h>
#include <malloc.h>
/* It should access 16-bit instead of 8-bit */
.oobfree = { {2, 3}, {14, 2}, {18, 3}, {30, 2} }
};
-static const unsigned char ffchars[] = {
+/*
+ * 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[] = {
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,
static int onenand_wait(struct mtd_info *mtd, int state)
{
struct onenand_chip *this = mtd->priv;
- unsigned int flags = ONENAND_INT_MASTER;
unsigned int interrupt = 0;
unsigned int ctrl;
- while (1) {
+ /* Wait at most 20ms ... */
+ u32 timeo = (CONFIG_SYS_HZ * 20) / 1000;
+ u32 time_start = get_timer(0);
+ do {
+ WATCHDOG_RESET();
+ if (get_timer(time_start) > timeo)
+ return -EIO;
interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
- if (interrupt & flags)
- break;
- }
+ } while ((interrupt & ONENAND_INT_MASTER) == 0);
ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
uint8_t *oob_buf = this->oob_buf;
free = this->ecclayout->oobfree;
- for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && 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 && free->length; i++, free++) {
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && 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 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);
+ pr_debug("onenand_read_ops_nolock: from = 0x%08x, len = %i\n",
+ (unsigned int) from, (int) len);
if (ops->mode == MTD_OPS_AUTO_OOB)
oobsize = this->ecclayout->oobavail;
if (mtd->ecc_stats.failed - stats.failed)
return -EBADMSG;
- return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+ /* return max bitflips per ecc step; ONENANDs correct 1 bit only */
+ return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
}
/**
from += ops->ooboffs;
- MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+ pr_debug("onenand_read_oob_nolock: from = 0x%08x, len = %i\n",
+ (unsigned int) from, (int) len);
/* Initialize return length value */
ops->oobretlen = 0;
static int onenand_bbt_wait(struct mtd_info *mtd, int state)
{
struct onenand_chip *this = mtd->priv;
- unsigned int flags = ONENAND_INT_MASTER;
unsigned int interrupt;
unsigned int ctrl;
- while (1) {
+ /* Wait at most 20ms ... */
+ u32 timeo = (CONFIG_SYS_HZ * 20) / 1000;
+ u32 time_start = get_timer(0);
+ do {
+ WATCHDOG_RESET();
+ if (get_timer(time_start) > timeo)
+ return ONENAND_BBT_READ_FATAL_ERROR;
interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
- if (interrupt & flags)
- break;
- }
+ } while ((interrupt & ONENAND_INT_MASTER) == 0);
/* To get correct interrupt status in timeout case */
interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
size_t len = ops->ooblen;
u_char *buf = ops->oobbuf;
- MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len);
+ pr_debug("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;
unsigned int i;
free = this->ecclayout->oobfree;
- for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && 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 && free->length; i++, free++) {
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && 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);
u_char *oobbuf;
int ret = 0;
- MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+ pr_debug("onenand_write_ops_nolock: to = 0x%08x, len = %i\n",
+ (unsigned int) to, (int) len);
/* Initialize retlen, in case of early exit */
ops->retlen = 0;
to += ops->ooboffs;
- MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+ pr_debug("onenand_write_oob_nolock: to = 0x%08x, len = %i\n",
+ (unsigned int) to, (int) len);
/* Initialize retlen, in case of early exit */
ops->oobretlen = 0;
struct mtd_erase_region_info *region = NULL;
unsigned int region_end = 0;
- MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n",
+ pr_debug("onenand_erase: start = 0x%08x, len = %i\n",
(unsigned int) addr, len);
if (FLEXONENAND(this)) {
* Erase region's start offset is always block start address.
*/
if (unlikely((addr - region->offset) & (block_size - 1))) {
- MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:"
- " Unaligned address\n");
+ pr_debug("onenand_erase:" " Unaligned address\n");
return -EINVAL;
}
} else {
/* Start address must align on block boundary */
if (unlikely(addr & (block_size - 1))) {
- MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:"
- "Unaligned address\n");
+ pr_debug("onenand_erase:" "Unaligned address\n");
return -EINVAL;
}
}
/* Length must align on block boundary */
if (unlikely(len & (block_size - 1))) {
- MTDDEBUG (MTD_DEBUG_LEVEL0,
- "onenand_erase: Length not block aligned\n");
+ pr_debug("onenand_erase: Length not block aligned\n");
return -EINVAL;
}
/* Check, if it is write protected */
if (ret) {
if (ret == -EPERM)
- MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: "
- "Device is write protected!!!\n");
+ pr_debug("onenand_erase: "
+ "Device is write protected!!!\n");
else
- MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: "
- "Failed erase, block %d\n",
- onenand_block(this, addr));
+ pr_debug("onenand_erase: "
+ "Failed erase, block %d\n",
+ onenand_block(this, addr));
instr->state = MTD_ERASE_FAILED;
instr->fail_addr = addr;
*/
void onenand_sync(struct mtd_info *mtd)
{
- MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_sync: called\n");
+ pr_debug("onenand_sync: called\n");
/* Grab the lock and see if the device is available */
onenand_get_device(mtd, FL_SYNCING);
*/
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);
+ onenand_get_device(mtd, FL_WRITING);
+ ret = this->block_markbad(mtd, ofs);
+ onenand_release_device(mtd);
+
return ret;
}
/**
* flexonenand_get_size - Fill up fields in onenand_chip and mtd_info
- * boundary[], diesize[], mtd->size, mtd->erasesize,
- * mtd->eraseregions
+ * boundary[], diesize[], mtd->size, mtd->erasesize,
+ * mtd->eraseregions
* @param mtd - MTD device structure
*/
static void flexonenand_get_size(struct mtd_info *mtd)
this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
/* Wait reset */
- this->wait(mtd, FL_RESETING);
+ if (this->wait(mtd, FL_RESETING))
+ return -ENXIO;
/* Restore system configuration 1 */
this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
else
mtd->size = this->chipsize;
+ mtd->type = ONENAND_IS_MLC(this) ? MTD_MLCNANDFLASH : MTD_NANDFLASH;
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->writebufsize = mtd->writesize;
return 0;
}
* the out of band area
*/
this->ecclayout->oobavail = 0;
- for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
+
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE &&
this->ecclayout->oobfree[i].length; i++)
this->ecclayout->oobavail +=
this->ecclayout->oobfree[i].length;