summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r--drivers/mtd/nand/Makefile34
-rw-r--r--drivers/mtd/nand/fsl_elbc_nand.c2
-rw-r--r--drivers/mtd/nand/fsl_elbc_spl.c168
-rw-r--r--drivers/mtd/nand/fsl_ifc_nand.c64
-rw-r--r--drivers/mtd/nand/mxs_nand.c3
-rw-r--r--drivers/mtd/nand/nand_base.c9
-rw-r--r--drivers/mtd/nand/nand_util.c188
7 files changed, 422 insertions, 46 deletions
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index beb99ca..28e52bd 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -26,21 +26,33 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libnand.o
ifdef CONFIG_CMD_NAND
+
ifdef CONFIG_SPL_BUILD
-ifdef CONFIG_SPL_NAND_SIMPLE
-COBJS-y += nand_spl_simple.o
-endif
-ifdef CONFIG_SPL_NAND_LOAD
-COBJS-y += nand_spl_load.o
+
+ifdef CONFIG_SPL_NAND_DRIVERS
+NORMAL_DRIVERS=y
endif
-else
+
+COBJS-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o
+COBJS-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o
+COBJS-$(CONFIG_SPL_NAND_ECC) += nand_ecc.o
+COBJS-$(CONFIG_SPL_NAND_BASE) += nand_base.o
+
+else # not spl
+
+NORMAL_DRIVERS=y
+
COBJS-y += nand.o
COBJS-y += nand_bbt.o
COBJS-y += nand_ids.o
COBJS-y += nand_util.o
-endif
COBJS-y += nand_ecc.o
COBJS-y += nand_base.o
+
+endif # not spl
+
+ifdef NORMAL_DRIVERS
+
COBJS-$(CONFIG_NAND_ECC_BCH) += nand_bch.o
COBJS-$(CONFIG_NAND_ATMEL) += atmel_nand.o
@@ -65,7 +77,13 @@ COBJS-$(CONFIG_NAND_SPEAR) += spr_nand.o
COBJS-$(CONFIG_TEGRA_NAND) += tegra_nand.o
COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
COBJS-$(CONFIG_NAND_PLAT) += nand_plat.o
-endif
+
+else # minimal SPL drivers
+
+COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o
+
+endif # drivers
+endif # nand
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
index 9076ad4..834a8a6 100644
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/fsl_elbc_nand.c
@@ -748,7 +748,7 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr)
/* set up nand options */
nand->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
- NAND_USE_FLASH_BBT;
+ NAND_USE_FLASH_BBT | NAND_NO_SUBPAGE_WRITE;
nand->controller = &elbc_ctrl->controller;
nand->priv = priv;
diff --git a/drivers/mtd/nand/fsl_elbc_spl.c b/drivers/mtd/nand/fsl_elbc_spl.c
new file mode 100644
index 0000000..50ff4fe
--- /dev/null
+++ b/drivers/mtd/nand/fsl_elbc_spl.c
@@ -0,0 +1,168 @@
+/*
+ * NAND boot for Freescale Enhanced Local Bus Controller, Flash Control Machine
+ *
+ * (C) Copyright 2006-2008
+ * Stefan Roese, DENX Software Engineering, sr@denx.de.
+ *
+ * Copyright (c) 2008 Freescale Semiconductor, Inc.
+ * Author: Scott Wood <scottwood@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/fsl_lbc.h>
+#include <nand.h>
+
+#define WINDOW_SIZE 8192
+
+static void nand_wait(void)
+{
+ fsl_lbc_t *regs = LBC_BASE_ADDR;
+
+ for (;;) {
+ uint32_t status = in_be32(&regs->ltesr);
+
+ if (status == 1)
+ return;
+
+ if (status & 1) {
+ puts("read failed (ltesr)\n");
+ for (;;);
+ }
+ }
+}
+
+static int nand_load_image(uint32_t offs, unsigned int uboot_size, void *vdst)
+{
+ fsl_lbc_t *regs = LBC_BASE_ADDR;
+ uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE;
+ const int large = CONFIG_SYS_NAND_OR_PRELIM & OR_FCM_PGS;
+ const int block_shift = large ? 17 : 14;
+ const int block_size = 1 << block_shift;
+ const int page_size = large ? 2048 : 512;
+ const int bad_marker = large ? page_size + 0 : page_size + 5;
+ int fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT) | 2;
+ int pos = 0;
+ char *dst = vdst;
+
+ if (offs & (block_size - 1)) {
+ puts("bad offset\n");
+ for (;;);
+ }
+
+ if (large) {
+ fmr |= FMR_ECCM;
+ out_be32(&regs->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
+ (NAND_CMD_READSTART << FCR_CMD1_SHIFT));
+ out_be32(&regs->fir,
+ (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_CW1 << FIR_OP3_SHIFT) |
+ (FIR_OP_RBW << FIR_OP4_SHIFT));
+ } else {
+ out_be32(&regs->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT);
+ out_be32(&regs->fir,
+ (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_RBW << FIR_OP3_SHIFT));
+ }
+
+ out_be32(&regs->fbcr, 0);
+ clrsetbits_be32(&regs->bank[0].br, BR_DECC, BR_DECC_CHK_GEN);
+
+ while (pos < uboot_size) {
+ int i = 0;
+ out_be32(&regs->fbar, offs >> block_shift);
+
+ do {
+ int j;
+ unsigned int page_offs = (offs & (block_size - 1)) << 1;
+
+ out_be32(&regs->ltesr, ~0);
+ out_be32(&regs->lteatr, 0);
+ out_be32(&regs->fpar, page_offs);
+ out_be32(&regs->fmr, fmr);
+ out_be32(&regs->lsor, 0);
+ nand_wait();
+
+ page_offs %= WINDOW_SIZE;
+
+ /*
+ * If either of the first two pages are marked bad,
+ * continue to the next block.
+ */
+ if (i++ < 2 && buf[page_offs + bad_marker] != 0xff) {
+ puts("skipping\n");
+ offs = (offs + block_size) & ~(block_size - 1);
+ pos &= ~(block_size - 1);
+ break;
+ }
+
+ for (j = 0; j < page_size; j++)
+ dst[pos + j] = buf[page_offs + j];
+
+ pos += page_size;
+ offs += page_size;
+ } while ((offs & (block_size - 1)) && (pos < uboot_size));
+ }
+
+ return 0;
+}
+
+/*
+ * The main entry for NAND booting. It's necessary that SDRAM is already
+ * configured and available since this code loads the main U-Boot image
+ * from NAND into SDRAM and starts it from there.
+ */
+void nand_boot(void)
+{
+ __attribute__((noreturn)) void (*uboot)(void);
+ /*
+ * Load U-Boot image from NAND into RAM
+ */
+ nand_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
+ CONFIG_SYS_NAND_U_BOOT_SIZE,
+ (void *)CONFIG_SYS_NAND_U_BOOT_DST);
+
+#ifdef CONFIG_NAND_ENV_DST
+ nand_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
+ (void *)CONFIG_NAND_ENV_DST);
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ nand_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE,
+ (void *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE);
+#endif
+#endif
+
+#ifdef CONFIG_SPL_FLUSH_IMAGE
+ /*
+ * Clean d-cache and invalidate i-cache, to
+ * make sure that no stale data is executed.
+ */
+ flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE);
+#endif
+
+ puts("transfering control\n");
+ /*
+ * Jump to U-Boot image
+ */
+ uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;
+ (*uboot)();
+}
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index b3b7c70..0878bec 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -30,6 +30,7 @@
#include <asm/errno.h>
#include <asm/fsl_ifc.h>
+#define FSL_IFC_V1_1_0 0x01010000
#define MAX_BANKS 4
#define ERR_BYTE 0xFF /* Value returned for read bytes
when read failed */
@@ -738,11 +739,66 @@ static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip)
{
}
+static void fsl_ifc_sram_init(void)
+{
+ struct fsl_ifc *ifc = ifc_ctrl->regs;
+ uint32_t cs = 0, csor = 0, csor_8k = 0, csor_ext = 0;
+ long long end_tick;
+
+ cs = ifc_ctrl->cs_nand >> IFC_NAND_CSEL_SHIFT;
+
+ /* Save CSOR and CSOR_ext */
+ csor = in_be32(&ifc_ctrl->regs->csor_cs[cs].csor);
+ csor_ext = in_be32(&ifc_ctrl->regs->csor_cs[cs].csor_ext);
+
+ /* chage PageSize 8K and SpareSize 1K*/
+ csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000;
+ out_be32(&ifc_ctrl->regs->csor_cs[cs].csor, csor_8k);
+ out_be32(&ifc_ctrl->regs->csor_cs[cs].csor_ext, 0x0000400);
+
+ /* READID */
+ out_be32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CMD0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT));
+ out_be32(&ifc->ifc_nand.nand_fcr0,
+ NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT);
+ out_be32(&ifc->ifc_nand.row3, 0x0);
+
+ out_be32(&ifc->ifc_nand.nand_fbcr, 0x0);
+
+ /* Program ROW0/COL0 */
+ out_be32(&ifc->ifc_nand.row0, 0x0);
+ out_be32(&ifc->ifc_nand.col0, 0x0);
+
+ /* set the chip select for NAND Transaction */
+ out_be32(&ifc->ifc_nand.nand_csel, ifc_ctrl->cs_nand);
+
+ /* start read seq */
+ out_be32(&ifc->ifc_nand.nandseq_strt, IFC_NAND_SEQ_STRT_FIR_STRT);
+
+ /* wait for NAND Machine complete flag or timeout */
+ end_tick = usec2ticks(IFC_TIMEOUT_MSECS * 1000) + get_ticks();
+
+ while (end_tick > get_ticks()) {
+ ifc_ctrl->status = in_be32(&ifc->ifc_nand.nand_evter_stat);
+
+ if (ifc_ctrl->status & IFC_NAND_EVTER_STAT_OPC)
+ break;
+ }
+
+ out_be32(&ifc->ifc_nand.nand_evter_stat, ifc_ctrl->status);
+
+ /* Restore CSOR and CSOR_ext */
+ out_be32(&ifc_ctrl->regs->csor_cs[cs].csor, csor);
+ out_be32(&ifc_ctrl->regs->csor_cs[cs].csor_ext, csor_ext);
+}
+
int board_nand_init(struct nand_chip *nand)
{
struct fsl_ifc_mtd *priv;
struct nand_ecclayout *layout;
- uint32_t cspr = 0, csor = 0;
+ uint32_t cspr = 0, csor = 0, ver = 0;
if (!ifc_ctrl) {
fsl_ifc_ctrl_init();
@@ -797,7 +853,7 @@ int board_nand_init(struct nand_chip *nand)
/* set up nand options */
nand->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
- NAND_USE_FLASH_BBT;
+ NAND_USE_FLASH_BBT | NAND_NO_SUBPAGE_WRITE;
if (cspr & CSPR_PORT_SIZE_16) {
nand->read_byte = fsl_ifc_read_byte16;
@@ -861,5 +917,9 @@ int board_nand_init(struct nand_chip *nand)
nand->ecc.mode = NAND_ECC_SOFT;
}
+ ver = in_be32(&ifc_ctrl->regs->ifc_rev);
+ if (ver == FSL_IFC_V1_1_0)
+ fsl_ifc_sram_init();
+
return 0;
}
diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c
index 4701be8..e38e151 100644
--- a/drivers/mtd/nand/mxs_nand.c
+++ b/drivers/mtd/nand/mxs_nand.c
@@ -1058,6 +1058,8 @@ int mxs_nand_init(struct mxs_nand_info *info)
{
struct mxs_gpmi_regs *gpmi_regs =
(struct mxs_gpmi_regs *)MXS_GPMI_BASE;
+ struct mxs_bch_regs *bch_regs =
+ (struct mxs_bch_regs *)MXS_BCH_BASE;
int i = 0, j;
info->desc = malloc(sizeof(struct mxs_dma_desc *) *
@@ -1081,6 +1083,7 @@ int mxs_nand_init(struct mxs_nand_info *info)
/* Reset the GPMI block. */
mxs_reset_block(&gpmi_regs->hw_gpmi_ctrl0_reg);
+ mxs_reset_block(&bch_regs->hw_bch_ctrl_reg);
/*
* Choose NAND mode, set IRQ polarity, disable write protection and
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index d3b71a5..a2d06be 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1245,7 +1245,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
if (unlikely(ops->mode == MTD_OOB_RAW))
ret = chip->ecc.read_page_raw(mtd, chip,
bufpoi, page);
- else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
+ else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
+ !oob)
ret = chip->ecc.read_subpage(mtd, chip,
col, bytes, bufpoi);
else
@@ -1256,7 +1257,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
/* Transfer not aligned data */
if (!aligned) {
- if (!NAND_SUBPAGE_READ(chip) && !oob &&
+ if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
!(mtd->ecc_stats.failed - stats.failed))
chip->pagebuf = realpage;
memcpy(buf, chip->buffers->databuf + col, bytes);
@@ -3150,6 +3151,10 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Invalidate the pagebuffer reference */
chip->pagebuf = -1;
+ /* Large page NAND with SOFT_ECC should support subpage reads */
+ if ((chip->ecc.mode == NAND_ECC_SOFT) && (chip->page_shift > 9))
+ chip->options |= NAND_SUBPAGE_READ;
+
/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c
index c4752a7..2ba0c5e 100644
--- a/drivers/mtd/nand/nand_util.c
+++ b/drivers/mtd/nand/nand_util.c
@@ -50,8 +50,8 @@
#include <nand.h>
#include <jffs2/jffs2.h>
-typedef struct erase_info erase_info_t;
-typedef struct mtd_info mtd_info_t;
+typedef struct erase_info erase_info_t;
+typedef struct mtd_info mtd_info_t;
/* support only for native endian JFFS2 */
#define cpu_to_je16(x) (x)
@@ -59,7 +59,7 @@ typedef struct mtd_info mtd_info_t;
/**
* nand_erase_opts: - erase NAND flash with support for various options
- * (jffs2 formating)
+ * (jffs2 formatting)
*
* @param meminfo NAND device to erase
* @param opts options, @see struct nand_erase_options
@@ -80,8 +80,8 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
struct mtd_oob_ops oob_opts;
struct nand_chip *chip = meminfo->priv;
- if ((opts->offset & (meminfo->writesize - 1)) != 0) {
- printf("Attempt to erase non page aligned data\n");
+ if ((opts->offset & (meminfo->erasesize - 1)) != 0) {
+ printf("Attempt to erase non block-aligned data\n");
return -1;
}
@@ -94,8 +94,8 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
erase_length = lldiv(opts->length + meminfo->erasesize - 1,
meminfo->erasesize);
- cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
- cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
+ cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
cleanmarker.totlen = cpu_to_je32(8);
/* scrub option allows to erase badblock. To prevent internal
@@ -118,7 +118,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
erased_length < erase_length;
erase.addr += meminfo->erasesize) {
- WATCHDOG_RESET ();
+ WATCHDOG_RESET();
if (!opts->scrub && bbtest) {
int ret = meminfo->block_isbad(meminfo, erase.addr);
@@ -259,7 +259,7 @@ int nand_lock(struct mtd_info *mtd, int tight)
* flash
*
* @param mtd nand mtd instance
- * @param offset page address to query (muss be page aligned!)
+ * @param offset page address to query (must be page-aligned!)
*
* @return -1 in case of error
* >0 lock status:
@@ -281,7 +281,7 @@ int nand_get_lock_status(struct mtd_info *mtd, loff_t offset)
if ((offset & (mtd->writesize - 1)) != 0) {
- printf ("nand_get_lock_status: "
+ printf("nand_get_lock_status: "
"Start address must be beginning of "
"nand page!\n");
ret = -1;
@@ -332,20 +332,20 @@ int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length,
/* check the WP bit */
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
if (!(chip->read_byte(mtd) & NAND_STATUS_WP)) {
- printf ("nand_unlock: Device is write protected!\n");
+ printf("nand_unlock: Device is write protected!\n");
ret = -1;
goto out;
}
if ((start & (mtd->erasesize - 1)) != 0) {
- printf ("nand_unlock: Start address must be beginning of "
+ printf("nand_unlock: Start address must be beginning of "
"nand block!\n");
ret = -1;
goto out;
}
if (length == 0 || (length & (mtd->erasesize - 1)) != 0) {
- printf ("nand_unlock: Length must be a multiple of nand block "
+ printf("nand_unlock: Length must be a multiple of nand block "
"size %08x!\n", mtd->erasesize);
ret = -1;
goto out;
@@ -485,7 +485,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
pages = nand->erasesize / nand->writesize;
blocksize = (pages * nand->oobsize) + nand->erasesize;
if (*length % (nand->writesize + nand->oobsize)) {
- printf ("Attempt to write incomplete page"
+ printf("Attempt to write incomplete page"
" in yaffs mode\n");
return -EINVAL;
}
@@ -507,25 +507,25 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
* partition boundary). So don't try to handle that.
*/
if ((offset & (nand->writesize - 1)) != 0) {
- printf ("Attempt to write non page aligned data\n");
+ printf("Attempt to write non page-aligned data\n");
*length = 0;
return -EINVAL;
}
need_skip = check_skip_len(nand, offset, *length);
if (need_skip < 0) {
- printf ("Attempt to write outside the flash area\n");
+ printf("Attempt to write outside the flash area\n");
*length = 0;
return -EINVAL;
}
if (!need_skip && !(flags & WITH_DROP_FFS)) {
- rval = nand_write (nand, offset, length, buffer);
+ rval = nand_write(nand, offset, length, buffer);
if (rval == 0)
return 0;
*length = 0;
- printf ("NAND write to offset %llx failed %d\n",
+ printf("NAND write to offset %llx failed %d\n",
offset, rval);
return rval;
}
@@ -534,10 +534,10 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
size_t block_offset = offset & (nand->erasesize - 1);
size_t write_size, truncated_write_size;
- WATCHDOG_RESET ();
+ WATCHDOG_RESET();
- if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
- printf ("Skip bad block 0x%08llx\n",
+ if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) {
+ printf("Skip bad block 0x%08llx\n",
offset & ~(nand->erasesize - 1));
offset += nand->erasesize - block_offset;
continue;
@@ -592,7 +592,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
}
if (rval != 0) {
- printf ("NAND write to offset %llx failed %d\n",
+ printf("NAND write to offset %llx failed %d\n",
offset, rval);
*length -= left_to_write;
return rval;
@@ -608,13 +608,13 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
* nand_read_skip_bad:
*
* Read image from NAND flash.
- * Blocks that are marked bad are skipped and the next block is readen
+ * Blocks that are marked bad are skipped and the next block is read
* instead as long as the image is short enough to fit even after skipping the
* bad blocks.
*
* @param nand NAND device
* @param offset offset in flash
- * @param length buffer length, on return holds remaining bytes to read
+ * @param length buffer length, on return holds number of read bytes
* @param buffer buffer to write to
* @return 0 in case of success
*/
@@ -627,25 +627,25 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
int need_skip;
if ((offset & (nand->writesize - 1)) != 0) {
- printf ("Attempt to read non page aligned data\n");
+ printf("Attempt to read non page-aligned data\n");
*length = 0;
return -EINVAL;
}
need_skip = check_skip_len(nand, offset, *length);
if (need_skip < 0) {
- printf ("Attempt to read outside the flash area\n");
+ printf("Attempt to read outside the flash area\n");
*length = 0;
return -EINVAL;
}
if (!need_skip) {
- rval = nand_read (nand, offset, length, buffer);
+ rval = nand_read(nand, offset, length, buffer);
if (!rval || rval == -EUCLEAN)
return 0;
*length = 0;
- printf ("NAND read from offset %llx failed %d\n",
+ printf("NAND read from offset %llx failed %d\n",
offset, rval);
return rval;
}
@@ -654,10 +654,10 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
size_t block_offset = offset & (nand->erasesize - 1);
size_t read_length;
- WATCHDOG_RESET ();
+ WATCHDOG_RESET();
- if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
- printf ("Skipping bad block 0x%08llx\n",
+ if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) {
+ printf("Skipping bad block 0x%08llx\n",
offset & ~(nand->erasesize - 1));
offset += nand->erasesize - block_offset;
continue;
@@ -668,9 +668,9 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
else
read_length = nand->erasesize - block_offset;
- rval = nand_read (nand, offset, &read_length, p_buffer);
+ rval = nand_read(nand, offset, &read_length, p_buffer);
if (rval && rval != -EUCLEAN) {
- printf ("NAND read from offset %llx failed %d\n",
+ printf("NAND read from offset %llx failed %d\n",
offset, rval);
*length -= left_to_read;
return rval;
@@ -683,3 +683,125 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
return 0;
}
+
+#ifdef CONFIG_CMD_NAND_TORTURE
+
+/**
+ * check_pattern:
+ *
+ * Check if buffer contains only a certain byte pattern.
+ *
+ * @param buf buffer to check
+ * @param patt the pattern to check
+ * @param size buffer size in bytes
+ * @return 1 if there are only patt bytes in buf
+ * 0 if something else was found
+ */
+static int check_pattern(const u_char *buf, u_char patt, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (buf[i] != patt)
+ return 0;
+ return 1;
+}
+
+/**
+ * nand_torture:
+ *
+ * Torture a block of NAND flash.
+ * This is useful to determine if a block that caused a write error is still
+ * good or should be marked as bad.
+ *
+ * @param nand NAND device
+ * @param offset offset in flash
+ * @return 0 if the block is still good
+ */
+int nand_torture(nand_info_t *nand, loff_t offset)
+{
+ u_char patterns[] = {0xa5, 0x5a, 0x00};
+ struct erase_info instr = {
+ .mtd = nand,
+ .addr = offset,
+ .len = nand->erasesize,
+ };
+ size_t retlen;
+ int err, ret = -1, i, patt_count;
+ u_char *buf;
+
+ if ((offset & (nand->erasesize - 1)) != 0) {
+ puts("Attempt to torture a block at a non block-aligned offset\n");
+ return -EINVAL;
+ }
+
+ if (offset + nand->erasesize > nand->size) {
+ puts("Attempt to torture a block outside the flash area\n");
+ return -EINVAL;
+ }
+
+ patt_count = ARRAY_SIZE(patterns);
+
+ buf = malloc(nand->erasesize);
+ if (buf == NULL) {
+ puts("Out of memory for erase block buffer\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < patt_count; i++) {
+ err = nand->erase(nand, &instr);
+ if (err) {
+ printf("%s: erase() failed for block at 0x%llx: %d\n",
+ nand->name, instr.addr, err);
+ goto out;
+ }
+
+ /* Make sure the block contains only 0xff bytes */
+ err = nand->read(nand, offset, nand->erasesize, &retlen, buf);
+ if ((err && err != -EUCLEAN) || retlen != nand->erasesize) {
+ printf("%s: read() failed for block at 0x%llx: %d\n",
+ nand->name, instr.addr, err);
+ goto out;
+ }
+
+ err = check_pattern(buf, 0xff, nand->erasesize);
+ if (!err) {
+ printf("Erased block at 0x%llx, but a non-0xff byte was found\n",
+ offset);
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Write a pattern and check it */
+ memset(buf, patterns[i], nand->erasesize);
+ err = nand->write(nand, offset, nand->erasesize, &retlen, buf);
+ if (err || retlen != nand->erasesize) {
+ printf("%s: write() failed for block at 0x%llx: %d\n",
+ nand->name, instr.addr, err);
+ goto out;
+ }
+
+ err = nand->read(nand, offset, nand->erasesize, &retlen, buf);
+ if ((err && err != -EUCLEAN) || retlen != nand->erasesize) {
+ printf("%s: read() failed for block at 0x%llx: %d\n",
+ nand->name, instr.addr, err);
+ goto out;
+ }
+
+ err = check_pattern(buf, patterns[i], nand->erasesize);
+ if (!err) {
+ printf("Pattern 0x%.2x checking failed for block at "
+ "0x%llx\n", patterns[i], offset);
+ ret = -EIO;
+ goto out;
+ }
+ }
+
+ ret = 0;
+
+out:
+ free(buf);
+ return ret;
+}
+
+#endif