109c32807SHeiko Schocher /* 209c32807SHeiko Schocher * (C) Copyright 2014 309c32807SHeiko Schocher * Heiko Schocher, DENX Software Engineering, hs@denx.de. 409c32807SHeiko Schocher * 509c32807SHeiko Schocher * SPDX-License-Identifier: GPL-2.0+ 609c32807SHeiko Schocher */ 709c32807SHeiko Schocher #include <common.h> 8f87151b3SMiquel Raynal #include <dm/device.h> 9f87151b3SMiquel Raynal #include <dm/uclass-internal.h> 10f87151b3SMiquel Raynal #include <jffs2/jffs2.h> /* LEGACY */ 1109c32807SHeiko Schocher #include <linux/mtd/mtd.h> 12f87151b3SMiquel Raynal #include <linux/mtd/partitions.h> 13f87151b3SMiquel Raynal #include <mtd.h> 14f87151b3SMiquel Raynal 15f87151b3SMiquel Raynal #define MTD_NAME_MAX_LEN 20 16f87151b3SMiquel Raynal 174ac4e964SBoris Brezillon void board_mtdparts_default(const char **mtdids, const char **mtdparts); 184ac4e964SBoris Brezillon 194ac4e964SBoris Brezillon static const char *get_mtdids(void) 204ac4e964SBoris Brezillon { 214ac4e964SBoris Brezillon __maybe_unused const char *mtdparts = NULL; 224ac4e964SBoris Brezillon const char *mtdids = env_get("mtdids"); 234ac4e964SBoris Brezillon 244ac4e964SBoris Brezillon if (mtdids) 254ac4e964SBoris Brezillon return mtdids; 264ac4e964SBoris Brezillon 274ac4e964SBoris Brezillon #if defined(CONFIG_SYS_MTDPARTS_RUNTIME) 284ac4e964SBoris Brezillon board_mtdparts_default(&mtdids, &mtdparts); 294ac4e964SBoris Brezillon #elif defined(MTDIDS_DEFAULT) 304ac4e964SBoris Brezillon mtdids = MTDIDS_DEFAULT; 314ac4e964SBoris Brezillon #elif defined(CONFIG_MTDIDS_DEFAULT) 324ac4e964SBoris Brezillon mtdids = CONFIG_MTDIDS_DEFAULT; 334ac4e964SBoris Brezillon #endif 344ac4e964SBoris Brezillon 354ac4e964SBoris Brezillon if (mtdids) 364ac4e964SBoris Brezillon env_set("mtdids", mtdids); 374ac4e964SBoris Brezillon 384ac4e964SBoris Brezillon return mtdids; 394ac4e964SBoris Brezillon } 4050466819SMiquel Raynal 4150466819SMiquel Raynal /** 4250466819SMiquel Raynal * mtd_search_alternate_name - Search an alternate name for @mtdname thanks to 4350466819SMiquel Raynal * the mtdids legacy environment variable. 4450466819SMiquel Raynal * 4550466819SMiquel Raynal * The mtdids string is a list of comma-separated 'dev_id=mtd_id' tupples. 4650466819SMiquel Raynal * Check if one of the mtd_id matches mtdname, in this case save dev_id in 4750466819SMiquel Raynal * altname. 4850466819SMiquel Raynal * 4950466819SMiquel Raynal * @mtdname: Current MTD device name 5050466819SMiquel Raynal * @altname: Alternate name to return 5150466819SMiquel Raynal * @max_len: Length of the alternate name buffer 5250466819SMiquel Raynal * 5350466819SMiquel Raynal * @return 0 on success, an error otherwise. 5450466819SMiquel Raynal */ 5550466819SMiquel Raynal int mtd_search_alternate_name(const char *mtdname, char *altname, 5650466819SMiquel Raynal unsigned int max_len) 5750466819SMiquel Raynal { 5850466819SMiquel Raynal const char *mtdids, *equal, *comma, *dev_id, *mtd_id; 5950466819SMiquel Raynal int dev_id_len, mtd_id_len; 6050466819SMiquel Raynal 614ac4e964SBoris Brezillon mtdids = get_mtdids(); 6250466819SMiquel Raynal if (!mtdids) 6350466819SMiquel Raynal return -EINVAL; 6450466819SMiquel Raynal 6550466819SMiquel Raynal do { 6650466819SMiquel Raynal /* Find the '=' sign */ 6750466819SMiquel Raynal dev_id = mtdids; 6850466819SMiquel Raynal equal = strchr(dev_id, '='); 6950466819SMiquel Raynal if (!equal) 7050466819SMiquel Raynal break; 7150466819SMiquel Raynal dev_id_len = equal - mtdids; 7250466819SMiquel Raynal mtd_id = equal + 1; 7350466819SMiquel Raynal 7450466819SMiquel Raynal /* Find the end of the tupple */ 7550466819SMiquel Raynal comma = strchr(mtdids, ','); 7650466819SMiquel Raynal if (comma) 7750466819SMiquel Raynal mtd_id_len = comma - mtd_id; 7850466819SMiquel Raynal else 7950466819SMiquel Raynal mtd_id_len = &mtdids[strlen(mtdids)] - mtd_id + 1; 8050466819SMiquel Raynal 8150466819SMiquel Raynal if (!dev_id_len || !mtd_id_len) 8250466819SMiquel Raynal return -EINVAL; 8350466819SMiquel Raynal 8450466819SMiquel Raynal if (dev_id_len + 1 > max_len) 8550466819SMiquel Raynal continue; 8650466819SMiquel Raynal 8750466819SMiquel Raynal /* Compare the name we search with the current mtd_id */ 8850466819SMiquel Raynal if (!strncmp(mtdname, mtd_id, mtd_id_len)) { 8950466819SMiquel Raynal strncpy(altname, dev_id, dev_id_len); 9050466819SMiquel Raynal altname[dev_id_len] = 0; 9150466819SMiquel Raynal 9250466819SMiquel Raynal return 0; 9350466819SMiquel Raynal } 9450466819SMiquel Raynal 9550466819SMiquel Raynal /* Go to the next tupple */ 9650466819SMiquel Raynal mtdids = comma + 1; 9750466819SMiquel Raynal } while (comma); 9850466819SMiquel Raynal 9950466819SMiquel Raynal return -EINVAL; 10050466819SMiquel Raynal } 10150466819SMiquel Raynal 102f87151b3SMiquel Raynal #if IS_ENABLED(CONFIG_MTD) 103f87151b3SMiquel Raynal static void mtd_probe_uclass_mtd_devs(void) 104f87151b3SMiquel Raynal { 105f87151b3SMiquel Raynal struct udevice *dev; 106f87151b3SMiquel Raynal int idx = 0; 107f87151b3SMiquel Raynal 108f87151b3SMiquel Raynal /* Probe devices with DM compliant drivers */ 109f87151b3SMiquel Raynal while (!uclass_find_device(UCLASS_MTD, idx, &dev) && dev) { 110f87151b3SMiquel Raynal mtd_probe(dev); 111f87151b3SMiquel Raynal idx++; 112f87151b3SMiquel Raynal } 113f87151b3SMiquel Raynal } 114f87151b3SMiquel Raynal #else 115f87151b3SMiquel Raynal static void mtd_probe_uclass_mtd_devs(void) { } 116f87151b3SMiquel Raynal #endif 117f87151b3SMiquel Raynal 118f87151b3SMiquel Raynal #if defined(CONFIG_MTD_PARTITIONS) 119b95c8f6cSBoris Brezillon 120b95c8f6cSBoris Brezillon #define MTDPARTS_MAXLEN 512 121b95c8f6cSBoris Brezillon 122b95c8f6cSBoris Brezillon static const char *get_mtdparts(void) 123b95c8f6cSBoris Brezillon { 124b95c8f6cSBoris Brezillon __maybe_unused const char *mtdids = NULL; 125b95c8f6cSBoris Brezillon static char tmp_parts[MTDPARTS_MAXLEN]; 126b95c8f6cSBoris Brezillon static bool use_defaults = true; 127b95c8f6cSBoris Brezillon const char *mtdparts = NULL; 128b95c8f6cSBoris Brezillon 129b95c8f6cSBoris Brezillon if (gd->flags & GD_FLG_ENV_READY) 130b95c8f6cSBoris Brezillon mtdparts = env_get("mtdparts"); 131b95c8f6cSBoris Brezillon else if (env_get_f("mtdparts", tmp_parts, sizeof(tmp_parts)) != -1) 132b95c8f6cSBoris Brezillon mtdparts = tmp_parts; 133b95c8f6cSBoris Brezillon 134b95c8f6cSBoris Brezillon if (mtdparts || !use_defaults) 135b95c8f6cSBoris Brezillon return mtdparts; 136b95c8f6cSBoris Brezillon 137b95c8f6cSBoris Brezillon #if defined(CONFIG_SYS_MTDPARTS_RUNTIME) 138b95c8f6cSBoris Brezillon board_mtdparts_default(&mtdids, &mtdparts); 139b95c8f6cSBoris Brezillon #elif defined(MTDPARTS_DEFAULT) 140b95c8f6cSBoris Brezillon mtdparts = MTDPARTS_DEFAULT; 141b95c8f6cSBoris Brezillon #elif defined(CONFIG_MTDPARTS_DEFAULT) 142b95c8f6cSBoris Brezillon mtdparts = CONFIG_MTDPARTS_DEFAULT; 143b95c8f6cSBoris Brezillon #endif 144b95c8f6cSBoris Brezillon 145b95c8f6cSBoris Brezillon if (mtdparts) 146b95c8f6cSBoris Brezillon env_set("mtdparts", mtdparts); 147b95c8f6cSBoris Brezillon 148b95c8f6cSBoris Brezillon use_defaults = false; 149b95c8f6cSBoris Brezillon 150b95c8f6cSBoris Brezillon return mtdparts; 151b95c8f6cSBoris Brezillon } 152b95c8f6cSBoris Brezillon 153f87151b3SMiquel Raynal int mtd_probe_devices(void) 154f87151b3SMiquel Raynal { 155f87151b3SMiquel Raynal static char *old_mtdparts; 156f87151b3SMiquel Raynal static char *old_mtdids; 157b95c8f6cSBoris Brezillon const char *mtdparts = get_mtdparts(); 158b95c8f6cSBoris Brezillon const char *mtdids = get_mtdids(); 159*a38bf0a9SBoris Brezillon const char *mtdparts_next = mtdparts; 160f87151b3SMiquel Raynal bool remaining_partitions = true; 161f87151b3SMiquel Raynal struct mtd_info *mtd; 162f87151b3SMiquel Raynal 163f87151b3SMiquel Raynal mtd_probe_uclass_mtd_devs(); 164f87151b3SMiquel Raynal 1654c33ae3bSBoris Brezillon /* 1664c33ae3bSBoris Brezillon * Check if mtdparts/mtdids changed or if the MTD dev list was updated 1674c33ae3bSBoris Brezillon * since last call, otherwise: exit 1684c33ae3bSBoris Brezillon */ 1694696f08fSAdam Ford if ((!mtdparts && !old_mtdparts && !mtdids && !old_mtdids) || 1704696f08fSAdam Ford (mtdparts && old_mtdparts && mtdids && old_mtdids && 1714c33ae3bSBoris Brezillon !mtd_dev_list_updated() && 1724696f08fSAdam Ford !strcmp(mtdparts, old_mtdparts) && 1734696f08fSAdam Ford !strcmp(mtdids, old_mtdids))) 174f87151b3SMiquel Raynal return 0; 175f87151b3SMiquel Raynal 176f87151b3SMiquel Raynal /* Update the local copy of mtdparts */ 177f87151b3SMiquel Raynal free(old_mtdparts); 178f87151b3SMiquel Raynal free(old_mtdids); 179f87151b3SMiquel Raynal old_mtdparts = strdup(mtdparts); 180f87151b3SMiquel Raynal old_mtdids = strdup(mtdids); 181f87151b3SMiquel Raynal 182f87151b3SMiquel Raynal /* If at least one partition is still in use, do not delete anything */ 183f87151b3SMiquel Raynal mtd_for_each_device(mtd) { 184f87151b3SMiquel Raynal if (mtd->usecount) { 185f87151b3SMiquel Raynal printf("Partition \"%s\" already in use, aborting\n", 186f87151b3SMiquel Raynal mtd->name); 187f87151b3SMiquel Raynal return -EACCES; 188f87151b3SMiquel Raynal } 189f87151b3SMiquel Raynal } 190f87151b3SMiquel Raynal 191f87151b3SMiquel Raynal /* 192f87151b3SMiquel Raynal * Everything looks clear, remove all partitions. It is not safe to 193f87151b3SMiquel Raynal * remove entries from the mtd_for_each_device loop as it uses idr 194f87151b3SMiquel Raynal * indexes and the partitions removal is done in bulk (all partitions of 195f87151b3SMiquel Raynal * one device at the same time), so break and iterate from start each 196f87151b3SMiquel Raynal * time a new partition is found and deleted. 197f87151b3SMiquel Raynal */ 198f87151b3SMiquel Raynal while (remaining_partitions) { 199f87151b3SMiquel Raynal remaining_partitions = false; 200f87151b3SMiquel Raynal mtd_for_each_device(mtd) { 201f87151b3SMiquel Raynal if (!mtd_is_partition(mtd) && mtd_has_partitions(mtd)) { 202f87151b3SMiquel Raynal del_mtd_partitions(mtd); 203f87151b3SMiquel Raynal remaining_partitions = true; 204f87151b3SMiquel Raynal break; 205f87151b3SMiquel Raynal } 206f87151b3SMiquel Raynal } 207f87151b3SMiquel Raynal } 208f87151b3SMiquel Raynal 2094c33ae3bSBoris Brezillon /* 2104c33ae3bSBoris Brezillon * Call mtd_dev_list_updated() to clear updates generated by our own 2114c33ae3bSBoris Brezillon * parts removal loop. 2124c33ae3bSBoris Brezillon */ 2134c33ae3bSBoris Brezillon mtd_dev_list_updated(); 2144c33ae3bSBoris Brezillon 2154696f08fSAdam Ford /* If either mtdparts or mtdids is empty, then exit */ 2164696f08fSAdam Ford if (!mtdparts || !mtdids) 2174696f08fSAdam Ford return 0; 2184696f08fSAdam Ford 219f87151b3SMiquel Raynal /* Start the parsing by ignoring the extra 'mtdparts=' prefix, if any */ 220e3112a2cSBoris Brezillon if (!strncmp(mtdparts, "mtdparts=", sizeof("mtdparts=") - 1)) 221f87151b3SMiquel Raynal mtdparts += 9; 222f87151b3SMiquel Raynal 223f87151b3SMiquel Raynal /* For each MTD device in mtdparts */ 224*a38bf0a9SBoris Brezillon for (; mtdparts[0] != '\0'; mtdparts = mtdparts_next) { 225f87151b3SMiquel Raynal char mtd_name[MTD_NAME_MAX_LEN], *colon; 226f87151b3SMiquel Raynal struct mtd_partition *parts; 2277a13c786SBoris Brezillon unsigned int mtd_name_len; 2287a13c786SBoris Brezillon int nparts, ret; 229f87151b3SMiquel Raynal 230*a38bf0a9SBoris Brezillon mtdparts_next = strchr(mtdparts, ';'); 231*a38bf0a9SBoris Brezillon if (!mtdparts_next) 232*a38bf0a9SBoris Brezillon mtdparts_next = mtdparts + strlen(mtdparts); 233*a38bf0a9SBoris Brezillon else 234*a38bf0a9SBoris Brezillon mtdparts_next++; 235*a38bf0a9SBoris Brezillon 236f87151b3SMiquel Raynal colon = strchr(mtdparts, ':'); 237*a38bf0a9SBoris Brezillon if (colon > mtdparts_next) 238*a38bf0a9SBoris Brezillon colon = NULL; 239*a38bf0a9SBoris Brezillon 240f87151b3SMiquel Raynal if (!colon) { 241f87151b3SMiquel Raynal printf("Wrong mtdparts: %s\n", mtdparts); 242f87151b3SMiquel Raynal return -EINVAL; 243f87151b3SMiquel Raynal } 244f87151b3SMiquel Raynal 2457a13c786SBoris Brezillon mtd_name_len = (unsigned int)(colon - mtdparts); 2467a13c786SBoris Brezillon if (mtd_name_len + 1 > sizeof(mtd_name)) { 2477a13c786SBoris Brezillon printf("MTD name too long: %s\n", mtdparts); 2487a13c786SBoris Brezillon return -EINVAL; 2497a13c786SBoris Brezillon } 2507a13c786SBoris Brezillon 251f87151b3SMiquel Raynal strncpy(mtd_name, mtdparts, mtd_name_len); 252f87151b3SMiquel Raynal mtd_name[mtd_name_len] = '\0'; 253f87151b3SMiquel Raynal /* Move the pointer forward (including the ':') */ 254f87151b3SMiquel Raynal mtdparts += mtd_name_len + 1; 255f87151b3SMiquel Raynal mtd = get_mtd_device_nm(mtd_name); 256f87151b3SMiquel Raynal if (IS_ERR_OR_NULL(mtd)) { 257f87151b3SMiquel Raynal char linux_name[MTD_NAME_MAX_LEN]; 258f87151b3SMiquel Raynal 259f87151b3SMiquel Raynal /* 260f87151b3SMiquel Raynal * The MTD device named "mtd_name" does not exist. Try 261f87151b3SMiquel Raynal * to find a correspondance with an MTD device having 262f87151b3SMiquel Raynal * the same type and number as defined in the mtdids. 263f87151b3SMiquel Raynal */ 264f87151b3SMiquel Raynal debug("No device named %s\n", mtd_name); 265f87151b3SMiquel Raynal ret = mtd_search_alternate_name(mtd_name, linux_name, 266f87151b3SMiquel Raynal MTD_NAME_MAX_LEN); 267f87151b3SMiquel Raynal if (!ret) 268f87151b3SMiquel Raynal mtd = get_mtd_device_nm(linux_name); 269f87151b3SMiquel Raynal 270f87151b3SMiquel Raynal /* 271f87151b3SMiquel Raynal * If no device could be found, move the mtdparts 272f87151b3SMiquel Raynal * pointer forward until the next set of partitions. 273f87151b3SMiquel Raynal */ 274f87151b3SMiquel Raynal if (ret || IS_ERR_OR_NULL(mtd)) { 275f87151b3SMiquel Raynal printf("Could not find a valid device for %s\n", 276f87151b3SMiquel Raynal mtd_name); 277*a38bf0a9SBoris Brezillon mtdparts = mtdparts_next; 278f87151b3SMiquel Raynal continue; 279f87151b3SMiquel Raynal } 280f87151b3SMiquel Raynal } 281f87151b3SMiquel Raynal 282f87151b3SMiquel Raynal /* 283f87151b3SMiquel Raynal * Parse the MTD device partitions. It will update the mtdparts 284f87151b3SMiquel Raynal * pointer, create an array of parts (that must be freed), and 285f87151b3SMiquel Raynal * return the number of partition structures in the array. 286f87151b3SMiquel Raynal */ 287f87151b3SMiquel Raynal ret = mtd_parse_partitions(mtd, &mtdparts, &parts, &nparts); 288f87151b3SMiquel Raynal if (ret) { 289f87151b3SMiquel Raynal printf("Could not parse device %s\n", mtd->name); 290f87151b3SMiquel Raynal put_mtd_device(mtd); 291f87151b3SMiquel Raynal return -EINVAL; 292f87151b3SMiquel Raynal } 293f87151b3SMiquel Raynal 294f87151b3SMiquel Raynal if (!nparts) 295f87151b3SMiquel Raynal continue; 296f87151b3SMiquel Raynal 297f87151b3SMiquel Raynal /* Create the new MTD partitions */ 298f87151b3SMiquel Raynal add_mtd_partitions(mtd, parts, nparts); 299f87151b3SMiquel Raynal 300f87151b3SMiquel Raynal /* Free the structures allocated during the parsing */ 301f87151b3SMiquel Raynal mtd_free_parsed_partitions(parts, nparts); 302f87151b3SMiquel Raynal 303f87151b3SMiquel Raynal put_mtd_device(mtd); 304f87151b3SMiquel Raynal } 305f87151b3SMiquel Raynal 3064c33ae3bSBoris Brezillon /* 3074c33ae3bSBoris Brezillon * Call mtd_dev_list_updated() to clear updates generated by our own 3084c33ae3bSBoris Brezillon * parts registration loop. 3094c33ae3bSBoris Brezillon */ 3104c33ae3bSBoris Brezillon mtd_dev_list_updated(); 3114c33ae3bSBoris Brezillon 312f87151b3SMiquel Raynal return 0; 313f87151b3SMiquel Raynal } 314f87151b3SMiquel Raynal #else 315f87151b3SMiquel Raynal int mtd_probe_devices(void) 316f87151b3SMiquel Raynal { 317f87151b3SMiquel Raynal mtd_probe_uclass_mtd_devs(); 318f87151b3SMiquel Raynal 319f87151b3SMiquel Raynal return 0; 320f87151b3SMiquel Raynal } 321f87151b3SMiquel Raynal #endif /* defined(CONFIG_MTD_PARTITIONS) */ 322f87151b3SMiquel Raynal 32350466819SMiquel Raynal /* Legacy */ 32409c32807SHeiko Schocher 32509c32807SHeiko Schocher static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size, 32609c32807SHeiko Schocher loff_t *maxsize, int devtype) 32709c32807SHeiko Schocher { 32809c32807SHeiko Schocher #ifdef CONFIG_CMD_MTDPARTS 32909c32807SHeiko Schocher struct mtd_device *dev; 33009c32807SHeiko Schocher struct part_info *part; 33109c32807SHeiko Schocher u8 pnum; 33209c32807SHeiko Schocher int ret; 33309c32807SHeiko Schocher 33409c32807SHeiko Schocher ret = mtdparts_init(); 33509c32807SHeiko Schocher if (ret) 33609c32807SHeiko Schocher return ret; 33709c32807SHeiko Schocher 33809c32807SHeiko Schocher ret = find_dev_and_part(partname, &dev, &pnum, &part); 33909c32807SHeiko Schocher if (ret) 34009c32807SHeiko Schocher return ret; 34109c32807SHeiko Schocher 34209c32807SHeiko Schocher if (dev->id->type != devtype) { 34309c32807SHeiko Schocher printf("not same typ %d != %d\n", dev->id->type, devtype); 34409c32807SHeiko Schocher return -1; 34509c32807SHeiko Schocher } 34609c32807SHeiko Schocher 34709c32807SHeiko Schocher *off = part->offset; 34809c32807SHeiko Schocher *size = part->size; 34909c32807SHeiko Schocher *maxsize = part->size; 35009c32807SHeiko Schocher *idx = dev->id->num; 35109c32807SHeiko Schocher 35209c32807SHeiko Schocher return 0; 35309c32807SHeiko Schocher #else 35410b69712SMaxime Ripard puts("mtdparts support missing.\n"); 35509c32807SHeiko Schocher return -1; 35609c32807SHeiko Schocher #endif 35709c32807SHeiko Schocher } 35809c32807SHeiko Schocher 35909c32807SHeiko Schocher int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size, 360f18d1116SMasahiro Yamada loff_t *maxsize, int devtype, uint64_t chipsize) 36109c32807SHeiko Schocher { 36209c32807SHeiko Schocher if (!str2off(arg, off)) 36309c32807SHeiko Schocher return get_part(arg, idx, off, size, maxsize, devtype); 36409c32807SHeiko Schocher 36509c32807SHeiko Schocher if (*off >= chipsize) { 36609c32807SHeiko Schocher puts("Offset exceeds device limit\n"); 36709c32807SHeiko Schocher return -1; 36809c32807SHeiko Schocher } 36909c32807SHeiko Schocher 37009c32807SHeiko Schocher *maxsize = chipsize - *off; 37109c32807SHeiko Schocher *size = *maxsize; 37209c32807SHeiko Schocher return 0; 37309c32807SHeiko Schocher } 37409c32807SHeiko Schocher 37509c32807SHeiko Schocher int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, 376f18d1116SMasahiro Yamada loff_t *size, loff_t *maxsize, int devtype, 377f18d1116SMasahiro Yamada uint64_t chipsize) 37809c32807SHeiko Schocher { 37909c32807SHeiko Schocher int ret; 38009c32807SHeiko Schocher 38109c32807SHeiko Schocher if (argc == 0) { 38209c32807SHeiko Schocher *off = 0; 38309c32807SHeiko Schocher *size = chipsize; 38409c32807SHeiko Schocher *maxsize = *size; 38509c32807SHeiko Schocher goto print; 38609c32807SHeiko Schocher } 38709c32807SHeiko Schocher 38809c32807SHeiko Schocher ret = mtd_arg_off(argv[0], idx, off, size, maxsize, devtype, 38909c32807SHeiko Schocher chipsize); 39009c32807SHeiko Schocher if (ret) 39109c32807SHeiko Schocher return ret; 39209c32807SHeiko Schocher 39309c32807SHeiko Schocher if (argc == 1) 39409c32807SHeiko Schocher goto print; 39509c32807SHeiko Schocher 39609c32807SHeiko Schocher if (!str2off(argv[1], size)) { 39709c32807SHeiko Schocher printf("'%s' is not a number\n", argv[1]); 39809c32807SHeiko Schocher return -1; 39909c32807SHeiko Schocher } 40009c32807SHeiko Schocher 40109c32807SHeiko Schocher if (*size > *maxsize) { 40209c32807SHeiko Schocher puts("Size exceeds partition or device limit\n"); 40309c32807SHeiko Schocher return -1; 40409c32807SHeiko Schocher } 40509c32807SHeiko Schocher 40609c32807SHeiko Schocher print: 40709c32807SHeiko Schocher printf("device %d ", *idx); 40809c32807SHeiko Schocher if (*size == chipsize) 40909c32807SHeiko Schocher puts("whole chip\n"); 41009c32807SHeiko Schocher else 41109c32807SHeiko Schocher printf("offset 0x%llx, size 0x%llx\n", 41209c32807SHeiko Schocher (unsigned long long)*off, (unsigned long long)*size); 41309c32807SHeiko Schocher return 0; 41409c32807SHeiko Schocher } 415