1 /* 2 * (C) Copyright 2014 3 * Heiko Schocher, DENX Software Engineering, hs@denx.de. 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 */ 7 #include <common.h> 8 #include <dm/device.h> 9 #include <dm/uclass-internal.h> 10 #include <jffs2/jffs2.h> /* LEGACY */ 11 #include <linux/mtd/mtd.h> 12 #include <linux/mtd/partitions.h> 13 #include <mtd.h> 14 15 #define MTD_NAME_MAX_LEN 20 16 17 18 /** 19 * mtd_search_alternate_name - Search an alternate name for @mtdname thanks to 20 * the mtdids legacy environment variable. 21 * 22 * The mtdids string is a list of comma-separated 'dev_id=mtd_id' tupples. 23 * Check if one of the mtd_id matches mtdname, in this case save dev_id in 24 * altname. 25 * 26 * @mtdname: Current MTD device name 27 * @altname: Alternate name to return 28 * @max_len: Length of the alternate name buffer 29 * 30 * @return 0 on success, an error otherwise. 31 */ 32 int mtd_search_alternate_name(const char *mtdname, char *altname, 33 unsigned int max_len) 34 { 35 const char *mtdids, *equal, *comma, *dev_id, *mtd_id; 36 int dev_id_len, mtd_id_len; 37 38 mtdids = env_get("mtdids"); 39 if (!mtdids) 40 return -EINVAL; 41 42 do { 43 /* Find the '=' sign */ 44 dev_id = mtdids; 45 equal = strchr(dev_id, '='); 46 if (!equal) 47 break; 48 dev_id_len = equal - mtdids; 49 mtd_id = equal + 1; 50 51 /* Find the end of the tupple */ 52 comma = strchr(mtdids, ','); 53 if (comma) 54 mtd_id_len = comma - mtd_id; 55 else 56 mtd_id_len = &mtdids[strlen(mtdids)] - mtd_id + 1; 57 58 if (!dev_id_len || !mtd_id_len) 59 return -EINVAL; 60 61 if (dev_id_len + 1 > max_len) 62 continue; 63 64 /* Compare the name we search with the current mtd_id */ 65 if (!strncmp(mtdname, mtd_id, mtd_id_len)) { 66 strncpy(altname, dev_id, dev_id_len); 67 altname[dev_id_len] = 0; 68 69 return 0; 70 } 71 72 /* Go to the next tupple */ 73 mtdids = comma + 1; 74 } while (comma); 75 76 return -EINVAL; 77 } 78 79 #if IS_ENABLED(CONFIG_MTD) 80 static void mtd_probe_uclass_mtd_devs(void) 81 { 82 struct udevice *dev; 83 int idx = 0; 84 85 /* Probe devices with DM compliant drivers */ 86 while (!uclass_find_device(UCLASS_MTD, idx, &dev) && dev) { 87 mtd_probe(dev); 88 idx++; 89 } 90 } 91 #else 92 static void mtd_probe_uclass_mtd_devs(void) { } 93 #endif 94 95 #if defined(CONFIG_MTD_PARTITIONS) 96 int mtd_probe_devices(void) 97 { 98 static char *old_mtdparts; 99 static char *old_mtdids; 100 const char *mtdparts = env_get("mtdparts"); 101 const char *mtdids = env_get("mtdids"); 102 bool remaining_partitions = true; 103 struct mtd_info *mtd; 104 105 mtd_probe_uclass_mtd_devs(); 106 107 /* Check if mtdparts/mtdids changed since last call, otherwise: exit */ 108 if (!strcmp(mtdparts, old_mtdparts) && !strcmp(mtdids, old_mtdids)) 109 return 0; 110 111 /* Update the local copy of mtdparts */ 112 free(old_mtdparts); 113 free(old_mtdids); 114 old_mtdparts = strdup(mtdparts); 115 old_mtdids = strdup(mtdids); 116 117 /* If at least one partition is still in use, do not delete anything */ 118 mtd_for_each_device(mtd) { 119 if (mtd->usecount) { 120 printf("Partition \"%s\" already in use, aborting\n", 121 mtd->name); 122 return -EACCES; 123 } 124 } 125 126 /* 127 * Everything looks clear, remove all partitions. It is not safe to 128 * remove entries from the mtd_for_each_device loop as it uses idr 129 * indexes and the partitions removal is done in bulk (all partitions of 130 * one device at the same time), so break and iterate from start each 131 * time a new partition is found and deleted. 132 */ 133 while (remaining_partitions) { 134 remaining_partitions = false; 135 mtd_for_each_device(mtd) { 136 if (!mtd_is_partition(mtd) && mtd_has_partitions(mtd)) { 137 del_mtd_partitions(mtd); 138 remaining_partitions = true; 139 break; 140 } 141 } 142 } 143 144 /* Start the parsing by ignoring the extra 'mtdparts=' prefix, if any */ 145 if (strstr(mtdparts, "mtdparts=")) 146 mtdparts += 9; 147 148 /* For each MTD device in mtdparts */ 149 while (mtdparts[0] != '\0') { 150 char mtd_name[MTD_NAME_MAX_LEN], *colon; 151 struct mtd_partition *parts; 152 int mtd_name_len, nparts; 153 int ret; 154 155 colon = strchr(mtdparts, ':'); 156 if (!colon) { 157 printf("Wrong mtdparts: %s\n", mtdparts); 158 return -EINVAL; 159 } 160 161 mtd_name_len = colon - mtdparts; 162 strncpy(mtd_name, mtdparts, mtd_name_len); 163 mtd_name[mtd_name_len] = '\0'; 164 /* Move the pointer forward (including the ':') */ 165 mtdparts += mtd_name_len + 1; 166 mtd = get_mtd_device_nm(mtd_name); 167 if (IS_ERR_OR_NULL(mtd)) { 168 char linux_name[MTD_NAME_MAX_LEN]; 169 170 /* 171 * The MTD device named "mtd_name" does not exist. Try 172 * to find a correspondance with an MTD device having 173 * the same type and number as defined in the mtdids. 174 */ 175 debug("No device named %s\n", mtd_name); 176 ret = mtd_search_alternate_name(mtd_name, linux_name, 177 MTD_NAME_MAX_LEN); 178 if (!ret) 179 mtd = get_mtd_device_nm(linux_name); 180 181 /* 182 * If no device could be found, move the mtdparts 183 * pointer forward until the next set of partitions. 184 */ 185 if (ret || IS_ERR_OR_NULL(mtd)) { 186 printf("Could not find a valid device for %s\n", 187 mtd_name); 188 mtdparts = strchr(mtdparts, ';'); 189 if (mtdparts) 190 mtdparts++; 191 192 continue; 193 } 194 } 195 196 /* 197 * Parse the MTD device partitions. It will update the mtdparts 198 * pointer, create an array of parts (that must be freed), and 199 * return the number of partition structures in the array. 200 */ 201 ret = mtd_parse_partitions(mtd, &mtdparts, &parts, &nparts); 202 if (ret) { 203 printf("Could not parse device %s\n", mtd->name); 204 put_mtd_device(mtd); 205 return -EINVAL; 206 } 207 208 if (!nparts) 209 continue; 210 211 /* Create the new MTD partitions */ 212 add_mtd_partitions(mtd, parts, nparts); 213 214 /* Free the structures allocated during the parsing */ 215 mtd_free_parsed_partitions(parts, nparts); 216 217 put_mtd_device(mtd); 218 } 219 220 return 0; 221 } 222 #else 223 int mtd_probe_devices(void) 224 { 225 mtd_probe_uclass_mtd_devs(); 226 227 return 0; 228 } 229 #endif /* defined(CONFIG_MTD_PARTITIONS) */ 230 231 /* Legacy */ 232 233 static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size, 234 loff_t *maxsize, int devtype) 235 { 236 #ifdef CONFIG_CMD_MTDPARTS 237 struct mtd_device *dev; 238 struct part_info *part; 239 u8 pnum; 240 int ret; 241 242 ret = mtdparts_init(); 243 if (ret) 244 return ret; 245 246 ret = find_dev_and_part(partname, &dev, &pnum, &part); 247 if (ret) 248 return ret; 249 250 if (dev->id->type != devtype) { 251 printf("not same typ %d != %d\n", dev->id->type, devtype); 252 return -1; 253 } 254 255 *off = part->offset; 256 *size = part->size; 257 *maxsize = part->size; 258 *idx = dev->id->num; 259 260 return 0; 261 #else 262 puts("mtdparts support missing.\n"); 263 return -1; 264 #endif 265 } 266 267 int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size, 268 loff_t *maxsize, int devtype, uint64_t chipsize) 269 { 270 if (!str2off(arg, off)) 271 return get_part(arg, idx, off, size, maxsize, devtype); 272 273 if (*off >= chipsize) { 274 puts("Offset exceeds device limit\n"); 275 return -1; 276 } 277 278 *maxsize = chipsize - *off; 279 *size = *maxsize; 280 return 0; 281 } 282 283 int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, 284 loff_t *size, loff_t *maxsize, int devtype, 285 uint64_t chipsize) 286 { 287 int ret; 288 289 if (argc == 0) { 290 *off = 0; 291 *size = chipsize; 292 *maxsize = *size; 293 goto print; 294 } 295 296 ret = mtd_arg_off(argv[0], idx, off, size, maxsize, devtype, 297 chipsize); 298 if (ret) 299 return ret; 300 301 if (argc == 1) 302 goto print; 303 304 if (!str2off(argv[1], size)) { 305 printf("'%s' is not a number\n", argv[1]); 306 return -1; 307 } 308 309 if (*size > *maxsize) { 310 puts("Size exceeds partition or device limit\n"); 311 return -1; 312 } 313 314 print: 315 printf("device %d ", *idx); 316 if (*size == chipsize) 317 puts("whole chip\n"); 318 else 319 printf("offset 0x%llx, size 0x%llx\n", 320 (unsigned long long)*off, (unsigned long long)*size); 321 return 0; 322 } 323