]>
Commit | Line | Data |
---|---|---|
2255b2d2 | 1 | /* |
7817cb20 | 2 | * drivers/mtd/nand/nand_util.c |
2255b2d2 SR |
3 | * |
4 | * Copyright (C) 2006 by Weiss-Electronic GmbH. | |
5 | * All rights reserved. | |
6 | * | |
7 | * @author: Guido Classen <clagix@gmail.com> | |
8 | * @descr: NAND Flash support | |
9 | * @references: borrowed heavily from Linux mtd-utils code: | |
10 | * flash_eraseall.c by Arcom Control System Ltd | |
11 | * nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com) | |
12 | * and Thomas Gleixner (tglx@linutronix.de) | |
13 | * | |
169d54d8 BG |
14 | * Copyright (C) 2008 Nokia Corporation: drop_ffs() function by |
15 | * Artem Bityutskiy <dedekind1@gmail.com> from mtd-utils | |
16 | * | |
1a459660 | 17 | * SPDX-License-Identifier: GPL-2.0+ |
2255b2d2 SR |
18 | */ |
19 | ||
20 | #include <common.h> | |
2255b2d2 SR |
21 | #include <command.h> |
22 | #include <watchdog.h> | |
23 | #include <malloc.h> | |
3a6d56c2 | 24 | #include <div64.h> |
2255b2d2 | 25 | |
cfa460ad WJ |
26 | #include <asm/errno.h> |
27 | #include <linux/mtd/mtd.h> | |
2255b2d2 SR |
28 | #include <nand.h> |
29 | #include <jffs2/jffs2.h> | |
30 | ||
bd74280d BT |
31 | typedef struct erase_info erase_info_t; |
32 | typedef struct mtd_info mtd_info_t; | |
2255b2d2 SR |
33 | |
34 | /* support only for native endian JFFS2 */ | |
35 | #define cpu_to_je16(x) (x) | |
36 | #define cpu_to_je32(x) (x) | |
37 | ||
2255b2d2 SR |
38 | /** |
39 | * nand_erase_opts: - erase NAND flash with support for various options | |
bd74280d | 40 | * (jffs2 formatting) |
2255b2d2 SR |
41 | * |
42 | * @param meminfo NAND device to erase | |
43 | * @param opts options, @see struct nand_erase_options | |
44 | * @return 0 in case of success | |
45 | * | |
46 | * This code is ported from flash_eraseall.c from Linux mtd utils by | |
47 | * Arcom Control System Ltd. | |
48 | */ | |
49 | int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) | |
50 | { | |
51 | struct jffs2_unknown_node cleanmarker; | |
2255b2d2 | 52 | erase_info_t erase; |
30486322 | 53 | unsigned long erase_length, erased_length; /* in blocks */ |
2255b2d2 SR |
54 | int result; |
55 | int percent_complete = -1; | |
2255b2d2 | 56 | const char *mtd_device = meminfo->name; |
cfa460ad WJ |
57 | struct mtd_oob_ops oob_opts; |
58 | struct nand_chip *chip = meminfo->priv; | |
2255b2d2 | 59 | |
8156f732 BT |
60 | if ((opts->offset & (meminfo->erasesize - 1)) != 0) { |
61 | printf("Attempt to erase non block-aligned data\n"); | |
30486322 SW |
62 | return -1; |
63 | } | |
64 | ||
2255b2d2 | 65 | memset(&erase, 0, sizeof(erase)); |
cfa460ad | 66 | memset(&oob_opts, 0, sizeof(oob_opts)); |
2255b2d2 SR |
67 | |
68 | erase.mtd = meminfo; | |
69 | erase.len = meminfo->erasesize; | |
856f0544 | 70 | erase.addr = opts->offset; |
30486322 SW |
71 | erase_length = lldiv(opts->length + meminfo->erasesize - 1, |
72 | meminfo->erasesize); | |
2255b2d2 | 73 | |
bd74280d BT |
74 | cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); |
75 | cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); | |
cfa460ad | 76 | cleanmarker.totlen = cpu_to_je32(8); |
2255b2d2 SR |
77 | |
78 | /* scrub option allows to erase badblock. To prevent internal | |
79 | * check from erase() method, set block check method to dummy | |
80 | * and disable bad block table while erasing. | |
81 | */ | |
82 | if (opts->scrub) { | |
6d41419f MV |
83 | erase.scrub = opts->scrub; |
84 | /* | |
85 | * We don't need the bad block table anymore... | |
2255b2d2 SR |
86 | * after scrub, there are no bad blocks left! |
87 | */ | |
6d41419f MV |
88 | if (chip->bbt) { |
89 | kfree(chip->bbt); | |
2255b2d2 | 90 | } |
6d41419f | 91 | chip->bbt = NULL; |
2255b2d2 SR |
92 | } |
93 | ||
30486322 SW |
94 | for (erased_length = 0; |
95 | erased_length < erase_length; | |
2255b2d2 | 96 | erase.addr += meminfo->erasesize) { |
4cbb651b | 97 | |
bd74280d | 98 | WATCHDOG_RESET(); |
2255b2d2 | 99 | |
a67cc37e HS |
100 | if (opts->lim && (erase.addr >= (opts->offset + opts->lim))) { |
101 | puts("Size of erase exceeds limit\n"); | |
102 | return -EFBIG; | |
103 | } | |
47b6dad3 | 104 | if (!opts->scrub) { |
dfe64e2c | 105 | int ret = mtd_block_isbad(meminfo, erase.addr); |
2255b2d2 SR |
106 | if (ret > 0) { |
107 | if (!opts->quiet) | |
108 | printf("\rSkipping bad block at " | |
8d2effea | 109 | "0x%08llx " |
87621bc2 WD |
110 | " \n", |
111 | erase.addr); | |
30486322 SW |
112 | |
113 | if (!opts->spread) | |
114 | erased_length++; | |
115 | ||
2255b2d2 SR |
116 | continue; |
117 | ||
118 | } else if (ret < 0) { | |
119 | printf("\n%s: MTD get bad block failed: %d\n", | |
120 | mtd_device, | |
121 | ret); | |
122 | return -1; | |
123 | } | |
124 | } | |
125 | ||
30486322 SW |
126 | erased_length++; |
127 | ||
dfe64e2c | 128 | result = mtd_erase(meminfo, &erase); |
2255b2d2 SR |
129 | if (result != 0) { |
130 | printf("\n%s: MTD Erase failure: %d\n", | |
131 | mtd_device, result); | |
132 | continue; | |
133 | } | |
134 | ||
135 | /* format for JFFS2 ? */ | |
bd78bc6b | 136 | if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) { |
dfe64e2c SL |
137 | struct mtd_oob_ops ops; |
138 | ops.ooblen = 8; | |
139 | ops.datbuf = NULL; | |
140 | ops.oobbuf = (uint8_t *)&cleanmarker; | |
141 | ops.ooboffs = 0; | |
142 | ops.mode = MTD_OPS_AUTO_OOB; | |
4cbb651b | 143 | |
dfe64e2c | 144 | result = mtd_write_oob(meminfo, |
93e14596 WD |
145 | erase.addr, |
146 | &ops); | |
cfa460ad WJ |
147 | if (result != 0) { |
148 | printf("\n%s: MTD writeoob failure: %d\n", | |
bd78bc6b | 149 | mtd_device, result); |
cfa460ad | 150 | continue; |
2255b2d2 SR |
151 | } |
152 | } | |
153 | ||
154 | if (!opts->quiet) { | |
30486322 | 155 | unsigned long long n = erased_length * 100ULL; |
5bd7fe9a MF |
156 | int percent; |
157 | ||
158 | do_div(n, erase_length); | |
159 | percent = (int)n; | |
2255b2d2 SR |
160 | |
161 | /* output progress message only at whole percent | |
162 | * steps to reduce the number of messages printed | |
163 | * on (slow) serial consoles | |
164 | */ | |
165 | if (percent != percent_complete) { | |
166 | percent_complete = percent; | |
167 | ||
8d2effea | 168 | printf("\rErasing at 0x%llx -- %3d%% complete.", |
bd78bc6b | 169 | erase.addr, percent); |
2255b2d2 SR |
170 | |
171 | if (opts->jffs2 && result == 0) | |
8d2effea | 172 | printf(" Cleanmarker written at 0x%llx.", |
bd78bc6b | 173 | erase.addr); |
2255b2d2 SR |
174 | } |
175 | } | |
176 | } | |
177 | if (!opts->quiet) | |
178 | printf("\n"); | |
179 | ||
6d41419f MV |
180 | if (opts->scrub) |
181 | chip->scan_bbt(meminfo); | |
2255b2d2 SR |
182 | |
183 | return 0; | |
184 | } | |
185 | ||
50657c27 NM |
186 | #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK |
187 | ||
2255b2d2 SR |
188 | /****************************************************************************** |
189 | * Support for locking / unlocking operations of some NAND devices | |
190 | *****************************************************************************/ | |
191 | ||
2255b2d2 SR |
192 | /** |
193 | * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT | |
194 | * state | |
195 | * | |
50657c27 | 196 | * @param mtd nand mtd instance |
2255b2d2 SR |
197 | * @param tight bring device in lock tight mode |
198 | * | |
199 | * @return 0 on success, -1 in case of error | |
200 | * | |
201 | * The lock / lock-tight command only applies to the whole chip. To get some | |
202 | * parts of the chip lock and others unlocked use the following sequence: | |
203 | * | |
204 | * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin) | |
205 | * - Call nand_unlock() once for each consecutive area to be unlocked | |
206 | * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1) | |
207 | * | |
208 | * If the device is in lock-tight state software can't change the | |
209 | * current active lock/unlock state of all pages. nand_lock() / nand_unlock() | |
210 | * calls will fail. It is only posible to leave lock-tight state by | |
211 | * an hardware signal (low pulse on _WP pin) or by power down. | |
212 | */ | |
50657c27 | 213 | int nand_lock(struct mtd_info *mtd, int tight) |
2255b2d2 SR |
214 | { |
215 | int ret = 0; | |
216 | int status; | |
50657c27 | 217 | struct nand_chip *chip = mtd->priv; |
2255b2d2 SR |
218 | |
219 | /* select the NAND device */ | |
50657c27 | 220 | chip->select_chip(mtd, 0); |
2255b2d2 | 221 | |
fcecb4a5 JH |
222 | /* check the Lock Tight Status */ |
223 | chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, 0); | |
224 | if (chip->read_byte(mtd) & NAND_LOCK_STATUS_TIGHT) { | |
225 | printf("nand_lock: Device is locked tight!\n"); | |
226 | ret = -1; | |
227 | goto out; | |
228 | } | |
229 | ||
50657c27 | 230 | chip->cmdfunc(mtd, |
2255b2d2 SR |
231 | (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK), |
232 | -1, -1); | |
233 | ||
234 | /* call wait ready function */ | |
50657c27 | 235 | status = chip->waitfunc(mtd, chip); |
2255b2d2 SR |
236 | |
237 | /* see if device thinks it succeeded */ | |
238 | if (status & 0x01) { | |
239 | ret = -1; | |
240 | } | |
241 | ||
fcecb4a5 | 242 | out: |
2255b2d2 | 243 | /* de-select the NAND device */ |
50657c27 | 244 | chip->select_chip(mtd, -1); |
2255b2d2 SR |
245 | return ret; |
246 | } | |
247 | ||
248 | /** | |
249 | * nand_get_lock_status: - query current lock state from one page of NAND | |
250 | * flash | |
251 | * | |
50657c27 | 252 | * @param mtd nand mtd instance |
bd74280d | 253 | * @param offset page address to query (must be page-aligned!) |
2255b2d2 SR |
254 | * |
255 | * @return -1 in case of error | |
256 | * >0 lock status: | |
257 | * bitfield with the following combinations: | |
258 | * NAND_LOCK_STATUS_TIGHT: page in tight state | |
2255b2d2 SR |
259 | * NAND_LOCK_STATUS_UNLOCK: page unlocked |
260 | * | |
261 | */ | |
378adfcd | 262 | int nand_get_lock_status(struct mtd_info *mtd, loff_t offset) |
2255b2d2 SR |
263 | { |
264 | int ret = 0; | |
265 | int chipnr; | |
266 | int page; | |
50657c27 | 267 | struct nand_chip *chip = mtd->priv; |
2255b2d2 SR |
268 | |
269 | /* select the NAND device */ | |
50657c27 NM |
270 | chipnr = (int)(offset >> chip->chip_shift); |
271 | chip->select_chip(mtd, chipnr); | |
2255b2d2 SR |
272 | |
273 | ||
50657c27 | 274 | if ((offset & (mtd->writesize - 1)) != 0) { |
bd74280d | 275 | printf("nand_get_lock_status: " |
2255b2d2 SR |
276 | "Start address must be beginning of " |
277 | "nand page!\n"); | |
278 | ret = -1; | |
279 | goto out; | |
280 | } | |
281 | ||
282 | /* check the Lock Status */ | |
50657c27 NM |
283 | page = (int)(offset >> chip->page_shift); |
284 | chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask); | |
2255b2d2 | 285 | |
50657c27 | 286 | ret = chip->read_byte(mtd) & (NAND_LOCK_STATUS_TIGHT |
2255b2d2 SR |
287 | | NAND_LOCK_STATUS_UNLOCK); |
288 | ||
289 | out: | |
290 | /* de-select the NAND device */ | |
50657c27 | 291 | chip->select_chip(mtd, -1); |
2255b2d2 SR |
292 | return ret; |
293 | } | |
294 | ||
295 | /** | |
296 | * nand_unlock: - Unlock area of NAND pages | |
297 | * only one consecutive area can be unlocked at one time! | |
298 | * | |
50657c27 | 299 | * @param mtd nand mtd instance |
2255b2d2 SR |
300 | * @param start start byte address |
301 | * @param length number of bytes to unlock (must be a multiple of | |
cfa460ad | 302 | * page size nand->writesize) |
eee623a5 | 303 | * @param allexcept if set, unlock everything not selected |
2255b2d2 SR |
304 | * |
305 | * @return 0 on success, -1 in case of error | |
306 | */ | |
e331ab2e JH |
307 | int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length, |
308 | int allexcept) | |
2255b2d2 SR |
309 | { |
310 | int ret = 0; | |
311 | int chipnr; | |
312 | int status; | |
313 | int page; | |
50657c27 | 314 | struct nand_chip *chip = mtd->priv; |
eee623a5 | 315 | |
e331ab2e | 316 | debug("nand_unlock%s: start: %08llx, length: %d!\n", |
eee623a5 | 317 | allexcept ? " (allexcept)" : "", start, length); |
2255b2d2 SR |
318 | |
319 | /* select the NAND device */ | |
50657c27 NM |
320 | chipnr = (int)(start >> chip->chip_shift); |
321 | chip->select_chip(mtd, chipnr); | |
2255b2d2 SR |
322 | |
323 | /* check the WP bit */ | |
50657c27 NM |
324 | chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); |
325 | if (!(chip->read_byte(mtd) & NAND_STATUS_WP)) { | |
bd74280d | 326 | printf("nand_unlock: Device is write protected!\n"); |
2255b2d2 SR |
327 | ret = -1; |
328 | goto out; | |
329 | } | |
330 | ||
fcecb4a5 JH |
331 | /* check the Lock Tight Status */ |
332 | page = (int)(start >> chip->page_shift); | |
333 | chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask); | |
334 | if (chip->read_byte(mtd) & NAND_LOCK_STATUS_TIGHT) { | |
335 | printf("nand_unlock: Device is locked tight!\n"); | |
336 | ret = -1; | |
337 | goto out; | |
338 | } | |
339 | ||
50657c27 | 340 | if ((start & (mtd->erasesize - 1)) != 0) { |
bd74280d | 341 | printf("nand_unlock: Start address must be beginning of " |
50657c27 | 342 | "nand block!\n"); |
2255b2d2 SR |
343 | ret = -1; |
344 | goto out; | |
345 | } | |
346 | ||
50657c27 | 347 | if (length == 0 || (length & (mtd->erasesize - 1)) != 0) { |
bd74280d | 348 | printf("nand_unlock: Length must be a multiple of nand block " |
50657c27 | 349 | "size %08x!\n", mtd->erasesize); |
2255b2d2 SR |
350 | ret = -1; |
351 | goto out; | |
352 | } | |
353 | ||
50657c27 NM |
354 | /* |
355 | * Set length so that the last address is set to the | |
356 | * starting address of the last block | |
357 | */ | |
358 | length -= mtd->erasesize; | |
359 | ||
2255b2d2 | 360 | /* submit address of first page to unlock */ |
50657c27 | 361 | chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask); |
2255b2d2 SR |
362 | |
363 | /* submit ADDRESS of LAST page to unlock */ | |
50657c27 | 364 | page += (int)(length >> chip->page_shift); |
eee623a5 JH |
365 | |
366 | /* | |
367 | * Page addresses for unlocking are supposed to be block-aligned. | |
368 | * At least some NAND chips use the low bit to indicate that the | |
369 | * page range should be inverted. | |
370 | */ | |
371 | if (allexcept) | |
372 | page |= 1; | |
373 | ||
50657c27 | 374 | chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, page & chip->pagemask); |
2255b2d2 SR |
375 | |
376 | /* call wait ready function */ | |
50657c27 | 377 | status = chip->waitfunc(mtd, chip); |
2255b2d2 SR |
378 | /* see if device thinks it succeeded */ |
379 | if (status & 0x01) { | |
380 | /* there was an error */ | |
381 | ret = -1; | |
382 | goto out; | |
383 | } | |
384 | ||
385 | out: | |
386 | /* de-select the NAND device */ | |
50657c27 | 387 | chip->select_chip(mtd, -1); |
2255b2d2 SR |
388 | return ret; |
389 | } | |
cfa460ad | 390 | #endif |
2255b2d2 | 391 | |
dfbf617f | 392 | /** |
f9a52541 | 393 | * check_skip_len |
dfbf617f | 394 | * |
f9a52541 SW |
395 | * Check if there are any bad blocks, and whether length including bad |
396 | * blocks fits into device | |
dfbf617f SW |
397 | * |
398 | * @param nand NAND device | |
399 | * @param offset offset in flash | |
400 | * @param length image length | |
c39d6a0e | 401 | * @param used length of flash needed for the requested length |
f9a52541 SW |
402 | * @return 0 if the image fits and there are no bad blocks |
403 | * 1 if the image fits, but there are bad blocks | |
404 | * -1 if the image does not fit | |
dfbf617f | 405 | */ |
c39d6a0e TR |
406 | static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length, |
407 | size_t *used) | |
dfbf617f | 408 | { |
dfbf617f | 409 | size_t len_excl_bad = 0; |
f9a52541 | 410 | int ret = 0; |
dfbf617f SW |
411 | |
412 | while (len_excl_bad < length) { | |
f9a52541 SW |
413 | size_t block_len, block_off; |
414 | loff_t block_start; | |
dfbf617f | 415 | |
f9a52541 SW |
416 | if (offset >= nand->size) |
417 | return -1; | |
dfbf617f | 418 | |
f9a52541 SW |
419 | block_start = offset & ~(loff_t)(nand->erasesize - 1); |
420 | block_off = offset & (nand->erasesize - 1); | |
421 | block_len = nand->erasesize - block_off; | |
dfbf617f | 422 | |
f9a52541 SW |
423 | if (!nand_block_isbad(nand, block_start)) |
424 | len_excl_bad += block_len; | |
425 | else | |
426 | ret = 1; | |
427 | ||
428 | offset += block_len; | |
c39d6a0e | 429 | *used += block_len; |
dfbf617f SW |
430 | } |
431 | ||
c39d6a0e TR |
432 | /* If the length is not a multiple of block_len, adjust. */ |
433 | if (len_excl_bad > length) | |
434 | *used -= (len_excl_bad - length); | |
435 | ||
f9a52541 | 436 | return ret; |
dfbf617f SW |
437 | } |
438 | ||
169d54d8 BG |
439 | #ifdef CONFIG_CMD_NAND_TRIMFFS |
440 | static size_t drop_ffs(const nand_info_t *nand, const u_char *buf, | |
441 | const size_t *len) | |
442 | { | |
453db368 | 443 | size_t l = *len; |
444 | ssize_t i; | |
169d54d8 BG |
445 | |
446 | for (i = l - 1; i >= 0; i--) | |
447 | if (buf[i] != 0xFF) | |
448 | break; | |
449 | ||
450 | /* The resulting length must be aligned to the minimum flash I/O size */ | |
451 | l = i + 1; | |
452 | l = (l + nand->writesize - 1) / nand->writesize; | |
453 | l *= nand->writesize; | |
454 | ||
455 | /* | |
456 | * since the input length may be unaligned, prevent access past the end | |
457 | * of the buffer | |
458 | */ | |
459 | return min(l, *len); | |
460 | } | |
461 | #endif | |
462 | ||
dfbf617f SW |
463 | /** |
464 | * nand_write_skip_bad: | |
465 | * | |
466 | * Write image to NAND flash. | |
467 | * Blocks that are marked bad are skipped and the is written to the next | |
468 | * block instead as long as the image is short enough to fit even after | |
c39d6a0e TR |
469 | * skipping the bad blocks. Due to bad blocks we may not be able to |
470 | * perform the requested write. In the case where the write would | |
471 | * extend beyond the end of the NAND device, both length and actual (if | |
472 | * not NULL) are set to 0. In the case where the write would extend | |
473 | * beyond the limit we are passed, length is set to 0 and actual is set | |
474 | * to the required length. | |
dfbf617f SW |
475 | * |
476 | * @param nand NAND device | |
477 | * @param offset offset in flash | |
478 | * @param length buffer length | |
c39d6a0e TR |
479 | * @param actual set to size required to write length worth of |
480 | * buffer or 0 on error, if not NULL | |
481 | * @param lim maximum size that actual may be in order to not | |
482 | * exceed the buffer | |
47fc18f1 | 483 | * @param buffer buffer to read from |
a6c9aa1f | 484 | * @param flags flags modifying the behaviour of the write to NAND |
dfbf617f SW |
485 | * @return 0 in case of success |
486 | */ | |
378adfcd | 487 | int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, |
c39d6a0e | 488 | size_t *actual, loff_t lim, u_char *buffer, int flags) |
dfbf617f | 489 | { |
47fc18f1 | 490 | int rval = 0, blocksize; |
dfbf617f | 491 | size_t left_to_write = *length; |
c39d6a0e | 492 | size_t used_for_write = 0; |
dfbf617f | 493 | u_char *p_buffer = buffer; |
f9a52541 | 494 | int need_skip; |
dfbf617f | 495 | |
c39d6a0e TR |
496 | if (actual) |
497 | *actual = 0; | |
498 | ||
47fc18f1 | 499 | #ifdef CONFIG_CMD_NAND_YAFFS |
a6c9aa1f | 500 | if (flags & WITH_YAFFS_OOB) { |
c135456f BG |
501 | if (flags & ~WITH_YAFFS_OOB) |
502 | return -EINVAL; | |
503 | ||
47fc18f1 LW |
504 | int pages; |
505 | pages = nand->erasesize / nand->writesize; | |
506 | blocksize = (pages * nand->oobsize) + nand->erasesize; | |
507 | if (*length % (nand->writesize + nand->oobsize)) { | |
bd74280d | 508 | printf("Attempt to write incomplete page" |
47fc18f1 LW |
509 | " in yaffs mode\n"); |
510 | return -EINVAL; | |
511 | } | |
512 | } else | |
513 | #endif | |
514 | { | |
515 | blocksize = nand->erasesize; | |
516 | } | |
517 | ||
f9a52541 SW |
518 | /* |
519 | * nand_write() handles unaligned, partial page writes. | |
520 | * | |
521 | * We allow length to be unaligned, for convenience in | |
522 | * using the $filesize variable. | |
523 | * | |
524 | * However, starting at an unaligned offset makes the | |
525 | * semantics of bad block skipping ambiguous (really, | |
526 | * you should only start a block skipping access at a | |
527 | * partition boundary). So don't try to handle that. | |
528 | */ | |
529 | if ((offset & (nand->writesize - 1)) != 0) { | |
bd74280d | 530 | printf("Attempt to write non page-aligned data\n"); |
f9a52541 | 531 | *length = 0; |
dfbf617f SW |
532 | return -EINVAL; |
533 | } | |
534 | ||
c39d6a0e TR |
535 | need_skip = check_skip_len(nand, offset, *length, &used_for_write); |
536 | ||
537 | if (actual) | |
538 | *actual = used_for_write; | |
539 | ||
f9a52541 | 540 | if (need_skip < 0) { |
bd74280d | 541 | printf("Attempt to write outside the flash area\n"); |
f9a52541 | 542 | *length = 0; |
dfbf617f SW |
543 | return -EINVAL; |
544 | } | |
545 | ||
c39d6a0e TR |
546 | if (used_for_write > lim) { |
547 | puts("Size of write exceeds partition or device limit\n"); | |
548 | *length = 0; | |
549 | return -EFBIG; | |
550 | } | |
551 | ||
169d54d8 | 552 | if (!need_skip && !(flags & WITH_DROP_FFS)) { |
bd74280d | 553 | rval = nand_write(nand, offset, length, buffer); |
f9a52541 SW |
554 | if (rval == 0) |
555 | return 0; | |
2077e348 | 556 | |
f9a52541 | 557 | *length = 0; |
bd74280d | 558 | printf("NAND write to offset %llx failed %d\n", |
f9a52541 | 559 | offset, rval); |
2077e348 | 560 | return rval; |
dfbf617f SW |
561 | } |
562 | ||
563 | while (left_to_write > 0) { | |
564 | size_t block_offset = offset & (nand->erasesize - 1); | |
169d54d8 | 565 | size_t write_size, truncated_write_size; |
dfbf617f | 566 | |
bd74280d | 567 | WATCHDOG_RESET(); |
1fc1d9ae | 568 | |
bd74280d BT |
569 | if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) { |
570 | printf("Skip bad block 0x%08llx\n", | |
dfbf617f SW |
571 | offset & ~(nand->erasesize - 1)); |
572 | offset += nand->erasesize - block_offset; | |
573 | continue; | |
574 | } | |
575 | ||
47fc18f1 | 576 | if (left_to_write < (blocksize - block_offset)) |
dfbf617f SW |
577 | write_size = left_to_write; |
578 | else | |
47fc18f1 LW |
579 | write_size = blocksize - block_offset; |
580 | ||
581 | #ifdef CONFIG_CMD_NAND_YAFFS | |
a6c9aa1f | 582 | if (flags & WITH_YAFFS_OOB) { |
47fc18f1 LW |
583 | int page, pages; |
584 | size_t pagesize = nand->writesize; | |
585 | size_t pagesize_oob = pagesize + nand->oobsize; | |
586 | struct mtd_oob_ops ops; | |
587 | ||
588 | ops.len = pagesize; | |
589 | ops.ooblen = nand->oobsize; | |
dfe64e2c | 590 | ops.mode = MTD_OPS_AUTO_OOB; |
47fc18f1 LW |
591 | ops.ooboffs = 0; |
592 | ||
593 | pages = write_size / pagesize_oob; | |
594 | for (page = 0; page < pages; page++) { | |
6f2ffc3d SW |
595 | WATCHDOG_RESET(); |
596 | ||
47fc18f1 LW |
597 | ops.datbuf = p_buffer; |
598 | ops.oobbuf = ops.datbuf + pagesize; | |
599 | ||
dfe64e2c | 600 | rval = mtd_write_oob(nand, offset, &ops); |
65683026 | 601 | if (rval != 0) |
47fc18f1 LW |
602 | break; |
603 | ||
604 | offset += pagesize; | |
605 | p_buffer += pagesize_oob; | |
606 | } | |
607 | } | |
608 | else | |
609 | #endif | |
610 | { | |
169d54d8 BG |
611 | truncated_write_size = write_size; |
612 | #ifdef CONFIG_CMD_NAND_TRIMFFS | |
613 | if (flags & WITH_DROP_FFS) | |
614 | truncated_write_size = drop_ffs(nand, p_buffer, | |
615 | &write_size); | |
616 | #endif | |
617 | ||
618 | rval = nand_write(nand, offset, &truncated_write_size, | |
619 | p_buffer); | |
47fc18f1 LW |
620 | offset += write_size; |
621 | p_buffer += write_size; | |
622 | } | |
dfbf617f | 623 | |
dfbf617f | 624 | if (rval != 0) { |
bd74280d | 625 | printf("NAND write to offset %llx failed %d\n", |
4b070809 | 626 | offset, rval); |
dfbf617f SW |
627 | *length -= left_to_write; |
628 | return rval; | |
629 | } | |
630 | ||
631 | left_to_write -= write_size; | |
dfbf617f SW |
632 | } |
633 | ||
634 | return 0; | |
635 | } | |
636 | ||
637 | /** | |
638 | * nand_read_skip_bad: | |
639 | * | |
640 | * Read image from NAND flash. | |
bd74280d | 641 | * Blocks that are marked bad are skipped and the next block is read |
c39d6a0e TR |
642 | * instead as long as the image is short enough to fit even after |
643 | * skipping the bad blocks. Due to bad blocks we may not be able to | |
644 | * perform the requested read. In the case where the read would extend | |
645 | * beyond the end of the NAND device, both length and actual (if not | |
646 | * NULL) are set to 0. In the case where the read would extend beyond | |
647 | * the limit we are passed, length is set to 0 and actual is set to the | |
648 | * required length. | |
dfbf617f SW |
649 | * |
650 | * @param nand NAND device | |
651 | * @param offset offset in flash | |
bd74280d | 652 | * @param length buffer length, on return holds number of read bytes |
c39d6a0e TR |
653 | * @param actual set to size required to read length worth of buffer or 0 |
654 | * on error, if not NULL | |
655 | * @param lim maximum size that actual may be in order to not exceed the | |
656 | * buffer | |
dfbf617f SW |
657 | * @param buffer buffer to write to |
658 | * @return 0 in case of success | |
659 | */ | |
378adfcd | 660 | int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, |
c39d6a0e | 661 | size_t *actual, loff_t lim, u_char *buffer) |
dfbf617f SW |
662 | { |
663 | int rval; | |
664 | size_t left_to_read = *length; | |
c39d6a0e | 665 | size_t used_for_read = 0; |
dfbf617f | 666 | u_char *p_buffer = buffer; |
f9a52541 | 667 | int need_skip; |
dfbf617f | 668 | |
f9a52541 | 669 | if ((offset & (nand->writesize - 1)) != 0) { |
bd74280d | 670 | printf("Attempt to read non page-aligned data\n"); |
f9a52541 | 671 | *length = 0; |
c39d6a0e TR |
672 | if (actual) |
673 | *actual = 0; | |
f9a52541 SW |
674 | return -EINVAL; |
675 | } | |
dfbf617f | 676 | |
c39d6a0e TR |
677 | need_skip = check_skip_len(nand, offset, *length, &used_for_read); |
678 | ||
679 | if (actual) | |
680 | *actual = used_for_read; | |
681 | ||
f9a52541 | 682 | if (need_skip < 0) { |
bd74280d | 683 | printf("Attempt to read outside the flash area\n"); |
f9a52541 | 684 | *length = 0; |
dfbf617f SW |
685 | return -EINVAL; |
686 | } | |
687 | ||
c39d6a0e TR |
688 | if (used_for_read > lim) { |
689 | puts("Size of read exceeds partition or device limit\n"); | |
690 | *length = 0; | |
691 | return -EFBIG; | |
692 | } | |
693 | ||
f9a52541 | 694 | if (!need_skip) { |
bd74280d | 695 | rval = nand_read(nand, offset, length, buffer); |
3ebf70db VG |
696 | if (!rval || rval == -EUCLEAN) |
697 | return 0; | |
f9a52541 SW |
698 | |
699 | *length = 0; | |
bd74280d | 700 | printf("NAND read from offset %llx failed %d\n", |
3ebf70db | 701 | offset, rval); |
2077e348 | 702 | return rval; |
dfbf617f SW |
703 | } |
704 | ||
705 | while (left_to_read > 0) { | |
706 | size_t block_offset = offset & (nand->erasesize - 1); | |
707 | size_t read_length; | |
708 | ||
bd74280d | 709 | WATCHDOG_RESET(); |
1fc1d9ae | 710 | |
bd74280d BT |
711 | if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) { |
712 | printf("Skipping bad block 0x%08llx\n", | |
dfbf617f SW |
713 | offset & ~(nand->erasesize - 1)); |
714 | offset += nand->erasesize - block_offset; | |
715 | continue; | |
716 | } | |
717 | ||
718 | if (left_to_read < (nand->erasesize - block_offset)) | |
719 | read_length = left_to_read; | |
720 | else | |
721 | read_length = nand->erasesize - block_offset; | |
722 | ||
bd74280d | 723 | rval = nand_read(nand, offset, &read_length, p_buffer); |
3ebf70db | 724 | if (rval && rval != -EUCLEAN) { |
bd74280d | 725 | printf("NAND read from offset %llx failed %d\n", |
4b070809 | 726 | offset, rval); |
dfbf617f SW |
727 | *length -= left_to_read; |
728 | return rval; | |
729 | } | |
730 | ||
731 | left_to_read -= read_length; | |
732 | offset += read_length; | |
733 | p_buffer += read_length; | |
734 | } | |
735 | ||
736 | return 0; | |
737 | } | |
3287f6d3 BT |
738 | |
739 | #ifdef CONFIG_CMD_NAND_TORTURE | |
740 | ||
741 | /** | |
742 | * check_pattern: | |
743 | * | |
744 | * Check if buffer contains only a certain byte pattern. | |
745 | * | |
746 | * @param buf buffer to check | |
747 | * @param patt the pattern to check | |
748 | * @param size buffer size in bytes | |
749 | * @return 1 if there are only patt bytes in buf | |
750 | * 0 if something else was found | |
751 | */ | |
752 | static int check_pattern(const u_char *buf, u_char patt, int size) | |
753 | { | |
754 | int i; | |
755 | ||
756 | for (i = 0; i < size; i++) | |
757 | if (buf[i] != patt) | |
758 | return 0; | |
759 | return 1; | |
760 | } | |
761 | ||
762 | /** | |
763 | * nand_torture: | |
764 | * | |
765 | * Torture a block of NAND flash. | |
766 | * This is useful to determine if a block that caused a write error is still | |
767 | * good or should be marked as bad. | |
768 | * | |
769 | * @param nand NAND device | |
770 | * @param offset offset in flash | |
771 | * @return 0 if the block is still good | |
772 | */ | |
773 | int nand_torture(nand_info_t *nand, loff_t offset) | |
774 | { | |
775 | u_char patterns[] = {0xa5, 0x5a, 0x00}; | |
776 | struct erase_info instr = { | |
777 | .mtd = nand, | |
778 | .addr = offset, | |
779 | .len = nand->erasesize, | |
780 | }; | |
781 | size_t retlen; | |
782 | int err, ret = -1, i, patt_count; | |
783 | u_char *buf; | |
784 | ||
785 | if ((offset & (nand->erasesize - 1)) != 0) { | |
786 | puts("Attempt to torture a block at a non block-aligned offset\n"); | |
787 | return -EINVAL; | |
788 | } | |
789 | ||
790 | if (offset + nand->erasesize > nand->size) { | |
791 | puts("Attempt to torture a block outside the flash area\n"); | |
792 | return -EINVAL; | |
793 | } | |
794 | ||
795 | patt_count = ARRAY_SIZE(patterns); | |
796 | ||
797 | buf = malloc(nand->erasesize); | |
798 | if (buf == NULL) { | |
799 | puts("Out of memory for erase block buffer\n"); | |
800 | return -ENOMEM; | |
801 | } | |
802 | ||
803 | for (i = 0; i < patt_count; i++) { | |
804 | err = nand->erase(nand, &instr); | |
805 | if (err) { | |
806 | printf("%s: erase() failed for block at 0x%llx: %d\n", | |
807 | nand->name, instr.addr, err); | |
808 | goto out; | |
809 | } | |
810 | ||
811 | /* Make sure the block contains only 0xff bytes */ | |
812 | err = nand->read(nand, offset, nand->erasesize, &retlen, buf); | |
813 | if ((err && err != -EUCLEAN) || retlen != nand->erasesize) { | |
814 | printf("%s: read() failed for block at 0x%llx: %d\n", | |
815 | nand->name, instr.addr, err); | |
816 | goto out; | |
817 | } | |
818 | ||
819 | err = check_pattern(buf, 0xff, nand->erasesize); | |
820 | if (!err) { | |
821 | printf("Erased block at 0x%llx, but a non-0xff byte was found\n", | |
822 | offset); | |
823 | ret = -EIO; | |
824 | goto out; | |
825 | } | |
826 | ||
827 | /* Write a pattern and check it */ | |
828 | memset(buf, patterns[i], nand->erasesize); | |
829 | err = nand->write(nand, offset, nand->erasesize, &retlen, buf); | |
830 | if (err || retlen != nand->erasesize) { | |
831 | printf("%s: write() failed for block at 0x%llx: %d\n", | |
832 | nand->name, instr.addr, err); | |
833 | goto out; | |
834 | } | |
835 | ||
836 | err = nand->read(nand, offset, nand->erasesize, &retlen, buf); | |
837 | if ((err && err != -EUCLEAN) || retlen != nand->erasesize) { | |
838 | printf("%s: read() failed for block at 0x%llx: %d\n", | |
839 | nand->name, instr.addr, err); | |
840 | goto out; | |
841 | } | |
842 | ||
843 | err = check_pattern(buf, patterns[i], nand->erasesize); | |
844 | if (!err) { | |
845 | printf("Pattern 0x%.2x checking failed for block at " | |
846 | "0x%llx\n", patterns[i], offset); | |
847 | ret = -EIO; | |
848 | goto out; | |
849 | } | |
850 | } | |
851 | ||
852 | ret = 0; | |
853 | ||
854 | out: | |
855 | free(buf); | |
856 | return ret; | |
857 | } | |
858 | ||
859 | #endif |