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 17*4ac4e964SBoris Brezillon void board_mtdparts_default(const char **mtdids, const char **mtdparts); 18*4ac4e964SBoris Brezillon 19*4ac4e964SBoris Brezillon static const char *get_mtdids(void) 20*4ac4e964SBoris Brezillon { 21*4ac4e964SBoris Brezillon __maybe_unused const char *mtdparts = NULL; 22*4ac4e964SBoris Brezillon const char *mtdids = env_get("mtdids"); 23*4ac4e964SBoris Brezillon 24*4ac4e964SBoris Brezillon if (mtdids) 25*4ac4e964SBoris Brezillon return mtdids; 26*4ac4e964SBoris Brezillon 27*4ac4e964SBoris Brezillon #if defined(CONFIG_SYS_MTDPARTS_RUNTIME) 28*4ac4e964SBoris Brezillon board_mtdparts_default(&mtdids, &mtdparts); 29*4ac4e964SBoris Brezillon #elif defined(MTDIDS_DEFAULT) 30*4ac4e964SBoris Brezillon mtdids = MTDIDS_DEFAULT; 31*4ac4e964SBoris Brezillon #elif defined(CONFIG_MTDIDS_DEFAULT) 32*4ac4e964SBoris Brezillon mtdids = CONFIG_MTDIDS_DEFAULT; 33*4ac4e964SBoris Brezillon #endif 34*4ac4e964SBoris Brezillon 35*4ac4e964SBoris Brezillon if (mtdids) 36*4ac4e964SBoris Brezillon env_set("mtdids", mtdids); 37*4ac4e964SBoris Brezillon 38*4ac4e964SBoris Brezillon return mtdids; 39*4ac4e964SBoris 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 61*4ac4e964SBoris 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(); 159f87151b3SMiquel Raynal bool remaining_partitions = true; 160f87151b3SMiquel Raynal struct mtd_info *mtd; 161f87151b3SMiquel Raynal 162f87151b3SMiquel Raynal mtd_probe_uclass_mtd_devs(); 163f87151b3SMiquel Raynal 1644c33ae3bSBoris Brezillon /* 1654c33ae3bSBoris Brezillon * Check if mtdparts/mtdids changed or if the MTD dev list was updated 1664c33ae3bSBoris Brezillon * since last call, otherwise: exit 1674c33ae3bSBoris Brezillon */ 1684696f08fSAdam Ford if ((!mtdparts && !old_mtdparts && !mtdids && !old_mtdids) || 1694696f08fSAdam Ford (mtdparts && old_mtdparts && mtdids && old_mtdids && 1704c33ae3bSBoris Brezillon !mtd_dev_list_updated() && 1714696f08fSAdam Ford !strcmp(mtdparts, old_mtdparts) && 1724696f08fSAdam Ford !strcmp(mtdids, old_mtdids))) 173f87151b3SMiquel Raynal return 0; 174f87151b3SMiquel Raynal 175f87151b3SMiquel Raynal /* Update the local copy of mtdparts */ 176f87151b3SMiquel Raynal free(old_mtdparts); 177f87151b3SMiquel Raynal free(old_mtdids); 178f87151b3SMiquel Raynal old_mtdparts = strdup(mtdparts); 179f87151b3SMiquel Raynal old_mtdids = strdup(mtdids); 180f87151b3SMiquel Raynal 181f87151b3SMiquel Raynal /* If at least one partition is still in use, do not delete anything */ 182f87151b3SMiquel Raynal mtd_for_each_device(mtd) { 183f87151b3SMiquel Raynal if (mtd->usecount) { 184f87151b3SMiquel Raynal printf("Partition \"%s\" already in use, aborting\n", 185f87151b3SMiquel Raynal mtd->name); 186f87151b3SMiquel Raynal return -EACCES; 187f87151b3SMiquel Raynal } 188f87151b3SMiquel Raynal } 189f87151b3SMiquel Raynal 190f87151b3SMiquel Raynal /* 191f87151b3SMiquel Raynal * Everything looks clear, remove all partitions. It is not safe to 192f87151b3SMiquel Raynal * remove entries from the mtd_for_each_device loop as it uses idr 193f87151b3SMiquel Raynal * indexes and the partitions removal is done in bulk (all partitions of 194f87151b3SMiquel Raynal * one device at the same time), so break and iterate from start each 195f87151b3SMiquel Raynal * time a new partition is found and deleted. 196f87151b3SMiquel Raynal */ 197f87151b3SMiquel Raynal while (remaining_partitions) { 198f87151b3SMiquel Raynal remaining_partitions = false; 199f87151b3SMiquel Raynal mtd_for_each_device(mtd) { 200f87151b3SMiquel Raynal if (!mtd_is_partition(mtd) && mtd_has_partitions(mtd)) { 201f87151b3SMiquel Raynal del_mtd_partitions(mtd); 202f87151b3SMiquel Raynal remaining_partitions = true; 203f87151b3SMiquel Raynal break; 204f87151b3SMiquel Raynal } 205f87151b3SMiquel Raynal } 206f87151b3SMiquel Raynal } 207f87151b3SMiquel Raynal 2084c33ae3bSBoris Brezillon /* 2094c33ae3bSBoris Brezillon * Call mtd_dev_list_updated() to clear updates generated by our own 2104c33ae3bSBoris Brezillon * parts removal loop. 2114c33ae3bSBoris Brezillon */ 2124c33ae3bSBoris Brezillon mtd_dev_list_updated(); 2134c33ae3bSBoris Brezillon 2144696f08fSAdam Ford /* If either mtdparts or mtdids is empty, then exit */ 2154696f08fSAdam Ford if (!mtdparts || !mtdids) 2164696f08fSAdam Ford return 0; 2174696f08fSAdam Ford 218f87151b3SMiquel Raynal /* Start the parsing by ignoring the extra 'mtdparts=' prefix, if any */ 219f87151b3SMiquel Raynal if (strstr(mtdparts, "mtdparts=")) 220f87151b3SMiquel Raynal mtdparts += 9; 221f87151b3SMiquel Raynal 222f87151b3SMiquel Raynal /* For each MTD device in mtdparts */ 223f87151b3SMiquel Raynal while (mtdparts[0] != '\0') { 224f87151b3SMiquel Raynal char mtd_name[MTD_NAME_MAX_LEN], *colon; 225f87151b3SMiquel Raynal struct mtd_partition *parts; 226f87151b3SMiquel Raynal int mtd_name_len, nparts; 227f87151b3SMiquel Raynal int ret; 228f87151b3SMiquel Raynal 229f87151b3SMiquel Raynal colon = strchr(mtdparts, ':'); 230f87151b3SMiquel Raynal if (!colon) { 231f87151b3SMiquel Raynal printf("Wrong mtdparts: %s\n", mtdparts); 232f87151b3SMiquel Raynal return -EINVAL; 233f87151b3SMiquel Raynal } 234f87151b3SMiquel Raynal 235f87151b3SMiquel Raynal mtd_name_len = colon - mtdparts; 236f87151b3SMiquel Raynal strncpy(mtd_name, mtdparts, mtd_name_len); 237f87151b3SMiquel Raynal mtd_name[mtd_name_len] = '\0'; 238f87151b3SMiquel Raynal /* Move the pointer forward (including the ':') */ 239f87151b3SMiquel Raynal mtdparts += mtd_name_len + 1; 240f87151b3SMiquel Raynal mtd = get_mtd_device_nm(mtd_name); 241f87151b3SMiquel Raynal if (IS_ERR_OR_NULL(mtd)) { 242f87151b3SMiquel Raynal char linux_name[MTD_NAME_MAX_LEN]; 243f87151b3SMiquel Raynal 244f87151b3SMiquel Raynal /* 245f87151b3SMiquel Raynal * The MTD device named "mtd_name" does not exist. Try 246f87151b3SMiquel Raynal * to find a correspondance with an MTD device having 247f87151b3SMiquel Raynal * the same type and number as defined in the mtdids. 248f87151b3SMiquel Raynal */ 249f87151b3SMiquel Raynal debug("No device named %s\n", mtd_name); 250f87151b3SMiquel Raynal ret = mtd_search_alternate_name(mtd_name, linux_name, 251f87151b3SMiquel Raynal MTD_NAME_MAX_LEN); 252f87151b3SMiquel Raynal if (!ret) 253f87151b3SMiquel Raynal mtd = get_mtd_device_nm(linux_name); 254f87151b3SMiquel Raynal 255f87151b3SMiquel Raynal /* 256f87151b3SMiquel Raynal * If no device could be found, move the mtdparts 257f87151b3SMiquel Raynal * pointer forward until the next set of partitions. 258f87151b3SMiquel Raynal */ 259f87151b3SMiquel Raynal if (ret || IS_ERR_OR_NULL(mtd)) { 260f87151b3SMiquel Raynal printf("Could not find a valid device for %s\n", 261f87151b3SMiquel Raynal mtd_name); 262f87151b3SMiquel Raynal mtdparts = strchr(mtdparts, ';'); 263f87151b3SMiquel Raynal if (mtdparts) 264f87151b3SMiquel Raynal mtdparts++; 265f87151b3SMiquel Raynal 266f87151b3SMiquel Raynal continue; 267f87151b3SMiquel Raynal } 268f87151b3SMiquel Raynal } 269f87151b3SMiquel Raynal 270f87151b3SMiquel Raynal /* 271f87151b3SMiquel Raynal * Parse the MTD device partitions. It will update the mtdparts 272f87151b3SMiquel Raynal * pointer, create an array of parts (that must be freed), and 273f87151b3SMiquel Raynal * return the number of partition structures in the array. 274f87151b3SMiquel Raynal */ 275f87151b3SMiquel Raynal ret = mtd_parse_partitions(mtd, &mtdparts, &parts, &nparts); 276f87151b3SMiquel Raynal if (ret) { 277f87151b3SMiquel Raynal printf("Could not parse device %s\n", mtd->name); 278f87151b3SMiquel Raynal put_mtd_device(mtd); 279f87151b3SMiquel Raynal return -EINVAL; 280f87151b3SMiquel Raynal } 281f87151b3SMiquel Raynal 282f87151b3SMiquel Raynal if (!nparts) 283f87151b3SMiquel Raynal continue; 284f87151b3SMiquel Raynal 285f87151b3SMiquel Raynal /* Create the new MTD partitions */ 286f87151b3SMiquel Raynal add_mtd_partitions(mtd, parts, nparts); 287f87151b3SMiquel Raynal 288f87151b3SMiquel Raynal /* Free the structures allocated during the parsing */ 289f87151b3SMiquel Raynal mtd_free_parsed_partitions(parts, nparts); 290f87151b3SMiquel Raynal 291f87151b3SMiquel Raynal put_mtd_device(mtd); 292f87151b3SMiquel Raynal } 293f87151b3SMiquel Raynal 2944c33ae3bSBoris Brezillon /* 2954c33ae3bSBoris Brezillon * Call mtd_dev_list_updated() to clear updates generated by our own 2964c33ae3bSBoris Brezillon * parts registration loop. 2974c33ae3bSBoris Brezillon */ 2984c33ae3bSBoris Brezillon mtd_dev_list_updated(); 2994c33ae3bSBoris Brezillon 300f87151b3SMiquel Raynal return 0; 301f87151b3SMiquel Raynal } 302f87151b3SMiquel Raynal #else 303f87151b3SMiquel Raynal int mtd_probe_devices(void) 304f87151b3SMiquel Raynal { 305f87151b3SMiquel Raynal mtd_probe_uclass_mtd_devs(); 306f87151b3SMiquel Raynal 307f87151b3SMiquel Raynal return 0; 308f87151b3SMiquel Raynal } 309f87151b3SMiquel Raynal #endif /* defined(CONFIG_MTD_PARTITIONS) */ 310f87151b3SMiquel Raynal 31150466819SMiquel Raynal /* Legacy */ 31209c32807SHeiko Schocher 31309c32807SHeiko Schocher static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size, 31409c32807SHeiko Schocher loff_t *maxsize, int devtype) 31509c32807SHeiko Schocher { 31609c32807SHeiko Schocher #ifdef CONFIG_CMD_MTDPARTS 31709c32807SHeiko Schocher struct mtd_device *dev; 31809c32807SHeiko Schocher struct part_info *part; 31909c32807SHeiko Schocher u8 pnum; 32009c32807SHeiko Schocher int ret; 32109c32807SHeiko Schocher 32209c32807SHeiko Schocher ret = mtdparts_init(); 32309c32807SHeiko Schocher if (ret) 32409c32807SHeiko Schocher return ret; 32509c32807SHeiko Schocher 32609c32807SHeiko Schocher ret = find_dev_and_part(partname, &dev, &pnum, &part); 32709c32807SHeiko Schocher if (ret) 32809c32807SHeiko Schocher return ret; 32909c32807SHeiko Schocher 33009c32807SHeiko Schocher if (dev->id->type != devtype) { 33109c32807SHeiko Schocher printf("not same typ %d != %d\n", dev->id->type, devtype); 33209c32807SHeiko Schocher return -1; 33309c32807SHeiko Schocher } 33409c32807SHeiko Schocher 33509c32807SHeiko Schocher *off = part->offset; 33609c32807SHeiko Schocher *size = part->size; 33709c32807SHeiko Schocher *maxsize = part->size; 33809c32807SHeiko Schocher *idx = dev->id->num; 33909c32807SHeiko Schocher 34009c32807SHeiko Schocher return 0; 34109c32807SHeiko Schocher #else 34210b69712SMaxime Ripard puts("mtdparts support missing.\n"); 34309c32807SHeiko Schocher return -1; 34409c32807SHeiko Schocher #endif 34509c32807SHeiko Schocher } 34609c32807SHeiko Schocher 34709c32807SHeiko Schocher int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size, 348f18d1116SMasahiro Yamada loff_t *maxsize, int devtype, uint64_t chipsize) 34909c32807SHeiko Schocher { 35009c32807SHeiko Schocher if (!str2off(arg, off)) 35109c32807SHeiko Schocher return get_part(arg, idx, off, size, maxsize, devtype); 35209c32807SHeiko Schocher 35309c32807SHeiko Schocher if (*off >= chipsize) { 35409c32807SHeiko Schocher puts("Offset exceeds device limit\n"); 35509c32807SHeiko Schocher return -1; 35609c32807SHeiko Schocher } 35709c32807SHeiko Schocher 35809c32807SHeiko Schocher *maxsize = chipsize - *off; 35909c32807SHeiko Schocher *size = *maxsize; 36009c32807SHeiko Schocher return 0; 36109c32807SHeiko Schocher } 36209c32807SHeiko Schocher 36309c32807SHeiko Schocher int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, 364f18d1116SMasahiro Yamada loff_t *size, loff_t *maxsize, int devtype, 365f18d1116SMasahiro Yamada uint64_t chipsize) 36609c32807SHeiko Schocher { 36709c32807SHeiko Schocher int ret; 36809c32807SHeiko Schocher 36909c32807SHeiko Schocher if (argc == 0) { 37009c32807SHeiko Schocher *off = 0; 37109c32807SHeiko Schocher *size = chipsize; 37209c32807SHeiko Schocher *maxsize = *size; 37309c32807SHeiko Schocher goto print; 37409c32807SHeiko Schocher } 37509c32807SHeiko Schocher 37609c32807SHeiko Schocher ret = mtd_arg_off(argv[0], idx, off, size, maxsize, devtype, 37709c32807SHeiko Schocher chipsize); 37809c32807SHeiko Schocher if (ret) 37909c32807SHeiko Schocher return ret; 38009c32807SHeiko Schocher 38109c32807SHeiko Schocher if (argc == 1) 38209c32807SHeiko Schocher goto print; 38309c32807SHeiko Schocher 38409c32807SHeiko Schocher if (!str2off(argv[1], size)) { 38509c32807SHeiko Schocher printf("'%s' is not a number\n", argv[1]); 38609c32807SHeiko Schocher return -1; 38709c32807SHeiko Schocher } 38809c32807SHeiko Schocher 38909c32807SHeiko Schocher if (*size > *maxsize) { 39009c32807SHeiko Schocher puts("Size exceeds partition or device limit\n"); 39109c32807SHeiko Schocher return -1; 39209c32807SHeiko Schocher } 39309c32807SHeiko Schocher 39409c32807SHeiko Schocher print: 39509c32807SHeiko Schocher printf("device %d ", *idx); 39609c32807SHeiko Schocher if (*size == chipsize) 39709c32807SHeiko Schocher puts("whole chip\n"); 39809c32807SHeiko Schocher else 39909c32807SHeiko Schocher printf("offset 0x%llx, size 0x%llx\n", 40009c32807SHeiko Schocher (unsigned long long)*off, (unsigned long long)*size); 40109c32807SHeiko Schocher return 0; 40209c32807SHeiko Schocher } 403