xref: /openbmc/u-boot/cmd/mtdparts.c (revision a1588ac8)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
22e192b24SSimon Glass /*
32e192b24SSimon Glass  * (C) Copyright 2002
42e192b24SSimon Glass  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
52e192b24SSimon Glass  *
62e192b24SSimon Glass  * (C) Copyright 2002
72e192b24SSimon Glass  * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de>
82e192b24SSimon Glass  *
92e192b24SSimon Glass  * (C) Copyright 2003
102e192b24SSimon Glass  * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de>
112e192b24SSimon Glass  *
122e192b24SSimon Glass  * (C) Copyright 2005
132e192b24SSimon Glass  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
142e192b24SSimon Glass  *
152e192b24SSimon Glass  *   Added support for reading flash partition table from environment.
162e192b24SSimon Glass  *   Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4
172e192b24SSimon Glass  *   kernel tree.
182e192b24SSimon Glass  *
192e192b24SSimon Glass  * (C) Copyright 2008
202e192b24SSimon Glass  * Harald Welte, OpenMoko, Inc., Harald Welte <laforge@openmoko.org>
212e192b24SSimon Glass  *
222e192b24SSimon Glass  *   $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $
232e192b24SSimon Glass  *   Copyright 2002 SYSGO Real-Time Solutions GmbH
242e192b24SSimon Glass  */
252e192b24SSimon Glass 
262e192b24SSimon Glass /*
272e192b24SSimon Glass  * Three environment variables are used by the parsing routines:
282e192b24SSimon Glass  *
292e192b24SSimon Glass  * 'partition' - keeps current partition identifier
302e192b24SSimon Glass  *
312e192b24SSimon Glass  * partition  := <part-id>
322e192b24SSimon Glass  * <part-id>  := <dev-id>,part_num
332e192b24SSimon Glass  *
342e192b24SSimon Glass  *
352e192b24SSimon Glass  * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping
362e192b24SSimon Glass  *
372e192b24SSimon Glass  * mtdids=<idmap>[,<idmap>,...]
382e192b24SSimon Glass  *
392e192b24SSimon Glass  * <idmap>    := <dev-id>=<mtd-id>
4000ac922dSMiquel Raynal  * <dev-id>   := 'nand'|'nor'|'onenand'|'spi-nand'<dev-num>
412e192b24SSimon Glass  * <dev-num>  := mtd device number, 0...
422e192b24SSimon Glass  * <mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)
432e192b24SSimon Glass  *
442e192b24SSimon Glass  *
452e192b24SSimon Glass  * 'mtdparts' - partition list
462e192b24SSimon Glass  *
47*d60aea94SMiquel Raynal  * mtdparts=[mtdparts=]<mtd-def>[;<mtd-def>...]
482e192b24SSimon Glass  *
492e192b24SSimon Glass  * <mtd-def>  := <mtd-id>:<part-def>[,<part-def>...]
502e192b24SSimon Glass  * <mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)
512e192b24SSimon Glass  * <part-def> := <size>[@<offset>][<name>][<ro-flag>]
522e192b24SSimon Glass  * <size>     := standard linux memsize OR '-' to denote all remaining space
532e192b24SSimon Glass  * <offset>   := partition start offset within the device
542e192b24SSimon Glass  * <name>     := '(' NAME ')'
552e192b24SSimon Glass  * <ro-flag>  := when set to 'ro' makes partition read-only (not used, passed to kernel)
562e192b24SSimon Glass  *
572e192b24SSimon Glass  * Notes:
582e192b24SSimon Glass  * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping
592e192b24SSimon Glass  * - if the above variables are not set defaults for a given target are used
602e192b24SSimon Glass  *
612e192b24SSimon Glass  * Examples:
622e192b24SSimon Glass  *
632e192b24SSimon Glass  * 1 NOR Flash, with 1 single writable partition:
642e192b24SSimon Glass  * mtdids=nor0=edb7312-nor
65*d60aea94SMiquel Raynal  * mtdparts=[mtdparts=]edb7312-nor:-
662e192b24SSimon Glass  *
672e192b24SSimon Glass  * 1 NOR Flash with 2 partitions, 1 NAND with one
682e192b24SSimon Glass  * mtdids=nor0=edb7312-nor,nand0=edb7312-nand
69*d60aea94SMiquel Raynal  * mtdparts=[mtdparts=]edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
702e192b24SSimon Glass  *
712e192b24SSimon Glass  */
722e192b24SSimon Glass 
732e192b24SSimon Glass #include <common.h>
742e192b24SSimon Glass #include <command.h>
752e192b24SSimon Glass #include <malloc.h>
762e192b24SSimon Glass #include <jffs2/load_kernel.h>
772e192b24SSimon Glass #include <linux/list.h>
782e192b24SSimon Glass #include <linux/ctype.h>
792e192b24SSimon Glass #include <linux/err.h>
802e192b24SSimon Glass #include <linux/mtd/mtd.h>
812e192b24SSimon Glass 
822e192b24SSimon Glass #if defined(CONFIG_CMD_NAND)
836ae3900aSMasahiro Yamada #include <linux/mtd/rawnand.h>
842e192b24SSimon Glass #include <nand.h>
852e192b24SSimon Glass #endif
862e192b24SSimon Glass 
872e192b24SSimon Glass #if defined(CONFIG_CMD_ONENAND)
882e192b24SSimon Glass #include <linux/mtd/onenand.h>
892e192b24SSimon Glass #include <onenand_uboot.h>
902e192b24SSimon Glass #endif
912e192b24SSimon Glass 
922e192b24SSimon Glass DECLARE_GLOBAL_DATA_PTR;
932e192b24SSimon Glass 
942e192b24SSimon Glass /* special size referring to all the remaining space in a partition */
952e192b24SSimon Glass #define SIZE_REMAINING		(~0llu)
962e192b24SSimon Glass 
972e192b24SSimon Glass /* special offset value, it is used when not provided by user
982e192b24SSimon Glass  *
992e192b24SSimon Glass  * this value is used temporarily during parsing, later such offests
1002e192b24SSimon Glass  * are recalculated */
1012e192b24SSimon Glass #define OFFSET_NOT_SPECIFIED	(~0llu)
1022e192b24SSimon Glass 
1032e192b24SSimon Glass /* minimum partition size */
1042e192b24SSimon Glass #define MIN_PART_SIZE		4096
1052e192b24SSimon Glass 
1062e192b24SSimon Glass /* this flag needs to be set in part_info struct mask_flags
1072e192b24SSimon Glass  * field for read-only partitions */
1082e192b24SSimon Glass #define MTD_WRITEABLE_CMD		1
1092e192b24SSimon Glass 
1102e192b24SSimon Glass /* default values for mtdids and mtdparts variables */
111af324436SLadislav Michl #if !defined(MTDIDS_DEFAULT)
1120269dfaeSMaxime Ripard #ifdef CONFIG_MTDIDS_DEFAULT
1130269dfaeSMaxime Ripard #define MTDIDS_DEFAULT CONFIG_MTDIDS_DEFAULT
1140269dfaeSMaxime Ripard #else
115af324436SLadislav Michl #define MTDIDS_DEFAULT NULL
1162e192b24SSimon Glass #endif
1170269dfaeSMaxime Ripard #endif
118af324436SLadislav Michl #if !defined(MTDPARTS_DEFAULT)
1190269dfaeSMaxime Ripard #ifdef CONFIG_MTDPARTS_DEFAULT
1200269dfaeSMaxime Ripard #define MTDPARTS_DEFAULT CONFIG_MTDPARTS_DEFAULT
1210269dfaeSMaxime Ripard #else
122af324436SLadislav Michl #define MTDPARTS_DEFAULT NULL
1232e192b24SSimon Glass #endif
1240269dfaeSMaxime Ripard #endif
125af324436SLadislav Michl #if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
126af324436SLadislav Michl extern void board_mtdparts_default(const char **mtdids, const char **mtdparts);
127af324436SLadislav Michl #endif
128af324436SLadislav Michl static const char *mtdids_default = MTDIDS_DEFAULT;
129af324436SLadislav Michl static const char *mtdparts_default = MTDPARTS_DEFAULT;
1302e192b24SSimon Glass 
1312e192b24SSimon Glass /* copies of last seen 'mtdids', 'mtdparts' and 'partition' env variables */
1322e192b24SSimon Glass #define MTDIDS_MAXLEN		128
1332e192b24SSimon Glass #define MTDPARTS_MAXLEN		512
1342e192b24SSimon Glass #define PARTITION_MAXLEN	16
13554f1792cSTom Rini static char last_ids[MTDIDS_MAXLEN + 1];
13654f1792cSTom Rini static char last_parts[MTDPARTS_MAXLEN + 1];
13754f1792cSTom Rini static char last_partition[PARTITION_MAXLEN + 1];
1382e192b24SSimon Glass 
1392e192b24SSimon Glass /* low level jffs2 cache cleaning routine */
1402e192b24SSimon Glass extern void jffs2_free_cache(struct part_info *part);
1412e192b24SSimon Glass 
1422e192b24SSimon Glass /* mtdids mapping list, filled by parse_ids() */
1432e192b24SSimon Glass static struct list_head mtdids;
1442e192b24SSimon Glass 
1452e192b24SSimon Glass /* device/partition list, parse_cmdline() parses into here */
1462e192b24SSimon Glass static struct list_head devices;
1472e192b24SSimon Glass 
1482e192b24SSimon Glass /* current active device and partition number */
1492e192b24SSimon Glass struct mtd_device *current_mtd_dev = NULL;
1502e192b24SSimon Glass u8 current_mtd_partnum = 0;
1512e192b24SSimon Glass 
152f8f744a3SLadislav Michl u8 use_defaults;
153f8f744a3SLadislav Michl 
1542e192b24SSimon Glass static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num);
1552e192b24SSimon Glass 
1562e192b24SSimon Glass /* command line only routines */
1572e192b24SSimon Glass static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len);
1582e192b24SSimon Glass static int device_del(struct mtd_device *dev);
1592e192b24SSimon Glass 
1602e192b24SSimon Glass /**
1612e192b24SSimon Glass  * Parses a string into a number.  The number stored at ptr is
1622e192b24SSimon Glass  * potentially suffixed with K (for kilobytes, or 1024 bytes),
1632e192b24SSimon Glass  * M (for megabytes, or 1048576 bytes), or G (for gigabytes, or
1642e192b24SSimon Glass  * 1073741824).  If the number is suffixed with K, M, or G, then
1652e192b24SSimon Glass  * the return value is the number multiplied by one kilobyte, one
1662e192b24SSimon Glass  * megabyte, or one gigabyte, respectively.
1672e192b24SSimon Glass  *
1682e192b24SSimon Glass  * @param ptr where parse begins
1692e192b24SSimon Glass  * @param retptr output pointer to next char after parse completes (output)
1702e192b24SSimon Glass  * @return resulting unsigned int
1712e192b24SSimon Glass  */
memsize_parse(const char * const ptr,const char ** retptr)1722e192b24SSimon Glass static u64 memsize_parse (const char *const ptr, const char **retptr)
1732e192b24SSimon Glass {
1742e192b24SSimon Glass 	u64 ret = simple_strtoull(ptr, (char **)retptr, 0);
1752e192b24SSimon Glass 
1762e192b24SSimon Glass 	switch (**retptr) {
1772e192b24SSimon Glass 		case 'G':
1782e192b24SSimon Glass 		case 'g':
1792e192b24SSimon Glass 			ret <<= 10;
1802b9ace55SMiquel Raynal 			/* Fallthrough */
1812e192b24SSimon Glass 		case 'M':
1822e192b24SSimon Glass 		case 'm':
1832e192b24SSimon Glass 			ret <<= 10;
1842b9ace55SMiquel Raynal 			/* Fallthrough */
1852e192b24SSimon Glass 		case 'K':
1862e192b24SSimon Glass 		case 'k':
1872e192b24SSimon Glass 			ret <<= 10;
1882e192b24SSimon Glass 			(*retptr)++;
1892b9ace55SMiquel Raynal 			/* Fallthrough */
1902e192b24SSimon Glass 		default:
1912e192b24SSimon Glass 			break;
1922e192b24SSimon Glass 	}
1932e192b24SSimon Glass 
1942e192b24SSimon Glass 	return ret;
1952e192b24SSimon Glass }
1962e192b24SSimon Glass 
1972e192b24SSimon Glass /**
1982e192b24SSimon Glass  * Format string describing supplied size. This routine does the opposite job
1992e192b24SSimon Glass  * to memsize_parse(). Size in bytes is converted to string and if possible
2002e192b24SSimon Glass  * shortened by using k (kilobytes), m (megabytes) or g (gigabytes) suffix.
2012e192b24SSimon Glass  *
2022e192b24SSimon Glass  * Note, that this routine does not check for buffer overflow, it's the caller
2032e192b24SSimon Glass  * who must assure enough space.
2042e192b24SSimon Glass  *
2052e192b24SSimon Glass  * @param buf output buffer
2062e192b24SSimon Glass  * @param size size to be converted to string
2072e192b24SSimon Glass  */
memsize_format(char * buf,u64 size)2082e192b24SSimon Glass static void memsize_format(char *buf, u64 size)
2092e192b24SSimon Glass {
2102e192b24SSimon Glass #define SIZE_GB ((u32)1024*1024*1024)
2112e192b24SSimon Glass #define SIZE_MB ((u32)1024*1024)
2122e192b24SSimon Glass #define SIZE_KB ((u32)1024)
2132e192b24SSimon Glass 
2142e192b24SSimon Glass 	if ((size % SIZE_GB) == 0)
2152e192b24SSimon Glass 		sprintf(buf, "%llug", size/SIZE_GB);
2162e192b24SSimon Glass 	else if ((size % SIZE_MB) == 0)
2172e192b24SSimon Glass 		sprintf(buf, "%llum", size/SIZE_MB);
2182e192b24SSimon Glass 	else if (size % SIZE_KB == 0)
2192e192b24SSimon Glass 		sprintf(buf, "%lluk", size/SIZE_KB);
2202e192b24SSimon Glass 	else
2212e192b24SSimon Glass 		sprintf(buf, "%llu", size);
2222e192b24SSimon Glass }
2232e192b24SSimon Glass 
2242e192b24SSimon Glass /**
2252e192b24SSimon Glass  * This routine does global indexing of all partitions. Resulting index for
2262e192b24SSimon Glass  * current partition is saved in 'mtddevnum'. Current partition name in
2272e192b24SSimon Glass  * 'mtddevname'.
2282e192b24SSimon Glass  */
index_partitions(void)2292e192b24SSimon Glass static void index_partitions(void)
2302e192b24SSimon Glass {
2312e192b24SSimon Glass 	u16 mtddevnum;
2322e192b24SSimon Glass 	struct part_info *part;
2332e192b24SSimon Glass 	struct list_head *dentry;
2342e192b24SSimon Glass 	struct mtd_device *dev;
2352e192b24SSimon Glass 
2362e192b24SSimon Glass 	debug("--- index partitions ---\n");
2372e192b24SSimon Glass 
2382e192b24SSimon Glass 	if (current_mtd_dev) {
2392e192b24SSimon Glass 		mtddevnum = 0;
2402e192b24SSimon Glass 		list_for_each(dentry, &devices) {
2412e192b24SSimon Glass 			dev = list_entry(dentry, struct mtd_device, link);
2422e192b24SSimon Glass 			if (dev == current_mtd_dev) {
2432e192b24SSimon Glass 				mtddevnum += current_mtd_partnum;
244018f5303SSimon Glass 				env_set_ulong("mtddevnum", mtddevnum);
24554f1792cSTom Rini 				debug("=> mtddevnum %d,\n", mtddevnum);
2462e192b24SSimon Glass 				break;
2472e192b24SSimon Glass 			}
2482e192b24SSimon Glass 			mtddevnum += dev->num_parts;
2492e192b24SSimon Glass 		}
2502e192b24SSimon Glass 
2512e192b24SSimon Glass 		part = mtd_part_info(current_mtd_dev, current_mtd_partnum);
25254f1792cSTom Rini 		if (part) {
253382bee57SSimon Glass 			env_set("mtddevname", part->name);
2542e192b24SSimon Glass 
25554f1792cSTom Rini 			debug("=> mtddevname %s\n", part->name);
25654f1792cSTom Rini 		} else {
25754f1792cSTom Rini 			env_set("mtddevname", NULL);
25854f1792cSTom Rini 
25954f1792cSTom Rini 			debug("=> mtddevname NULL\n");
26054f1792cSTom Rini 		}
2612e192b24SSimon Glass 	} else {
262382bee57SSimon Glass 		env_set("mtddevnum", NULL);
263382bee57SSimon Glass 		env_set("mtddevname", NULL);
2642e192b24SSimon Glass 
2652e192b24SSimon Glass 		debug("=> mtddevnum NULL\n=> mtddevname NULL\n");
2662e192b24SSimon Glass 	}
2672e192b24SSimon Glass }
2682e192b24SSimon Glass 
2692e192b24SSimon Glass /**
2702e192b24SSimon Glass  * Save current device and partition in environment variable 'partition'.
2712e192b24SSimon Glass  */
current_save(void)2722e192b24SSimon Glass static void current_save(void)
2732e192b24SSimon Glass {
2742e192b24SSimon Glass 	char buf[16];
2752e192b24SSimon Glass 
2762e192b24SSimon Glass 	debug("--- current_save ---\n");
2772e192b24SSimon Glass 
2782e192b24SSimon Glass 	if (current_mtd_dev) {
2792e192b24SSimon Glass 		sprintf(buf, "%s%d,%d", MTD_DEV_TYPE(current_mtd_dev->id->type),
2802e192b24SSimon Glass 					current_mtd_dev->id->num, current_mtd_partnum);
2812e192b24SSimon Glass 
282382bee57SSimon Glass 		env_set("partition", buf);
2832e192b24SSimon Glass 		strncpy(last_partition, buf, 16);
2842e192b24SSimon Glass 
2852e192b24SSimon Glass 		debug("=> partition %s\n", buf);
2862e192b24SSimon Glass 	} else {
287382bee57SSimon Glass 		env_set("partition", NULL);
2882e192b24SSimon Glass 		last_partition[0] = '\0';
2892e192b24SSimon Glass 
2902e192b24SSimon Glass 		debug("=> partition NULL\n");
2912e192b24SSimon Glass 	}
2922e192b24SSimon Glass 	index_partitions();
2932e192b24SSimon Glass }
2942e192b24SSimon Glass 
2952e192b24SSimon Glass 
2962e192b24SSimon Glass /**
2972e192b24SSimon Glass  * Produce a mtd_info given a type and num.
2982e192b24SSimon Glass  *
2992e192b24SSimon Glass  * @param type mtd type
3002e192b24SSimon Glass  * @param num mtd number
3012e192b24SSimon Glass  * @param mtd a pointer to an mtd_info instance (output)
3022e192b24SSimon Glass  * @return 0 if device is valid, 1 otherwise
3032e192b24SSimon Glass  */
get_mtd_info(u8 type,u8 num,struct mtd_info ** mtd)3042e192b24SSimon Glass static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd)
3052e192b24SSimon Glass {
3062e192b24SSimon Glass 	char mtd_dev[16];
3072e192b24SSimon Glass 
3082e192b24SSimon Glass 	sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num);
3092e192b24SSimon Glass 	*mtd = get_mtd_device_nm(mtd_dev);
3102e192b24SSimon Glass 	if (IS_ERR(*mtd)) {
3112e192b24SSimon Glass 		printf("Device %s not found!\n", mtd_dev);
3122e192b24SSimon Glass 		return 1;
3132e192b24SSimon Glass 	}
3142e192b24SSimon Glass 	put_mtd_device(*mtd);
3152e192b24SSimon Glass 
3162e192b24SSimon Glass 	return 0;
3172e192b24SSimon Glass }
3182e192b24SSimon Glass 
3192e192b24SSimon Glass /**
3202e192b24SSimon Glass  * Performs sanity check for supplied flash partition.
3212e192b24SSimon Glass  * Table of existing MTD flash devices is searched and partition device
3222e192b24SSimon Glass  * is located. Alignment with the granularity of nand erasesize is verified.
3232e192b24SSimon Glass  *
3242e192b24SSimon Glass  * @param id of the parent device
3252e192b24SSimon Glass  * @param part partition to validate
3262e192b24SSimon Glass  * @return 0 if partition is valid, 1 otherwise
3272e192b24SSimon Glass  */
part_validate_eraseblock(struct mtdids * id,struct part_info * part)3282e192b24SSimon Glass static int part_validate_eraseblock(struct mtdids *id, struct part_info *part)
3292e192b24SSimon Glass {
3302e192b24SSimon Glass 	struct mtd_info *mtd = NULL;
3312e192b24SSimon Glass 	int i, j;
3322e192b24SSimon Glass 	ulong start;
3332e192b24SSimon Glass 	u64 offset, size;
3342e192b24SSimon Glass 
3352e192b24SSimon Glass 	if (get_mtd_info(id->type, id->num, &mtd))
3362e192b24SSimon Glass 		return 1;
3372e192b24SSimon Glass 
3382e192b24SSimon Glass 	part->sector_size = mtd->erasesize;
3392e192b24SSimon Glass 
3402e192b24SSimon Glass 	if (!mtd->numeraseregions) {
3412e192b24SSimon Glass 		/*
34200ac922dSMiquel Raynal 		 * Only one eraseregion (NAND, SPI-NAND, OneNAND or uniform NOR),
3432e192b24SSimon Glass 		 * checking for alignment is easy here
3442e192b24SSimon Glass 		 */
3452e192b24SSimon Glass 		offset = part->offset;
3462e192b24SSimon Glass 		if (do_div(offset, mtd->erasesize)) {
3472e192b24SSimon Glass 			printf("%s%d: partition (%s) start offset"
3482e192b24SSimon Glass 			       "alignment incorrect\n",
3492e192b24SSimon Glass 			       MTD_DEV_TYPE(id->type), id->num, part->name);
3502e192b24SSimon Glass 			return 1;
3512e192b24SSimon Glass 		}
3522e192b24SSimon Glass 
3532e192b24SSimon Glass 		size = part->size;
3542e192b24SSimon Glass 		if (do_div(size, mtd->erasesize)) {
3552e192b24SSimon Glass 			printf("%s%d: partition (%s) size alignment incorrect\n",
3562e192b24SSimon Glass 			       MTD_DEV_TYPE(id->type), id->num, part->name);
3572e192b24SSimon Glass 			return 1;
3582e192b24SSimon Glass 		}
3592e192b24SSimon Glass 	} else {
3602e192b24SSimon Glass 		/*
3612e192b24SSimon Glass 		 * Multiple eraseregions (non-uniform NOR),
3622e192b24SSimon Glass 		 * checking for alignment is more complex here
3632e192b24SSimon Glass 		 */
3642e192b24SSimon Glass 
3652e192b24SSimon Glass 		/* Check start alignment */
3662e192b24SSimon Glass 		for (i = 0; i < mtd->numeraseregions; i++) {
3672e192b24SSimon Glass 			start = mtd->eraseregions[i].offset;
3682e192b24SSimon Glass 			for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
3692e192b24SSimon Glass 				if (part->offset == start)
3702e192b24SSimon Glass 					goto start_ok;
3712e192b24SSimon Glass 				start += mtd->eraseregions[i].erasesize;
3722e192b24SSimon Glass 			}
3732e192b24SSimon Glass 		}
3742e192b24SSimon Glass 
3752e192b24SSimon Glass 		printf("%s%d: partition (%s) start offset alignment incorrect\n",
3762e192b24SSimon Glass 		       MTD_DEV_TYPE(id->type), id->num, part->name);
3772e192b24SSimon Glass 		return 1;
3782e192b24SSimon Glass 
3792e192b24SSimon Glass 	start_ok:
3802e192b24SSimon Glass 
3812e192b24SSimon Glass 		/* Check end/size alignment */
3822e192b24SSimon Glass 		for (i = 0; i < mtd->numeraseregions; i++) {
3832e192b24SSimon Glass 			start = mtd->eraseregions[i].offset;
3842e192b24SSimon Glass 			for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
3852e192b24SSimon Glass 				if ((part->offset + part->size) == start)
3862e192b24SSimon Glass 					goto end_ok;
3872e192b24SSimon Glass 				start += mtd->eraseregions[i].erasesize;
3882e192b24SSimon Glass 			}
3892e192b24SSimon Glass 		}
3902e192b24SSimon Glass 		/* Check last sector alignment */
3912e192b24SSimon Glass 		if ((part->offset + part->size) == start)
3922e192b24SSimon Glass 			goto end_ok;
3932e192b24SSimon Glass 
3942e192b24SSimon Glass 		printf("%s%d: partition (%s) size alignment incorrect\n",
3952e192b24SSimon Glass 		       MTD_DEV_TYPE(id->type), id->num, part->name);
3962e192b24SSimon Glass 		return 1;
3972e192b24SSimon Glass 
3982e192b24SSimon Glass 	end_ok:
3992e192b24SSimon Glass 		return 0;
4002e192b24SSimon Glass 	}
4012e192b24SSimon Glass 
4022e192b24SSimon Glass 	return 0;
4032e192b24SSimon Glass }
4042e192b24SSimon Glass 
4052e192b24SSimon Glass 
4062e192b24SSimon Glass /**
4072e192b24SSimon Glass  * Performs sanity check for supplied partition. Offset and size are
4082e192b24SSimon Glass  * verified to be within valid range. Partition type is checked and
4092e192b24SSimon Glass  * part_validate_eraseblock() is called with the argument of part.
4102e192b24SSimon Glass  *
4112e192b24SSimon Glass  * @param id of the parent device
4122e192b24SSimon Glass  * @param part partition to validate
4132e192b24SSimon Glass  * @return 0 if partition is valid, 1 otherwise
4142e192b24SSimon Glass  */
part_validate(struct mtdids * id,struct part_info * part)4152e192b24SSimon Glass static int part_validate(struct mtdids *id, struct part_info *part)
4162e192b24SSimon Glass {
4172e192b24SSimon Glass 	if (part->size == SIZE_REMAINING)
4182e192b24SSimon Glass 		part->size = id->size - part->offset;
4192e192b24SSimon Glass 
4202e192b24SSimon Glass 	if (part->offset > id->size) {
4212e192b24SSimon Glass 		printf("%s: offset %08llx beyond flash size %08llx\n",
4222e192b24SSimon Glass 				id->mtd_id, part->offset, id->size);
4232e192b24SSimon Glass 		return 1;
4242e192b24SSimon Glass 	}
4252e192b24SSimon Glass 
4262e192b24SSimon Glass 	if ((part->offset + part->size) <= part->offset) {
4272e192b24SSimon Glass 		printf("%s%d: partition (%s) size too big\n",
4282e192b24SSimon Glass 				MTD_DEV_TYPE(id->type), id->num, part->name);
4292e192b24SSimon Glass 		return 1;
4302e192b24SSimon Glass 	}
4312e192b24SSimon Glass 
4322e192b24SSimon Glass 	if (part->offset + part->size > id->size) {
4332e192b24SSimon Glass 		printf("%s: partitioning exceeds flash size\n", id->mtd_id);
4342e192b24SSimon Glass 		return 1;
4352e192b24SSimon Glass 	}
4362e192b24SSimon Glass 
4372e192b24SSimon Glass 	/*
4382e192b24SSimon Glass 	 * Now we need to check if the partition starts and ends on
4392e192b24SSimon Glass 	 * sector (eraseblock) regions
4402e192b24SSimon Glass 	 */
4412e192b24SSimon Glass 	return part_validate_eraseblock(id, part);
4422e192b24SSimon Glass }
4432e192b24SSimon Glass 
4442e192b24SSimon Glass /**
4452e192b24SSimon Glass  * Delete selected partition from the partition list of the specified device.
4462e192b24SSimon Glass  *
4472e192b24SSimon Glass  * @param dev device to delete partition from
4482e192b24SSimon Glass  * @param part partition to delete
4492e192b24SSimon Glass  * @return 0 on success, 1 otherwise
4502e192b24SSimon Glass  */
part_del(struct mtd_device * dev,struct part_info * part)4512e192b24SSimon Glass static int part_del(struct mtd_device *dev, struct part_info *part)
4522e192b24SSimon Glass {
4532e192b24SSimon Glass 	u8 current_save_needed = 0;
4542e192b24SSimon Glass 
4552e192b24SSimon Glass 	/* if there is only one partition, remove whole device */
4562e192b24SSimon Glass 	if (dev->num_parts == 1)
4572e192b24SSimon Glass 		return device_del(dev);
4582e192b24SSimon Glass 
4592e192b24SSimon Glass 	/* otherwise just delete this partition */
4602e192b24SSimon Glass 
4612e192b24SSimon Glass 	if (dev == current_mtd_dev) {
4622e192b24SSimon Glass 		/* we are modyfing partitions for the current device,
4632e192b24SSimon Glass 		 * update current */
4642e192b24SSimon Glass 		struct part_info *curr_pi;
4652e192b24SSimon Glass 		curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum);
4662e192b24SSimon Glass 
4672e192b24SSimon Glass 		if (curr_pi) {
4682e192b24SSimon Glass 			if (curr_pi == part) {
4692e192b24SSimon Glass 				printf("current partition deleted, resetting current to 0\n");
4702e192b24SSimon Glass 				current_mtd_partnum = 0;
4712e192b24SSimon Glass 			} else if (part->offset <= curr_pi->offset) {
4722e192b24SSimon Glass 				current_mtd_partnum--;
4732e192b24SSimon Glass 			}
4742e192b24SSimon Glass 			current_save_needed = 1;
4752e192b24SSimon Glass 		}
4762e192b24SSimon Glass 	}
4772e192b24SSimon Glass 
4782e192b24SSimon Glass 	list_del(&part->link);
4792e192b24SSimon Glass 	free(part);
4802e192b24SSimon Glass 	dev->num_parts--;
4812e192b24SSimon Glass 
4822e192b24SSimon Glass 	if (current_save_needed > 0)
4832e192b24SSimon Glass 		current_save();
4842e192b24SSimon Glass 	else
4852e192b24SSimon Glass 		index_partitions();
4862e192b24SSimon Glass 
4872e192b24SSimon Glass 	return 0;
4882e192b24SSimon Glass }
4892e192b24SSimon Glass 
4902e192b24SSimon Glass /**
4912e192b24SSimon Glass  * Delete all partitions from parts head list, free memory.
4922e192b24SSimon Glass  *
4932e192b24SSimon Glass  * @param head list of partitions to delete
4942e192b24SSimon Glass  */
part_delall(struct list_head * head)4952e192b24SSimon Glass static void part_delall(struct list_head *head)
4962e192b24SSimon Glass {
4972e192b24SSimon Glass 	struct list_head *entry, *n;
4982e192b24SSimon Glass 	struct part_info *part_tmp;
4992e192b24SSimon Glass 
5002e192b24SSimon Glass 	/* clean tmp_list and free allocated memory */
5012e192b24SSimon Glass 	list_for_each_safe(entry, n, head) {
5022e192b24SSimon Glass 		part_tmp = list_entry(entry, struct part_info, link);
5032e192b24SSimon Glass 
5042e192b24SSimon Glass 		list_del(entry);
5052e192b24SSimon Glass 		free(part_tmp);
5062e192b24SSimon Glass 	}
5072e192b24SSimon Glass }
5082e192b24SSimon Glass 
5092e192b24SSimon Glass /**
5102e192b24SSimon Glass  * Add new partition to the supplied partition list. Make sure partitions are
5112e192b24SSimon Glass  * sorted by offset in ascending order.
5122e192b24SSimon Glass  *
5132e192b24SSimon Glass  * @param head list this partition is to be added to
5142e192b24SSimon Glass  * @param new partition to be added
5152e192b24SSimon Glass  */
part_sort_add(struct mtd_device * dev,struct part_info * part)5162e192b24SSimon Glass static int part_sort_add(struct mtd_device *dev, struct part_info *part)
5172e192b24SSimon Glass {
5182e192b24SSimon Glass 	struct list_head *entry;
5192e192b24SSimon Glass 	struct part_info *new_pi, *curr_pi;
5202e192b24SSimon Glass 
5212e192b24SSimon Glass 	/* link partition to parrent dev */
5222e192b24SSimon Glass 	part->dev = dev;
5232e192b24SSimon Glass 
5242e192b24SSimon Glass 	if (list_empty(&dev->parts)) {
5252e192b24SSimon Glass 		debug("part_sort_add: list empty\n");
5262e192b24SSimon Glass 		list_add(&part->link, &dev->parts);
5272e192b24SSimon Glass 		dev->num_parts++;
5282e192b24SSimon Glass 		index_partitions();
5292e192b24SSimon Glass 		return 0;
5302e192b24SSimon Glass 	}
5312e192b24SSimon Glass 
5322e192b24SSimon Glass 	new_pi = list_entry(&part->link, struct part_info, link);
5332e192b24SSimon Glass 
5342e192b24SSimon Glass 	/* get current partition info if we are updating current device */
5352e192b24SSimon Glass 	curr_pi = NULL;
5362e192b24SSimon Glass 	if (dev == current_mtd_dev)
5372e192b24SSimon Glass 		curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum);
5382e192b24SSimon Glass 
5392e192b24SSimon Glass 	list_for_each(entry, &dev->parts) {
5402e192b24SSimon Glass 		struct part_info *pi;
5412e192b24SSimon Glass 
5422e192b24SSimon Glass 		pi = list_entry(entry, struct part_info, link);
5432e192b24SSimon Glass 
5442e192b24SSimon Glass 		/* be compliant with kernel cmdline, allow only one partition at offset zero */
5452e192b24SSimon Glass 		if ((new_pi->offset == pi->offset) && (pi->offset == 0)) {
5462e192b24SSimon Glass 			printf("cannot add second partition at offset 0\n");
5472e192b24SSimon Glass 			return 1;
5482e192b24SSimon Glass 		}
5492e192b24SSimon Glass 
5502e192b24SSimon Glass 		if (new_pi->offset <= pi->offset) {
5512e192b24SSimon Glass 			list_add_tail(&part->link, entry);
5522e192b24SSimon Glass 			dev->num_parts++;
5532e192b24SSimon Glass 
5542e192b24SSimon Glass 			if (curr_pi && (pi->offset <= curr_pi->offset)) {
5552e192b24SSimon Glass 				/* we are modyfing partitions for the current
5562e192b24SSimon Glass 				 * device, update current */
5572e192b24SSimon Glass 				current_mtd_partnum++;
5582e192b24SSimon Glass 				current_save();
5592e192b24SSimon Glass 			} else {
5602e192b24SSimon Glass 				index_partitions();
5612e192b24SSimon Glass 			}
5622e192b24SSimon Glass 			return 0;
5632e192b24SSimon Glass 		}
5642e192b24SSimon Glass 	}
5652e192b24SSimon Glass 
5662e192b24SSimon Glass 	list_add_tail(&part->link, &dev->parts);
5672e192b24SSimon Glass 	dev->num_parts++;
5682e192b24SSimon Glass 	index_partitions();
5692e192b24SSimon Glass 	return 0;
5702e192b24SSimon Glass }
5712e192b24SSimon Glass 
5722e192b24SSimon Glass /**
5732e192b24SSimon Glass  * Add provided partition to the partition list of a given device.
5742e192b24SSimon Glass  *
5752e192b24SSimon Glass  * @param dev device to which partition is added
5762e192b24SSimon Glass  * @param part partition to be added
5772e192b24SSimon Glass  * @return 0 on success, 1 otherwise
5782e192b24SSimon Glass  */
part_add(struct mtd_device * dev,struct part_info * part)5792e192b24SSimon Glass static int part_add(struct mtd_device *dev, struct part_info *part)
5802e192b24SSimon Glass {
5812e192b24SSimon Glass 	/* verify alignment and size */
5822e192b24SSimon Glass 	if (part_validate(dev->id, part) != 0)
5832e192b24SSimon Glass 		return 1;
5842e192b24SSimon Glass 
5852e192b24SSimon Glass 	/* partition is ok, add it to the list */
5862e192b24SSimon Glass 	if (part_sort_add(dev, part) != 0)
5872e192b24SSimon Glass 		return 1;
5882e192b24SSimon Glass 
5892e192b24SSimon Glass 	return 0;
5902e192b24SSimon Glass }
5912e192b24SSimon Glass 
5922e192b24SSimon Glass /**
5932e192b24SSimon Glass  * Parse one partition definition, allocate memory and return pointer to this
5942e192b24SSimon Glass  * location in retpart.
5952e192b24SSimon Glass  *
5962e192b24SSimon Glass  * @param partdef pointer to the partition definition string i.e. <part-def>
5972e192b24SSimon Glass  * @param ret output pointer to next char after parse completes (output)
5982e192b24SSimon Glass  * @param retpart pointer to the allocated partition (output)
5992e192b24SSimon Glass  * @return 0 on success, 1 otherwise
6002e192b24SSimon Glass  */
part_parse(const char * const partdef,const char ** ret,struct part_info ** retpart)6012e192b24SSimon Glass static int part_parse(const char *const partdef, const char **ret, struct part_info **retpart)
6022e192b24SSimon Glass {
6032e192b24SSimon Glass 	struct part_info *part;
6042e192b24SSimon Glass 	u64 size;
6052e192b24SSimon Glass 	u64 offset;
6062e192b24SSimon Glass 	const char *name;
6072e192b24SSimon Glass 	int name_len;
6082e192b24SSimon Glass 	unsigned int mask_flags;
6092e192b24SSimon Glass 	const char *p;
6102e192b24SSimon Glass 
6112e192b24SSimon Glass 	p = partdef;
6122e192b24SSimon Glass 	*retpart = NULL;
6132e192b24SSimon Glass 	*ret = NULL;
6142e192b24SSimon Glass 
6152e192b24SSimon Glass 	/* fetch the partition size */
6162e192b24SSimon Glass 	if (*p == '-') {
6172e192b24SSimon Glass 		/* assign all remaining space to this partition */
6182e192b24SSimon Glass 		debug("'-': remaining size assigned\n");
6192e192b24SSimon Glass 		size = SIZE_REMAINING;
6202e192b24SSimon Glass 		p++;
6212e192b24SSimon Glass 	} else {
6222e192b24SSimon Glass 		size = memsize_parse(p, &p);
6232e192b24SSimon Glass 		if (size < MIN_PART_SIZE) {
6242e192b24SSimon Glass 			printf("partition size too small (%llx)\n", size);
6252e192b24SSimon Glass 			return 1;
6262e192b24SSimon Glass 		}
6272e192b24SSimon Glass 	}
6282e192b24SSimon Glass 
6292e192b24SSimon Glass 	/* check for offset */
6302e192b24SSimon Glass 	offset = OFFSET_NOT_SPECIFIED;
6312e192b24SSimon Glass 	if (*p == '@') {
6322e192b24SSimon Glass 		p++;
6332e192b24SSimon Glass 		offset = memsize_parse(p, &p);
6342e192b24SSimon Glass 	}
6352e192b24SSimon Glass 
6362e192b24SSimon Glass 	/* now look for the name */
6372e192b24SSimon Glass 	if (*p == '(') {
6382e192b24SSimon Glass 		name = ++p;
6392e192b24SSimon Glass 		if ((p = strchr(name, ')')) == NULL) {
6402e192b24SSimon Glass 			printf("no closing ) found in partition name\n");
6412e192b24SSimon Glass 			return 1;
6422e192b24SSimon Glass 		}
6432e192b24SSimon Glass 		name_len = p - name + 1;
6442e192b24SSimon Glass 		if ((name_len - 1) == 0) {
6452e192b24SSimon Glass 			printf("empty partition name\n");
6462e192b24SSimon Glass 			return 1;
6472e192b24SSimon Glass 		}
6482e192b24SSimon Glass 		p++;
6492e192b24SSimon Glass 	} else {
6502e192b24SSimon Glass 		/* 0x00000000@0x00000000 */
6512e192b24SSimon Glass 		name_len = 22;
6522e192b24SSimon Glass 		name = NULL;
6532e192b24SSimon Glass 	}
6542e192b24SSimon Glass 
6552e192b24SSimon Glass 	/* test for options */
6562e192b24SSimon Glass 	mask_flags = 0;
6572e192b24SSimon Glass 	if (strncmp(p, "ro", 2) == 0) {
6582e192b24SSimon Glass 		mask_flags |= MTD_WRITEABLE_CMD;
6592e192b24SSimon Glass 		p += 2;
6602e192b24SSimon Glass 	}
6612e192b24SSimon Glass 
6622e192b24SSimon Glass 	/* check for next partition definition */
6632e192b24SSimon Glass 	if (*p == ',') {
6642e192b24SSimon Glass 		if (size == SIZE_REMAINING) {
6652e192b24SSimon Glass 			*ret = NULL;
6662e192b24SSimon Glass 			printf("no partitions allowed after a fill-up partition\n");
6672e192b24SSimon Glass 			return 1;
6682e192b24SSimon Glass 		}
6692e192b24SSimon Glass 		*ret = ++p;
6702e192b24SSimon Glass 	} else if ((*p == ';') || (*p == '\0')) {
6712e192b24SSimon Glass 		*ret = p;
6722e192b24SSimon Glass 	} else {
6732e192b24SSimon Glass 		printf("unexpected character '%c' at the end of partition\n", *p);
6742e192b24SSimon Glass 		*ret = NULL;
6752e192b24SSimon Glass 		return 1;
6762e192b24SSimon Glass 	}
6772e192b24SSimon Glass 
6782e192b24SSimon Glass 	/*  allocate memory */
6792e192b24SSimon Glass 	part = (struct part_info *)malloc(sizeof(struct part_info) + name_len);
6802e192b24SSimon Glass 	if (!part) {
6812e192b24SSimon Glass 		printf("out of memory\n");
6822e192b24SSimon Glass 		return 1;
6832e192b24SSimon Glass 	}
6842e192b24SSimon Glass 	memset(part, 0, sizeof(struct part_info) + name_len);
6852e192b24SSimon Glass 	part->size = size;
6862e192b24SSimon Glass 	part->offset = offset;
6872e192b24SSimon Glass 	part->mask_flags = mask_flags;
6882e192b24SSimon Glass 	part->name = (char *)(part + 1);
6892e192b24SSimon Glass 
6902e192b24SSimon Glass 	if (name) {
6912e192b24SSimon Glass 		/* copy user provided name */
6922e192b24SSimon Glass 		strncpy(part->name, name, name_len - 1);
6932e192b24SSimon Glass 		part->auto_name = 0;
6942e192b24SSimon Glass 	} else {
6952e192b24SSimon Glass 		/* auto generated name in form of size@offset */
696149c21b0SKay Potthoff 		snprintf(part->name, name_len, "0x%08llx@0x%08llx", size, offset);
6972e192b24SSimon Glass 		part->auto_name = 1;
6982e192b24SSimon Glass 	}
6992e192b24SSimon Glass 
7002e192b24SSimon Glass 	part->name[name_len - 1] = '\0';
7012e192b24SSimon Glass 	INIT_LIST_HEAD(&part->link);
7022e192b24SSimon Glass 
7032e192b24SSimon Glass 	debug("+ partition: name %-22s size 0x%08llx offset 0x%08llx mask flags %d\n",
7042e192b24SSimon Glass 			part->name, part->size,
7052e192b24SSimon Glass 			part->offset, part->mask_flags);
7062e192b24SSimon Glass 
7072e192b24SSimon Glass 	*retpart = part;
7082e192b24SSimon Glass 	return 0;
7092e192b24SSimon Glass }
7102e192b24SSimon Glass 
7112e192b24SSimon Glass /**
7122e192b24SSimon Glass  * Check device number to be within valid range for given device type.
7132e192b24SSimon Glass  *
7142e192b24SSimon Glass  * @param type mtd type
7152e192b24SSimon Glass  * @param num mtd number
7162e192b24SSimon Glass  * @param size a pointer to the size of the mtd device (output)
7172e192b24SSimon Glass  * @return 0 if device is valid, 1 otherwise
7182e192b24SSimon Glass  */
mtd_device_validate(u8 type,u8 num,u64 * size)7192e192b24SSimon Glass static int mtd_device_validate(u8 type, u8 num, u64 *size)
7202e192b24SSimon Glass {
7212e192b24SSimon Glass 	struct mtd_info *mtd = NULL;
7222e192b24SSimon Glass 
7232e192b24SSimon Glass 	if (get_mtd_info(type, num, &mtd))
7242e192b24SSimon Glass 		return 1;
7252e192b24SSimon Glass 
7262e192b24SSimon Glass 	*size = mtd->size;
7272e192b24SSimon Glass 
7282e192b24SSimon Glass 	return 0;
7292e192b24SSimon Glass }
7302e192b24SSimon Glass 
7312e192b24SSimon Glass /**
7322e192b24SSimon Glass  * Delete all mtd devices from a supplied devices list, free memory allocated for
7332e192b24SSimon Glass  * each device and delete all device partitions.
7342e192b24SSimon Glass  *
7352e192b24SSimon Glass  * @return 0 on success, 1 otherwise
7362e192b24SSimon Glass  */
device_delall(struct list_head * head)7372e192b24SSimon Glass static int device_delall(struct list_head *head)
7382e192b24SSimon Glass {
7392e192b24SSimon Glass 	struct list_head *entry, *n;
7402e192b24SSimon Glass 	struct mtd_device *dev_tmp;
7412e192b24SSimon Glass 
7422e192b24SSimon Glass 	/* clean devices list */
7432e192b24SSimon Glass 	list_for_each_safe(entry, n, head) {
7442e192b24SSimon Glass 		dev_tmp = list_entry(entry, struct mtd_device, link);
7452e192b24SSimon Glass 		list_del(entry);
7462e192b24SSimon Glass 		part_delall(&dev_tmp->parts);
7472e192b24SSimon Glass 		free(dev_tmp);
7482e192b24SSimon Glass 	}
7492e192b24SSimon Glass 	INIT_LIST_HEAD(&devices);
7502e192b24SSimon Glass 
7512e192b24SSimon Glass 	return 0;
7522e192b24SSimon Glass }
7532e192b24SSimon Glass 
7542e192b24SSimon Glass /**
7552e192b24SSimon Glass  * If provided device exists it's partitions are deleted, device is removed
7562e192b24SSimon Glass  * from device list and device memory is freed.
7572e192b24SSimon Glass  *
7582e192b24SSimon Glass  * @param dev device to be deleted
7592e192b24SSimon Glass  * @return 0 on success, 1 otherwise
7602e192b24SSimon Glass  */
device_del(struct mtd_device * dev)7612e192b24SSimon Glass static int device_del(struct mtd_device *dev)
7622e192b24SSimon Glass {
7632e192b24SSimon Glass 	part_delall(&dev->parts);
7642e192b24SSimon Glass 	list_del(&dev->link);
7652e192b24SSimon Glass 	free(dev);
7662e192b24SSimon Glass 
7672e192b24SSimon Glass 	if (dev == current_mtd_dev) {
7682e192b24SSimon Glass 		/* we just deleted current device */
7692e192b24SSimon Glass 		if (list_empty(&devices)) {
7702e192b24SSimon Glass 			current_mtd_dev = NULL;
7712e192b24SSimon Glass 		} else {
7722e192b24SSimon Glass 			/* reset first partition from first dev from the
7732e192b24SSimon Glass 			 * devices list as current */
7742e192b24SSimon Glass 			current_mtd_dev = list_entry(devices.next, struct mtd_device, link);
7752e192b24SSimon Glass 			current_mtd_partnum = 0;
7762e192b24SSimon Glass 		}
7772e192b24SSimon Glass 		current_save();
7782e192b24SSimon Glass 		return 0;
7792e192b24SSimon Glass 	}
7802e192b24SSimon Glass 
7812e192b24SSimon Glass 	index_partitions();
7822e192b24SSimon Glass 	return 0;
7832e192b24SSimon Glass }
7842e192b24SSimon Glass 
7852e192b24SSimon Glass /**
7862e192b24SSimon Glass  * Search global device list and return pointer to the device of type and num
7872e192b24SSimon Glass  * specified.
7882e192b24SSimon Glass  *
7892e192b24SSimon Glass  * @param type device type
7902e192b24SSimon Glass  * @param num device number
7912e192b24SSimon Glass  * @return NULL if requested device does not exist
7922e192b24SSimon Glass  */
device_find(u8 type,u8 num)7932e192b24SSimon Glass struct mtd_device *device_find(u8 type, u8 num)
7942e192b24SSimon Glass {
7952e192b24SSimon Glass 	struct list_head *entry;
7962e192b24SSimon Glass 	struct mtd_device *dev_tmp;
7972e192b24SSimon Glass 
7982e192b24SSimon Glass 	list_for_each(entry, &devices) {
7992e192b24SSimon Glass 		dev_tmp = list_entry(entry, struct mtd_device, link);
8002e192b24SSimon Glass 
8012e192b24SSimon Glass 		if ((dev_tmp->id->type == type) && (dev_tmp->id->num == num))
8022e192b24SSimon Glass 			return dev_tmp;
8032e192b24SSimon Glass 	}
8042e192b24SSimon Glass 
8052e192b24SSimon Glass 	return NULL;
8062e192b24SSimon Glass }
8072e192b24SSimon Glass 
8082e192b24SSimon Glass /**
8092e192b24SSimon Glass  * Add specified device to the global device list.
8102e192b24SSimon Glass  *
8112e192b24SSimon Glass  * @param dev device to be added
8122e192b24SSimon Glass  */
device_add(struct mtd_device * dev)8132e192b24SSimon Glass static void device_add(struct mtd_device *dev)
8142e192b24SSimon Glass {
8152e192b24SSimon Glass 	u8 current_save_needed = 0;
8162e192b24SSimon Glass 
8172e192b24SSimon Glass 	if (list_empty(&devices)) {
8182e192b24SSimon Glass 		current_mtd_dev = dev;
8192e192b24SSimon Glass 		current_mtd_partnum = 0;
8202e192b24SSimon Glass 		current_save_needed = 1;
8212e192b24SSimon Glass 	}
8222e192b24SSimon Glass 
8232e192b24SSimon Glass 	list_add_tail(&dev->link, &devices);
8242e192b24SSimon Glass 
8252e192b24SSimon Glass 	if (current_save_needed > 0)
8262e192b24SSimon Glass 		current_save();
8272e192b24SSimon Glass 	else
8282e192b24SSimon Glass 		index_partitions();
8292e192b24SSimon Glass }
8302e192b24SSimon Glass 
8312e192b24SSimon Glass /**
8322e192b24SSimon Glass  * Parse device type, name and mtd-id. If syntax is ok allocate memory and
8332e192b24SSimon Glass  * return pointer to the device structure.
8342e192b24SSimon Glass  *
8352e192b24SSimon Glass  * @param mtd_dev pointer to the device definition string i.e. <mtd-dev>
8362e192b24SSimon Glass  * @param ret output pointer to next char after parse completes (output)
8372e192b24SSimon Glass  * @param retdev pointer to the allocated device (output)
8382e192b24SSimon Glass  * @return 0 on success, 1 otherwise
8392e192b24SSimon Glass  */
device_parse(const char * const mtd_dev,const char ** ret,struct mtd_device ** retdev)8402e192b24SSimon Glass static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_device **retdev)
8412e192b24SSimon Glass {
8422e192b24SSimon Glass 	struct mtd_device *dev;
8432e192b24SSimon Glass 	struct part_info *part;
8442e192b24SSimon Glass 	struct mtdids *id;
8452e192b24SSimon Glass 	const char *mtd_id;
8462e192b24SSimon Glass 	unsigned int mtd_id_len;
8472e192b24SSimon Glass 	const char *p;
8482e192b24SSimon Glass 	const char *pend;
8492e192b24SSimon Glass 	LIST_HEAD(tmp_list);
8502e192b24SSimon Glass 	struct list_head *entry, *n;
8512e192b24SSimon Glass 	u16 num_parts;
8522e192b24SSimon Glass 	u64 offset;
8532e192b24SSimon Glass 	int err = 1;
8542e192b24SSimon Glass 
8552e192b24SSimon Glass 	debug("===device_parse===\n");
8562e192b24SSimon Glass 
8572e192b24SSimon Glass 	assert(retdev);
8582e192b24SSimon Glass 	*retdev = NULL;
8592e192b24SSimon Glass 
8602e192b24SSimon Glass 	if (ret)
8612e192b24SSimon Glass 		*ret = NULL;
8622e192b24SSimon Glass 
8632e192b24SSimon Glass 	/* fetch <mtd-id> */
8642e192b24SSimon Glass 	mtd_id = p = mtd_dev;
8652e192b24SSimon Glass 	if (!(p = strchr(mtd_id, ':'))) {
8662e192b24SSimon Glass 		printf("no <mtd-id> identifier\n");
8672e192b24SSimon Glass 		return 1;
8682e192b24SSimon Glass 	}
8692e192b24SSimon Glass 	mtd_id_len = p - mtd_id + 1;
8702e192b24SSimon Glass 	p++;
8712e192b24SSimon Glass 
8722e192b24SSimon Glass 	/* verify if we have a valid device specified */
8732e192b24SSimon Glass 	if ((id = id_find_by_mtd_id(mtd_id, mtd_id_len - 1)) == NULL) {
8742e192b24SSimon Glass 		printf("invalid mtd device '%.*s'\n", mtd_id_len - 1, mtd_id);
8752e192b24SSimon Glass 		return 1;
8762e192b24SSimon Glass 	}
8772e192b24SSimon Glass 
8782e192b24SSimon Glass 	pend = strchr(p, ';');
8792e192b24SSimon Glass 	debug("dev type = %d (%s), dev num = %d, mtd-id = %s\n",
8802e192b24SSimon Glass 			id->type, MTD_DEV_TYPE(id->type),
8812e192b24SSimon Glass 			id->num, id->mtd_id);
8822e192b24SSimon Glass 	debug("parsing partitions %.*s\n", (int)(pend ? pend - p : strlen(p)), p);
8832e192b24SSimon Glass 
8842e192b24SSimon Glass 	/* parse partitions */
8852e192b24SSimon Glass 	num_parts = 0;
8862e192b24SSimon Glass 
8872e192b24SSimon Glass 	offset = 0;
8882e192b24SSimon Glass 	if ((dev = device_find(id->type, id->num)) != NULL) {
8892e192b24SSimon Glass 		/* if device already exists start at the end of the last partition */
8902e192b24SSimon Glass 		part = list_entry(dev->parts.prev, struct part_info, link);
8912e192b24SSimon Glass 		offset = part->offset + part->size;
8922e192b24SSimon Glass 	}
8932e192b24SSimon Glass 
8942e192b24SSimon Glass 	while (p && (*p != '\0') && (*p != ';')) {
8952e192b24SSimon Glass 		err = 1;
8962e192b24SSimon Glass 		if ((part_parse(p, &p, &part) != 0) || (!part))
8972e192b24SSimon Glass 			break;
8982e192b24SSimon Glass 
8992e192b24SSimon Glass 		/* calculate offset when not specified */
9002e192b24SSimon Glass 		if (part->offset == OFFSET_NOT_SPECIFIED)
9012e192b24SSimon Glass 			part->offset = offset;
9022e192b24SSimon Glass 		else
9032e192b24SSimon Glass 			offset = part->offset;
9042e192b24SSimon Glass 
9052e192b24SSimon Glass 		/* verify alignment and size */
9062e192b24SSimon Glass 		if (part_validate(id, part) != 0)
9072e192b24SSimon Glass 			break;
9082e192b24SSimon Glass 
9092e192b24SSimon Glass 		offset += part->size;
9102e192b24SSimon Glass 
9112e192b24SSimon Glass 		/* partition is ok, add it to the list */
9122e192b24SSimon Glass 		list_add_tail(&part->link, &tmp_list);
9132e192b24SSimon Glass 		num_parts++;
9142e192b24SSimon Glass 		err = 0;
9152e192b24SSimon Glass 	}
9162e192b24SSimon Glass 	if (err == 1) {
9172e192b24SSimon Glass 		part_delall(&tmp_list);
9182e192b24SSimon Glass 		return 1;
9192e192b24SSimon Glass 	}
9202e192b24SSimon Glass 
9212e192b24SSimon Glass 	debug("\ntotal partitions: %d\n", num_parts);
9222e192b24SSimon Glass 
9232e192b24SSimon Glass 	/* check for next device presence */
9242e192b24SSimon Glass 	if (p) {
9252e192b24SSimon Glass 		if (*p == ';') {
9262e192b24SSimon Glass 			if (ret)
9272e192b24SSimon Glass 				*ret = ++p;
9282e192b24SSimon Glass 		} else if (*p == '\0') {
9292e192b24SSimon Glass 			if (ret)
9302e192b24SSimon Glass 				*ret = p;
9312e192b24SSimon Glass 		} else {
9322e192b24SSimon Glass 			printf("unexpected character '%c' at the end of device\n", *p);
9332e192b24SSimon Glass 			if (ret)
9342e192b24SSimon Glass 				*ret = NULL;
9352e192b24SSimon Glass 			return 1;
9362e192b24SSimon Glass 		}
9372e192b24SSimon Glass 	}
9382e192b24SSimon Glass 
9392e192b24SSimon Glass 	/* allocate memory for mtd_device structure */
9402e192b24SSimon Glass 	if ((dev = (struct mtd_device *)malloc(sizeof(struct mtd_device))) == NULL) {
9412e192b24SSimon Glass 		printf("out of memory\n");
9422e192b24SSimon Glass 		return 1;
9432e192b24SSimon Glass 	}
9442e192b24SSimon Glass 	memset(dev, 0, sizeof(struct mtd_device));
9452e192b24SSimon Glass 	dev->id = id;
9462e192b24SSimon Glass 	dev->num_parts = 0; /* part_sort_add increments num_parts */
9472e192b24SSimon Glass 	INIT_LIST_HEAD(&dev->parts);
9482e192b24SSimon Glass 	INIT_LIST_HEAD(&dev->link);
9492e192b24SSimon Glass 
9502e192b24SSimon Glass 	/* move partitions from tmp_list to dev->parts */
9512e192b24SSimon Glass 	list_for_each_safe(entry, n, &tmp_list) {
9522e192b24SSimon Glass 		part = list_entry(entry, struct part_info, link);
9532e192b24SSimon Glass 		list_del(entry);
9542e192b24SSimon Glass 		if (part_sort_add(dev, part) != 0) {
9552e192b24SSimon Glass 			device_del(dev);
9562e192b24SSimon Glass 			return 1;
9572e192b24SSimon Glass 		}
9582e192b24SSimon Glass 	}
9592e192b24SSimon Glass 
9602e192b24SSimon Glass 	*retdev = dev;
9612e192b24SSimon Glass 
9622e192b24SSimon Glass 	debug("===\n\n");
9632e192b24SSimon Glass 	return 0;
9642e192b24SSimon Glass }
9652e192b24SSimon Glass 
9662e192b24SSimon Glass /**
9672e192b24SSimon Glass  * Initialize global device list.
9682e192b24SSimon Glass  *
9692e192b24SSimon Glass  * @return 0 on success, 1 otherwise
9702e192b24SSimon Glass  */
mtd_devices_init(void)9712e192b24SSimon Glass static int mtd_devices_init(void)
9722e192b24SSimon Glass {
9732e192b24SSimon Glass 	last_parts[0] = '\0';
9742e192b24SSimon Glass 	current_mtd_dev = NULL;
9752e192b24SSimon Glass 	current_save();
9762e192b24SSimon Glass 
9772e192b24SSimon Glass 	return device_delall(&devices);
9782e192b24SSimon Glass }
9792e192b24SSimon Glass 
9802e192b24SSimon Glass /*
9812e192b24SSimon Glass  * Search global mtdids list and find id of requested type and number.
9822e192b24SSimon Glass  *
9832e192b24SSimon Glass  * @return pointer to the id if it exists, NULL otherwise
9842e192b24SSimon Glass  */
id_find(u8 type,u8 num)9852e192b24SSimon Glass static struct mtdids* id_find(u8 type, u8 num)
9862e192b24SSimon Glass {
9872e192b24SSimon Glass 	struct list_head *entry;
9882e192b24SSimon Glass 	struct mtdids *id;
9892e192b24SSimon Glass 
9902e192b24SSimon Glass 	list_for_each(entry, &mtdids) {
9912e192b24SSimon Glass 		id = list_entry(entry, struct mtdids, link);
9922e192b24SSimon Glass 
9932e192b24SSimon Glass 		if ((id->type == type) && (id->num == num))
9942e192b24SSimon Glass 			return id;
9952e192b24SSimon Glass 	}
9962e192b24SSimon Glass 
9972e192b24SSimon Glass 	return NULL;
9982e192b24SSimon Glass }
9992e192b24SSimon Glass 
10002e192b24SSimon Glass /**
10012e192b24SSimon Glass  * Search global mtdids list and find id of a requested mtd_id.
10022e192b24SSimon Glass  *
10032e192b24SSimon Glass  * Note: first argument is not null terminated.
10042e192b24SSimon Glass  *
10052e192b24SSimon Glass  * @param mtd_id string containing requested mtd_id
10062e192b24SSimon Glass  * @param mtd_id_len length of supplied mtd_id
10072e192b24SSimon Glass  * @return pointer to the id if it exists, NULL otherwise
10082e192b24SSimon Glass  */
id_find_by_mtd_id(const char * mtd_id,unsigned int mtd_id_len)10092e192b24SSimon Glass static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len)
10102e192b24SSimon Glass {
10112e192b24SSimon Glass 	struct list_head *entry;
10122e192b24SSimon Glass 	struct mtdids *id;
10132e192b24SSimon Glass 
10142e192b24SSimon Glass 	debug("--- id_find_by_mtd_id: '%.*s' (len = %d)\n",
10152e192b24SSimon Glass 			mtd_id_len, mtd_id, mtd_id_len);
10162e192b24SSimon Glass 
10172e192b24SSimon Glass 	list_for_each(entry, &mtdids) {
10182e192b24SSimon Glass 		id = list_entry(entry, struct mtdids, link);
10192e192b24SSimon Glass 
10202e192b24SSimon Glass 		debug("entry: '%s' (len = %zu)\n",
10212e192b24SSimon Glass 				id->mtd_id, strlen(id->mtd_id));
10222e192b24SSimon Glass 
10232e192b24SSimon Glass 		if (mtd_id_len != strlen(id->mtd_id))
10242e192b24SSimon Glass 			continue;
10252e192b24SSimon Glass 		if (strncmp(id->mtd_id, mtd_id, mtd_id_len) == 0)
10262e192b24SSimon Glass 			return id;
10272e192b24SSimon Glass 	}
10282e192b24SSimon Glass 
10292e192b24SSimon Glass 	return NULL;
10302e192b24SSimon Glass }
10312e192b24SSimon Glass 
10322e192b24SSimon Glass /**
103300ac922dSMiquel Raynal  * Parse device id string <dev-id> := 'nand'|'nor'|'onenand'|'spi-nand'<dev-num>,
10342e192b24SSimon Glass  * return device type and number.
10352e192b24SSimon Glass  *
10362e192b24SSimon Glass  * @param id string describing device id
10372e192b24SSimon Glass  * @param ret_id output pointer to next char after parse completes (output)
10382e192b24SSimon Glass  * @param dev_type parsed device type (output)
10392e192b24SSimon Glass  * @param dev_num parsed device number (output)
10402e192b24SSimon Glass  * @return 0 on success, 1 otherwise
10412e192b24SSimon Glass  */
mtd_id_parse(const char * id,const char ** ret_id,u8 * dev_type,u8 * dev_num)10422e192b24SSimon Glass int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type,
10432e192b24SSimon Glass 		 u8 *dev_num)
10442e192b24SSimon Glass {
10452e192b24SSimon Glass 	const char *p = id;
10462e192b24SSimon Glass 
10472e192b24SSimon Glass 	*dev_type = 0;
10482e192b24SSimon Glass 	if (strncmp(p, "nand", 4) == 0) {
10492e192b24SSimon Glass 		*dev_type = MTD_DEV_TYPE_NAND;
10502e192b24SSimon Glass 		p += 4;
10512e192b24SSimon Glass 	} else if (strncmp(p, "nor", 3) == 0) {
10522e192b24SSimon Glass 		*dev_type = MTD_DEV_TYPE_NOR;
10532e192b24SSimon Glass 		p += 3;
10542e192b24SSimon Glass 	} else if (strncmp(p, "onenand", 7) == 0) {
10552e192b24SSimon Glass 		*dev_type = MTD_DEV_TYPE_ONENAND;
10562e192b24SSimon Glass 		p += 7;
105700ac922dSMiquel Raynal 	} else if (strncmp(p, "spi-nand", 8) == 0) {
105800ac922dSMiquel Raynal 		*dev_type = MTD_DEV_TYPE_SPINAND;
105900ac922dSMiquel Raynal 		p += 8;
10602e192b24SSimon Glass 	} else {
10612e192b24SSimon Glass 		printf("incorrect device type in %s\n", id);
10622e192b24SSimon Glass 		return 1;
10632e192b24SSimon Glass 	}
10642e192b24SSimon Glass 
10652e192b24SSimon Glass 	if (!isdigit(*p)) {
10662e192b24SSimon Glass 		printf("incorrect device number in %s\n", id);
10672e192b24SSimon Glass 		return 1;
10682e192b24SSimon Glass 	}
10692e192b24SSimon Glass 
10702e192b24SSimon Glass 	*dev_num = simple_strtoul(p, (char **)&p, 0);
10712e192b24SSimon Glass 	if (ret_id)
10722e192b24SSimon Glass 		*ret_id = p;
10732e192b24SSimon Glass 	return 0;
10742e192b24SSimon Glass }
10752e192b24SSimon Glass 
10762e192b24SSimon Glass /**
10772e192b24SSimon Glass  * Process all devices and generate corresponding mtdparts string describing
10782e192b24SSimon Glass  * all partitions on all devices.
10792e192b24SSimon Glass  *
10802e192b24SSimon Glass  * @param buf output buffer holding generated mtdparts string (output)
10812e192b24SSimon Glass  * @param buflen buffer size
10822e192b24SSimon Glass  * @return 0 on success, 1 otherwise
10832e192b24SSimon Glass  */
generate_mtdparts(char * buf,u32 buflen)10842e192b24SSimon Glass static int generate_mtdparts(char *buf, u32 buflen)
10852e192b24SSimon Glass {
10862e192b24SSimon Glass 	struct list_head *pentry, *dentry;
10872e192b24SSimon Glass 	struct mtd_device *dev;
10882e192b24SSimon Glass 	struct part_info *part, *prev_part;
10892e192b24SSimon Glass 	char *p = buf;
10902e192b24SSimon Glass 	char tmpbuf[32];
10912e192b24SSimon Glass 	u64 size, offset;
10922e192b24SSimon Glass 	u32 len, part_cnt;
10932e192b24SSimon Glass 	u32 maxlen = buflen - 1;
10942e192b24SSimon Glass 
10952e192b24SSimon Glass 	debug("--- generate_mtdparts ---\n");
10962e192b24SSimon Glass 
10972e192b24SSimon Glass 	if (list_empty(&devices)) {
10982e192b24SSimon Glass 		buf[0] = '\0';
10992e192b24SSimon Glass 		return 0;
11002e192b24SSimon Glass 	}
11012e192b24SSimon Glass 
11022e192b24SSimon Glass 	list_for_each(dentry, &devices) {
11032e192b24SSimon Glass 		dev = list_entry(dentry, struct mtd_device, link);
11042e192b24SSimon Glass 
11052e192b24SSimon Glass 		/* copy mtd_id */
11062e192b24SSimon Glass 		len = strlen(dev->id->mtd_id) + 1;
11072e192b24SSimon Glass 		if (len > maxlen)
11082e192b24SSimon Glass 			goto cleanup;
11092e192b24SSimon Glass 		memcpy(p, dev->id->mtd_id, len - 1);
11102e192b24SSimon Glass 		p += len - 1;
11112e192b24SSimon Glass 		*(p++) = ':';
11122e192b24SSimon Glass 		maxlen -= len;
11132e192b24SSimon Glass 
11142e192b24SSimon Glass 		/* format partitions */
11152e192b24SSimon Glass 		prev_part = NULL;
11162e192b24SSimon Glass 		part_cnt = 0;
11172e192b24SSimon Glass 		list_for_each(pentry, &dev->parts) {
11182e192b24SSimon Glass 			part = list_entry(pentry, struct part_info, link);
11192e192b24SSimon Glass 			size = part->size;
11202e192b24SSimon Glass 			offset = part->offset;
11212e192b24SSimon Glass 			part_cnt++;
11222e192b24SSimon Glass 
11232e192b24SSimon Glass 			/* partition size */
11242e192b24SSimon Glass 			memsize_format(tmpbuf, size);
11252e192b24SSimon Glass 			len = strlen(tmpbuf);
11262e192b24SSimon Glass 			if (len > maxlen)
11272e192b24SSimon Glass 				goto cleanup;
11282e192b24SSimon Glass 			memcpy(p, tmpbuf, len);
11292e192b24SSimon Glass 			p += len;
11302e192b24SSimon Glass 			maxlen -= len;
11312e192b24SSimon Glass 
11322e192b24SSimon Glass 
11332e192b24SSimon Glass 			/* add offset only when there is a gap between
11342e192b24SSimon Glass 			 * partitions */
11352e192b24SSimon Glass 			if ((!prev_part && (offset != 0)) ||
11362e192b24SSimon Glass 					(prev_part && ((prev_part->offset + prev_part->size) != part->offset))) {
11372e192b24SSimon Glass 
11382e192b24SSimon Glass 				memsize_format(tmpbuf, offset);
11392e192b24SSimon Glass 				len = strlen(tmpbuf) + 1;
11402e192b24SSimon Glass 				if (len > maxlen)
11412e192b24SSimon Glass 					goto cleanup;
11422e192b24SSimon Glass 				*(p++) = '@';
11432e192b24SSimon Glass 				memcpy(p, tmpbuf, len - 1);
11442e192b24SSimon Glass 				p += len - 1;
11452e192b24SSimon Glass 				maxlen -= len;
11462e192b24SSimon Glass 			}
11472e192b24SSimon Glass 
11482e192b24SSimon Glass 			/* copy name only if user supplied */
11492e192b24SSimon Glass 			if(!part->auto_name) {
11502e192b24SSimon Glass 				len = strlen(part->name) + 2;
11512e192b24SSimon Glass 				if (len > maxlen)
11522e192b24SSimon Glass 					goto cleanup;
11532e192b24SSimon Glass 
11542e192b24SSimon Glass 				*(p++) = '(';
11552e192b24SSimon Glass 				memcpy(p, part->name, len - 2);
11562e192b24SSimon Glass 				p += len - 2;
11572e192b24SSimon Glass 				*(p++) = ')';
11582e192b24SSimon Glass 				maxlen -= len;
11592e192b24SSimon Glass 			}
11602e192b24SSimon Glass 
11612e192b24SSimon Glass 			/* ro mask flag */
11622e192b24SSimon Glass 			if (part->mask_flags && MTD_WRITEABLE_CMD) {
11632e192b24SSimon Glass 				len = 2;
11642e192b24SSimon Glass 				if (len > maxlen)
11652e192b24SSimon Glass 					goto cleanup;
11662e192b24SSimon Glass 				*(p++) = 'r';
11672e192b24SSimon Glass 				*(p++) = 'o';
11682e192b24SSimon Glass 				maxlen -= 2;
11692e192b24SSimon Glass 			}
11702e192b24SSimon Glass 
11712e192b24SSimon Glass 			/* print ',' separator if there are other partitions
11722e192b24SSimon Glass 			 * following */
11732e192b24SSimon Glass 			if (dev->num_parts > part_cnt) {
11742e192b24SSimon Glass 				if (1 > maxlen)
11752e192b24SSimon Glass 					goto cleanup;
11762e192b24SSimon Glass 				*(p++) = ',';
11772e192b24SSimon Glass 				maxlen--;
11782e192b24SSimon Glass 			}
11792e192b24SSimon Glass 			prev_part = part;
11802e192b24SSimon Glass 		}
11812e192b24SSimon Glass 		/* print ';' separator if there are other devices following */
11822e192b24SSimon Glass 		if (dentry->next != &devices) {
11832e192b24SSimon Glass 			if (1 > maxlen)
11842e192b24SSimon Glass 				goto cleanup;
11852e192b24SSimon Glass 			*(p++) = ';';
11862e192b24SSimon Glass 			maxlen--;
11872e192b24SSimon Glass 		}
11882e192b24SSimon Glass 	}
11892e192b24SSimon Glass 
11902e192b24SSimon Glass 	/* we still have at least one char left, as we decremented maxlen at
11912e192b24SSimon Glass 	 * the begining */
11922e192b24SSimon Glass 	*p = '\0';
11932e192b24SSimon Glass 
11942e192b24SSimon Glass 	return 0;
11952e192b24SSimon Glass 
11962e192b24SSimon Glass cleanup:
11972e192b24SSimon Glass 	last_parts[0] = '\0';
11982e192b24SSimon Glass 	return 1;
11992e192b24SSimon Glass }
12002e192b24SSimon Glass 
12012e192b24SSimon Glass /**
12022e192b24SSimon Glass  * Call generate_mtdparts to process all devices and generate corresponding
12032e192b24SSimon Glass  * mtdparts string, save it in mtdparts environment variable.
12042e192b24SSimon Glass  *
12052e192b24SSimon Glass  * @param buf output buffer holding generated mtdparts string (output)
12062e192b24SSimon Glass  * @param buflen buffer size
12072e192b24SSimon Glass  * @return 0 on success, 1 otherwise
12082e192b24SSimon Glass  */
generate_mtdparts_save(char * buf,u32 buflen)12092e192b24SSimon Glass static int generate_mtdparts_save(char *buf, u32 buflen)
12102e192b24SSimon Glass {
12112e192b24SSimon Glass 	int ret;
12122e192b24SSimon Glass 
12132e192b24SSimon Glass 	ret = generate_mtdparts(buf, buflen);
12142e192b24SSimon Glass 
12152e192b24SSimon Glass 	if ((buf[0] != '\0') && (ret == 0))
1216382bee57SSimon Glass 		env_set("mtdparts", buf);
12172e192b24SSimon Glass 	else
1218382bee57SSimon Glass 		env_set("mtdparts", NULL);
12192e192b24SSimon Glass 
12202e192b24SSimon Glass 	return ret;
12212e192b24SSimon Glass }
12222e192b24SSimon Glass 
12232e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
12242e192b24SSimon Glass /**
12252e192b24SSimon Glass  * Get the net size (w/o bad blocks) of the given partition.
12262e192b24SSimon Glass  *
12272e192b24SSimon Glass  * @param mtd the mtd info
12282e192b24SSimon Glass  * @param part the partition
12292e192b24SSimon Glass  * @return the calculated net size of this partition
12302e192b24SSimon Glass  */
net_part_size(struct mtd_info * mtd,struct part_info * part)12312e192b24SSimon Glass static uint64_t net_part_size(struct mtd_info *mtd, struct part_info *part)
12322e192b24SSimon Glass {
12332e192b24SSimon Glass 	uint64_t i, net_size = 0;
12342e192b24SSimon Glass 
12352e192b24SSimon Glass 	if (!mtd->block_isbad)
12362e192b24SSimon Glass 		return part->size;
12372e192b24SSimon Glass 
12382e192b24SSimon Glass 	for (i = 0; i < part->size; i += mtd->erasesize) {
12392e192b24SSimon Glass 		if (!mtd->block_isbad(mtd, part->offset + i))
12402e192b24SSimon Glass 			net_size += mtd->erasesize;
12412e192b24SSimon Glass 	}
12422e192b24SSimon Glass 
12432e192b24SSimon Glass 	return net_size;
12442e192b24SSimon Glass }
12452e192b24SSimon Glass #endif
12462e192b24SSimon Glass 
print_partition_table(void)12472e192b24SSimon Glass static void print_partition_table(void)
12482e192b24SSimon Glass {
12492e192b24SSimon Glass 	struct list_head *dentry, *pentry;
12502e192b24SSimon Glass 	struct part_info *part;
12512e192b24SSimon Glass 	struct mtd_device *dev;
12522e192b24SSimon Glass 	int part_num;
12532e192b24SSimon Glass 
12542e192b24SSimon Glass 	list_for_each(dentry, &devices) {
12552e192b24SSimon Glass 		dev = list_entry(dentry, struct mtd_device, link);
12562e192b24SSimon Glass 		/* list partitions for given device */
12572e192b24SSimon Glass 		part_num = 0;
12582e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
12592e192b24SSimon Glass 		struct mtd_info *mtd;
12602e192b24SSimon Glass 
12612e192b24SSimon Glass 		if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
12622e192b24SSimon Glass 			return;
12632e192b24SSimon Glass 
12642e192b24SSimon Glass 		printf("\ndevice %s%d <%s>, # parts = %d\n",
12652e192b24SSimon Glass 				MTD_DEV_TYPE(dev->id->type), dev->id->num,
12662e192b24SSimon Glass 				dev->id->mtd_id, dev->num_parts);
12672e192b24SSimon Glass 		printf(" #: name\t\tsize\t\tnet size\toffset\t\tmask_flags\n");
12682e192b24SSimon Glass 
12692e192b24SSimon Glass 		list_for_each(pentry, &dev->parts) {
12702e192b24SSimon Glass 			u32 net_size;
12712e192b24SSimon Glass 			char *size_note;
12722e192b24SSimon Glass 
12732e192b24SSimon Glass 			part = list_entry(pentry, struct part_info, link);
12742e192b24SSimon Glass 			net_size = net_part_size(mtd, part);
12752e192b24SSimon Glass 			size_note = part->size == net_size ? " " : " (!)";
12762e192b24SSimon Glass 			printf("%2d: %-20s0x%08x\t0x%08x%s\t0x%08x\t%d\n",
12772e192b24SSimon Glass 					part_num, part->name, part->size,
12782e192b24SSimon Glass 					net_size, size_note, part->offset,
12792e192b24SSimon Glass 					part->mask_flags);
12802e192b24SSimon Glass #else /* !defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
12812e192b24SSimon Glass 		printf("\ndevice %s%d <%s>, # parts = %d\n",
12822e192b24SSimon Glass 				MTD_DEV_TYPE(dev->id->type), dev->id->num,
12832e192b24SSimon Glass 				dev->id->mtd_id, dev->num_parts);
12842e192b24SSimon Glass 		printf(" #: name\t\tsize\t\toffset\t\tmask_flags\n");
12852e192b24SSimon Glass 
12862e192b24SSimon Glass 		list_for_each(pentry, &dev->parts) {
12872e192b24SSimon Glass 			part = list_entry(pentry, struct part_info, link);
12882e192b24SSimon Glass 			printf("%2d: %-20s0x%08llx\t0x%08llx\t%d\n",
12892e192b24SSimon Glass 					part_num, part->name, part->size,
12902e192b24SSimon Glass 					part->offset, part->mask_flags);
12912e192b24SSimon Glass #endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
12922e192b24SSimon Glass 			part_num++;
12932e192b24SSimon Glass 		}
12942e192b24SSimon Glass 	}
12952e192b24SSimon Glass 
12962e192b24SSimon Glass 	if (list_empty(&devices))
12972e192b24SSimon Glass 		printf("no partitions defined\n");
12982e192b24SSimon Glass }
12992e192b24SSimon Glass 
13002e192b24SSimon Glass /**
13012e192b24SSimon Glass  * Format and print out a partition list for each device from global device
13022e192b24SSimon Glass  * list.
13032e192b24SSimon Glass  */
13042e192b24SSimon Glass static void list_partitions(void)
13052e192b24SSimon Glass {
13062e192b24SSimon Glass 	struct part_info *part;
13072e192b24SSimon Glass 
13082e192b24SSimon Glass 	debug("\n---list_partitions---\n");
13092e192b24SSimon Glass 	print_partition_table();
13102e192b24SSimon Glass 
13112e192b24SSimon Glass 	/* current_mtd_dev is not NULL only when we have non empty device list */
13122e192b24SSimon Glass 	if (current_mtd_dev) {
13132e192b24SSimon Glass 		part = mtd_part_info(current_mtd_dev, current_mtd_partnum);
13142e192b24SSimon Glass 		if (part) {
13152e192b24SSimon Glass 			printf("\nactive partition: %s%d,%d - (%s) 0x%08llx @ 0x%08llx\n",
13162e192b24SSimon Glass 					MTD_DEV_TYPE(current_mtd_dev->id->type),
13172e192b24SSimon Glass 					current_mtd_dev->id->num, current_mtd_partnum,
13182e192b24SSimon Glass 					part->name, part->size, part->offset);
13192e192b24SSimon Glass 		} else {
13202e192b24SSimon Glass 			printf("could not get current partition info\n\n");
13212e192b24SSimon Glass 		}
13222e192b24SSimon Glass 	}
13232e192b24SSimon Glass 
13242e192b24SSimon Glass 	printf("\ndefaults:\n");
13252e192b24SSimon Glass 	printf("mtdids  : %s\n",
13262e192b24SSimon Glass 		mtdids_default ? mtdids_default : "none");
13272e192b24SSimon Glass 	/*
13282e192b24SSimon Glass 	 * Using printf() here results in printbuffer overflow
13292e192b24SSimon Glass 	 * if default mtdparts string is greater than console
13302e192b24SSimon Glass 	 * printbuffer. Use puts() to prevent system crashes.
13312e192b24SSimon Glass 	 */
13322e192b24SSimon Glass 	puts("mtdparts: ");
13332e192b24SSimon Glass 	puts(mtdparts_default ? mtdparts_default : "none");
13342e192b24SSimon Glass 	puts("\n");
13352e192b24SSimon Glass }
13362e192b24SSimon Glass 
13372e192b24SSimon Glass /**
13382e192b24SSimon Glass  * Given partition identifier in form of <dev_type><dev_num>,<part_num> find
13392e192b24SSimon Glass  * corresponding device and verify partition number.
13402e192b24SSimon Glass  *
13412e192b24SSimon Glass  * @param id string describing device and partition or partition name
13422e192b24SSimon Glass  * @param dev pointer to the requested device (output)
13432e192b24SSimon Glass  * @param part_num verified partition number (output)
13442e192b24SSimon Glass  * @param part pointer to requested partition (output)
13452e192b24SSimon Glass  * @return 0 on success, 1 otherwise
13462e192b24SSimon Glass  */
13472e192b24SSimon Glass int find_dev_and_part(const char *id, struct mtd_device **dev,
13482e192b24SSimon Glass 		u8 *part_num, struct part_info **part)
13492e192b24SSimon Glass {
13502e192b24SSimon Glass 	struct list_head *dentry, *pentry;
13512e192b24SSimon Glass 	u8 type, dnum, pnum;
13522e192b24SSimon Glass 	const char *p;
13532e192b24SSimon Glass 
13542e192b24SSimon Glass 	debug("--- find_dev_and_part ---\nid = %s\n", id);
13552e192b24SSimon Glass 
13562e192b24SSimon Glass 	list_for_each(dentry, &devices) {
13572e192b24SSimon Glass 		*part_num = 0;
13582e192b24SSimon Glass 		*dev = list_entry(dentry, struct mtd_device, link);
13592e192b24SSimon Glass 		list_for_each(pentry, &(*dev)->parts) {
13602e192b24SSimon Glass 			*part = list_entry(pentry, struct part_info, link);
13612e192b24SSimon Glass 			if (strcmp((*part)->name, id) == 0)
13622e192b24SSimon Glass 				return 0;
13632e192b24SSimon Glass 			(*part_num)++;
13642e192b24SSimon Glass 		}
13652e192b24SSimon Glass 	}
13662e192b24SSimon Glass 
13672e192b24SSimon Glass 	p = id;
13682e192b24SSimon Glass 	*dev = NULL;
13692e192b24SSimon Glass 	*part = NULL;
13702e192b24SSimon Glass 	*part_num = 0;
13712e192b24SSimon Glass 
13722e192b24SSimon Glass 	if (mtd_id_parse(p, &p, &type, &dnum) != 0)
13732e192b24SSimon Glass 		return 1;
13742e192b24SSimon Glass 
13752e192b24SSimon Glass 	if ((*p++ != ',') || (*p == '\0')) {
13762e192b24SSimon Glass 		printf("no partition number specified\n");
13772e192b24SSimon Glass 		return 1;
13782e192b24SSimon Glass 	}
13792e192b24SSimon Glass 	pnum = simple_strtoul(p, (char **)&p, 0);
13802e192b24SSimon Glass 	if (*p != '\0') {
13812e192b24SSimon Glass 		printf("unexpected trailing character '%c'\n", *p);
13822e192b24SSimon Glass 		return 1;
13832e192b24SSimon Glass 	}
13842e192b24SSimon Glass 
13852e192b24SSimon Glass 	if ((*dev = device_find(type, dnum)) == NULL) {
13862e192b24SSimon Glass 		printf("no such device %s%d\n", MTD_DEV_TYPE(type), dnum);
13872e192b24SSimon Glass 		return 1;
13882e192b24SSimon Glass 	}
13892e192b24SSimon Glass 
13902e192b24SSimon Glass 	if ((*part = mtd_part_info(*dev, pnum)) == NULL) {
13912e192b24SSimon Glass 		printf("no such partition\n");
13922e192b24SSimon Glass 		*dev = NULL;
13932e192b24SSimon Glass 		return 1;
13942e192b24SSimon Glass 	}
13952e192b24SSimon Glass 
13962e192b24SSimon Glass 	*part_num = pnum;
13972e192b24SSimon Glass 
13982e192b24SSimon Glass 	return 0;
13992e192b24SSimon Glass }
14002e192b24SSimon Glass 
14012e192b24SSimon Glass /**
14022e192b24SSimon Glass  * Find and delete partition. For partition id format see find_dev_and_part().
14032e192b24SSimon Glass  *
14042e192b24SSimon Glass  * @param id string describing device and partition
14052e192b24SSimon Glass  * @return 0 on success, 1 otherwise
14062e192b24SSimon Glass  */
14072e192b24SSimon Glass static int delete_partition(const char *id)
14082e192b24SSimon Glass {
14092e192b24SSimon Glass 	u8 pnum;
14102e192b24SSimon Glass 	struct mtd_device *dev;
14112e192b24SSimon Glass 	struct part_info *part;
14122e192b24SSimon Glass 
14132e192b24SSimon Glass 	if (find_dev_and_part(id, &dev, &pnum, &part) == 0) {
14142e192b24SSimon Glass 
14152e192b24SSimon Glass 		debug("delete_partition: device = %s%d, partition %d = (%s) 0x%08llx@0x%08llx\n",
14162e192b24SSimon Glass 				MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum,
14172e192b24SSimon Glass 				part->name, part->size, part->offset);
14182e192b24SSimon Glass 
14192e192b24SSimon Glass 		if (part_del(dev, part) != 0)
14202e192b24SSimon Glass 			return 1;
14212e192b24SSimon Glass 
14222e192b24SSimon Glass 		if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
14232e192b24SSimon Glass 			printf("generated mtdparts too long, resetting to null\n");
14242e192b24SSimon Glass 			return 1;
14252e192b24SSimon Glass 		}
14262e192b24SSimon Glass 		return 0;
14272e192b24SSimon Glass 	}
14282e192b24SSimon Glass 
14292e192b24SSimon Glass 	printf("partition %s not found\n", id);
14302e192b24SSimon Glass 	return 1;
14312e192b24SSimon Glass }
14322e192b24SSimon Glass 
14332e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS_SPREAD)
14342e192b24SSimon Glass /**
14352e192b24SSimon Glass  * Increase the size of the given partition so that it's net size is at least
14362e192b24SSimon Glass  * as large as the size member and such that the next partition would start on a
14372e192b24SSimon Glass  * good block if it were adjacent to this partition.
14382e192b24SSimon Glass  *
14392e192b24SSimon Glass  * @param mtd the mtd device
14402e192b24SSimon Glass  * @param part the partition
14412e192b24SSimon Glass  * @param next_offset pointer to the offset of the next partition after this
14422e192b24SSimon Glass  *                    partition's size has been modified (output)
14432e192b24SSimon Glass  */
14442e192b24SSimon Glass static void spread_partition(struct mtd_info *mtd, struct part_info *part,
14452e192b24SSimon Glass 			     uint64_t *next_offset)
14462e192b24SSimon Glass {
14472e192b24SSimon Glass 	uint64_t net_size, padding_size = 0;
14482e192b24SSimon Glass 	int truncated;
14492e192b24SSimon Glass 
14502e192b24SSimon Glass 	mtd_get_len_incl_bad(mtd, part->offset, part->size, &net_size,
14512e192b24SSimon Glass 			     &truncated);
14522e192b24SSimon Glass 
14532e192b24SSimon Glass 	/*
14542e192b24SSimon Glass 	 * Absorb bad blocks immediately following this
14552e192b24SSimon Glass 	 * partition also into the partition, such that
14562e192b24SSimon Glass 	 * the next partition starts with a good block.
14572e192b24SSimon Glass 	 */
14582e192b24SSimon Glass 	if (!truncated) {
14592e192b24SSimon Glass 		mtd_get_len_incl_bad(mtd, part->offset + net_size,
14602e192b24SSimon Glass 				     mtd->erasesize, &padding_size, &truncated);
14612e192b24SSimon Glass 		if (truncated)
14622e192b24SSimon Glass 			padding_size = 0;
14632e192b24SSimon Glass 		else
14642e192b24SSimon Glass 			padding_size -= mtd->erasesize;
14652e192b24SSimon Glass 	}
14662e192b24SSimon Glass 
14672e192b24SSimon Glass 	if (truncated) {
14682e192b24SSimon Glass 		printf("truncated partition %s to %lld bytes\n", part->name,
14692e192b24SSimon Glass 		       (uint64_t) net_size + padding_size);
14702e192b24SSimon Glass 	}
14712e192b24SSimon Glass 
14722e192b24SSimon Glass 	part->size = net_size + padding_size;
14732e192b24SSimon Glass 	*next_offset = part->offset + part->size;
14742e192b24SSimon Glass }
14752e192b24SSimon Glass 
14762e192b24SSimon Glass /**
14772e192b24SSimon Glass  * Adjust all of the partition sizes, such that all partitions are at least
14782e192b24SSimon Glass  * as big as their mtdparts environment variable sizes and they each start
14792e192b24SSimon Glass  * on a good block.
14802e192b24SSimon Glass  *
14812e192b24SSimon Glass  * @return 0 on success, 1 otherwise
14822e192b24SSimon Glass  */
14832e192b24SSimon Glass static int spread_partitions(void)
14842e192b24SSimon Glass {
14852e192b24SSimon Glass 	struct list_head *dentry, *pentry;
14862e192b24SSimon Glass 	struct mtd_device *dev;
14872e192b24SSimon Glass 	struct part_info *part;
14882e192b24SSimon Glass 	struct mtd_info *mtd;
14892e192b24SSimon Glass 	int part_num;
14902e192b24SSimon Glass 	uint64_t cur_offs;
14912e192b24SSimon Glass 
14922e192b24SSimon Glass 	list_for_each(dentry, &devices) {
14932e192b24SSimon Glass 		dev = list_entry(dentry, struct mtd_device, link);
14942e192b24SSimon Glass 
14952e192b24SSimon Glass 		if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
14962e192b24SSimon Glass 			return 1;
14972e192b24SSimon Glass 
14982e192b24SSimon Glass 		part_num = 0;
14992e192b24SSimon Glass 		cur_offs = 0;
15002e192b24SSimon Glass 		list_for_each(pentry, &dev->parts) {
15012e192b24SSimon Glass 			part = list_entry(pentry, struct part_info, link);
15022e192b24SSimon Glass 
15032e192b24SSimon Glass 			debug("spread_partitions: device = %s%d, partition %d ="
150459441ac3SSteve Rae 				" (%s) 0x%08llx@0x%08llx\n",
15052e192b24SSimon Glass 				MTD_DEV_TYPE(dev->id->type), dev->id->num,
15062e192b24SSimon Glass 				part_num, part->name, part->size,
15072e192b24SSimon Glass 				part->offset);
15082e192b24SSimon Glass 
15092e192b24SSimon Glass 			if (cur_offs > part->offset)
15102e192b24SSimon Glass 				part->offset = cur_offs;
15112e192b24SSimon Glass 
15122e192b24SSimon Glass 			spread_partition(mtd, part, &cur_offs);
15132e192b24SSimon Glass 
15142e192b24SSimon Glass 			part_num++;
15152e192b24SSimon Glass 		}
15162e192b24SSimon Glass 	}
15172e192b24SSimon Glass 
15182e192b24SSimon Glass 	index_partitions();
15192e192b24SSimon Glass 
15202e192b24SSimon Glass 	if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
15212e192b24SSimon Glass 		printf("generated mtdparts too long, resetting to null\n");
15222e192b24SSimon Glass 		return 1;
15232e192b24SSimon Glass 	}
15242e192b24SSimon Glass 	return 0;
15252e192b24SSimon Glass }
15262e192b24SSimon Glass #endif /* CONFIG_CMD_MTDPARTS_SPREAD */
15272e192b24SSimon Glass 
15282e192b24SSimon Glass /**
15291c2a262aSLadislav Michl  * The mtdparts variable tends to be long. If we need to access it
15301c2a262aSLadislav Michl  * before the env is relocated, then we need to use our own stack
15311c2a262aSLadislav Michl  * buffer.  gd->env_buf will be too small.
15321c2a262aSLadislav Michl  *
15331c2a262aSLadislav Michl  * @param buf temporary buffer pointer MTDPARTS_MAXLEN long
15341c2a262aSLadislav Michl  * @return mtdparts variable string, NULL if not found
15351c2a262aSLadislav Michl  */
1536723806ccSSimon Glass static const char *env_get_mtdparts(char *buf)
15371c2a262aSLadislav Michl {
15381c2a262aSLadislav Michl 	if (gd->flags & GD_FLG_ENV_READY)
153900caae6dSSimon Glass 		return env_get("mtdparts");
154000caae6dSSimon Glass 	if (env_get_f("mtdparts", buf, MTDPARTS_MAXLEN) != -1)
15411c2a262aSLadislav Michl 		return buf;
15421c2a262aSLadislav Michl 	return NULL;
15431c2a262aSLadislav Michl }
15441c2a262aSLadislav Michl 
15451c2a262aSLadislav Michl /**
15462e192b24SSimon Glass  * Accept character string describing mtd partitions and call device_parse()
15472e192b24SSimon Glass  * for each entry. Add created devices to the global devices list.
15482e192b24SSimon Glass  *
15492e192b24SSimon Glass  * @param mtdparts string specifing mtd partitions
15502e192b24SSimon Glass  * @return 0 on success, 1 otherwise
15512e192b24SSimon Glass  */
15522e192b24SSimon Glass static int parse_mtdparts(const char *const mtdparts)
15532e192b24SSimon Glass {
155406a040a3SLadislav Michl 	const char *p;
15552e192b24SSimon Glass 	struct mtd_device *dev;
15562e192b24SSimon Glass 	int err = 1;
15572e192b24SSimon Glass 	char tmp_parts[MTDPARTS_MAXLEN];
15582e192b24SSimon Glass 
15591aca4d5aSLothar Waßmann 	debug("\n---parse_mtdparts---\nmtdparts = %s\n\n", mtdparts);
15602e192b24SSimon Glass 
15612e192b24SSimon Glass 	/* delete all devices and partitions */
15622e192b24SSimon Glass 	if (mtd_devices_init() != 0) {
15632e192b24SSimon Glass 		printf("could not initialise device list\n");
15642e192b24SSimon Glass 		return err;
15652e192b24SSimon Glass 	}
15662e192b24SSimon Glass 
15672e192b24SSimon Glass 	/* re-read 'mtdparts' variable, mtd_devices_init may be updating env */
1568723806ccSSimon Glass 	p = env_get_mtdparts(tmp_parts);
156906a040a3SLadislav Michl 	if (!p)
157006a040a3SLadislav Michl 		p = mtdparts;
157106a040a3SLadislav Michl 
1572*d60aea94SMiquel Raynal 	/* Skip the useless prefix, if any */
1573*d60aea94SMiquel Raynal 	if (strncmp(p, "mtdparts=", 9) == 0)
15742e192b24SSimon Glass 		p += 9;
15752e192b24SSimon Glass 
157606a040a3SLadislav Michl 	while (*p != '\0') {
15772e192b24SSimon Glass 		err = 1;
15782e192b24SSimon Glass 		if ((device_parse(p, &p, &dev) != 0) || (!dev))
15792e192b24SSimon Glass 			break;
15802e192b24SSimon Glass 
15812e192b24SSimon Glass 		debug("+ device: %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
15822e192b24SSimon Glass 				dev->id->num, dev->id->mtd_id);
15832e192b24SSimon Glass 
15842e192b24SSimon Glass 		/* check if parsed device is already on the list */
15852e192b24SSimon Glass 		if (device_find(dev->id->type, dev->id->num) != NULL) {
15862e192b24SSimon Glass 			printf("device %s%d redefined, please correct mtdparts variable\n",
15872e192b24SSimon Glass 					MTD_DEV_TYPE(dev->id->type), dev->id->num);
15882e192b24SSimon Glass 			break;
15892e192b24SSimon Glass 		}
15902e192b24SSimon Glass 
15912e192b24SSimon Glass 		list_add_tail(&dev->link, &devices);
15922e192b24SSimon Glass 		err = 0;
15932e192b24SSimon Glass 	}
159454f1792cSTom Rini 	if (err == 1) {
159554f1792cSTom Rini 		free(dev);
15962e192b24SSimon Glass 		device_delall(&devices);
159754f1792cSTom Rini 	}
15982e192b24SSimon Glass 
159906a040a3SLadislav Michl 	return err;
16002e192b24SSimon Glass }
16012e192b24SSimon Glass 
16022e192b24SSimon Glass /**
16032e192b24SSimon Glass  * Parse provided string describing mtdids mapping (see file header for mtdids
16042e192b24SSimon Glass  * variable format). Allocate memory for each entry and add all found entries
16052e192b24SSimon Glass  * to the global mtdids list.
16062e192b24SSimon Glass  *
16072e192b24SSimon Glass  * @param ids mapping string
16082e192b24SSimon Glass  * @return 0 on success, 1 otherwise
16092e192b24SSimon Glass  */
16102e192b24SSimon Glass static int parse_mtdids(const char *const ids)
16112e192b24SSimon Glass {
16122e192b24SSimon Glass 	const char *p = ids;
16132e192b24SSimon Glass 	const char *mtd_id;
16142e192b24SSimon Glass 	int mtd_id_len;
16152e192b24SSimon Glass 	struct mtdids *id;
16162e192b24SSimon Glass 	struct list_head *entry, *n;
16172e192b24SSimon Glass 	struct mtdids *id_tmp;
16182e192b24SSimon Glass 	u8 type, num;
16192e192b24SSimon Glass 	u64 size;
16202e192b24SSimon Glass 	int ret = 1;
16212e192b24SSimon Glass 
16222e192b24SSimon Glass 	debug("\n---parse_mtdids---\nmtdids = %s\n\n", ids);
16232e192b24SSimon Glass 
16242e192b24SSimon Glass 	/* clean global mtdids list */
16252e192b24SSimon Glass 	list_for_each_safe(entry, n, &mtdids) {
16262e192b24SSimon Glass 		id_tmp = list_entry(entry, struct mtdids, link);
16272e192b24SSimon Glass 		debug("mtdids del: %d %d\n", id_tmp->type, id_tmp->num);
16282e192b24SSimon Glass 		list_del(entry);
16292e192b24SSimon Glass 		free(id_tmp);
16302e192b24SSimon Glass 	}
16312e192b24SSimon Glass 	last_ids[0] = '\0';
16322e192b24SSimon Glass 	INIT_LIST_HEAD(&mtdids);
16332e192b24SSimon Glass 
16342e192b24SSimon Glass 	while(p && (*p != '\0')) {
16352e192b24SSimon Glass 
16362e192b24SSimon Glass 		ret = 1;
163700ac922dSMiquel Raynal 		/* parse 'nor'|'nand'|'onenand'|'spi-nand'<dev-num> */
16382e192b24SSimon Glass 		if (mtd_id_parse(p, &p, &type, &num) != 0)
16392e192b24SSimon Glass 			break;
16402e192b24SSimon Glass 
16412e192b24SSimon Glass 		if (*p != '=') {
16422e192b24SSimon Glass 			printf("mtdids: incorrect <dev-num>\n");
16432e192b24SSimon Glass 			break;
16442e192b24SSimon Glass 		}
16452e192b24SSimon Glass 		p++;
16462e192b24SSimon Glass 
16472e192b24SSimon Glass 		/* check if requested device exists */
16482e192b24SSimon Glass 		if (mtd_device_validate(type, num, &size) != 0)
16492e192b24SSimon Glass 			return 1;
16502e192b24SSimon Glass 
16512e192b24SSimon Glass 		/* locate <mtd-id> */
16522e192b24SSimon Glass 		mtd_id = p;
16532e192b24SSimon Glass 		if ((p = strchr(mtd_id, ',')) != NULL) {
16542e192b24SSimon Glass 			mtd_id_len = p - mtd_id + 1;
16552e192b24SSimon Glass 			p++;
16562e192b24SSimon Glass 		} else {
16572e192b24SSimon Glass 			mtd_id_len = strlen(mtd_id) + 1;
16582e192b24SSimon Glass 		}
16592e192b24SSimon Glass 		if (mtd_id_len == 0) {
16602e192b24SSimon Glass 			printf("mtdids: no <mtd-id> identifier\n");
16612e192b24SSimon Glass 			break;
16622e192b24SSimon Glass 		}
16632e192b24SSimon Glass 
16642e192b24SSimon Glass 		/* check if this id is already on the list */
16652e192b24SSimon Glass 		int double_entry = 0;
16662e192b24SSimon Glass 		list_for_each(entry, &mtdids) {
16672e192b24SSimon Glass 			id_tmp = list_entry(entry, struct mtdids, link);
16682e192b24SSimon Glass 			if ((id_tmp->type == type) && (id_tmp->num == num)) {
16692e192b24SSimon Glass 				double_entry = 1;
16702e192b24SSimon Glass 				break;
16712e192b24SSimon Glass 			}
16722e192b24SSimon Glass 		}
16732e192b24SSimon Glass 		if (double_entry) {
16742e192b24SSimon Glass 			printf("device id %s%d redefined, please correct mtdids variable\n",
16752e192b24SSimon Glass 					MTD_DEV_TYPE(type), num);
16762e192b24SSimon Glass 			break;
16772e192b24SSimon Glass 		}
16782e192b24SSimon Glass 
16792e192b24SSimon Glass 		/* allocate mtdids structure */
16802e192b24SSimon Glass 		if (!(id = (struct mtdids *)malloc(sizeof(struct mtdids) + mtd_id_len))) {
16812e192b24SSimon Glass 			printf("out of memory\n");
16822e192b24SSimon Glass 			break;
16832e192b24SSimon Glass 		}
16842e192b24SSimon Glass 		memset(id, 0, sizeof(struct mtdids) + mtd_id_len);
16852e192b24SSimon Glass 		id->num = num;
16862e192b24SSimon Glass 		id->type = type;
16872e192b24SSimon Glass 		id->size = size;
16882e192b24SSimon Glass 		id->mtd_id = (char *)(id + 1);
16892e192b24SSimon Glass 		strncpy(id->mtd_id, mtd_id, mtd_id_len - 1);
16902e192b24SSimon Glass 		id->mtd_id[mtd_id_len - 1] = '\0';
16912e192b24SSimon Glass 		INIT_LIST_HEAD(&id->link);
16922e192b24SSimon Glass 
16932e192b24SSimon Glass 		debug("+ id %s%d\t%16lld bytes\t%s\n",
16942e192b24SSimon Glass 				MTD_DEV_TYPE(id->type), id->num,
16952e192b24SSimon Glass 				id->size, id->mtd_id);
16962e192b24SSimon Glass 
16972e192b24SSimon Glass 		list_add_tail(&id->link, &mtdids);
16982e192b24SSimon Glass 		ret = 0;
16992e192b24SSimon Glass 	}
17002e192b24SSimon Glass 	if (ret == 1) {
17012e192b24SSimon Glass 		/* clean mtdids list and free allocated memory */
17022e192b24SSimon Glass 		list_for_each_safe(entry, n, &mtdids) {
17032e192b24SSimon Glass 			id_tmp = list_entry(entry, struct mtdids, link);
17042e192b24SSimon Glass 			list_del(entry);
17052e192b24SSimon Glass 			free(id_tmp);
17062e192b24SSimon Glass 		}
17072e192b24SSimon Glass 		return 1;
17082e192b24SSimon Glass 	}
17092e192b24SSimon Glass 
17102e192b24SSimon Glass 	return 0;
17112e192b24SSimon Glass }
17122e192b24SSimon Glass 
17131c2a262aSLadislav Michl 
17142e192b24SSimon Glass /**
17152e192b24SSimon Glass  * Parse and initialize global mtdids mapping and create global
17162e192b24SSimon Glass  * device/partition list.
17172e192b24SSimon Glass  *
17182e192b24SSimon Glass  * @return 0 on success, 1 otherwise
17192e192b24SSimon Glass  */
17202e192b24SSimon Glass int mtdparts_init(void)
17212e192b24SSimon Glass {
17222e192b24SSimon Glass 	static int initialized = 0;
17232e192b24SSimon Glass 	const char *ids, *parts;
17242e192b24SSimon Glass 	const char *current_partition;
17252e192b24SSimon Glass 	int ids_changed;
1726bc028345STom Rini 	char tmp_ep[PARTITION_MAXLEN + 1];
17272e192b24SSimon Glass 	char tmp_parts[MTDPARTS_MAXLEN];
17282e192b24SSimon Glass 
17292e192b24SSimon Glass 	debug("\n---mtdparts_init---\n");
17302e192b24SSimon Glass 	if (!initialized) {
17312e192b24SSimon Glass 		INIT_LIST_HEAD(&mtdids);
17322e192b24SSimon Glass 		INIT_LIST_HEAD(&devices);
173354f1792cSTom Rini 		memset(last_ids, 0, sizeof(last_ids));
173454f1792cSTom Rini 		memset(last_parts, 0, sizeof(last_parts));
173554f1792cSTom Rini 		memset(last_partition, 0, sizeof(last_partition));
1736af324436SLadislav Michl #if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
1737af324436SLadislav Michl 		board_mtdparts_default(&mtdids_default, &mtdparts_default);
1738af324436SLadislav Michl #endif
1739f8f744a3SLadislav Michl 		use_defaults = 1;
17402e192b24SSimon Glass 		initialized = 1;
17412e192b24SSimon Glass 	}
17422e192b24SSimon Glass 
17432e192b24SSimon Glass 	/* get variables */
174400caae6dSSimon Glass 	ids = env_get("mtdids");
1745723806ccSSimon Glass 	parts = env_get_mtdparts(tmp_parts);
174600caae6dSSimon Glass 	current_partition = env_get("partition");
17472e192b24SSimon Glass 
17482e192b24SSimon Glass 	/* save it for later parsing, cannot rely on current partition pointer
17492e192b24SSimon Glass 	 * as 'partition' variable may be updated during init */
1750bc028345STom Rini 	memset(tmp_parts, 0, sizeof(tmp_parts));
17518b3cec7dSTom Rini 	memset(tmp_ep, 0, sizeof(tmp_ep));
17522e192b24SSimon Glass 	if (current_partition)
17532e192b24SSimon Glass 		strncpy(tmp_ep, current_partition, PARTITION_MAXLEN);
17542e192b24SSimon Glass 
17552e192b24SSimon Glass 	debug("last_ids  : %s\n", last_ids);
17562e192b24SSimon Glass 	debug("env_ids   : %s\n", ids);
17572e192b24SSimon Glass 	debug("last_parts: %s\n", last_parts);
17582e192b24SSimon Glass 	debug("env_parts : %s\n\n", parts);
17592e192b24SSimon Glass 
17602e192b24SSimon Glass 	debug("last_partition : %s\n", last_partition);
17612e192b24SSimon Glass 	debug("env_partition  : %s\n", current_partition);
17622e192b24SSimon Glass 
17631cc0a9f4SRobert P. J. Day 	/* if mtdids variable is empty try to use defaults */
17642e192b24SSimon Glass 	if (!ids) {
17652e192b24SSimon Glass 		if (mtdids_default) {
17662e192b24SSimon Glass 			debug("mtdids variable not defined, using default\n");
17672e192b24SSimon Glass 			ids = mtdids_default;
1768382bee57SSimon Glass 			env_set("mtdids", (char *)ids);
17692e192b24SSimon Glass 		} else {
17702e192b24SSimon Glass 			printf("mtdids not defined, no default present\n");
17712e192b24SSimon Glass 			return 1;
17722e192b24SSimon Glass 		}
17732e192b24SSimon Glass 	}
17742e192b24SSimon Glass 	if (strlen(ids) > MTDIDS_MAXLEN - 1) {
17752e192b24SSimon Glass 		printf("mtdids too long (> %d)\n", MTDIDS_MAXLEN);
17762e192b24SSimon Glass 		return 1;
17772e192b24SSimon Glass 	}
17782e192b24SSimon Glass 
1779f8f744a3SLadislav Michl 	/* use defaults when mtdparts variable is not defined
1780f8f744a3SLadislav Michl 	 * once mtdparts is saved environment, drop use_defaults flag */
1781f8f744a3SLadislav Michl 	if (!parts) {
1782f8f744a3SLadislav Michl 		if (mtdparts_default && use_defaults) {
1783f8f744a3SLadislav Michl 			parts = mtdparts_default;
1784382bee57SSimon Glass 			if (env_set("mtdparts", (char *)parts) == 0)
1785f8f744a3SLadislav Michl 				use_defaults = 0;
1786f8f744a3SLadislav Michl 		} else
17872e192b24SSimon Glass 			printf("mtdparts variable not set, see 'help mtdparts'\n");
1788f8f744a3SLadislav Michl 	}
17892e192b24SSimon Glass 
17902e192b24SSimon Glass 	if (parts && (strlen(parts) > MTDPARTS_MAXLEN - 1)) {
17912e192b24SSimon Glass 		printf("mtdparts too long (> %d)\n", MTDPARTS_MAXLEN);
17922e192b24SSimon Glass 		return 1;
17932e192b24SSimon Glass 	}
17942e192b24SSimon Glass 
17952e192b24SSimon Glass 	/* check if we have already parsed those mtdids */
17962e192b24SSimon Glass 	if ((last_ids[0] != '\0') && (strcmp(last_ids, ids) == 0)) {
17972e192b24SSimon Glass 		ids_changed = 0;
17982e192b24SSimon Glass 	} else {
17992e192b24SSimon Glass 		ids_changed = 1;
18002e192b24SSimon Glass 
18012e192b24SSimon Glass 		if (parse_mtdids(ids) != 0) {
18022e192b24SSimon Glass 			mtd_devices_init();
18032e192b24SSimon Glass 			return 1;
18042e192b24SSimon Glass 		}
18052e192b24SSimon Glass 
18062e192b24SSimon Glass 		/* ok it's good, save new ids */
18072e192b24SSimon Glass 		strncpy(last_ids, ids, MTDIDS_MAXLEN);
18082e192b24SSimon Glass 	}
18092e192b24SSimon Glass 
18102e192b24SSimon Glass 	/* parse partitions if either mtdparts or mtdids were updated */
18112e192b24SSimon Glass 	if (parts && ((last_parts[0] == '\0') || ((strcmp(last_parts, parts) != 0)) || ids_changed)) {
18122e192b24SSimon Glass 		if (parse_mtdparts(parts) != 0)
18132e192b24SSimon Glass 			return 1;
18142e192b24SSimon Glass 
18152e192b24SSimon Glass 		if (list_empty(&devices)) {
18162e192b24SSimon Glass 			printf("mtdparts_init: no valid partitions\n");
18172e192b24SSimon Glass 			return 1;
18182e192b24SSimon Glass 		}
18192e192b24SSimon Glass 
18202e192b24SSimon Glass 		/* ok it's good, save new parts */
18212e192b24SSimon Glass 		strncpy(last_parts, parts, MTDPARTS_MAXLEN);
18222e192b24SSimon Glass 
18232e192b24SSimon Glass 		/* reset first partition from first dev from the list as current */
18242e192b24SSimon Glass 		current_mtd_dev = list_entry(devices.next, struct mtd_device, link);
18252e192b24SSimon Glass 		current_mtd_partnum = 0;
18262e192b24SSimon Glass 		current_save();
18272e192b24SSimon Glass 
18282e192b24SSimon Glass 		debug("mtdparts_init: current_mtd_dev  = %s%d, current_mtd_partnum = %d\n",
18292e192b24SSimon Glass 				MTD_DEV_TYPE(current_mtd_dev->id->type),
18302e192b24SSimon Glass 				current_mtd_dev->id->num, current_mtd_partnum);
18312e192b24SSimon Glass 	}
18322e192b24SSimon Glass 
18332e192b24SSimon Glass 	/* mtdparts variable was reset to NULL, delete all devices/partitions */
18342e192b24SSimon Glass 	if (!parts && (last_parts[0] != '\0'))
18352e192b24SSimon Glass 		return mtd_devices_init();
18362e192b24SSimon Glass 
18372e192b24SSimon Glass 	/* do not process current partition if mtdparts variable is null */
18382e192b24SSimon Glass 	if (!parts)
18392e192b24SSimon Glass 		return 0;
18402e192b24SSimon Glass 
18412e192b24SSimon Glass 	/* is current partition set in environment? if so, use it */
18422e192b24SSimon Glass 	if ((tmp_ep[0] != '\0') && (strcmp(tmp_ep, last_partition) != 0)) {
18432e192b24SSimon Glass 		struct part_info *p;
18442e192b24SSimon Glass 		struct mtd_device *cdev;
18452e192b24SSimon Glass 		u8 pnum;
18462e192b24SSimon Glass 
18472e192b24SSimon Glass 		debug("--- getting current partition: %s\n", tmp_ep);
18482e192b24SSimon Glass 
18492e192b24SSimon Glass 		if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p) == 0) {
18502e192b24SSimon Glass 			current_mtd_dev = cdev;
18512e192b24SSimon Glass 			current_mtd_partnum = pnum;
18522e192b24SSimon Glass 			current_save();
18532e192b24SSimon Glass 		}
185400caae6dSSimon Glass 	} else if (env_get("partition") == NULL) {
18552e192b24SSimon Glass 		debug("no partition variable set, setting...\n");
18562e192b24SSimon Glass 		current_save();
18572e192b24SSimon Glass 	}
18582e192b24SSimon Glass 
18592e192b24SSimon Glass 	return 0;
18602e192b24SSimon Glass }
18612e192b24SSimon Glass 
18622e192b24SSimon Glass /**
18632e192b24SSimon Glass  * Return pointer to the partition of a requested number from a requested
18642e192b24SSimon Glass  * device.
18652e192b24SSimon Glass  *
18662e192b24SSimon Glass  * @param dev device that is to be searched for a partition
18672e192b24SSimon Glass  * @param part_num requested partition number
18682e192b24SSimon Glass  * @return pointer to the part_info, NULL otherwise
18692e192b24SSimon Glass  */
18702e192b24SSimon Glass static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num)
18712e192b24SSimon Glass {
18722e192b24SSimon Glass 	struct list_head *entry;
18732e192b24SSimon Glass 	struct part_info *part;
18742e192b24SSimon Glass 	int num;
18752e192b24SSimon Glass 
18762e192b24SSimon Glass 	if (!dev)
18772e192b24SSimon Glass 		return NULL;
18782e192b24SSimon Glass 
18792e192b24SSimon Glass 	debug("\n--- mtd_part_info: partition number %d for device %s%d (%s)\n",
18802e192b24SSimon Glass 			part_num, MTD_DEV_TYPE(dev->id->type),
18812e192b24SSimon Glass 			dev->id->num, dev->id->mtd_id);
18822e192b24SSimon Glass 
18832e192b24SSimon Glass 	if (part_num >= dev->num_parts) {
18842e192b24SSimon Glass 		printf("invalid partition number %d for device %s%d (%s)\n",
18852e192b24SSimon Glass 				part_num, MTD_DEV_TYPE(dev->id->type),
18862e192b24SSimon Glass 				dev->id->num, dev->id->mtd_id);
18872e192b24SSimon Glass 		return NULL;
18882e192b24SSimon Glass 	}
18892e192b24SSimon Glass 
18902e192b24SSimon Glass 	/* locate partition number, return it */
18912e192b24SSimon Glass 	num = 0;
18922e192b24SSimon Glass 	list_for_each(entry, &dev->parts) {
18932e192b24SSimon Glass 		part = list_entry(entry, struct part_info, link);
18942e192b24SSimon Glass 
18952e192b24SSimon Glass 		if (part_num == num++) {
18962e192b24SSimon Glass 			return part;
18972e192b24SSimon Glass 		}
18982e192b24SSimon Glass 	}
18992e192b24SSimon Glass 
19002e192b24SSimon Glass 	return NULL;
19012e192b24SSimon Glass }
19022e192b24SSimon Glass 
19032e192b24SSimon Glass /***************************************************/
1904a187559eSBin Meng /* U-Boot commands				   */
19052e192b24SSimon Glass /***************************************************/
19062e192b24SSimon Glass /* command line only */
19072e192b24SSimon Glass /**
19082e192b24SSimon Glass  * Routine implementing u-boot chpart command. Sets new current partition based
19092e192b24SSimon Glass  * on the user supplied partition id. For partition id format see find_dev_and_part().
19102e192b24SSimon Glass  *
19112e192b24SSimon Glass  * @param cmdtp command internal data
19122e192b24SSimon Glass  * @param flag command flag
19132e192b24SSimon Glass  * @param argc number of arguments supplied to the command
19142e192b24SSimon Glass  * @param argv arguments list
19152e192b24SSimon Glass  * @return 0 on success, 1 otherwise
19162e192b24SSimon Glass  */
19172e192b24SSimon Glass static int do_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
19182e192b24SSimon Glass {
19192e192b24SSimon Glass /* command line only */
19202e192b24SSimon Glass 	struct mtd_device *dev;
19212e192b24SSimon Glass 	struct part_info *part;
19222e192b24SSimon Glass 	u8 pnum;
19232e192b24SSimon Glass 
19242e192b24SSimon Glass 	if (mtdparts_init() !=0)
19252e192b24SSimon Glass 		return 1;
19262e192b24SSimon Glass 
19272e192b24SSimon Glass 	if (argc < 2) {
19282e192b24SSimon Glass 		printf("no partition id specified\n");
19292e192b24SSimon Glass 		return 1;
19302e192b24SSimon Glass 	}
19312e192b24SSimon Glass 
19322e192b24SSimon Glass 	if (find_dev_and_part(argv[1], &dev, &pnum, &part) != 0)
19332e192b24SSimon Glass 		return 1;
19342e192b24SSimon Glass 
19352e192b24SSimon Glass 	current_mtd_dev = dev;
19362e192b24SSimon Glass 	current_mtd_partnum = pnum;
19372e192b24SSimon Glass 	current_save();
19382e192b24SSimon Glass 
19392e192b24SSimon Glass 	printf("partition changed to %s%d,%d\n",
19402e192b24SSimon Glass 			MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum);
19412e192b24SSimon Glass 
19422e192b24SSimon Glass 	return 0;
19432e192b24SSimon Glass }
19442e192b24SSimon Glass 
19452e192b24SSimon Glass /**
19462e192b24SSimon Glass  * Routine implementing u-boot mtdparts command. Initialize/update default global
19472e192b24SSimon Glass  * partition list and process user partition request (list, add, del).
19482e192b24SSimon Glass  *
19492e192b24SSimon Glass  * @param cmdtp command internal data
19502e192b24SSimon Glass  * @param flag command flag
19512e192b24SSimon Glass  * @param argc number of arguments supplied to the command
19522e192b24SSimon Glass  * @param argv arguments list
19532e192b24SSimon Glass  * @return 0 on success, 1 otherwise
19542e192b24SSimon Glass  */
19552e192b24SSimon Glass static int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc,
19562e192b24SSimon Glass 		       char * const argv[])
19572e192b24SSimon Glass {
19582e192b24SSimon Glass 	if (argc == 2) {
19592e192b24SSimon Glass 		if (strcmp(argv[1], "default") == 0) {
1960382bee57SSimon Glass 			env_set("mtdids", NULL);
1961382bee57SSimon Glass 			env_set("mtdparts", NULL);
1962382bee57SSimon Glass 			env_set("partition", NULL);
1963f8f744a3SLadislav Michl 			use_defaults = 1;
19642e192b24SSimon Glass 
19652e192b24SSimon Glass 			mtdparts_init();
19662e192b24SSimon Glass 			return 0;
19672e192b24SSimon Glass 		} else if (strcmp(argv[1], "delall") == 0) {
19682e192b24SSimon Glass 			/* this may be the first run, initialize lists if needed */
19692e192b24SSimon Glass 			mtdparts_init();
19702e192b24SSimon Glass 
1971382bee57SSimon Glass 			env_set("mtdparts", NULL);
19722e192b24SSimon Glass 
19732e192b24SSimon Glass 			/* mtd_devices_init() calls current_save() */
19742e192b24SSimon Glass 			return mtd_devices_init();
19752e192b24SSimon Glass 		}
19762e192b24SSimon Glass 	}
19772e192b24SSimon Glass 
19782e192b24SSimon Glass 	/* make sure we are in sync with env variables */
19792e192b24SSimon Glass 	if (mtdparts_init() != 0)
19802e192b24SSimon Glass 		return 1;
19812e192b24SSimon Glass 
19822e192b24SSimon Glass 	if (argc == 1) {
19832e192b24SSimon Glass 		list_partitions();
19842e192b24SSimon Glass 		return 0;
19852e192b24SSimon Glass 	}
19862e192b24SSimon Glass 
19872e192b24SSimon Glass 	/* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */
19882e192b24SSimon Glass 	if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) {
19892e192b24SSimon Glass #define PART_ADD_DESC_MAXLEN 64
19902e192b24SSimon Glass 		char tmpbuf[PART_ADD_DESC_MAXLEN];
19912e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS_SPREAD)
19922e192b24SSimon Glass 		struct mtd_info *mtd;
19932e192b24SSimon Glass 		uint64_t next_offset;
19942e192b24SSimon Glass #endif
19952e192b24SSimon Glass 		u8 type, num, len;
19962e192b24SSimon Glass 		struct mtd_device *dev;
19972e192b24SSimon Glass 		struct mtd_device *dev_tmp;
19982e192b24SSimon Glass 		struct mtdids *id;
19992e192b24SSimon Glass 		struct part_info *p;
20002e192b24SSimon Glass 
20012e192b24SSimon Glass 		if (mtd_id_parse(argv[2], NULL, &type, &num) != 0)
20022e192b24SSimon Glass 			return 1;
20032e192b24SSimon Glass 
20042e192b24SSimon Glass 		if ((id = id_find(type, num)) == NULL) {
20052e192b24SSimon Glass 			printf("no such device %s defined in mtdids variable\n", argv[2]);
20062e192b24SSimon Glass 			return 1;
20072e192b24SSimon Glass 		}
20082e192b24SSimon Glass 
20092e192b24SSimon Glass 		len = strlen(id->mtd_id) + 1;	/* 'mtd_id:' */
20102e192b24SSimon Glass 		len += strlen(argv[3]);		/* size@offset */
20112e192b24SSimon Glass 		len += strlen(argv[4]) + 2;	/* '(' name ')' */
20122e192b24SSimon Glass 		if (argv[5] && (strlen(argv[5]) == 2))
20132e192b24SSimon Glass 			len += 2;		/* 'ro' */
20142e192b24SSimon Glass 
20152e192b24SSimon Glass 		if (len >= PART_ADD_DESC_MAXLEN) {
20162e192b24SSimon Glass 			printf("too long partition description\n");
20172e192b24SSimon Glass 			return 1;
20182e192b24SSimon Glass 		}
20192e192b24SSimon Glass 		sprintf(tmpbuf, "%s:%s(%s)%s",
20202e192b24SSimon Glass 				id->mtd_id, argv[3], argv[4], argv[5] ? argv[5] : "");
20212e192b24SSimon Glass 		debug("add tmpbuf: %s\n", tmpbuf);
20222e192b24SSimon Glass 
20232e192b24SSimon Glass 		if ((device_parse(tmpbuf, NULL, &dev) != 0) || (!dev))
20242e192b24SSimon Glass 			return 1;
20252e192b24SSimon Glass 
20262e192b24SSimon Glass 		debug("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
20272e192b24SSimon Glass 				dev->id->num, dev->id->mtd_id);
20282e192b24SSimon Glass 
20292e192b24SSimon Glass 		p = list_entry(dev->parts.next, struct part_info, link);
20302e192b24SSimon Glass 
20312e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS_SPREAD)
20322e192b24SSimon Glass 		if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
20332e192b24SSimon Glass 			return 1;
20342e192b24SSimon Glass 
20352e192b24SSimon Glass 		if (!strcmp(&argv[1][3], ".spread")) {
20362e192b24SSimon Glass 			spread_partition(mtd, p, &next_offset);
203759441ac3SSteve Rae 			debug("increased %s to %llu bytes\n", p->name, p->size);
20382e192b24SSimon Glass 		}
20392e192b24SSimon Glass #endif
20402e192b24SSimon Glass 
20412e192b24SSimon Glass 		dev_tmp = device_find(dev->id->type, dev->id->num);
20422e192b24SSimon Glass 		if (dev_tmp == NULL) {
20432e192b24SSimon Glass 			device_add(dev);
20442e192b24SSimon Glass 		} else if (part_add(dev_tmp, p) != 0) {
20452e192b24SSimon Glass 			/* merge new partition with existing ones*/
20462e192b24SSimon Glass 			device_del(dev);
20472e192b24SSimon Glass 			return 1;
20482e192b24SSimon Glass 		}
20492e192b24SSimon Glass 
20502e192b24SSimon Glass 		if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
20512e192b24SSimon Glass 			printf("generated mtdparts too long, resetting to null\n");
20522e192b24SSimon Glass 			return 1;
20532e192b24SSimon Glass 		}
20542e192b24SSimon Glass 
20552e192b24SSimon Glass 		return 0;
20562e192b24SSimon Glass 	}
20572e192b24SSimon Glass 
20582e192b24SSimon Glass 	/* mtdparts del part-id */
20592e192b24SSimon Glass 	if ((argc == 3) && (strcmp(argv[1], "del") == 0)) {
20602e192b24SSimon Glass 		debug("del: part-id = %s\n", argv[2]);
20612e192b24SSimon Glass 
20622e192b24SSimon Glass 		return delete_partition(argv[2]);
20632e192b24SSimon Glass 	}
20642e192b24SSimon Glass 
20652e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS_SPREAD)
20662e192b24SSimon Glass 	if ((argc == 2) && (strcmp(argv[1], "spread") == 0))
20672e192b24SSimon Glass 		return spread_partitions();
20682e192b24SSimon Glass #endif /* CONFIG_CMD_MTDPARTS_SPREAD */
20692e192b24SSimon Glass 
20702e192b24SSimon Glass 	return CMD_RET_USAGE;
20712e192b24SSimon Glass }
20722e192b24SSimon Glass 
20732e192b24SSimon Glass /***************************************************/
20742e192b24SSimon Glass U_BOOT_CMD(
20752e192b24SSimon Glass 	chpart,	2,	0,	do_chpart,
20762e192b24SSimon Glass 	"change active partition",
20772e192b24SSimon Glass 	"part-id\n"
20782e192b24SSimon Glass 	"    - change active partition (e.g. part-id = nand0,1)"
20792e192b24SSimon Glass );
20802e192b24SSimon Glass 
20812e192b24SSimon Glass #ifdef CONFIG_SYS_LONGHELP
20822e192b24SSimon Glass static char mtdparts_help_text[] =
20832e192b24SSimon Glass 	"\n"
20842e192b24SSimon Glass 	"    - list partition table\n"
20852e192b24SSimon Glass 	"mtdparts delall\n"
20862e192b24SSimon Glass 	"    - delete all partitions\n"
20872e192b24SSimon Glass 	"mtdparts del part-id\n"
20882e192b24SSimon Glass 	"    - delete partition (e.g. part-id = nand0,1)\n"
20892e192b24SSimon Glass 	"mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n"
20902e192b24SSimon Glass 	"    - add partition\n"
20912e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS_SPREAD)
20922e192b24SSimon Glass 	"mtdparts add.spread <mtd-dev> <size>[@<offset>] [<name>] [ro]\n"
20932e192b24SSimon Glass 	"    - add partition, padding size by skipping bad blocks\n"
20942e192b24SSimon Glass #endif
20952e192b24SSimon Glass 	"mtdparts default\n"
20962e192b24SSimon Glass 	"    - reset partition table to defaults\n"
20972e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS_SPREAD)
20982e192b24SSimon Glass 	"mtdparts spread\n"
20992e192b24SSimon Glass 	"    - adjust the sizes of the partitions so they are\n"
21002e192b24SSimon Glass 	"      at least as big as the mtdparts variable specifies\n"
21012e192b24SSimon Glass 	"      and they each start on a good block\n\n"
21022e192b24SSimon Glass #else
21032e192b24SSimon Glass 	"\n"
21042e192b24SSimon Glass #endif /* CONFIG_CMD_MTDPARTS_SPREAD */
21052e192b24SSimon Glass 	"-----\n\n"
21062e192b24SSimon Glass 	"this command uses three environment variables:\n\n"
21072e192b24SSimon Glass 	"'partition' - keeps current partition identifier\n\n"
21082e192b24SSimon Glass 	"partition  := <part-id>\n"
21092e192b24SSimon Glass 	"<part-id>  := <dev-id>,part_num\n\n"
21102e192b24SSimon Glass 	"'mtdids' - linux kernel mtd device id <-> u-boot device id mapping\n\n"
21112e192b24SSimon Glass 	"mtdids=<idmap>[,<idmap>,...]\n\n"
21122e192b24SSimon Glass 	"<idmap>    := <dev-id>=<mtd-id>\n"
211300ac922dSMiquel Raynal 	"<dev-id>   := 'nand'|'nor'|'onenand'|'spi-nand'<dev-num>\n"
21142e192b24SSimon Glass 	"<dev-num>  := mtd device number, 0...\n"
21152e192b24SSimon Glass 	"<mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)\n\n"
21162e192b24SSimon Glass 	"'mtdparts' - partition list\n\n"
21172e192b24SSimon Glass 	"mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]\n\n"
21182e192b24SSimon Glass 	"<mtd-def>  := <mtd-id>:<part-def>[,<part-def>...]\n"
21192e192b24SSimon Glass 	"<mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)\n"
21202e192b24SSimon Glass 	"<part-def> := <size>[@<offset>][<name>][<ro-flag>]\n"
21212e192b24SSimon Glass 	"<size>     := standard linux memsize OR '-' to denote all remaining space\n"
21222e192b24SSimon Glass 	"<offset>   := partition start offset within the device\n"
21232e192b24SSimon Glass 	"<name>     := '(' NAME ')'\n"
21242e192b24SSimon Glass 	"<ro-flag>  := when set to 'ro' makes partition read-only (not used, passed to kernel)";
21252e192b24SSimon Glass #endif
21262e192b24SSimon Glass 
21272e192b24SSimon Glass U_BOOT_CMD(
21282e192b24SSimon Glass 	mtdparts,	6,	0,	do_mtdparts,
21292e192b24SSimon Glass 	"define flash/nand partitions", mtdparts_help_text
21302e192b24SSimon Glass );
21312e192b24SSimon Glass /***************************************************/
2132