This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

am335x nand write problem

We adapted yocto project and u-boot 's version is u-boot-ti-staging (2015 07), we add

--- a/board/ti/am335x/mux.c
+++ b/board/ti/am335x/mux.c
@@ -359,6 +359,7 @@ void enable_board_pin_mux(struct am335x_baseboard_id *header
                configure_module_pin_mux(gpio0_7_pin_mux);
                configure_module_pin_mux(rgmii1_pin_mux);
                configure_module_pin_mux(mmc0_pin_mux_sk_evm);
+               configure_module_pin_mux(nand_pin_mux);


and when boot it can recognize nand flash.

U-Boot 2015.07 (Oct 21 2015 - 13:07:17 +0800)

       Watchdog enabled
I2C:   ready
DRAM:  256 MiB
NAND:  256 MiB
MMC:   OMAP SD/MMC: 0, OMAP SD/MMC: 1
Net:   cpsw, usb_ether

then I type these instructions below:

U-Boot # mmc rescan
U-Boot # nand erase.chip
U-Boot # fatload mmc 0 0x82000000 MLO
U-Boot # nand write 0x82000000 NAND.SPL
U-Boot # nand write 0x82000000 NAND.SPL.backup1
U-Boot # nand write 0x82000000 NAND.SPL.backup2
U-Boot # nand write 0x82000000 NAND.SPL.backup3
U-Boot # fatload mmc 0 0x82000000 u-boot.img
U-Boot # nand write 0x82000000 NAND.u-boot
U-Boot # fatload mmc 0 0x82000000 zImage
U-Boot # nand write 0x82000000 NAND.kernel
U-Boot # run findfdt
U-Boot # load mmc 0 0x82000000 $fdtfile
U-Boot # nand write 0x82000000 NAND.u-boot-spl-os
U-Boot # fatload mmc 0 0x82000000 ubi.img
U-Boot # nand write 0x82000000 NAND.file-system
U-Boot # saveenv


MLO to device tree file has not any problem, but when I flash ubi.img, there is error:

NAND write: device 0 offset 0xa00000, size 0xf600000
NAND write to offset a00000 failed -5
 0 bytes written: ERROR

Dose anyone kindly tell me what I lost?  Thank you.

  • I will forward this to the SW team.
  • Hello Bernie,

    Are the NAND device related configurations in include/configs/am335x_evm.h match the NAND specifications? Is this issue reproducible if you go through these steps?

    # Inject a fake bit that is stuck low (2K page flash being used)
    nand erase 0 0x20000
    mw.b 0x82000000 0xff 0x1000
    mw.b 0x82000000 0xfe 1
    nand write.raw 0x82000000 0x0 0x1
    
    # Write some data which needs to toggle the fake stuck bit
    mw.b 0x82000000 0xab 1
    nand write 0x82000000 0x0 0x800
    
    An error will occur:
    NAND write: device 0 offset 0x0, size 0x800
    NAND write to offset 0 failed -5
    0 bytes written: ERROR
    
    But you can verify data was correctly written:
    # Show data is correct when ECC is used
    nand read 0x82000000 0x0 0x800
    md 0x82000000
    
    # Show the bit is still stuck low
    nand read.raw 0x82000000 0x0 0x1
    md 0x82000000


    Best regards,
    Kemal

  • Dear Kemal

    We follow your step below:

       U-Boot# mw.b 0x82000000 0xff 0x1000
    U-Boot# mw.b 0x82000000 0xfe 1
    U-Boot# nand write.raw 0x82000000 0x0 0x1

    NAND write:  2112 bytes written: OK
    U-Boot# mw.b 0x82000000 0xab 1
    U-Boot# nand write 0x82000000 0x0 0x800

    NAND write: device 0 offset 0x0, size 0x800
    nand: bit-flip corrected @data=0
     2048 bytes written: OK
    U-Boot# nand read 0x82000000 0x0 0x800

    NAND read: device 0 offset 0x0, size 0x800
    nand: bit-flip corrected @data=0
     2048 bytes read: OK
    U-Boot# md 0x82000000
    82000000: ffffffab ffffffff ffffffff ffffffff    ................
    82000010: ffffffff ffffffff ffffffff ffffffff    ................
    82000020: ffffffff ffffffff ffffffff ffffffff    ................
    82000030: ffffffff ffffffff ffffffff ffffffff    ................
    82000040: ffffffff ffffffff ffffffff ffffffff    ................
    82000050: ffffffff ffffffff ffffffff ffffffff    ................
    82000060: ffffffff ffffffff ffffffff ffffffff    ................
    82000070: ffffffff ffffffff ffffffff ffffffff    ................
    82000080: ffffffff ffffffff ffffffff ffffffff    ................
    82000090: ffffffff ffffffff ffffffff ffffffff    ................
    820000a0: ffffffff ffffffff ffffffff ffffffff    ................
    820000b0: ffffffff ffffffff ffffffff ffffffff    ................
    820000c0: ffffffff ffffffff ffffffff ffffffff    ................
    820000d0: ffffffff ffffffff ffffffff ffffffff    ................
    820000e0: ffffffff ffffffff ffffffff ffffffff    ................
    820000f0: ffffffff ffffffff ffffffff ffffffff    ................
    U-Boot#

    =====================================================================================

    But There are no error you mentioned.



    Q:"Are the NAND device related configurations in include/configs/am335x_evm.h match the NAND specifications?"
    A: Did you mean mtdpart args in am335x_evm.h? We have not do any change in am335x_evm.h.

    only add

    +#define CONFIG_CMD_UBI
    +#define CONFIG_CMD_UBIFS
    +#define CONFIG_RBTREE
    +#define CONFIG_MTD_PARTITIONS
    +#define CONFIG_LZO

    Thank you.
  • Dear kemal

    Even if an error of "NAND write: device 0 offset 0xa00000, size 0xf600000
    NAND write to offset a00000 failed -5
    0 bytes written: ERROR" when write ubi.img to NAND , the kernel can mount filesystem from NAND.

    Thank you.
  • Yes, I meant these NAND device specific ones,

    /* NAND: device related configs */
    #define CONFIG_SYS_NAND_5_ADDR_CYCLE
    #define CONFIG_SYS_NAND_PAGE_COUNT (CONFIG_SYS_NAND_BLOCK_SIZE / \
    CONFIG_SYS_NAND_PAGE_SIZE)
    #define CONFIG_SYS_NAND_PAGE_SIZE 2048
    #define CONFIG_SYS_NAND_OOBSIZE 64
    #define CONFIG_SYS_NAND_BLOCK_SIZE (128*1024)


    but since the kernel can mount the file-system the error may indicate a skipped block in this offset. I suppose it might be skipped due to write protected or not properly aligned block.

  • Hi,

    Do you know of any solution yet?

    I have the exact same issue when writing ubi.img (around 70M) to the 'NAND.filesystem' block.

    'Timeout!NAND write to offset a00000 failed -5'

    All other blocks were fine. 

    Thanks in advance.

    George

  • Sorry

    I have not yet.

  • Hi.

    If you start searching through the U-Boot code. You will see that, this "Timeout!" is printed out because of a timeout in nand_wait() method:

    drivers/mtd/nand/nand_base.c

    /**
     * nand_wait - [DEFAULT] wait until the command is done
     * @mtd: MTD device structure
     * @chip: NAND chip structure
     *
     * Wait for command done. This applies to erase and program only. Erase can
     * take up to 400ms and program up to 20ms according to general NAND and
     * SmartMedia specs.
     */
    static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
    {
    	unsigned long	timeo;
    	int state = chip->state;
    	u32 time_start;
    
    	if (state == FL_ERASING)
    		timeo = (CONFIG_SYS_HZ * 400) / 1000;
    	else
    		timeo = (CONFIG_SYS_HZ * 20) / 1000;
    
    	if ((state == FL_ERASING) && (chip->options & NAND_IS_AND))
    		chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1);
    	else
    		chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
    
    	time_start = get_timer(0);
    
    	while (1) {
    		if (get_timer(time_start) > timeo) {
    			printf("Timeout!");
    			return 0x01;
    		}
    
    		if (chip->dev_ready) {
    			if (chip->dev_ready(mtd))
    				break;
    		} else {
    			if (chip->read_byte(mtd) & NAND_STATUS_READY)
    				break;
    		}
    	}
    #ifdef PPCHAMELON_NAND_TIMER_HACK
    	time_start = get_timer(0);
    	while (get_timer(time_start) < 10)
    		;
    #endif /*  PPCHAMELON_NAND_TIMER_HACK */
    
    	return (int)chip->read_byte(mtd);
    }

    And the following -5 I/O error is the returned value of nand_write() within nand_write_skip_bad() method.

    drivers/mtd/nand/nand_util.c

    /**
     * nand_write_skip_bad:
     *
     * Write image to NAND flash.
     * Blocks that are marked bad are skipped and the is written to the next
     * block instead as long as the image is short enough to fit even after
     * skipping the bad blocks.  Due to bad blocks we may not be able to
     * perform the requested write.  In the case where the write would
     * extend beyond the end of the NAND device, both length and actual (if
     * not NULL) are set to 0.  In the case where the write would extend
     * beyond the limit we are passed, length is set to 0 and actual is set
     * to the required length.
     *
     * @param nand  	NAND device
     * @param offset	offset in flash
     * @param length	buffer length
     * @param actual	set to size required to write length worth of
     *			buffer or 0 on error, if not NULL
     * @param lim		maximum size that actual may be in order to not
     *			exceed the buffer
     * @param buffer        buffer to read from
     * @param flags		flags modifying the behaviour of the write to NAND
     * @return		0 in case of success
     */
    int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
    		size_t *actual, loff_t lim, u_char *buffer, int flags)
    {
    	int rval = 0, blocksize;
    	size_t left_to_write = *length;
    	size_t used_for_write = 0;
    	u_char *p_buffer = buffer;
    	int need_skip;
    
    	if (actual)
    		*actual = 0;
    
    #ifdef CONFIG_CMD_NAND_YAFFS
    	if (flags & WITH_YAFFS_OOB) {
    		if (flags & ~WITH_YAFFS_OOB)
    			return -EINVAL;
    
    		int pages;
    		pages = nand->erasesize / nand->writesize;
    		blocksize = (pages * nand->oobsize) + nand->erasesize;
    		if (*length % (nand->writesize + nand->oobsize)) {
    			printf("Attempt to write incomplete page"
    				" in yaffs mode\n");
    			return -EINVAL;
    		}
    	} else
    #endif
    	{
    		blocksize = nand->erasesize;
    	}
    
    	/*
    	 * nand_write() handles unaligned, partial page writes.
    	 *
    	 * We allow length to be unaligned, for convenience in
    	 * using the $filesize variable.
    	 *
    	 * However, starting at an unaligned offset makes the
    	 * semantics of bad block skipping ambiguous (really,
    	 * you should only start a block skipping access at a
    	 * partition boundary).  So don't try to handle that.
    	 */
    	if ((offset & (nand->writesize - 1)) != 0) {
    		printf("Attempt to write non page-aligned data\n");
    		*length = 0;
    		return -EINVAL;
    	}
    
    	need_skip = check_skip_len(nand, offset, *length, &used_for_write);
    
    	if (actual)
    		*actual = used_for_write;
    
    	if (need_skip < 0) {
    		printf("Attempt to write outside the flash area\n");
    		*length = 0;
    		return -EINVAL;
    	}
    
    	if (used_for_write > lim) {
    		puts("Size of write exceeds partition or device limit\n");
    		*length = 0;
    		return -EFBIG;
    	}
    
    	if (!need_skip && !(flags & WITH_DROP_FFS)) {
    		rval = nand_write(nand, offset, length, buffer);
    		if (rval == 0)
    			return 0;
    
    		*length = 0;
    		printf("NAND write to offset %llx failed %d\n",
    			offset, rval);
    		return rval;
    	}
    
    	while (left_to_write > 0) {
    		size_t block_offset = offset & (nand->erasesize - 1);
    		size_t write_size, truncated_write_size;
    
    		WATCHDOG_RESET();
    
    		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;
    		}
    
    		if (left_to_write < (blocksize - block_offset))
    			write_size = left_to_write;
    		else
    			write_size = blocksize - block_offset;
    
    #ifdef CONFIG_CMD_NAND_YAFFS
    		if (flags & WITH_YAFFS_OOB) {
    			int page, pages;
    			size_t pagesize = nand->writesize;
    			size_t pagesize_oob = pagesize + nand->oobsize;
    			struct mtd_oob_ops ops;
    
    			ops.len = pagesize;
    			ops.ooblen = nand->oobsize;
    			ops.mode = MTD_OPS_AUTO_OOB;
    			ops.ooboffs = 0;
    
    			pages = write_size / pagesize_oob;
    			for (page = 0; page < pages; page++) {
    				WATCHDOG_RESET();
    
    				ops.datbuf = p_buffer;
    				ops.oobbuf = ops.datbuf + pagesize;
    
    				rval = mtd_write_oob(nand, offset, &ops);
    				if (rval != 0)
    					break;
    
    				offset += pagesize;
    				p_buffer += pagesize_oob;
    			}
    		}
    		else
    #endif
    		{
    			truncated_write_size = write_size;
    #ifdef CONFIG_CMD_NAND_TRIMFFS
    			if (flags & WITH_DROP_FFS)
    				truncated_write_size = drop_ffs(nand, p_buffer,
    						&write_size);
    #endif
    
    			rval = nand_write(nand, offset, &truncated_write_size,
    					p_buffer);
    			offset += write_size;
    			p_buffer += write_size;
    		}
    
    		if (rval != 0) {
    			printf("NAND write to offset %llx failed %d\n",
    				offset, rval);
    			*length -= left_to_write;
    			return rval;
    		}
    
    		left_to_write -= write_size;
    	}
    
    	return 0;
    }

    Examining nand_write().

    drivers/mtd/nand/nand_base.c

    /**
     * nand_write - [MTD Interface] NAND write with ECC
     * @mtd: MTD device structure
     * @to: offset to write to
     * @len: number of bytes to write
     * @retlen: pointer to variable to store the number of written bytes
     * @buf: the data to write
     *
     * NAND write with ECC.
     */
    static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
    			  size_t *retlen, const uint8_t *buf)
    {
    	struct nand_chip *chip = mtd->priv;
    	struct mtd_oob_ops ops;
    	int ret;
    
    	nand_get_device(chip, mtd, FL_WRITING);
    	ops.len = len;
    	ops.datbuf = (uint8_t *)buf;
    	ops.oobbuf = NULL;
    	ops.mode = MTD_OPS_PLACE_OOB;
    	ret = nand_do_write_ops(mtd, to, &ops);
    	*retlen = ops.retlen;
    	nand_release_device(mtd);
    	return ret;
    }

    The return value of nand_write() is the exit code of nand_do_write_ops() method:

    drivers/mtd/nand/nand_base.c

    /**
     * nand_do_write_ops - [INTERN] NAND write with ECC
     * @mtd: MTD device structure
     * @to: offset to write to
     * @ops: oob operations description structure
     *
     * NAND write with ECC.
     */
    static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
    			     struct mtd_oob_ops *ops)
    {
    	int chipnr, realpage, page, blockmask, column;
    	struct nand_chip *chip = mtd->priv;
    	uint32_t writelen = ops->len;
    
    	uint32_t oobwritelen = ops->ooblen;
    	uint32_t oobmaxlen = ops->mode == MTD_OPS_AUTO_OOB ?
    				mtd->oobavail : mtd->oobsize;
    
    	uint8_t *oob = ops->oobbuf;
    	uint8_t *buf = ops->datbuf;
    	int ret, subpage;
    	int oob_required = oob ? 1 : 0;
    
    	ops->retlen = 0;
    	if (!writelen)
    		return 0;
    
    	column = to & (mtd->writesize - 1);
    	subpage = column || (writelen & (mtd->writesize - 1));
    
    	if (subpage && oob)
    		return -EINVAL;
    
    	chipnr = (int)(to >> chip->chip_shift);
    	chip->select_chip(mtd, chipnr);
    
    	/* Check, if it is write protected */
    	if (nand_check_wp(mtd)) {
    		printk (KERN_NOTICE "nand_do_write_ops: Device is write protected\n");
    		return -EIO;
    	}
    
    	realpage = (int)(to >> chip->page_shift);
    	page = realpage & chip->pagemask;
    	blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
    
    	/* Invalidate the page cache, when we write to the cached page */
    	if (to <= (chip->pagebuf << chip->page_shift) &&
    	    (chip->pagebuf << chip->page_shift) < (to + ops->len))
    		chip->pagebuf = -1;
    
    	/* Don't allow multipage oob writes with offset */
    	if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen))
    		return -EINVAL;
    
    	while (1) {
    		WATCHDOG_RESET();
    
    		int bytes = mtd->writesize;
    		int cached = writelen > bytes && page != blockmask;
    		uint8_t *wbuf = buf;
    
    		/* Partial page write? */
    		if (unlikely(column || writelen < mtd->writesize)) {
    			cached = 0;
    			bytes = min_t(int, bytes - column, (int) writelen);
    			chip->pagebuf = -1;
    			memset(chip->buffers->databuf, 0xff, mtd->writesize);
    			memcpy(&chip->buffers->databuf[column], buf, bytes);
    			wbuf = chip->buffers->databuf;
    		}
    
    		if (unlikely(oob)) {
    			size_t len = min(oobwritelen, oobmaxlen);
    			oob = nand_fill_oob(mtd, oob, len, ops);
    			oobwritelen -= len;
    		} else {
    			/* We still need to erase leftover OOB data */
    			memset(chip->oob_poi, 0xff, mtd->oobsize);
    		}
    
    		ret = chip->write_page(mtd, chip, wbuf, oob_required, page,
    				       cached, (ops->mode == MTD_OPS_RAW));
    		if (ret)
    			break;
    
    		writelen -= bytes;
    		if (!writelen)
    			break;
    
    		column = 0;
    		buf += bytes;
    		realpage++;
    
    		page = realpage & chip->pagemask;
    		/* Check, if we cross a chip boundary */
    		if (!page) {
    			chipnr++;
    			chip->select_chip(mtd, -1);
    			chip->select_chip(mtd, chipnr);
    		}
    	}
    
    	ops->retlen = ops->len - writelen;
    	if (unlikely(oob))
    		ops->oobretlen = ops->ooblen;
    	return ret;
    }

    The nand_do_write_ops() returns the exit code of chip->write_page().

    If you define CONFIG_MTD_DEBUG and set the highest verbosity level in include/configs/ti_armv7_common.h, you should be able to get more clue on this issue. You can also place your custom MTDDEBUG labels and print out the needed variables respectively.

    include/configs/ti_armv7_common.h

    #define CONFIG_MTD_DEBUG
    #define CONFIG_MTD_DEBUG_VERBOSE 3

    Best regards,
    Kemal

  • Hi George,

    Even I'm facing exactly same problem while writing ubi.img(around 180M) to nand filesystem.

    If you were able to solve your problem, please explain the solution.

    Any kind of support will be appreciated.

    Thanks,

    Deshvir

  • Seems these folks have found the root cause of this issue. The answer is in this post.