Commit ea533c260a801c4e51f92f75165cebe6d7b01e35
1 parent
f9a5254111
Exists in
master
and in
54 other branches
cmd_nand: some infrastructure fixes and refactoring
- If the current device is overridden by a named partition, - update the caller's pointer/index, rather than copy over the nand_info struct, and - be sure to call board_nand_select_device even when the device is overridden by a named partition. - Support 64-bit offsets/sizes in a few more places. - Refactor arg_off_size for added readability and flexibility, and some added checks such as partition size. - Remove redundant check for bad subcommands -- if there's no match it'll print usage when it gets to the end anyway. Signed-off-by: Scott Wood <scottwood@freescale.com> Tested-by: Ben Gardiner <bengardiner@nanometrics.ca>
Showing 1 changed file with 167 additions and 107 deletions Side-by-side Diff
common/cmd_nand.c
... | ... | @@ -10,6 +10,13 @@ |
10 | 10 | * (C) Copyright 2006-2007 OpenMoko, Inc. |
11 | 11 | * Added 16-bit nand support |
12 | 12 | * (C) 2004 Texas Instruments |
13 | + * | |
14 | + * Copyright 2010 Freescale Semiconductor | |
15 | + * The portions of this file whose copyright is held by Freescale and which | |
16 | + * are not considered a derived work of GPL v2-only code may be distributed | |
17 | + * and/or modified under the terms of the GNU General Public License as | |
18 | + * published by the Free Software Foundation; either version 2 of the | |
19 | + * License, or (at your option) any later version. | |
13 | 20 | */ |
14 | 21 | |
15 | 22 | #include <common.h> |
16 | 23 | |
17 | 24 | |
18 | 25 | |
19 | 26 | |
20 | 27 | |
21 | 28 | |
22 | 29 | |
23 | 30 | |
24 | 31 | |
25 | 32 | |
26 | 33 | |
27 | 34 | |
28 | 35 | |
29 | 36 | |
... | ... | @@ -85,74 +92,132 @@ |
85 | 92 | |
86 | 93 | /* ------------------------------------------------------------------------- */ |
87 | 94 | |
88 | -static inline int str2long(char *p, ulong *num) | |
95 | +static int set_dev(int dev) | |
89 | 96 | { |
97 | + if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || | |
98 | + !nand_info[dev].name) { | |
99 | + puts("No such device\n"); | |
100 | + return -1; | |
101 | + } | |
102 | + | |
103 | + if (nand_curr_device == dev) | |
104 | + return 0; | |
105 | + | |
106 | + printf("Device %d: %s", dev, nand_info[dev].name); | |
107 | + puts("... is now current device\n"); | |
108 | + nand_curr_device = dev; | |
109 | + | |
110 | +#ifdef CONFIG_SYS_NAND_SELECT_DEVICE | |
111 | + board_nand_select_device(nand_info[dev].priv, dev); | |
112 | +#endif | |
113 | + | |
114 | + return 0; | |
115 | +} | |
116 | + | |
117 | +static inline int str2off(const char *p, loff_t *num) | |
118 | +{ | |
90 | 119 | char *endptr; |
91 | 120 | |
121 | + *num = simple_strtoull(p, &endptr, 16); | |
122 | + return *p != '\0' && *endptr == '\0'; | |
123 | +} | |
124 | + | |
125 | +static inline int str2long(const char *p, ulong *num) | |
126 | +{ | |
127 | + char *endptr; | |
128 | + | |
92 | 129 | *num = simple_strtoul(p, &endptr, 16); |
93 | - return (*p != '\0' && *endptr == '\0') ? 1 : 0; | |
130 | + return *p != '\0' && *endptr == '\0'; | |
94 | 131 | } |
95 | 132 | |
96 | -static int | |
97 | -arg_off_size(int argc, char * const argv[], nand_info_t *nand, ulong *off, size_t *size) | |
133 | +static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size) | |
98 | 134 | { |
99 | - int idx = nand_curr_device; | |
100 | -#if defined(CONFIG_CMD_MTDPARTS) | |
135 | +#ifdef CONFIG_CMD_MTDPARTS | |
101 | 136 | struct mtd_device *dev; |
102 | 137 | struct part_info *part; |
103 | 138 | u8 pnum; |
139 | + int ret; | |
104 | 140 | |
105 | - if (argc >= 1 && !(str2long(argv[0], off))) { | |
106 | - if ((mtdparts_init() == 0) && | |
107 | - (find_dev_and_part(argv[0], &dev, &pnum, &part) == 0)) { | |
108 | - if (dev->id->type != MTD_DEV_TYPE_NAND) { | |
109 | - puts("not a NAND device\n"); | |
110 | - return -1; | |
111 | - } | |
112 | - *off = part->offset; | |
113 | - if (argc >= 2) { | |
114 | - if (!(str2long(argv[1], (ulong *)size))) { | |
115 | - printf("'%s' is not a number\n", argv[1]); | |
116 | - return -1; | |
117 | - } | |
118 | - if (*size > part->size) | |
119 | - *size = part->size; | |
120 | - } else { | |
121 | - *size = part->size; | |
122 | - } | |
123 | - idx = dev->id->num; | |
124 | - *nand = nand_info[idx]; | |
125 | - goto out; | |
126 | - } | |
141 | + ret = mtdparts_init(); | |
142 | + if (ret) | |
143 | + return ret; | |
144 | + | |
145 | + ret = find_dev_and_part(partname, &dev, &pnum, &part); | |
146 | + if (ret) | |
147 | + return ret; | |
148 | + | |
149 | + if (dev->id->type != MTD_DEV_TYPE_NAND) { | |
150 | + puts("not a NAND device\n"); | |
151 | + return -1; | |
127 | 152 | } |
153 | + | |
154 | + *off = part->offset; | |
155 | + *size = part->size; | |
156 | + *idx = dev->id->num; | |
157 | + | |
158 | + ret = set_dev(*idx); | |
159 | + if (ret) | |
160 | + return ret; | |
161 | + | |
162 | + return 0; | |
163 | +#else | |
164 | + puts("offset is not a number\n"); | |
165 | + return -1; | |
128 | 166 | #endif |
167 | +} | |
129 | 168 | |
130 | - if (argc >= 1) { | |
131 | - if (!(str2long(argv[0], off))) { | |
132 | - printf("'%s' is not a number\n", argv[0]); | |
133 | - return -1; | |
134 | - } | |
135 | - } else { | |
169 | +static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *maxsize) | |
170 | +{ | |
171 | + if (!str2off(arg, off)) | |
172 | + return get_part(arg, idx, off, maxsize); | |
173 | + | |
174 | + if (*off >= nand_info[*idx].size) { | |
175 | + puts("Offset exceeds device limit\n"); | |
176 | + return -1; | |
177 | + } | |
178 | + | |
179 | + *maxsize = nand_info[*idx].size - *off; | |
180 | + return 0; | |
181 | +} | |
182 | + | |
183 | +static int arg_off_size(int argc, char *const argv[], int *idx, | |
184 | + loff_t *off, loff_t *size) | |
185 | +{ | |
186 | + int ret; | |
187 | + loff_t maxsize; | |
188 | + | |
189 | + if (argc == 0) { | |
136 | 190 | *off = 0; |
191 | + *size = nand_info[*idx].size; | |
192 | + goto print; | |
137 | 193 | } |
138 | 194 | |
139 | - if (argc >= 2) { | |
140 | - if (!(str2long(argv[1], (ulong *)size))) { | |
141 | - printf("'%s' is not a number\n", argv[1]); | |
142 | - return -1; | |
143 | - } | |
144 | - } else { | |
145 | - *size = nand->size - *off; | |
195 | + ret = arg_off(argv[0], idx, off, &maxsize); | |
196 | + if (ret) | |
197 | + return ret; | |
198 | + | |
199 | + if (argc == 1) { | |
200 | + *size = maxsize; | |
201 | + goto print; | |
146 | 202 | } |
147 | 203 | |
148 | -#if defined(CONFIG_CMD_MTDPARTS) | |
149 | -out: | |
150 | -#endif | |
151 | - printf("device %d ", idx); | |
152 | - if (*size == nand->size) | |
204 | + if (!str2off(argv[1], size)) { | |
205 | + printf("'%s' is not a number\n", argv[1]); | |
206 | + return -1; | |
207 | + } | |
208 | + | |
209 | + if (*size > maxsize) { | |
210 | + puts("Size exceeds partition or device limit\n"); | |
211 | + return -1; | |
212 | + } | |
213 | + | |
214 | +print: | |
215 | + printf("device %d ", *idx); | |
216 | + if (*size == nand_info[*idx].size) | |
153 | 217 | puts("whole chip\n"); |
154 | 218 | else |
155 | - printf("offset 0x%lx, size 0x%zx\n", *off, *size); | |
219 | + printf("offset 0x%llx, size 0x%llx\n", | |
220 | + (unsigned long long)*off, (unsigned long long)*size); | |
156 | 221 | return 0; |
157 | 222 | } |
158 | 223 | |
159 | 224 | |
160 | 225 | |
... | ... | @@ -200,14 +265,20 @@ |
200 | 265 | #ifdef CONFIG_ENV_OFFSET_OOB |
201 | 266 | unsigned long nand_env_oob_offset; |
202 | 267 | |
203 | -int do_nand_env_oob(cmd_tbl_t *cmdtp, nand_info_t *nand, | |
204 | - int argc, char * const argv[]) | |
268 | +int do_nand_env_oob(cmd_tbl_t *cmdtp, int argc, char *const argv[]) | |
205 | 269 | { |
206 | 270 | int ret; |
207 | 271 | uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; |
208 | - | |
272 | + nand_info_t *nand = &nand_info[0]; | |
209 | 273 | char *cmd = argv[1]; |
210 | 274 | |
275 | + if (CONFIG_SYS_MAX_NAND_DEVICE == 0 || !nand->name) { | |
276 | + puts("no devices available\n"); | |
277 | + return 1; | |
278 | + } | |
279 | + | |
280 | + set_dev(0); | |
281 | + | |
211 | 282 | if (!strcmp(cmd, "get")) { |
212 | 283 | ret = get_nand_env_oob(nand, &nand_env_oob_offset); |
213 | 284 | if (ret) |
214 | 285 | |
215 | 286 | |
216 | 287 | |
... | ... | @@ -215,19 +286,24 @@ |
215 | 286 | |
216 | 287 | printf("0x%08lx\n", nand_env_oob_offset); |
217 | 288 | } else if (!strcmp(cmd, "set")) { |
218 | - ulong addr; | |
219 | - size_t dummy_size; | |
289 | + loff_t addr; | |
290 | + loff_t maxsize; | |
220 | 291 | struct mtd_oob_ops ops; |
292 | + int idx = 0; | |
221 | 293 | |
222 | 294 | if (argc < 3) |
223 | 295 | goto usage; |
224 | 296 | |
225 | - if (arg_off_size(argc - 2, argv + 2, nand, &addr, | |
226 | - &dummy_size) < 0) { | |
227 | - printf("Offset or partition name expected\n"); | |
297 | + if (arg_off(argv[2], &idx, &addr, &maxsize)) { | |
298 | + puts("Offset or partition name expected\n"); | |
228 | 299 | return 1; |
229 | 300 | } |
230 | 301 | |
302 | + if (idx != 0) { | |
303 | + puts("Partition not on first NAND device\n"); | |
304 | + return 1; | |
305 | + } | |
306 | + | |
231 | 307 | if (nand->oobavail < ENV_OFFSET_SIZE) { |
232 | 308 | printf("Insufficient available OOB bytes:\n" |
233 | 309 | "%d OOB bytes available but %d required for " |
... | ... | @@ -264,8 +340,8 @@ |
264 | 340 | |
265 | 341 | if (addr != nand_env_oob_offset) { |
266 | 342 | printf("Verification of env offset in OOB failed: " |
267 | - "0x%08lx expected but got 0x%08lx\n", | |
268 | - addr, nand_env_oob_offset); | |
343 | + "0x%08llx expected but got 0x%08lx\n", | |
344 | + (unsigned long long)addr, nand_env_oob_offset); | |
269 | 345 | return 1; |
270 | 346 | } |
271 | 347 | } else { |
... | ... | @@ -293,9 +369,9 @@ |
293 | 369 | |
294 | 370 | int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) |
295 | 371 | { |
296 | - int i, dev, ret = 0; | |
297 | - ulong addr, off; | |
298 | - size_t size; | |
372 | + int i, ret = 0; | |
373 | + ulong addr; | |
374 | + loff_t off, size; | |
299 | 375 | char *cmd, *s; |
300 | 376 | nand_info_t *nand; |
301 | 377 | #ifdef CONFIG_SYS_NAND_QUIET |
... | ... | @@ -304,6 +380,7 @@ |
304 | 380 | int quiet = 0; |
305 | 381 | #endif |
306 | 382 | const char *quiet_str = getenv("quiet"); |
383 | + int dev = nand_curr_device; | |
307 | 384 | |
308 | 385 | /* at least two arguments please */ |
309 | 386 | if (argc < 2) |
310 | 387 | |
311 | 388 | |
312 | 389 | |
313 | 390 | |
314 | 391 | |
315 | 392 | |
316 | 393 | |
317 | 394 | |
318 | 395 | |
319 | 396 | |
320 | 397 | |
321 | 398 | |
... | ... | @@ -325,68 +402,45 @@ |
325 | 402 | } |
326 | 403 | |
327 | 404 | if (strcmp(cmd, "device") == 0) { |
328 | - | |
329 | 405 | if (argc < 3) { |
330 | 406 | putc('\n'); |
331 | - if ((nand_curr_device < 0) || | |
332 | - (nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE)) | |
407 | + if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE) | |
333 | 408 | puts("no devices available\n"); |
334 | 409 | else |
335 | - nand_print_info(nand_curr_device); | |
410 | + nand_print_info(dev); | |
336 | 411 | return 0; |
337 | 412 | } |
413 | + | |
338 | 414 | dev = (int)simple_strtoul(argv[2], NULL, 10); |
339 | - if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev].name) { | |
340 | - puts("No such device\n"); | |
341 | - return 1; | |
342 | - } | |
343 | - printf("Device %d: %s", dev, nand_info[dev].name); | |
344 | - puts("... is now current device\n"); | |
345 | - nand_curr_device = dev; | |
415 | + set_dev(dev); | |
346 | 416 | |
347 | -#ifdef CONFIG_SYS_NAND_SELECT_DEVICE | |
348 | - /* | |
349 | - * Select the chip in the board/cpu specific driver | |
350 | - */ | |
351 | - board_nand_select_device(nand_info[dev].priv, dev); | |
352 | -#endif | |
353 | - | |
354 | 417 | return 0; |
355 | 418 | } |
356 | 419 | |
357 | - if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 && | |
358 | - strncmp(cmd, "dump", 4) != 0 && | |
359 | - strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && | |
360 | - strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && | |
361 | - strcmp(cmd, "biterr") != 0 && | |
362 | - strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 | |
363 | 420 | #ifdef CONFIG_ENV_OFFSET_OOB |
364 | - && strcmp(cmd, "env.oob") != 0 | |
365 | -#endif | |
366 | - ) | |
367 | - goto usage; | |
368 | - | |
369 | -#ifdef CONFIG_ENV_OFFSET_OOB | |
370 | 421 | /* this command operates only on the first nand device */ |
371 | - if (strcmp(cmd, "env.oob") == 0) { | |
372 | - return do_nand_env_oob(cmdtp, &nand_info[0], | |
373 | - argc - 1, argv + 1); | |
374 | - } | |
422 | + if (strcmp(cmd, "env.oob") == 0) | |
423 | + return do_nand_env_oob(cmdtp, argc - 1, argv + 1); | |
375 | 424 | #endif |
376 | 425 | |
377 | - /* the following commands operate on the current device */ | |
378 | - if (nand_curr_device < 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || | |
379 | - !nand_info[nand_curr_device].name) { | |
426 | + /* The following commands operate on the current device, unless | |
427 | + * overridden by a partition specifier. Note that if somehow the | |
428 | + * current device is invalid, it will have to be changed to a valid | |
429 | + * one before these commands can run, even if a partition specifier | |
430 | + * for another device is to be used. | |
431 | + */ | |
432 | + if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || | |
433 | + !nand_info[dev].name) { | |
380 | 434 | puts("\nno devices available\n"); |
381 | 435 | return 1; |
382 | 436 | } |
383 | - nand = &nand_info[nand_curr_device]; | |
437 | + nand = &nand_info[dev]; | |
384 | 438 | |
385 | 439 | if (strcmp(cmd, "bad") == 0) { |
386 | - printf("\nDevice %d bad blocks:\n", nand_curr_device); | |
440 | + printf("\nDevice %d bad blocks:\n", dev); | |
387 | 441 | for (off = 0; off < nand->size; off += nand->erasesize) |
388 | 442 | if (nand_block_isbad(nand, off)) |
389 | - printf(" %08lx\n", off); | |
443 | + printf(" %08llx\n", (unsigned long long)off); | |
390 | 444 | return 0; |
391 | 445 | } |
392 | 446 | |
393 | 447 | |
... | ... | @@ -404,9 +458,11 @@ |
404 | 458 | |
405 | 459 | printf("\nNAND %s: ", scrub ? "scrub" : "erase"); |
406 | 460 | /* skip first two or three arguments, look for offset and size */ |
407 | - if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0) | |
461 | + if (arg_off_size(argc - o, argv + o, &dev, &off, &size) != 0) | |
408 | 462 | return 1; |
409 | 463 | |
464 | + nand = &nand_info[dev]; | |
465 | + | |
410 | 466 | memset(&opts, 0, sizeof(opts)); |
411 | 467 | opts.offset = off; |
412 | 468 | opts.length = size; |
... | ... | @@ -462,6 +518,7 @@ |
462 | 518 | } |
463 | 519 | |
464 | 520 | if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { |
521 | + size_t rwsize; | |
465 | 522 | int read; |
466 | 523 | |
467 | 524 | if (argc < 4) |
468 | 525 | |
469 | 526 | |
470 | 527 | |
471 | 528 | |
... | ... | @@ -471,23 +528,26 @@ |
471 | 528 | |
472 | 529 | read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ |
473 | 530 | printf("\nNAND %s: ", read ? "read" : "write"); |
474 | - if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) | |
531 | + if (arg_off_size(argc - 3, argv + 3, &dev, &off, &size) != 0) | |
475 | 532 | return 1; |
476 | 533 | |
534 | + nand = &nand_info[dev]; | |
535 | + rwsize = size; | |
536 | + | |
477 | 537 | s = strchr(cmd, '.'); |
478 | 538 | if (!s || !strcmp(s, ".jffs2") || |
479 | 539 | !strcmp(s, ".e") || !strcmp(s, ".i")) { |
480 | 540 | if (read) |
481 | - ret = nand_read_skip_bad(nand, off, &size, | |
541 | + ret = nand_read_skip_bad(nand, off, &rwsize, | |
482 | 542 | (u_char *)addr); |
483 | 543 | else |
484 | - ret = nand_write_skip_bad(nand, off, &size, | |
544 | + ret = nand_write_skip_bad(nand, off, &rwsize, | |
485 | 545 | (u_char *)addr); |
486 | 546 | } else if (!strcmp(s, ".oob")) { |
487 | 547 | /* out-of-band data */ |
488 | 548 | mtd_oob_ops_t ops = { |
489 | 549 | .oobbuf = (u8 *)addr, |
490 | - .ooblen = size, | |
550 | + .ooblen = rwsize, | |
491 | 551 | .mode = MTD_OOB_RAW |
492 | 552 | }; |
493 | 553 | |
... | ... | @@ -500,7 +560,7 @@ |
500 | 560 | return 1; |
501 | 561 | } |
502 | 562 | |
503 | - printf(" %zu bytes %s: %s\n", size, | |
563 | + printf(" %zu bytes %s: %s\n", rwsize, | |
504 | 564 | read ? "read" : "written", ret ? "ERROR" : "OK"); |
505 | 565 | |
506 | 566 | return ret == 0 ? 0 : 1; |
... | ... | @@ -564,7 +624,7 @@ |
564 | 624 | if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0) |
565 | 625 | return 1; |
566 | 626 | |
567 | - if (!nand_unlock(nand, off, size)) { | |
627 | + if (!nand_unlock(&nand_info[dev], off, size)) { | |
568 | 628 | puts("NAND flash successfully unlocked\n"); |
569 | 629 | } else { |
570 | 630 | puts("Error unlocking NAND flash, " |