1// SPDX-License-Identifier:  GPL-2.0+
2/*
3 * mtd.c
4 *
5 * Generic command to handle basic operations on any memory device.
6 *
7 * Copyright: Bootlin, 2018
8 * Author: Miqu��l Raynal <miquel.raynal@bootlin.com>
9 */
10
11#include <command.h>
12#include <common.h>
13#include <console.h>
14#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
15#include <hexdump.h>
16#endif
17#include <malloc.h>
18#include <mapmem.h>
19#include <mtd.h>
20#include <dm/devres.h>
21#include <linux/err.h>
22
23#include <linux/ctype.h>
24
25static struct mtd_info *get_mtd_by_name(const char *name)
26{
27	struct mtd_info *mtd;
28
29	mtd_probe_devices();
30
31	mtd = get_mtd_device_nm(name);
32	if (IS_ERR_OR_NULL(mtd))
33		printf("MTD device %s not found, ret %ld\n", name,
34		       PTR_ERR(mtd));
35
36	return mtd;
37}
38
39static uint mtd_len_to_pages(struct mtd_info *mtd, u64 len)
40{
41	do_div(len, mtd->writesize);
42
43	return len;
44}
45
46static bool mtd_is_aligned_with_min_io_size(struct mtd_info *mtd, u64 size)
47{
48	return !do_div(size, mtd->writesize);
49}
50
51static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
52{
53	return !do_div(size, mtd->erasesize);
54}
55
56static void mtd_dump_buf(const u8 *buf, uint len, uint offset)
57{
58	int i, j;
59
60	for (i = 0; i < len; ) {
61		printf("0x%08x:\t", offset + i);
62		for (j = 0; j < 8; j++)
63			printf("%02x ", buf[i + j]);
64		printf(" ");
65		i += 8;
66		for (j = 0; j < 8; j++)
67			printf("%02x ", buf[i + j]);
68		printf("\n");
69		i += 8;
70	}
71}
72
73static void mtd_dump_device_buf(struct mtd_info *mtd, u64 start_off,
74				const u8 *buf, u64 len, bool woob)
75{
76	bool has_pages = mtd->type == MTD_NANDFLASH ||
77		mtd->type == MTD_MLCNANDFLASH;
78	int npages = mtd_len_to_pages(mtd, len);
79	uint page;
80
81	if (has_pages) {
82		for (page = 0; page < npages; page++) {
83			u64 data_off = (u64)page * mtd->writesize;
84
85			printf("\nDump %d data bytes from 0x%08llx:\n",
86			       mtd->writesize, start_off + data_off);
87			mtd_dump_buf(&buf[data_off],
88				     mtd->writesize, start_off + data_off);
89
90			if (woob) {
91				u64 oob_off = (u64)page * mtd->oobsize;
92
93				printf("Dump %d OOB bytes from page at 0x%08llx:\n",
94				       mtd->oobsize, start_off + data_off);
95				mtd_dump_buf(&buf[len + oob_off],
96					     mtd->oobsize, 0);
97			}
98		}
99	} else {
100		printf("\nDump %lld data bytes from 0x%llx:\n",
101		       len, start_off);
102		mtd_dump_buf(buf, len, start_off);
103	}
104}
105
106static void mtd_show_parts(struct mtd_info *mtd, int level)
107{
108	struct mtd_info *part;
109	int i;
110
111	list_for_each_entry(part, &mtd->partitions, node) {
112		for (i = 0; i < level; i++)
113			printf("\t");
114		printf("  - 0x%012llx-0x%012llx : \"%s\"\n",
115		       part->offset, part->offset + part->size, part->name);
116
117		mtd_show_parts(part, level + 1);
118	}
119}
120
121static void mtd_show_device(struct mtd_info *mtd)
122{
123	/* Device */
124	printf("* %s\n", mtd->name);
125#if defined(CONFIG_DM)
126	if (mtd->dev) {
127		printf("  - device: %s\n", mtd->dev->name);
128		printf("  - parent: %s\n", mtd->dev->parent->name);
129		printf("  - driver: %s\n", mtd->dev->driver->name);
130	}
131#endif
132	if (IS_ENABLED(CONFIG_OF_CONTROL) && mtd->dev) {
133		char buf[256];
134		int res;
135
136		res = ofnode_get_path(mtd_get_ofnode(mtd), buf, 256);
137		printf("  - path: %s\n", res == 0 ? buf : "unavailable");
138	}
139
140	/* MTD device information */
141	printf("  - type: ");
142	switch (mtd->type) {
143	case MTD_RAM:
144		printf("RAM\n");
145		break;
146	case MTD_ROM:
147		printf("ROM\n");
148		break;
149	case MTD_NORFLASH:
150		printf("NOR flash\n");
151		break;
152	case MTD_NANDFLASH:
153		printf("NAND flash\n");
154		break;
155	case MTD_DATAFLASH:
156		printf("Data flash\n");
157		break;
158	case MTD_UBIVOLUME:
159		printf("UBI volume\n");
160		break;
161	case MTD_MLCNANDFLASH:
162		printf("MLC NAND flash\n");
163		break;
164	case MTD_ABSENT:
165	default:
166		printf("Unknown\n");
167		break;
168	}
169
170	printf("  - block size: 0x%x bytes\n", mtd->erasesize);
171	printf("  - min I/O: 0x%x bytes\n", mtd->writesize);
172
173	if (mtd->oobsize) {
174		printf("  - OOB size: %u bytes\n", mtd->oobsize);
175		printf("  - OOB available: %u bytes\n", mtd->oobavail);
176	}
177
178	if (mtd->ecc_strength) {
179		printf("  - ECC strength: %u bits\n", mtd->ecc_strength);
180		printf("  - ECC step size: %u bytes\n", mtd->ecc_step_size);
181		printf("  - bitflip threshold: %u bits\n",
182		       mtd->bitflip_threshold);
183	}
184
185	printf("  - 0x%012llx-0x%012llx : \"%s\"\n",
186	       mtd->offset, mtd->offset + mtd->size, mtd->name);
187
188	/* MTD partitions, if any */
189	mtd_show_parts(mtd, 1);
190}
191
192/* Logic taken from fs/ubifs/recovery.c:is_empty() */
193static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op)
194{
195	int i;
196
197	for (i = 0; i < op->len; i++)
198		if (op->datbuf[i] != 0xff)
199			return false;
200
201	for (i = 0; i < op->ooblen; i++)
202		if (op->oobbuf[i] != 0xff)
203			return false;
204
205	return true;
206}
207
208#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
209static int do_mtd_otp_read(struct cmd_tbl *cmdtp, int flag, int argc,
210			   char *const argv[])
211{
212	struct mtd_info *mtd;
213	size_t retlen;
214	off_t from;
215	size_t len;
216	bool user;
217	int ret;
218	u8 *buf;
219
220	if (argc != 5)
221		return CMD_RET_USAGE;
222
223	if (!strcmp(argv[2], "u"))
224		user = true;
225	else if (!strcmp(argv[2], "f"))
226		user = false;
227	else
228		return CMD_RET_USAGE;
229
230	mtd = get_mtd_by_name(argv[1]);
231	if (IS_ERR_OR_NULL(mtd))
232		return CMD_RET_FAILURE;
233
234	from = simple_strtoul(argv[3], NULL, 0);
235	len = simple_strtoul(argv[4], NULL, 0);
236
237	ret = CMD_RET_FAILURE;
238
239	buf = malloc(len);
240	if (!buf)
241		goto put_mtd;
242
243	printf("Reading %s OTP from 0x%lx, %zu bytes\n",
244	       user ? "user" : "factory", from, len);
245
246	if (user)
247		ret = mtd_read_user_prot_reg(mtd, from, len, &retlen, buf);
248	else
249		ret = mtd_read_fact_prot_reg(mtd, from, len, &retlen, buf);
250	if (ret) {
251		free(buf);
252		pr_err("OTP read failed: %d\n", ret);
253		ret = CMD_RET_FAILURE;
254		goto put_mtd;
255	}
256
257	if (retlen != len)
258		pr_err("OTP read returns %zu, but %zu expected\n",
259		       retlen, len);
260
261	print_hex_dump("", 0, 16, 1, buf, retlen, true);
262
263	free(buf);
264
265	ret = CMD_RET_SUCCESS;
266
267put_mtd:
268	put_mtd_device(mtd);
269
270	return ret;
271}
272
273static int do_mtd_otp_lock(struct cmd_tbl *cmdtp, int flag, int argc,
274			   char *const argv[])
275{
276	struct mtd_info *mtd;
277	off_t from;
278	size_t len;
279	int ret;
280
281	if (argc != 4)
282		return CMD_RET_USAGE;
283
284	mtd = get_mtd_by_name(argv[1]);
285	if (IS_ERR_OR_NULL(mtd))
286		return CMD_RET_FAILURE;
287
288	from = simple_strtoul(argv[2], NULL, 0);
289	len = simple_strtoul(argv[3], NULL, 0);
290
291	ret = mtd_lock_user_prot_reg(mtd, from, len);
292	if (ret) {
293		pr_err("OTP lock failed: %d\n", ret);
294		ret = CMD_RET_FAILURE;
295		goto put_mtd;
296	}
297
298	ret = CMD_RET_SUCCESS;
299
300put_mtd:
301	put_mtd_device(mtd);
302
303	return ret;
304}
305
306static int do_mtd_otp_write(struct cmd_tbl *cmdtp, int flag, int argc,
307			    char *const argv[])
308{
309	struct mtd_info *mtd;
310	size_t retlen;
311	size_t binlen;
312	u8 *binbuf;
313	off_t from;
314	int ret;
315
316	if (argc != 4)
317		return CMD_RET_USAGE;
318
319	mtd = get_mtd_by_name(argv[1]);
320	if (IS_ERR_OR_NULL(mtd))
321		return CMD_RET_FAILURE;
322
323	from = simple_strtoul(argv[2], NULL, 0);
324	binlen = strlen(argv[3]) / 2;
325
326	ret = CMD_RET_FAILURE;
327	binbuf = malloc(binlen);
328	if (!binbuf)
329		goto put_mtd;
330
331	hex2bin(binbuf, argv[3], binlen);
332
333	printf("Will write:\n");
334
335	print_hex_dump("", 0, 16, 1, binbuf, binlen, true);
336
337	printf("to 0x%lx\n", from);
338
339	printf("Continue (y/n)?\n");
340
341	if (confirm_yesno() != 1) {
342		pr_err("OTP write canceled\n");
343		ret = CMD_RET_SUCCESS;
344		goto put_mtd;
345	}
346
347	ret = mtd_write_user_prot_reg(mtd, from, binlen, &retlen, binbuf);
348	if (ret) {
349		pr_err("OTP write failed: %d\n", ret);
350		ret = CMD_RET_FAILURE;
351		goto put_mtd;
352	}
353
354	if (retlen != binlen)
355		pr_err("OTP write returns %zu, but %zu expected\n",
356		       retlen, binlen);
357
358	ret = CMD_RET_SUCCESS;
359
360put_mtd:
361	free(binbuf);
362	put_mtd_device(mtd);
363
364	return ret;
365}
366
367static int do_mtd_otp_info(struct cmd_tbl *cmdtp, int flag, int argc,
368			   char *const argv[])
369{
370	struct otp_info otp_info;
371	struct mtd_info *mtd;
372	size_t retlen;
373	bool user;
374	int ret;
375
376	if (argc != 3)
377		return CMD_RET_USAGE;
378
379	if (!strcmp(argv[2], "u"))
380		user = true;
381	else if (!strcmp(argv[2], "f"))
382		user = false;
383	else
384		return CMD_RET_USAGE;
385
386	mtd = get_mtd_by_name(argv[1]);
387	if (IS_ERR_OR_NULL(mtd))
388		return CMD_RET_FAILURE;
389
390	if (user)
391		ret = mtd_get_user_prot_info(mtd, sizeof(otp_info), &retlen,
392					     &otp_info);
393	else
394		ret = mtd_get_fact_prot_info(mtd, sizeof(otp_info), &retlen,
395					     &otp_info);
396	if (ret) {
397		pr_err("OTP info failed: %d\n", ret);
398		ret = CMD_RET_FAILURE;
399		goto put_mtd;
400	}
401
402	if (retlen != sizeof(otp_info)) {
403		pr_err("OTP info returns %zu, but %zu expected\n",
404		       retlen, sizeof(otp_info));
405		ret = CMD_RET_FAILURE;
406		goto put_mtd;
407	}
408
409	printf("%s OTP region info:\n", user ? "User" : "Factory");
410	printf("\tstart: %u\n", otp_info.start);
411	printf("\tlength: %u\n", otp_info.length);
412	printf("\tlocked: %u\n", otp_info.locked);
413
414	ret = CMD_RET_SUCCESS;
415
416put_mtd:
417	put_mtd_device(mtd);
418
419	return ret;
420}
421#endif
422
423static int do_mtd_list(struct cmd_tbl *cmdtp, int flag, int argc,
424		       char *const argv[])
425{
426	struct mtd_info *mtd;
427	int dev_nb = 0;
428
429	/* Ensure all devices (and their partitions) are probed */
430	mtd_probe_devices();
431
432	printf("List of MTD devices:\n");
433	mtd_for_each_device(mtd) {
434		if (!mtd_is_partition(mtd))
435			mtd_show_device(mtd);
436
437		dev_nb++;
438	}
439
440	if (!dev_nb) {
441		printf("No MTD device found\n");
442		return CMD_RET_FAILURE;
443	}
444
445	return CMD_RET_SUCCESS;
446}
447
448static int mtd_special_write_oob(struct mtd_info *mtd, u64 off,
449				 struct mtd_oob_ops *io_op,
450				 bool write_empty_pages, bool woob)
451{
452	int ret = 0;
453
454	/*
455	 * By default, do not write an empty page.
456	 * Skip it by simulating a successful write.
457	 */
458	if (!write_empty_pages && mtd_oob_write_is_empty(io_op)) {
459		io_op->retlen = mtd->writesize;
460		io_op->oobretlen = woob ? mtd->oobsize : 0;
461	} else {
462		ret = mtd_write_oob(mtd, off, io_op);
463	}
464
465	return ret;
466}
467
468static int do_mtd_io(struct cmd_tbl *cmdtp, int flag, int argc,
469		     char *const argv[])
470{
471	bool dump, read, raw, woob, write_empty_pages, has_pages = false;
472	u64 start_off, off, len, remaining, default_len;
473	struct mtd_oob_ops io_op = {};
474	uint user_addr = 0, npages;
475	const char *cmd = argv[0];
476	struct mtd_info *mtd;
477	u32 oob_len;
478	u8 *buf;
479	int ret;
480
481	if (argc < 2)
482		return CMD_RET_USAGE;
483
484	mtd = get_mtd_by_name(argv[1]);
485	if (IS_ERR_OR_NULL(mtd))
486		return CMD_RET_FAILURE;
487
488	if (mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH)
489		has_pages = true;
490
491	dump = !strncmp(cmd, "dump", 4);
492	read = dump || !strncmp(cmd, "read", 4);
493	raw = strstr(cmd, ".raw");
494	woob = strstr(cmd, ".oob");
495	write_empty_pages = !has_pages || strstr(cmd, ".dontskipff");
496
497	argc -= 2;
498	argv += 2;
499
500	if (!dump) {
501		if (!argc) {
502			ret = CMD_RET_USAGE;
503			goto out_put_mtd;
504		}
505
506		user_addr = hextoul(argv[0], NULL);
507		argc--;
508		argv++;
509	}
510
511	start_off = argc > 0 ? hextoul(argv[0], NULL) : 0;
512	if (!mtd_is_aligned_with_min_io_size(mtd, start_off)) {
513		printf("Offset not aligned with a page (0x%x)\n",
514		       mtd->writesize);
515		ret = CMD_RET_FAILURE;
516		goto out_put_mtd;
517	}
518
519	default_len = dump ? mtd->writesize : mtd->size;
520	len = argc > 1 ? hextoul(argv[1], NULL) : default_len;
521	if (!mtd_is_aligned_with_min_io_size(mtd, len)) {
522		len = round_up(len, mtd->writesize);
523		printf("Size not on a page boundary (0x%x), rounding to 0x%llx\n",
524		       mtd->writesize, len);
525	}
526
527	remaining = len;
528	npages = mtd_len_to_pages(mtd, len);
529	oob_len = woob ? npages * mtd->oobsize : 0;
530
531	if (dump)
532		buf = kmalloc(len + oob_len, GFP_KERNEL);
533	else
534		buf = map_sysmem(user_addr, 0);
535
536	if (!buf) {
537		printf("Could not map/allocate the user buffer\n");
538		ret = CMD_RET_FAILURE;
539		goto out_put_mtd;
540	}
541
542	if (has_pages)
543		printf("%s %lld byte(s) (%d page(s)) at offset 0x%08llx%s%s%s\n",
544		       read ? "Reading" : "Writing", len, npages, start_off,
545		       raw ? " [raw]" : "", woob ? " [oob]" : "",
546		       !read && write_empty_pages ? " [dontskipff]" : "");
547	else
548		printf("%s %lld byte(s) at offset 0x%08llx\n",
549		       read ? "Reading" : "Writing", len, start_off);
550
551	io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB;
552	io_op.len = has_pages ? mtd->writesize : len;
553	io_op.ooblen = woob ? mtd->oobsize : 0;
554	io_op.datbuf = buf;
555	io_op.oobbuf = woob ? &buf[len] : NULL;
556
557	/* Search for the first good block after the given offset */
558	off = start_off;
559	while (mtd_block_isbad(mtd, off))
560		off += mtd->erasesize;
561
562	/* Loop over the pages to do the actual read/write */
563	while (remaining) {
564		/* Skip the block if it is bad */
565		if (mtd_is_aligned_with_block_size(mtd, off) &&
566		    mtd_block_isbad(mtd, off)) {
567			off += mtd->erasesize;
568			continue;
569		}
570
571		if (read)
572			ret = mtd_read_oob(mtd, off, &io_op);
573		else
574			ret = mtd_special_write_oob(mtd, off, &io_op,
575						    write_empty_pages, woob);
576
577		if (ret) {
578			printf("Failure while %s at offset 0x%llx\n",
579			       read ? "reading" : "writing", off);
580			break;
581		}
582
583		off += io_op.retlen;
584		remaining -= io_op.retlen;
585		io_op.datbuf += io_op.retlen;
586		io_op.oobbuf += io_op.oobretlen;
587	}
588
589	if (!ret && dump)
590		mtd_dump_device_buf(mtd, start_off, buf, len, woob);
591
592	if (dump)
593		kfree(buf);
594	else
595		unmap_sysmem(buf);
596
597	if (ret) {
598		printf("%s on %s failed with error %d\n",
599		       read ? "Read" : "Write", mtd->name, ret);
600		ret = CMD_RET_FAILURE;
601	} else {
602		ret = CMD_RET_SUCCESS;
603	}
604
605out_put_mtd:
606	put_mtd_device(mtd);
607
608	return ret;
609}
610
611static int do_mtd_erase(struct cmd_tbl *cmdtp, int flag, int argc,
612			char *const argv[])
613{
614	struct erase_info erase_op = {};
615	struct mtd_info *mtd;
616	u64 off, len;
617	bool scrub;
618	int ret = 0;
619
620	if (argc < 2)
621		return CMD_RET_USAGE;
622
623	mtd = get_mtd_by_name(argv[1]);
624	if (IS_ERR_OR_NULL(mtd))
625		return CMD_RET_FAILURE;
626
627	scrub = strstr(argv[0], ".dontskipbad");
628
629	argc -= 2;
630	argv += 2;
631
632	off = argc > 0 ? hextoul(argv[0], NULL) : 0;
633	len = argc > 1 ? hextoul(argv[1], NULL) : mtd->size;
634
635	if (!mtd_is_aligned_with_block_size(mtd, off)) {
636		printf("Offset not aligned with a block (0x%x)\n",
637		       mtd->erasesize);
638		ret = CMD_RET_FAILURE;
639		goto out_put_mtd;
640	}
641
642	if (!mtd_is_aligned_with_block_size(mtd, len)) {
643		printf("Size not a multiple of a block (0x%x)\n",
644		       mtd->erasesize);
645		ret = CMD_RET_FAILURE;
646		goto out_put_mtd;
647	}
648
649	printf("Erasing 0x%08llx ... 0x%08llx (%d eraseblock(s))\n",
650	       off, off + len - 1, mtd_div_by_eb(len, mtd));
651
652	erase_op.mtd = mtd;
653	erase_op.addr = off;
654	erase_op.len = mtd->erasesize;
655
656	while (len) {
657		if (!scrub) {
658			ret = mtd_block_isbad(mtd, erase_op.addr);
659			if (ret < 0) {
660				printf("Failed to get bad block at 0x%08llx\n",
661				       erase_op.addr);
662				ret = CMD_RET_FAILURE;
663				goto out_put_mtd;
664			}
665
666			if (ret > 0) {
667				printf("Skipping bad block at 0x%08llx\n",
668				       erase_op.addr);
669				ret = 0;
670				len -= mtd->erasesize;
671				erase_op.addr += mtd->erasesize;
672				continue;
673			}
674		}
675
676		ret = mtd_erase(mtd, &erase_op);
677		if (ret && ret != -EIO)
678			break;
679
680		len -= mtd->erasesize;
681		erase_op.addr += mtd->erasesize;
682	}
683
684	if (ret && ret != -EIO)
685		ret = CMD_RET_FAILURE;
686	else
687		ret = CMD_RET_SUCCESS;
688
689out_put_mtd:
690	put_mtd_device(mtd);
691
692	return ret;
693}
694
695static int do_mtd_bad(struct cmd_tbl *cmdtp, int flag, int argc,
696		      char *const argv[])
697{
698	struct mtd_info *mtd;
699	loff_t off;
700
701	if (argc < 2)
702		return CMD_RET_USAGE;
703
704	mtd = get_mtd_by_name(argv[1]);
705	if (IS_ERR_OR_NULL(mtd))
706		return CMD_RET_FAILURE;
707
708	if (!mtd_can_have_bb(mtd)) {
709		printf("Only NAND-based devices can have bad blocks\n");
710		goto out_put_mtd;
711	}
712
713	printf("MTD device %s bad blocks list:\n", mtd->name);
714	for (off = 0; off < mtd->size; off += mtd->erasesize) {
715		if (mtd_block_isbad(mtd, off))
716			printf("\t0x%08llx\n", off);
717	}
718
719out_put_mtd:
720	put_mtd_device(mtd);
721
722	return CMD_RET_SUCCESS;
723}
724
725#ifdef CONFIG_AUTO_COMPLETE
726static int mtd_name_complete(int argc, char *const argv[], char last_char,
727			     int maxv, char *cmdv[])
728{
729	int len = 0, n_found = 0;
730	struct mtd_info *mtd;
731
732	argc--;
733	argv++;
734
735	if (argc > 1 ||
736	    (argc == 1 && (last_char == '\0' || isblank(last_char))))
737		return 0;
738
739	if (argc)
740		len = strlen(argv[0]);
741
742	mtd_for_each_device(mtd) {
743		if (argc &&
744		    (len > strlen(mtd->name) ||
745		     strncmp(argv[0], mtd->name, len)))
746			continue;
747
748		if (n_found >= maxv - 2) {
749			cmdv[n_found++] = "...";
750			break;
751		}
752
753		cmdv[n_found++] = mtd->name;
754	}
755
756	cmdv[n_found] = NULL;
757
758	return n_found;
759}
760#endif /* CONFIG_AUTO_COMPLETE */
761
762U_BOOT_LONGHELP(mtd,
763	"- generic operations on memory technology devices\n\n"
764	"mtd list\n"
765	"mtd read[.raw][.oob]                  <name> <addr> [<off> [<size>]]\n"
766	"mtd dump[.raw][.oob]                  <name>        [<off> [<size>]]\n"
767	"mtd write[.raw][.oob][.dontskipff]    <name> <addr> [<off> [<size>]]\n"
768	"mtd erase[.dontskipbad]               <name>        [<off> [<size>]]\n"
769	"\n"
770	"Specific functions:\n"
771	"mtd bad                               <name>\n"
772#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
773	"mtd otpread                           <name> [u|f] <off> <size>\n"
774	"mtd otpwrite                          <name> <off> <hex string>\n"
775	"mtd otplock                           <name> <off> <size>\n"
776	"mtd otpinfo                           <name> [u|f]\n"
777#endif
778	"\n"
779	"With:\n"
780	"\t<name>: NAND partition/chip name (or corresponding DM device name or OF path)\n"
781	"\t<addr>: user address from/to which data will be retrieved/stored\n"
782	"\t<off>: offset in <name> in bytes (default: start of the part)\n"
783	"\t\t* must be block-aligned for erase\n"
784	"\t\t* must be page-aligned otherwise\n"
785	"\t<size>: length of the operation in bytes (default: the entire device)\n"
786	"\t\t* must be a multiple of a block for erase\n"
787	"\t\t* must be a multiple of a page otherwise (special case: default is a page with dump)\n"
788#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
789	"\t<hex string>: hex string without '0x' and spaces. Example: ABCD1234\n"
790	"\t[u|f]: user or factory OTP region\n"
791#endif
792	"\n"
793	"The .dontskipff option forces writing empty pages, don't use it if unsure.\n");
794
795U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text,
796#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
797		U_BOOT_SUBCMD_MKENT(otpread, 5, 1, do_mtd_otp_read),
798		U_BOOT_SUBCMD_MKENT(otpwrite, 4, 1, do_mtd_otp_write),
799		U_BOOT_SUBCMD_MKENT(otplock, 4, 1, do_mtd_otp_lock),
800		U_BOOT_SUBCMD_MKENT(otpinfo, 3, 1, do_mtd_otp_info),
801#endif
802		U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mtd_list),
803		U_BOOT_SUBCMD_MKENT_COMPLETE(read, 5, 0, do_mtd_io,
804					     mtd_name_complete),
805		U_BOOT_SUBCMD_MKENT_COMPLETE(write, 5, 0, do_mtd_io,
806					     mtd_name_complete),
807		U_BOOT_SUBCMD_MKENT_COMPLETE(dump, 4, 0, do_mtd_io,
808					     mtd_name_complete),
809		U_BOOT_SUBCMD_MKENT_COMPLETE(erase, 4, 0, do_mtd_erase,
810					     mtd_name_complete),
811		U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad,
812					     mtd_name_complete));
813