uboot/cmd/flash.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2000
   4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   5 */
   6
   7/*
   8 * FLASH support
   9 */
  10#include <common.h>
  11#include <command.h>
  12#include <log.h>
  13#include <uuid.h>
  14
  15#if defined(CONFIG_CMD_MTDPARTS)
  16#include <jffs2/jffs2.h>
  17
  18/* partition handling routines */
  19int mtdparts_init(void);
  20int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num);
  21int find_dev_and_part(const char *id, struct mtd_device **dev,
  22                      u8 *part_num, struct part_info **part);
  23#endif
  24
  25#ifdef CONFIG_MTD_NOR_FLASH
  26#include <flash.h>
  27#include <mtd/cfi_flash.h>
  28extern flash_info_t flash_info[];       /* info for FLASH chips */
  29
  30/*
  31 * The user interface starts numbering for Flash banks with 1
  32 * for historical reasons.
  33 */
  34
  35/*
  36 * this routine looks for an abbreviated flash range specification.
  37 * the syntax is B:SF[-SL], where B is the bank number, SF is the first
  38 * sector to erase, and SL is the last sector to erase (defaults to SF).
  39 * bank numbers start at 1 to be consistent with other specs, sector numbers
  40 * start at zero.
  41 *
  42 * returns:     1       - correct spec; *pinfo, *psf and *psl are
  43 *                        set appropriately
  44 *              0       - doesn't look like an abbreviated spec
  45 *              -1      - looks like an abbreviated spec, but got
  46 *                        a parsing error, a number out of range,
  47 *                        or an invalid flash bank.
  48 */
  49static int
  50abbrev_spec(char *str, flash_info_t **pinfo, int *psf, int *psl)
  51{
  52        flash_info_t *fp;
  53        int bank, first, last;
  54        char *p, *ep;
  55
  56        p = strchr(str, ':');
  57        if (!p)
  58                return 0;
  59        *p++ = '\0';
  60
  61        bank = dectoul(str, &ep);
  62        if (ep == str || *ep != '\0' ||
  63            bank < 1 || bank > CFI_FLASH_BANKS)
  64                return -1;
  65
  66        fp = &flash_info[bank - 1];
  67        if (fp->flash_id == FLASH_UNKNOWN)
  68                return -1;
  69
  70        str = p;
  71        p = strchr(str, '-');
  72        if (p)
  73                *p++ = '\0';
  74
  75        first = dectoul(str, &ep);
  76        if (ep == str || *ep != '\0' || first >= fp->sector_count)
  77                return -1;
  78
  79        if (p) {
  80                last = dectoul(p, &ep);
  81                if (ep == p || *ep != '\0' ||
  82                    last < first || last >= fp->sector_count)
  83                        return -1;
  84        } else {
  85                last = first;
  86        }
  87
  88        *pinfo = fp;
  89        *psf = first;
  90        *psl = last;
  91
  92        return 1;
  93}
  94
  95/*
  96 * Take *addr in Flash and adjust it to fall on the end of its sector
  97 */
  98int flash_sect_roundb(ulong *addr)
  99{
 100        flash_info_t *info;
 101        ulong bank, sector_end_addr;
 102        char found;
 103        int i;
 104
 105        /* find the end addr of the sector where the *addr is */
 106        found = 0;
 107        for (bank = 0; bank < CFI_FLASH_BANKS && !found; ++bank) {
 108                info = &flash_info[bank];
 109                for (i = 0; i < info->sector_count && !found; ++i) {
 110                        /* get the end address of the sector */
 111                        if (i == info->sector_count - 1) {
 112                                sector_end_addr = info->start[0] +
 113                                                                info->size - 1;
 114                        } else {
 115                                sector_end_addr = info->start[i + 1] - 1;
 116                        }
 117
 118                        if (*addr <= sector_end_addr && *addr >= info->start[i]) {
 119                                found = 1;
 120                                /* adjust *addr if necessary */
 121                                if (*addr < sector_end_addr)
 122                                        *addr = sector_end_addr;
 123                        } /* sector */
 124                } /* bank */
 125        }
 126        if (!found) {
 127                /* error, address not in flash */
 128                printf("Error: end address (0x%08lx) not in flash!\n", *addr);
 129                return 1;
 130        }
 131
 132        return 0;
 133}
 134
 135/*
 136 * This function computes the start and end addresses for both
 137 * erase and protect commands. The range of the addresses on which
 138 * either of the commands is to operate can be given in two forms:
 139 * 1. <cmd> start end - operate on <'start',  'end')
 140 * 2. <cmd> start +length - operate on <'start', start + length)
 141 * If the second form is used and the end address doesn't fall on the
 142 * sector boundary, than it will be adjusted to the next sector boundary.
 143 * If it isn't in the flash, the function will fail (return -1).
 144 * Input:
 145 *    arg1, arg2: address specification (i.e. both command arguments)
 146 * Output:
 147 *    addr_first, addr_last: computed address range
 148 * Return:
 149 *    1: success
 150 *   -1: failure (bad format, bad address).
 151 */
 152static int
 153addr_spec(char *arg1, char *arg2, ulong *addr_first, ulong *addr_last)
 154{
 155        char *ep;
 156        char len_used; /* indicates if the "start +length" form used */
 157
 158        *addr_first = hextoul(arg1, &ep);
 159        if (ep == arg1 || *ep != '\0')
 160                return -1;
 161
 162        len_used = 0;
 163        if (arg2 && *arg2 == '+') {
 164                len_used = 1;
 165                ++arg2;
 166        }
 167
 168        *addr_last = hextoul(arg2, &ep);
 169        if (ep == arg2 || *ep != '\0')
 170                return -1;
 171
 172        if (len_used) {
 173                /*
 174                 * *addr_last has the length, compute correct *addr_last
 175                 * XXX watch out for the integer overflow! Right now it is
 176                 * checked for in both the callers.
 177                 */
 178                *addr_last = *addr_first + *addr_last - 1;
 179
 180                /*
 181                 * It may happen that *addr_last doesn't fall on the sector
 182                 * boundary. We want to round such an address to the next
 183                 * sector boundary, so that the commands don't fail later on.
 184                 */
 185
 186                if (flash_sect_roundb(addr_last) > 0)
 187                        return -1;
 188        } /* "start +length" from used */
 189
 190        return 1;
 191}
 192
 193static int
 194flash_fill_sect_ranges(ulong addr_first, ulong addr_last,
 195                       int *s_first, int *s_last,
 196                       int *s_count)
 197{
 198        flash_info_t *info;
 199        ulong bank;
 200        int rcode = 0;
 201
 202        *s_count = 0;
 203
 204        for (bank = 0; bank < CFI_FLASH_BANKS; ++bank) {
 205                s_first[bank] = -1;     /* first sector to erase        */
 206                s_last[bank] = -1;      /* last  sector to erase        */
 207        }
 208
 209        for (bank = 0, info = &flash_info[0];
 210             (bank < CFI_FLASH_BANKS) && (addr_first <= addr_last);
 211             ++bank, ++info) {
 212                ulong b_end;
 213                int sect;
 214                short s_end;
 215
 216                if (info->flash_id == FLASH_UNKNOWN)
 217                        continue;
 218
 219                b_end = info->start[0] + info->size - 1;        /* bank end addr */
 220                s_end = info->sector_count - 1;                 /* last sector   */
 221
 222                for (sect = 0; sect < info->sector_count; ++sect) {
 223                        ulong end;      /* last address in current sect */
 224
 225                        end = (sect == s_end) ? b_end : info->start[sect + 1] - 1;
 226
 227                        if (addr_first > end)
 228                                continue;
 229                        if (addr_last < info->start[sect])
 230                                continue;
 231
 232                        if (addr_first == info->start[sect])
 233                                s_first[bank] = sect;
 234
 235                        if (addr_last  == end)
 236                                s_last[bank]  = sect;
 237                }
 238                if (s_first[bank] >= 0) {
 239                        if (s_last[bank] < 0) {
 240                                if (addr_last > b_end) {
 241                                        s_last[bank] = s_end;
 242                                } else {
 243                                        puts("Error: end address not on sector boundary\n");
 244                                        rcode = 1;
 245                                        break;
 246                                }
 247                        }
 248                        if (s_last[bank] < s_first[bank]) {
 249                                puts("Error: end sector precedes start sector\n");
 250                                rcode = 1;
 251                                break;
 252                        }
 253                        sect = s_last[bank];
 254                        addr_first = (sect == s_end) ? b_end + 1 : info->start[sect + 1];
 255                        (*s_count) += s_last[bank] - s_first[bank] + 1;
 256                } else if (addr_first >= info->start[0] && addr_first < b_end) {
 257                        puts("Error: start address not on sector boundary\n");
 258                        rcode = 1;
 259                        break;
 260                } else if (s_last[bank] >= 0) {
 261                        puts("Error: cannot span across banks when they are"
 262                             " mapped in reverse order\n");
 263                        rcode = 1;
 264                        break;
 265                }
 266        }
 267
 268        return rcode;
 269}
 270#endif /* CONFIG_MTD_NOR_FLASH */
 271
 272static int do_flinfo(struct cmd_tbl *cmdtp, int flag, int argc,
 273                     char *const argv[])
 274{
 275#ifdef CONFIG_MTD_NOR_FLASH
 276        ulong bank;
 277#endif
 278
 279#ifdef CONFIG_MTD_NOR_FLASH
 280        if (argc == 1) {        /* print info for all FLASH banks */
 281                for (bank = 0; bank < CFI_FLASH_BANKS; ++bank) {
 282                        printf("\nBank # %ld: ", bank + 1);
 283
 284                        flash_print_info(&flash_info[bank]);
 285                }
 286                return 0;
 287        }
 288
 289        bank = hextoul(argv[1], NULL);
 290        if (bank < 1 || bank > CFI_FLASH_BANKS) {
 291                printf("Only FLASH Banks # 1 ... # %d supported\n",
 292                       CFI_FLASH_BANKS);
 293                return 1;
 294        }
 295        printf("\nBank # %ld: ", bank);
 296        flash_print_info(&flash_info[bank - 1]);
 297#endif /* CONFIG_MTD_NOR_FLASH */
 298        return 0;
 299}
 300
 301static int do_flerase(struct cmd_tbl *cmdtp, int flag, int argc,
 302                      char *const argv[])
 303{
 304#ifdef CONFIG_MTD_NOR_FLASH
 305        flash_info_t *info = NULL;
 306        ulong bank, addr_first, addr_last;
 307        int n, sect_first = 0, sect_last = 0;
 308#if defined(CONFIG_CMD_MTDPARTS)
 309        struct mtd_device *dev;
 310        struct part_info *part;
 311        u8 dev_type, dev_num, pnum;
 312#endif
 313        int rcode = 0;
 314
 315        if (argc < 2)
 316                return CMD_RET_USAGE;
 317
 318        if (strcmp(argv[1], "all") == 0) {
 319                for (bank = 1; bank <= CFI_FLASH_BANKS; ++bank) {
 320                        printf("Erase Flash Bank # %ld ", bank);
 321                        info = &flash_info[bank - 1];
 322                        rcode = flash_erase(info, 0, info->sector_count - 1);
 323                }
 324                return rcode;
 325        }
 326
 327        n = abbrev_spec(argv[1], &info, &sect_first, &sect_last);
 328        if (n) {
 329                if (n < 0) {
 330                        puts("Bad sector specification\n");
 331                        return 1;
 332                }
 333                printf("Erase Flash Sectors %d-%d in Bank # %zu ",
 334                       sect_first, sect_last, (info - flash_info) + 1);
 335                rcode = flash_erase(info, sect_first, sect_last);
 336                return rcode;
 337        }
 338
 339#if defined(CONFIG_CMD_MTDPARTS)
 340        /* erase <part-id> - erase partition */
 341        if (argc == 2 && mtd_id_parse(argv[1], NULL, &dev_type, &dev_num) == 0) {
 342                mtdparts_init();
 343                if (find_dev_and_part(argv[1], &dev, &pnum, &part) == 0) {
 344                        if (dev->id->type == MTD_DEV_TYPE_NOR) {
 345                                bank = dev->id->num;
 346                                info = &flash_info[bank];
 347                                addr_first = part->offset + info->start[0];
 348                                addr_last = addr_first + part->size - 1;
 349
 350                                printf("Erase Flash Partition %s, bank %ld, 0x%08lx - 0x%08lx ",
 351                                       argv[1], bank, addr_first,
 352                                       addr_last);
 353
 354                                rcode = flash_sect_erase(addr_first, addr_last);
 355                                return rcode;
 356                        }
 357
 358                        printf("cannot erase, not a NOR device\n");
 359                        return 1;
 360                }
 361        }
 362#endif
 363
 364        if (argc != 3)
 365                return CMD_RET_USAGE;
 366
 367        if (strcmp(argv[1], "bank") == 0) {
 368                bank = hextoul(argv[2], NULL);
 369                if (bank < 1 || bank > CFI_FLASH_BANKS) {
 370                        printf("Only FLASH Banks # 1 ... # %d supported\n",
 371                               CFI_FLASH_BANKS);
 372                        return 1;
 373                }
 374                printf("Erase Flash Bank # %ld ", bank);
 375                info = &flash_info[bank - 1];
 376                rcode = flash_erase(info, 0, info->sector_count - 1);
 377                return rcode;
 378        }
 379
 380        if (addr_spec(argv[1], argv[2], &addr_first, &addr_last) < 0) {
 381                printf("Bad address format\n");
 382                return 1;
 383        }
 384
 385        if (addr_first >= addr_last)
 386                return CMD_RET_USAGE;
 387
 388        rcode = flash_sect_erase(addr_first, addr_last);
 389        return rcode;
 390#else
 391        return 0;
 392#endif /* CONFIG_MTD_NOR_FLASH */
 393}
 394
 395#ifdef CONFIG_MTD_NOR_FLASH
 396int flash_sect_erase(ulong addr_first, ulong addr_last)
 397{
 398        flash_info_t *info;
 399        ulong bank;
 400        int s_first[CFI_FLASH_BANKS], s_last[CFI_FLASH_BANKS];
 401        int erased = 0;
 402        int planned;
 403        int rcode = 0;
 404
 405        rcode = flash_fill_sect_ranges(addr_first, addr_last, s_first, s_last, &planned);
 406
 407        if (planned && rcode == 0) {
 408                for (bank = 0, info = &flash_info[0];
 409                     bank < CFI_FLASH_BANKS && rcode == 0;
 410                     ++bank, ++info) {
 411                        if (s_first[bank] >= 0) {
 412                                erased += s_last[bank] - s_first[bank] + 1;
 413                                debug("Erase Flash from 0x%08lx to 0x%08lx in Bank # %ld ",
 414                                      info->start[s_first[bank]],
 415                                      (s_last[bank] == info->sector_count) ?
 416                                      info->start[0] + info->size - 1 :
 417                                      info->start[s_last[bank] + 1] - 1,
 418                                      bank + 1);
 419                                rcode = flash_erase(info, s_first[bank],
 420                                                    s_last[bank]);
 421                        }
 422                }
 423                if (rcode == 0)
 424                        printf("Erased %d sectors\n", erased);
 425        } else if (rcode == 0) {
 426                puts("Error: start and/or end address not on sector boundary\n");
 427                rcode = 1;
 428        }
 429        return rcode;
 430}
 431#endif /* CONFIG_MTD_NOR_FLASH */
 432
 433static int do_protect(struct cmd_tbl *cmdtp, int flag, int argc,
 434                      char *const argv[])
 435{
 436        int rcode = 0;
 437#ifdef CONFIG_MTD_NOR_FLASH
 438        flash_info_t *info = NULL;
 439        ulong bank;
 440        int i, n, sect_first = 0, sect_last = 0;
 441#if defined(CONFIG_CMD_MTDPARTS)
 442        struct mtd_device *dev;
 443        struct part_info *part;
 444        u8 dev_type, dev_num, pnum;
 445#endif
 446#endif /* CONFIG_MTD_NOR_FLASH */
 447#if defined(CONFIG_MTD_NOR_FLASH)
 448        int p;
 449        ulong addr_first, addr_last;
 450#endif
 451
 452        if (argc < 3)
 453                return CMD_RET_USAGE;
 454
 455#if defined(CONFIG_MTD_NOR_FLASH)
 456        if (strcmp(argv[1], "off") == 0)
 457                p = 0;
 458        else if (strcmp(argv[1], "on") == 0)
 459                p = 1;
 460        else
 461                return CMD_RET_USAGE;
 462#endif
 463
 464#ifdef CONFIG_MTD_NOR_FLASH
 465        if (strcmp(argv[2], "all") == 0) {
 466                for (bank = 1; bank <= CFI_FLASH_BANKS; ++bank) {
 467                        info = &flash_info[bank - 1];
 468                        if (info->flash_id == FLASH_UNKNOWN)
 469                                continue;
 470
 471                        printf("%sProtect Flash Bank # %ld\n",
 472                               p ? "" : "Un-", bank);
 473
 474                        for (i = 0; i < info->sector_count; ++i) {
 475#if defined(CONFIG_SYS_FLASH_PROTECTION)
 476                                if (flash_real_protect(info, i, p))
 477                                        rcode = 1;
 478                                putc('.');
 479#else
 480                                info->protect[i] = p;
 481#endif  /* CONFIG_SYS_FLASH_PROTECTION */
 482                        }
 483#if defined(CONFIG_SYS_FLASH_PROTECTION)
 484                        if (!rcode)
 485                                puts(" done\n");
 486#endif  /* CONFIG_SYS_FLASH_PROTECTION */
 487                }
 488                return rcode;
 489        }
 490        n = abbrev_spec(argv[2], &info, &sect_first, &sect_last);
 491        if (n) {
 492                if (n < 0) {
 493                        puts("Bad sector specification\n");
 494                        return 1;
 495                }
 496                printf("%sProtect Flash Sectors %d-%d in Bank # %zu\n",
 497                       p ? "" : "Un-", sect_first, sect_last,
 498                       (info - flash_info) + 1);
 499                for (i = sect_first; i <= sect_last; i++) {
 500#if defined(CONFIG_SYS_FLASH_PROTECTION)
 501                        if (flash_real_protect(info, i, p))
 502                                rcode =  1;
 503                        putc('.');
 504#else
 505                        info->protect[i] = p;
 506#endif  /* CONFIG_SYS_FLASH_PROTECTION */
 507                }
 508
 509#if defined(CONFIG_SYS_FLASH_PROTECTION)
 510                if (!rcode)
 511                        puts(" done\n");
 512#endif  /* CONFIG_SYS_FLASH_PROTECTION */
 513
 514                return rcode;
 515        }
 516
 517#if defined(CONFIG_CMD_MTDPARTS)
 518        /* protect on/off <part-id> */
 519        if (argc == 3 && mtd_id_parse(argv[2], NULL, &dev_type, &dev_num) == 0) {
 520                mtdparts_init();
 521                if (find_dev_and_part(argv[2], &dev, &pnum, &part) == 0) {
 522                        if (dev->id->type == MTD_DEV_TYPE_NOR) {
 523                                bank = dev->id->num;
 524                                info = &flash_info[bank];
 525                                addr_first = part->offset + info->start[0];
 526                                addr_last = addr_first + part->size - 1;
 527
 528                                printf("%sProtect Flash Partition %s, "
 529                                       "bank %ld, 0x%08lx - 0x%08lx\n",
 530                                       p ? "" : "Un", argv[1],
 531                                       bank, addr_first, addr_last);
 532
 533                                rcode = flash_sect_protect(p, addr_first,
 534                                                           addr_last);
 535                                return rcode;
 536                        }
 537
 538                        printf("cannot %sprotect, not a NOR device\n",
 539                               p ? "" : "un");
 540                        return 1;
 541                }
 542        }
 543#endif
 544
 545        if (argc != 4)
 546                return CMD_RET_USAGE;
 547
 548        if (strcmp(argv[2], "bank") == 0) {
 549                bank = hextoul(argv[3], NULL);
 550                if (bank < 1 || bank > CFI_FLASH_BANKS) {
 551                        printf("Only FLASH Banks # 1 ... # %d supported\n",
 552                               CFI_FLASH_BANKS);
 553                        return 1;
 554                }
 555                printf("%sProtect Flash Bank # %ld\n",
 556                       p ? "" : "Un-", bank);
 557                info = &flash_info[bank - 1];
 558
 559                if (info->flash_id == FLASH_UNKNOWN) {
 560                        puts("missing or unknown FLASH type\n");
 561                        return 1;
 562                }
 563                for (i = 0; i < info->sector_count; ++i) {
 564#if defined(CONFIG_SYS_FLASH_PROTECTION)
 565                        if (flash_real_protect(info, i, p))
 566                                rcode =  1;
 567                        putc('.');
 568#else
 569                        info->protect[i] = p;
 570#endif  /* CONFIG_SYS_FLASH_PROTECTION */
 571                }
 572
 573#if defined(CONFIG_SYS_FLASH_PROTECTION)
 574                if (!rcode)
 575                        puts(" done\n");
 576#endif  /* CONFIG_SYS_FLASH_PROTECTION */
 577
 578                return rcode;
 579        }
 580
 581        if (addr_spec(argv[2], argv[3], &addr_first, &addr_last) < 0) {
 582                printf("Bad address format\n");
 583                return 1;
 584        }
 585
 586        if (addr_first >= addr_last)
 587                return CMD_RET_USAGE;
 588
 589        rcode = flash_sect_protect(p, addr_first, addr_last);
 590#endif /* CONFIG_MTD_NOR_FLASH */
 591        return rcode;
 592}
 593
 594#ifdef CONFIG_MTD_NOR_FLASH
 595int flash_sect_protect(int p, ulong addr_first, ulong addr_last)
 596{
 597        flash_info_t *info;
 598        ulong bank;
 599        int s_first[CFI_FLASH_BANKS], s_last[CFI_FLASH_BANKS];
 600        int protected, i;
 601        int planned;
 602        int rcode;
 603
 604        rcode = flash_fill_sect_ranges(addr_first, addr_last, s_first, s_last, &planned);
 605
 606        protected = 0;
 607
 608        if (planned && rcode == 0) {
 609                for (bank = 0, info = &flash_info[0];
 610                     bank < CFI_FLASH_BANKS; ++bank, ++info) {
 611                        if (info->flash_id == FLASH_UNKNOWN)
 612                                continue;
 613
 614                        if (s_first[bank] >= 0 && s_first[bank] <= s_last[bank]) {
 615                                debug("%sProtecting sectors %d..%d in bank %ld\n",
 616                                      p ? "" : "Un-", s_first[bank],
 617                                      s_last[bank], bank + 1);
 618                                protected += s_last[bank] - s_first[bank] + 1;
 619                                for (i = s_first[bank]; i <= s_last[bank]; ++i) {
 620#if defined(CONFIG_SYS_FLASH_PROTECTION)
 621                                        if (flash_real_protect(info, i, p))
 622                                                rcode = 1;
 623                                        putc('.');
 624#else
 625                                        info->protect[i] = p;
 626#endif  /* CONFIG_SYS_FLASH_PROTECTION */
 627                                }
 628                        }
 629                }
 630#if defined(CONFIG_SYS_FLASH_PROTECTION)
 631                puts(" done\n");
 632#endif  /* CONFIG_SYS_FLASH_PROTECTION */
 633
 634                printf("%sProtected %d sectors\n",
 635                       p ? "" : "Un-", protected);
 636        } else if (rcode == 0) {
 637                puts("Error: start and/or end address not on sector boundary\n");
 638                rcode = 1;
 639        }
 640        return rcode;
 641}
 642#endif /* CONFIG_MTD_NOR_FLASH */
 643
 644/**************************************************/
 645#if defined(CONFIG_CMD_MTDPARTS)
 646# define TMP_ERASE      "erase <part-id>\n    - erase partition\n"
 647# define TMP_PROT_ON    "protect on <part-id>\n    - protect partition\n"
 648# define TMP_PROT_OFF   "protect off <part-id>\n    - make partition writable\n"
 649#else
 650# define TMP_ERASE      /* empty */
 651# define TMP_PROT_ON    /* empty */
 652# define TMP_PROT_OFF   /* empty */
 653#endif
 654
 655U_BOOT_CMD(
 656        flinfo,    2,    1,    do_flinfo,
 657        "print FLASH memory information",
 658        "\n    - print information for all FLASH memory banks\n"
 659        "flinfo N\n    - print information for FLASH memory bank # N"
 660);
 661
 662U_BOOT_CMD(
 663        erase,   3,   0,  do_flerase,
 664        "erase FLASH memory",
 665        "start end\n"
 666        "    - erase FLASH from addr 'start' to addr 'end'\n"
 667        "erase start +len\n"
 668        "    - erase FLASH from addr 'start' to the end of sect w/addr 'start'+'len'-1\n"
 669        "erase N:SF[-SL]\n    - erase sectors SF-SL in FLASH bank # N\n"
 670        "erase bank N\n    - erase FLASH bank # N\n"
 671        TMP_ERASE
 672        "erase all\n    - erase all FLASH banks"
 673);
 674
 675U_BOOT_CMD(
 676        protect,  4,  0,   do_protect,
 677        "enable or disable FLASH write protection",
 678        "on  start end\n"
 679        "    - protect FLASH from addr 'start' to addr 'end'\n"
 680        "protect on start +len\n"
 681        "    - protect FLASH from addr 'start' to end of sect w/addr 'start'+'len'-1\n"
 682        "protect on  N:SF[-SL]\n"
 683        "    - protect sectors SF-SL in FLASH bank # N\n"
 684        "protect on  bank N\n    - protect FLASH bank # N\n"
 685        TMP_PROT_ON
 686        "protect on  all\n    - protect all FLASH banks\n"
 687        "protect off start end\n"
 688        "    - make FLASH from addr 'start' to addr 'end' writable\n"
 689        "protect off start +len\n"
 690        "    - make FLASH from addr 'start' to end of sect w/addr 'start'+'len'-1 wrtable\n"
 691        "protect off N:SF[-SL]\n"
 692        "    - make sectors SF-SL writable in FLASH bank # N\n"
 693        "protect off bank N\n    - make FLASH bank # N writable\n"
 694        TMP_PROT_OFF
 695        "protect off all\n    - make all FLASH banks writable"
 696);
 697
 698#undef  TMP_ERASE
 699#undef  TMP_PROT_ON
 700#undef  TMP_PROT_OFF
 701