3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5 * See file CREDITS for list of people who contributed to this
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
30 #if defined(CONFIG_CMD_FLASH)
32 extern flash_info_t flash_info[]; /* info for FLASH chips */
35 * The user interface starts numbering for Flash banks with 1
36 * for historical reasons.
40 * this routine looks for an abbreviated flash range specification.
41 * the syntax is B:SF[-SL], where B is the bank number, SF is the first
42 * sector to erase, and SL is the last sector to erase (defaults to SF).
43 * bank numbers start at 1 to be consistent with other specs, sector numbers
46 * returns: 1 - correct spec; *pinfo, *psf and *psl are
48 * 0 - doesn't look like an abbreviated spec
49 * -1 - looks like an abbreviated spec, but got
50 * a parsing error, a number out of range,
51 * or an invalid flash bank.
53 static int abbrev_spec(char *str, flash_info_t ** pinfo, int *psf, int *psl){
55 int bank, first, last;
58 if((p = strchr(str, ':')) == NULL){
64 bank = simple_strtoul(str, &ep, 10);
66 if(ep == str || *ep != '\0' || bank < 1 || bank > CFG_MAX_FLASH_BANKS || (fp = &flash_info[bank - 1])->flash_id == FLASH_UNKNOWN){
72 if((p = strchr(str, '-')) != NULL){
76 first = simple_strtoul(str, &ep, 10);
78 if(ep == str || *ep != '\0' || first >= fp->sector_count){
83 last = simple_strtoul(p, &ep, 10);
85 if(ep == p || *ep != '\0' || last < first || last >= fp->sector_count){
100 * This function computes the start and end addresses for both
101 * erase and protect commands. The range of the addresses on which
102 * either of the commands is to operate can be given in two forms:
103 * 1. <cmd> start end - operate on <'start', 'end')
104 * 2. <cmd> start +length - operate on <'start', start + length)
105 * If the second form is used and the end address doesn't fall on the
106 * sector boundary, than it will be adjusted to the next sector boundary.
107 * If it isn't in the flash, the function will fail (return -1).
109 * arg1, arg2: address specification (i.e. both command arguments)
111 * addr_first, addr_last: computed address range
114 * -1: failure (bad format, bad address).
116 static int addr_spec(char *arg1, char *arg2, ulong *addr_first, ulong *addr_last){
117 char len_used = 0; /* indicates if the "start +length" form used */
120 *addr_first = simple_strtoul(arg1, &ep, 16);
122 if(ep == arg1 || *ep != '\0'){
126 if(arg2 && *arg2 == '+'){
131 *addr_last = simple_strtoul(arg2, &ep, 16);
133 if(ep == arg2 || *ep != '\0'){
142 * *addr_last has the length, compute correct *addr_last
143 * XXX watch out for the integer overflow! Right now it is
144 * checked for in both the callers.
146 *addr_last = *addr_first + *addr_last - 1;
149 * It may happen that *addr_last doesn't fall on the sector
150 * boundary. We want to round such an address to the next
151 * sector boundary, so that the commands don't fail later on.
154 /* find the end addr of the sector where the *addr_last is */
155 for(bank = 0; bank < CFG_MAX_FLASH_BANKS && !found; ++bank){
157 flash_info_t *info = &flash_info[bank];
159 for(i = 0; i < info->sector_count && !found; ++i){
160 /* get the end address of the sector */
161 ulong sector_end_addr;
163 if(i == info->sector_count - 1){
164 sector_end_addr = info->start[0] + info->size - 1;
166 sector_end_addr = info->start[i + 1] - 1;
169 if(*addr_last <= sector_end_addr && *addr_last >= info->start[i]){
173 /* adjust *addr_last if necessary */
174 if(*addr_last < sector_end_addr){
175 *addr_last = sector_end_addr;
182 /* error, addres not in flash */
183 printf_err("end address (0x%08lx) not in FLASH!\n", *addr_last);
187 } /* "start +length" from used */
192 static int flash_fill_sect_ranges(ulong addr_first, ulong addr_last, int *s_first, int *s_last, int *s_count){
199 for(bank = 0; bank < CFG_MAX_FLASH_BANKS; ++bank){
200 s_first[bank] = -1; /* first sector to erase */
201 s_last[bank] = -1; /* last sector to erase */
204 for(bank = 0, info = &flash_info[0]; (bank < CFG_MAX_FLASH_BANKS) && (addr_first <= addr_last); ++bank, ++info){
209 if(info->flash_id == FLASH_UNKNOWN){
213 b_end = info->start[0] + info->size - 1; /* bank end addr */
214 s_end = info->sector_count - 1; /* last sector */
216 for(sect = 0; sect < info->sector_count; ++sect){
217 ulong end; /* last address in current sect */
219 end = (sect == s_end) ? b_end : info->start[sect + 1] - 1;
221 if(addr_first > end){
225 if(addr_last < info->start[sect]){
229 if(addr_first == info->start[sect]){
230 s_first[bank] = sect;
233 if(addr_last == end){
238 if(s_first[bank] >= 0){
239 if(s_last[bank] < 0){
240 if(addr_last > b_end){
241 s_last[bank] = s_end;
243 printf_err("end address not on sector boundary\n");
249 if(s_last[bank] < s_first[bank]){
250 printf_err("end sector precedes start sector\n");
256 addr_first = (sect == s_end) ? b_end + 1 : info->start[sect + 1];
257 (*s_count) += s_last[bank] - s_first[bank] + 1;
258 } else if(addr_first >= info->start[0] && addr_first < b_end){
259 printf_err("start address not on sector boundary\n");
262 } else if(s_last[bank] >= 0){
263 printf_err("cannot span across banks when they are mapped in reverse order\n");
272 int do_flerase(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){
274 ulong bank, addr_first, addr_last;
277 int sect_first = 0, sect_last = 0;
280 print_cmd_help(cmdtp);
284 // erase whole flash?
285 if(strcmp(argv[1], "all") == 0){
286 for(bank = 1; bank <= CFG_MAX_FLASH_BANKS; ++bank){
287 printf("Erase FLASH bank #%ld ", bank);
288 info = &flash_info[bank - 1];
289 rcode = flash_erase(info, 0, info->sector_count - 1);
294 if((n = abbrev_spec(argv[1], &info, §_first, §_last)) != 0){
296 printf_err("bad sector spec\n");
299 printf("Erase FLASH sectors %d-%d in bank #%d ", sect_first, sect_last, (info - flash_info) + 1);
300 rcode = flash_erase(info, sect_first, sect_last);
305 print_cmd_help(cmdtp);
309 if(strcmp(argv[1], "bank") == 0){
310 bank = simple_strtoul(argv[2], NULL, 16);
312 if((bank < 1) || (bank > CFG_MAX_FLASH_BANKS)){
313 printf_err("only FLASH banks #1...#%d supported\n", CFG_MAX_FLASH_BANKS);
317 printf("Erase FLASH bank #%ld ", bank);
318 info = &flash_info[bank - 1];
319 rcode = flash_erase(info, 0, info->sector_count - 1);
323 if(addr_spec(argv[1], argv[2], &addr_first, &addr_last) < 0){
324 printf_err("bad address format\n");
328 if(addr_first >= addr_last){
329 print_cmd_help(cmdtp);
333 rcode = flash_sect_erase(addr_first, addr_last);
337 int flash_sect_erase(ulong addr_first, ulong addr_last){
340 int s_first[CFG_MAX_FLASH_BANKS], s_last[CFG_MAX_FLASH_BANKS];
345 rcode = flash_fill_sect_ranges(addr_first, addr_last, s_first, s_last, &planned);
347 if(planned && (rcode == 0)){
348 for(bank = 0, info = &flash_info[0]; (bank < CFG_MAX_FLASH_BANKS) && (rcode == 0); ++bank, ++info){
349 if(s_first[bank] >= 0){
350 erased += s_last[bank] - s_first[bank] + 1;
351 printf("Erase FLASH from 0x%08lX to 0x%08lX in bank #%ld\n", info->start[s_first[bank]], (s_last[bank] == info->sector_count - 1) ? info->start[0] + info->size - 1 : info->start[s_last[bank] + 1] - 1, bank + 1);
352 rcode = flash_erase(info, s_first[bank], s_last[bank]);
355 printf("Erased sectors: %d\n\n", erased);
356 } else if(rcode == 0){
357 printf_err("start and/or end address not on sector boundary\n");
363 /**************************************************/
365 U_BOOT_CMD(erase, 3, 1, do_flerase, "erase FLASH memory\n",
367 "\t- erase FLASH from addr 'start' to addr 'end'\n"
369 "\t- erase FLASH from addr 'start' to the end of sect w/addr 'start'+'len'-1\n"
371 "\t- erase sectors SF-SL in FLASH bank #N\n"
373 "\t- erase FLASH bank #N\n"
375 "\t- erase all FLASH banks\n");
377 #endif /* CONFIG_CMD_FLASH */