[U-Boot-Users] [PATCH] New NAND subsystem: mtd like commands support jffs2 and bad blocks

Stefan Roese sr at denx.de
Sun Oct 8 12:26:29 CEST 2006


Hi Guido,

On Saturday 07 October 2006 16:20, Guido Classen wrote:
> > Looks promising. I'm not completely happy with the implementation though:
>
> thank you for reviewing the patch. Sorry for wrong coding style on some
> places. Some code was as said ported from linux mtd-utils code.

Understood. Thanks.

> I have followed your suggestion to rename the old NAND sub-system to
> "legacy NAND sub-system". It was very important during my tests to
> quickly identify which type of NAND sub-system was installed in the
> different boards laying around. Thus I renamed the new one.

OK.

> Attached a revised patch following your suggestions regarding coding style.

Nearly done. Here some further remarks:

> diff -Nrub --exclude='*~' --exclude=.depend
> u-boot-weiss-unpatched/common/cmd_nand.c u-boot-weiss/common/cmd_nand.c ---
> u-boot-weiss-unpatched/common/cmd_nand.c	2006-10-07 14:46:21.000000000
> +0200 +++ u-boot-weiss/common/cmd_nand.c	2006-10-07 15:57:14.000000000
> +0200 @@ -178,7 +178,10 @@
>
>  	if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 &&
>  	    strncmp(cmd, "dump", 4) != 0 &&
> -	    strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0)
> +	    strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 &&
> +	    strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 &&
> +	    strcmp(cmd, "biterr") != 0 &&
> +	    strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 )
>  		goto usage;
>
>  	/* the following commands operate on the current device */
> @@ -197,14 +200,68 @@
>  		return 0;
>  	}
>
> -	if (strcmp(cmd, "erase") == 0) {
> -		arg_off_size(argc - 2, argv + 2, &off, &size, nand->size);
> +	if (strcmp(cmd, "erase") == 0
> +	    || strcmp(cmd, "scrub") == 0) {
> +		nand_erase_options_t opts;
> +		int clean = argc >= 3 && !strcmp("clean", argv[2]);
> +		int rest_argc = argc - 2;
> +		char **rest_argv = argv + 2;
> +		int scrub = !strcmp(cmd, "scrub");
> +
> +		if (clean) {
> +			rest_argc--;
> +			rest_argv++;
> +		}
> +
> +		if (rest_argc == 0) {
> +
> +			printf("\nNAND %s: device %d whole chip\n",
> +			       cmd,
> +			       nand_curr_device);
> +
> +			off = size = 0;
> +		} else {
> +			arg_off_size(rest_argc, rest_argv, &off, &size,
> +				     nand->size);
> +
>  		if (off == 0 && size == 0)
>  			return 1;

Indentation wrong here.

>
> -		printf("\nNAND erase: device %d offset 0x%x, size 0x%x ",
> +			printf("\nNAND %s: device %d offset 0x%x, "
> +			       "size 0x%x\n",
> +			       cmd,
>  		       nand_curr_device, off, size);
> -		ret = nand_erase(nand, off, size);
> +		}
> +
> +		memset(&opts, 0, sizeof(opts));
> +		opts.offset = off;
> +		opts.length = size;
> +		opts.jffs2  = clean;
> +		opts.quiet  = 1;
> +
> +		if (scrub) {
> +
> +			printf("Warning: "
> +			       "scrub option will erase all factory set "
> +			       "bad blocks!\n"
> +			       "	 "
> +			       "There is now reliable way to bring them back.\n"
> +			       "	 "
> +			       "Use this command only for testing purposes "
> +			       "if you\n"
> +			       "	 "
> +			       "are shure what you are doing!\n"
> +			       "\nRealy scrub this NAND flash? <y/N>\n"
> +				);
> +
> +			if (getc() == 'y' && getc() == '\r') {
> +				opts.scrub = 1;
> +			} else {
> +				printf ("scrub aborted\n");
> +				return -1;
> +			}
> +		}
> +		ret = nand_erase_opts(nand, &opts);
>  		printf("%s\n", ret ? "ERROR" : "OK");
>
>  		return ret == 0 ? 0 : 1;
> @@ -228,37 +285,158 @@
>
>  	/* read write */
>  	if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
> +		int read;
> +
>  		if (argc < 4)
>  			goto usage;
> -/*
> -		s = strchr(cmd, '.');
> -		clean = CLEAN_NONE;
> -		if (s != NULL) {
> -			if (strcmp(s, ".jffs2") == 0 || strcmp(s, ".e") == 0
> -			    || strcmp(s, ".i"))
> -				clean = CLEAN_JFFS2;
> -		}
> -*/
> +
>  		addr = (ulong)simple_strtoul(argv[2], NULL, 16);
>
>  		arg_off_size(argc - 3, argv + 3, &off, &size, nand->size);
>  		if (off == 0 && size == 0)
>  			return 1;
>
> -		i = strncmp(cmd, "read", 4) == 0;	/* 1 = read, 0 = write */
> +		read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
>  		printf("\nNAND %s: device %d offset %u, size %u ... ",
> -		       i ? "read" : "write", nand_curr_device, off, size);
> +		       read ? "read" : "write", nand_curr_device, off, size);
> +
> +		s = strchr(cmd, '.');
> +		if (s != NULL
> +		    && (!strcmp(s, ".jffs2") || !strcmp(s, ".e")
> +			|| !strcmp(s, ".i"))) {
> +			if (read) {
> +				/* read */
> +				nand_read_options_t opts;
> +				memset(&opts, 0, sizeof(opts));
> +				opts.buffer	= (u_char*) addr;
> +				opts.length	= size;
> +				opts.offset	= off;
> +				opts.quiet      = 1;
> +				ret = nand_read_opts(nand, &opts);
> +			} else {
> +				/* write */
> +				nand_write_options_t opts;
> +				memset(&opts, 0, sizeof(opts));
> +				opts.buffer	= (u_char*) addr;
> +				opts.length	= size;
> +				opts.offset	= off;
> +				/* opts.forcejffs2 = 1; */
> +				opts.pad	= 1;
> +				opts.blockalign = 1;
> +				opts.quiet      = 1;
> +				ret = nand_write_opts(nand, &opts);
> +			}
> +			printf("%s\n", ret ? "ERROR" : "OK");
> +			return ret == 0 ? 0 : 1;
> +		}
>
> -		if (i)
> +		if (read)
>  			ret = nand_read(nand, off, &size, (u_char *)addr);
>  		else
>  			ret = nand_write(nand, off, &size, (u_char *)addr);
>
>  		printf(" %d bytes %s: %s\n", size,
> -		       i ? "read" : "written", ret ? "ERROR" : "OK");
> +		       read ? "read" : "written", ret ? "ERROR" : "OK");
>
>  		return ret == 0 ? 0 : 1;
>  	}
> +
> +	/* 2006-09-28 gc: implement missing commands */
> +	if (strcmp(cmd, "markbad") == 0) {
> +		addr = (ulong)simple_strtoul(argv[2], NULL, 16);
> +
> +		int ret = nand->block_markbad(nand, addr);
> +		if (ret == 0) {
> +			printf("block 0x%08lx successfully marked as bad\n",
> +			       (ulong) addr);
> +			return 0;
> +		} else {
> +			printf("block 0x%08lx NOT marked as bad! ERROR %d\n",
> +			       (ulong) addr, ret);
> +		}
> +		return 1;
> +	}
> +	if (strcmp(cmd, "biterr") == 0) {
> +		/* todo */
> +		return 1;
> +	}
> +
> +	if (strcmp(cmd, "lock") == 0) {
> +		int tight  = 0;
> +		int status = 0;
> +		if (argc == 3) {
> +			if (!strcmp("tight", argv[2]))
> +				tight = 1;
> +			if (!strcmp("status", argv[2]))
> +				status = 1;
> +		}
> +
> +		if (status) {
> +			ulong block_start = 0;
> +			ulong off;
> +			int last_status = -1;
> +
> +			struct nand_chip *nand_chip = nand->priv;
> +			/* check the WP bit */
> +			nand_chip->cmdfunc (nand, NAND_CMD_STATUS, -1, -1);
> +			printf("device is %swrite protected\n",
> +			       (nand_chip->read_byte(nand) & 0x80 ?
> +				"NOT " : "" ) );
> +
> +			for (off = 0; off < nand->size; off += nand->oobblock) {
> +				int s = nand_get_lock_status( nand, off);
> +
> +				/* print message only if status has changed
> +				 * or at end of chip
> +				 */
> +				if (off == nand->size - nand->oobblock
> +				    || (s != last_status && off != 0))	{
> +
> +					printf("%08x - %08x: %8d pages %s%s%s\n",
> +					       block_start,
> +					       off-1,
> +					       (off-block_start)/nand->oobblock,
> +					       ((last_status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""),
> +					       ((last_status & NAND_LOCK_STATUS_LOCK) ? "LOCK " : ""),
> +					       ((last_status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : "")
> +						);
> +				}
> +
> +				last_status = s;
> +		       }
> +
> +		} else {
> +
> +			if (!nand_lock( nand, tight)) {
> +				printf ("NAND flash successfully locked\n");
> +			} else {
> +				printf ("Error locking NAND flash. \n");
> +				return 1;
> +			}
> +		}
> +		return 0;
> +	}
> +
> +	if (strcmp(cmd, "unlock") == 0) {
> +
> +		if (argc == 2) {
> +			off = 0;
> +			size = nand->size;
> +		} else {
> +			arg_off_size(argc - 2, argv + 2, &off, &size,
> +				     nand->size);
> +		}
> +
> +		if (!nand_unlock( nand, off, size)) {
> +			printf ("NAND flash successfully unlocked\n");
> +		} else {
> +			printf ("Error unlocking NAND flash. "
> +				"Write and erase will probably fail\n");
> +			return 1;
> +		}
> +		return 0;
> +	}
> +
>  usage:
>  	printf("Usage:\n%s\n", cmdtp->usage);
>  	return 1;
> @@ -277,7 +455,9 @@
>  	"nand dump[.oob] off - dump page\n"
>  	"nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n"
>  	"nand markbad off - mark bad block at offset (UNSAFE)\n"
> -	"nand biterr off - make a bit error at offset (UNSAFE)\n");
> +	"nand biterr off - make a bit error at offset (UNSAFE)\n"
> +	"nand lock [tight] [status] - bring nand to lock state or display locked
> pages\n" +	"nand unlock [offset] [size] - unlock section\n");
>
>  int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
>  {
> @@ -635,7 +815,7 @@
>
>  U_BOOT_CMD(
>  	nand,	5,	1,	do_nand,
> -	"nand    - NAND sub-system\n",
> +	"nand    - legacy NAND sub-system\n",
>  	"info  - show available NAND devices\n"
>  	"nand device [dev] - show or set current device\n"
>  	"nand read[.jffs2[s]]  addr off size\n"
> diff -Nrub --exclude='*~' --exclude=.depend
> u-boot-weiss-unpatched/drivers/nand/Makefile
> u-boot-weiss/drivers/nand/Makefile ---
> u-boot-weiss-unpatched/drivers/nand/Makefile	2006-10-07 14:47:02.000000000
> +0200 +++ u-boot-weiss/drivers/nand/Makefile	2006-10-07 15:57:14.000000000
> +0200 @@ -25,7 +25,7 @@
>
>  LIB 	:= $(obj)libnand.a
>
> -COBJS 	:= nand.o nand_base.o nand_ids.o nand_ecc.o nand_bbt.o
> +COBJS 	:= nand.o nand_base.o nand_ids.o nand_ecc.o nand_bbt.o nand_util.o
>
>  SRCS 	:= $(COBJS:.o=.c)
>  OBJS 	:= $(addprefix $(obj),$(COBJS))
> diff -Nrub --exclude='*~' --exclude=.depend
> u-boot-weiss-unpatched/drivers/nand/nand_util.c
> u-boot-weiss/drivers/nand/nand_util.c ---
> u-boot-weiss-unpatched/drivers/nand/nand_util.c	1970-01-01
> 01:00:00.000000000 +0100 +++
> u-boot-weiss/drivers/nand/nand_util.c	2006-10-07 15:57:14.000000000 +0200
> @@ -0,0 +1,839 @@
> +/*
> + * drivers/nand/nand_util.c
> + *
> + * Copyright (C) 2006 by Weiss-Electronic GmbH.
> + * All rights reserved.
> + *
> + * @author:	Guido Classen <clagix at gmail.com>
> + * @descr:	NAND Flash support
> + * @references: borrowed heavily from Linux mtd-utils code:
> + *		flash_eraseall.c by Arcom Control System Ltd
> + *		nandwrite.c by Steven J. Hill (sjhill at realitydiluted.com)
> + *			       and Thomas Gleixner (tglx at linutronix.de)
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License version
> + * 2 as published by the Free Software Foundation.
> + *
> + * 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>
> +
> +#if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY)
> +
> +#include <command.h>
> +#include <watchdog.h>
> +#include <malloc.h>
> +
> +#include <nand.h>
> +#include <jffs2/jffs2.h>
> +
> +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)
> +#define cpu_to_je32(x) (x)
> +
> +/*************************************************************************
>****/ +static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int
> getchip) +{
> +	return 0;
> +}
> +
> +/**
> + * nand_erase_opts: - erase NAND flash with support for various options
> + *		      (jffs2 formating)
> + *
> + * @param meminfo	NAND device to erase
> + * @param opts		options,  @see struct nand_erase_options
> + * @return		0 in case of success
> + *
> + * This code is ported from flash_eraseall.c from Linux mtd utils by
> + * Arcom Control System Ltd.
> + */
> +int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t
> *opts) +{
> +	struct jffs2_unknown_node cleanmarker;
> +	int clmpos = 0;
> +	int clmlen = 8;
> +	erase_info_t erase;
> +	ulong erase_length;
> +	int isNAND;
> +	int bbtest = 1;
> +	int result;
> +	int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL;
> +	const char *mtd_device = meminfo->name;
> +
> +	memset(&erase, 0, sizeof(erase));
> +
> +	erase.mtd = meminfo;
> +	erase.len  = meminfo->erasesize;
> +	if (opts->offset == 0 && opts->length == 0) {
> +		/* erase complete chip */
> +		erase.addr = 0;
> +		erase_length = meminfo->size;
> +	} else {
> +		/* erase specified region */
> +		erase.addr = opts->offset;
> +		erase_length = opts->length;
> +	}
> +
> +	isNAND = meminfo->type == MTD_NANDFLASH ? 1 : 0;
> +
> +	if (opts->jffs2) {
> +		cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
> +		cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
> +		if (isNAND) {
> +			struct nand_oobinfo *oobinfo = &meminfo->oobinfo;
> +
> +			/* check for autoplacement */
> +			if (oobinfo->useecc == MTD_NANDECC_AUTOPLACE) {
> +				/* get the position of the free bytes */
> +				if (!oobinfo->oobfree[0][1]) {
> +					printf(" Eeep. Autoplacement selected "
> +					       "and no empty space in oob\n");
> +					return -1;
> +				}
> +				clmpos = oobinfo->oobfree[0][0];
> +				clmlen = oobinfo->oobfree[0][1];
> +				if (clmlen > 8)
> +					clmlen = 8;
> +			} else {
> +				/* legacy mode */
> +				switch (meminfo->oobsize) {
> +				case 8:
> +					clmpos = 6;
> +					clmlen = 2;
> +					break;
> +				case 16:
> +					clmpos = 8;
> +					clmlen = 8;
> +					break;
> +				case 64:
> +					clmpos = 16;
> +					clmlen = 8;
> +					break;
> +				}
> +			}
> +
> +			cleanmarker.totlen = cpu_to_je32(8);
> +		} else {
> +			cleanmarker.totlen =
> +				cpu_to_je32 (sizeof(struct jffs2_unknown_node));
> +		}
> +		cleanmarker.hdr_crc =  cpu_to_je32(
> +			crc32_no_comp(0, (unsigned char *) &cleanmarker,
> +				      sizeof(struct jffs2_unknown_node) - 4));
> +	}
> +
> +	/* scrub option allows to erase badblock. To prevent internal
> +	 * check from erase() method, set block check method to dummy
> +	 * and disable bad block table while erasing.
> +	 */
> +	if (opts->scrub) {
> +		struct nand_chip *priv_nand = meminfo->priv;
> +
> +		nand_block_bad_old = priv_nand->block_bad;
> +		priv_nand->block_bad = nand_block_bad_scrub;
> +		/* we don't need the bad block table anymore...
> +		 * after scrub, there are no bad blocks left!
> +		 */
> +		if (priv_nand->bbt) {
> +			kfree(priv_nand->bbt);
> +		}
> +		priv_nand->bbt = NULL;
> +	}
> +
> +	for (;
> +	     erase.addr < opts->offset + erase_length;
> +	     erase.addr += meminfo->erasesize) {
> +
> +		WATCHDOG_RESET ();
> +
> +		if ( !opts->scrub && bbtest ) {

No spaces after opening brace and before closing brace.

> +			int ret = meminfo->block_isbad(meminfo, erase.addr);
> +			if (ret > 0) {
> +				if (!opts->quiet)
> +					printf ("\rSkipping bad block at  "
> +						"0x%08x                   "
> +						"                         \n",
> +						erase.addr);
> +				continue;
> +
> +			} else if (ret < 0) {
> +				printf("\n%s: MTD get bad block failed: %d\n",
> +				       mtd_device,
> +				       ret);
> +				return -1;
> +			}
> +		}
> +
> +		if (!opts->quiet) {
> +			printf("\rErasing %d Kibyte at %x -- %2lu%% complete.",
> +			       meminfo->erasesize >> 10, erase.addr,
> +			       (unsigned long)
> +			       ((unsigned long long)
> +				erase.addr * 100 / meminfo->size));
> +		}
> +
> +		result = meminfo->erase(meminfo, &erase);
> +		if (result != 0) {
> +			printf("\n%s: MTD Erase failure: %d\n",
> +			       mtd_device, result);
> +			continue;
> +		}
> +
> +		/* format for JFFS2 ? */
> +		if (!opts->jffs2)
> +			continue;
> +
> +		/* write cleanmarker */
> +		if (isNAND) {
> +			size_t written;
> +			result = meminfo->write_oob(meminfo,
> +						    erase.addr + clmpos,
> +						    clmlen,
> +						    &written,
> +						    (unsigned char *)
> +						    &cleanmarker);
> +			if (result != 0) {
> +				printf("\n%s: MTD writeoob failure: %d\n",
> +				       mtd_device, result);
> +				continue;
> +			}
> +		} else {
> +			printf("\n%s: this erase routine only supports"
> +			       " NAND devices!\n",
> +			       mtd_device);
> +
> +		}
> +		if (!opts->quiet)
> +			printf(" Cleanmarker written at %x.", erase.addr);
> +	}
> +	if (!opts->quiet)
> +		printf("\n");
> +
> +	if (nand_block_bad_old) {
> +		struct nand_chip *priv_nand = meminfo->priv;
> +
> +		priv_nand->block_bad = nand_block_bad_old;
> +		priv_nand->scan_bbt(meminfo);
> +	}
> +
> +	return 0;
> +}
> +
> +
> +#define MAX_PAGE_SIZE	2048
> +#define MAX_OOB_SIZE	64
> +
> +/*
> + * buffer array used for writing data
> + */
> +static unsigned char data_buf[MAX_PAGE_SIZE];
> +static unsigned char oob_buf[MAX_OOB_SIZE];
> +
> +/* OOB layouts to pass into the kernel as default */
> +static struct nand_oobinfo none_oobinfo = {
> +	.useecc = MTD_NANDECC_OFF,
> +};
> +
> +static struct nand_oobinfo jffs2_oobinfo = {
> +	.useecc = MTD_NANDECC_PLACE,
> +	.eccbytes = 6,
> +	.eccpos = { 0, 1, 2, 3, 6, 7 }
> +};
> +
> +static struct nand_oobinfo yaffs_oobinfo = {
> +	.useecc = MTD_NANDECC_PLACE,
> +	.eccbytes = 6,
> +	.eccpos = { 8, 9, 10, 13, 14, 15}
> +};
> +
> +static struct nand_oobinfo autoplace_oobinfo = {
> +	.useecc = MTD_NANDECC_AUTOPLACE
> +};
> +
> +
> +/**
> + * nand_write_opts: - write image to NAND flash with support for various
> options + *
> + * @param meminfo	NAND device to erase
> + * @param opts		write options (@see nand_write_options)
> + * @return		0 in case of success
> + *
> + * This code is ported from nandwrite.c from Linux mtd utils by
> + * Steven J. Hill and Thomas Gleixner.
> + */
> +int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t
> *opts) +{
> +	int imglen = 0;
> +	int pagelen;
> +	int baderaseblock;
> +	int blockstart = -1;
> +	loff_t offs;
> +	int readlen;
> +	int oobinfochanged = 0;
> +	struct nand_oobinfo old_oobinfo;
> +	ulong mtdoffset = opts->offset;
> +	ulong erasesize_blockalign;
> +	u_char *buffer = opts->buffer;
> +	size_t written;
> +	int result;
> +
> +	if (opts->pad && opts->writeoob) {
> +		printf("Can't pad when oob data is present.\n");
> +		return -1;
> +	}
> +
> +	/* set erasesize to specified number of blocks - to match
> +	 * jffs2 (virtual) block size */
> +	if (opts->blockalign == 0) {
> +		erasesize_blockalign = meminfo->erasesize;
> +	} else {
> +		erasesize_blockalign = meminfo->erasesize * opts->blockalign;
> +	}
> +
> +	/* make sure device page sizes are valid */
> +	if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512)
> +	    && !(meminfo->oobsize == 8 && meminfo->oobblock == 256)
> +	    && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) {
> +		printf("Unknown flash (not normal NAND)\n");
> +		return -1;
> +	}
> +
> +	/* read the current oob info */
> +	memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo));
> +
> +	/* write without ecc? */
> +	if (opts->noecc) {
> +		memcpy(&meminfo->oobinfo, &none_oobinfo,
> +		       sizeof(meminfo->oobinfo));
> +		oobinfochanged = 1;
> +	}
> +
> +	/* autoplace ECC? */
> +	if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) {
> +
> +		memcpy(&meminfo->oobinfo, &autoplace_oobinfo,
> +		       sizeof(meminfo->oobinfo));
> +		oobinfochanged = 1;
> +	}
> +
> +	/* force OOB layout for jffs2 or yaffs? */
> +	if (opts->forcejffs2 || opts->forceyaffs) {
> +		struct nand_oobinfo *oobsel =
> +			opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo;
> +
> +		if (meminfo->oobsize == 8) {
> +			if (opts->forceyaffs) {
> +				printf("YAFSS cannot operate on "
> +				       "256 Byte page size\n");
> +				goto restoreoob;
> +			}
> +			/* Adjust number of ecc bytes */
> +			jffs2_oobinfo.eccbytes = 3;
> +		}
> +
> +		memcpy(&meminfo->oobinfo, oobsel, sizeof(meminfo->oobinfo));
> +	}
> +
> +	/* get image length */
> +	imglen = opts->length;
> +	pagelen = meminfo->oobblock
> +		+ ((opts->writeoob != 0) ? meminfo->oobsize : 0);
> +
> +	/* check, if file is pagealigned */
> +	if ((!opts->pad) && ((imglen % pagelen) != 0)) {
> +		printf("Input block length is not page aligned\n");
> +		goto restoreoob;
> +	}
> +
> +	/* check, if length fits into device */
> +	if ( ((imglen / pagelen) * meminfo->oobblock)

No space after opening brace.

> +	     > (meminfo->size - opts->offset)) {
> +		printf("Image %d bytes, NAND page %d bytes, "
> +		       "OOB area %u bytes, device size %u bytes\n",
> +		       imglen, pagelen, meminfo->oobblock, meminfo->size);
> +		printf("Input block does not fit into device\n");
> +		goto restoreoob;
> +	}
> +
> +	if (!opts->quiet)
> +		printf("\n");
> +
> +	/* get data from input and write to the device */
> +	while (imglen && (mtdoffset < meminfo->size)) {
> +
> +		WATCHDOG_RESET ();
> +
> +		/*
> +		 * new eraseblock, check for bad block(s). Stay in the
> +		 * loop to be sure if the offset changes because of
> +		 * a bad block, that the next block that will be
> +		 * written to is also checked. Thus avoiding errors if
> +		 * the block(s) after the skipped block(s) is also bad
> +		 * (number of blocks depending on the blockalign
> +		 */
> +		while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) {
> +			blockstart = mtdoffset & (~erasesize_blockalign+1);
> +			offs = blockstart;
> +			baderaseblock = 0;
> +			if (!opts->quiet)
> +				printf("Writing data to block %x \r",
> +				       blockstart);
> +
> +			/* check all the blocks in an erase block for
> +			 * bad blocks */
> +			do {
> +				int ret = meminfo->block_isbad(meminfo, offs);
> +
> +				if (ret < 0 ) {

No space before closing brace.

> +					printf("Bad block check failed\n");
> +					goto restoreoob;
> +				}
> +				if (ret == 1) {
> +					baderaseblock = 1;
> +					if (!opts->quiet)
> +						printf("Bad block at %lx, "
> +						       "%u block(s) from "
> +						       "%x will be skipped\n",
> +						       (long) offs,
> +						       opts->blockalign,
> +						       blockstart);
> +				}
> +
> +				if (baderaseblock) {
> +					mtdoffset = blockstart
> +						+ erasesize_blockalign;
> +				}
> +				offs +=	 erasesize_blockalign
> +					/ opts->blockalign;
> +			} while ( offs < blockstart + erasesize_blockalign );

Spaces in braces again.

> +		}
> +
> +		readlen = meminfo->oobblock;
> +		if (opts->pad && (imglen < readlen)) {
> +			readlen = imglen;
> +			memset(data_buf + readlen, 0xff,
> +			       meminfo->oobblock - readlen);
> +		}
> +
> +		/* read page data from input memory buffer */
> +		memcpy(data_buf, buffer, readlen);
> +		buffer += readlen;
> +
> +		if (opts->writeoob) {
> +			/* read OOB data from input memory block, exit
> +			 * on failure */
> +			memcpy(oob_buf, buffer, meminfo->oobsize);
> +			buffer += meminfo->oobsize;
> +
> +			/* write OOB data first, as ecc will be placed
> +			 * in there*/
> +			result = meminfo->write_oob(meminfo,
> +						    mtdoffset,
> +						    meminfo->oobsize,
> +						    &written,
> +						    (unsigned char *)
> +						    &oob_buf);
> +
> +			if (result != 0) {
> +				printf("\nMTD writeoob failure: %d\n",
> +				       result);
> +				goto restoreoob;
> +			}
> +			imglen -= meminfo->oobsize;
> +		}
> +
> +		/* write out the page data */
> +		result = meminfo->write(meminfo,
> +					mtdoffset,
> +					meminfo->oobblock,
> +					&written,
> +					(unsigned char *) &data_buf);
> +
> +		if (result != 0) {
> +			printf("writing NAND page at offset %lx failed\n",
> +			       mtdoffset);
> +			goto restoreoob;
> +		}
> +		imglen -= readlen;
> +		mtdoffset += meminfo->oobblock;
> +	}
> +
> +	if (!opts->quiet)
> +		printf("\n");
> +
> +restoreoob:
> +	if (oobinfochanged) {
> +		memcpy(&meminfo->oobinfo, &old_oobinfo,
> +		       sizeof(meminfo->oobinfo));
> +	}
> +
> +	if (imglen > 0) {
> +		printf("Data did not fit into device, due to bad blocks\n");
> +		return -1;
> +	}
> +
> +	/* return happy */
> +	return 0;
> +}
> +
> +
> +/**
> + * nand_read_opts: - read image from NAND flash with support for various
> options + *
> + * @param meminfo	NAND device to erase
> + * @param opts		read options (@see struct nand_read_options)
> + * @return		0 in case of success
> + *
> + */
> +int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)
> +{
> +	int imglen = opts->length;
> +	int pagelen;
> +	int baderaseblock;
> +	int blockstart = -1;
> +	loff_t offs;
> +	size_t readlen;
> +	ulong mtdoffset = opts->offset;
> +	u_char *buffer = opts->buffer;
> +	int result;
> +
> +        printf("nand_read_opts\n");
> +	/* make sure device page sizes are valid */
> +	if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512)
> +	    && !(meminfo->oobsize == 8 && meminfo->oobblock == 256)
> +	    && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) {
> +		printf("Unknown flash (not normal NAND)\n");
> +		return -1;
> +	}
> +
> +	pagelen = meminfo->oobblock
> +		+ ((opts->readoob != 0) ? meminfo->oobsize : 0);
> +
> +	/* check, if length is not larger than device */
> +	if ( ((imglen / pagelen) * meminfo->oobblock)

Space in brace.

> +	     > (meminfo->size - opts->offset)) {
> +		printf("Image %d bytes, NAND page %d bytes, "
> +		       "OOB area %u bytes, device size %u bytes\n",
> +		       imglen, pagelen, meminfo->oobblock, meminfo->size);
> +		printf("Input block is larger than device\n");
> +		return -1;
> +	}
> +
> +	if (!opts->quiet)
> +		printf("\n");
> +
> +	/* get data from input and write to the device */
> +	while (imglen && (mtdoffset < meminfo->size)) {
> +
> +		WATCHDOG_RESET ();
> +
> +		/*
> +		 * new eraseblock, check for bad block(s). Stay in the
> +		 * loop to be sure if the offset changes because of
> +		 * a bad block, that the next block that will be
> +		 * written to is also checked. Thus avoiding errors if
> +		 * the block(s) after the skipped block(s) is also bad
> +		 * (number of blocks depending on the blockalign
> +		 */
> +		while (blockstart != (mtdoffset & (~meminfo->erasesize+1))) {
> +			blockstart = mtdoffset & (~meminfo->erasesize+1);
> +			offs = blockstart;
> +			baderaseblock = 0;
> +			if (!opts->quiet)
> +				printf("Reading data from block %x \r",
> +				       blockstart);
> +
> +			/* check all the blocks in an erase block for
> +			 * bad blocks */
> +			do {
> +				int ret = meminfo->block_isbad(meminfo, offs);
> +
> +				if (ret < 0 ) {

Space in brace.

> +					printf("Bad block check failed\n");
> +					return -1;
> +				}
> +				if (ret == 1) {
> +					baderaseblock = 1;
> +					if (!opts->quiet)
> +						printf("Bad block at %lx in "
> +						       "erase block from %x "
> +						       "will be skipped\n",
> +						       (long) offs,
> +						       blockstart);
> +				}
> +
> +				if (baderaseblock) {
> +					mtdoffset = blockstart
> +						+ meminfo->erasesize;
> +				}
> +				offs +=	 meminfo->erasesize;
> +
> +			} while (offs < blockstart + meminfo->erasesize);
> +		}
> +
> +
> +		/* read page data to memory buffer */
> +		result = meminfo->read(meminfo,
> +				       mtdoffset,
> +				       meminfo->oobblock,
> +				       &readlen,
> +				       (unsigned char *) &data_buf);
> +
> +		if (result != 0) {
> +			printf("reading NAND page at offset %lx failed\n",
> +			       mtdoffset);
> +			return -1;
> +		}
> +
> +		if (imglen < readlen) {
> +			readlen = imglen;
> +		}
> +
> +		memcpy(buffer, data_buf, readlen);
> +		buffer += readlen;
> +		imglen -= readlen;
> +
> +		if (opts->readoob) {
> +			result = meminfo->read_oob(meminfo,
> +						   mtdoffset,
> +						   meminfo->oobsize,
> +						   &readlen,
> +						   (unsigned char *)
> +						   &oob_buf);
> +
> +			if (result != 0) {
> +				printf("\nMTD readoob failure: %d\n",
> +				       result);
> +				return -1;
> +			}
> +
> +
> +			if (imglen < readlen) {
> +				readlen = imglen;
> +			}
> +
> +			memcpy(buffer, oob_buf, readlen);
> +
> +			buffer += readlen;
> +			imglen -= readlen;
> +		}
> +
> +		mtdoffset += meminfo->oobblock;
> +	}
> +
> +	if (!opts->quiet)
> +		printf("\n");
> +
> +	if (imglen > 0) {
> +		printf("Could not read entire image due to bad blocks\n");
> +		return -1;
> +	}
> +
> +	/* return happy */
> +	return 0;
> +}
> +
> +
> +/*************************************************************************
>***** + * Support for locking / unlocking operations of some NAND devices +
> ***************************************************************************
>**/ +
> +#define NAND_CMD_LOCK		0x2a
> +#define NAND_CMD_LOCK_TIGHT	0x2c
> +#define NAND_CMD_UNLOCK1	0x23
> +#define NAND_CMD_UNLOCK2	0x24
> +#define NAND_CMD_LOCK_STATUS	0x7a
> +
> +/**
> + * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT
> + *	      state
> + *
> + * @param meminfo	nand mtd instance
> + * @param tight		bring device in lock tight mode
> + *
> + * @return		0 on success, -1 in case of error
> + *
> + * The lock / lock-tight command only applies to the whole chip. To get
> some + * parts of the chip lock and others unlocked use the following
> sequence: + *
> + * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre
> pin) + * - Call nand_unlock() once for each consecutive area to be unlocked
> + * - If desired: Bring the chip to the lock-tight state using
> nand_lock(mtd, 1) + *
> + *   If the device is in lock-tight state software can't change the
> + *   current active lock/unlock state of all pages. nand_lock() /
> nand_unlock() + *   calls will fail. It is only posible to leave lock-tight
> state by + *   an hardware signal (low pulse on _WP pin) or by power down.
> + */
> +int nand_lock(nand_info_t *meminfo, int tight)
> +{
> +	int ret = 0;
> +	int status;
> +	struct nand_chip *this = meminfo->priv;
> +
> +	/* select the NAND device */
> +	this->select_chip(meminfo, 0);
> +
> +	this->cmdfunc(meminfo,
> +		      (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK),
> +		      -1, -1);
> +
> +	/* call wait ready function */
> +	status = this->waitfunc(meminfo, this, FL_WRITING);
> +
> +	/* see if device thinks it succeeded */
> +	if (status & 0x01) {
> +		ret = -1;
> +	}
> +
> +	/* de-select the NAND device */
> +	this->select_chip(meminfo, -1);
> +	return ret;
> +}
> +
> +
> +/**
> + * nand_get_lock_status: - query current lock state from one page of NAND
> + *			   flash
> + *
> + * @param meminfo	nand mtd instance
> + * @param offset	page address to query (muss be page aligned!)
> + *
> + * @return		-1 in case of error
> + *			>0 lock status:
> + *			  bitfield with the following combinations:
> + *			  NAND_LOCK_STATUS_TIGHT: page in tight state
> + *			  NAND_LOCK_STATUS_LOCK:  page locked
> + *			  NAND_LOCK_STATUS_UNLOCK: page unlocked
> + *
> + */
> +int nand_get_lock_status(nand_info_t *meminfo, ulong offset)
> +{
> +	int ret = 0;
> +	int chipnr;
> +	int page;
> +	struct nand_chip *this = meminfo->priv;
> +
> +	/* select the NAND device */
> +	chipnr = (int)(offset >> this->chip_shift);
> +	this->select_chip(meminfo, chipnr);
> +
> +
> +	if ((offset & (meminfo->oobblock - 1)) != 0) {
> +		printf ("nand_get_lock_status: "
> +			"Start address must be beginning of "
> +			"nand page!\n");
> +		ret = -1;
> +		goto out;
> +	}
> +
> +	/* check the Lock Status */
> +	page = (int)(offset >> this->page_shift);
> +	this->cmdfunc(meminfo, NAND_CMD_LOCK_STATUS, -1, page & this->pagemask);
> +
> +	ret = this->read_byte(meminfo) & (NAND_LOCK_STATUS_TIGHT
> +					  | NAND_LOCK_STATUS_LOCK
> +					  | NAND_LOCK_STATUS_UNLOCK);
> +
> + out:
> +	/* de-select the NAND device */
> +	this->select_chip(meminfo, -1);
> +	return ret;
> +}
> +
> +/**
> + * nand_unlock: - Unlock area of NAND pages
> + *		  only one consecutive area can be unlocked at one time!
> + *
> + * @param meminfo	nand mtd instance
> + * @param start		start byte address
> + * @param length	number of bytes to unlock (must be a multiple of
> + *			page size nand->oobblock)
> + *
> + * @return		0 on success, -1 in case of error
> + */
> +int nand_unlock(nand_info_t *meminfo, ulong start, ulong length)
> +{
> +	int ret = 0;
> +	int chipnr;
> +	int status;
> +	int page;
> +	struct nand_chip *this = meminfo->priv;
> +	printf ("nand_unlock: start: %08x, length: %d!\n",
> +		(int)start, (int)length);
> +
> +	/* select the NAND device */
> +	chipnr = (int)(start >> this->chip_shift);
> +	this->select_chip(meminfo, chipnr);
> +
> +	/* check the WP bit */
> +	this->cmdfunc(meminfo, NAND_CMD_STATUS, -1, -1);
> +	if ((this->read_byte(meminfo) & 0x80) == 0) {
> +		printf ("nand_unlock: Device is write protected!\n");
> +		ret = -1;
> +		goto out;
> +	}
> +
> +	if ((start & (meminfo->oobblock - 1)) != 0) {
> +		printf ("nand_unlock: Start address must be beginning of "
> +			"nand page!\n");
> +		ret = -1;
> +		goto out;
> +	}
> +
> +	if (length == 0 || (length & (meminfo->oobblock - 1)) != 0) {
> +		printf ("nand_unlock: Length must be a multiple of nand page "
> +			"size!\n");
> +		ret = -1;
> +		goto out;
> +	}
> +
> +	/* submit address of first page to unlock */
> +	page = (int)(start >> this->page_shift);
> +	this->cmdfunc(meminfo, NAND_CMD_UNLOCK1, -1, page & this->pagemask);
> +
> +	/* submit ADDRESS of LAST page to unlock */
> +	page += (int)(length >> this->page_shift) - 1;
> +	this->cmdfunc(meminfo, NAND_CMD_UNLOCK2, -1, page & this->pagemask);
> +
> +	/* call wait ready function */
> +	status = this->waitfunc(meminfo, this, FL_WRITING);
> +	/* see if device thinks it succeeded */
> +	if (status & 0x01) {
> +		/* there was an error */
> +		ret = -1;
> +		goto out;
> +	}
> +
> + out:
> +	/* de-select the NAND device */
> +	this->select_chip(meminfo, -1);
> +	return ret;
> +}
> +
> +
> +#endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY) */
> +
> +/*
> + *Local Variables:
> + * mode: c
> + * End:
> + */

Please remove this c-style paragraph.

> diff -Nrub --exclude='*~' --exclude=.depend
> u-boot-weiss-unpatched/include/nand.h u-boot-weiss/include/nand.h ---
> u-boot-weiss-unpatched/include/nand.h	2006-10-07 14:45:24.000000000 +0200
> +++ u-boot-weiss/include/nand.h	2006-10-07 15:57:14.000000000 +0200 @@
> -60,4 +60,61 @@
>  	return info->erase(info, &instr);
>  }
>
> +
> +/*************************************************************************
>**** + * declarations from nand_util.c
> +
> ***************************************************************************
>*/ +
> +struct nand_write_options {
> +	u_char *buffer;		/* memory block containing image to write */
> +	ulong length;		/* number of bytes to write */
> +	ulong offset;		/* start address in NAND */
> +	int quiet;		/* don't display progress messages */
> +	int autoplace;		/* if true use auto oob layout */
> +	int forcejffs2;		/* force jffs2 oob layout */
> +	int forceyaffs;		/* force yaffs oob layout */
> +	int noecc;		/* write without ecc */
> +	int writeoob;		/* image contains oob data */
> +	int pad;		/* pad to page size */
> +	int blockalign;		/* 1|2|4 set multiple of eraseblocks
> +				 * to align to */
> +};
> +
> +typedef struct nand_write_options nand_write_options_t;
> +
> +struct nand_read_options {
> +	u_char *buffer;		/* memory block in which read image is written*/
> +	ulong length;		/* number of bytes to read */
> +	ulong offset;		/* start address in NAND */
> +	int quiet;		/* don't display progress messages */
> +	int readoob;		/* put oob data in image */
> +};
> +
> +typedef struct nand_read_options nand_read_options_t;
> +
> +struct nand_erase_options {
> +	ulong length;		/* number of bytes to erase */
> +	ulong offset;		/* first address in NAND to erase */
> +	int quiet;		/* don't display progress messages */
> +	int jffs2;		/* if true: format for jffs2 usage
> +				 * (write appropriate cleanmarker blocks) */
> +	int scrub;		/* if true, really clean NAND by erasing
> +				 * bad blocks (UNSAFE) */
> +};
> +
> +typedef struct nand_erase_options nand_erase_options_t;
> +
> +int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t
> *opts); +
> +int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts);
> +int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t
> *opts); +
> +#define NAND_LOCK_STATUS_TIGHT	0x01
> +#define NAND_LOCK_STATUS_LOCK	0x02
> +#define NAND_LOCK_STATUS_UNLOCK 0x04
> +
> +int nand_lock( nand_info_t *meminfo, int tight );
> +int nand_unlock( nand_info_t *meminfo, ulong start, ulong length );
> +int nand_get_lock_status(nand_info_t *meminfo, ulong offset);
> +
>  #endif
> diff -Nrub --exclude='*~' --exclude=.depend
> u-boot-weiss-unpatched/lib_generic/crc32.c u-boot-weiss/lib_generic/crc32.c
> --- u-boot-weiss-unpatched/lib_generic/crc32.c	2006-10-07
> 15:55:01.000000000 +0200 +++ u-boot-weiss/lib_generic/crc32.c	2006-10-07
> 15:58:13.000000000 +0200 @@ -171,7 +171,9 @@
>      return crc ^ 0xffffffffL;
>  }
>
> -#if (CONFIG_COMMANDS & CFG_CMD_JFFS2)
> +#if (CONFIG_COMMANDS & CFG_CMD_JFFS2)					\
> +	|| (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY)
> +
>
>  /* No ones complement version. JFFS2 (and other things ?)
>   * don't use ones compliment in their CRC calculations.

Now it compiles clean for my testplatform. Thanks.

As it seems you have set the "quiet" option for all commands. Because of
this we don't see the skipping of bad blocks message for example. So please
disable the quiet option.

Thanks.

Best regards,
Stefan




More information about the U-Boot mailing list