blob: 3a604d00d5d62f9ae338db8005eaee5e02bdb76a [file] [log] [blame]
wdenkc7de8292002-11-19 11:04:11 +00001/*
2 * (C) Copyright 2000
3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4 *
Wolfgang Denk1a459662013-07-08 09:37:19 +02005 * SPDX-License-Identifier: GPL-2.0+
wdenkc7de8292002-11-19 11:04:11 +00006 */
7
8/*
9 * FLASH support
10 */
11#include <common.h>
12#include <command.h>
wdenkc7de8292002-11-19 11:04:11 +000013#include <flash.h>
14
Jon Loeligerc91898b2007-07-09 17:46:09 -050015#if defined(CONFIG_CMD_FLASH)
wdenkc7de8292002-11-19 11:04:11 +000016
17extern flash_info_t flash_info[]; /* info for FLASH chips */
18
19/*
20 * The user interface starts numbering for Flash banks with 1
21 * for historical reasons.
22 */
23
24/*
25 * this routine looks for an abbreviated flash range specification.
26 * the syntax is B:SF[-SL], where B is the bank number, SF is the first
27 * sector to erase, and SL is the last sector to erase (defaults to SF).
28 * bank numbers start at 1 to be consistent with other specs, sector numbers
29 * start at zero.
30 *
31 * returns: 1 - correct spec; *pinfo, *psf and *psl are
32 * set appropriately
33 * 0 - doesn't look like an abbreviated spec
34 * -1 - looks like an abbreviated spec, but got
35 * a parsing error, a number out of range,
36 * or an invalid flash bank.
37 */
38static int
39abbrev_spec(char *str, flash_info_t **pinfo, int *psf, int *psl)
40{
41 flash_info_t *fp;
42 int bank, first, last;
43 char *p, *ep;
44
45 if ((p = strchr(str, ':')) == NULL)
46 return 0;
47 *p++ = '\0';
48
49 bank = simple_strtoul(str, &ep, 10);
50 if (ep == str || *ep != '\0' ||
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +020051 bank < 1 || bank > CONFIG_SYS_MAX_FLASH_BANKS ||
wdenkc7de8292002-11-19 11:04:11 +000052 (fp = &flash_info[bank - 1])->flash_id == FLASH_UNKNOWN)
53 return -1;
54
55 str = p;
56 if ((p = strchr(str, '-')) != NULL)
57 *p++ = '\0';
58
59 first = simple_strtoul(str, &ep, 10);
60 if (ep == str || *ep != '\0' || first >= fp->sector_count)
61 return -1;
62
63 if (p != NULL) {
64 last = simple_strtoul(p, &ep, 10);
65 if (ep == p || *ep != '\0' ||
66 last < first || last >= fp->sector_count)
67 return -1;
68 }
69 else
70 last = first;
71
72 *pinfo = fp;
73 *psf = first;
74 *psl = last;
75
76 return 1;
77}
78int do_flinfo (cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[])
79{
80 ulong bank;
81
82 if (argc == 1) { /* print info for all FLASH banks */
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +020083 for (bank=0; bank <CONFIG_SYS_MAX_FLASH_BANKS; ++bank) {
wdenk27b207f2003-07-24 23:38:38 +000084 printf ("\nBank # %ld: ", bank+1);
wdenkc7de8292002-11-19 11:04:11 +000085
86 flash_print_info (&flash_info[bank]);
87 }
88 return 0;
89 }
90
91 bank = simple_strtoul(argv[1], NULL, 16);
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +020092 if ((bank < 1) || (bank > CONFIG_SYS_MAX_FLASH_BANKS)) {
wdenk27b207f2003-07-24 23:38:38 +000093 printf ("Only FLASH Banks # 1 ... # %d supported\n",
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +020094 CONFIG_SYS_MAX_FLASH_BANKS);
wdenkc7de8292002-11-19 11:04:11 +000095 return 1;
96 }
wdenk27b207f2003-07-24 23:38:38 +000097 printf ("\nBank # %ld: ", bank);
wdenkc7de8292002-11-19 11:04:11 +000098 flash_print_info (&flash_info[bank-1]);
99 return 0;
100}
101int do_flerase(cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[])
102{
103 flash_info_t *info;
104 ulong bank, addr_first, addr_last;
105 int n, sect_first, sect_last;
106 int rcode = 0;
107
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200108 if (argc < 2)
109 return cmd_usage(cmdtp);
wdenkc7de8292002-11-19 11:04:11 +0000110
111 if (strcmp(argv[1], "all") == 0) {
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200112 for (bank=1; bank<=CONFIG_SYS_MAX_FLASH_BANKS; ++bank) {
wdenk27b207f2003-07-24 23:38:38 +0000113 printf ("Erase Flash Bank # %ld ", bank);
wdenkc7de8292002-11-19 11:04:11 +0000114 info = &flash_info[bank-1];
115 rcode = flash_erase (info, 0, info->sector_count-1);
116 }
117 return rcode;
118 }
119
120 if ((n = abbrev_spec(argv[1], &info, &sect_first, &sect_last)) != 0) {
121 if (n < 0) {
wdenk27b207f2003-07-24 23:38:38 +0000122 printf("Bad sector specification\n");
wdenkc7de8292002-11-19 11:04:11 +0000123 return 1;
124 }
wdenk27b207f2003-07-24 23:38:38 +0000125 printf ("Erase Flash Sectors %d-%d in Bank # %d ",
wdenkc7de8292002-11-19 11:04:11 +0000126 sect_first, sect_last, (info-flash_info)+1);
127 rcode = flash_erase(info, sect_first, sect_last);
128 return rcode;
129 }
130
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200131 if (argc != 3)
132 return cmd_usage(cmdtp);
wdenkc7de8292002-11-19 11:04:11 +0000133
134 if (strcmp(argv[1], "bank") == 0) {
135 bank = simple_strtoul(argv[2], NULL, 16);
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200136 if ((bank < 1) || (bank > CONFIG_SYS_MAX_FLASH_BANKS)) {
wdenk27b207f2003-07-24 23:38:38 +0000137 printf ("Only FLASH Banks # 1 ... # %d supported\n",
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200138 CONFIG_SYS_MAX_FLASH_BANKS);
wdenkc7de8292002-11-19 11:04:11 +0000139 return 1;
140 }
wdenk27b207f2003-07-24 23:38:38 +0000141 printf ("Erase Flash Bank # %ld ", bank);
wdenkc7de8292002-11-19 11:04:11 +0000142 info = &flash_info[bank-1];
143 rcode = flash_erase (info, 0, info->sector_count-1);
144 return rcode;
145 }
146
147 addr_first = simple_strtoul(argv[1], NULL, 16);
148 addr_last = simple_strtoul(argv[2], NULL, 16);
149
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200150 if (addr_first >= addr_last)
151 return cmd_usage(cmdtp);
wdenkc7de8292002-11-19 11:04:11 +0000152
wdenk27b207f2003-07-24 23:38:38 +0000153 printf ("Erase Flash from 0x%08lx to 0x%08lx ", addr_first, addr_last);
wdenkc7de8292002-11-19 11:04:11 +0000154 rcode = flash_sect_erase(addr_first, addr_last);
155 return rcode;
156}
157
158int flash_sect_erase (ulong addr_first, ulong addr_last)
159{
160 flash_info_t *info;
161 ulong bank;
162 int s_first, s_last;
163 int erased;
164 int rcode = 0;
165
166 erased = 0;
167
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200168 for (bank=0,info = &flash_info[0]; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank, ++info) {
wdenkc7de8292002-11-19 11:04:11 +0000169 ulong b_end;
170 int sect;
171
172 if (info->flash_id == FLASH_UNKNOWN) {
173 continue;
174 }
175
176 b_end = info->start[0] + info->size - 1; /* bank end addr */
177
178 s_first = -1; /* first sector to erase */
179 s_last = -1; /* last sector to erase */
180
181 for (sect=0; sect < info->sector_count; ++sect) {
182 ulong end; /* last address in current sect */
183 short s_end;
184
185 s_end = info->sector_count - 1;
186
187 end = (sect == s_end) ? b_end : info->start[sect + 1] - 1;
188
189 if (addr_first > end)
190 continue;
191 if (addr_last < info->start[sect])
192 continue;
193
194 if (addr_first == info->start[sect]) {
195 s_first = sect;
196 }
197 if (addr_last == end) {
198 s_last = sect;
199 }
200 }
201 if (s_first>=0 && s_first<=s_last) {
202 erased += s_last - s_first + 1;
203 rcode = flash_erase (info, s_first, s_last);
204 }
205 }
206 if (erased) {
wdenk27b207f2003-07-24 23:38:38 +0000207 /* printf ("Erased %d sectors\n", erased); */
wdenkc7de8292002-11-19 11:04:11 +0000208 } else {
wdenk27b207f2003-07-24 23:38:38 +0000209 printf ("Error: start and/or end address"
wdenkc7de8292002-11-19 11:04:11 +0000210 " not on sector boundary\n");
211 rcode = 1;
212 }
213 return rcode;
214}
215
216
217int do_protect(cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[])
218{
219 flash_info_t *info;
220 ulong bank, addr_first, addr_last;
221 int i, p, n, sect_first, sect_last;
222 int rcode = 0;
223
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200224 if (argc < 3)
225 return cmd_usage(cmdtp);
wdenkc7de8292002-11-19 11:04:11 +0000226
227 if (strcmp(argv[1], "off") == 0)
228 p = 0;
229 else if (strcmp(argv[1], "on") == 0)
230 p = 1;
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200231 else
232 return cmd_usage(cmdtp);
wdenkc7de8292002-11-19 11:04:11 +0000233
234 if (strcmp(argv[2], "all") == 0) {
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200235 for (bank=1; bank<=CONFIG_SYS_MAX_FLASH_BANKS; ++bank) {
wdenkc7de8292002-11-19 11:04:11 +0000236 info = &flash_info[bank-1];
237 if (info->flash_id == FLASH_UNKNOWN) {
238 continue;
239 }
wdenk27b207f2003-07-24 23:38:38 +0000240 /*printf ("%sProtect Flash Bank # %ld\n", */
wdenk8bde7f72003-06-27 21:31:46 +0000241 /* p ? "" : "Un-", bank); */
wdenkc7de8292002-11-19 11:04:11 +0000242
243 for (i=0; i<info->sector_count; ++i) {
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200244#if defined(CONFIG_SYS_FLASH_PROTECTION)
wdenkc7de8292002-11-19 11:04:11 +0000245 if (flash_real_protect(info, i, p))
246 rcode = 1;
247 putc ('.');
248#else
249 info->protect[i] = p;
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200250#endif /* CONFIG_SYS_FLASH_PROTECTION */
wdenkc7de8292002-11-19 11:04:11 +0000251 }
252 }
253
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200254#if defined(CONFIG_SYS_FLASH_PROTECTION)
wdenkc7de8292002-11-19 11:04:11 +0000255 if (!rcode) puts (" done\n");
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200256#endif /* CONFIG_SYS_FLASH_PROTECTION */
wdenkc7de8292002-11-19 11:04:11 +0000257
258 return rcode;
259 }
260
261 if ((n = abbrev_spec(argv[2], &info, &sect_first, &sect_last)) != 0) {
262 if (n < 0) {
wdenk27b207f2003-07-24 23:38:38 +0000263 printf("Bad sector specification\n");
wdenkc7de8292002-11-19 11:04:11 +0000264 return 1;
265 }
wdenk27b207f2003-07-24 23:38:38 +0000266 /*printf("%sProtect Flash Sectors %d-%d in Bank # %d\n", */
wdenk8bde7f72003-06-27 21:31:46 +0000267 /* p ? "" : "Un-", sect_first, sect_last, */
268 /* (info-flash_info)+1); */
wdenkc7de8292002-11-19 11:04:11 +0000269 for (i = sect_first; i <= sect_last; i++) {
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200270#if defined(CONFIG_SYS_FLASH_PROTECTION)
wdenkc7de8292002-11-19 11:04:11 +0000271 if (flash_real_protect(info, i, p))
272 rcode = 1;
273 putc ('.');
274#else
275 info->protect[i] = p;
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200276#endif /* CONFIG_SYS_FLASH_PROTECTION */
wdenkc7de8292002-11-19 11:04:11 +0000277 }
278
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200279#if defined(CONFIG_SYS_FLASH_PROTECTION)
wdenkc7de8292002-11-19 11:04:11 +0000280 if (!rcode) puts (" done\n");
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200281#endif /* CONFIG_SYS_FLASH_PROTECTION */
wdenkc7de8292002-11-19 11:04:11 +0000282
283 return rcode;
284 }
285
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200286 if (argc != 4)
287 return cmd_usage(cmdtp);
wdenkc7de8292002-11-19 11:04:11 +0000288
289 if (strcmp(argv[2], "bank") == 0) {
290 bank = simple_strtoul(argv[3], NULL, 16);
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200291 if ((bank < 1) || (bank > CONFIG_SYS_MAX_FLASH_BANKS)) {
wdenk27b207f2003-07-24 23:38:38 +0000292 printf ("Only FLASH Banks # 1 ... # %d supported\n",
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200293 CONFIG_SYS_MAX_FLASH_BANKS);
wdenkc7de8292002-11-19 11:04:11 +0000294 return 1;
295 }
wdenk27b207f2003-07-24 23:38:38 +0000296 printf ("%sProtect Flash Bank # %ld\n",
wdenkc7de8292002-11-19 11:04:11 +0000297 p ? "" : "Un-", bank);
298 info = &flash_info[bank-1];
299
300 if (info->flash_id == FLASH_UNKNOWN) {
wdenk27b207f2003-07-24 23:38:38 +0000301 printf ("missing or unknown FLASH type\n");
wdenkc7de8292002-11-19 11:04:11 +0000302 return 1;
303 }
304 for (i=0; i<info->sector_count; ++i) {
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200305#if defined(CONFIG_SYS_FLASH_PROTECTION)
wdenkc7de8292002-11-19 11:04:11 +0000306 if (flash_real_protect(info, i, p))
307 rcode = 1;
308 putc ('.');
309#else
310 info->protect[i] = p;
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200311#endif /* CONFIG_SYS_FLASH_PROTECTION */
wdenkc7de8292002-11-19 11:04:11 +0000312 }
313
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200314#if defined(CONFIG_SYS_FLASH_PROTECTION)
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200315 if (!rcode)
316 puts(" done\n");
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200317#endif /* CONFIG_SYS_FLASH_PROTECTION */
wdenkc7de8292002-11-19 11:04:11 +0000318
319 return rcode;
320 }
321
322 addr_first = simple_strtoul(argv[2], NULL, 16);
323 addr_last = simple_strtoul(argv[3], NULL, 16);
324
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200325 if (addr_first >= addr_last)
326 return cmd_usage(cmdtp);
327
328 return flash_sect_protect (p, addr_first, addr_last);
wdenkc7de8292002-11-19 11:04:11 +0000329}
330int flash_sect_protect (int p, ulong addr_first, ulong addr_last)
331{
332 flash_info_t *info;
333 ulong bank;
334 int s_first, s_last;
335 int protected, i;
336 int rcode = 0;
337
338 protected = 0;
339
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200340 for (bank=0,info = &flash_info[0]; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank, ++info) {
wdenkc7de8292002-11-19 11:04:11 +0000341 ulong b_end;
342 int sect;
343
344 if (info->flash_id == FLASH_UNKNOWN) {
345 continue;
346 }
347
348 b_end = info->start[0] + info->size - 1; /* bank end addr */
349
350 s_first = -1; /* first sector to erase */
351 s_last = -1; /* last sector to erase */
352
353 for (sect=0; sect < info->sector_count; ++sect) {
354 ulong end; /* last address in current sect */
355 short s_end;
356
357 s_end = info->sector_count - 1;
358
359 end = (sect == s_end) ? b_end : info->start[sect + 1] - 1;
360
361 if (addr_first > end)
362 continue;
363 if (addr_last < info->start[sect])
364 continue;
365
366 if (addr_first == info->start[sect]) {
367 s_first = sect;
368 }
369 if (addr_last == end) {
370 s_last = sect;
371 }
372 }
373 if (s_first>=0 && s_first<=s_last) {
374 protected += s_last - s_first + 1;
375 for (i=s_first; i<=s_last; ++i) {
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200376#if defined(CONFIG_SYS_FLASH_PROTECTION)
wdenkc7de8292002-11-19 11:04:11 +0000377 if (flash_real_protect(info, i, p))
378 rcode = 1;
379 putc ('.');
380#else
381 info->protect[i] = p;
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200382#endif /* CONFIG_SYS_FLASH_PROTECTION */
wdenkc7de8292002-11-19 11:04:11 +0000383 }
384 }
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200385#if defined(CONFIG_SYS_FLASH_PROTECTION)
wdenkc7de8292002-11-19 11:04:11 +0000386 if (!rcode) putc ('\n');
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200387#endif /* CONFIG_SYS_FLASH_PROTECTION */
wdenkc7de8292002-11-19 11:04:11 +0000388
389 }
390 if (protected) {
wdenk27b207f2003-07-24 23:38:38 +0000391 /* printf ("%sProtected %d sectors\n", */
wdenk8bde7f72003-06-27 21:31:46 +0000392 /* p ? "" : "Un-", protected); */
wdenkc7de8292002-11-19 11:04:11 +0000393 } else {
wdenk27b207f2003-07-24 23:38:38 +0000394 printf ("Error: start and/or end address"
wdenkc7de8292002-11-19 11:04:11 +0000395 " not on sector boundary\n");
396 rcode = 1;
397 }
398 return rcode;
399}
400
Jon Loeligerb3aff0c2007-07-10 11:19:50 -0500401#endif