Commit c9494866df835bcee68e17339aec1090faa704da
Committed by
Scott Wood
1 parent
169d54d8b3
Exists in
master
and in
54 other branches
cmd_nand: add nand write.trimffs command
Add another nand write. variant, trimffs. This command will request of nand_write_skip_bad() that all trailing all-0xff pages will be dropped from eraseblocks when they are written to flash as-per the reccommended behaviour of the UBI FAQ [1]. The function that implements this timming is the drop_ffs() function by Artem Bityutskiy, ported from the mtd-utils tree. [1] http://www.linux-mtd.infradead.org/doc/ubi.html#L_flasher_algo Signed-off-by: Ben Gardiner <bengardiner@nanometrics.ca> CC: Artem Bityutskiy <dedekind1@gmail.com> CC: Detlev Zundel <dzu@denx.de> Acked-by: Scott Wood <scottwood@freescale.com> Signed-off-by: Scott Wood <scottwood@freescale.com>
Showing 2 changed files with 26 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 | * Ported 'dynenv' to 'nand env.oob' command | 7 | * Ported 'dynenv' to 'nand env.oob' command |
8 | * (C) 2010 Nanometrics, Inc. | 8 | * (C) 2010 Nanometrics, Inc. |
9 | * 'dynenv' -- Dynamic environment offset in NAND OOB | 9 | * 'dynenv' -- Dynamic environment offset in NAND OOB |
10 | * (C) Copyright 2006-2007 OpenMoko, Inc. | 10 | * (C) Copyright 2006-2007 OpenMoko, Inc. |
11 | * Added 16-bit nand support | 11 | * Added 16-bit nand support |
12 | * (C) 2004 Texas Instruments | 12 | * (C) 2004 Texas Instruments |
13 | * | 13 | * |
14 | * Copyright 2010 Freescale Semiconductor | 14 | * Copyright 2010 Freescale Semiconductor |
15 | * The portions of this file whose copyright is held by Freescale and which | 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 | 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 | 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 | 18 | * published by the Free Software Foundation; either version 2 of the |
19 | * License, or (at your option) any later version. | 19 | * License, or (at your option) any later version. |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <common.h> | 22 | #include <common.h> |
23 | #include <linux/mtd/mtd.h> | 23 | #include <linux/mtd/mtd.h> |
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_MTDPARTS) | 31 | #if defined(CONFIG_CMD_MTDPARTS) |
32 | 32 | ||
33 | /* partition handling routines */ | 33 | /* partition 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 | static int nand_dump(nand_info_t *nand, ulong off, int only_oob, int repeat) | 40 | static int nand_dump(nand_info_t *nand, ulong off, int only_oob, int repeat) |
41 | { | 41 | { |
42 | int i; | 42 | int i; |
43 | u_char *datbuf, *oobbuf, *p; | 43 | u_char *datbuf, *oobbuf, *p; |
44 | static loff_t last; | 44 | static loff_t last; |
45 | 45 | ||
46 | if (repeat) | 46 | if (repeat) |
47 | off = last + nand->writesize; | 47 | off = last + nand->writesize; |
48 | 48 | ||
49 | last = off; | 49 | last = off; |
50 | 50 | ||
51 | datbuf = malloc(nand->writesize + nand->oobsize); | 51 | datbuf = malloc(nand->writesize + nand->oobsize); |
52 | oobbuf = malloc(nand->oobsize); | 52 | oobbuf = malloc(nand->oobsize); |
53 | if (!datbuf || !oobbuf) { | 53 | if (!datbuf || !oobbuf) { |
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->writesize - 1); | 57 | off &= ~(nand->writesize - 1); |
58 | loff_t addr = (loff_t) off; | 58 | loff_t addr = (loff_t) off; |
59 | struct mtd_oob_ops ops; | 59 | struct mtd_oob_ops ops; |
60 | memset(&ops, 0, sizeof(ops)); | 60 | memset(&ops, 0, sizeof(ops)); |
61 | ops.datbuf = datbuf; | 61 | ops.datbuf = datbuf; |
62 | ops.oobbuf = oobbuf; /* must exist, but oob data will be appended to ops.datbuf */ | 62 | ops.oobbuf = oobbuf; /* must exist, but oob data will be appended to ops.datbuf */ |
63 | ops.len = nand->writesize; | 63 | ops.len = nand->writesize; |
64 | ops.ooblen = nand->oobsize; | 64 | ops.ooblen = nand->oobsize; |
65 | ops.mode = MTD_OOB_RAW; | 65 | ops.mode = MTD_OOB_RAW; |
66 | i = nand->read_oob(nand, addr, &ops); | 66 | i = nand->read_oob(nand, addr, &ops); |
67 | if (i < 0) { | 67 | if (i < 0) { |
68 | printf("Error (%d) reading page %08lx\n", i, off); | 68 | printf("Error (%d) reading page %08lx\n", i, off); |
69 | free(datbuf); | 69 | free(datbuf); |
70 | free(oobbuf); | 70 | free(oobbuf); |
71 | return 1; | 71 | return 1; |
72 | } | 72 | } |
73 | printf("Page %08lx dump:\n", off); | 73 | printf("Page %08lx dump:\n", off); |
74 | i = nand->writesize >> 4; | 74 | i = nand->writesize >> 4; |
75 | p = datbuf; | 75 | p = datbuf; |
76 | 76 | ||
77 | while (i--) { | 77 | while (i--) { |
78 | if (!only_oob) | 78 | if (!only_oob) |
79 | printf("\t%02x %02x %02x %02x %02x %02x %02x %02x" | 79 | printf("\t%02x %02x %02x %02x %02x %02x %02x %02x" |
80 | " %02x %02x %02x %02x %02x %02x %02x %02x\n", | 80 | " %02x %02x %02x %02x %02x %02x %02x %02x\n", |
81 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], | 81 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], |
82 | p[8], p[9], p[10], p[11], p[12], p[13], p[14], | 82 | p[8], p[9], p[10], p[11], p[12], p[13], p[14], |
83 | p[15]); | 83 | p[15]); |
84 | p += 16; | 84 | p += 16; |
85 | } | 85 | } |
86 | puts("OOB:\n"); | 86 | puts("OOB:\n"); |
87 | i = nand->oobsize >> 3; | 87 | i = nand->oobsize >> 3; |
88 | while (i--) { | 88 | while (i--) { |
89 | printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n", | 89 | printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n", |
90 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); | 90 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); |
91 | p += 8; | 91 | p += 8; |
92 | } | 92 | } |
93 | free(datbuf); | 93 | free(datbuf); |
94 | free(oobbuf); | 94 | free(oobbuf); |
95 | 95 | ||
96 | return 0; | 96 | return 0; |
97 | } | 97 | } |
98 | 98 | ||
99 | /* ------------------------------------------------------------------------- */ | 99 | /* ------------------------------------------------------------------------- */ |
100 | 100 | ||
101 | static int set_dev(int dev) | 101 | static int set_dev(int dev) |
102 | { | 102 | { |
103 | if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || | 103 | if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || |
104 | !nand_info[dev].name) { | 104 | !nand_info[dev].name) { |
105 | puts("No such device\n"); | 105 | puts("No such device\n"); |
106 | return -1; | 106 | return -1; |
107 | } | 107 | } |
108 | 108 | ||
109 | if (nand_curr_device == dev) | 109 | if (nand_curr_device == dev) |
110 | return 0; | 110 | return 0; |
111 | 111 | ||
112 | printf("Device %d: %s", dev, nand_info[dev].name); | 112 | printf("Device %d: %s", dev, nand_info[dev].name); |
113 | puts("... is now current device\n"); | 113 | puts("... is now current device\n"); |
114 | nand_curr_device = dev; | 114 | nand_curr_device = dev; |
115 | 115 | ||
116 | #ifdef CONFIG_SYS_NAND_SELECT_DEVICE | 116 | #ifdef CONFIG_SYS_NAND_SELECT_DEVICE |
117 | board_nand_select_device(nand_info[dev].priv, dev); | 117 | board_nand_select_device(nand_info[dev].priv, dev); |
118 | #endif | 118 | #endif |
119 | 119 | ||
120 | return 0; | 120 | return 0; |
121 | } | 121 | } |
122 | 122 | ||
123 | static inline int str2off(const char *p, loff_t *num) | 123 | static inline int str2off(const char *p, loff_t *num) |
124 | { | 124 | { |
125 | char *endptr; | 125 | char *endptr; |
126 | 126 | ||
127 | *num = simple_strtoull(p, &endptr, 16); | 127 | *num = simple_strtoull(p, &endptr, 16); |
128 | return *p != '\0' && *endptr == '\0'; | 128 | return *p != '\0' && *endptr == '\0'; |
129 | } | 129 | } |
130 | 130 | ||
131 | static inline int str2long(const char *p, ulong *num) | 131 | static inline int str2long(const char *p, ulong *num) |
132 | { | 132 | { |
133 | char *endptr; | 133 | char *endptr; |
134 | 134 | ||
135 | *num = simple_strtoul(p, &endptr, 16); | 135 | *num = simple_strtoul(p, &endptr, 16); |
136 | return *p != '\0' && *endptr == '\0'; | 136 | return *p != '\0' && *endptr == '\0'; |
137 | } | 137 | } |
138 | 138 | ||
139 | static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size) | 139 | static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size) |
140 | { | 140 | { |
141 | #ifdef CONFIG_CMD_MTDPARTS | 141 | #ifdef CONFIG_CMD_MTDPARTS |
142 | struct mtd_device *dev; | 142 | struct mtd_device *dev; |
143 | struct part_info *part; | 143 | struct part_info *part; |
144 | u8 pnum; | 144 | u8 pnum; |
145 | int ret; | 145 | int ret; |
146 | 146 | ||
147 | ret = mtdparts_init(); | 147 | ret = mtdparts_init(); |
148 | if (ret) | 148 | if (ret) |
149 | return ret; | 149 | return ret; |
150 | 150 | ||
151 | ret = find_dev_and_part(partname, &dev, &pnum, &part); | 151 | ret = find_dev_and_part(partname, &dev, &pnum, &part); |
152 | if (ret) | 152 | if (ret) |
153 | return ret; | 153 | return ret; |
154 | 154 | ||
155 | if (dev->id->type != MTD_DEV_TYPE_NAND) { | 155 | if (dev->id->type != MTD_DEV_TYPE_NAND) { |
156 | puts("not a NAND device\n"); | 156 | puts("not a NAND device\n"); |
157 | return -1; | 157 | return -1; |
158 | } | 158 | } |
159 | 159 | ||
160 | *off = part->offset; | 160 | *off = part->offset; |
161 | *size = part->size; | 161 | *size = part->size; |
162 | *idx = dev->id->num; | 162 | *idx = dev->id->num; |
163 | 163 | ||
164 | ret = set_dev(*idx); | 164 | ret = set_dev(*idx); |
165 | if (ret) | 165 | if (ret) |
166 | return ret; | 166 | return ret; |
167 | 167 | ||
168 | return 0; | 168 | return 0; |
169 | #else | 169 | #else |
170 | puts("offset is not a number\n"); | 170 | puts("offset is not a number\n"); |
171 | return -1; | 171 | return -1; |
172 | #endif | 172 | #endif |
173 | } | 173 | } |
174 | 174 | ||
175 | static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *maxsize) | 175 | static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *maxsize) |
176 | { | 176 | { |
177 | if (!str2off(arg, off)) | 177 | if (!str2off(arg, off)) |
178 | return get_part(arg, idx, off, maxsize); | 178 | return get_part(arg, idx, off, maxsize); |
179 | 179 | ||
180 | if (*off >= nand_info[*idx].size) { | 180 | if (*off >= nand_info[*idx].size) { |
181 | puts("Offset exceeds device limit\n"); | 181 | puts("Offset exceeds device limit\n"); |
182 | return -1; | 182 | return -1; |
183 | } | 183 | } |
184 | 184 | ||
185 | *maxsize = nand_info[*idx].size - *off; | 185 | *maxsize = nand_info[*idx].size - *off; |
186 | return 0; | 186 | return 0; |
187 | } | 187 | } |
188 | 188 | ||
189 | static int arg_off_size(int argc, char *const argv[], int *idx, | 189 | static int arg_off_size(int argc, char *const argv[], int *idx, |
190 | loff_t *off, loff_t *size) | 190 | loff_t *off, loff_t *size) |
191 | { | 191 | { |
192 | int ret; | 192 | int ret; |
193 | loff_t maxsize; | 193 | loff_t maxsize; |
194 | 194 | ||
195 | if (argc == 0) { | 195 | if (argc == 0) { |
196 | *off = 0; | 196 | *off = 0; |
197 | *size = nand_info[*idx].size; | 197 | *size = nand_info[*idx].size; |
198 | goto print; | 198 | goto print; |
199 | } | 199 | } |
200 | 200 | ||
201 | ret = arg_off(argv[0], idx, off, &maxsize); | 201 | ret = arg_off(argv[0], idx, off, &maxsize); |
202 | if (ret) | 202 | if (ret) |
203 | return ret; | 203 | return ret; |
204 | 204 | ||
205 | if (argc == 1) { | 205 | if (argc == 1) { |
206 | *size = maxsize; | 206 | *size = maxsize; |
207 | goto print; | 207 | goto print; |
208 | } | 208 | } |
209 | 209 | ||
210 | if (!str2off(argv[1], size)) { | 210 | if (!str2off(argv[1], size)) { |
211 | printf("'%s' is not a number\n", argv[1]); | 211 | printf("'%s' is not a number\n", argv[1]); |
212 | return -1; | 212 | return -1; |
213 | } | 213 | } |
214 | 214 | ||
215 | if (*size > maxsize) { | 215 | if (*size > maxsize) { |
216 | puts("Size exceeds partition or device limit\n"); | 216 | puts("Size exceeds partition or device limit\n"); |
217 | return -1; | 217 | return -1; |
218 | } | 218 | } |
219 | 219 | ||
220 | print: | 220 | print: |
221 | printf("device %d ", *idx); | 221 | printf("device %d ", *idx); |
222 | if (*size == nand_info[*idx].size) | 222 | if (*size == nand_info[*idx].size) |
223 | puts("whole chip\n"); | 223 | puts("whole chip\n"); |
224 | else | 224 | else |
225 | printf("offset 0x%llx, size 0x%llx\n", | 225 | printf("offset 0x%llx, size 0x%llx\n", |
226 | (unsigned long long)*off, (unsigned long long)*size); | 226 | (unsigned long long)*off, (unsigned long long)*size); |
227 | return 0; | 227 | return 0; |
228 | } | 228 | } |
229 | 229 | ||
230 | #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK | 230 | #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK |
231 | static void print_status(ulong start, ulong end, ulong erasesize, int status) | 231 | static void print_status(ulong start, ulong end, ulong erasesize, int status) |
232 | { | 232 | { |
233 | printf("%08lx - %08lx: %08lx blocks %s%s%s\n", | 233 | printf("%08lx - %08lx: %08lx blocks %s%s%s\n", |
234 | start, | 234 | start, |
235 | end - 1, | 235 | end - 1, |
236 | (end - start) / erasesize, | 236 | (end - start) / erasesize, |
237 | ((status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""), | 237 | ((status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""), |
238 | ((status & NAND_LOCK_STATUS_LOCK) ? "LOCK " : ""), | 238 | ((status & NAND_LOCK_STATUS_LOCK) ? "LOCK " : ""), |
239 | ((status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : "")); | 239 | ((status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : "")); |
240 | } | 240 | } |
241 | 241 | ||
242 | static void do_nand_status(nand_info_t *nand) | 242 | static void do_nand_status(nand_info_t *nand) |
243 | { | 243 | { |
244 | ulong block_start = 0; | 244 | ulong block_start = 0; |
245 | ulong off; | 245 | ulong off; |
246 | int last_status = -1; | 246 | int last_status = -1; |
247 | 247 | ||
248 | struct nand_chip *nand_chip = nand->priv; | 248 | struct nand_chip *nand_chip = nand->priv; |
249 | /* check the WP bit */ | 249 | /* check the WP bit */ |
250 | nand_chip->cmdfunc(nand, NAND_CMD_STATUS, -1, -1); | 250 | nand_chip->cmdfunc(nand, NAND_CMD_STATUS, -1, -1); |
251 | printf("device is %swrite protected\n", | 251 | printf("device is %swrite protected\n", |
252 | (nand_chip->read_byte(nand) & 0x80 ? | 252 | (nand_chip->read_byte(nand) & 0x80 ? |
253 | "NOT " : "")); | 253 | "NOT " : "")); |
254 | 254 | ||
255 | for (off = 0; off < nand->size; off += nand->erasesize) { | 255 | for (off = 0; off < nand->size; off += nand->erasesize) { |
256 | int s = nand_get_lock_status(nand, off); | 256 | int s = nand_get_lock_status(nand, off); |
257 | 257 | ||
258 | /* print message only if status has changed */ | 258 | /* print message only if status has changed */ |
259 | if (s != last_status && off != 0) { | 259 | if (s != last_status && off != 0) { |
260 | print_status(block_start, off, nand->erasesize, | 260 | print_status(block_start, off, nand->erasesize, |
261 | last_status); | 261 | last_status); |
262 | block_start = off; | 262 | block_start = off; |
263 | } | 263 | } |
264 | last_status = s; | 264 | last_status = s; |
265 | } | 265 | } |
266 | /* Print the last block info */ | 266 | /* Print the last block info */ |
267 | print_status(block_start, off, nand->erasesize, last_status); | 267 | print_status(block_start, off, nand->erasesize, last_status); |
268 | } | 268 | } |
269 | #endif | 269 | #endif |
270 | 270 | ||
271 | #ifdef CONFIG_ENV_OFFSET_OOB | 271 | #ifdef CONFIG_ENV_OFFSET_OOB |
272 | unsigned long nand_env_oob_offset; | 272 | unsigned long nand_env_oob_offset; |
273 | 273 | ||
274 | int do_nand_env_oob(cmd_tbl_t *cmdtp, int argc, char *const argv[]) | 274 | int do_nand_env_oob(cmd_tbl_t *cmdtp, int argc, char *const argv[]) |
275 | { | 275 | { |
276 | int ret; | 276 | int ret; |
277 | uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; | 277 | uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; |
278 | nand_info_t *nand = &nand_info[0]; | 278 | nand_info_t *nand = &nand_info[0]; |
279 | char *cmd = argv[1]; | 279 | char *cmd = argv[1]; |
280 | 280 | ||
281 | if (CONFIG_SYS_MAX_NAND_DEVICE == 0 || !nand->name) { | 281 | if (CONFIG_SYS_MAX_NAND_DEVICE == 0 || !nand->name) { |
282 | puts("no devices available\n"); | 282 | puts("no devices available\n"); |
283 | return 1; | 283 | return 1; |
284 | } | 284 | } |
285 | 285 | ||
286 | set_dev(0); | 286 | set_dev(0); |
287 | 287 | ||
288 | if (!strcmp(cmd, "get")) { | 288 | if (!strcmp(cmd, "get")) { |
289 | ret = get_nand_env_oob(nand, &nand_env_oob_offset); | 289 | ret = get_nand_env_oob(nand, &nand_env_oob_offset); |
290 | if (ret) | 290 | if (ret) |
291 | return 1; | 291 | return 1; |
292 | 292 | ||
293 | printf("0x%08lx\n", nand_env_oob_offset); | 293 | printf("0x%08lx\n", nand_env_oob_offset); |
294 | } else if (!strcmp(cmd, "set")) { | 294 | } else if (!strcmp(cmd, "set")) { |
295 | loff_t addr; | 295 | loff_t addr; |
296 | loff_t maxsize; | 296 | loff_t maxsize; |
297 | struct mtd_oob_ops ops; | 297 | struct mtd_oob_ops ops; |
298 | int idx = 0; | 298 | int idx = 0; |
299 | 299 | ||
300 | if (argc < 3) | 300 | if (argc < 3) |
301 | goto usage; | 301 | goto usage; |
302 | 302 | ||
303 | if (arg_off(argv[2], &idx, &addr, &maxsize)) { | 303 | if (arg_off(argv[2], &idx, &addr, &maxsize)) { |
304 | puts("Offset or partition name expected\n"); | 304 | puts("Offset or partition name expected\n"); |
305 | return 1; | 305 | return 1; |
306 | } | 306 | } |
307 | 307 | ||
308 | if (idx != 0) { | 308 | if (idx != 0) { |
309 | puts("Partition not on first NAND device\n"); | 309 | puts("Partition not on first NAND device\n"); |
310 | return 1; | 310 | return 1; |
311 | } | 311 | } |
312 | 312 | ||
313 | if (nand->oobavail < ENV_OFFSET_SIZE) { | 313 | if (nand->oobavail < ENV_OFFSET_SIZE) { |
314 | printf("Insufficient available OOB bytes:\n" | 314 | printf("Insufficient available OOB bytes:\n" |
315 | "%d OOB bytes available but %d required for " | 315 | "%d OOB bytes available but %d required for " |
316 | "env.oob support\n", | 316 | "env.oob support\n", |
317 | nand->oobavail, ENV_OFFSET_SIZE); | 317 | nand->oobavail, ENV_OFFSET_SIZE); |
318 | return 1; | 318 | return 1; |
319 | } | 319 | } |
320 | 320 | ||
321 | if ((addr & (nand->erasesize - 1)) != 0) { | 321 | if ((addr & (nand->erasesize - 1)) != 0) { |
322 | printf("Environment offset must be block-aligned\n"); | 322 | printf("Environment offset must be block-aligned\n"); |
323 | return 1; | 323 | return 1; |
324 | } | 324 | } |
325 | 325 | ||
326 | ops.datbuf = NULL; | 326 | ops.datbuf = NULL; |
327 | ops.mode = MTD_OOB_AUTO; | 327 | ops.mode = MTD_OOB_AUTO; |
328 | ops.ooboffs = 0; | 328 | ops.ooboffs = 0; |
329 | ops.ooblen = ENV_OFFSET_SIZE; | 329 | ops.ooblen = ENV_OFFSET_SIZE; |
330 | ops.oobbuf = (void *) oob_buf; | 330 | ops.oobbuf = (void *) oob_buf; |
331 | 331 | ||
332 | oob_buf[0] = ENV_OOB_MARKER; | 332 | oob_buf[0] = ENV_OOB_MARKER; |
333 | oob_buf[1] = addr / nand->erasesize; | 333 | oob_buf[1] = addr / nand->erasesize; |
334 | 334 | ||
335 | ret = nand->write_oob(nand, ENV_OFFSET_SIZE, &ops); | 335 | ret = nand->write_oob(nand, ENV_OFFSET_SIZE, &ops); |
336 | if (ret) { | 336 | if (ret) { |
337 | printf("Error writing OOB block 0\n"); | 337 | printf("Error writing OOB block 0\n"); |
338 | return ret; | 338 | return ret; |
339 | } | 339 | } |
340 | 340 | ||
341 | ret = get_nand_env_oob(nand, &nand_env_oob_offset); | 341 | ret = get_nand_env_oob(nand, &nand_env_oob_offset); |
342 | if (ret) { | 342 | if (ret) { |
343 | printf("Error reading env offset in OOB\n"); | 343 | printf("Error reading env offset in OOB\n"); |
344 | return ret; | 344 | return ret; |
345 | } | 345 | } |
346 | 346 | ||
347 | if (addr != nand_env_oob_offset) { | 347 | if (addr != nand_env_oob_offset) { |
348 | printf("Verification of env offset in OOB failed: " | 348 | printf("Verification of env offset in OOB failed: " |
349 | "0x%08llx expected but got 0x%08lx\n", | 349 | "0x%08llx expected but got 0x%08lx\n", |
350 | (unsigned long long)addr, nand_env_oob_offset); | 350 | (unsigned long long)addr, nand_env_oob_offset); |
351 | return 1; | 351 | return 1; |
352 | } | 352 | } |
353 | } else { | 353 | } else { |
354 | goto usage; | 354 | goto usage; |
355 | } | 355 | } |
356 | 356 | ||
357 | return ret; | 357 | return ret; |
358 | 358 | ||
359 | usage: | 359 | usage: |
360 | return cmd_usage(cmdtp); | 360 | return cmd_usage(cmdtp); |
361 | } | 361 | } |
362 | 362 | ||
363 | #endif | 363 | #endif |
364 | 364 | ||
365 | static void nand_print_info(int idx) | 365 | static void nand_print_info(int idx) |
366 | { | 366 | { |
367 | nand_info_t *nand = &nand_info[idx]; | 367 | nand_info_t *nand = &nand_info[idx]; |
368 | struct nand_chip *chip = nand->priv; | 368 | struct nand_chip *chip = nand->priv; |
369 | printf("Device %d: ", idx); | 369 | printf("Device %d: ", idx); |
370 | if (chip->numchips > 1) | 370 | if (chip->numchips > 1) |
371 | printf("%dx ", chip->numchips); | 371 | printf("%dx ", chip->numchips); |
372 | printf("%s, sector size %u KiB\n", | 372 | printf("%s, sector size %u KiB\n", |
373 | nand->name, nand->erasesize >> 10); | 373 | nand->name, nand->erasesize >> 10); |
374 | } | 374 | } |
375 | 375 | ||
376 | int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) | 376 | int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) |
377 | { | 377 | { |
378 | int i, ret = 0; | 378 | int i, ret = 0; |
379 | ulong addr; | 379 | ulong addr; |
380 | loff_t off, size; | 380 | loff_t off, size; |
381 | char *cmd, *s; | 381 | char *cmd, *s; |
382 | nand_info_t *nand; | 382 | nand_info_t *nand; |
383 | #ifdef CONFIG_SYS_NAND_QUIET | 383 | #ifdef CONFIG_SYS_NAND_QUIET |
384 | int quiet = CONFIG_SYS_NAND_QUIET; | 384 | int quiet = CONFIG_SYS_NAND_QUIET; |
385 | #else | 385 | #else |
386 | int quiet = 0; | 386 | int quiet = 0; |
387 | #endif | 387 | #endif |
388 | const char *quiet_str = getenv("quiet"); | 388 | const char *quiet_str = getenv("quiet"); |
389 | int dev = nand_curr_device; | 389 | int dev = nand_curr_device; |
390 | int repeat = flag & CMD_FLAG_REPEAT; | 390 | int repeat = flag & CMD_FLAG_REPEAT; |
391 | 391 | ||
392 | /* at least two arguments please */ | 392 | /* at least two arguments please */ |
393 | if (argc < 2) | 393 | if (argc < 2) |
394 | goto usage; | 394 | goto usage; |
395 | 395 | ||
396 | if (quiet_str) | 396 | if (quiet_str) |
397 | quiet = simple_strtoul(quiet_str, NULL, 0) != 0; | 397 | quiet = simple_strtoul(quiet_str, NULL, 0) != 0; |
398 | 398 | ||
399 | cmd = argv[1]; | 399 | cmd = argv[1]; |
400 | 400 | ||
401 | /* Only "dump" is repeatable. */ | 401 | /* Only "dump" is repeatable. */ |
402 | if (repeat && strcmp(cmd, "dump")) | 402 | if (repeat && strcmp(cmd, "dump")) |
403 | return 0; | 403 | return 0; |
404 | 404 | ||
405 | if (strcmp(cmd, "info") == 0) { | 405 | if (strcmp(cmd, "info") == 0) { |
406 | 406 | ||
407 | putc('\n'); | 407 | putc('\n'); |
408 | for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { | 408 | for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { |
409 | if (nand_info[i].name) | 409 | if (nand_info[i].name) |
410 | nand_print_info(i); | 410 | nand_print_info(i); |
411 | } | 411 | } |
412 | return 0; | 412 | return 0; |
413 | } | 413 | } |
414 | 414 | ||
415 | if (strcmp(cmd, "device") == 0) { | 415 | if (strcmp(cmd, "device") == 0) { |
416 | if (argc < 3) { | 416 | if (argc < 3) { |
417 | putc('\n'); | 417 | putc('\n'); |
418 | if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE) | 418 | if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE) |
419 | puts("no devices available\n"); | 419 | puts("no devices available\n"); |
420 | else | 420 | else |
421 | nand_print_info(dev); | 421 | nand_print_info(dev); |
422 | return 0; | 422 | return 0; |
423 | } | 423 | } |
424 | 424 | ||
425 | dev = (int)simple_strtoul(argv[2], NULL, 10); | 425 | dev = (int)simple_strtoul(argv[2], NULL, 10); |
426 | set_dev(dev); | 426 | set_dev(dev); |
427 | 427 | ||
428 | return 0; | 428 | return 0; |
429 | } | 429 | } |
430 | 430 | ||
431 | #ifdef CONFIG_ENV_OFFSET_OOB | 431 | #ifdef CONFIG_ENV_OFFSET_OOB |
432 | /* this command operates only on the first nand device */ | 432 | /* this command operates only on the first nand device */ |
433 | if (strcmp(cmd, "env.oob") == 0) | 433 | if (strcmp(cmd, "env.oob") == 0) |
434 | return do_nand_env_oob(cmdtp, argc - 1, argv + 1); | 434 | return do_nand_env_oob(cmdtp, argc - 1, argv + 1); |
435 | #endif | 435 | #endif |
436 | 436 | ||
437 | /* The following commands operate on the current device, unless | 437 | /* The following commands operate on the current device, unless |
438 | * overridden by a partition specifier. Note that if somehow the | 438 | * overridden by a partition specifier. Note that if somehow the |
439 | * current device is invalid, it will have to be changed to a valid | 439 | * current device is invalid, it will have to be changed to a valid |
440 | * one before these commands can run, even if a partition specifier | 440 | * one before these commands can run, even if a partition specifier |
441 | * for another device is to be used. | 441 | * for another device is to be used. |
442 | */ | 442 | */ |
443 | if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || | 443 | if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || |
444 | !nand_info[dev].name) { | 444 | !nand_info[dev].name) { |
445 | puts("\nno devices available\n"); | 445 | puts("\nno devices available\n"); |
446 | return 1; | 446 | return 1; |
447 | } | 447 | } |
448 | nand = &nand_info[dev]; | 448 | nand = &nand_info[dev]; |
449 | 449 | ||
450 | if (strcmp(cmd, "bad") == 0) { | 450 | if (strcmp(cmd, "bad") == 0) { |
451 | printf("\nDevice %d bad blocks:\n", dev); | 451 | printf("\nDevice %d bad blocks:\n", dev); |
452 | for (off = 0; off < nand->size; off += nand->erasesize) | 452 | for (off = 0; off < nand->size; off += nand->erasesize) |
453 | if (nand_block_isbad(nand, off)) | 453 | if (nand_block_isbad(nand, off)) |
454 | printf(" %08llx\n", (unsigned long long)off); | 454 | printf(" %08llx\n", (unsigned long long)off); |
455 | return 0; | 455 | return 0; |
456 | } | 456 | } |
457 | 457 | ||
458 | /* | 458 | /* |
459 | * Syntax is: | 459 | * Syntax is: |
460 | * 0 1 2 3 4 | 460 | * 0 1 2 3 4 |
461 | * nand erase [clean] [off size] | 461 | * nand erase [clean] [off size] |
462 | */ | 462 | */ |
463 | if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) { | 463 | if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) { |
464 | nand_erase_options_t opts; | 464 | nand_erase_options_t opts; |
465 | /* "clean" at index 2 means request to write cleanmarker */ | 465 | /* "clean" at index 2 means request to write cleanmarker */ |
466 | int clean = argc > 2 && !strcmp("clean", argv[2]); | 466 | int clean = argc > 2 && !strcmp("clean", argv[2]); |
467 | int o = clean ? 3 : 2; | 467 | int o = clean ? 3 : 2; |
468 | int scrub = !strncmp(cmd, "scrub", 5); | 468 | int scrub = !strncmp(cmd, "scrub", 5); |
469 | int part = 0; | 469 | int part = 0; |
470 | int chip = 0; | 470 | int chip = 0; |
471 | int spread = 0; | 471 | int spread = 0; |
472 | int args = 2; | 472 | int args = 2; |
473 | 473 | ||
474 | if (cmd[5] != 0) { | 474 | if (cmd[5] != 0) { |
475 | if (!strcmp(&cmd[5], ".spread")) { | 475 | if (!strcmp(&cmd[5], ".spread")) { |
476 | spread = 1; | 476 | spread = 1; |
477 | } else if (!strcmp(&cmd[5], ".part")) { | 477 | } else if (!strcmp(&cmd[5], ".part")) { |
478 | part = 1; | 478 | part = 1; |
479 | args = 1; | 479 | args = 1; |
480 | } else if (!strcmp(&cmd[5], ".chip")) { | 480 | } else if (!strcmp(&cmd[5], ".chip")) { |
481 | chip = 1; | 481 | chip = 1; |
482 | args = 0; | 482 | args = 0; |
483 | } else { | 483 | } else { |
484 | goto usage; | 484 | goto usage; |
485 | } | 485 | } |
486 | } | 486 | } |
487 | 487 | ||
488 | /* | 488 | /* |
489 | * Don't allow missing arguments to cause full chip/partition | 489 | * Don't allow missing arguments to cause full chip/partition |
490 | * erases -- easy to do accidentally, e.g. with a misspelled | 490 | * erases -- easy to do accidentally, e.g. with a misspelled |
491 | * variable name. | 491 | * variable name. |
492 | */ | 492 | */ |
493 | if (argc != o + args) | 493 | if (argc != o + args) |
494 | goto usage; | 494 | goto usage; |
495 | 495 | ||
496 | printf("\nNAND %s: ", cmd); | 496 | printf("\nNAND %s: ", cmd); |
497 | /* skip first two or three arguments, look for offset and size */ | 497 | /* skip first two or three arguments, look for offset and size */ |
498 | if (arg_off_size(argc - o, argv + o, &dev, &off, &size) != 0) | 498 | if (arg_off_size(argc - o, argv + o, &dev, &off, &size) != 0) |
499 | return 1; | 499 | return 1; |
500 | 500 | ||
501 | nand = &nand_info[dev]; | 501 | nand = &nand_info[dev]; |
502 | 502 | ||
503 | memset(&opts, 0, sizeof(opts)); | 503 | memset(&opts, 0, sizeof(opts)); |
504 | opts.offset = off; | 504 | opts.offset = off; |
505 | opts.length = size; | 505 | opts.length = size; |
506 | opts.jffs2 = clean; | 506 | opts.jffs2 = clean; |
507 | opts.quiet = quiet; | 507 | opts.quiet = quiet; |
508 | opts.spread = spread; | 508 | opts.spread = spread; |
509 | 509 | ||
510 | if (scrub) { | 510 | if (scrub) { |
511 | puts("Warning: " | 511 | puts("Warning: " |
512 | "scrub option will erase all factory set " | 512 | "scrub option will erase all factory set " |
513 | "bad blocks!\n" | 513 | "bad blocks!\n" |
514 | " " | 514 | " " |
515 | "There is no reliable way to recover them.\n" | 515 | "There is no reliable way to recover them.\n" |
516 | " " | 516 | " " |
517 | "Use this command only for testing purposes " | 517 | "Use this command only for testing purposes " |
518 | "if you\n" | 518 | "if you\n" |
519 | " " | 519 | " " |
520 | "are sure of what you are doing!\n" | 520 | "are sure of what you are doing!\n" |
521 | "\nReally scrub this NAND flash? <y/N>\n"); | 521 | "\nReally scrub this NAND flash? <y/N>\n"); |
522 | 522 | ||
523 | if (getc() == 'y') { | 523 | if (getc() == 'y') { |
524 | puts("y"); | 524 | puts("y"); |
525 | if (getc() == '\r') | 525 | if (getc() == '\r') |
526 | opts.scrub = 1; | 526 | opts.scrub = 1; |
527 | else { | 527 | else { |
528 | puts("scrub aborted\n"); | 528 | puts("scrub aborted\n"); |
529 | return -1; | 529 | return -1; |
530 | } | 530 | } |
531 | } else { | 531 | } else { |
532 | puts("scrub aborted\n"); | 532 | puts("scrub aborted\n"); |
533 | return -1; | 533 | return -1; |
534 | } | 534 | } |
535 | } | 535 | } |
536 | ret = nand_erase_opts(nand, &opts); | 536 | ret = nand_erase_opts(nand, &opts); |
537 | printf("%s\n", ret ? "ERROR" : "OK"); | 537 | printf("%s\n", ret ? "ERROR" : "OK"); |
538 | 538 | ||
539 | return ret == 0 ? 0 : 1; | 539 | return ret == 0 ? 0 : 1; |
540 | } | 540 | } |
541 | 541 | ||
542 | if (strncmp(cmd, "dump", 4) == 0) { | 542 | if (strncmp(cmd, "dump", 4) == 0) { |
543 | if (argc < 3) | 543 | if (argc < 3) |
544 | goto usage; | 544 | goto usage; |
545 | 545 | ||
546 | off = (int)simple_strtoul(argv[2], NULL, 16); | 546 | off = (int)simple_strtoul(argv[2], NULL, 16); |
547 | ret = nand_dump(nand, off, !strcmp(&cmd[4], ".oob"), repeat); | 547 | ret = nand_dump(nand, off, !strcmp(&cmd[4], ".oob"), repeat); |
548 | 548 | ||
549 | return ret == 0 ? 1 : 0; | 549 | return ret == 0 ? 1 : 0; |
550 | } | 550 | } |
551 | 551 | ||
552 | if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { | 552 | if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { |
553 | size_t rwsize; | 553 | size_t rwsize; |
554 | int read; | 554 | int read; |
555 | 555 | ||
556 | if (argc < 4) | 556 | if (argc < 4) |
557 | goto usage; | 557 | goto usage; |
558 | 558 | ||
559 | addr = (ulong)simple_strtoul(argv[2], NULL, 16); | 559 | addr = (ulong)simple_strtoul(argv[2], NULL, 16); |
560 | 560 | ||
561 | read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ | 561 | read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ |
562 | printf("\nNAND %s: ", read ? "read" : "write"); | 562 | printf("\nNAND %s: ", read ? "read" : "write"); |
563 | if (arg_off_size(argc - 3, argv + 3, &dev, &off, &size) != 0) | 563 | if (arg_off_size(argc - 3, argv + 3, &dev, &off, &size) != 0) |
564 | return 1; | 564 | return 1; |
565 | 565 | ||
566 | nand = &nand_info[dev]; | 566 | nand = &nand_info[dev]; |
567 | rwsize = size; | 567 | rwsize = size; |
568 | 568 | ||
569 | s = strchr(cmd, '.'); | 569 | s = strchr(cmd, '.'); |
570 | if (!s || !strcmp(s, ".jffs2") || | 570 | if (!s || !strcmp(s, ".jffs2") || |
571 | !strcmp(s, ".e") || !strcmp(s, ".i")) { | 571 | !strcmp(s, ".e") || !strcmp(s, ".i")) { |
572 | if (read) | 572 | if (read) |
573 | ret = nand_read_skip_bad(nand, off, &rwsize, | 573 | ret = nand_read_skip_bad(nand, off, &rwsize, |
574 | (u_char *)addr); | 574 | (u_char *)addr); |
575 | else | 575 | else |
576 | ret = nand_write_skip_bad(nand, off, &rwsize, | 576 | ret = nand_write_skip_bad(nand, off, &rwsize, |
577 | (u_char *)addr, 0); | 577 | (u_char *)addr, 0); |
578 | #ifdef CONFIG_CMD_NAND_TRIMFFS | ||
579 | } else if (!strcmp(s, ".trimffs")) { | ||
580 | if (read) { | ||
581 | printf("Unknown nand command suffix '%s'\n", s); | ||
582 | return 1; | ||
583 | } | ||
584 | ret = nand_write_skip_bad(nand, off, &rwsize, | ||
585 | (u_char *)addr, | ||
586 | WITH_DROP_FFS); | ||
587 | #endif | ||
578 | #ifdef CONFIG_CMD_NAND_YAFFS | 588 | #ifdef CONFIG_CMD_NAND_YAFFS |
579 | } else if (!strcmp(s, ".yaffs")) { | 589 | } else if (!strcmp(s, ".yaffs")) { |
580 | if (read) { | 590 | if (read) { |
581 | printf("Unknown nand command suffix '%s'.\n", s); | 591 | printf("Unknown nand command suffix '%s'.\n", s); |
582 | return 1; | 592 | return 1; |
583 | } | 593 | } |
584 | ret = nand_write_skip_bad(nand, off, &rwsize, | 594 | ret = nand_write_skip_bad(nand, off, &rwsize, |
585 | (u_char *)addr, WITH_YAFFS_OOB); | 595 | (u_char *)addr, WITH_YAFFS_OOB); |
586 | #endif | 596 | #endif |
587 | } else if (!strcmp(s, ".oob")) { | 597 | } else if (!strcmp(s, ".oob")) { |
588 | /* out-of-band data */ | 598 | /* out-of-band data */ |
589 | mtd_oob_ops_t ops = { | 599 | mtd_oob_ops_t ops = { |
590 | .oobbuf = (u8 *)addr, | 600 | .oobbuf = (u8 *)addr, |
591 | .ooblen = rwsize, | 601 | .ooblen = rwsize, |
592 | .mode = MTD_OOB_RAW | 602 | .mode = MTD_OOB_RAW |
593 | }; | 603 | }; |
594 | 604 | ||
595 | if (read) | 605 | if (read) |
596 | ret = nand->read_oob(nand, off, &ops); | 606 | ret = nand->read_oob(nand, off, &ops); |
597 | else | 607 | else |
598 | ret = nand->write_oob(nand, off, &ops); | 608 | ret = nand->write_oob(nand, off, &ops); |
599 | } else { | 609 | } else { |
600 | printf("Unknown nand command suffix '%s'.\n", s); | 610 | printf("Unknown nand command suffix '%s'.\n", s); |
601 | return 1; | 611 | return 1; |
602 | } | 612 | } |
603 | 613 | ||
604 | printf(" %zu bytes %s: %s\n", rwsize, | 614 | printf(" %zu bytes %s: %s\n", rwsize, |
605 | read ? "read" : "written", ret ? "ERROR" : "OK"); | 615 | read ? "read" : "written", ret ? "ERROR" : "OK"); |
606 | 616 | ||
607 | return ret == 0 ? 0 : 1; | 617 | return ret == 0 ? 0 : 1; |
608 | } | 618 | } |
609 | 619 | ||
610 | if (strcmp(cmd, "markbad") == 0) { | 620 | if (strcmp(cmd, "markbad") == 0) { |
611 | argc -= 2; | 621 | argc -= 2; |
612 | argv += 2; | 622 | argv += 2; |
613 | 623 | ||
614 | if (argc <= 0) | 624 | if (argc <= 0) |
615 | goto usage; | 625 | goto usage; |
616 | 626 | ||
617 | while (argc > 0) { | 627 | while (argc > 0) { |
618 | addr = simple_strtoul(*argv, NULL, 16); | 628 | addr = simple_strtoul(*argv, NULL, 16); |
619 | 629 | ||
620 | if (nand->block_markbad(nand, addr)) { | 630 | if (nand->block_markbad(nand, addr)) { |
621 | printf("block 0x%08lx NOT marked " | 631 | printf("block 0x%08lx NOT marked " |
622 | "as bad! ERROR %d\n", | 632 | "as bad! ERROR %d\n", |
623 | addr, ret); | 633 | addr, ret); |
624 | ret = 1; | 634 | ret = 1; |
625 | } else { | 635 | } else { |
626 | printf("block 0x%08lx successfully " | 636 | printf("block 0x%08lx successfully " |
627 | "marked as bad\n", | 637 | "marked as bad\n", |
628 | addr); | 638 | addr); |
629 | } | 639 | } |
630 | --argc; | 640 | --argc; |
631 | ++argv; | 641 | ++argv; |
632 | } | 642 | } |
633 | return ret; | 643 | return ret; |
634 | } | 644 | } |
635 | 645 | ||
636 | if (strcmp(cmd, "biterr") == 0) { | 646 | if (strcmp(cmd, "biterr") == 0) { |
637 | /* todo */ | 647 | /* todo */ |
638 | return 1; | 648 | return 1; |
639 | } | 649 | } |
640 | 650 | ||
641 | #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK | 651 | #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK |
642 | if (strcmp(cmd, "lock") == 0) { | 652 | if (strcmp(cmd, "lock") == 0) { |
643 | int tight = 0; | 653 | int tight = 0; |
644 | int status = 0; | 654 | int status = 0; |
645 | if (argc == 3) { | 655 | if (argc == 3) { |
646 | if (!strcmp("tight", argv[2])) | 656 | if (!strcmp("tight", argv[2])) |
647 | tight = 1; | 657 | tight = 1; |
648 | if (!strcmp("status", argv[2])) | 658 | if (!strcmp("status", argv[2])) |
649 | status = 1; | 659 | status = 1; |
650 | } | 660 | } |
651 | if (status) { | 661 | if (status) { |
652 | do_nand_status(nand); | 662 | do_nand_status(nand); |
653 | } else { | 663 | } else { |
654 | if (!nand_lock(nand, tight)) { | 664 | if (!nand_lock(nand, tight)) { |
655 | puts("NAND flash successfully locked\n"); | 665 | puts("NAND flash successfully locked\n"); |
656 | } else { | 666 | } else { |
657 | puts("Error locking NAND flash\n"); | 667 | puts("Error locking NAND flash\n"); |
658 | return 1; | 668 | return 1; |
659 | } | 669 | } |
660 | } | 670 | } |
661 | return 0; | 671 | return 0; |
662 | } | 672 | } |
663 | 673 | ||
664 | if (strcmp(cmd, "unlock") == 0) { | 674 | if (strcmp(cmd, "unlock") == 0) { |
665 | if (arg_off_size(argc - 2, argv + 2, &dev, &off, &size) < 0) | 675 | if (arg_off_size(argc - 2, argv + 2, &dev, &off, &size) < 0) |
666 | return 1; | 676 | return 1; |
667 | 677 | ||
668 | if (!nand_unlock(&nand_info[dev], off, size)) { | 678 | if (!nand_unlock(&nand_info[dev], off, size)) { |
669 | puts("NAND flash successfully unlocked\n"); | 679 | puts("NAND flash successfully unlocked\n"); |
670 | } else { | 680 | } else { |
671 | puts("Error unlocking NAND flash, " | 681 | puts("Error unlocking NAND flash, " |
672 | "write and erase will probably fail\n"); | 682 | "write and erase will probably fail\n"); |
673 | return 1; | 683 | return 1; |
674 | } | 684 | } |
675 | return 0; | 685 | return 0; |
676 | } | 686 | } |
677 | #endif | 687 | #endif |
678 | 688 | ||
679 | usage: | 689 | usage: |
680 | return cmd_usage(cmdtp); | 690 | return cmd_usage(cmdtp); |
681 | } | 691 | } |
682 | 692 | ||
683 | U_BOOT_CMD( | 693 | U_BOOT_CMD( |
684 | nand, CONFIG_SYS_MAXARGS, 1, do_nand, | 694 | nand, CONFIG_SYS_MAXARGS, 1, do_nand, |
685 | "NAND sub-system", | 695 | "NAND sub-system", |
686 | "info - show available NAND devices\n" | 696 | "info - show available NAND devices\n" |
687 | "nand device [dev] - show or set current device\n" | 697 | "nand device [dev] - show or set current device\n" |
688 | "nand read - addr off|partition size\n" | 698 | "nand read - addr off|partition size\n" |
689 | "nand write - addr off|partition size\n" | 699 | "nand write - addr off|partition size\n" |
690 | " read/write 'size' bytes starting at offset 'off'\n" | 700 | " read/write 'size' bytes starting at offset 'off'\n" |
691 | " to/from memory address 'addr', skipping bad blocks.\n" | 701 | " to/from memory address 'addr', skipping bad blocks.\n" |
702 | #ifdef CONFIG_CMD_NAND_TRIMFFS | ||
703 | "nand write.trimffs - addr off|partition size\n" | ||
704 | " write 'size' bytes starting at offset 'off' from memory address\n" | ||
705 | " 'addr', skipping bad blocks and dropping any pages at the end\n" | ||
706 | " of eraseblocks that contain only 0xFF\n" | ||
707 | #endif | ||
692 | #ifdef CONFIG_CMD_NAND_YAFFS | 708 | #ifdef CONFIG_CMD_NAND_YAFFS |
693 | "nand write.yaffs - addr off|partition size\n" | 709 | "nand write.yaffs - addr off|partition size\n" |
694 | " write 'size' bytes starting at offset 'off' with yaffs format\n" | 710 | " write 'size' bytes starting at offset 'off' with yaffs format\n" |
695 | " from memory address 'addr', skipping bad blocks.\n" | 711 | " from memory address 'addr', skipping bad blocks.\n" |
696 | #endif | 712 | #endif |
697 | "nand erase[.spread] [clean] off size - erase 'size' bytes " | 713 | "nand erase[.spread] [clean] off size - erase 'size' bytes " |
698 | "from offset 'off'\n" | 714 | "from offset 'off'\n" |
699 | " With '.spread', erase enough for given file size, otherwise,\n" | 715 | " With '.spread', erase enough for given file size, otherwise,\n" |
700 | " 'size' includes skipped bad blocks.\n" | 716 | " 'size' includes skipped bad blocks.\n" |
701 | "nand erase.part [clean] partition - erase entire mtd partition'\n" | 717 | "nand erase.part [clean] partition - erase entire mtd partition'\n" |
702 | "nand erase.chip [clean] - erase entire chip'\n" | 718 | "nand erase.chip [clean] - erase entire chip'\n" |
703 | "nand bad - show bad blocks\n" | 719 | "nand bad - show bad blocks\n" |
704 | "nand dump[.oob] off - dump page\n" | 720 | "nand dump[.oob] off - dump page\n" |
705 | "nand scrub off size | scrub.part partition | scrub.chip\n" | 721 | "nand scrub off size | scrub.part partition | scrub.chip\n" |
706 | " really clean NAND erasing bad blocks (UNSAFE)\n" | 722 | " really clean NAND erasing bad blocks (UNSAFE)\n" |
707 | "nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n" | 723 | "nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n" |
708 | "nand biterr off - make a bit error at offset (UNSAFE)" | 724 | "nand biterr off - make a bit error at offset (UNSAFE)" |
709 | #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK | 725 | #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK |
710 | "\n" | 726 | "\n" |
711 | "nand lock [tight] [status]\n" | 727 | "nand lock [tight] [status]\n" |
712 | " bring nand to lock state or display locked pages\n" | 728 | " bring nand to lock state or display locked pages\n" |
713 | "nand unlock [offset] [size] - unlock section" | 729 | "nand unlock [offset] [size] - unlock section" |
714 | #endif | 730 | #endif |
715 | #ifdef CONFIG_ENV_OFFSET_OOB | 731 | #ifdef CONFIG_ENV_OFFSET_OOB |
716 | "\n" | 732 | "\n" |
717 | "nand env.oob - environment offset in OOB of block 0 of" | 733 | "nand env.oob - environment offset in OOB of block 0 of" |
718 | " first device.\n" | 734 | " first device.\n" |
719 | "nand env.oob set off|partition - set enviromnent offset\n" | 735 | "nand env.oob set off|partition - set enviromnent offset\n" |
720 | "nand env.oob get - get environment offset" | 736 | "nand env.oob get - get environment offset" |
721 | #endif | 737 | #endif |
722 | ); | 738 | ); |
723 | 739 | ||
724 | static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, | 740 | static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, |
725 | ulong offset, ulong addr, char *cmd) | 741 | ulong offset, ulong addr, char *cmd) |
726 | { | 742 | { |
727 | int r; | 743 | int r; |
728 | char *ep, *s; | 744 | char *ep, *s; |
729 | size_t cnt; | 745 | size_t cnt; |
730 | image_header_t *hdr; | 746 | image_header_t *hdr; |
731 | #if defined(CONFIG_FIT) | 747 | #if defined(CONFIG_FIT) |
732 | const void *fit_hdr = NULL; | 748 | const void *fit_hdr = NULL; |
733 | #endif | 749 | #endif |
734 | 750 | ||
735 | s = strchr(cmd, '.'); | 751 | s = strchr(cmd, '.'); |
736 | if (s != NULL && | 752 | if (s != NULL && |
737 | (strcmp(s, ".jffs2") && strcmp(s, ".e") && strcmp(s, ".i"))) { | 753 | (strcmp(s, ".jffs2") && strcmp(s, ".e") && strcmp(s, ".i"))) { |
738 | printf("Unknown nand load suffix '%s'\n", s); | 754 | printf("Unknown nand load suffix '%s'\n", s); |
739 | show_boot_progress(-53); | 755 | show_boot_progress(-53); |
740 | return 1; | 756 | return 1; |
741 | } | 757 | } |
742 | 758 | ||
743 | printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset); | 759 | printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset); |
744 | 760 | ||
745 | cnt = nand->writesize; | 761 | cnt = nand->writesize; |
746 | r = nand_read_skip_bad(nand, offset, &cnt, (u_char *) addr); | 762 | r = nand_read_skip_bad(nand, offset, &cnt, (u_char *) addr); |
747 | if (r) { | 763 | if (r) { |
748 | puts("** Read error\n"); | 764 | puts("** Read error\n"); |
749 | show_boot_progress (-56); | 765 | show_boot_progress (-56); |
750 | return 1; | 766 | return 1; |
751 | } | 767 | } |
752 | show_boot_progress (56); | 768 | show_boot_progress (56); |
753 | 769 | ||
754 | switch (genimg_get_format ((void *)addr)) { | 770 | switch (genimg_get_format ((void *)addr)) { |
755 | case IMAGE_FORMAT_LEGACY: | 771 | case IMAGE_FORMAT_LEGACY: |
756 | hdr = (image_header_t *)addr; | 772 | hdr = (image_header_t *)addr; |
757 | 773 | ||
758 | show_boot_progress (57); | 774 | show_boot_progress (57); |
759 | image_print_contents (hdr); | 775 | image_print_contents (hdr); |
760 | 776 | ||
761 | cnt = image_get_image_size (hdr); | 777 | cnt = image_get_image_size (hdr); |
762 | break; | 778 | break; |
763 | #if defined(CONFIG_FIT) | 779 | #if defined(CONFIG_FIT) |
764 | case IMAGE_FORMAT_FIT: | 780 | case IMAGE_FORMAT_FIT: |
765 | fit_hdr = (const void *)addr; | 781 | fit_hdr = (const void *)addr; |
766 | puts ("Fit image detected...\n"); | 782 | puts ("Fit image detected...\n"); |
767 | 783 | ||
768 | cnt = fit_get_size (fit_hdr); | 784 | cnt = fit_get_size (fit_hdr); |
769 | break; | 785 | break; |
770 | #endif | 786 | #endif |
771 | default: | 787 | default: |
772 | show_boot_progress (-57); | 788 | show_boot_progress (-57); |
773 | puts ("** Unknown image type\n"); | 789 | puts ("** Unknown image type\n"); |
774 | return 1; | 790 | return 1; |
775 | } | 791 | } |
776 | show_boot_progress (57); | 792 | show_boot_progress (57); |
777 | 793 | ||
778 | r = nand_read_skip_bad(nand, offset, &cnt, (u_char *) addr); | 794 | r = nand_read_skip_bad(nand, offset, &cnt, (u_char *) addr); |
779 | if (r) { | 795 | if (r) { |
780 | puts("** Read error\n"); | 796 | puts("** Read error\n"); |
781 | show_boot_progress (-58); | 797 | show_boot_progress (-58); |
782 | return 1; | 798 | return 1; |
783 | } | 799 | } |
784 | show_boot_progress (58); | 800 | show_boot_progress (58); |
785 | 801 | ||
786 | #if defined(CONFIG_FIT) | 802 | #if defined(CONFIG_FIT) |
787 | /* This cannot be done earlier, we need complete FIT image in RAM first */ | 803 | /* This cannot be done earlier, we need complete FIT image in RAM first */ |
788 | if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) { | 804 | if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) { |
789 | if (!fit_check_format (fit_hdr)) { | 805 | if (!fit_check_format (fit_hdr)) { |
790 | show_boot_progress (-150); | 806 | show_boot_progress (-150); |
791 | puts ("** Bad FIT image format\n"); | 807 | puts ("** Bad FIT image format\n"); |
792 | return 1; | 808 | return 1; |
793 | } | 809 | } |
794 | show_boot_progress (151); | 810 | show_boot_progress (151); |
795 | fit_print_contents (fit_hdr); | 811 | fit_print_contents (fit_hdr); |
796 | } | 812 | } |
797 | #endif | 813 | #endif |
798 | 814 | ||
799 | /* Loading ok, update default load address */ | 815 | /* Loading ok, update default load address */ |
800 | 816 | ||
801 | load_addr = addr; | 817 | load_addr = addr; |
802 | 818 | ||
803 | /* Check if we should attempt an auto-start */ | 819 | /* Check if we should attempt an auto-start */ |
804 | if (((ep = getenv("autostart")) != NULL) && (strcmp(ep, "yes") == 0)) { | 820 | if (((ep = getenv("autostart")) != NULL) && (strcmp(ep, "yes") == 0)) { |
805 | char *local_args[2]; | 821 | char *local_args[2]; |
806 | 822 | ||
807 | local_args[0] = cmd; | 823 | local_args[0] = cmd; |
808 | local_args[1] = NULL; | 824 | local_args[1] = NULL; |
809 | 825 | ||
810 | printf("Automatic boot of image at addr 0x%08lx ...\n", addr); | 826 | printf("Automatic boot of image at addr 0x%08lx ...\n", addr); |
811 | 827 | ||
812 | do_bootm(cmdtp, 0, 1, local_args); | 828 | do_bootm(cmdtp, 0, 1, local_args); |
813 | return 1; | 829 | return 1; |
814 | } | 830 | } |
815 | return 0; | 831 | return 0; |
816 | } | 832 | } |
817 | 833 | ||
818 | int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) | 834 | int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) |
819 | { | 835 | { |
820 | char *boot_device = NULL; | 836 | char *boot_device = NULL; |
821 | int idx; | 837 | int idx; |
822 | ulong addr, offset = 0; | 838 | ulong addr, offset = 0; |
823 | #if defined(CONFIG_CMD_MTDPARTS) | 839 | #if defined(CONFIG_CMD_MTDPARTS) |
824 | struct mtd_device *dev; | 840 | struct mtd_device *dev; |
825 | struct part_info *part; | 841 | struct part_info *part; |
826 | u8 pnum; | 842 | u8 pnum; |
827 | 843 | ||
828 | if (argc >= 2) { | 844 | if (argc >= 2) { |
829 | char *p = (argc == 2) ? argv[1] : argv[2]; | 845 | char *p = (argc == 2) ? argv[1] : argv[2]; |
830 | if (!(str2long(p, &addr)) && (mtdparts_init() == 0) && | 846 | if (!(str2long(p, &addr)) && (mtdparts_init() == 0) && |
831 | (find_dev_and_part(p, &dev, &pnum, &part) == 0)) { | 847 | (find_dev_and_part(p, &dev, &pnum, &part) == 0)) { |
832 | if (dev->id->type != MTD_DEV_TYPE_NAND) { | 848 | if (dev->id->type != MTD_DEV_TYPE_NAND) { |
833 | puts("Not a NAND device\n"); | 849 | puts("Not a NAND device\n"); |
834 | return 1; | 850 | return 1; |
835 | } | 851 | } |
836 | if (argc > 3) | 852 | if (argc > 3) |
837 | goto usage; | 853 | goto usage; |
838 | if (argc == 3) | 854 | if (argc == 3) |
839 | addr = simple_strtoul(argv[1], NULL, 16); | 855 | addr = simple_strtoul(argv[1], NULL, 16); |
840 | else | 856 | else |
841 | addr = CONFIG_SYS_LOAD_ADDR; | 857 | addr = CONFIG_SYS_LOAD_ADDR; |
842 | return nand_load_image(cmdtp, &nand_info[dev->id->num], | 858 | return nand_load_image(cmdtp, &nand_info[dev->id->num], |
843 | part->offset, addr, argv[0]); | 859 | part->offset, addr, argv[0]); |
844 | } | 860 | } |
845 | } | 861 | } |
846 | #endif | 862 | #endif |
847 | 863 | ||
848 | show_boot_progress(52); | 864 | show_boot_progress(52); |
849 | switch (argc) { | 865 | switch (argc) { |
850 | case 1: | 866 | case 1: |
851 | addr = CONFIG_SYS_LOAD_ADDR; | 867 | addr = CONFIG_SYS_LOAD_ADDR; |
852 | boot_device = getenv("bootdevice"); | 868 | boot_device = getenv("bootdevice"); |
853 | break; | 869 | break; |
854 | case 2: | 870 | case 2: |
855 | addr = simple_strtoul(argv[1], NULL, 16); | 871 | addr = simple_strtoul(argv[1], NULL, 16); |
856 | boot_device = getenv("bootdevice"); | 872 | boot_device = getenv("bootdevice"); |
857 | break; | 873 | break; |
858 | case 3: | 874 | case 3: |
859 | addr = simple_strtoul(argv[1], NULL, 16); | 875 | addr = simple_strtoul(argv[1], NULL, 16); |
860 | boot_device = argv[2]; | 876 | boot_device = argv[2]; |
861 | break; | 877 | break; |
862 | case 4: | 878 | case 4: |
863 | addr = simple_strtoul(argv[1], NULL, 16); | 879 | addr = simple_strtoul(argv[1], NULL, 16); |
864 | boot_device = argv[2]; | 880 | boot_device = argv[2]; |
865 | offset = simple_strtoul(argv[3], NULL, 16); | 881 | offset = simple_strtoul(argv[3], NULL, 16); |
866 | break; | 882 | break; |
867 | default: | 883 | default: |
868 | #if defined(CONFIG_CMD_MTDPARTS) | 884 | #if defined(CONFIG_CMD_MTDPARTS) |
869 | usage: | 885 | usage: |
870 | #endif | 886 | #endif |
871 | show_boot_progress(-53); | 887 | show_boot_progress(-53); |
872 | return cmd_usage(cmdtp); | 888 | return cmd_usage(cmdtp); |
873 | } | 889 | } |
874 | 890 | ||
875 | show_boot_progress(53); | 891 | show_boot_progress(53); |
876 | if (!boot_device) { | 892 | if (!boot_device) { |
877 | puts("\n** No boot device **\n"); | 893 | puts("\n** No boot device **\n"); |
878 | show_boot_progress(-54); | 894 | show_boot_progress(-54); |
879 | return 1; | 895 | return 1; |
880 | } | 896 | } |
881 | show_boot_progress(54); | 897 | show_boot_progress(54); |
882 | 898 | ||
883 | idx = simple_strtoul(boot_device, NULL, 16); | 899 | idx = simple_strtoul(boot_device, NULL, 16); |
884 | 900 | ||
885 | if (idx < 0 || idx >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[idx].name) { | 901 | if (idx < 0 || idx >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[idx].name) { |
886 | printf("\n** Device %d not available\n", idx); | 902 | printf("\n** Device %d not available\n", idx); |
887 | show_boot_progress(-55); | 903 | show_boot_progress(-55); |
888 | return 1; | 904 | return 1; |
889 | } | 905 | } |
890 | show_boot_progress(55); | 906 | show_boot_progress(55); |
891 | 907 | ||
892 | return nand_load_image(cmdtp, &nand_info[idx], offset, addr, argv[0]); | 908 | return nand_load_image(cmdtp, &nand_info[idx], offset, addr, argv[0]); |
893 | } | 909 | } |
894 | 910 | ||
895 | U_BOOT_CMD(nboot, 4, 1, do_nandboot, | 911 | U_BOOT_CMD(nboot, 4, 1, do_nandboot, |
896 | "boot from NAND device", | 912 | "boot from NAND device", |
897 | "[partition] | [[[loadAddr] dev] offset]" | 913 | "[partition] | [[[loadAddr] dev] offset]" |
898 | ); | 914 | ); |
899 | 915 |
doc/README.nand
1 | NAND FLASH commands and notes | 1 | NAND FLASH commands and notes |
2 | 2 | ||
3 | See NOTE below!!! | 3 | See NOTE below!!! |
4 | 4 | ||
5 | # (C) Copyright 2003 | 5 | # (C) Copyright 2003 |
6 | # Dave Ellis, SIXNET, dge@sixnetio.com | 6 | # Dave Ellis, SIXNET, dge@sixnetio.com |
7 | # | 7 | # |
8 | # See file CREDITS for list of people who contributed to this | 8 | # See file CREDITS for list of people who contributed to this |
9 | # project. | 9 | # project. |
10 | # | 10 | # |
11 | # This program is free software; you can redistribute it and/or | 11 | # This program is free software; you can redistribute it and/or |
12 | # modify it under the terms of the GNU General Public License as | 12 | # modify it under the terms of the GNU General Public License as |
13 | # published by the Free Software Foundation; either version 2 of | 13 | # published by the Free Software Foundation; either version 2 of |
14 | # the License, or (at your option) any later version. | 14 | # the License, or (at your option) any later version. |
15 | # | 15 | # |
16 | # This program is distributed in the hope that it will be useful, | 16 | # This program is distributed in the hope that it will be useful, |
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | # GNU General Public License for more details. | 19 | # GNU General Public License for more details. |
20 | # | 20 | # |
21 | # You should have received a copy of the GNU General Public License | 21 | # You should have received a copy of the GNU General Public License |
22 | # along with this program; if not, write to the Free Software | 22 | # along with this program; if not, write to the Free Software |
23 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 23 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
24 | # MA 02111-1307 USA | 24 | # MA 02111-1307 USA |
25 | 25 | ||
26 | Commands: | 26 | Commands: |
27 | 27 | ||
28 | nand bad | 28 | nand bad |
29 | Print a list of all of the bad blocks in the current device. | 29 | Print a list of all of the bad blocks in the current device. |
30 | 30 | ||
31 | nand device | 31 | nand device |
32 | Print information about the current NAND device. | 32 | Print information about the current NAND device. |
33 | 33 | ||
34 | nand device num | 34 | nand device num |
35 | Make device `num' the current device and print information about it. | 35 | Make device `num' the current device and print information about it. |
36 | 36 | ||
37 | nand erase off|partition size | 37 | nand erase off|partition size |
38 | nand erase clean [off|partition size] | 38 | nand erase clean [off|partition size] |
39 | Erase `size' bytes starting at offset `off'. Alternatively partition | 39 | Erase `size' bytes starting at offset `off'. Alternatively partition |
40 | name can be specified, in this case size will be eventually limited | 40 | name can be specified, in this case size will be eventually limited |
41 | to not exceed partition size (this behaviour applies also to read | 41 | to not exceed partition size (this behaviour applies also to read |
42 | and write commands). Only complete erase blocks can be erased. | 42 | and write commands). Only complete erase blocks can be erased. |
43 | 43 | ||
44 | If `erase' is specified without an offset or size, the entire flash | 44 | If `erase' is specified without an offset or size, the entire flash |
45 | is erased. If `erase' is specified with partition but without an | 45 | is erased. If `erase' is specified with partition but without an |
46 | size, the entire partition is erased. | 46 | size, the entire partition is erased. |
47 | 47 | ||
48 | If `clean' is specified, a JFFS2-style clean marker is written to | 48 | If `clean' is specified, a JFFS2-style clean marker is written to |
49 | each block after it is erased. | 49 | each block after it is erased. |
50 | 50 | ||
51 | This command will not erase blocks that are marked bad. There is | 51 | This command will not erase blocks that are marked bad. There is |
52 | a debug option in cmd_nand.c to allow bad blocks to be erased. | 52 | a debug option in cmd_nand.c to allow bad blocks to be erased. |
53 | Please read the warning there before using it, as blocks marked | 53 | Please read the warning there before using it, as blocks marked |
54 | bad by the manufacturer must _NEVER_ be erased. | 54 | bad by the manufacturer must _NEVER_ be erased. |
55 | 55 | ||
56 | nand info | 56 | nand info |
57 | Print information about all of the NAND devices found. | 57 | Print information about all of the NAND devices found. |
58 | 58 | ||
59 | nand read addr ofs|partition size | 59 | nand read addr ofs|partition size |
60 | Read `size' bytes from `ofs' in NAND flash to `addr'. Blocks that | 60 | Read `size' bytes from `ofs' in NAND flash to `addr'. Blocks that |
61 | are marked bad are skipped. If a page cannot be read because an | 61 | are marked bad are skipped. If a page cannot be read because an |
62 | uncorrectable data error is found, the command stops with an error. | 62 | uncorrectable data error is found, the command stops with an error. |
63 | 63 | ||
64 | nand read.oob addr ofs|partition size | 64 | nand read.oob addr ofs|partition size |
65 | Read `size' bytes from the out-of-band data area corresponding to | 65 | Read `size' bytes from the out-of-band data area corresponding to |
66 | `ofs' in NAND flash to `addr'. This is limited to the 16 bytes of | 66 | `ofs' in NAND flash to `addr'. This is limited to the 16 bytes of |
67 | data for one 512-byte page or 2 256-byte pages. There is no check | 67 | data for one 512-byte page or 2 256-byte pages. There is no check |
68 | for bad blocks or ECC errors. | 68 | for bad blocks or ECC errors. |
69 | 69 | ||
70 | nand write addr ofs|partition size | 70 | nand write addr ofs|partition size |
71 | Write `size' bytes from `addr' to `ofs' in NAND flash. Blocks that | 71 | Write `size' bytes from `addr' to `ofs' in NAND flash. Blocks that |
72 | are marked bad are skipped. If a page cannot be read because an | 72 | are marked bad are skipped. If a page cannot be read because an |
73 | uncorrectable data error is found, the command stops with an error. | 73 | uncorrectable data error is found, the command stops with an error. |
74 | 74 | ||
75 | As JFFS2 skips blocks similarly, this allows writing a JFFS2 image, | 75 | As JFFS2 skips blocks similarly, this allows writing a JFFS2 image, |
76 | as long as the image is short enough to fit even after skipping the | 76 | as long as the image is short enough to fit even after skipping the |
77 | bad blocks. Compact images, such as those produced by mkfs.jffs2 | 77 | bad blocks. Compact images, such as those produced by mkfs.jffs2 |
78 | should work well, but loading an image copied from another flash is | 78 | should work well, but loading an image copied from another flash is |
79 | going to be trouble if there are any bad blocks. | 79 | going to be trouble if there are any bad blocks. |
80 | 80 | ||
81 | nand write.trimffs addr ofs|partition size | ||
82 | Enabled by the CONFIG_CMD_NAND_TRIMFFS macro. This command will write to | ||
83 | the NAND flash in a manner identical to the 'nand write' command | ||
84 | described above -- with the additional check that all pages at the end | ||
85 | of eraseblocks which contain only 0xff data will not be written to the | ||
86 | NAND flash. This behaviour is required when flashing UBI images | ||
87 | containing UBIFS volumes as per the UBI FAQ[1]. | ||
88 | |||
89 | [1] http://www.linux-mtd.infradead.org/doc/ubi.html#L_flasher_algo | ||
90 | |||
81 | nand write.oob addr ofs|partition size | 91 | nand write.oob addr ofs|partition size |
82 | Write `size' bytes from `addr' to the out-of-band data area | 92 | Write `size' bytes from `addr' to the out-of-band data area |
83 | corresponding to `ofs' in NAND flash. This is limited to the 16 bytes | 93 | corresponding to `ofs' in NAND flash. This is limited to the 16 bytes |
84 | of data for one 512-byte page or 2 256-byte pages. There is no check | 94 | of data for one 512-byte page or 2 256-byte pages. There is no check |
85 | for bad blocks. | 95 | for bad blocks. |
86 | 96 | ||
87 | Configuration Options: | 97 | Configuration Options: |
88 | 98 | ||
89 | CONFIG_CMD_NAND | 99 | CONFIG_CMD_NAND |
90 | Enables NAND support and commmands. | 100 | Enables NAND support and commmands. |
91 | 101 | ||
92 | CONFIG_MTD_NAND_ECC_JFFS2 | 102 | CONFIG_MTD_NAND_ECC_JFFS2 |
93 | Define this if you want the Error Correction Code information in | 103 | Define this if you want the Error Correction Code information in |
94 | the out-of-band data to be formatted to match the JFFS2 file system. | 104 | the out-of-band data to be formatted to match the JFFS2 file system. |
95 | CONFIG_MTD_NAND_ECC_YAFFS would be another useful choice for | 105 | CONFIG_MTD_NAND_ECC_YAFFS would be another useful choice for |
96 | someone to implement. | 106 | someone to implement. |
97 | 107 | ||
98 | CONFIG_SYS_MAX_NAND_DEVICE | 108 | CONFIG_SYS_MAX_NAND_DEVICE |
99 | The maximum number of NAND devices you want to support. | 109 | The maximum number of NAND devices you want to support. |
100 | 110 | ||
101 | CONFIG_SYS_NAND_MAX_CHIPS | 111 | CONFIG_SYS_NAND_MAX_CHIPS |
102 | The maximum number of NAND chips per device to be supported. | 112 | The maximum number of NAND chips per device to be supported. |
103 | 113 | ||
104 | NOTE: | 114 | NOTE: |
105 | ===== | 115 | ===== |
106 | 116 | ||
107 | The current NAND implementation is based on what is in recent | 117 | The current NAND implementation is based on what is in recent |
108 | Linux kernels. The old legacy implementation has been removed. | 118 | Linux kernels. The old legacy implementation has been removed. |
109 | 119 | ||
110 | If you have board code which used CONFIG_NAND_LEGACY, you'll need | 120 | If you have board code which used CONFIG_NAND_LEGACY, you'll need |
111 | to convert to the current NAND interface for it to continue to work. | 121 | to convert to the current NAND interface for it to continue to work. |
112 | 122 | ||
113 | The Disk On Chip driver is currently broken and has been for some time. | 123 | The Disk On Chip driver is currently broken and has been for some time. |
114 | There is a driver in drivers/mtd/nand, taken from Linux, that works with | 124 | There is a driver in drivers/mtd/nand, taken from Linux, that works with |
115 | the current NAND system but has not yet been adapted to the u-boot | 125 | the current NAND system but has not yet been adapted to the u-boot |
116 | environment. | 126 | environment. |
117 | 127 | ||
118 | Additional improvements to the NAND subsystem by Guido Classen, 10-10-2006 | 128 | Additional improvements to the NAND subsystem by Guido Classen, 10-10-2006 |
119 | 129 | ||
120 | JFFS2 related commands: | 130 | JFFS2 related commands: |
121 | 131 | ||
122 | implement "nand erase clean" and old "nand erase" | 132 | implement "nand erase clean" and old "nand erase" |
123 | using both the new code which is able to skip bad blocks | 133 | using both the new code which is able to skip bad blocks |
124 | "nand erase clean" additionally writes JFFS2-cleanmarkers in the oob. | 134 | "nand erase clean" additionally writes JFFS2-cleanmarkers in the oob. |
125 | 135 | ||
126 | Miscellaneous and testing commands: | 136 | Miscellaneous and testing commands: |
127 | "markbad [offset]" | 137 | "markbad [offset]" |
128 | create an artificial bad block (for testing bad block handling) | 138 | create an artificial bad block (for testing bad block handling) |
129 | 139 | ||
130 | "scrub [offset length]" | 140 | "scrub [offset length]" |
131 | like "erase" but don't skip bad block. Instead erase them. | 141 | like "erase" but don't skip bad block. Instead erase them. |
132 | DANGEROUS!!! Factory set bad blocks will be lost. Use only | 142 | DANGEROUS!!! Factory set bad blocks will be lost. Use only |
133 | to remove artificial bad blocks created with the "markbad" command. | 143 | to remove artificial bad blocks created with the "markbad" command. |
134 | 144 | ||
135 | 145 | ||
136 | NAND locking command (for chips with active LOCKPRE pin) | 146 | NAND locking command (for chips with active LOCKPRE pin) |
137 | 147 | ||
138 | "nand lock" | 148 | "nand lock" |
139 | set NAND chip to lock state (all pages locked) | 149 | set NAND chip to lock state (all pages locked) |
140 | 150 | ||
141 | "nand lock tight" | 151 | "nand lock tight" |
142 | set NAND chip to lock tight state (software can't change locking anymore) | 152 | set NAND chip to lock tight state (software can't change locking anymore) |
143 | 153 | ||
144 | "nand lock status" | 154 | "nand lock status" |
145 | displays current locking status of all pages | 155 | displays current locking status of all pages |
146 | 156 | ||
147 | "nand unlock [offset] [size]" | 157 | "nand unlock [offset] [size]" |
148 | unlock consecutive area (can be called multiple times for different areas) | 158 | unlock consecutive area (can be called multiple times for different areas) |
149 | 159 | ||
150 | 160 | ||
151 | I have tested the code with board containing 128MiB NAND large page chips | 161 | I have tested the code with board containing 128MiB NAND large page chips |
152 | and 32MiB small page chips. | 162 | and 32MiB small page chips. |
153 | 163 |
-
mentioned in commit 562274