Fix 'maybe-uninitialized' warnings
[oweals/u-boot_mod.git] / u-boot / common / cmd_flash.c
1 /*
2  * (C) Copyright 2000
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
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.
12  *
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.
17  *
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,
21  * MA 02111-1307 USA
22  */
23
24 /*
25  * FLASH support
26  */
27 #include <common.h>
28 #include <command.h>
29
30 #if defined(CONFIG_CMD_FLASH)
31
32 extern flash_info_t flash_info[]; /* info for FLASH chips */
33
34 /*
35  * The user interface starts numbering for Flash banks with 1
36  * for historical reasons.
37  */
38
39 /*
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
44  * start at zero.
45  *
46  * returns:     1       - correct spec; *pinfo, *psf and *psl are
47  *                        set appropriately
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.
52  */
53 static int abbrev_spec(char *str, flash_info_t ** pinfo, int *psf, int *psl){
54         flash_info_t *fp;
55         int bank, first, last;
56         char *p, *ep;
57
58         if((p = strchr(str, ':')) == NULL){
59                 return(0);
60         }
61
62         *p++ = '\0';
63
64         bank = simple_strtoul(str, &ep, 10);
65
66         if(ep == str || *ep != '\0' || bank < 1 || bank > CFG_MAX_FLASH_BANKS || (fp = &flash_info[bank - 1])->flash_id == FLASH_UNKNOWN){
67                 return(-1);
68         }
69
70         str = p;
71
72         if((p = strchr(str, '-')) != NULL){
73                 *p++ = '\0';
74         }
75
76         first = simple_strtoul(str, &ep, 10);
77
78         if(ep == str || *ep != '\0' || first >= fp->sector_count){
79                 return(-1);
80         }
81
82         if(p != NULL){
83                 last = simple_strtoul(p, &ep, 10);
84
85                 if(ep == p || *ep != '\0' || last < first || last >= fp->sector_count){
86                         return(-1);
87                 }
88         } else {
89                 last = first;
90         }
91
92         *pinfo = fp;
93         *psf = first;
94         *psl = last;
95
96         return(1);
97 }
98
99 /*
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).
108  * Input:
109  *    arg1, arg2: address specification (i.e. both command arguments)
110  * Output:
111  *    addr_first, addr_last: computed address range
112  * Return:
113  *    1: success
114  *   -1: failure (bad format, bad address).
115  */
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 */
118         char *ep;
119
120         *addr_first = simple_strtoul(arg1, &ep, 16);
121
122         if(ep == arg1 || *ep != '\0'){
123                 return(-1);
124         }
125
126         if(arg2 && *arg2 == '+'){
127                 len_used = 1;
128                 ++arg2;
129         }
130
131         *addr_last = simple_strtoul(arg2, &ep, 16);
132
133         if(ep == arg2 || *ep != '\0'){
134                 return(-1);
135         }
136
137         if(len_used){
138                 char found = 0;
139                 ulong bank;
140
141                 /*
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.
145                  */
146                 *addr_last = *addr_first + *addr_last - 1;
147
148                 /*
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.
152                  */
153
154                 /* find the end addr of the sector where the *addr_last is */
155                 for(bank = 0; bank < CFG_MAX_FLASH_BANKS && !found; ++bank){
156                         int i;
157                         flash_info_t *info = &flash_info[bank];
158
159                         for(i = 0; i < info->sector_count && !found; ++i){
160                                 /* get the end address of the sector */
161                                 ulong sector_end_addr;
162
163                                 if(i == info->sector_count - 1){
164                                         sector_end_addr = info->start[0] + info->size - 1;
165                                 } else {
166                                         sector_end_addr = info->start[i + 1] - 1;
167                                 }
168
169                                 if(*addr_last <= sector_end_addr && *addr_last >= info->start[i]){
170                                         /* sector found */
171                                         found = 1;
172
173                                         /* adjust *addr_last if necessary */
174                                         if(*addr_last < sector_end_addr){
175                                                 *addr_last = sector_end_addr;
176                                         }
177                                 }
178                         } /* sector */
179                 } /* bank */
180
181                 if(!found){
182                         /* error, addres not in flash */
183                         printf_err("end address (0x%08lx) not in FLASH!\n", *addr_last);
184                         return(-1);
185                 }
186
187         } /* "start +length" from used */
188
189         return(1);
190 }
191
192 static int flash_fill_sect_ranges(ulong addr_first, ulong addr_last, int *s_first, int *s_last, int *s_count){
193         flash_info_t *info;
194         ulong bank;
195         int rcode = 0;
196
197         *s_count = 0;
198
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 */
202         }
203
204         for(bank = 0, info = &flash_info[0]; (bank < CFG_MAX_FLASH_BANKS) && (addr_first <= addr_last); ++bank, ++info){
205                 ulong b_end;
206                 int sect;
207                 short s_end;
208
209                 if(info->flash_id == FLASH_UNKNOWN){
210                         continue;
211                 }
212
213                 b_end = info->start[0] + info->size - 1;        /* bank end addr */
214                 s_end = info->sector_count - 1;                         /* last sector   */
215
216                 for(sect = 0; sect < info->sector_count; ++sect){
217                         ulong end; /* last address in current sect      */
218
219                         end = (sect == s_end) ? b_end : info->start[sect + 1] - 1;
220
221                         if(addr_first > end){
222                                 continue;
223                         }
224
225                         if(addr_last < info->start[sect]){
226                                 continue;
227                         }
228
229                         if(addr_first == info->start[sect]){
230                                 s_first[bank] = sect;
231                         }
232
233                         if(addr_last == end){
234                                 s_last[bank] = sect;
235                         }
236                 }
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                                         printf_err("end address not on sector boundary\n");
244                                         rcode = 1;
245                                         break;
246                                 }
247                         }
248
249                         if(s_last[bank] < s_first[bank]){
250                                 printf_err("end sector precedes start sector\n");
251                                 rcode = 1;
252                                 break;
253                         }
254
255                         sect = s_last[bank];
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");
260                         rcode = 1;
261                         break;
262                 } else if(s_last[bank] >= 0){
263                         printf_err("cannot span across banks when they are mapped in reverse order\n");
264                         rcode = 1;
265                         break;
266                 }
267         }
268
269         return(rcode);
270 }
271
272 int do_flerase(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){
273         flash_info_t *info;
274         ulong bank, addr_first, addr_last;
275         int n;
276         int rcode = 0;
277         int sect_first = 0, sect_last = 0;
278
279         if(argc < 2){
280                 print_cmd_help(cmdtp);
281                 return(1);
282         }
283
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);
290                 }
291                 return(rcode);
292         }
293
294         if((n = abbrev_spec(argv[1], &info, &sect_first, &sect_last)) != 0){
295                 if(n < 0){
296                         printf_err("bad sector spec\n");
297                         return(1);
298                 }
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);
301                 return(rcode);
302         }
303
304         if(argc != 3){
305                 print_cmd_help(cmdtp);
306                 return(1);
307         }
308
309         if(strcmp(argv[1], "bank") == 0){
310                 bank = simple_strtoul(argv[2], NULL, 16);
311
312                 if((bank < 1) || (bank > CFG_MAX_FLASH_BANKS)){
313                         printf_err("only FLASH banks #1...#%d supported\n", CFG_MAX_FLASH_BANKS);
314                         return(1);
315                 }
316
317                 printf("Erase FLASH bank #%ld ", bank);
318                 info = &flash_info[bank - 1];
319                 rcode = flash_erase(info, 0, info->sector_count - 1);
320                 return(rcode);
321         }
322
323         if(addr_spec(argv[1], argv[2], &addr_first, &addr_last) < 0){
324                 printf_err("bad address format\n");
325                 return(1);
326         }
327
328         if(addr_first >= addr_last){
329                 print_cmd_help(cmdtp);
330                 return(1);
331         }
332
333         rcode = flash_sect_erase(addr_first, addr_last);
334         return(rcode);
335 }
336
337 int flash_sect_erase(ulong addr_first, ulong addr_last){
338         flash_info_t *info;
339         ulong bank;
340         int s_first[CFG_MAX_FLASH_BANKS], s_last[CFG_MAX_FLASH_BANKS];
341         int erased = 0;
342         int planned;
343         int rcode = 0;
344
345         rcode = flash_fill_sect_ranges(addr_first, addr_last, s_first, s_last, &planned);
346
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]);
353                         }
354                 }
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");
358                 rcode = 1;
359         }
360         return(rcode);
361 }
362
363 /**************************************************/
364
365 U_BOOT_CMD(erase, 3, 1, do_flerase, "erase FLASH memory\n",
366                 "start end\n"
367                 "\t- erase FLASH from addr 'start' to addr 'end'\n"
368                 "erase start +len\n"
369                 "\t- erase FLASH from addr 'start' to the end of sect w/addr 'start'+'len'-1\n"
370                 "erase N:SF[-SL]\n"
371                 "\t- erase sectors SF-SL in FLASH bank #N\n"
372                 "erase bank N\n"
373                 "\t- erase FLASH bank #N\n"
374                 "erase all\n"
375                 "\t- erase all FLASH banks\n");
376
377 #endif /* CONFIG_CMD_FLASH */