1 /* 2 * (C) Copyright 2000 3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 */ 7 8 /* 9 * FLASH support 10 */ 11 #include <common.h> 12 #include <command.h> 13 14 #if defined(CONFIG_CMD_MTDPARTS) 15 #include <jffs2/jffs2.h> 16 17 /* partition handling routines */ 18 int mtdparts_init(void); 19 int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num); 20 int find_dev_and_part(const char *id, struct mtd_device **dev, 21 u8 *part_num, struct part_info **part); 22 #endif 23 24 #ifdef CONFIG_MTD_NOR_FLASH 25 #include <flash.h> 26 #include <mtd/cfi_flash.h> 27 extern flash_info_t flash_info[]; /* info for FLASH chips */ 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 */ 48 static int 49 abbrev_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 if ((p = strchr (str, ':')) == NULL) 56 return 0; 57 *p++ = '\0'; 58 59 bank = simple_strtoul (str, &ep, 10); 60 if (ep == str || *ep != '\0' || 61 bank < 1 || bank > CONFIG_SYS_MAX_FLASH_BANKS || 62 (fp = &flash_info[bank - 1])->flash_id == FLASH_UNKNOWN) 63 return -1; 64 65 str = p; 66 if ((p = strchr (str, '-')) != NULL) 67 *p++ = '\0'; 68 69 first = simple_strtoul (str, &ep, 10); 70 if (ep == str || *ep != '\0' || first >= fp->sector_count) 71 return -1; 72 73 if (p != NULL) { 74 last = simple_strtoul (p, &ep, 10); 75 if (ep == p || *ep != '\0' || 76 last < first || last >= fp->sector_count) 77 return -1; 78 } else { 79 last = first; 80 } 81 82 *pinfo = fp; 83 *psf = first; 84 *psl = last; 85 86 return 1; 87 } 88 89 /* 90 * Take *addr in Flash and adjust it to fall on the end of its sector 91 */ 92 int flash_sect_roundb (ulong *addr) 93 { 94 flash_info_t *info; 95 ulong bank, sector_end_addr; 96 char found; 97 int i; 98 99 /* find the end addr of the sector where the *addr is */ 100 found = 0; 101 for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS && !found; ++bank) { 102 info = &flash_info[bank]; 103 for (i = 0; i < info->sector_count && !found; ++i) { 104 /* get the end address of the sector */ 105 if (i == info->sector_count - 1) { 106 sector_end_addr = info->start[0] + 107 info->size - 1; 108 } else { 109 sector_end_addr = info->start[i+1] - 1; 110 } 111 112 if (*addr <= sector_end_addr && 113 *addr >= info->start[i]) { 114 found = 1; 115 /* adjust *addr if necessary */ 116 if (*addr < sector_end_addr) 117 *addr = sector_end_addr; 118 } /* sector */ 119 } /* bank */ 120 } 121 if (!found) { 122 /* error, address not in flash */ 123 printf("Error: end address (0x%08lx) not in flash!\n", *addr); 124 return 1; 125 } 126 127 return 0; 128 } 129 130 /* 131 * This function computes the start and end addresses for both 132 * erase and protect commands. The range of the addresses on which 133 * either of the commands is to operate can be given in two forms: 134 * 1. <cmd> start end - operate on <'start', 'end') 135 * 2. <cmd> start +length - operate on <'start', start + length) 136 * If the second form is used and the end address doesn't fall on the 137 * sector boundary, than it will be adjusted to the next sector boundary. 138 * If it isn't in the flash, the function will fail (return -1). 139 * Input: 140 * arg1, arg2: address specification (i.e. both command arguments) 141 * Output: 142 * addr_first, addr_last: computed address range 143 * Return: 144 * 1: success 145 * -1: failure (bad format, bad address). 146 */ 147 static int 148 addr_spec(char *arg1, char *arg2, ulong *addr_first, ulong *addr_last) 149 { 150 char *ep; 151 char len_used; /* indicates if the "start +length" form used */ 152 153 *addr_first = simple_strtoul(arg1, &ep, 16); 154 if (ep == arg1 || *ep != '\0') 155 return -1; 156 157 len_used = 0; 158 if (arg2 && *arg2 == '+'){ 159 len_used = 1; 160 ++arg2; 161 } 162 163 *addr_last = simple_strtoul(arg2, &ep, 16); 164 if (ep == arg2 || *ep != '\0') 165 return -1; 166 167 if (len_used){ 168 /* 169 * *addr_last has the length, compute correct *addr_last 170 * XXX watch out for the integer overflow! Right now it is 171 * checked for in both the callers. 172 */ 173 *addr_last = *addr_first + *addr_last - 1; 174 175 /* 176 * It may happen that *addr_last doesn't fall on the sector 177 * boundary. We want to round such an address to the next 178 * sector boundary, so that the commands don't fail later on. 179 */ 180 181 if (flash_sect_roundb(addr_last) > 0) 182 return -1; 183 } /* "start +length" from used */ 184 185 return 1; 186 } 187 188 static int 189 flash_fill_sect_ranges (ulong addr_first, ulong addr_last, 190 int *s_first, int *s_last, 191 int *s_count ) 192 { 193 flash_info_t *info; 194 ulong bank; 195 int rcode = 0; 196 197 *s_count = 0; 198 199 for (bank=0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank) { 200 s_first[bank] = -1; /* first sector to erase */ 201 s_last [bank] = -1; /* last sector to erase */ 202 } 203 204 for (bank=0,info = &flash_info[0]; 205 (bank < CONFIG_SYS_MAX_FLASH_BANKS) && (addr_first <= addr_last); 206 ++bank, ++info) { 207 ulong b_end; 208 int sect; 209 short s_end; 210 211 if (info->flash_id == FLASH_UNKNOWN) { 212 continue; 213 } 214 215 b_end = info->start[0] + info->size - 1; /* bank end addr */ 216 s_end = info->sector_count - 1; /* last sector */ 217 218 219 for (sect=0; sect < info->sector_count; ++sect) { 220 ulong end; /* last address in current sect */ 221 222 end = (sect == s_end) ? b_end : info->start[sect + 1] - 1; 223 224 if (addr_first > end) 225 continue; 226 if (addr_last < info->start[sect]) 227 continue; 228 229 if (addr_first == info->start[sect]) { 230 s_first[bank] = sect; 231 } 232 if (addr_last == end) { 233 s_last[bank] = sect; 234 } 235 } 236 if (s_first[bank] >= 0) { 237 if (s_last[bank] < 0) { 238 if (addr_last > b_end) { 239 s_last[bank] = s_end; 240 } else { 241 puts ("Error: end address" 242 " 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" 249 " 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 272 static int do_flinfo(cmd_tbl_t *cmdtp, int flag, int argc, 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 <CONFIG_SYS_MAX_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 = simple_strtoul(argv[1], NULL, 16); 289 if ((bank < 1) || (bank > CONFIG_SYS_MAX_FLASH_BANKS)) { 290 printf ("Only FLASH Banks # 1 ... # %d supported\n", 291 CONFIG_SYS_MAX_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 300 static int do_flerase(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 301 { 302 #ifdef CONFIG_MTD_NOR_FLASH 303 flash_info_t *info = NULL; 304 ulong bank, addr_first, addr_last; 305 int n, sect_first = 0, sect_last = 0; 306 #if defined(CONFIG_CMD_MTDPARTS) 307 struct mtd_device *dev; 308 struct part_info *part; 309 u8 dev_type, dev_num, pnum; 310 #endif 311 int rcode = 0; 312 313 if (argc < 2) 314 return CMD_RET_USAGE; 315 316 if (strcmp(argv[1], "all") == 0) { 317 for (bank=1; bank<=CONFIG_SYS_MAX_FLASH_BANKS; ++bank) { 318 printf ("Erase Flash Bank # %ld ", bank); 319 info = &flash_info[bank-1]; 320 rcode = flash_erase (info, 0, info->sector_count-1); 321 } 322 return rcode; 323 } 324 325 if ((n = abbrev_spec(argv[1], &info, §_first, §_last)) != 0) { 326 if (n < 0) { 327 puts ("Bad sector specification\n"); 328 return 1; 329 } 330 printf ("Erase Flash Sectors %d-%d in Bank # %zu ", 331 sect_first, sect_last, (info-flash_info)+1); 332 rcode = flash_erase(info, sect_first, sect_last); 333 return rcode; 334 } 335 336 #if defined(CONFIG_CMD_MTDPARTS) 337 /* erase <part-id> - erase partition */ 338 if ((argc == 2) && (mtd_id_parse(argv[1], NULL, &dev_type, &dev_num) == 0)) { 339 mtdparts_init(); 340 if (find_dev_and_part(argv[1], &dev, &pnum, &part) == 0) { 341 if (dev->id->type == MTD_DEV_TYPE_NOR) { 342 bank = dev->id->num; 343 info = &flash_info[bank]; 344 addr_first = part->offset + info->start[0]; 345 addr_last = addr_first + part->size - 1; 346 347 printf ("Erase Flash Partition %s, " 348 "bank %ld, 0x%08lx - 0x%08lx ", 349 argv[1], bank, addr_first, 350 addr_last); 351 352 rcode = flash_sect_erase(addr_first, addr_last); 353 return rcode; 354 } 355 356 printf("cannot erase, not a NOR device\n"); 357 return 1; 358 } 359 } 360 #endif 361 362 if (argc != 3) 363 return CMD_RET_USAGE; 364 365 if (strcmp(argv[1], "bank") == 0) { 366 bank = simple_strtoul(argv[2], NULL, 16); 367 if ((bank < 1) || (bank > CONFIG_SYS_MAX_FLASH_BANKS)) { 368 printf ("Only FLASH Banks # 1 ... # %d supported\n", 369 CONFIG_SYS_MAX_FLASH_BANKS); 370 return 1; 371 } 372 printf ("Erase Flash Bank # %ld ", bank); 373 info = &flash_info[bank-1]; 374 rcode = flash_erase (info, 0, info->sector_count-1); 375 return rcode; 376 } 377 378 if (addr_spec(argv[1], argv[2], &addr_first, &addr_last) < 0){ 379 printf ("Bad address format\n"); 380 return 1; 381 } 382 383 if (addr_first >= addr_last) 384 return CMD_RET_USAGE; 385 386 rcode = flash_sect_erase(addr_first, addr_last); 387 return rcode; 388 #else 389 return 0; 390 #endif /* CONFIG_MTD_NOR_FLASH */ 391 } 392 393 #ifdef CONFIG_MTD_NOR_FLASH 394 int flash_sect_erase (ulong addr_first, ulong addr_last) 395 { 396 flash_info_t *info; 397 ulong bank; 398 int s_first[CONFIG_SYS_MAX_FLASH_BANKS], s_last[CONFIG_SYS_MAX_FLASH_BANKS]; 399 int erased = 0; 400 int planned; 401 int rcode = 0; 402 403 rcode = flash_fill_sect_ranges (addr_first, addr_last, 404 s_first, s_last, &planned ); 405 406 if (planned && (rcode == 0)) { 407 for (bank=0,info = &flash_info[0]; 408 (bank < CONFIG_SYS_MAX_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 " 413 "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], 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" 426 " not on sector boundary\n"); 427 rcode = 1; 428 } 429 return rcode; 430 } 431 #endif /* CONFIG_MTD_NOR_FLASH */ 432 433 static int do_protect(cmd_tbl_t *cmdtp, int flag, int argc, 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<=CONFIG_SYS_MAX_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) puts (" done\n"); 484 #endif /* CONFIG_SYS_FLASH_PROTECTION */ 485 } 486 return rcode; 487 } 488 489 if ((n = abbrev_spec(argv[2], &info, §_first, §_last)) != 0) { 490 if (n < 0) { 491 puts ("Bad sector specification\n"); 492 return 1; 493 } 494 printf("%sProtect Flash Sectors %d-%d in Bank # %zu\n", 495 p ? "" : "Un-", sect_first, sect_last, 496 (info-flash_info)+1); 497 for (i = sect_first; i <= sect_last; i++) { 498 #if defined(CONFIG_SYS_FLASH_PROTECTION) 499 if (flash_real_protect(info, i, p)) 500 rcode = 1; 501 putc ('.'); 502 #else 503 info->protect[i] = p; 504 #endif /* CONFIG_SYS_FLASH_PROTECTION */ 505 } 506 507 #if defined(CONFIG_SYS_FLASH_PROTECTION) 508 if (!rcode) puts (" done\n"); 509 #endif /* CONFIG_SYS_FLASH_PROTECTION */ 510 511 return rcode; 512 } 513 514 #if defined(CONFIG_CMD_MTDPARTS) 515 /* protect on/off <part-id> */ 516 if ((argc == 3) && (mtd_id_parse(argv[2], NULL, &dev_type, &dev_num) == 0)) { 517 mtdparts_init(); 518 if (find_dev_and_part(argv[2], &dev, &pnum, &part) == 0) { 519 if (dev->id->type == MTD_DEV_TYPE_NOR) { 520 bank = dev->id->num; 521 info = &flash_info[bank]; 522 addr_first = part->offset + info->start[0]; 523 addr_last = addr_first + part->size - 1; 524 525 printf ("%sProtect Flash Partition %s, " 526 "bank %ld, 0x%08lx - 0x%08lx\n", 527 p ? "" : "Un", argv[1], 528 bank, addr_first, addr_last); 529 530 rcode = flash_sect_protect (p, addr_first, addr_last); 531 return rcode; 532 } 533 534 printf("cannot %sprotect, not a NOR device\n", 535 p ? "" : "un"); 536 return 1; 537 } 538 } 539 #endif 540 541 if (argc != 4) 542 return CMD_RET_USAGE; 543 544 if (strcmp(argv[2], "bank") == 0) { 545 bank = simple_strtoul(argv[3], NULL, 16); 546 if ((bank < 1) || (bank > CONFIG_SYS_MAX_FLASH_BANKS)) { 547 printf ("Only FLASH Banks # 1 ... # %d supported\n", 548 CONFIG_SYS_MAX_FLASH_BANKS); 549 return 1; 550 } 551 printf ("%sProtect Flash Bank # %ld\n", 552 p ? "" : "Un-", bank); 553 info = &flash_info[bank-1]; 554 555 if (info->flash_id == FLASH_UNKNOWN) { 556 puts ("missing or unknown FLASH type\n"); 557 return 1; 558 } 559 for (i=0; i<info->sector_count; ++i) { 560 #if defined(CONFIG_SYS_FLASH_PROTECTION) 561 if (flash_real_protect(info, i, p)) 562 rcode = 1; 563 putc ('.'); 564 #else 565 info->protect[i] = p; 566 #endif /* CONFIG_SYS_FLASH_PROTECTION */ 567 } 568 569 #if defined(CONFIG_SYS_FLASH_PROTECTION) 570 if (!rcode) puts (" done\n"); 571 #endif /* CONFIG_SYS_FLASH_PROTECTION */ 572 573 return rcode; 574 } 575 576 if (addr_spec(argv[2], argv[3], &addr_first, &addr_last) < 0){ 577 printf("Bad address format\n"); 578 return 1; 579 } 580 581 if (addr_first >= addr_last) 582 return CMD_RET_USAGE; 583 584 rcode = flash_sect_protect (p, addr_first, addr_last); 585 #endif /* CONFIG_MTD_NOR_FLASH */ 586 return rcode; 587 } 588 589 #ifdef CONFIG_MTD_NOR_FLASH 590 int flash_sect_protect (int p, ulong addr_first, ulong addr_last) 591 { 592 flash_info_t *info; 593 ulong bank; 594 int s_first[CONFIG_SYS_MAX_FLASH_BANKS], s_last[CONFIG_SYS_MAX_FLASH_BANKS]; 595 int protected, i; 596 int planned; 597 int rcode; 598 599 rcode = flash_fill_sect_ranges( addr_first, addr_last, s_first, s_last, &planned ); 600 601 protected = 0; 602 603 if (planned && (rcode == 0)) { 604 for (bank=0,info = &flash_info[0]; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank, ++info) { 605 if (info->flash_id == FLASH_UNKNOWN) { 606 continue; 607 } 608 609 if (s_first[bank]>=0 && s_first[bank]<=s_last[bank]) { 610 debug ("%sProtecting sectors %d..%d in bank %ld\n", 611 p ? "" : "Un-", 612 s_first[bank], s_last[bank], bank+1); 613 protected += s_last[bank] - s_first[bank] + 1; 614 for (i=s_first[bank]; i<=s_last[bank]; ++i) { 615 #if defined(CONFIG_SYS_FLASH_PROTECTION) 616 if (flash_real_protect(info, i, p)) 617 rcode = 1; 618 putc ('.'); 619 #else 620 info->protect[i] = p; 621 #endif /* CONFIG_SYS_FLASH_PROTECTION */ 622 } 623 } 624 } 625 #if defined(CONFIG_SYS_FLASH_PROTECTION) 626 puts (" done\n"); 627 #endif /* CONFIG_SYS_FLASH_PROTECTION */ 628 629 printf ("%sProtected %d sectors\n", 630 p ? "" : "Un-", protected); 631 } else if (rcode == 0) { 632 puts ("Error: start and/or end address" 633 " not on sector boundary\n"); 634 rcode = 1; 635 } 636 return rcode; 637 } 638 #endif /* CONFIG_MTD_NOR_FLASH */ 639 640 641 /**************************************************/ 642 #if defined(CONFIG_CMD_MTDPARTS) 643 # define TMP_ERASE "erase <part-id>\n - erase partition\n" 644 # define TMP_PROT_ON "protect on <part-id>\n - protect partition\n" 645 # define TMP_PROT_OFF "protect off <part-id>\n - make partition writable\n" 646 #else 647 # define TMP_ERASE /* empty */ 648 # define TMP_PROT_ON /* empty */ 649 # define TMP_PROT_OFF /* empty */ 650 #endif 651 652 U_BOOT_CMD( 653 flinfo, 2, 1, do_flinfo, 654 "print FLASH memory information", 655 "\n - print information for all FLASH memory banks\n" 656 "flinfo N\n - print information for FLASH memory bank # N" 657 ); 658 659 U_BOOT_CMD( 660 erase, 3, 0, do_flerase, 661 "erase FLASH memory", 662 "start end\n" 663 " - erase FLASH from addr 'start' to addr 'end'\n" 664 "erase start +len\n" 665 " - erase FLASH from addr 'start' to the end of sect " 666 "w/addr 'start'+'len'-1\n" 667 "erase N:SF[-SL]\n - erase sectors SF-SL in FLASH bank # N\n" 668 "erase bank N\n - erase FLASH bank # N\n" 669 TMP_ERASE 670 "erase all\n - erase all FLASH banks" 671 ); 672 673 U_BOOT_CMD( 674 protect, 4, 0, do_protect, 675 "enable or disable FLASH write protection", 676 "on start end\n" 677 " - protect FLASH from addr 'start' to addr 'end'\n" 678 "protect on start +len\n" 679 " - protect FLASH from addr 'start' to end of sect " 680 "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 " 690 "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