[PATCH] NAND: Partition name support added to NAND subsystem

chpart, nboot and NAND subsystem related commands now accept also partition
name to specify offset.

Signed-off-by: Ladislav Michl <ladis@linux-mips.org>
Signed-off-by: Stefan Roese <sr@denx.de>
diff --git a/common/cmd_nand.c b/common/cmd_nand.c
index 4fb3b65..7286726 100644
--- a/common/cmd_nand.c
+++ b/common/cmd_nand.c
@@ -36,6 +36,15 @@
 #include <jffs2/jffs2.h>
 #include <nand.h>
 
+#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
+
+/* parition handling routines */
+int mtdparts_init(void);
+int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num);
+int find_dev_and_part(const char *id, struct mtd_device **dev,
+		u8 *part_num, struct part_info **part);
+#endif
+
 extern nand_info_t nand_info[];       /* info for NAND chips */
 
 static int nand_dump_oob(nand_info_t *nand, ulong off)
@@ -83,50 +92,75 @@
 
 /* ------------------------------------------------------------------------- */
 
-static void
-arg_off_size(int argc, char *argv[], ulong *off, ulong *size, ulong totsize)
+static inline int str2long(char *p, ulong *num)
 {
-	*off = 0;
-	*size = 0;
+	char *endptr;
 
-#if defined(CONFIG_JFFS2_NAND) && defined(CFG_JFFS_CUSTOM_PART)
-	if (argc >= 1 && strcmp(argv[0], "partition") == 0) {
-		int part_num;
-		struct part_info *part;
-		const char *partstr;
+	*num = simple_strtoul(p, &endptr, 16);
+	return (*p != '\0' && *endptr == '\0') ? 1 : 0;
+}
 
-		if (argc >= 2)
-			partstr = argv[1];
-		else
-			partstr = getenv("partition");
+static int
+arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, ulong *size)
+{
+	int idx = nand_curr_device;
+#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
+	struct mtd_device *dev;
+	struct part_info *part;
+	u8 pnum;
 
-		if (partstr)
-			part_num = (int)simple_strtoul(partstr, NULL, 10);
-		else
-			part_num = 0;
-
-		part = jffs2_part_info(part_num);
-		if (part == NULL) {
-			printf("\nInvalid partition %d\n", part_num);
-			return;
+	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], 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;
 		}
-		*size = part->size;
-		*off = (ulong)part->offset;
-	} else
+	}
 #endif
-	{
-		if (argc >= 1)
-			*off = (ulong)simple_strtoul(argv[0], NULL, 16);
-		else
-			*off = 0;
 
-		if (argc >= 2)
-			*size = (ulong)simple_strtoul(argv[1], NULL, 16);
-		else
-			*size = totsize - *off;
-
+	if (argc >= 1) {
+		if (!(str2long(argv[0], off))) {
+			printf("'%s' is not a number\n", argv[0]);
+			return -1;
+		}
+	} else {
+		*off = 0;
 	}
 
+	if (argc >= 2) {
+		if (!(str2long(argv[1], size))) {
+			printf("'%s' is not a number\n", argv[1]);
+			return -1;
+		}
+	} else {
+		*size = nand->size - *off;
+	}
+
+#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
+out:
+#endif
+	printf("device %d ", idx);
+	if (*size == nand->size)
+		puts("whole chip\n");
+	else
+		printf("offset 0x%x, size 0x%x\n", *off, *size);
+	return 0;
 }
 
 int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
@@ -213,35 +247,22 @@
 		return 0;
 	}
 
+	/*
+	 * Syntax is:
+	 *   0    1     2       3    4
+	 *   nand erase [clean] [off 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;
+		/* "clean" at index 2 means request to write cleanmarker */
+		int clean = argc > 2 && !strcmp("clean", argv[2]);
+		int o = clean ? 3 : 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;
-
-			printf("\nNAND %s: device %d offset 0x%x, size 0x%x\n",
-			       cmd, nand_curr_device, off, size);
-		}
+		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)
+			return 1;
 
 		memset(&opts, 0, sizeof(opts));
 		opts.offset = off;
@@ -250,23 +271,22 @@
 		opts.quiet  = quiet;
 
 		if (scrub) {
-			printf("Warning: "
-			       "scrub option will erase all factory set "
-			       "bad blocks!\n"
-			       "	 "
-			       "There is no reliable way to recover them.\n"
-			       "	 "
-			       "Use this command only for testing purposes "
-			       "if you\n"
-			       "	 "
-			       "are sure of what you are doing!\n"
-			       "\nReally scrub this NAND flash? <y/N>\n"
-				);
+			puts("Warning: "
+			     "scrub option will erase all factory set "
+			     "bad blocks!\n"
+			     "         "
+			     "There is no reliable way to recover them.\n"
+			     "         "
+			     "Use this command only for testing purposes "
+			     "if you\n"
+			     "         "
+			     "are sure of what you are doing!\n"
+			     "\nReally scrub this NAND flash? <y/N>\n");
 
 			if (getc() == 'y' && getc() == '\r') {
 				opts.scrub = 1;
 			} else {
-				printf("scrub aborted\n");
+				puts("scrub aborted\n");
 				return -1;
 			}
 		}
@@ -301,13 +321,10 @@
 
 		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;
-
 		read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
-		printf("\nNAND %s: device %d offset %u, size %u ... ",
-		       read ? "read" : "write", nand_curr_device, off, size);
+		printf("\nNAND %s: ", read ? "read" : "write");
+		if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0)
+			return 1;
 
 		s = strchr(cmd, '.');
 		if (s != NULL &&
@@ -334,15 +351,13 @@
 				opts.quiet      = quiet;
 				ret = nand_write_opts(nand, &opts);
 			}
-			printf("%s\n", ret ? "ERROR" : "OK");
-			return ret == 0 ? 0 : 1;
+		} else {
+			if (read)
+				ret = nand_read(nand, off, &size, (u_char *)addr);
+			else
+				ret = nand_write(nand, off, &size, (u_char *)addr);
 		}
 
-		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,
 		       read ? "read" : "written", ret ? "ERROR" : "OK");
 
@@ -412,9 +427,9 @@
 		       }
 		} else {
 			if (!nand_lock(nand, tight)) {
-				printf ("NAND flash successfully locked\n");
+				puts("NAND flash successfully locked\n");
 			} else {
-				printf ("Error locking NAND flash. \n");
+				puts("Error locking NAND flash\n");
 				return 1;
 			}
 		}
@@ -422,19 +437,14 @@
 	}
 
 	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 (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0)
+			return 1;
 
 		if (!nand_unlock(nand, off, size)) {
-			printf("NAND flash successfully unlocked\n");
+			puts("NAND flash successfully unlocked\n");
 		} else {
-			printf("Error unlocking NAND flash. "
-			       "Write and erase will probably fail\n");
+			puts("Error unlocking NAND flash, "
+			     "write and erase will probably fail\n");
 			return 1;
 		}
 		return 0;
@@ -449,8 +459,8 @@
 	"nand    - NAND sub-system\n",
 	"info                  - show available NAND devices\n"
 	"nand device [dev]     - show or set current device\n"
-	"nand read[.jffs2]     - addr off size\n"
-	"nand write[.jffs2]    - addr off size - read/write `size' bytes starting\n"
+	"nand read[.jffs2]     - addr off|partition size\n"
+	"nand write[.jffs2]    - addr off|partiton size - read/write `size' bytes starting\n"
 	"    at offset `off' to/from memory address `addr'\n"
 	"nand erase [clean] [off size] - erase `size' bytes from\n"
 	"    offset `off' (entire device if not specified)\n"
@@ -462,15 +472,92 @@
 	"nand lock [tight] [status] - bring nand to lock state or display locked pages\n"
 	"nand unlock [offset] [size] - unlock section\n");
 
+static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
+			   ulong offset, ulong addr, char *cmd)
+{
+	int r;
+	char *ep;
+	ulong cnt;
+	image_header_t *hdr;
+
+	printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset);
+
+	cnt = nand->oobblock;
+	r = nand_read(nand, offset, &cnt, (u_char *) addr);
+	if (r) {
+		puts("** Read error\n");
+		SHOW_BOOT_PROGRESS(-1);
+		return 1;
+	}
+
+	hdr = (image_header_t *) addr;
+
+	if (ntohl(hdr->ih_magic) != IH_MAGIC) {
+		printf("\n** Bad Magic Number 0x%x **\n", hdr->ih_magic);
+		SHOW_BOOT_PROGRESS(-1);
+		return 1;
+	}
+
+	print_image_hdr(hdr);
+
+	cnt = (ntohl(hdr->ih_size) + sizeof (image_header_t));
+
+	r = nand_read(nand, offset, &cnt, (u_char *) addr);
+	if (r) {
+		puts("** Read error\n");
+		SHOW_BOOT_PROGRESS(-1);
+		return 1;
+	}
+
+	/* Loading ok, update default load address */
+
+	load_addr = addr;
+
+	/* Check if we should attempt an auto-start */
+	if (((ep = getenv("autostart")) != NULL) && (strcmp(ep, "yes") == 0)) {
+		char *local_args[2];
+		extern int do_bootm(cmd_tbl_t *, int, int, char *[]);
+
+		local_args[0] = cmd;
+		local_args[1] = NULL;
+
+		printf("Automatic boot of image at addr 0x%08lx ...\n", addr);
+
+		do_bootm(cmdtp, 0, 1, local_args);
+		return 1;
+	}
+	return 0;
+}
+
 int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 {
 	char *boot_device = NULL;
-	char *ep;
-	int dev;
-	int r;
-	ulong addr, cnt, offset = 0;
-	image_header_t *hdr;
-	nand_info_t *nand;
+	int idx;
+	ulong addr, offset = 0;
+#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
+	struct mtd_device *dev;
+	struct part_info *part;
+	u8 pnum;
+
+	if (argc >= 2) {
+		char *p = (argc == 2) ? argv[1] : argv[2];
+		if (!(str2long(p, &addr)) && (mtdparts_init() == 0) &&
+		    (find_dev_and_part(p, &dev, &pnum, &part) == 0)) {
+			if (dev->id->type != MTD_DEV_TYPE_NAND) {
+				puts("Not a NAND device\n");
+				return 1;
+			}
+			if (argc > 3)
+				goto usage;
+			if (argc == 3)
+				addr = simple_strtoul(argv[2], NULL, 16);
+			else
+				addr = CFG_LOAD_ADDR;
+			return nand_load_image(cmdtp, &nand_info[dev->id->num],
+					       part->offset, addr, argv[0]);
+		}
+	}
+#endif
 
 	switch (argc) {
 	case 1:
@@ -491,6 +578,9 @@
 		offset = simple_strtoul(argv[3], NULL, 16);
 		break;
 	default:
+#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
+usage:
+#endif
 		printf("Usage:\n%s\n", cmdtp->usage);
 		SHOW_BOOT_PROGRESS(-1);
 		return 1;
@@ -502,68 +592,20 @@
 		return 1;
 	}
 
-	dev = simple_strtoul(boot_device, &ep, 16);
+	idx = simple_strtoul(boot_device, NULL, 16);
 
-	if (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) {
-		printf("\n** Device %d not available\n", dev);
+	if (idx < 0 || idx >= CFG_MAX_NAND_DEVICE || !nand_info[idx].name) {
+		printf("\n** Device %d not available\n", idx);
 		SHOW_BOOT_PROGRESS(-1);
 		return 1;
 	}
 
-	nand = &nand_info[dev];
-	printf("\nLoading from device %d: %s (offset 0x%lx)\n",
-	       dev, nand->name, offset);
-
-	cnt = nand->oobblock;
-	r = nand_read(nand, offset, &cnt, (u_char *) addr);
-	if (r) {
-		printf("** Read error on %d\n", dev);
-		SHOW_BOOT_PROGRESS(-1);
-		return 1;
-	}
-
-	hdr = (image_header_t *) addr;
-
-	if (ntohl(hdr->ih_magic) != IH_MAGIC) {
-		printf("\n** Bad Magic Number 0x%x **\n", hdr->ih_magic);
-		SHOW_BOOT_PROGRESS(-1);
-		return 1;
-	}
-
-	print_image_hdr(hdr);
-
-	cnt = (ntohl(hdr->ih_size) + sizeof (image_header_t));
-
-	r = nand_read(nand, offset, &cnt, (u_char *) addr);
-	if (r) {
-		printf("** Read error on %d\n", dev);
-		SHOW_BOOT_PROGRESS(-1);
-		return 1;
-	}
-
-	/* Loading ok, update default load address */
-
-	load_addr = addr;
-
-	/* Check if we should attempt an auto-start */
-	if (((ep = getenv("autostart")) != NULL) && (strcmp(ep, "yes") == 0)) {
-		char *local_args[2];
-		extern int do_bootm(cmd_tbl_t *, int, int, char *[]);
-
-		local_args[0] = argv[0];
-		local_args[1] = NULL;
-
-		printf("Automatic boot of image at addr 0x%08lx ...\n", addr);
-
-		do_bootm(cmdtp, 0, 1, local_args);
-		return 1;
-	}
-	return 0;
+	return nand_load_image(cmdtp, &nand_info[idx], offset, addr, argv[0]);
 }
 
 U_BOOT_CMD(nboot, 4, 1, do_nandboot,
-	"nboot   - boot from NAND device\n", "loadAddr dev\n");
-
+	"nboot   - boot from NAND device\n",
+	"[partition] | [[[loadAddr] dev] offset]\n");
 
 #endif				/* (CONFIG_COMMANDS & CFG_CMD_NAND) */