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