xref: /rk3399_rockchip-uboot/drivers/mtd/mtd_uboot.c (revision 8ea74120565f81f5f3f7e7fa29bb3cd5be62a91e)
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 void board_mtdparts_default(const char **mtdids, const char **mtdparts);
18 
19 static const char *get_mtdids(void)
20 {
21 	__maybe_unused const char *mtdparts = NULL;
22 	const char *mtdids = env_get("mtdids");
23 
24 	if (mtdids)
25 		return mtdids;
26 
27 #if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
28 	board_mtdparts_default(&mtdids, &mtdparts);
29 #elif defined(MTDIDS_DEFAULT)
30 	mtdids = MTDIDS_DEFAULT;
31 #elif defined(CONFIG_MTDIDS_DEFAULT)
32 	mtdids = CONFIG_MTDIDS_DEFAULT;
33 #endif
34 
35 	if (mtdids)
36 		env_set("mtdids", mtdids);
37 
38 	return mtdids;
39 }
40 
41 /**
42  * mtd_search_alternate_name - Search an alternate name for @mtdname thanks to
43  *                             the mtdids legacy environment variable.
44  *
45  * The mtdids string is a list of comma-separated 'dev_id=mtd_id' tupples.
46  * Check if one of the mtd_id matches mtdname, in this case save dev_id in
47  * altname.
48  *
49  * @mtdname: Current MTD device name
50  * @altname: Alternate name to return
51  * @max_len: Length of the alternate name buffer
52  *
53  * @return 0 on success, an error otherwise.
54  */
55 int mtd_search_alternate_name(const char *mtdname, char *altname,
56 			      unsigned int max_len)
57 {
58 	const char *mtdids, *equal, *comma, *dev_id, *mtd_id;
59 	int dev_id_len, mtd_id_len;
60 
61 	mtdids = get_mtdids();
62 	if (!mtdids)
63 		return -EINVAL;
64 
65 	do {
66 		/* Find the '=' sign */
67 		dev_id = mtdids;
68 		equal = strchr(dev_id, '=');
69 		if (!equal)
70 			break;
71 		dev_id_len = equal - mtdids;
72 		mtd_id = equal + 1;
73 
74 		/* Find the end of the tupple */
75 		comma = strchr(mtdids, ',');
76 		if (comma)
77 			mtd_id_len = comma - mtd_id;
78 		else
79 			mtd_id_len = &mtdids[strlen(mtdids)] - mtd_id + 1;
80 
81 		if (!dev_id_len || !mtd_id_len)
82 			return -EINVAL;
83 
84 		if (dev_id_len + 1 > max_len)
85 			continue;
86 
87 		/* Compare the name we search with the current mtd_id */
88 		if (!strncmp(mtdname, mtd_id, mtd_id_len)) {
89 			strncpy(altname, dev_id, dev_id_len);
90 			altname[dev_id_len] = 0;
91 
92 			return 0;
93 		}
94 
95 		/* Go to the next tupple */
96 		mtdids = comma + 1;
97 	} while (comma);
98 
99 	return -EINVAL;
100 }
101 
102 #if IS_ENABLED(CONFIG_MTD)
103 static void mtd_probe_uclass_mtd_devs(void)
104 {
105 	struct udevice *dev;
106 	int idx = 0;
107 
108 	/* Probe devices with DM compliant drivers */
109 	while (!uclass_find_device(UCLASS_MTD, idx, &dev) && dev) {
110 		mtd_probe(dev);
111 		idx++;
112 	}
113 }
114 #else
115 static void mtd_probe_uclass_mtd_devs(void) { }
116 #endif
117 
118 #if defined(CONFIG_MTD_PARTITIONS)
119 
120 #define MTDPARTS_MAXLEN         512
121 
122 static const char *get_mtdparts(void)
123 {
124 	__maybe_unused const char *mtdids = NULL;
125 	static char tmp_parts[MTDPARTS_MAXLEN];
126 	static bool use_defaults = true;
127 	const char *mtdparts = NULL;
128 
129 	if (gd->flags & GD_FLG_ENV_READY)
130 		mtdparts = env_get("mtdparts");
131 	else if (env_get_f("mtdparts", tmp_parts, sizeof(tmp_parts)) != -1)
132 		mtdparts = tmp_parts;
133 
134 	if (mtdparts || !use_defaults)
135 		return mtdparts;
136 
137 #if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
138 	board_mtdparts_default(&mtdids, &mtdparts);
139 #elif defined(MTDPARTS_DEFAULT)
140 	mtdparts = MTDPARTS_DEFAULT;
141 #elif defined(CONFIG_MTDPARTS_DEFAULT)
142 	mtdparts = CONFIG_MTDPARTS_DEFAULT;
143 #endif
144 
145 	if (mtdparts)
146 		env_set("mtdparts", mtdparts);
147 
148 	use_defaults = false;
149 
150 	return mtdparts;
151 }
152 
153 static int mtd_del_parts(struct mtd_info *mtd, bool quiet)
154 {
155 	int ret;
156 
157 	if (!mtd_has_partitions(mtd))
158 		return 0;
159 
160 	/* do not delete partitions if they are in use. */
161 	if (mtd_partitions_used(mtd)) {
162 		if (!quiet)
163 			printf("\"%s\" partitions still in use, can't delete them\n",
164 			       mtd->name);
165 		return -EACCES;
166 	}
167 
168 	ret = del_mtd_partitions(mtd);
169 	if (ret)
170 		return ret;
171 
172 	return 1;
173 }
174 
175 static bool mtd_del_all_parts_failed;
176 
177 static void mtd_del_all_parts(void)
178 {
179 	struct mtd_info *mtd;
180 	int ret = 0;
181 
182 	mtd_del_all_parts_failed = false;
183 
184 	/*
185 	 * It is not safe to remove entries from the mtd_for_each_device loop
186 	 * as it uses idr indexes and the partitions removal is done in bulk
187 	 * (all partitions of one device at the same time), so break and
188 	 * iterate from start each time a new partition is found and deleted.
189 	 */
190 	do {
191 		mtd_for_each_device(mtd) {
192 			ret = mtd_del_parts(mtd, false);
193 			if (ret > 0)
194 				break;
195 			else if (ret < 0)
196 				mtd_del_all_parts_failed = true;
197 		}
198 	} while (ret > 0);
199 }
200 
201 int mtd_probe_devices(void)
202 {
203 	static char *old_mtdparts;
204 	static char *old_mtdids;
205 	const char *mtdparts = get_mtdparts();
206 	const char *mtdids = get_mtdids();
207 	const char *mtdparts_next = mtdparts;
208 	struct mtd_info *mtd;
209 
210 	mtd_probe_uclass_mtd_devs();
211 
212 	/*
213 	 * Check if mtdparts/mtdids changed, if the MTD dev list was updated
214 	 * or if our previous attempt to delete existing partititions failed.
215 	 * In any of these cases we want to update the partitions, otherwise,
216 	 * everything is up-to-date and we can return 0 directly.
217 	 */
218 	if ((!mtdparts && !old_mtdparts && !mtdids && !old_mtdids) ||
219 	    (mtdparts && old_mtdparts && mtdids && old_mtdids &&
220 	     !mtd_dev_list_updated() && !mtd_del_all_parts_failed &&
221 	     !strcmp(mtdparts, old_mtdparts) &&
222 	     !strcmp(mtdids, old_mtdids)))
223 		return 0;
224 
225 	/* Update the local copy of mtdparts */
226 	free(old_mtdparts);
227 	free(old_mtdids);
228 	old_mtdparts = strdup(mtdparts);
229 	old_mtdids = strdup(mtdids);
230 
231 	/*
232 	 * Remove all old parts. Note that partition removal can fail in case
233 	 * one of the partition is still being used by an MTD user, so this
234 	 * does not guarantee that all old partitions are gone.
235 	 */
236 	mtd_del_all_parts();
237 
238 	/*
239 	 * Call mtd_dev_list_updated() to clear updates generated by our own
240 	 * parts removal loop.
241 	 */
242 	mtd_dev_list_updated();
243 
244 	/* If either mtdparts or mtdids is empty, then exit */
245 	if (!mtdparts || !mtdids)
246 		return 0;
247 
248 	/* Start the parsing by ignoring the extra 'mtdparts=' prefix, if any */
249 	if (!strncmp(mtdparts, "mtdparts=", sizeof("mtdparts=") - 1))
250 		mtdparts += 9;
251 
252 	/* For each MTD device in mtdparts */
253 	for (; mtdparts[0] != '\0'; mtdparts = mtdparts_next) {
254 		char mtd_name[MTD_NAME_MAX_LEN], *colon;
255 		struct mtd_partition *parts;
256 		unsigned int mtd_name_len;
257 		int nparts, ret;
258 
259 		mtdparts_next = strchr(mtdparts, ';');
260 		if (!mtdparts_next)
261 			mtdparts_next = mtdparts + strlen(mtdparts);
262 		else
263 			mtdparts_next++;
264 
265 		colon = strchr(mtdparts, ':');
266 		if (colon > mtdparts_next)
267 			colon = NULL;
268 
269 		if (!colon) {
270 			printf("Wrong mtdparts: %s\n", mtdparts);
271 			return -EINVAL;
272 		}
273 
274 		mtd_name_len = (unsigned int)(colon - mtdparts);
275 		if (mtd_name_len + 1 > sizeof(mtd_name)) {
276 			printf("MTD name too long: %s\n", mtdparts);
277 			return -EINVAL;
278 		}
279 
280 		strncpy(mtd_name, mtdparts, mtd_name_len);
281 		mtd_name[mtd_name_len] = '\0';
282 		/* Move the pointer forward (including the ':') */
283 		mtdparts += mtd_name_len + 1;
284 		mtd = get_mtd_device_nm(mtd_name);
285 		if (IS_ERR_OR_NULL(mtd)) {
286 			char linux_name[MTD_NAME_MAX_LEN];
287 
288 			/*
289 			 * The MTD device named "mtd_name" does not exist. Try
290 			 * to find a correspondance with an MTD device having
291 			 * the same type and number as defined in the mtdids.
292 			 */
293 			debug("No device named %s\n", mtd_name);
294 			ret = mtd_search_alternate_name(mtd_name, linux_name,
295 							MTD_NAME_MAX_LEN);
296 			if (!ret)
297 				mtd = get_mtd_device_nm(linux_name);
298 
299 			/*
300 			 * If no device could be found, move the mtdparts
301 			 * pointer forward until the next set of partitions.
302 			 */
303 			if (ret || IS_ERR_OR_NULL(mtd)) {
304 				printf("Could not find a valid device for %s\n",
305 				       mtd_name);
306 				mtdparts = mtdparts_next;
307 				continue;
308 			}
309 		}
310 
311 		/*
312 		 * Call mtd_del_parts() again, even if it's already been called
313 		 * in mtd_del_all_parts(). We need to know if old partitions are
314 		 * still around (because they are still being used by someone),
315 		 * and if they are, we shouldn't create new partitions, so just
316 		 * skip this MTD device and try the next one.
317 		 */
318 		ret = mtd_del_parts(mtd, true);
319 		if (ret < 0)
320 			continue;
321 
322 		/*
323 		 * Parse the MTD device partitions. It will update the mtdparts
324 		 * pointer, create an array of parts (that must be freed), and
325 		 * return the number of partition structures in the array.
326 		 */
327 		ret = mtd_parse_partitions(mtd, &mtdparts, &parts, &nparts);
328 		if (ret) {
329 			printf("Could not parse device %s\n", mtd->name);
330 			put_mtd_device(mtd);
331 			return -EINVAL;
332 		}
333 
334 		if (!nparts)
335 			continue;
336 
337 		/* Create the new MTD partitions */
338 		add_mtd_partitions(mtd, parts, nparts);
339 
340 		/* Free the structures allocated during the parsing */
341 		mtd_free_parsed_partitions(parts, nparts);
342 
343 		put_mtd_device(mtd);
344 	}
345 
346 	/*
347 	 * Call mtd_dev_list_updated() to clear updates generated by our own
348 	 * parts registration loop.
349 	 */
350 	mtd_dev_list_updated();
351 
352 	return 0;
353 }
354 #else
355 int mtd_probe_devices(void)
356 {
357 	mtd_probe_uclass_mtd_devs();
358 
359 	return 0;
360 }
361 #endif /* defined(CONFIG_MTD_PARTITIONS) */
362 
363 /* Legacy */
364 
365 static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size,
366 		loff_t *maxsize, int devtype)
367 {
368 #ifdef CONFIG_CMD_MTDPARTS
369 	struct mtd_device *dev;
370 	struct part_info *part;
371 	u8 pnum;
372 	int ret;
373 
374 	ret = mtdparts_init();
375 	if (ret)
376 		return ret;
377 
378 	ret = find_dev_and_part(partname, &dev, &pnum, &part);
379 	if (ret)
380 		return ret;
381 
382 	if (dev->id->type != devtype) {
383 		printf("not same typ %d != %d\n", dev->id->type, devtype);
384 		return -1;
385 	}
386 
387 	*off = part->offset;
388 	*size = part->size;
389 	*maxsize = part->size;
390 	*idx = dev->id->num;
391 
392 	return 0;
393 #else
394 	puts("mtdparts support missing.\n");
395 	return -1;
396 #endif
397 }
398 
399 int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size,
400 		loff_t *maxsize, int devtype, uint64_t chipsize)
401 {
402 	if (!str2off(arg, off))
403 		return get_part(arg, idx, off, size, maxsize, devtype);
404 
405 	if (*off >= chipsize) {
406 		puts("Offset exceeds device limit\n");
407 		return -1;
408 	}
409 
410 	*maxsize = chipsize - *off;
411 	*size = *maxsize;
412 	return 0;
413 }
414 
415 int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off,
416 		     loff_t *size, loff_t *maxsize, int devtype,
417 		     uint64_t chipsize)
418 {
419 	int ret;
420 
421 	if (argc == 0) {
422 		*off = 0;
423 		*size = chipsize;
424 		*maxsize = *size;
425 		goto print;
426 	}
427 
428 	ret = mtd_arg_off(argv[0], idx, off, size, maxsize, devtype,
429 			  chipsize);
430 	if (ret)
431 		return ret;
432 
433 	if (argc == 1)
434 		goto print;
435 
436 	if (!str2off(argv[1], size)) {
437 		printf("'%s' is not a number\n", argv[1]);
438 		return -1;
439 	}
440 
441 	if (*size > *maxsize) {
442 		puts("Size exceeds partition or device limit\n");
443 		return -1;
444 	}
445 
446 print:
447 	printf("device %d ", *idx);
448 	if (*size == chipsize)
449 		puts("whole chip\n");
450 	else
451 		printf("offset 0x%llx, size 0x%llx\n",
452 		       (unsigned long long)*off, (unsigned long long)*size);
453 	return 0;
454 }
455