]>
Commit | Line | Data |
---|---|---|
c7de829c WD |
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> | |
c7de829c WD |
29 | #include <flash.h> |
30 | ||
c91898bb | 31 | #if defined(CONFIG_CMD_FLASH) |
c7de829c WD |
32 | |
33 | extern flash_info_t flash_info[]; /* info for FLASH chips */ | |
34 | ||
35 | /* | |
36 | * The user interface starts numbering for Flash banks with 1 | |
37 | * for historical reasons. | |
38 | */ | |
39 | ||
40 | /* | |
41 | * this routine looks for an abbreviated flash range specification. | |
42 | * the syntax is B:SF[-SL], where B is the bank number, SF is the first | |
43 | * sector to erase, and SL is the last sector to erase (defaults to SF). | |
44 | * bank numbers start at 1 to be consistent with other specs, sector numbers | |
45 | * start at zero. | |
46 | * | |
47 | * returns: 1 - correct spec; *pinfo, *psf and *psl are | |
48 | * set appropriately | |
49 | * 0 - doesn't look like an abbreviated spec | |
50 | * -1 - looks like an abbreviated spec, but got | |
51 | * a parsing error, a number out of range, | |
52 | * or an invalid flash bank. | |
53 | */ | |
54 | static int | |
55 | abbrev_spec(char *str, flash_info_t **pinfo, int *psf, int *psl) | |
56 | { | |
57 | flash_info_t *fp; | |
58 | int bank, first, last; | |
59 | char *p, *ep; | |
60 | ||
61 | if ((p = strchr(str, ':')) == NULL) | |
62 | return 0; | |
63 | *p++ = '\0'; | |
64 | ||
65 | bank = simple_strtoul(str, &ep, 10); | |
66 | if (ep == str || *ep != '\0' || | |
67 | bank < 1 || bank > CFG_MAX_FLASH_BANKS || | |
68 | (fp = &flash_info[bank - 1])->flash_id == FLASH_UNKNOWN) | |
69 | return -1; | |
70 | ||
71 | str = p; | |
72 | if ((p = strchr(str, '-')) != NULL) | |
73 | *p++ = '\0'; | |
74 | ||
75 | first = simple_strtoul(str, &ep, 10); | |
76 | if (ep == str || *ep != '\0' || first >= fp->sector_count) | |
77 | return -1; | |
78 | ||
79 | if (p != NULL) { | |
80 | last = simple_strtoul(p, &ep, 10); | |
81 | if (ep == p || *ep != '\0' || | |
82 | last < first || last >= fp->sector_count) | |
83 | return -1; | |
84 | } | |
85 | else | |
86 | last = first; | |
87 | ||
88 | *pinfo = fp; | |
89 | *psf = first; | |
90 | *psl = last; | |
91 | ||
92 | return 1; | |
93 | } | |
94 | int do_flinfo (cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[]) | |
95 | { | |
96 | ulong bank; | |
97 | ||
98 | if (argc == 1) { /* print info for all FLASH banks */ | |
99 | for (bank=0; bank <CFG_MAX_FLASH_BANKS; ++bank) { | |
27b207fd | 100 | printf ("\nBank # %ld: ", bank+1); |
c7de829c WD |
101 | |
102 | flash_print_info (&flash_info[bank]); | |
103 | } | |
104 | return 0; | |
105 | } | |
106 | ||
107 | bank = simple_strtoul(argv[1], NULL, 16); | |
108 | if ((bank < 1) || (bank > CFG_MAX_FLASH_BANKS)) { | |
27b207fd | 109 | printf ("Only FLASH Banks # 1 ... # %d supported\n", |
c7de829c WD |
110 | CFG_MAX_FLASH_BANKS); |
111 | return 1; | |
112 | } | |
27b207fd | 113 | printf ("\nBank # %ld: ", bank); |
c7de829c WD |
114 | flash_print_info (&flash_info[bank-1]); |
115 | return 0; | |
116 | } | |
117 | int do_flerase(cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[]) | |
118 | { | |
119 | flash_info_t *info; | |
120 | ulong bank, addr_first, addr_last; | |
121 | int n, sect_first, sect_last; | |
122 | int rcode = 0; | |
123 | ||
124 | if (argc < 2) { | |
27b207fd | 125 | printf ("Usage:\n%s\n", cmdtp->usage); |
c7de829c WD |
126 | return 1; |
127 | } | |
128 | ||
129 | if (strcmp(argv[1], "all") == 0) { | |
130 | for (bank=1; bank<=CFG_MAX_FLASH_BANKS; ++bank) { | |
27b207fd | 131 | printf ("Erase Flash Bank # %ld ", bank); |
c7de829c WD |
132 | info = &flash_info[bank-1]; |
133 | rcode = flash_erase (info, 0, info->sector_count-1); | |
134 | } | |
135 | return rcode; | |
136 | } | |
137 | ||
138 | if ((n = abbrev_spec(argv[1], &info, §_first, §_last)) != 0) { | |
139 | if (n < 0) { | |
27b207fd | 140 | printf("Bad sector specification\n"); |
c7de829c WD |
141 | return 1; |
142 | } | |
27b207fd | 143 | printf ("Erase Flash Sectors %d-%d in Bank # %d ", |
c7de829c WD |
144 | sect_first, sect_last, (info-flash_info)+1); |
145 | rcode = flash_erase(info, sect_first, sect_last); | |
146 | return rcode; | |
147 | } | |
148 | ||
149 | if (argc != 3) { | |
27b207fd | 150 | printf ("Usage:\n%s\n", cmdtp->usage); |
c7de829c WD |
151 | return 1; |
152 | } | |
153 | ||
154 | if (strcmp(argv[1], "bank") == 0) { | |
155 | bank = simple_strtoul(argv[2], NULL, 16); | |
156 | if ((bank < 1) || (bank > CFG_MAX_FLASH_BANKS)) { | |
27b207fd | 157 | printf ("Only FLASH Banks # 1 ... # %d supported\n", |
c7de829c WD |
158 | CFG_MAX_FLASH_BANKS); |
159 | return 1; | |
160 | } | |
27b207fd | 161 | printf ("Erase Flash Bank # %ld ", bank); |
c7de829c WD |
162 | info = &flash_info[bank-1]; |
163 | rcode = flash_erase (info, 0, info->sector_count-1); | |
164 | return rcode; | |
165 | } | |
166 | ||
167 | addr_first = simple_strtoul(argv[1], NULL, 16); | |
168 | addr_last = simple_strtoul(argv[2], NULL, 16); | |
169 | ||
170 | if (addr_first >= addr_last) { | |
27b207fd | 171 | printf ("Usage:\n%s\n", cmdtp->usage); |
c7de829c WD |
172 | return 1; |
173 | } | |
174 | ||
27b207fd | 175 | printf ("Erase Flash from 0x%08lx to 0x%08lx ", addr_first, addr_last); |
c7de829c WD |
176 | rcode = flash_sect_erase(addr_first, addr_last); |
177 | return rcode; | |
178 | } | |
179 | ||
180 | int flash_sect_erase (ulong addr_first, ulong addr_last) | |
181 | { | |
182 | flash_info_t *info; | |
183 | ulong bank; | |
184 | int s_first, s_last; | |
185 | int erased; | |
186 | int rcode = 0; | |
187 | ||
188 | erased = 0; | |
189 | ||
190 | for (bank=0,info=&flash_info[0]; bank < CFG_MAX_FLASH_BANKS; ++bank, ++info) { | |
191 | ulong b_end; | |
192 | int sect; | |
193 | ||
194 | if (info->flash_id == FLASH_UNKNOWN) { | |
195 | continue; | |
196 | } | |
197 | ||
198 | b_end = info->start[0] + info->size - 1; /* bank end addr */ | |
199 | ||
200 | s_first = -1; /* first sector to erase */ | |
201 | s_last = -1; /* last sector to erase */ | |
202 | ||
203 | for (sect=0; sect < info->sector_count; ++sect) { | |
204 | ulong end; /* last address in current sect */ | |
205 | short s_end; | |
206 | ||
207 | s_end = info->sector_count - 1; | |
208 | ||
209 | end = (sect == s_end) ? b_end : info->start[sect + 1] - 1; | |
210 | ||
211 | if (addr_first > end) | |
212 | continue; | |
213 | if (addr_last < info->start[sect]) | |
214 | continue; | |
215 | ||
216 | if (addr_first == info->start[sect]) { | |
217 | s_first = sect; | |
218 | } | |
219 | if (addr_last == end) { | |
220 | s_last = sect; | |
221 | } | |
222 | } | |
223 | if (s_first>=0 && s_first<=s_last) { | |
224 | erased += s_last - s_first + 1; | |
225 | rcode = flash_erase (info, s_first, s_last); | |
226 | } | |
227 | } | |
228 | if (erased) { | |
27b207fd | 229 | /* printf ("Erased %d sectors\n", erased); */ |
c7de829c | 230 | } else { |
27b207fd | 231 | printf ("Error: start and/or end address" |
c7de829c WD |
232 | " not on sector boundary\n"); |
233 | rcode = 1; | |
234 | } | |
235 | return rcode; | |
236 | } | |
237 | ||
238 | ||
239 | int do_protect(cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[]) | |
240 | { | |
241 | flash_info_t *info; | |
242 | ulong bank, addr_first, addr_last; | |
243 | int i, p, n, sect_first, sect_last; | |
244 | int rcode = 0; | |
245 | ||
246 | if (argc < 3) { | |
27b207fd | 247 | printf ("Usage:\n%s\n", cmdtp->usage); |
c7de829c WD |
248 | return 1; |
249 | } | |
250 | ||
251 | if (strcmp(argv[1], "off") == 0) | |
252 | p = 0; | |
253 | else if (strcmp(argv[1], "on") == 0) | |
254 | p = 1; | |
255 | else { | |
27b207fd | 256 | printf ("Usage:\n%s\n", cmdtp->usage); |
c7de829c WD |
257 | return 1; |
258 | } | |
259 | ||
260 | if (strcmp(argv[2], "all") == 0) { | |
261 | for (bank=1; bank<=CFG_MAX_FLASH_BANKS; ++bank) { | |
262 | info = &flash_info[bank-1]; | |
263 | if (info->flash_id == FLASH_UNKNOWN) { | |
264 | continue; | |
265 | } | |
27b207fd | 266 | /*printf ("%sProtect Flash Bank # %ld\n", */ |
8bde7f77 | 267 | /* p ? "" : "Un-", bank); */ |
c7de829c WD |
268 | |
269 | for (i=0; i<info->sector_count; ++i) { | |
270 | #if defined(CFG_FLASH_PROTECTION) | |
271 | if (flash_real_protect(info, i, p)) | |
272 | rcode = 1; | |
273 | putc ('.'); | |
274 | #else | |
275 | info->protect[i] = p; | |
276 | #endif /* CFG_FLASH_PROTECTION */ | |
277 | } | |
278 | } | |
279 | ||
280 | #if defined(CFG_FLASH_PROTECTION) | |
281 | if (!rcode) puts (" done\n"); | |
282 | #endif /* CFG_FLASH_PROTECTION */ | |
283 | ||
284 | return rcode; | |
285 | } | |
286 | ||
287 | if ((n = abbrev_spec(argv[2], &info, §_first, §_last)) != 0) { | |
288 | if (n < 0) { | |
27b207fd | 289 | printf("Bad sector specification\n"); |
c7de829c WD |
290 | return 1; |
291 | } | |
27b207fd | 292 | /*printf("%sProtect Flash Sectors %d-%d in Bank # %d\n", */ |
8bde7f77 WD |
293 | /* p ? "" : "Un-", sect_first, sect_last, */ |
294 | /* (info-flash_info)+1); */ | |
c7de829c WD |
295 | for (i = sect_first; i <= sect_last; i++) { |
296 | #if defined(CFG_FLASH_PROTECTION) | |
297 | if (flash_real_protect(info, i, p)) | |
298 | rcode = 1; | |
299 | putc ('.'); | |
300 | #else | |
301 | info->protect[i] = p; | |
302 | #endif /* CFG_FLASH_PROTECTION */ | |
303 | } | |
304 | ||
305 | #if defined(CFG_FLASH_PROTECTION) | |
306 | if (!rcode) puts (" done\n"); | |
307 | #endif /* CFG_FLASH_PROTECTION */ | |
308 | ||
309 | return rcode; | |
310 | } | |
311 | ||
312 | if (argc != 4) { | |
27b207fd | 313 | printf ("Usage:\n%s\n", cmdtp->usage); |
c7de829c WD |
314 | return 1; |
315 | } | |
316 | ||
317 | if (strcmp(argv[2], "bank") == 0) { | |
318 | bank = simple_strtoul(argv[3], NULL, 16); | |
319 | if ((bank < 1) || (bank > CFG_MAX_FLASH_BANKS)) { | |
27b207fd | 320 | printf ("Only FLASH Banks # 1 ... # %d supported\n", |
c7de829c WD |
321 | CFG_MAX_FLASH_BANKS); |
322 | return 1; | |
323 | } | |
27b207fd | 324 | printf ("%sProtect Flash Bank # %ld\n", |
c7de829c WD |
325 | p ? "" : "Un-", bank); |
326 | info = &flash_info[bank-1]; | |
327 | ||
328 | if (info->flash_id == FLASH_UNKNOWN) { | |
27b207fd | 329 | printf ("missing or unknown FLASH type\n"); |
c7de829c WD |
330 | return 1; |
331 | } | |
332 | for (i=0; i<info->sector_count; ++i) { | |
333 | #if defined(CFG_FLASH_PROTECTION) | |
334 | if (flash_real_protect(info, i, p)) | |
335 | rcode = 1; | |
336 | putc ('.'); | |
337 | #else | |
338 | info->protect[i] = p; | |
339 | #endif /* CFG_FLASH_PROTECTION */ | |
340 | } | |
341 | ||
342 | #if defined(CFG_FLASH_PROTECTION) | |
343 | if (!rcode) puts (" done\n"); | |
344 | #endif /* CFG_FLASH_PROTECTION */ | |
345 | ||
346 | return rcode; | |
347 | } | |
348 | ||
349 | addr_first = simple_strtoul(argv[2], NULL, 16); | |
350 | addr_last = simple_strtoul(argv[3], NULL, 16); | |
351 | ||
352 | if (addr_first >= addr_last) { | |
27b207fd | 353 | printf ("Usage:\n%s\n", cmdtp->usage); |
c7de829c WD |
354 | return 1; |
355 | } | |
356 | rcode = flash_sect_protect (p, addr_first, addr_last); | |
357 | return rcode; | |
358 | } | |
359 | int flash_sect_protect (int p, ulong addr_first, ulong addr_last) | |
360 | { | |
361 | flash_info_t *info; | |
362 | ulong bank; | |
363 | int s_first, s_last; | |
364 | int protected, i; | |
365 | int rcode = 0; | |
366 | ||
367 | protected = 0; | |
368 | ||
369 | for (bank=0,info=&flash_info[0]; bank < CFG_MAX_FLASH_BANKS; ++bank, ++info) { | |
370 | ulong b_end; | |
371 | int sect; | |
372 | ||
373 | if (info->flash_id == FLASH_UNKNOWN) { | |
374 | continue; | |
375 | } | |
376 | ||
377 | b_end = info->start[0] + info->size - 1; /* bank end addr */ | |
378 | ||
379 | s_first = -1; /* first sector to erase */ | |
380 | s_last = -1; /* last sector to erase */ | |
381 | ||
382 | for (sect=0; sect < info->sector_count; ++sect) { | |
383 | ulong end; /* last address in current sect */ | |
384 | short s_end; | |
385 | ||
386 | s_end = info->sector_count - 1; | |
387 | ||
388 | end = (sect == s_end) ? b_end : info->start[sect + 1] - 1; | |
389 | ||
390 | if (addr_first > end) | |
391 | continue; | |
392 | if (addr_last < info->start[sect]) | |
393 | continue; | |
394 | ||
395 | if (addr_first == info->start[sect]) { | |
396 | s_first = sect; | |
397 | } | |
398 | if (addr_last == end) { | |
399 | s_last = sect; | |
400 | } | |
401 | } | |
402 | if (s_first>=0 && s_first<=s_last) { | |
403 | protected += s_last - s_first + 1; | |
404 | for (i=s_first; i<=s_last; ++i) { | |
405 | #if defined(CFG_FLASH_PROTECTION) | |
406 | if (flash_real_protect(info, i, p)) | |
407 | rcode = 1; | |
408 | putc ('.'); | |
409 | #else | |
410 | info->protect[i] = p; | |
411 | #endif /* CFG_FLASH_PROTECTION */ | |
412 | } | |
413 | } | |
414 | #if defined(CFG_FLASH_PROTECTION) | |
415 | if (!rcode) putc ('\n'); | |
416 | #endif /* CFG_FLASH_PROTECTION */ | |
417 | ||
418 | } | |
419 | if (protected) { | |
27b207fd | 420 | /* printf ("%sProtected %d sectors\n", */ |
8bde7f77 | 421 | /* p ? "" : "Un-", protected); */ |
c7de829c | 422 | } else { |
27b207fd | 423 | printf ("Error: start and/or end address" |
c7de829c WD |
424 | " not on sector boundary\n"); |
425 | rcode = 1; | |
426 | } | |
427 | return rcode; | |
428 | } | |
429 | ||
430 | #endif /* CFG_CMD_FLASH */ |