diff options
| author | Scott Wood <scottwood@freescale.com> | 2010-08-02 13:04:24 -0500 | 
|---|---|---|
| committer | Scott Wood <scottwood@freescale.com> | 2010-10-11 15:09:54 -0500 | 
| commit | ea533c260a801c4e51f92f75165cebe6d7b01e35 (patch) | |
| tree | a445ddd13f93806631e2305ec62a5aa8149edcdf /common/cmd_nand.c | |
| parent | f9a5254111a6be2a39464f65a96f4fc2305e3c76 (diff) | |
| download | olio-uboot-2014.01-ea533c260a801c4e51f92f75165cebe6d7b01e35.tar.xz olio-uboot-2014.01-ea533c260a801c4e51f92f75165cebe6d7b01e35.zip | |
cmd_nand: some infrastructure fixes and refactoring
- If the current device is overridden by a named partition,
  - update the caller's pointer/index, rather than copy over the
    nand_info struct, and
  - be sure to call board_nand_select_device even when the device
    is overridden by a named partition.
- Support 64-bit offsets/sizes in a few more places.
- Refactor arg_off_size for added readability and flexibility,
  and some added checks such as partition size.
- Remove redundant check for bad subcommands -- if there's no match
  it'll print usage when it gets to the end anyway.
Signed-off-by: Scott Wood <scottwood@freescale.com>
Tested-by: Ben Gardiner <bengardiner@nanometrics.ca>
Diffstat (limited to 'common/cmd_nand.c')
| -rw-r--r-- | common/cmd_nand.c | 274 | 
1 files changed, 167 insertions, 107 deletions
| diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 3f1d077ff..41aaf7f26 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -10,6 +10,13 @@   * (C) Copyright 2006-2007 OpenMoko, Inc.   * Added 16-bit nand support   * (C) 2004 Texas Instruments + * + * Copyright 2010 Freescale Semiconductor + * The portions of this file whose copyright is held by Freescale and which + * are not considered a derived work of GPL v2-only code may be distributed + * and/or modified 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.   */  #include <common.h> @@ -85,74 +92,132 @@ static int nand_dump(nand_info_t *nand, ulong off, int only_oob)  /* ------------------------------------------------------------------------- */ -static inline int str2long(char *p, ulong *num) +static int set_dev(int dev) +{ +	if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || +	    !nand_info[dev].name) { +		puts("No such device\n"); +		return -1; +	} + +	if (nand_curr_device == dev) +		return 0; + +	printf("Device %d: %s", dev, nand_info[dev].name); +	puts("... is now current device\n"); +	nand_curr_device = dev; + +#ifdef CONFIG_SYS_NAND_SELECT_DEVICE +	board_nand_select_device(nand_info[dev].priv, dev); +#endif + +	return 0; +} + +static inline int str2off(const char *p, loff_t *num) +{ +	char *endptr; + +	*num = simple_strtoull(p, &endptr, 16); +	return *p != '\0' && *endptr == '\0'; +} + +static inline int str2long(const char *p, ulong *num)  {  	char *endptr;  	*num = simple_strtoul(p, &endptr, 16); -	return (*p != '\0' && *endptr == '\0') ? 1 : 0; +	return *p != '\0' && *endptr == '\0';  } -static int -arg_off_size(int argc, char * const argv[], nand_info_t *nand, ulong *off, size_t *size) +static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size)  { -	int idx = nand_curr_device; -#if defined(CONFIG_CMD_MTDPARTS) +#ifdef CONFIG_CMD_MTDPARTS  	struct mtd_device *dev;  	struct part_info *part;  	u8 pnum; +	int ret; -	if (argc >= 1 && !(str2long(argv[0], off))) { -		if ((mtdparts_init() == 0) && -		    (find_dev_and_part(argv[0], &dev, &pnum, &part) == 0)) { -			if (dev->id->type != MTD_DEV_TYPE_NAND) { -				puts("not a NAND device\n"); -				return -1; -			} -			*off = part->offset; -			if (argc >= 2) { -				if (!(str2long(argv[1], (ulong *)size))) { -					printf("'%s' is not a number\n", argv[1]); -					return -1; -				} -				if (*size > part->size) -					*size = part->size; -			} else { -				*size = part->size; -			} -			idx = dev->id->num; -			*nand = nand_info[idx]; -			goto out; -		} +	ret = mtdparts_init(); +	if (ret) +		return ret; + +	ret = find_dev_and_part(partname, &dev, &pnum, &part); +	if (ret) +		return ret; + +	if (dev->id->type != MTD_DEV_TYPE_NAND) { +		puts("not a NAND device\n"); +		return -1;  	} + +	*off = part->offset; +	*size = part->size; +	*idx = dev->id->num; + +	ret = set_dev(*idx); +	if (ret) +		return ret; + +	return 0; +#else +	puts("offset is not a number\n"); +	return -1;  #endif +} -	if (argc >= 1) { -		if (!(str2long(argv[0], off))) { -			printf("'%s' is not a number\n", argv[0]); -			return -1; -		} -	} else { +static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *maxsize) +{ +	if (!str2off(arg, off)) +		return get_part(arg, idx, off, maxsize); + +	if (*off >= nand_info[*idx].size) { +		puts("Offset exceeds device limit\n"); +		return -1; +	} + +	*maxsize = nand_info[*idx].size - *off; +	return 0; +} + +static int arg_off_size(int argc, char *const argv[], int *idx, +			loff_t *off, loff_t *size) +{ +	int ret; +	loff_t maxsize; + +	if (argc == 0) {  		*off = 0; +		*size = nand_info[*idx].size; +		goto print;  	} -	if (argc >= 2) { -		if (!(str2long(argv[1], (ulong *)size))) { -			printf("'%s' is not a number\n", argv[1]); -			return -1; -		} -	} else { -		*size = nand->size - *off; +	ret = arg_off(argv[0], idx, off, &maxsize); +	if (ret) +		return ret; + +	if (argc == 1) { +		*size = maxsize; +		goto print;  	} -#if defined(CONFIG_CMD_MTDPARTS) -out: -#endif -	printf("device %d ", idx); -	if (*size == nand->size) +	if (!str2off(argv[1], size)) { +		printf("'%s' is not a number\n", argv[1]); +		return -1; +	} + +	if (*size > maxsize) { +		puts("Size exceeds partition or device limit\n"); +		return -1; +	} + +print: +	printf("device %d ", *idx); +	if (*size == nand_info[*idx].size)  		puts("whole chip\n");  	else -		printf("offset 0x%lx, size 0x%zx\n", *off, *size); +		printf("offset 0x%llx, size 0x%llx\n", +		       (unsigned long long)*off, (unsigned long long)*size);  	return 0;  } @@ -200,14 +265,20 @@ static void do_nand_status(nand_info_t *nand)  #ifdef CONFIG_ENV_OFFSET_OOB  unsigned long nand_env_oob_offset; -int do_nand_env_oob(cmd_tbl_t *cmdtp, nand_info_t *nand, -		    int argc, char * const argv[]) +int do_nand_env_oob(cmd_tbl_t *cmdtp, int argc, char *const argv[])  {  	int ret;  	uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; - +	nand_info_t *nand = &nand_info[0];  	char *cmd = argv[1]; +	if (CONFIG_SYS_MAX_NAND_DEVICE == 0 || !nand->name) { +		puts("no devices available\n"); +		return 1; +	} + +	set_dev(0); +  	if (!strcmp(cmd, "get")) {  		ret = get_nand_env_oob(nand, &nand_env_oob_offset);  		if (ret) @@ -215,16 +286,21 @@ int do_nand_env_oob(cmd_tbl_t *cmdtp, nand_info_t *nand,  		printf("0x%08lx\n", nand_env_oob_offset);  	} else if (!strcmp(cmd, "set")) { -		ulong addr; -		size_t dummy_size; +		loff_t addr; +		loff_t maxsize;  		struct mtd_oob_ops ops; +		int idx = 0;  		if (argc < 3)  			goto usage; -		if (arg_off_size(argc - 2, argv + 2, nand, &addr, -				 &dummy_size) < 0) { -			printf("Offset or partition name expected\n"); +		if (arg_off(argv[2], &idx, &addr, &maxsize)) { +			puts("Offset or partition name expected\n"); +			return 1; +		} + +		if (idx != 0) { +			puts("Partition not on first NAND device\n");  			return 1;  		} @@ -264,8 +340,8 @@ int do_nand_env_oob(cmd_tbl_t *cmdtp, nand_info_t *nand,  		if (addr != nand_env_oob_offset) {  			printf("Verification of env offset in OOB failed: " -			       "0x%08lx expected but got 0x%08lx\n", -			       addr, nand_env_oob_offset); +			       "0x%08llx expected but got 0x%08lx\n", +			       (unsigned long long)addr, nand_env_oob_offset);  			return 1;  		}  	} else { @@ -293,9 +369,9 @@ static void nand_print_info(int idx)  int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])  { -	int i, dev, ret = 0; -	ulong addr, off; -	size_t size; +	int i, ret = 0; +	ulong addr; +	loff_t off, size;  	char *cmd, *s;  	nand_info_t *nand;  #ifdef CONFIG_SYS_NAND_QUIET @@ -304,6 +380,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])  	int quiet = 0;  #endif  	const char *quiet_str = getenv("quiet"); +	int dev = nand_curr_device;  	/* at least two arguments please */  	if (argc < 2) @@ -325,68 +402,45 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])  	}  	if (strcmp(cmd, "device") == 0) { -  		if (argc < 3) {  			putc('\n'); -			if ((nand_curr_device < 0) || -			    (nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE)) +			if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE)  				puts("no devices available\n");  			else -				nand_print_info(nand_curr_device); +				nand_print_info(dev);  			return 0;  		} -		dev = (int)simple_strtoul(argv[2], NULL, 10); -		if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev].name) { -			puts("No such device\n"); -			return 1; -		} -		printf("Device %d: %s", dev, nand_info[dev].name); -		puts("... is now current device\n"); -		nand_curr_device = dev; -#ifdef CONFIG_SYS_NAND_SELECT_DEVICE -		/* -		 * Select the chip in the board/cpu specific driver -		 */ -		board_nand_select_device(nand_info[dev].priv, dev); -#endif +		dev = (int)simple_strtoul(argv[2], NULL, 10); +		set_dev(dev);  		return 0;  	} -	if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 && -	    strncmp(cmd, "dump", 4) != 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 -#ifdef CONFIG_ENV_OFFSET_OOB -	    && strcmp(cmd, "env.oob") != 0 -#endif -	    ) -		goto usage; -  #ifdef CONFIG_ENV_OFFSET_OOB  	/* this command operates only on the first nand device */ -	if (strcmp(cmd, "env.oob") == 0) { -		return do_nand_env_oob(cmdtp, &nand_info[0], -				       argc - 1, argv + 1); -	} +	if (strcmp(cmd, "env.oob") == 0) +		return do_nand_env_oob(cmdtp, argc - 1, argv + 1);  #endif -	/* the following commands operate on the current device */ -	if (nand_curr_device < 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || -	    !nand_info[nand_curr_device].name) { +	/* The following commands operate on the current device, unless +	 * overridden by a partition specifier.  Note that if somehow the +	 * current device is invalid, it will have to be changed to a valid +	 * one before these commands can run, even if a partition specifier +	 * for another device is to be used. +	 */ +	if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || +	    !nand_info[dev].name) {  		puts("\nno devices available\n");  		return 1;  	} -	nand = &nand_info[nand_curr_device]; +	nand = &nand_info[dev];  	if (strcmp(cmd, "bad") == 0) { -		printf("\nDevice %d bad blocks:\n", nand_curr_device); +		printf("\nDevice %d bad blocks:\n", dev);  		for (off = 0; off < nand->size; off += nand->erasesize)  			if (nand_block_isbad(nand, off)) -				printf("  %08lx\n", off); +				printf("  %08llx\n", (unsigned long long)off);  		return 0;  	} @@ -404,9 +458,11 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])  		printf("\nNAND %s: ", scrub ? "scrub" : "erase");  		/* skip first two or three arguments, look for offset and size */ -		if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0) +		if (arg_off_size(argc - o, argv + o, &dev, &off, &size) != 0)  			return 1; +		nand = &nand_info[dev]; +  		memset(&opts, 0, sizeof(opts));  		opts.offset = off;  		opts.length = size; @@ -462,6 +518,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])  	}  	if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { +		size_t rwsize;  		int read;  		if (argc < 4) @@ -471,23 +528,26 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])  		read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */  		printf("\nNAND %s: ", read ? "read" : "write"); -		if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) +		if (arg_off_size(argc - 3, argv + 3, &dev, &off, &size) != 0)  			return 1; +		nand = &nand_info[dev]; +		rwsize = size; +  		s = strchr(cmd, '.');  		if (!s || !strcmp(s, ".jffs2") ||  		    !strcmp(s, ".e") || !strcmp(s, ".i")) {  			if (read) -				ret = nand_read_skip_bad(nand, off, &size, +				ret = nand_read_skip_bad(nand, off, &rwsize,  							 (u_char *)addr);  			else -				ret = nand_write_skip_bad(nand, off, &size, +				ret = nand_write_skip_bad(nand, off, &rwsize,  							  (u_char *)addr);  		} else if (!strcmp(s, ".oob")) {  			/* out-of-band data */  			mtd_oob_ops_t ops = {  				.oobbuf = (u8 *)addr, -				.ooblen = size, +				.ooblen = rwsize,  				.mode = MTD_OOB_RAW  			}; @@ -500,7 +560,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])  			return 1;  		} -		printf(" %zu bytes %s: %s\n", size, +		printf(" %zu bytes %s: %s\n", rwsize,  		       read ? "read" : "written", ret ? "ERROR" : "OK");  		return ret == 0 ? 0 : 1; @@ -564,7 +624,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])  		if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0)  			return 1; -		if (!nand_unlock(nand, off, size)) { +		if (!nand_unlock(&nand_info[dev], off, size)) {  			puts("NAND flash successfully unlocked\n");  		} else {  			puts("Error unlocking NAND flash, " |