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 1750466819SMiquel Raynal 1850466819SMiquel Raynal /** 1950466819SMiquel Raynal * mtd_search_alternate_name - Search an alternate name for @mtdname thanks to 2050466819SMiquel Raynal * the mtdids legacy environment variable. 2150466819SMiquel Raynal * 2250466819SMiquel Raynal * The mtdids string is a list of comma-separated 'dev_id=mtd_id' tupples. 2350466819SMiquel Raynal * Check if one of the mtd_id matches mtdname, in this case save dev_id in 2450466819SMiquel Raynal * altname. 2550466819SMiquel Raynal * 2650466819SMiquel Raynal * @mtdname: Current MTD device name 2750466819SMiquel Raynal * @altname: Alternate name to return 2850466819SMiquel Raynal * @max_len: Length of the alternate name buffer 2950466819SMiquel Raynal * 3050466819SMiquel Raynal * @return 0 on success, an error otherwise. 3150466819SMiquel Raynal */ 3250466819SMiquel Raynal int mtd_search_alternate_name(const char *mtdname, char *altname, 3350466819SMiquel Raynal unsigned int max_len) 3450466819SMiquel Raynal { 3550466819SMiquel Raynal const char *mtdids, *equal, *comma, *dev_id, *mtd_id; 3650466819SMiquel Raynal int dev_id_len, mtd_id_len; 3750466819SMiquel Raynal 3850466819SMiquel Raynal mtdids = env_get("mtdids"); 3950466819SMiquel Raynal if (!mtdids) 4050466819SMiquel Raynal return -EINVAL; 4150466819SMiquel Raynal 4250466819SMiquel Raynal do { 4350466819SMiquel Raynal /* Find the '=' sign */ 4450466819SMiquel Raynal dev_id = mtdids; 4550466819SMiquel Raynal equal = strchr(dev_id, '='); 4650466819SMiquel Raynal if (!equal) 4750466819SMiquel Raynal break; 4850466819SMiquel Raynal dev_id_len = equal - mtdids; 4950466819SMiquel Raynal mtd_id = equal + 1; 5050466819SMiquel Raynal 5150466819SMiquel Raynal /* Find the end of the tupple */ 5250466819SMiquel Raynal comma = strchr(mtdids, ','); 5350466819SMiquel Raynal if (comma) 5450466819SMiquel Raynal mtd_id_len = comma - mtd_id; 5550466819SMiquel Raynal else 5650466819SMiquel Raynal mtd_id_len = &mtdids[strlen(mtdids)] - mtd_id + 1; 5750466819SMiquel Raynal 5850466819SMiquel Raynal if (!dev_id_len || !mtd_id_len) 5950466819SMiquel Raynal return -EINVAL; 6050466819SMiquel Raynal 6150466819SMiquel Raynal if (dev_id_len + 1 > max_len) 6250466819SMiquel Raynal continue; 6350466819SMiquel Raynal 6450466819SMiquel Raynal /* Compare the name we search with the current mtd_id */ 6550466819SMiquel Raynal if (!strncmp(mtdname, mtd_id, mtd_id_len)) { 6650466819SMiquel Raynal strncpy(altname, dev_id, dev_id_len); 6750466819SMiquel Raynal altname[dev_id_len] = 0; 6850466819SMiquel Raynal 6950466819SMiquel Raynal return 0; 7050466819SMiquel Raynal } 7150466819SMiquel Raynal 7250466819SMiquel Raynal /* Go to the next tupple */ 7350466819SMiquel Raynal mtdids = comma + 1; 7450466819SMiquel Raynal } while (comma); 7550466819SMiquel Raynal 7650466819SMiquel Raynal return -EINVAL; 7750466819SMiquel Raynal } 7850466819SMiquel Raynal 79f87151b3SMiquel Raynal #if IS_ENABLED(CONFIG_MTD) 80f87151b3SMiquel Raynal static void mtd_probe_uclass_mtd_devs(void) 81f87151b3SMiquel Raynal { 82f87151b3SMiquel Raynal struct udevice *dev; 83f87151b3SMiquel Raynal int idx = 0; 84f87151b3SMiquel Raynal 85f87151b3SMiquel Raynal /* Probe devices with DM compliant drivers */ 86f87151b3SMiquel Raynal while (!uclass_find_device(UCLASS_MTD, idx, &dev) && dev) { 87f87151b3SMiquel Raynal mtd_probe(dev); 88f87151b3SMiquel Raynal idx++; 89f87151b3SMiquel Raynal } 90f87151b3SMiquel Raynal } 91f87151b3SMiquel Raynal #else 92f87151b3SMiquel Raynal static void mtd_probe_uclass_mtd_devs(void) { } 93f87151b3SMiquel Raynal #endif 94f87151b3SMiquel Raynal 95f87151b3SMiquel Raynal #if defined(CONFIG_MTD_PARTITIONS) 96*b95c8f6cSBoris Brezillon extern void board_mtdparts_default(const char **mtdids, 97*b95c8f6cSBoris Brezillon const char **mtdparts); 98*b95c8f6cSBoris Brezillon 99*b95c8f6cSBoris Brezillon static const char *get_mtdids(void) 100*b95c8f6cSBoris Brezillon { 101*b95c8f6cSBoris Brezillon __maybe_unused const char *mtdparts = NULL; 102*b95c8f6cSBoris Brezillon const char *mtdids = env_get("mtdids"); 103*b95c8f6cSBoris Brezillon 104*b95c8f6cSBoris Brezillon if (mtdids) 105*b95c8f6cSBoris Brezillon return mtdids; 106*b95c8f6cSBoris Brezillon 107*b95c8f6cSBoris Brezillon #if defined(CONFIG_SYS_MTDPARTS_RUNTIME) 108*b95c8f6cSBoris Brezillon board_mtdparts_default(&mtdids, &mtdparts); 109*b95c8f6cSBoris Brezillon #elif defined(MTDIDS_DEFAULT) 110*b95c8f6cSBoris Brezillon mtdids = MTDIDS_DEFAULT; 111*b95c8f6cSBoris Brezillon #elif defined(CONFIG_MTDIDS_DEFAULT) 112*b95c8f6cSBoris Brezillon mtdids = CONFIG_MTDIDS_DEFAULT; 113*b95c8f6cSBoris Brezillon #endif 114*b95c8f6cSBoris Brezillon 115*b95c8f6cSBoris Brezillon if (mtdids) 116*b95c8f6cSBoris Brezillon env_set("mtdids", mtdids); 117*b95c8f6cSBoris Brezillon 118*b95c8f6cSBoris Brezillon return mtdids; 119*b95c8f6cSBoris Brezillon } 120*b95c8f6cSBoris Brezillon 121*b95c8f6cSBoris Brezillon #define MTDPARTS_MAXLEN 512 122*b95c8f6cSBoris Brezillon 123*b95c8f6cSBoris Brezillon static const char *get_mtdparts(void) 124*b95c8f6cSBoris Brezillon { 125*b95c8f6cSBoris Brezillon __maybe_unused const char *mtdids = NULL; 126*b95c8f6cSBoris Brezillon static char tmp_parts[MTDPARTS_MAXLEN]; 127*b95c8f6cSBoris Brezillon static bool use_defaults = true; 128*b95c8f6cSBoris Brezillon const char *mtdparts = NULL; 129*b95c8f6cSBoris Brezillon 130*b95c8f6cSBoris Brezillon if (gd->flags & GD_FLG_ENV_READY) 131*b95c8f6cSBoris Brezillon mtdparts = env_get("mtdparts"); 132*b95c8f6cSBoris Brezillon else if (env_get_f("mtdparts", tmp_parts, sizeof(tmp_parts)) != -1) 133*b95c8f6cSBoris Brezillon mtdparts = tmp_parts; 134*b95c8f6cSBoris Brezillon 135*b95c8f6cSBoris Brezillon if (mtdparts || !use_defaults) 136*b95c8f6cSBoris Brezillon return mtdparts; 137*b95c8f6cSBoris Brezillon 138*b95c8f6cSBoris Brezillon #if defined(CONFIG_SYS_MTDPARTS_RUNTIME) 139*b95c8f6cSBoris Brezillon board_mtdparts_default(&mtdids, &mtdparts); 140*b95c8f6cSBoris Brezillon #elif defined(MTDPARTS_DEFAULT) 141*b95c8f6cSBoris Brezillon mtdparts = MTDPARTS_DEFAULT; 142*b95c8f6cSBoris Brezillon #elif defined(CONFIG_MTDPARTS_DEFAULT) 143*b95c8f6cSBoris Brezillon mtdparts = CONFIG_MTDPARTS_DEFAULT; 144*b95c8f6cSBoris Brezillon #endif 145*b95c8f6cSBoris Brezillon 146*b95c8f6cSBoris Brezillon if (mtdparts) 147*b95c8f6cSBoris Brezillon env_set("mtdparts", mtdparts); 148*b95c8f6cSBoris Brezillon 149*b95c8f6cSBoris Brezillon use_defaults = false; 150*b95c8f6cSBoris Brezillon 151*b95c8f6cSBoris Brezillon return mtdparts; 152*b95c8f6cSBoris Brezillon } 153*b95c8f6cSBoris Brezillon 154f87151b3SMiquel Raynal int mtd_probe_devices(void) 155f87151b3SMiquel Raynal { 156f87151b3SMiquel Raynal static char *old_mtdparts; 157f87151b3SMiquel Raynal static char *old_mtdids; 158*b95c8f6cSBoris Brezillon const char *mtdparts = get_mtdparts(); 159*b95c8f6cSBoris Brezillon const char *mtdids = get_mtdids(); 160f87151b3SMiquel Raynal bool remaining_partitions = true; 161f87151b3SMiquel Raynal struct mtd_info *mtd; 162f87151b3SMiquel Raynal 163f87151b3SMiquel Raynal mtd_probe_uclass_mtd_devs(); 164f87151b3SMiquel Raynal 165f87151b3SMiquel Raynal /* Check if mtdparts/mtdids changed since last call, otherwise: exit */ 1664696f08fSAdam Ford if ((!mtdparts && !old_mtdparts && !mtdids && !old_mtdids) || 1674696f08fSAdam Ford (mtdparts && old_mtdparts && mtdids && old_mtdids && 1684696f08fSAdam Ford !strcmp(mtdparts, old_mtdparts) && 1694696f08fSAdam Ford !strcmp(mtdids, old_mtdids))) 170f87151b3SMiquel Raynal return 0; 171f87151b3SMiquel Raynal 172f87151b3SMiquel Raynal /* Update the local copy of mtdparts */ 173f87151b3SMiquel Raynal free(old_mtdparts); 174f87151b3SMiquel Raynal free(old_mtdids); 175f87151b3SMiquel Raynal old_mtdparts = strdup(mtdparts); 176f87151b3SMiquel Raynal old_mtdids = strdup(mtdids); 177f87151b3SMiquel Raynal 178f87151b3SMiquel Raynal /* If at least one partition is still in use, do not delete anything */ 179f87151b3SMiquel Raynal mtd_for_each_device(mtd) { 180f87151b3SMiquel Raynal if (mtd->usecount) { 181f87151b3SMiquel Raynal printf("Partition \"%s\" already in use, aborting\n", 182f87151b3SMiquel Raynal mtd->name); 183f87151b3SMiquel Raynal return -EACCES; 184f87151b3SMiquel Raynal } 185f87151b3SMiquel Raynal } 186f87151b3SMiquel Raynal 187f87151b3SMiquel Raynal /* 188f87151b3SMiquel Raynal * Everything looks clear, remove all partitions. It is not safe to 189f87151b3SMiquel Raynal * remove entries from the mtd_for_each_device loop as it uses idr 190f87151b3SMiquel Raynal * indexes and the partitions removal is done in bulk (all partitions of 191f87151b3SMiquel Raynal * one device at the same time), so break and iterate from start each 192f87151b3SMiquel Raynal * time a new partition is found and deleted. 193f87151b3SMiquel Raynal */ 194f87151b3SMiquel Raynal while (remaining_partitions) { 195f87151b3SMiquel Raynal remaining_partitions = false; 196f87151b3SMiquel Raynal mtd_for_each_device(mtd) { 197f87151b3SMiquel Raynal if (!mtd_is_partition(mtd) && mtd_has_partitions(mtd)) { 198f87151b3SMiquel Raynal del_mtd_partitions(mtd); 199f87151b3SMiquel Raynal remaining_partitions = true; 200f87151b3SMiquel Raynal break; 201f87151b3SMiquel Raynal } 202f87151b3SMiquel Raynal } 203f87151b3SMiquel Raynal } 204f87151b3SMiquel Raynal 2054696f08fSAdam Ford /* If either mtdparts or mtdids is empty, then exit */ 2064696f08fSAdam Ford if (!mtdparts || !mtdids) 2074696f08fSAdam Ford return 0; 2084696f08fSAdam Ford 209f87151b3SMiquel Raynal /* Start the parsing by ignoring the extra 'mtdparts=' prefix, if any */ 210f87151b3SMiquel Raynal if (strstr(mtdparts, "mtdparts=")) 211f87151b3SMiquel Raynal mtdparts += 9; 212f87151b3SMiquel Raynal 213f87151b3SMiquel Raynal /* For each MTD device in mtdparts */ 214f87151b3SMiquel Raynal while (mtdparts[0] != '\0') { 215f87151b3SMiquel Raynal char mtd_name[MTD_NAME_MAX_LEN], *colon; 216f87151b3SMiquel Raynal struct mtd_partition *parts; 217f87151b3SMiquel Raynal int mtd_name_len, nparts; 218f87151b3SMiquel Raynal int ret; 219f87151b3SMiquel Raynal 220f87151b3SMiquel Raynal colon = strchr(mtdparts, ':'); 221f87151b3SMiquel Raynal if (!colon) { 222f87151b3SMiquel Raynal printf("Wrong mtdparts: %s\n", mtdparts); 223f87151b3SMiquel Raynal return -EINVAL; 224f87151b3SMiquel Raynal } 225f87151b3SMiquel Raynal 226f87151b3SMiquel Raynal mtd_name_len = colon - mtdparts; 227f87151b3SMiquel Raynal strncpy(mtd_name, mtdparts, mtd_name_len); 228f87151b3SMiquel Raynal mtd_name[mtd_name_len] = '\0'; 229f87151b3SMiquel Raynal /* Move the pointer forward (including the ':') */ 230f87151b3SMiquel Raynal mtdparts += mtd_name_len + 1; 231f87151b3SMiquel Raynal mtd = get_mtd_device_nm(mtd_name); 232f87151b3SMiquel Raynal if (IS_ERR_OR_NULL(mtd)) { 233f87151b3SMiquel Raynal char linux_name[MTD_NAME_MAX_LEN]; 234f87151b3SMiquel Raynal 235f87151b3SMiquel Raynal /* 236f87151b3SMiquel Raynal * The MTD device named "mtd_name" does not exist. Try 237f87151b3SMiquel Raynal * to find a correspondance with an MTD device having 238f87151b3SMiquel Raynal * the same type and number as defined in the mtdids. 239f87151b3SMiquel Raynal */ 240f87151b3SMiquel Raynal debug("No device named %s\n", mtd_name); 241f87151b3SMiquel Raynal ret = mtd_search_alternate_name(mtd_name, linux_name, 242f87151b3SMiquel Raynal MTD_NAME_MAX_LEN); 243f87151b3SMiquel Raynal if (!ret) 244f87151b3SMiquel Raynal mtd = get_mtd_device_nm(linux_name); 245f87151b3SMiquel Raynal 246f87151b3SMiquel Raynal /* 247f87151b3SMiquel Raynal * If no device could be found, move the mtdparts 248f87151b3SMiquel Raynal * pointer forward until the next set of partitions. 249f87151b3SMiquel Raynal */ 250f87151b3SMiquel Raynal if (ret || IS_ERR_OR_NULL(mtd)) { 251f87151b3SMiquel Raynal printf("Could not find a valid device for %s\n", 252f87151b3SMiquel Raynal mtd_name); 253f87151b3SMiquel Raynal mtdparts = strchr(mtdparts, ';'); 254f87151b3SMiquel Raynal if (mtdparts) 255f87151b3SMiquel Raynal mtdparts++; 256f87151b3SMiquel Raynal 257f87151b3SMiquel Raynal continue; 258f87151b3SMiquel Raynal } 259f87151b3SMiquel Raynal } 260f87151b3SMiquel Raynal 261f87151b3SMiquel Raynal /* 262f87151b3SMiquel Raynal * Parse the MTD device partitions. It will update the mtdparts 263f87151b3SMiquel Raynal * pointer, create an array of parts (that must be freed), and 264f87151b3SMiquel Raynal * return the number of partition structures in the array. 265f87151b3SMiquel Raynal */ 266f87151b3SMiquel Raynal ret = mtd_parse_partitions(mtd, &mtdparts, &parts, &nparts); 267f87151b3SMiquel Raynal if (ret) { 268f87151b3SMiquel Raynal printf("Could not parse device %s\n", mtd->name); 269f87151b3SMiquel Raynal put_mtd_device(mtd); 270f87151b3SMiquel Raynal return -EINVAL; 271f87151b3SMiquel Raynal } 272f87151b3SMiquel Raynal 273f87151b3SMiquel Raynal if (!nparts) 274f87151b3SMiquel Raynal continue; 275f87151b3SMiquel Raynal 276f87151b3SMiquel Raynal /* Create the new MTD partitions */ 277f87151b3SMiquel Raynal add_mtd_partitions(mtd, parts, nparts); 278f87151b3SMiquel Raynal 279f87151b3SMiquel Raynal /* Free the structures allocated during the parsing */ 280f87151b3SMiquel Raynal mtd_free_parsed_partitions(parts, nparts); 281f87151b3SMiquel Raynal 282f87151b3SMiquel Raynal put_mtd_device(mtd); 283f87151b3SMiquel Raynal } 284f87151b3SMiquel Raynal 285f87151b3SMiquel Raynal return 0; 286f87151b3SMiquel Raynal } 287f87151b3SMiquel Raynal #else 288f87151b3SMiquel Raynal int mtd_probe_devices(void) 289f87151b3SMiquel Raynal { 290f87151b3SMiquel Raynal mtd_probe_uclass_mtd_devs(); 291f87151b3SMiquel Raynal 292f87151b3SMiquel Raynal return 0; 293f87151b3SMiquel Raynal } 294f87151b3SMiquel Raynal #endif /* defined(CONFIG_MTD_PARTITIONS) */ 295f87151b3SMiquel Raynal 29650466819SMiquel Raynal /* Legacy */ 29709c32807SHeiko Schocher 29809c32807SHeiko Schocher static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size, 29909c32807SHeiko Schocher loff_t *maxsize, int devtype) 30009c32807SHeiko Schocher { 30109c32807SHeiko Schocher #ifdef CONFIG_CMD_MTDPARTS 30209c32807SHeiko Schocher struct mtd_device *dev; 30309c32807SHeiko Schocher struct part_info *part; 30409c32807SHeiko Schocher u8 pnum; 30509c32807SHeiko Schocher int ret; 30609c32807SHeiko Schocher 30709c32807SHeiko Schocher ret = mtdparts_init(); 30809c32807SHeiko Schocher if (ret) 30909c32807SHeiko Schocher return ret; 31009c32807SHeiko Schocher 31109c32807SHeiko Schocher ret = find_dev_and_part(partname, &dev, &pnum, &part); 31209c32807SHeiko Schocher if (ret) 31309c32807SHeiko Schocher return ret; 31409c32807SHeiko Schocher 31509c32807SHeiko Schocher if (dev->id->type != devtype) { 31609c32807SHeiko Schocher printf("not same typ %d != %d\n", dev->id->type, devtype); 31709c32807SHeiko Schocher return -1; 31809c32807SHeiko Schocher } 31909c32807SHeiko Schocher 32009c32807SHeiko Schocher *off = part->offset; 32109c32807SHeiko Schocher *size = part->size; 32209c32807SHeiko Schocher *maxsize = part->size; 32309c32807SHeiko Schocher *idx = dev->id->num; 32409c32807SHeiko Schocher 32509c32807SHeiko Schocher return 0; 32609c32807SHeiko Schocher #else 32710b69712SMaxime Ripard puts("mtdparts support missing.\n"); 32809c32807SHeiko Schocher return -1; 32909c32807SHeiko Schocher #endif 33009c32807SHeiko Schocher } 33109c32807SHeiko Schocher 33209c32807SHeiko Schocher int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size, 333f18d1116SMasahiro Yamada loff_t *maxsize, int devtype, uint64_t chipsize) 33409c32807SHeiko Schocher { 33509c32807SHeiko Schocher if (!str2off(arg, off)) 33609c32807SHeiko Schocher return get_part(arg, idx, off, size, maxsize, devtype); 33709c32807SHeiko Schocher 33809c32807SHeiko Schocher if (*off >= chipsize) { 33909c32807SHeiko Schocher puts("Offset exceeds device limit\n"); 34009c32807SHeiko Schocher return -1; 34109c32807SHeiko Schocher } 34209c32807SHeiko Schocher 34309c32807SHeiko Schocher *maxsize = chipsize - *off; 34409c32807SHeiko Schocher *size = *maxsize; 34509c32807SHeiko Schocher return 0; 34609c32807SHeiko Schocher } 34709c32807SHeiko Schocher 34809c32807SHeiko Schocher int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, 349f18d1116SMasahiro Yamada loff_t *size, loff_t *maxsize, int devtype, 350f18d1116SMasahiro Yamada uint64_t chipsize) 35109c32807SHeiko Schocher { 35209c32807SHeiko Schocher int ret; 35309c32807SHeiko Schocher 35409c32807SHeiko Schocher if (argc == 0) { 35509c32807SHeiko Schocher *off = 0; 35609c32807SHeiko Schocher *size = chipsize; 35709c32807SHeiko Schocher *maxsize = *size; 35809c32807SHeiko Schocher goto print; 35909c32807SHeiko Schocher } 36009c32807SHeiko Schocher 36109c32807SHeiko Schocher ret = mtd_arg_off(argv[0], idx, off, size, maxsize, devtype, 36209c32807SHeiko Schocher chipsize); 36309c32807SHeiko Schocher if (ret) 36409c32807SHeiko Schocher return ret; 36509c32807SHeiko Schocher 36609c32807SHeiko Schocher if (argc == 1) 36709c32807SHeiko Schocher goto print; 36809c32807SHeiko Schocher 36909c32807SHeiko Schocher if (!str2off(argv[1], size)) { 37009c32807SHeiko Schocher printf("'%s' is not a number\n", argv[1]); 37109c32807SHeiko Schocher return -1; 37209c32807SHeiko Schocher } 37309c32807SHeiko Schocher 37409c32807SHeiko Schocher if (*size > *maxsize) { 37509c32807SHeiko Schocher puts("Size exceeds partition or device limit\n"); 37609c32807SHeiko Schocher return -1; 37709c32807SHeiko Schocher } 37809c32807SHeiko Schocher 37909c32807SHeiko Schocher print: 38009c32807SHeiko Schocher printf("device %d ", *idx); 38109c32807SHeiko Schocher if (*size == chipsize) 38209c32807SHeiko Schocher puts("whole chip\n"); 38309c32807SHeiko Schocher else 38409c32807SHeiko Schocher printf("offset 0x%llx, size 0x%llx\n", 38509c32807SHeiko Schocher (unsigned long long)*off, (unsigned long long)*size); 38609c32807SHeiko Schocher return 0; 38709c32807SHeiko Schocher } 388