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