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