diff options
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/Makefile | 34 | ||||
-rw-r--r-- | drivers/mtd/nand/fsl_elbc_nand.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/fsl_elbc_spl.c | 168 | ||||
-rw-r--r-- | drivers/mtd/nand/fsl_ifc_nand.c | 64 | ||||
-rw-r--r-- | drivers/mtd/nand/mxs_nand.c | 3 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 9 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_util.c | 188 |
7 files changed, 422 insertions, 46 deletions
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index beb99cacb66..28e52bd08ea 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 9076ad4cdc3..834a8a64983 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 00000000000..50ff4fe93e4 --- /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(®s->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(®s->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | + (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); + out_be32(®s->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(®s->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT); + out_be32(®s->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(®s->fbcr, 0); + clrsetbits_be32(®s->bank[0].br, BR_DECC, BR_DECC_CHK_GEN); + + while (pos < uboot_size) { + int i = 0; + out_be32(®s->fbar, offs >> block_shift); + + do { + int j; + unsigned int page_offs = (offs & (block_size - 1)) << 1; + + out_be32(®s->ltesr, ~0); + out_be32(®s->lteatr, 0); + out_be32(®s->fpar, page_offs); + out_be32(®s->fmr, fmr); + out_be32(®s->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 b3b7c705e18..0878bece675 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 4701be846c6..e38e1512540 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 d3b71a50adc..a2d06be99fc 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 c4752a7cbf8..2ba0c5ef956 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 |