Commit 62d4f4365341576f5a5307b2b205a5aa2e3c6be6
Committed by
Wolfgang Denk
1 parent
f540c42d95
Exists in
master
and in
55 other branches
Re-introduce the 'nand read.oob' and 'nand write.oob' commands
that used to exist with the legacy NAND code Signed-off-by: Harald Welte <laforge@openmoko.org>
Showing 1 changed file with 8 additions and 0 deletions Inline Diff
common/cmd_nand.c
1 | /* | 1 | /* |
2 | * Driver for NAND support, Rick Bronson | 2 | * Driver for NAND support, Rick Bronson |
3 | * borrowed heavily from: | 3 | * borrowed heavily from: |
4 | * (c) 1999 Machine Vision Holdings, Inc. | 4 | * (c) 1999 Machine Vision Holdings, Inc. |
5 | * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> | 5 | * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> |
6 | * | 6 | * |
7 | * Added 16-bit nand support | 7 | * Added 16-bit nand support |
8 | * (C) 2004 Texas Instruments | 8 | * (C) 2004 Texas Instruments |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <common.h> | 11 | #include <common.h> |
12 | 12 | ||
13 | 13 | ||
14 | #ifndef CFG_NAND_LEGACY | 14 | #ifndef CFG_NAND_LEGACY |
15 | /* | 15 | /* |
16 | * | 16 | * |
17 | * New NAND support | 17 | * New NAND support |
18 | * | 18 | * |
19 | */ | 19 | */ |
20 | #include <common.h> | 20 | #include <common.h> |
21 | 21 | ||
22 | #if defined(CONFIG_CMD_NAND) | 22 | #if defined(CONFIG_CMD_NAND) |
23 | 23 | ||
24 | #include <command.h> | 24 | #include <command.h> |
25 | #include <watchdog.h> | 25 | #include <watchdog.h> |
26 | #include <malloc.h> | 26 | #include <malloc.h> |
27 | #include <asm/byteorder.h> | 27 | #include <asm/byteorder.h> |
28 | #include <jffs2/jffs2.h> | 28 | #include <jffs2/jffs2.h> |
29 | #include <nand.h> | 29 | #include <nand.h> |
30 | 30 | ||
31 | #if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) | 31 | #if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) |
32 | 32 | ||
33 | /* parition handling routines */ | 33 | /* parition handling routines */ |
34 | int mtdparts_init(void); | 34 | int mtdparts_init(void); |
35 | int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num); | 35 | int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num); |
36 | int find_dev_and_part(const char *id, struct mtd_device **dev, | 36 | int find_dev_and_part(const char *id, struct mtd_device **dev, |
37 | u8 *part_num, struct part_info **part); | 37 | u8 *part_num, struct part_info **part); |
38 | #endif | 38 | #endif |
39 | 39 | ||
40 | extern nand_info_t nand_info[]; /* info for NAND chips */ | 40 | extern nand_info_t nand_info[]; /* info for NAND chips */ |
41 | 41 | ||
42 | static int nand_dump_oob(nand_info_t *nand, ulong off) | 42 | static int nand_dump_oob(nand_info_t *nand, ulong off) |
43 | { | 43 | { |
44 | return 0; | 44 | return 0; |
45 | } | 45 | } |
46 | 46 | ||
47 | static int nand_dump(nand_info_t *nand, ulong off) | 47 | static int nand_dump(nand_info_t *nand, ulong off) |
48 | { | 48 | { |
49 | int i; | 49 | int i; |
50 | u_char *buf, *p; | 50 | u_char *buf, *p; |
51 | 51 | ||
52 | buf = malloc(nand->oobblock + nand->oobsize); | 52 | buf = malloc(nand->oobblock + nand->oobsize); |
53 | if (!buf) { | 53 | if (!buf) { |
54 | puts("No memory for page buffer\n"); | 54 | puts("No memory for page buffer\n"); |
55 | return 1; | 55 | return 1; |
56 | } | 56 | } |
57 | off &= ~(nand->oobblock - 1); | 57 | off &= ~(nand->oobblock - 1); |
58 | i = nand_read_raw(nand, buf, off, nand->oobblock, nand->oobsize); | 58 | i = nand_read_raw(nand, buf, off, nand->oobblock, nand->oobsize); |
59 | if (i < 0) { | 59 | if (i < 0) { |
60 | printf("Error (%d) reading page %08x\n", i, off); | 60 | printf("Error (%d) reading page %08x\n", i, off); |
61 | free(buf); | 61 | free(buf); |
62 | return 1; | 62 | return 1; |
63 | } | 63 | } |
64 | printf("Page %08x dump:\n", off); | 64 | printf("Page %08x dump:\n", off); |
65 | i = nand->oobblock >> 4; p = buf; | 65 | i = nand->oobblock >> 4; p = buf; |
66 | while (i--) { | 66 | while (i--) { |
67 | printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x" | 67 | printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x" |
68 | " %02x %02x %02x %02x %02x %02x %02x %02x\n", | 68 | " %02x %02x %02x %02x %02x %02x %02x %02x\n", |
69 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], | 69 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], |
70 | p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); | 70 | p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); |
71 | p += 16; | 71 | p += 16; |
72 | } | 72 | } |
73 | puts("OOB:\n"); | 73 | puts("OOB:\n"); |
74 | i = nand->oobsize >> 3; | 74 | i = nand->oobsize >> 3; |
75 | while (i--) { | 75 | while (i--) { |
76 | printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x\n", | 76 | printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x\n", |
77 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); | 77 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); |
78 | p += 8; | 78 | p += 8; |
79 | } | 79 | } |
80 | free(buf); | 80 | free(buf); |
81 | 81 | ||
82 | return 0; | 82 | return 0; |
83 | } | 83 | } |
84 | 84 | ||
85 | /* ------------------------------------------------------------------------- */ | 85 | /* ------------------------------------------------------------------------- */ |
86 | 86 | ||
87 | static inline int str2long(char *p, ulong *num) | 87 | static inline int str2long(char *p, ulong *num) |
88 | { | 88 | { |
89 | char *endptr; | 89 | char *endptr; |
90 | 90 | ||
91 | *num = simple_strtoul(p, &endptr, 16); | 91 | *num = simple_strtoul(p, &endptr, 16); |
92 | return (*p != '\0' && *endptr == '\0') ? 1 : 0; | 92 | return (*p != '\0' && *endptr == '\0') ? 1 : 0; |
93 | } | 93 | } |
94 | 94 | ||
95 | static int | 95 | static int |
96 | arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, ulong *size) | 96 | arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, ulong *size) |
97 | { | 97 | { |
98 | int idx = nand_curr_device; | 98 | int idx = nand_curr_device; |
99 | #if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) | 99 | #if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) |
100 | struct mtd_device *dev; | 100 | struct mtd_device *dev; |
101 | struct part_info *part; | 101 | struct part_info *part; |
102 | u8 pnum; | 102 | u8 pnum; |
103 | 103 | ||
104 | if (argc >= 1 && !(str2long(argv[0], off))) { | 104 | if (argc >= 1 && !(str2long(argv[0], off))) { |
105 | if ((mtdparts_init() == 0) && | 105 | if ((mtdparts_init() == 0) && |
106 | (find_dev_and_part(argv[0], &dev, &pnum, &part) == 0)) { | 106 | (find_dev_and_part(argv[0], &dev, &pnum, &part) == 0)) { |
107 | if (dev->id->type != MTD_DEV_TYPE_NAND) { | 107 | if (dev->id->type != MTD_DEV_TYPE_NAND) { |
108 | puts("not a NAND device\n"); | 108 | puts("not a NAND device\n"); |
109 | return -1; | 109 | return -1; |
110 | } | 110 | } |
111 | *off = part->offset; | 111 | *off = part->offset; |
112 | if (argc >= 2) { | 112 | if (argc >= 2) { |
113 | if (!(str2long(argv[1], size))) { | 113 | if (!(str2long(argv[1], size))) { |
114 | printf("'%s' is not a number\n", argv[1]); | 114 | printf("'%s' is not a number\n", argv[1]); |
115 | return -1; | 115 | return -1; |
116 | } | 116 | } |
117 | if (*size > part->size) | 117 | if (*size > part->size) |
118 | *size = part->size; | 118 | *size = part->size; |
119 | } else { | 119 | } else { |
120 | *size = part->size; | 120 | *size = part->size; |
121 | } | 121 | } |
122 | idx = dev->id->num; | 122 | idx = dev->id->num; |
123 | *nand = nand_info[idx]; | 123 | *nand = nand_info[idx]; |
124 | goto out; | 124 | goto out; |
125 | } | 125 | } |
126 | } | 126 | } |
127 | #endif | 127 | #endif |
128 | 128 | ||
129 | if (argc >= 1) { | 129 | if (argc >= 1) { |
130 | if (!(str2long(argv[0], off))) { | 130 | if (!(str2long(argv[0], off))) { |
131 | printf("'%s' is not a number\n", argv[0]); | 131 | printf("'%s' is not a number\n", argv[0]); |
132 | return -1; | 132 | return -1; |
133 | } | 133 | } |
134 | } else { | 134 | } else { |
135 | *off = 0; | 135 | *off = 0; |
136 | } | 136 | } |
137 | 137 | ||
138 | if (argc >= 2) { | 138 | if (argc >= 2) { |
139 | if (!(str2long(argv[1], size))) { | 139 | if (!(str2long(argv[1], size))) { |
140 | printf("'%s' is not a number\n", argv[1]); | 140 | printf("'%s' is not a number\n", argv[1]); |
141 | return -1; | 141 | return -1; |
142 | } | 142 | } |
143 | } else { | 143 | } else { |
144 | *size = nand->size - *off; | 144 | *size = nand->size - *off; |
145 | } | 145 | } |
146 | 146 | ||
147 | #if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) | 147 | #if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) |
148 | out: | 148 | out: |
149 | #endif | 149 | #endif |
150 | printf("device %d ", idx); | 150 | printf("device %d ", idx); |
151 | if (*size == nand->size) | 151 | if (*size == nand->size) |
152 | puts("whole chip\n"); | 152 | puts("whole chip\n"); |
153 | else | 153 | else |
154 | printf("offset 0x%x, size 0x%x\n", *off, *size); | 154 | printf("offset 0x%x, size 0x%x\n", *off, *size); |
155 | return 0; | 155 | return 0; |
156 | } | 156 | } |
157 | 157 | ||
158 | int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) | 158 | int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
159 | { | 159 | { |
160 | int i, dev, ret; | 160 | int i, dev, ret; |
161 | ulong addr, off, size; | 161 | ulong addr, off, size; |
162 | char *cmd, *s; | 162 | char *cmd, *s; |
163 | nand_info_t *nand; | 163 | nand_info_t *nand; |
164 | #ifdef CFG_NAND_QUIET | 164 | #ifdef CFG_NAND_QUIET |
165 | int quiet = CFG_NAND_QUIET; | 165 | int quiet = CFG_NAND_QUIET; |
166 | #else | 166 | #else |
167 | int quiet = 0; | 167 | int quiet = 0; |
168 | #endif | 168 | #endif |
169 | const char *quiet_str = getenv("quiet"); | 169 | const char *quiet_str = getenv("quiet"); |
170 | 170 | ||
171 | /* at least two arguments please */ | 171 | /* at least two arguments please */ |
172 | if (argc < 2) | 172 | if (argc < 2) |
173 | goto usage; | 173 | goto usage; |
174 | 174 | ||
175 | if (quiet_str) | 175 | if (quiet_str) |
176 | quiet = simple_strtoul(quiet_str, NULL, 0) != 0; | 176 | quiet = simple_strtoul(quiet_str, NULL, 0) != 0; |
177 | 177 | ||
178 | cmd = argv[1]; | 178 | cmd = argv[1]; |
179 | 179 | ||
180 | if (strcmp(cmd, "info") == 0) { | 180 | if (strcmp(cmd, "info") == 0) { |
181 | 181 | ||
182 | putc('\n'); | 182 | putc('\n'); |
183 | for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) { | 183 | for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) { |
184 | if (nand_info[i].name) | 184 | if (nand_info[i].name) |
185 | printf("Device %d: %s, sector size %lu KiB\n", | 185 | printf("Device %d: %s, sector size %lu KiB\n", |
186 | i, nand_info[i].name, | 186 | i, nand_info[i].name, |
187 | nand_info[i].erasesize >> 10); | 187 | nand_info[i].erasesize >> 10); |
188 | } | 188 | } |
189 | return 0; | 189 | return 0; |
190 | } | 190 | } |
191 | 191 | ||
192 | if (strcmp(cmd, "device") == 0) { | 192 | if (strcmp(cmd, "device") == 0) { |
193 | 193 | ||
194 | if (argc < 3) { | 194 | if (argc < 3) { |
195 | if ((nand_curr_device < 0) || | 195 | if ((nand_curr_device < 0) || |
196 | (nand_curr_device >= CFG_MAX_NAND_DEVICE)) | 196 | (nand_curr_device >= CFG_MAX_NAND_DEVICE)) |
197 | puts("\nno devices available\n"); | 197 | puts("\nno devices available\n"); |
198 | else | 198 | else |
199 | printf("\nDevice %d: %s\n", nand_curr_device, | 199 | printf("\nDevice %d: %s\n", nand_curr_device, |
200 | nand_info[nand_curr_device].name); | 200 | nand_info[nand_curr_device].name); |
201 | return 0; | 201 | return 0; |
202 | } | 202 | } |
203 | dev = (int)simple_strtoul(argv[2], NULL, 10); | 203 | dev = (int)simple_strtoul(argv[2], NULL, 10); |
204 | if (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) { | 204 | if (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) { |
205 | puts("No such device\n"); | 205 | puts("No such device\n"); |
206 | return 1; | 206 | return 1; |
207 | } | 207 | } |
208 | printf("Device %d: %s", dev, nand_info[dev].name); | 208 | printf("Device %d: %s", dev, nand_info[dev].name); |
209 | puts("... is now current device\n"); | 209 | puts("... is now current device\n"); |
210 | nand_curr_device = dev; | 210 | nand_curr_device = dev; |
211 | 211 | ||
212 | #ifdef CFG_NAND_SELECT_DEVICE | 212 | #ifdef CFG_NAND_SELECT_DEVICE |
213 | /* | 213 | /* |
214 | * Select the chip in the board/cpu specific driver | 214 | * Select the chip in the board/cpu specific driver |
215 | */ | 215 | */ |
216 | board_nand_select_device(nand_info[dev].priv, dev); | 216 | board_nand_select_device(nand_info[dev].priv, dev); |
217 | #endif | 217 | #endif |
218 | 218 | ||
219 | return 0; | 219 | return 0; |
220 | } | 220 | } |
221 | 221 | ||
222 | if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 && | 222 | if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 && |
223 | strncmp(cmd, "dump", 4) != 0 && | 223 | strncmp(cmd, "dump", 4) != 0 && |
224 | strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && | 224 | strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && |
225 | strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && | 225 | strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && |
226 | strcmp(cmd, "biterr") != 0 && | 226 | strcmp(cmd, "biterr") != 0 && |
227 | strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 ) | 227 | strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 ) |
228 | goto usage; | 228 | goto usage; |
229 | 229 | ||
230 | /* the following commands operate on the current device */ | 230 | /* the following commands operate on the current device */ |
231 | if (nand_curr_device < 0 || nand_curr_device >= CFG_MAX_NAND_DEVICE || | 231 | if (nand_curr_device < 0 || nand_curr_device >= CFG_MAX_NAND_DEVICE || |
232 | !nand_info[nand_curr_device].name) { | 232 | !nand_info[nand_curr_device].name) { |
233 | puts("\nno devices available\n"); | 233 | puts("\nno devices available\n"); |
234 | return 1; | 234 | return 1; |
235 | } | 235 | } |
236 | nand = &nand_info[nand_curr_device]; | 236 | nand = &nand_info[nand_curr_device]; |
237 | 237 | ||
238 | if (strcmp(cmd, "bad") == 0) { | 238 | if (strcmp(cmd, "bad") == 0) { |
239 | printf("\nDevice %d bad blocks:\n", nand_curr_device); | 239 | printf("\nDevice %d bad blocks:\n", nand_curr_device); |
240 | for (off = 0; off < nand->size; off += nand->erasesize) | 240 | for (off = 0; off < nand->size; off += nand->erasesize) |
241 | if (nand_block_isbad(nand, off)) | 241 | if (nand_block_isbad(nand, off)) |
242 | printf(" %08x\n", off); | 242 | printf(" %08x\n", off); |
243 | return 0; | 243 | return 0; |
244 | } | 244 | } |
245 | 245 | ||
246 | /* | 246 | /* |
247 | * Syntax is: | 247 | * Syntax is: |
248 | * 0 1 2 3 4 | 248 | * 0 1 2 3 4 |
249 | * nand erase [clean] [off size] | 249 | * nand erase [clean] [off size] |
250 | */ | 250 | */ |
251 | if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) { | 251 | if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) { |
252 | nand_erase_options_t opts; | 252 | nand_erase_options_t opts; |
253 | /* "clean" at index 2 means request to write cleanmarker */ | 253 | /* "clean" at index 2 means request to write cleanmarker */ |
254 | int clean = argc > 2 && !strcmp("clean", argv[2]); | 254 | int clean = argc > 2 && !strcmp("clean", argv[2]); |
255 | int o = clean ? 3 : 2; | 255 | int o = clean ? 3 : 2; |
256 | int scrub = !strcmp(cmd, "scrub"); | 256 | int scrub = !strcmp(cmd, "scrub"); |
257 | 257 | ||
258 | printf("\nNAND %s: ", scrub ? "scrub" : "erase"); | 258 | printf("\nNAND %s: ", scrub ? "scrub" : "erase"); |
259 | /* skip first two or three arguments, look for offset and size */ | 259 | /* skip first two or three arguments, look for offset and size */ |
260 | if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0) | 260 | if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0) |
261 | return 1; | 261 | return 1; |
262 | 262 | ||
263 | memset(&opts, 0, sizeof(opts)); | 263 | memset(&opts, 0, sizeof(opts)); |
264 | opts.offset = off; | 264 | opts.offset = off; |
265 | opts.length = size; | 265 | opts.length = size; |
266 | opts.jffs2 = clean; | 266 | opts.jffs2 = clean; |
267 | opts.quiet = quiet; | 267 | opts.quiet = quiet; |
268 | 268 | ||
269 | if (scrub) { | 269 | if (scrub) { |
270 | puts("Warning: " | 270 | puts("Warning: " |
271 | "scrub option will erase all factory set " | 271 | "scrub option will erase all factory set " |
272 | "bad blocks!\n" | 272 | "bad blocks!\n" |
273 | " " | 273 | " " |
274 | "There is no reliable way to recover them.\n" | 274 | "There is no reliable way to recover them.\n" |
275 | " " | 275 | " " |
276 | "Use this command only for testing purposes " | 276 | "Use this command only for testing purposes " |
277 | "if you\n" | 277 | "if you\n" |
278 | " " | 278 | " " |
279 | "are sure of what you are doing!\n" | 279 | "are sure of what you are doing!\n" |
280 | "\nReally scrub this NAND flash? <y/N>\n"); | 280 | "\nReally scrub this NAND flash? <y/N>\n"); |
281 | 281 | ||
282 | if (getc() == 'y' && getc() == '\r') { | 282 | if (getc() == 'y' && getc() == '\r') { |
283 | opts.scrub = 1; | 283 | opts.scrub = 1; |
284 | } else { | 284 | } else { |
285 | puts("scrub aborted\n"); | 285 | puts("scrub aborted\n"); |
286 | return -1; | 286 | return -1; |
287 | } | 287 | } |
288 | } | 288 | } |
289 | ret = nand_erase_opts(nand, &opts); | 289 | ret = nand_erase_opts(nand, &opts); |
290 | printf("%s\n", ret ? "ERROR" : "OK"); | 290 | printf("%s\n", ret ? "ERROR" : "OK"); |
291 | 291 | ||
292 | return ret == 0 ? 0 : 1; | 292 | return ret == 0 ? 0 : 1; |
293 | } | 293 | } |
294 | 294 | ||
295 | if (strncmp(cmd, "dump", 4) == 0) { | 295 | if (strncmp(cmd, "dump", 4) == 0) { |
296 | if (argc < 3) | 296 | if (argc < 3) |
297 | goto usage; | 297 | goto usage; |
298 | 298 | ||
299 | s = strchr(cmd, '.'); | 299 | s = strchr(cmd, '.'); |
300 | off = (int)simple_strtoul(argv[2], NULL, 16); | 300 | off = (int)simple_strtoul(argv[2], NULL, 16); |
301 | 301 | ||
302 | if (s != NULL && strcmp(s, ".oob") == 0) | 302 | if (s != NULL && strcmp(s, ".oob") == 0) |
303 | ret = nand_dump_oob(nand, off); | 303 | ret = nand_dump_oob(nand, off); |
304 | else | 304 | else |
305 | ret = nand_dump(nand, off); | 305 | ret = nand_dump(nand, off); |
306 | 306 | ||
307 | return ret == 0 ? 1 : 0; | 307 | return ret == 0 ? 1 : 0; |
308 | 308 | ||
309 | } | 309 | } |
310 | 310 | ||
311 | /* read write */ | 311 | /* read write */ |
312 | if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { | 312 | if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { |
313 | int read; | 313 | int read; |
314 | 314 | ||
315 | if (argc < 4) | 315 | if (argc < 4) |
316 | goto usage; | 316 | goto usage; |
317 | 317 | ||
318 | addr = (ulong)simple_strtoul(argv[2], NULL, 16); | 318 | addr = (ulong)simple_strtoul(argv[2], NULL, 16); |
319 | 319 | ||
320 | read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ | 320 | read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ |
321 | printf("\nNAND %s: ", read ? "read" : "write"); | 321 | printf("\nNAND %s: ", read ? "read" : "write"); |
322 | if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) | 322 | if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) |
323 | return 1; | 323 | return 1; |
324 | 324 | ||
325 | s = strchr(cmd, '.'); | 325 | s = strchr(cmd, '.'); |
326 | if (s != NULL && | 326 | if (s != NULL && |
327 | (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) { | 327 | (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) { |
328 | if (read) { | 328 | if (read) { |
329 | /* read */ | 329 | /* read */ |
330 | nand_read_options_t opts; | 330 | nand_read_options_t opts; |
331 | memset(&opts, 0, sizeof(opts)); | 331 | memset(&opts, 0, sizeof(opts)); |
332 | opts.buffer = (u_char*) addr; | 332 | opts.buffer = (u_char*) addr; |
333 | opts.length = size; | 333 | opts.length = size; |
334 | opts.offset = off; | 334 | opts.offset = off; |
335 | opts.quiet = quiet; | 335 | opts.quiet = quiet; |
336 | ret = nand_read_opts(nand, &opts); | 336 | ret = nand_read_opts(nand, &opts); |
337 | } else { | 337 | } else { |
338 | /* write */ | 338 | /* write */ |
339 | nand_write_options_t opts; | 339 | nand_write_options_t opts; |
340 | memset(&opts, 0, sizeof(opts)); | 340 | memset(&opts, 0, sizeof(opts)); |
341 | opts.buffer = (u_char*) addr; | 341 | opts.buffer = (u_char*) addr; |
342 | opts.length = size; | 342 | opts.length = size; |
343 | opts.offset = off; | 343 | opts.offset = off; |
344 | /* opts.forcejffs2 = 1; */ | 344 | /* opts.forcejffs2 = 1; */ |
345 | opts.pad = 1; | 345 | opts.pad = 1; |
346 | opts.blockalign = 1; | 346 | opts.blockalign = 1; |
347 | opts.quiet = quiet; | 347 | opts.quiet = quiet; |
348 | ret = nand_write_opts(nand, &opts); | 348 | ret = nand_write_opts(nand, &opts); |
349 | } | 349 | } |
350 | } else if (s != NULL && !strcmp(s, ".oob")) { | ||
351 | /* read out-of-band data */ | ||
352 | if (read) | ||
353 | ret = nand->read_oob(nand, off, size, &size, | ||
354 | (u_char *) addr); | ||
355 | else | ||
356 | ret = nand->write_oob(nand, off, size, &size, | ||
357 | (u_char *) addr); | ||
350 | } else { | 358 | } else { |
351 | if (read) | 359 | if (read) |
352 | ret = nand_read(nand, off, &size, (u_char *)addr); | 360 | ret = nand_read(nand, off, &size, (u_char *)addr); |
353 | else | 361 | else |
354 | ret = nand_write(nand, off, &size, (u_char *)addr); | 362 | ret = nand_write(nand, off, &size, (u_char *)addr); |
355 | } | 363 | } |
356 | 364 | ||
357 | printf(" %d bytes %s: %s\n", size, | 365 | printf(" %d bytes %s: %s\n", size, |
358 | read ? "read" : "written", ret ? "ERROR" : "OK"); | 366 | read ? "read" : "written", ret ? "ERROR" : "OK"); |
359 | 367 | ||
360 | return ret == 0 ? 0 : 1; | 368 | return ret == 0 ? 0 : 1; |
361 | } | 369 | } |
362 | 370 | ||
363 | if (strcmp(cmd, "markbad") == 0) { | 371 | if (strcmp(cmd, "markbad") == 0) { |
364 | addr = (ulong)simple_strtoul(argv[2], NULL, 16); | 372 | addr = (ulong)simple_strtoul(argv[2], NULL, 16); |
365 | 373 | ||
366 | int ret = nand->block_markbad(nand, addr); | 374 | int ret = nand->block_markbad(nand, addr); |
367 | if (ret == 0) { | 375 | if (ret == 0) { |
368 | printf("block 0x%08lx successfully marked as bad\n", | 376 | printf("block 0x%08lx successfully marked as bad\n", |
369 | (ulong) addr); | 377 | (ulong) addr); |
370 | return 0; | 378 | return 0; |
371 | } else { | 379 | } else { |
372 | printf("block 0x%08lx NOT marked as bad! ERROR %d\n", | 380 | printf("block 0x%08lx NOT marked as bad! ERROR %d\n", |
373 | (ulong) addr, ret); | 381 | (ulong) addr, ret); |
374 | } | 382 | } |
375 | return 1; | 383 | return 1; |
376 | } | 384 | } |
377 | if (strcmp(cmd, "biterr") == 0) { | 385 | if (strcmp(cmd, "biterr") == 0) { |
378 | /* todo */ | 386 | /* todo */ |
379 | return 1; | 387 | return 1; |
380 | } | 388 | } |
381 | 389 | ||
382 | if (strcmp(cmd, "lock") == 0) { | 390 | if (strcmp(cmd, "lock") == 0) { |
383 | int tight = 0; | 391 | int tight = 0; |
384 | int status = 0; | 392 | int status = 0; |
385 | if (argc == 3) { | 393 | if (argc == 3) { |
386 | if (!strcmp("tight", argv[2])) | 394 | if (!strcmp("tight", argv[2])) |
387 | tight = 1; | 395 | tight = 1; |
388 | if (!strcmp("status", argv[2])) | 396 | if (!strcmp("status", argv[2])) |
389 | status = 1; | 397 | status = 1; |
390 | } | 398 | } |
391 | 399 | ||
392 | if (status) { | 400 | if (status) { |
393 | ulong block_start = 0; | 401 | ulong block_start = 0; |
394 | ulong off; | 402 | ulong off; |
395 | int last_status = -1; | 403 | int last_status = -1; |
396 | 404 | ||
397 | struct nand_chip *nand_chip = nand->priv; | 405 | struct nand_chip *nand_chip = nand->priv; |
398 | /* check the WP bit */ | 406 | /* check the WP bit */ |
399 | nand_chip->cmdfunc (nand, NAND_CMD_STATUS, -1, -1); | 407 | nand_chip->cmdfunc (nand, NAND_CMD_STATUS, -1, -1); |
400 | printf("device is %swrite protected\n", | 408 | printf("device is %swrite protected\n", |
401 | (nand_chip->read_byte(nand) & 0x80 ? | 409 | (nand_chip->read_byte(nand) & 0x80 ? |
402 | "NOT " : "" ) ); | 410 | "NOT " : "" ) ); |
403 | 411 | ||
404 | for (off = 0; off < nand->size; off += nand->oobblock) { | 412 | for (off = 0; off < nand->size; off += nand->oobblock) { |
405 | int s = nand_get_lock_status(nand, off); | 413 | int s = nand_get_lock_status(nand, off); |
406 | 414 | ||
407 | /* print message only if status has changed | 415 | /* print message only if status has changed |
408 | * or at end of chip | 416 | * or at end of chip |
409 | */ | 417 | */ |
410 | if (off == nand->size - nand->oobblock | 418 | if (off == nand->size - nand->oobblock |
411 | || (s != last_status && off != 0)) { | 419 | || (s != last_status && off != 0)) { |
412 | 420 | ||
413 | printf("%08x - %08x: %8d pages %s%s%s\n", | 421 | printf("%08x - %08x: %8d pages %s%s%s\n", |
414 | block_start, | 422 | block_start, |
415 | off-1, | 423 | off-1, |
416 | (off-block_start)/nand->oobblock, | 424 | (off-block_start)/nand->oobblock, |
417 | ((last_status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""), | 425 | ((last_status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""), |
418 | ((last_status & NAND_LOCK_STATUS_LOCK) ? "LOCK " : ""), | 426 | ((last_status & NAND_LOCK_STATUS_LOCK) ? "LOCK " : ""), |
419 | ((last_status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : "")); | 427 | ((last_status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : "")); |
420 | } | 428 | } |
421 | 429 | ||
422 | last_status = s; | 430 | last_status = s; |
423 | } | 431 | } |
424 | } else { | 432 | } else { |
425 | if (!nand_lock(nand, tight)) { | 433 | if (!nand_lock(nand, tight)) { |
426 | puts("NAND flash successfully locked\n"); | 434 | puts("NAND flash successfully locked\n"); |
427 | } else { | 435 | } else { |
428 | puts("Error locking NAND flash\n"); | 436 | puts("Error locking NAND flash\n"); |
429 | return 1; | 437 | return 1; |
430 | } | 438 | } |
431 | } | 439 | } |
432 | return 0; | 440 | return 0; |
433 | } | 441 | } |
434 | 442 | ||
435 | if (strcmp(cmd, "unlock") == 0) { | 443 | if (strcmp(cmd, "unlock") == 0) { |
436 | if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0) | 444 | if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0) |
437 | return 1; | 445 | return 1; |
438 | 446 | ||
439 | if (!nand_unlock(nand, off, size)) { | 447 | if (!nand_unlock(nand, off, size)) { |
440 | puts("NAND flash successfully unlocked\n"); | 448 | puts("NAND flash successfully unlocked\n"); |
441 | } else { | 449 | } else { |
442 | puts("Error unlocking NAND flash, " | 450 | puts("Error unlocking NAND flash, " |
443 | "write and erase will probably fail\n"); | 451 | "write and erase will probably fail\n"); |
444 | return 1; | 452 | return 1; |
445 | } | 453 | } |
446 | return 0; | 454 | return 0; |
447 | } | 455 | } |
448 | 456 | ||
449 | usage: | 457 | usage: |
450 | printf("Usage:\n%s\n", cmdtp->usage); | 458 | printf("Usage:\n%s\n", cmdtp->usage); |
451 | return 1; | 459 | return 1; |
452 | } | 460 | } |
453 | 461 | ||
454 | U_BOOT_CMD(nand, 5, 1, do_nand, | 462 | U_BOOT_CMD(nand, 5, 1, do_nand, |
455 | "nand - NAND sub-system\n", | 463 | "nand - NAND sub-system\n", |
456 | "info - show available NAND devices\n" | 464 | "info - show available NAND devices\n" |
457 | "nand device [dev] - show or set current device\n" | 465 | "nand device [dev] - show or set current device\n" |
458 | "nand read[.jffs2] - addr off|partition size\n" | 466 | "nand read[.jffs2] - addr off|partition size\n" |
459 | "nand write[.jffs2] - addr off|partition size - read/write `size' bytes starting\n" | 467 | "nand write[.jffs2] - addr off|partition size - read/write `size' bytes starting\n" |
460 | " at offset `off' to/from memory address `addr'\n" | 468 | " at offset `off' to/from memory address `addr'\n" |
461 | "nand erase [clean] [off size] - erase `size' bytes from\n" | 469 | "nand erase [clean] [off size] - erase `size' bytes from\n" |
462 | " offset `off' (entire device if not specified)\n" | 470 | " offset `off' (entire device if not specified)\n" |
463 | "nand bad - show bad blocks\n" | 471 | "nand bad - show bad blocks\n" |
464 | "nand dump[.oob] off - dump page\n" | 472 | "nand dump[.oob] off - dump page\n" |
465 | "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" | 473 | "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" |
466 | "nand markbad off - mark bad block at offset (UNSAFE)\n" | 474 | "nand markbad off - mark bad block at offset (UNSAFE)\n" |
467 | "nand biterr off - make a bit error at offset (UNSAFE)\n" | 475 | "nand biterr off - make a bit error at offset (UNSAFE)\n" |
468 | "nand lock [tight] [status] - bring nand to lock state or display locked pages\n" | 476 | "nand lock [tight] [status] - bring nand to lock state or display locked pages\n" |
469 | "nand unlock [offset] [size] - unlock section\n"); | 477 | "nand unlock [offset] [size] - unlock section\n"); |
470 | 478 | ||
471 | static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, | 479 | static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, |
472 | ulong offset, ulong addr, char *cmd) | 480 | ulong offset, ulong addr, char *cmd) |
473 | { | 481 | { |
474 | int r; | 482 | int r; |
475 | char *ep, *s; | 483 | char *ep, *s; |
476 | ulong cnt; | 484 | ulong cnt; |
477 | image_header_t *hdr; | 485 | image_header_t *hdr; |
478 | int jffs2 = 0; | 486 | int jffs2 = 0; |
479 | 487 | ||
480 | s = strchr(cmd, '.'); | 488 | s = strchr(cmd, '.'); |
481 | if (s != NULL && | 489 | if (s != NULL && |
482 | (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) | 490 | (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) |
483 | jffs2 = 1; | 491 | jffs2 = 1; |
484 | 492 | ||
485 | printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset); | 493 | printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset); |
486 | 494 | ||
487 | cnt = nand->oobblock; | 495 | cnt = nand->oobblock; |
488 | if (jffs2) { | 496 | if (jffs2) { |
489 | nand_read_options_t opts; | 497 | nand_read_options_t opts; |
490 | memset(&opts, 0, sizeof(opts)); | 498 | memset(&opts, 0, sizeof(opts)); |
491 | opts.buffer = (u_char*) addr; | 499 | opts.buffer = (u_char*) addr; |
492 | opts.length = cnt; | 500 | opts.length = cnt; |
493 | opts.offset = offset; | 501 | opts.offset = offset; |
494 | opts.quiet = 1; | 502 | opts.quiet = 1; |
495 | r = nand_read_opts(nand, &opts); | 503 | r = nand_read_opts(nand, &opts); |
496 | } else { | 504 | } else { |
497 | r = nand_read(nand, offset, &cnt, (u_char *) addr); | 505 | r = nand_read(nand, offset, &cnt, (u_char *) addr); |
498 | } | 506 | } |
499 | 507 | ||
500 | if (r) { | 508 | if (r) { |
501 | puts("** Read error\n"); | 509 | puts("** Read error\n"); |
502 | show_boot_progress (-56); | 510 | show_boot_progress (-56); |
503 | return 1; | 511 | return 1; |
504 | } | 512 | } |
505 | show_boot_progress (56); | 513 | show_boot_progress (56); |
506 | 514 | ||
507 | hdr = (image_header_t *) addr; | 515 | hdr = (image_header_t *) addr; |
508 | 516 | ||
509 | if (ntohl(hdr->ih_magic) != IH_MAGIC) { | 517 | if (ntohl(hdr->ih_magic) != IH_MAGIC) { |
510 | printf("\n** Bad Magic Number 0x%x **\n", hdr->ih_magic); | 518 | printf("\n** Bad Magic Number 0x%x **\n", hdr->ih_magic); |
511 | show_boot_progress (-57); | 519 | show_boot_progress (-57); |
512 | return 1; | 520 | return 1; |
513 | } | 521 | } |
514 | show_boot_progress (57); | 522 | show_boot_progress (57); |
515 | 523 | ||
516 | print_image_hdr(hdr); | 524 | print_image_hdr(hdr); |
517 | 525 | ||
518 | cnt = (ntohl(hdr->ih_size) + sizeof (image_header_t)); | 526 | cnt = (ntohl(hdr->ih_size) + sizeof (image_header_t)); |
519 | if (jffs2) { | 527 | if (jffs2) { |
520 | nand_read_options_t opts; | 528 | nand_read_options_t opts; |
521 | memset(&opts, 0, sizeof(opts)); | 529 | memset(&opts, 0, sizeof(opts)); |
522 | opts.buffer = (u_char*) addr; | 530 | opts.buffer = (u_char*) addr; |
523 | opts.length = cnt; | 531 | opts.length = cnt; |
524 | opts.offset = offset; | 532 | opts.offset = offset; |
525 | opts.quiet = 1; | 533 | opts.quiet = 1; |
526 | r = nand_read_opts(nand, &opts); | 534 | r = nand_read_opts(nand, &opts); |
527 | } else { | 535 | } else { |
528 | r = nand_read(nand, offset, &cnt, (u_char *) addr); | 536 | r = nand_read(nand, offset, &cnt, (u_char *) addr); |
529 | } | 537 | } |
530 | 538 | ||
531 | if (r) { | 539 | if (r) { |
532 | puts("** Read error\n"); | 540 | puts("** Read error\n"); |
533 | show_boot_progress (-58); | 541 | show_boot_progress (-58); |
534 | return 1; | 542 | return 1; |
535 | } | 543 | } |
536 | show_boot_progress (58); | 544 | show_boot_progress (58); |
537 | 545 | ||
538 | /* Loading ok, update default load address */ | 546 | /* Loading ok, update default load address */ |
539 | 547 | ||
540 | load_addr = addr; | 548 | load_addr = addr; |
541 | 549 | ||
542 | /* Check if we should attempt an auto-start */ | 550 | /* Check if we should attempt an auto-start */ |
543 | if (((ep = getenv("autostart")) != NULL) && (strcmp(ep, "yes") == 0)) { | 551 | if (((ep = getenv("autostart")) != NULL) && (strcmp(ep, "yes") == 0)) { |
544 | char *local_args[2]; | 552 | char *local_args[2]; |
545 | extern int do_bootm(cmd_tbl_t *, int, int, char *[]); | 553 | extern int do_bootm(cmd_tbl_t *, int, int, char *[]); |
546 | 554 | ||
547 | local_args[0] = cmd; | 555 | local_args[0] = cmd; |
548 | local_args[1] = NULL; | 556 | local_args[1] = NULL; |
549 | 557 | ||
550 | printf("Automatic boot of image at addr 0x%08lx ...\n", addr); | 558 | printf("Automatic boot of image at addr 0x%08lx ...\n", addr); |
551 | 559 | ||
552 | do_bootm(cmdtp, 0, 1, local_args); | 560 | do_bootm(cmdtp, 0, 1, local_args); |
553 | return 1; | 561 | return 1; |
554 | } | 562 | } |
555 | return 0; | 563 | return 0; |
556 | } | 564 | } |
557 | 565 | ||
558 | int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) | 566 | int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
559 | { | 567 | { |
560 | char *boot_device = NULL; | 568 | char *boot_device = NULL; |
561 | int idx; | 569 | int idx; |
562 | ulong addr, offset = 0; | 570 | ulong addr, offset = 0; |
563 | #if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) | 571 | #if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) |
564 | struct mtd_device *dev; | 572 | struct mtd_device *dev; |
565 | struct part_info *part; | 573 | struct part_info *part; |
566 | u8 pnum; | 574 | u8 pnum; |
567 | 575 | ||
568 | if (argc >= 2) { | 576 | if (argc >= 2) { |
569 | char *p = (argc == 2) ? argv[1] : argv[2]; | 577 | char *p = (argc == 2) ? argv[1] : argv[2]; |
570 | if (!(str2long(p, &addr)) && (mtdparts_init() == 0) && | 578 | if (!(str2long(p, &addr)) && (mtdparts_init() == 0) && |
571 | (find_dev_and_part(p, &dev, &pnum, &part) == 0)) { | 579 | (find_dev_and_part(p, &dev, &pnum, &part) == 0)) { |
572 | if (dev->id->type != MTD_DEV_TYPE_NAND) { | 580 | if (dev->id->type != MTD_DEV_TYPE_NAND) { |
573 | puts("Not a NAND device\n"); | 581 | puts("Not a NAND device\n"); |
574 | return 1; | 582 | return 1; |
575 | } | 583 | } |
576 | if (argc > 3) | 584 | if (argc > 3) |
577 | goto usage; | 585 | goto usage; |
578 | if (argc == 3) | 586 | if (argc == 3) |
579 | addr = simple_strtoul(argv[1], NULL, 16); | 587 | addr = simple_strtoul(argv[1], NULL, 16); |
580 | else | 588 | else |
581 | addr = CFG_LOAD_ADDR; | 589 | addr = CFG_LOAD_ADDR; |
582 | return nand_load_image(cmdtp, &nand_info[dev->id->num], | 590 | return nand_load_image(cmdtp, &nand_info[dev->id->num], |
583 | part->offset, addr, argv[0]); | 591 | part->offset, addr, argv[0]); |
584 | } | 592 | } |
585 | } | 593 | } |
586 | #endif | 594 | #endif |
587 | 595 | ||
588 | show_boot_progress(52); | 596 | show_boot_progress(52); |
589 | switch (argc) { | 597 | switch (argc) { |
590 | case 1: | 598 | case 1: |
591 | addr = CFG_LOAD_ADDR; | 599 | addr = CFG_LOAD_ADDR; |
592 | boot_device = getenv("bootdevice"); | 600 | boot_device = getenv("bootdevice"); |
593 | break; | 601 | break; |
594 | case 2: | 602 | case 2: |
595 | addr = simple_strtoul(argv[1], NULL, 16); | 603 | addr = simple_strtoul(argv[1], NULL, 16); |
596 | boot_device = getenv("bootdevice"); | 604 | boot_device = getenv("bootdevice"); |
597 | break; | 605 | break; |
598 | case 3: | 606 | case 3: |
599 | addr = simple_strtoul(argv[1], NULL, 16); | 607 | addr = simple_strtoul(argv[1], NULL, 16); |
600 | boot_device = argv[2]; | 608 | boot_device = argv[2]; |
601 | break; | 609 | break; |
602 | case 4: | 610 | case 4: |
603 | addr = simple_strtoul(argv[1], NULL, 16); | 611 | addr = simple_strtoul(argv[1], NULL, 16); |
604 | boot_device = argv[2]; | 612 | boot_device = argv[2]; |
605 | offset = simple_strtoul(argv[3], NULL, 16); | 613 | offset = simple_strtoul(argv[3], NULL, 16); |
606 | break; | 614 | break; |
607 | default: | 615 | default: |
608 | #if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) | 616 | #if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) |
609 | usage: | 617 | usage: |
610 | #endif | 618 | #endif |
611 | printf("Usage:\n%s\n", cmdtp->usage); | 619 | printf("Usage:\n%s\n", cmdtp->usage); |
612 | show_boot_progress(-53); | 620 | show_boot_progress(-53); |
613 | return 1; | 621 | return 1; |
614 | } | 622 | } |
615 | 623 | ||
616 | show_boot_progress(53); | 624 | show_boot_progress(53); |
617 | if (!boot_device) { | 625 | if (!boot_device) { |
618 | puts("\n** No boot device **\n"); | 626 | puts("\n** No boot device **\n"); |
619 | show_boot_progress(-54); | 627 | show_boot_progress(-54); |
620 | return 1; | 628 | return 1; |
621 | } | 629 | } |
622 | show_boot_progress(54); | 630 | show_boot_progress(54); |
623 | 631 | ||
624 | idx = simple_strtoul(boot_device, NULL, 16); | 632 | idx = simple_strtoul(boot_device, NULL, 16); |
625 | 633 | ||
626 | if (idx < 0 || idx >= CFG_MAX_NAND_DEVICE || !nand_info[idx].name) { | 634 | if (idx < 0 || idx >= CFG_MAX_NAND_DEVICE || !nand_info[idx].name) { |
627 | printf("\n** Device %d not available\n", idx); | 635 | printf("\n** Device %d not available\n", idx); |
628 | show_boot_progress(-55); | 636 | show_boot_progress(-55); |
629 | return 1; | 637 | return 1; |
630 | } | 638 | } |
631 | show_boot_progress(55); | 639 | show_boot_progress(55); |
632 | 640 | ||
633 | return nand_load_image(cmdtp, &nand_info[idx], offset, addr, argv[0]); | 641 | return nand_load_image(cmdtp, &nand_info[idx], offset, addr, argv[0]); |
634 | } | 642 | } |
635 | 643 | ||
636 | U_BOOT_CMD(nboot, 4, 1, do_nandboot, | 644 | U_BOOT_CMD(nboot, 4, 1, do_nandboot, |
637 | "nboot - boot from NAND device\n", | 645 | "nboot - boot from NAND device\n", |
638 | "[.jffs2] [partition] | [[[loadAddr] dev] offset]\n"); | 646 | "[.jffs2] [partition] | [[[loadAddr] dev] offset]\n"); |
639 | 647 | ||
640 | #endif | 648 | #endif |
641 | 649 | ||
642 | #else /* CFG_NAND_LEGACY */ | 650 | #else /* CFG_NAND_LEGACY */ |
643 | /* | 651 | /* |
644 | * | 652 | * |
645 | * Legacy NAND support - to be phased out | 653 | * Legacy NAND support - to be phased out |
646 | * | 654 | * |
647 | */ | 655 | */ |
648 | #include <command.h> | 656 | #include <command.h> |
649 | #include <malloc.h> | 657 | #include <malloc.h> |
650 | #include <asm/io.h> | 658 | #include <asm/io.h> |
651 | #include <watchdog.h> | 659 | #include <watchdog.h> |
652 | 660 | ||
653 | #ifdef CONFIG_show_boot_progress | 661 | #ifdef CONFIG_show_boot_progress |
654 | # include <status_led.h> | 662 | # include <status_led.h> |
655 | # define show_boot_progress(arg) show_boot_progress(arg) | 663 | # define show_boot_progress(arg) show_boot_progress(arg) |
656 | #else | 664 | #else |
657 | # define show_boot_progress(arg) | 665 | # define show_boot_progress(arg) |
658 | #endif | 666 | #endif |
659 | 667 | ||
660 | #if defined(CONFIG_CMD_NAND) | 668 | #if defined(CONFIG_CMD_NAND) |
661 | #include <linux/mtd/nand_legacy.h> | 669 | #include <linux/mtd/nand_legacy.h> |
662 | #if 0 | 670 | #if 0 |
663 | #include <linux/mtd/nand_ids.h> | 671 | #include <linux/mtd/nand_ids.h> |
664 | #include <jffs2/jffs2.h> | 672 | #include <jffs2/jffs2.h> |
665 | #endif | 673 | #endif |
666 | 674 | ||
667 | #ifdef CONFIG_OMAP1510 | 675 | #ifdef CONFIG_OMAP1510 |
668 | void archflashwp(void *archdata, int wp); | 676 | void archflashwp(void *archdata, int wp); |
669 | #endif | 677 | #endif |
670 | 678 | ||
671 | #define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) | 679 | #define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) |
672 | 680 | ||
673 | #undef NAND_DEBUG | 681 | #undef NAND_DEBUG |
674 | #undef PSYCHO_DEBUG | 682 | #undef PSYCHO_DEBUG |
675 | 683 | ||
676 | /* ****************** WARNING ********************* | 684 | /* ****************** WARNING ********************* |
677 | * When ALLOW_ERASE_BAD_DEBUG is non-zero the erase command will | 685 | * When ALLOW_ERASE_BAD_DEBUG is non-zero the erase command will |
678 | * erase (or at least attempt to erase) blocks that are marked | 686 | * erase (or at least attempt to erase) blocks that are marked |
679 | * bad. This can be very handy if you are _sure_ that the block | 687 | * bad. This can be very handy if you are _sure_ that the block |
680 | * is OK, say because you marked a good block bad to test bad | 688 | * is OK, say because you marked a good block bad to test bad |
681 | * block handling and you are done testing, or if you have | 689 | * block handling and you are done testing, or if you have |
682 | * accidentally marked blocks bad. | 690 | * accidentally marked blocks bad. |
683 | * | 691 | * |
684 | * Erasing factory marked bad blocks is a _bad_ idea. If the | 692 | * Erasing factory marked bad blocks is a _bad_ idea. If the |
685 | * erase succeeds there is no reliable way to find them again, | 693 | * erase succeeds there is no reliable way to find them again, |
686 | * and attempting to program or erase bad blocks can affect | 694 | * and attempting to program or erase bad blocks can affect |
687 | * the data in _other_ (good) blocks. | 695 | * the data in _other_ (good) blocks. |
688 | */ | 696 | */ |
689 | #define ALLOW_ERASE_BAD_DEBUG 0 | 697 | #define ALLOW_ERASE_BAD_DEBUG 0 |
690 | 698 | ||
691 | #define CONFIG_MTD_NAND_ECC /* enable ECC */ | 699 | #define CONFIG_MTD_NAND_ECC /* enable ECC */ |
692 | #define CONFIG_MTD_NAND_ECC_JFFS2 | 700 | #define CONFIG_MTD_NAND_ECC_JFFS2 |
693 | 701 | ||
694 | /* bits for nand_legacy_rw() `cmd'; or together as needed */ | 702 | /* bits for nand_legacy_rw() `cmd'; or together as needed */ |
695 | #define NANDRW_READ 0x01 | 703 | #define NANDRW_READ 0x01 |
696 | #define NANDRW_WRITE 0x00 | 704 | #define NANDRW_WRITE 0x00 |
697 | #define NANDRW_JFFS2 0x02 | 705 | #define NANDRW_JFFS2 0x02 |
698 | #define NANDRW_JFFS2_SKIP 0x04 | 706 | #define NANDRW_JFFS2_SKIP 0x04 |
699 | 707 | ||
700 | /* | 708 | /* |
701 | * Imports from nand_legacy.c | 709 | * Imports from nand_legacy.c |
702 | */ | 710 | */ |
703 | extern struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE]; | 711 | extern struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE]; |
704 | extern int curr_device; | 712 | extern int curr_device; |
705 | extern int nand_legacy_erase(struct nand_chip *nand, size_t ofs, | 713 | extern int nand_legacy_erase(struct nand_chip *nand, size_t ofs, |
706 | size_t len, int clean); | 714 | size_t len, int clean); |
707 | extern int nand_legacy_rw(struct nand_chip *nand, int cmd, size_t start, | 715 | extern int nand_legacy_rw(struct nand_chip *nand, int cmd, size_t start, |
708 | size_t len, size_t *retlen, u_char *buf); | 716 | size_t len, size_t *retlen, u_char *buf); |
709 | extern void nand_print(struct nand_chip *nand); | 717 | extern void nand_print(struct nand_chip *nand); |
710 | extern void nand_print_bad(struct nand_chip *nand); | 718 | extern void nand_print_bad(struct nand_chip *nand); |
711 | extern int nand_read_oob(struct nand_chip *nand, size_t ofs, | 719 | extern int nand_read_oob(struct nand_chip *nand, size_t ofs, |
712 | size_t len, size_t *retlen, u_char *buf); | 720 | size_t len, size_t *retlen, u_char *buf); |
713 | extern int nand_write_oob(struct nand_chip *nand, size_t ofs, | 721 | extern int nand_write_oob(struct nand_chip *nand, size_t ofs, |
714 | size_t len, size_t *retlen, const u_char *buf); | 722 | size_t len, size_t *retlen, const u_char *buf); |
715 | 723 | ||
716 | 724 | ||
717 | int do_nand (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) | 725 | int do_nand (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
718 | { | 726 | { |
719 | int rcode = 0; | 727 | int rcode = 0; |
720 | 728 | ||
721 | switch (argc) { | 729 | switch (argc) { |
722 | case 0: | 730 | case 0: |
723 | case 1: | 731 | case 1: |
724 | printf ("Usage:\n%s\n", cmdtp->usage); | 732 | printf ("Usage:\n%s\n", cmdtp->usage); |
725 | return 1; | 733 | return 1; |
726 | case 2: | 734 | case 2: |
727 | if (strcmp (argv[1], "info") == 0) { | 735 | if (strcmp (argv[1], "info") == 0) { |
728 | int i; | 736 | int i; |
729 | 737 | ||
730 | putc ('\n'); | 738 | putc ('\n'); |
731 | 739 | ||
732 | for (i = 0; i < CFG_MAX_NAND_DEVICE; ++i) { | 740 | for (i = 0; i < CFG_MAX_NAND_DEVICE; ++i) { |
733 | if (nand_dev_desc[i].ChipID == | 741 | if (nand_dev_desc[i].ChipID == |
734 | NAND_ChipID_UNKNOWN) | 742 | NAND_ChipID_UNKNOWN) |
735 | continue; /* list only known devices */ | 743 | continue; /* list only known devices */ |
736 | printf ("Device %d: ", i); | 744 | printf ("Device %d: ", i); |
737 | nand_print (&nand_dev_desc[i]); | 745 | nand_print (&nand_dev_desc[i]); |
738 | } | 746 | } |
739 | return 0; | 747 | return 0; |
740 | 748 | ||
741 | } else if (strcmp (argv[1], "device") == 0) { | 749 | } else if (strcmp (argv[1], "device") == 0) { |
742 | if ((curr_device < 0) | 750 | if ((curr_device < 0) |
743 | || (curr_device >= CFG_MAX_NAND_DEVICE)) { | 751 | || (curr_device >= CFG_MAX_NAND_DEVICE)) { |
744 | puts ("\nno devices available\n"); | 752 | puts ("\nno devices available\n"); |
745 | return 1; | 753 | return 1; |
746 | } | 754 | } |
747 | printf ("\nDevice %d: ", curr_device); | 755 | printf ("\nDevice %d: ", curr_device); |
748 | nand_print (&nand_dev_desc[curr_device]); | 756 | nand_print (&nand_dev_desc[curr_device]); |
749 | return 0; | 757 | return 0; |
750 | 758 | ||
751 | } else if (strcmp (argv[1], "bad") == 0) { | 759 | } else if (strcmp (argv[1], "bad") == 0) { |
752 | if ((curr_device < 0) | 760 | if ((curr_device < 0) |
753 | || (curr_device >= CFG_MAX_NAND_DEVICE)) { | 761 | || (curr_device >= CFG_MAX_NAND_DEVICE)) { |
754 | puts ("\nno devices available\n"); | 762 | puts ("\nno devices available\n"); |
755 | return 1; | 763 | return 1; |
756 | } | 764 | } |
757 | printf ("\nDevice %d bad blocks:\n", curr_device); | 765 | printf ("\nDevice %d bad blocks:\n", curr_device); |
758 | nand_print_bad (&nand_dev_desc[curr_device]); | 766 | nand_print_bad (&nand_dev_desc[curr_device]); |
759 | return 0; | 767 | return 0; |
760 | 768 | ||
761 | } | 769 | } |
762 | printf ("Usage:\n%s\n", cmdtp->usage); | 770 | printf ("Usage:\n%s\n", cmdtp->usage); |
763 | return 1; | 771 | return 1; |
764 | case 3: | 772 | case 3: |
765 | if (strcmp (argv[1], "device") == 0) { | 773 | if (strcmp (argv[1], "device") == 0) { |
766 | int dev = (int) simple_strtoul (argv[2], NULL, 10); | 774 | int dev = (int) simple_strtoul (argv[2], NULL, 10); |
767 | 775 | ||
768 | printf ("\nDevice %d: ", dev); | 776 | printf ("\nDevice %d: ", dev); |
769 | if (dev >= CFG_MAX_NAND_DEVICE) { | 777 | if (dev >= CFG_MAX_NAND_DEVICE) { |
770 | puts ("unknown device\n"); | 778 | puts ("unknown device\n"); |
771 | return 1; | 779 | return 1; |
772 | } | 780 | } |
773 | nand_print (&nand_dev_desc[dev]); | 781 | nand_print (&nand_dev_desc[dev]); |
774 | /*nand_print (dev); */ | 782 | /*nand_print (dev); */ |
775 | 783 | ||
776 | if (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN) { | 784 | if (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN) { |
777 | return 1; | 785 | return 1; |
778 | } | 786 | } |
779 | 787 | ||
780 | curr_device = dev; | 788 | curr_device = dev; |
781 | 789 | ||
782 | puts ("... is now current device\n"); | 790 | puts ("... is now current device\n"); |
783 | 791 | ||
784 | return 0; | 792 | return 0; |
785 | } else if (strcmp (argv[1], "erase") == 0 | 793 | } else if (strcmp (argv[1], "erase") == 0 |
786 | && strcmp (argv[2], "clean") == 0) { | 794 | && strcmp (argv[2], "clean") == 0) { |
787 | struct nand_chip *nand = &nand_dev_desc[curr_device]; | 795 | struct nand_chip *nand = &nand_dev_desc[curr_device]; |
788 | ulong off = 0; | 796 | ulong off = 0; |
789 | ulong size = nand->totlen; | 797 | ulong size = nand->totlen; |
790 | int ret; | 798 | int ret; |
791 | 799 | ||
792 | printf ("\nNAND erase: device %d offset %ld, size %ld ... ", curr_device, off, size); | 800 | printf ("\nNAND erase: device %d offset %ld, size %ld ... ", curr_device, off, size); |
793 | 801 | ||
794 | ret = nand_legacy_erase (nand, off, size, 1); | 802 | ret = nand_legacy_erase (nand, off, size, 1); |
795 | 803 | ||
796 | printf ("%s\n", ret ? "ERROR" : "OK"); | 804 | printf ("%s\n", ret ? "ERROR" : "OK"); |
797 | 805 | ||
798 | return ret; | 806 | return ret; |
799 | } | 807 | } |
800 | 808 | ||
801 | printf ("Usage:\n%s\n", cmdtp->usage); | 809 | printf ("Usage:\n%s\n", cmdtp->usage); |
802 | return 1; | 810 | return 1; |
803 | default: | 811 | default: |
804 | /* at least 4 args */ | 812 | /* at least 4 args */ |
805 | 813 | ||
806 | if (strncmp (argv[1], "read", 4) == 0 || | 814 | if (strncmp (argv[1], "read", 4) == 0 || |
807 | strncmp (argv[1], "write", 5) == 0) { | 815 | strncmp (argv[1], "write", 5) == 0) { |
808 | ulong addr = simple_strtoul (argv[2], NULL, 16); | 816 | ulong addr = simple_strtoul (argv[2], NULL, 16); |
809 | ulong off = simple_strtoul (argv[3], NULL, 16); | 817 | ulong off = simple_strtoul (argv[3], NULL, 16); |
810 | ulong size = simple_strtoul (argv[4], NULL, 16); | 818 | ulong size = simple_strtoul (argv[4], NULL, 16); |
811 | int cmd = (strncmp (argv[1], "read", 4) == 0) ? | 819 | int cmd = (strncmp (argv[1], "read", 4) == 0) ? |
812 | NANDRW_READ : NANDRW_WRITE; | 820 | NANDRW_READ : NANDRW_WRITE; |
813 | int ret, total; | 821 | int ret, total; |
814 | char *cmdtail = strchr (argv[1], '.'); | 822 | char *cmdtail = strchr (argv[1], '.'); |
815 | 823 | ||
816 | if (cmdtail && !strncmp (cmdtail, ".oob", 2)) { | 824 | if (cmdtail && !strncmp (cmdtail, ".oob", 2)) { |
817 | /* read out-of-band data */ | 825 | /* read out-of-band data */ |
818 | if (cmd & NANDRW_READ) { | 826 | if (cmd & NANDRW_READ) { |
819 | ret = nand_read_oob (nand_dev_desc + curr_device, | 827 | ret = nand_read_oob (nand_dev_desc + curr_device, |
820 | off, size, (size_t *) & total, | 828 | off, size, (size_t *) & total, |
821 | (u_char *) addr); | 829 | (u_char *) addr); |
822 | } else { | 830 | } else { |
823 | ret = nand_write_oob (nand_dev_desc + curr_device, | 831 | ret = nand_write_oob (nand_dev_desc + curr_device, |
824 | off, size, (size_t *) & total, | 832 | off, size, (size_t *) & total, |
825 | (u_char *) addr); | 833 | (u_char *) addr); |
826 | } | 834 | } |
827 | return ret; | 835 | return ret; |
828 | } else if (cmdtail && !strncmp (cmdtail, ".jffs2", 2)) | 836 | } else if (cmdtail && !strncmp (cmdtail, ".jffs2", 2)) |
829 | cmd |= NANDRW_JFFS2; /* skip bad blocks */ | 837 | cmd |= NANDRW_JFFS2; /* skip bad blocks */ |
830 | else if (cmdtail && !strncmp (cmdtail, ".jffs2s", 2)) { | 838 | else if (cmdtail && !strncmp (cmdtail, ".jffs2s", 2)) { |
831 | cmd |= NANDRW_JFFS2; /* skip bad blocks (on read too) */ | 839 | cmd |= NANDRW_JFFS2; /* skip bad blocks (on read too) */ |
832 | if (cmd & NANDRW_READ) | 840 | if (cmd & NANDRW_READ) |
833 | cmd |= NANDRW_JFFS2_SKIP; /* skip bad blocks (on read too) */ | 841 | cmd |= NANDRW_JFFS2_SKIP; /* skip bad blocks (on read too) */ |
834 | } | 842 | } |
835 | #ifdef SXNI855T | 843 | #ifdef SXNI855T |
836 | /* need ".e" same as ".j" for compatibility with older units */ | 844 | /* need ".e" same as ".j" for compatibility with older units */ |
837 | else if (cmdtail && !strcmp (cmdtail, ".e")) | 845 | else if (cmdtail && !strcmp (cmdtail, ".e")) |
838 | cmd |= NANDRW_JFFS2; /* skip bad blocks */ | 846 | cmd |= NANDRW_JFFS2; /* skip bad blocks */ |
839 | #endif | 847 | #endif |
840 | #ifdef CFG_NAND_SKIP_BAD_DOT_I | 848 | #ifdef CFG_NAND_SKIP_BAD_DOT_I |
841 | /* need ".i" same as ".jffs2s" for compatibility with older units (esd) */ | 849 | /* need ".i" same as ".jffs2s" for compatibility with older units (esd) */ |
842 | /* ".i" for image -> read skips bad block (no 0xff) */ | 850 | /* ".i" for image -> read skips bad block (no 0xff) */ |
843 | else if (cmdtail && !strcmp (cmdtail, ".i")) { | 851 | else if (cmdtail && !strcmp (cmdtail, ".i")) { |
844 | cmd |= NANDRW_JFFS2; /* skip bad blocks (on read too) */ | 852 | cmd |= NANDRW_JFFS2; /* skip bad blocks (on read too) */ |
845 | if (cmd & NANDRW_READ) | 853 | if (cmd & NANDRW_READ) |
846 | cmd |= NANDRW_JFFS2_SKIP; /* skip bad blocks (on read too) */ | 854 | cmd |= NANDRW_JFFS2_SKIP; /* skip bad blocks (on read too) */ |
847 | } | 855 | } |
848 | #endif /* CFG_NAND_SKIP_BAD_DOT_I */ | 856 | #endif /* CFG_NAND_SKIP_BAD_DOT_I */ |
849 | else if (cmdtail) { | 857 | else if (cmdtail) { |
850 | printf ("Usage:\n%s\n", cmdtp->usage); | 858 | printf ("Usage:\n%s\n", cmdtp->usage); |
851 | return 1; | 859 | return 1; |
852 | } | 860 | } |
853 | 861 | ||
854 | printf ("\nNAND %s: device %d offset %ld, size %ld ...\n", | 862 | printf ("\nNAND %s: device %d offset %ld, size %ld ...\n", |
855 | (cmd & NANDRW_READ) ? "read" : "write", | 863 | (cmd & NANDRW_READ) ? "read" : "write", |
856 | curr_device, off, size); | 864 | curr_device, off, size); |
857 | 865 | ||
858 | ret = nand_legacy_rw (nand_dev_desc + curr_device, | 866 | ret = nand_legacy_rw (nand_dev_desc + curr_device, |
859 | cmd, off, size, | 867 | cmd, off, size, |
860 | (size_t *) & total, | 868 | (size_t *) & total, |
861 | (u_char *) addr); | 869 | (u_char *) addr); |
862 | 870 | ||
863 | printf (" %d bytes %s: %s\n", total, | 871 | printf (" %d bytes %s: %s\n", total, |
864 | (cmd & NANDRW_READ) ? "read" : "written", | 872 | (cmd & NANDRW_READ) ? "read" : "written", |
865 | ret ? "ERROR" : "OK"); | 873 | ret ? "ERROR" : "OK"); |
866 | 874 | ||
867 | return ret; | 875 | return ret; |
868 | } else if (strcmp (argv[1], "erase") == 0 && | 876 | } else if (strcmp (argv[1], "erase") == 0 && |
869 | (argc == 4 || strcmp ("clean", argv[2]) == 0)) { | 877 | (argc == 4 || strcmp ("clean", argv[2]) == 0)) { |
870 | int clean = argc == 5; | 878 | int clean = argc == 5; |
871 | ulong off = | 879 | ulong off = |
872 | simple_strtoul (argv[2 + clean], NULL, 16); | 880 | simple_strtoul (argv[2 + clean], NULL, 16); |
873 | ulong size = | 881 | ulong size = |
874 | simple_strtoul (argv[3 + clean], NULL, 16); | 882 | simple_strtoul (argv[3 + clean], NULL, 16); |
875 | int ret; | 883 | int ret; |
876 | 884 | ||
877 | printf ("\nNAND erase: device %d offset %ld, size %ld ...\n", | 885 | printf ("\nNAND erase: device %d offset %ld, size %ld ...\n", |
878 | curr_device, off, size); | 886 | curr_device, off, size); |
879 | 887 | ||
880 | ret = nand_legacy_erase (nand_dev_desc + curr_device, | 888 | ret = nand_legacy_erase (nand_dev_desc + curr_device, |
881 | off, size, clean); | 889 | off, size, clean); |
882 | 890 | ||
883 | printf ("%s\n", ret ? "ERROR" : "OK"); | 891 | printf ("%s\n", ret ? "ERROR" : "OK"); |
884 | 892 | ||
885 | return ret; | 893 | return ret; |
886 | } else { | 894 | } else { |
887 | printf ("Usage:\n%s\n", cmdtp->usage); | 895 | printf ("Usage:\n%s\n", cmdtp->usage); |
888 | rcode = 1; | 896 | rcode = 1; |
889 | } | 897 | } |
890 | 898 | ||
891 | return rcode; | 899 | return rcode; |
892 | } | 900 | } |
893 | } | 901 | } |
894 | 902 | ||
895 | U_BOOT_CMD( | 903 | U_BOOT_CMD( |
896 | nand, 5, 1, do_nand, | 904 | nand, 5, 1, do_nand, |
897 | "nand - legacy NAND sub-system\n", | 905 | "nand - legacy NAND sub-system\n", |
898 | "info - show available NAND devices\n" | 906 | "info - show available NAND devices\n" |
899 | "nand device [dev] - show or set current device\n" | 907 | "nand device [dev] - show or set current device\n" |
900 | "nand read[.jffs2[s]] addr off size\n" | 908 | "nand read[.jffs2[s]] addr off size\n" |
901 | "nand write[.jffs2] addr off size - read/write `size' bytes starting\n" | 909 | "nand write[.jffs2] addr off size - read/write `size' bytes starting\n" |
902 | " at offset `off' to/from memory address `addr'\n" | 910 | " at offset `off' to/from memory address `addr'\n" |
903 | "nand erase [clean] [off size] - erase `size' bytes from\n" | 911 | "nand erase [clean] [off size] - erase `size' bytes from\n" |
904 | " offset `off' (entire device if not specified)\n" | 912 | " offset `off' (entire device if not specified)\n" |
905 | "nand bad - show bad blocks\n" | 913 | "nand bad - show bad blocks\n" |
906 | "nand read.oob addr off size - read out-of-band data\n" | 914 | "nand read.oob addr off size - read out-of-band data\n" |
907 | "nand write.oob addr off size - read out-of-band data\n" | 915 | "nand write.oob addr off size - read out-of-band data\n" |
908 | ); | 916 | ); |
909 | 917 | ||
910 | int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) | 918 | int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) |
911 | { | 919 | { |
912 | char *boot_device = NULL; | 920 | char *boot_device = NULL; |
913 | char *ep; | 921 | char *ep; |
914 | int dev; | 922 | int dev; |
915 | ulong cnt; | 923 | ulong cnt; |
916 | ulong addr; | 924 | ulong addr; |
917 | ulong offset = 0; | 925 | ulong offset = 0; |
918 | image_header_t *hdr; | 926 | image_header_t *hdr; |
919 | int rcode = 0; | 927 | int rcode = 0; |
920 | show_boot_progress (52); | 928 | show_boot_progress (52); |
921 | switch (argc) { | 929 | switch (argc) { |
922 | case 1: | 930 | case 1: |
923 | addr = CFG_LOAD_ADDR; | 931 | addr = CFG_LOAD_ADDR; |
924 | boot_device = getenv ("bootdevice"); | 932 | boot_device = getenv ("bootdevice"); |
925 | break; | 933 | break; |
926 | case 2: | 934 | case 2: |
927 | addr = simple_strtoul(argv[1], NULL, 16); | 935 | addr = simple_strtoul(argv[1], NULL, 16); |
928 | boot_device = getenv ("bootdevice"); | 936 | boot_device = getenv ("bootdevice"); |
929 | break; | 937 | break; |
930 | case 3: | 938 | case 3: |
931 | addr = simple_strtoul(argv[1], NULL, 16); | 939 | addr = simple_strtoul(argv[1], NULL, 16); |
932 | boot_device = argv[2]; | 940 | boot_device = argv[2]; |
933 | break; | 941 | break; |
934 | case 4: | 942 | case 4: |
935 | addr = simple_strtoul(argv[1], NULL, 16); | 943 | addr = simple_strtoul(argv[1], NULL, 16); |
936 | boot_device = argv[2]; | 944 | boot_device = argv[2]; |
937 | offset = simple_strtoul(argv[3], NULL, 16); | 945 | offset = simple_strtoul(argv[3], NULL, 16); |
938 | break; | 946 | break; |
939 | default: | 947 | default: |
940 | printf ("Usage:\n%s\n", cmdtp->usage); | 948 | printf ("Usage:\n%s\n", cmdtp->usage); |
941 | show_boot_progress (-53); | 949 | show_boot_progress (-53); |
942 | return 1; | 950 | return 1; |
943 | } | 951 | } |
944 | 952 | ||
945 | show_boot_progress (53); | 953 | show_boot_progress (53); |
946 | if (!boot_device) { | 954 | if (!boot_device) { |
947 | puts ("\n** No boot device **\n"); | 955 | puts ("\n** No boot device **\n"); |
948 | show_boot_progress (-54); | 956 | show_boot_progress (-54); |
949 | return 1; | 957 | return 1; |
950 | } | 958 | } |
951 | show_boot_progress (54); | 959 | show_boot_progress (54); |
952 | 960 | ||
953 | dev = simple_strtoul(boot_device, &ep, 16); | 961 | dev = simple_strtoul(boot_device, &ep, 16); |
954 | 962 | ||
955 | if ((dev >= CFG_MAX_NAND_DEVICE) || | 963 | if ((dev >= CFG_MAX_NAND_DEVICE) || |
956 | (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN)) { | 964 | (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN)) { |
957 | printf ("\n** Device %d not available\n", dev); | 965 | printf ("\n** Device %d not available\n", dev); |
958 | show_boot_progress (-55); | 966 | show_boot_progress (-55); |
959 | return 1; | 967 | return 1; |
960 | } | 968 | } |
961 | show_boot_progress (55); | 969 | show_boot_progress (55); |
962 | 970 | ||
963 | printf ("\nLoading from device %d: %s at 0x%lx (offset 0x%lx)\n", | 971 | printf ("\nLoading from device %d: %s at 0x%lx (offset 0x%lx)\n", |
964 | dev, nand_dev_desc[dev].name, nand_dev_desc[dev].IO_ADDR, | 972 | dev, nand_dev_desc[dev].name, nand_dev_desc[dev].IO_ADDR, |
965 | offset); | 973 | offset); |
966 | 974 | ||
967 | if (nand_legacy_rw (nand_dev_desc + dev, NANDRW_READ, offset, | 975 | if (nand_legacy_rw (nand_dev_desc + dev, NANDRW_READ, offset, |
968 | SECTORSIZE, NULL, (u_char *)addr)) { | 976 | SECTORSIZE, NULL, (u_char *)addr)) { |
969 | printf ("** Read error on %d\n", dev); | 977 | printf ("** Read error on %d\n", dev); |
970 | show_boot_progress (-56); | 978 | show_boot_progress (-56); |
971 | return 1; | 979 | return 1; |
972 | } | 980 | } |
973 | show_boot_progress (56); | 981 | show_boot_progress (56); |
974 | 982 | ||
975 | hdr = (image_header_t *)addr; | 983 | hdr = (image_header_t *)addr; |
976 | 984 | ||
977 | if (ntohl(hdr->ih_magic) == IH_MAGIC) { | 985 | if (ntohl(hdr->ih_magic) == IH_MAGIC) { |
978 | 986 | ||
979 | print_image_hdr (hdr); | 987 | print_image_hdr (hdr); |
980 | 988 | ||
981 | cnt = (ntohl(hdr->ih_size) + sizeof(image_header_t)); | 989 | cnt = (ntohl(hdr->ih_size) + sizeof(image_header_t)); |
982 | cnt -= SECTORSIZE; | 990 | cnt -= SECTORSIZE; |
983 | } else { | 991 | } else { |
984 | printf ("\n** Bad Magic Number 0x%x **\n", ntohl(hdr->ih_magic)); | 992 | printf ("\n** Bad Magic Number 0x%x **\n", ntohl(hdr->ih_magic)); |
985 | show_boot_progress (-57); | 993 | show_boot_progress (-57); |
986 | return 1; | 994 | return 1; |
987 | } | 995 | } |
988 | show_boot_progress (57); | 996 | show_boot_progress (57); |
989 | 997 | ||
990 | if (nand_legacy_rw (nand_dev_desc + dev, NANDRW_READ, | 998 | if (nand_legacy_rw (nand_dev_desc + dev, NANDRW_READ, |
991 | offset + SECTORSIZE, cnt, NULL, | 999 | offset + SECTORSIZE, cnt, NULL, |
992 | (u_char *)(addr+SECTORSIZE))) { | 1000 | (u_char *)(addr+SECTORSIZE))) { |
993 | printf ("** Read error on %d\n", dev); | 1001 | printf ("** Read error on %d\n", dev); |
994 | show_boot_progress (-58); | 1002 | show_boot_progress (-58); |
995 | return 1; | 1003 | return 1; |
996 | } | 1004 | } |
997 | show_boot_progress (58); | 1005 | show_boot_progress (58); |
998 | 1006 | ||
999 | /* Loading ok, update default load address */ | 1007 | /* Loading ok, update default load address */ |
1000 | 1008 | ||
1001 | load_addr = addr; | 1009 | load_addr = addr; |
1002 | 1010 | ||
1003 | /* Check if we should attempt an auto-start */ | 1011 | /* Check if we should attempt an auto-start */ |
1004 | if (((ep = getenv("autostart")) != NULL) && (strcmp(ep,"yes") == 0)) { | 1012 | if (((ep = getenv("autostart")) != NULL) && (strcmp(ep,"yes") == 0)) { |
1005 | char *local_args[2]; | 1013 | char *local_args[2]; |
1006 | extern int do_bootm (cmd_tbl_t *, int, int, char *[]); | 1014 | extern int do_bootm (cmd_tbl_t *, int, int, char *[]); |
1007 | 1015 | ||
1008 | local_args[0] = argv[0]; | 1016 | local_args[0] = argv[0]; |
1009 | local_args[1] = NULL; | 1017 | local_args[1] = NULL; |
1010 | 1018 | ||
1011 | printf ("Automatic boot of image at addr 0x%08lx ...\n", addr); | 1019 | printf ("Automatic boot of image at addr 0x%08lx ...\n", addr); |
1012 | 1020 | ||
1013 | do_bootm (cmdtp, 0, 1, local_args); | 1021 | do_bootm (cmdtp, 0, 1, local_args); |
1014 | rcode = 1; | 1022 | rcode = 1; |
1015 | } | 1023 | } |
1016 | return rcode; | 1024 | return rcode; |
1017 | } | 1025 | } |
1018 | 1026 | ||
1019 | U_BOOT_CMD( | 1027 | U_BOOT_CMD( |
1020 | nboot, 4, 1, do_nandboot, | 1028 | nboot, 4, 1, do_nandboot, |
1021 | "nboot - boot from NAND device\n", | 1029 | "nboot - boot from NAND device\n", |
1022 | "loadAddr dev\n" | 1030 | "loadAddr dev\n" |
1023 | ); | 1031 | ); |
1024 | 1032 | ||
1025 | #endif | 1033 | #endif |
1026 | 1034 | ||
1027 | #endif /* CFG_NAND_LEGACY */ | 1035 | #endif /* CFG_NAND_LEGACY */ |
1028 | 1036 |