]>
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 <[email protected]> | |
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 ([email protected]) | |
12 | * and Thomas Gleixner ([email protected]) | |
13 | * | |
169d54d8 BG |
14 | * Copyright (C) 2008 Nokia Corporation: drop_ffs() function by |
15 | * Artem Bityutskiy <[email protected]> from mtd-utils | |
16 | * | |
2255b2d2 SR |
17 | * See file CREDITS for list of people who contributed to this |
18 | * project. | |
19 | * | |
20 | * This program is free software; you can redistribute it and/or | |
21 | * modify it under the terms of the GNU General Public License version | |
22 | * 2 as published by the Free Software Foundation. | |
23 | * | |
24 | * This program is distributed in the hope that it will be useful, | |
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
27 | * GNU General Public License for more details. | |
28 | * | |
29 | * You should have received a copy of the GNU General Public License | |
30 | * along with this program; if not, write to the Free Software | |
31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
32 | * MA 02111-1307 USA | |
33 | * | |
f9a52541 SW |
34 | * Copyright 2010 Freescale Semiconductor |
35 | * The portions of this file whose copyright is held by Freescale and which | |
36 | * are not considered a derived work of GPL v2-only code may be distributed | |
37 | * and/or modified under the terms of the GNU General Public License as | |
38 | * published by the Free Software Foundation; either version 2 of the | |
39 | * License, or (at your option) any later version. | |
2255b2d2 SR |
40 | */ |
41 | ||
42 | #include <common.h> | |
2255b2d2 SR |
43 | #include <command.h> |
44 | #include <watchdog.h> | |
45 | #include <malloc.h> | |
3a6d56c2 | 46 | #include <div64.h> |
2255b2d2 | 47 | |
cfa460ad WJ |
48 | #include <asm/errno.h> |
49 | #include <linux/mtd/mtd.h> | |
2255b2d2 SR |
50 | #include <nand.h> |
51 | #include <jffs2/jffs2.h> | |
52 | ||
53 | typedef struct erase_info erase_info_t; | |
54 | typedef struct mtd_info mtd_info_t; | |
55 | ||
56 | /* support only for native endian JFFS2 */ | |
57 | #define cpu_to_je16(x) (x) | |
58 | #define cpu_to_je32(x) (x) | |
59 | ||
2255b2d2 SR |
60 | /** |
61 | * nand_erase_opts: - erase NAND flash with support for various options | |
62 | * (jffs2 formating) | |
63 | * | |
64 | * @param meminfo NAND device to erase | |
65 | * @param opts options, @see struct nand_erase_options | |
66 | * @return 0 in case of success | |
67 | * | |
68 | * This code is ported from flash_eraseall.c from Linux mtd utils by | |
69 | * Arcom Control System Ltd. | |
70 | */ | |
71 | int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) | |
72 | { | |
73 | struct jffs2_unknown_node cleanmarker; | |
2255b2d2 | 74 | erase_info_t erase; |
30486322 | 75 | unsigned long erase_length, erased_length; /* in blocks */ |
2255b2d2 SR |
76 | int bbtest = 1; |
77 | int result; | |
78 | int percent_complete = -1; | |
2255b2d2 | 79 | const char *mtd_device = meminfo->name; |
cfa460ad WJ |
80 | struct mtd_oob_ops oob_opts; |
81 | struct nand_chip *chip = meminfo->priv; | |
2255b2d2 | 82 | |
30486322 SW |
83 | if ((opts->offset & (meminfo->writesize - 1)) != 0) { |
84 | printf("Attempt to erase non page aligned data\n"); | |
85 | return -1; | |
86 | } | |
87 | ||
2255b2d2 | 88 | memset(&erase, 0, sizeof(erase)); |
cfa460ad | 89 | memset(&oob_opts, 0, sizeof(oob_opts)); |
2255b2d2 SR |
90 | |
91 | erase.mtd = meminfo; | |
92 | erase.len = meminfo->erasesize; | |
856f0544 | 93 | erase.addr = opts->offset; |
30486322 SW |
94 | erase_length = lldiv(opts->length + meminfo->erasesize - 1, |
95 | meminfo->erasesize); | |
2255b2d2 | 96 | |
cfa460ad WJ |
97 | cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); |
98 | cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); | |
99 | cleanmarker.totlen = cpu_to_je32(8); | |
2255b2d2 SR |
100 | |
101 | /* scrub option allows to erase badblock. To prevent internal | |
102 | * check from erase() method, set block check method to dummy | |
103 | * and disable bad block table while erasing. | |
104 | */ | |
105 | if (opts->scrub) { | |
6d41419f MV |
106 | erase.scrub = opts->scrub; |
107 | /* | |
108 | * We don't need the bad block table anymore... | |
2255b2d2 SR |
109 | * after scrub, there are no bad blocks left! |
110 | */ | |
6d41419f MV |
111 | if (chip->bbt) { |
112 | kfree(chip->bbt); | |
2255b2d2 | 113 | } |
6d41419f | 114 | chip->bbt = NULL; |
2255b2d2 SR |
115 | } |
116 | ||
30486322 SW |
117 | for (erased_length = 0; |
118 | erased_length < erase_length; | |
2255b2d2 | 119 | erase.addr += meminfo->erasesize) { |
4cbb651b | 120 | |
2255b2d2 SR |
121 | WATCHDOG_RESET (); |
122 | ||
123 | if (!opts->scrub && bbtest) { | |
124 | int ret = meminfo->block_isbad(meminfo, erase.addr); | |
125 | if (ret > 0) { | |
126 | if (!opts->quiet) | |
127 | printf("\rSkipping bad block at " | |
8d2effea | 128 | "0x%08llx " |
87621bc2 WD |
129 | " \n", |
130 | erase.addr); | |
30486322 SW |
131 | |
132 | if (!opts->spread) | |
133 | erased_length++; | |
134 | ||
2255b2d2 SR |
135 | continue; |
136 | ||
137 | } else if (ret < 0) { | |
138 | printf("\n%s: MTD get bad block failed: %d\n", | |
139 | mtd_device, | |
140 | ret); | |
141 | return -1; | |
142 | } | |
143 | } | |
144 | ||
30486322 SW |
145 | erased_length++; |
146 | ||
2255b2d2 SR |
147 | result = meminfo->erase(meminfo, &erase); |
148 | if (result != 0) { | |
149 | printf("\n%s: MTD Erase failure: %d\n", | |
150 | mtd_device, result); | |
151 | continue; | |
152 | } | |
153 | ||
154 | /* format for JFFS2 ? */ | |
bd78bc6b SW |
155 | if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) { |
156 | chip->ops.ooblen = 8; | |
cfa460ad | 157 | chip->ops.datbuf = NULL; |
bd78bc6b SW |
158 | chip->ops.oobbuf = (uint8_t *)&cleanmarker; |
159 | chip->ops.ooboffs = 0; | |
160 | chip->ops.mode = MTD_OOB_AUTO; | |
4cbb651b | 161 | |
cfa460ad | 162 | result = meminfo->write_oob(meminfo, |
bd78bc6b SW |
163 | erase.addr, |
164 | &chip->ops); | |
cfa460ad WJ |
165 | if (result != 0) { |
166 | printf("\n%s: MTD writeoob failure: %d\n", | |
bd78bc6b | 167 | mtd_device, result); |
cfa460ad | 168 | continue; |
2255b2d2 SR |
169 | } |
170 | } | |
171 | ||
172 | if (!opts->quiet) { | |
30486322 | 173 | unsigned long long n = erased_length * 100ULL; |
5bd7fe9a MF |
174 | int percent; |
175 | ||
176 | do_div(n, erase_length); | |
177 | percent = (int)n; | |
2255b2d2 SR |
178 | |
179 | /* output progress message only at whole percent | |
180 | * steps to reduce the number of messages printed | |
181 | * on (slow) serial consoles | |
182 | */ | |
183 | if (percent != percent_complete) { | |
184 | percent_complete = percent; | |
185 | ||
8d2effea | 186 | printf("\rErasing at 0x%llx -- %3d%% complete.", |
bd78bc6b | 187 | erase.addr, percent); |
2255b2d2 SR |
188 | |
189 | if (opts->jffs2 && result == 0) | |
8d2effea | 190 | printf(" Cleanmarker written at 0x%llx.", |
bd78bc6b | 191 | erase.addr); |
2255b2d2 SR |
192 | } |
193 | } | |
194 | } | |
195 | if (!opts->quiet) | |
196 | printf("\n"); | |
197 | ||
6d41419f MV |
198 | if (opts->scrub) |
199 | chip->scan_bbt(meminfo); | |
2255b2d2 SR |
200 | |
201 | return 0; | |
202 | } | |
203 | ||
50657c27 NM |
204 | #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK |
205 | ||
2255b2d2 SR |
206 | /****************************************************************************** |
207 | * Support for locking / unlocking operations of some NAND devices | |
208 | *****************************************************************************/ | |
209 | ||
210 | #define NAND_CMD_LOCK 0x2a | |
211 | #define NAND_CMD_LOCK_TIGHT 0x2c | |
212 | #define NAND_CMD_UNLOCK1 0x23 | |
213 | #define NAND_CMD_UNLOCK2 0x24 | |
214 | #define NAND_CMD_LOCK_STATUS 0x7a | |
215 | ||
216 | /** | |
217 | * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT | |
218 | * state | |
219 | * | |
50657c27 | 220 | * @param mtd nand mtd instance |
2255b2d2 SR |
221 | * @param tight bring device in lock tight mode |
222 | * | |
223 | * @return 0 on success, -1 in case of error | |
224 | * | |
225 | * The lock / lock-tight command only applies to the whole chip. To get some | |
226 | * parts of the chip lock and others unlocked use the following sequence: | |
227 | * | |
228 | * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin) | |
229 | * - Call nand_unlock() once for each consecutive area to be unlocked | |
230 | * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1) | |
231 | * | |
232 | * If the device is in lock-tight state software can't change the | |
233 | * current active lock/unlock state of all pages. nand_lock() / nand_unlock() | |
234 | * calls will fail. It is only posible to leave lock-tight state by | |
235 | * an hardware signal (low pulse on _WP pin) or by power down. | |
236 | */ | |
50657c27 | 237 | int nand_lock(struct mtd_info *mtd, int tight) |
2255b2d2 SR |
238 | { |
239 | int ret = 0; | |
240 | int status; | |
50657c27 | 241 | struct nand_chip *chip = mtd->priv; |
2255b2d2 SR |
242 | |
243 | /* select the NAND device */ | |
50657c27 | 244 | chip->select_chip(mtd, 0); |
2255b2d2 | 245 | |
50657c27 | 246 | chip->cmdfunc(mtd, |
2255b2d2 SR |
247 | (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK), |
248 | -1, -1); | |
249 | ||
250 | /* call wait ready function */ | |
50657c27 | 251 | status = chip->waitfunc(mtd, chip); |
2255b2d2 SR |
252 | |
253 | /* see if device thinks it succeeded */ | |
254 | if (status & 0x01) { | |
255 | ret = -1; | |
256 | } | |
257 | ||
258 | /* de-select the NAND device */ | |
50657c27 | 259 | chip->select_chip(mtd, -1); |
2255b2d2 SR |
260 | return ret; |
261 | } | |
262 | ||
263 | /** | |
264 | * nand_get_lock_status: - query current lock state from one page of NAND | |
265 | * flash | |
266 | * | |
50657c27 | 267 | * @param mtd nand mtd instance |
2255b2d2 SR |
268 | * @param offset page address to query (muss be page aligned!) |
269 | * | |
270 | * @return -1 in case of error | |
271 | * >0 lock status: | |
272 | * bitfield with the following combinations: | |
273 | * NAND_LOCK_STATUS_TIGHT: page in tight state | |
274 | * NAND_LOCK_STATUS_LOCK: page locked | |
275 | * NAND_LOCK_STATUS_UNLOCK: page unlocked | |
276 | * | |
277 | */ | |
378adfcd | 278 | int nand_get_lock_status(struct mtd_info *mtd, loff_t offset) |
2255b2d2 SR |
279 | { |
280 | int ret = 0; | |
281 | int chipnr; | |
282 | int page; | |
50657c27 | 283 | struct nand_chip *chip = mtd->priv; |
2255b2d2 SR |
284 | |
285 | /* select the NAND device */ | |
50657c27 NM |
286 | chipnr = (int)(offset >> chip->chip_shift); |
287 | chip->select_chip(mtd, chipnr); | |
2255b2d2 SR |
288 | |
289 | ||
50657c27 | 290 | if ((offset & (mtd->writesize - 1)) != 0) { |
2255b2d2 SR |
291 | printf ("nand_get_lock_status: " |
292 | "Start address must be beginning of " | |
293 | "nand page!\n"); | |
294 | ret = -1; | |
295 | goto out; | |
296 | } | |
297 | ||
298 | /* check the Lock Status */ | |
50657c27 NM |
299 | page = (int)(offset >> chip->page_shift); |
300 | chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask); | |
2255b2d2 | 301 | |
50657c27 | 302 | ret = chip->read_byte(mtd) & (NAND_LOCK_STATUS_TIGHT |
2255b2d2 SR |
303 | | NAND_LOCK_STATUS_LOCK |
304 | | NAND_LOCK_STATUS_UNLOCK); | |
305 | ||
306 | out: | |
307 | /* de-select the NAND device */ | |
50657c27 | 308 | chip->select_chip(mtd, -1); |
2255b2d2 SR |
309 | return ret; |
310 | } | |
311 | ||
312 | /** | |
313 | * nand_unlock: - Unlock area of NAND pages | |
314 | * only one consecutive area can be unlocked at one time! | |
315 | * | |
50657c27 | 316 | * @param mtd nand mtd instance |
2255b2d2 SR |
317 | * @param start start byte address |
318 | * @param length number of bytes to unlock (must be a multiple of | |
cfa460ad | 319 | * page size nand->writesize) |
2255b2d2 SR |
320 | * |
321 | * @return 0 on success, -1 in case of error | |
322 | */ | |
50657c27 | 323 | int nand_unlock(struct mtd_info *mtd, ulong start, ulong length) |
2255b2d2 SR |
324 | { |
325 | int ret = 0; | |
326 | int chipnr; | |
327 | int status; | |
328 | int page; | |
50657c27 | 329 | struct nand_chip *chip = mtd->priv; |
2255b2d2 SR |
330 | printf ("nand_unlock: start: %08x, length: %d!\n", |
331 | (int)start, (int)length); | |
332 | ||
333 | /* select the NAND device */ | |
50657c27 NM |
334 | chipnr = (int)(start >> chip->chip_shift); |
335 | chip->select_chip(mtd, chipnr); | |
2255b2d2 SR |
336 | |
337 | /* check the WP bit */ | |
50657c27 NM |
338 | chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); |
339 | if (!(chip->read_byte(mtd) & NAND_STATUS_WP)) { | |
2255b2d2 SR |
340 | printf ("nand_unlock: Device is write protected!\n"); |
341 | ret = -1; | |
342 | goto out; | |
343 | } | |
344 | ||
50657c27 | 345 | if ((start & (mtd->erasesize - 1)) != 0) { |
2255b2d2 | 346 | printf ("nand_unlock: Start address must be beginning of " |
50657c27 | 347 | "nand block!\n"); |
2255b2d2 SR |
348 | ret = -1; |
349 | goto out; | |
350 | } | |
351 | ||
50657c27 NM |
352 | if (length == 0 || (length & (mtd->erasesize - 1)) != 0) { |
353 | printf ("nand_unlock: Length must be a multiple of nand block " | |
354 | "size %08x!\n", mtd->erasesize); | |
2255b2d2 SR |
355 | ret = -1; |
356 | goto out; | |
357 | } | |
358 | ||
50657c27 NM |
359 | /* |
360 | * Set length so that the last address is set to the | |
361 | * starting address of the last block | |
362 | */ | |
363 | length -= mtd->erasesize; | |
364 | ||
2255b2d2 | 365 | /* submit address of first page to unlock */ |
50657c27 NM |
366 | page = (int)(start >> chip->page_shift); |
367 | chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask); | |
2255b2d2 SR |
368 | |
369 | /* submit ADDRESS of LAST page to unlock */ | |
50657c27 NM |
370 | page += (int)(length >> chip->page_shift); |
371 | chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, page & chip->pagemask); | |
2255b2d2 SR |
372 | |
373 | /* call wait ready function */ | |
50657c27 | 374 | status = chip->waitfunc(mtd, chip); |
2255b2d2 SR |
375 | /* see if device thinks it succeeded */ |
376 | if (status & 0x01) { | |
377 | /* there was an error */ | |
378 | ret = -1; | |
379 | goto out; | |
380 | } | |
381 | ||
382 | out: | |
383 | /* de-select the NAND device */ | |
50657c27 | 384 | chip->select_chip(mtd, -1); |
2255b2d2 SR |
385 | return ret; |
386 | } | |
cfa460ad | 387 | #endif |
2255b2d2 | 388 | |
dfbf617f | 389 | /** |
f9a52541 | 390 | * check_skip_len |
dfbf617f | 391 | * |
f9a52541 SW |
392 | * Check if there are any bad blocks, and whether length including bad |
393 | * blocks fits into device | |
dfbf617f SW |
394 | * |
395 | * @param nand NAND device | |
396 | * @param offset offset in flash | |
397 | * @param length image length | |
f9a52541 SW |
398 | * @return 0 if the image fits and there are no bad blocks |
399 | * 1 if the image fits, but there are bad blocks | |
400 | * -1 if the image does not fit | |
dfbf617f | 401 | */ |
f9a52541 | 402 | static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length) |
dfbf617f | 403 | { |
dfbf617f | 404 | size_t len_excl_bad = 0; |
f9a52541 | 405 | int ret = 0; |
dfbf617f SW |
406 | |
407 | while (len_excl_bad < length) { | |
f9a52541 SW |
408 | size_t block_len, block_off; |
409 | loff_t block_start; | |
dfbf617f | 410 | |
f9a52541 SW |
411 | if (offset >= nand->size) |
412 | return -1; | |
dfbf617f | 413 | |
f9a52541 SW |
414 | block_start = offset & ~(loff_t)(nand->erasesize - 1); |
415 | block_off = offset & (nand->erasesize - 1); | |
416 | block_len = nand->erasesize - block_off; | |
dfbf617f | 417 | |
f9a52541 SW |
418 | if (!nand_block_isbad(nand, block_start)) |
419 | len_excl_bad += block_len; | |
420 | else | |
421 | ret = 1; | |
422 | ||
423 | offset += block_len; | |
dfbf617f SW |
424 | } |
425 | ||
f9a52541 | 426 | return ret; |
dfbf617f SW |
427 | } |
428 | ||
169d54d8 BG |
429 | #ifdef CONFIG_CMD_NAND_TRIMFFS |
430 | static size_t drop_ffs(const nand_info_t *nand, const u_char *buf, | |
431 | const size_t *len) | |
432 | { | |
433 | size_t i, l = *len; | |
434 | ||
435 | for (i = l - 1; i >= 0; i--) | |
436 | if (buf[i] != 0xFF) | |
437 | break; | |
438 | ||
439 | /* The resulting length must be aligned to the minimum flash I/O size */ | |
440 | l = i + 1; | |
441 | l = (l + nand->writesize - 1) / nand->writesize; | |
442 | l *= nand->writesize; | |
443 | ||
444 | /* | |
445 | * since the input length may be unaligned, prevent access past the end | |
446 | * of the buffer | |
447 | */ | |
448 | return min(l, *len); | |
449 | } | |
450 | #endif | |
451 | ||
dfbf617f SW |
452 | /** |
453 | * nand_write_skip_bad: | |
454 | * | |
455 | * Write image to NAND flash. | |
456 | * Blocks that are marked bad are skipped and the is written to the next | |
457 | * block instead as long as the image is short enough to fit even after | |
458 | * skipping the bad blocks. | |
459 | * | |
460 | * @param nand NAND device | |
461 | * @param offset offset in flash | |
462 | * @param length buffer length | |
47fc18f1 | 463 | * @param buffer buffer to read from |
a6c9aa1f | 464 | * @param flags flags modifying the behaviour of the write to NAND |
dfbf617f SW |
465 | * @return 0 in case of success |
466 | */ | |
378adfcd | 467 | int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, |
a6c9aa1f | 468 | u_char *buffer, int flags) |
dfbf617f | 469 | { |
47fc18f1 | 470 | int rval = 0, blocksize; |
dfbf617f | 471 | size_t left_to_write = *length; |
dfbf617f | 472 | u_char *p_buffer = buffer; |
f9a52541 | 473 | int need_skip; |
dfbf617f | 474 | |
47fc18f1 | 475 | #ifdef CONFIG_CMD_NAND_YAFFS |
a6c9aa1f | 476 | if (flags & WITH_YAFFS_OOB) { |
c135456f BG |
477 | if (flags & ~WITH_YAFFS_OOB) |
478 | return -EINVAL; | |
479 | ||
47fc18f1 LW |
480 | int pages; |
481 | pages = nand->erasesize / nand->writesize; | |
482 | blocksize = (pages * nand->oobsize) + nand->erasesize; | |
483 | if (*length % (nand->writesize + nand->oobsize)) { | |
484 | printf ("Attempt to write incomplete page" | |
485 | " in yaffs mode\n"); | |
486 | return -EINVAL; | |
487 | } | |
488 | } else | |
489 | #endif | |
490 | { | |
491 | blocksize = nand->erasesize; | |
492 | } | |
493 | ||
f9a52541 SW |
494 | /* |
495 | * nand_write() handles unaligned, partial page writes. | |
496 | * | |
497 | * We allow length to be unaligned, for convenience in | |
498 | * using the $filesize variable. | |
499 | * | |
500 | * However, starting at an unaligned offset makes the | |
501 | * semantics of bad block skipping ambiguous (really, | |
502 | * you should only start a block skipping access at a | |
503 | * partition boundary). So don't try to handle that. | |
504 | */ | |
505 | if ((offset & (nand->writesize - 1)) != 0) { | |
dfbf617f | 506 | printf ("Attempt to write non page aligned data\n"); |
f9a52541 | 507 | *length = 0; |
dfbf617f SW |
508 | return -EINVAL; |
509 | } | |
510 | ||
f9a52541 SW |
511 | need_skip = check_skip_len(nand, offset, *length); |
512 | if (need_skip < 0) { | |
dfbf617f | 513 | printf ("Attempt to write outside the flash area\n"); |
f9a52541 | 514 | *length = 0; |
dfbf617f SW |
515 | return -EINVAL; |
516 | } | |
517 | ||
169d54d8 | 518 | if (!need_skip && !(flags & WITH_DROP_FFS)) { |
dfbf617f | 519 | rval = nand_write (nand, offset, length, buffer); |
f9a52541 SW |
520 | if (rval == 0) |
521 | return 0; | |
2077e348 | 522 | |
f9a52541 SW |
523 | *length = 0; |
524 | printf ("NAND write to offset %llx failed %d\n", | |
525 | offset, rval); | |
2077e348 | 526 | return rval; |
dfbf617f SW |
527 | } |
528 | ||
529 | while (left_to_write > 0) { | |
530 | size_t block_offset = offset & (nand->erasesize - 1); | |
169d54d8 | 531 | size_t write_size, truncated_write_size; |
dfbf617f | 532 | |
1fc1d9ae GB |
533 | WATCHDOG_RESET (); |
534 | ||
dfbf617f | 535 | if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) { |
378adfcd | 536 | printf ("Skip bad block 0x%08llx\n", |
dfbf617f SW |
537 | offset & ~(nand->erasesize - 1)); |
538 | offset += nand->erasesize - block_offset; | |
539 | continue; | |
540 | } | |
541 | ||
47fc18f1 | 542 | if (left_to_write < (blocksize - block_offset)) |
dfbf617f SW |
543 | write_size = left_to_write; |
544 | else | |
47fc18f1 LW |
545 | write_size = blocksize - block_offset; |
546 | ||
547 | #ifdef CONFIG_CMD_NAND_YAFFS | |
a6c9aa1f | 548 | if (flags & WITH_YAFFS_OOB) { |
47fc18f1 LW |
549 | int page, pages; |
550 | size_t pagesize = nand->writesize; | |
551 | size_t pagesize_oob = pagesize + nand->oobsize; | |
552 | struct mtd_oob_ops ops; | |
553 | ||
554 | ops.len = pagesize; | |
555 | ops.ooblen = nand->oobsize; | |
556 | ops.mode = MTD_OOB_AUTO; | |
557 | ops.ooboffs = 0; | |
558 | ||
559 | pages = write_size / pagesize_oob; | |
560 | for (page = 0; page < pages; page++) { | |
6f2ffc3d SW |
561 | WATCHDOG_RESET(); |
562 | ||
47fc18f1 LW |
563 | ops.datbuf = p_buffer; |
564 | ops.oobbuf = ops.datbuf + pagesize; | |
565 | ||
566 | rval = nand->write_oob(nand, offset, &ops); | |
567 | if (!rval) | |
568 | break; | |
569 | ||
570 | offset += pagesize; | |
571 | p_buffer += pagesize_oob; | |
572 | } | |
573 | } | |
574 | else | |
575 | #endif | |
576 | { | |
169d54d8 BG |
577 | truncated_write_size = write_size; |
578 | #ifdef CONFIG_CMD_NAND_TRIMFFS | |
579 | if (flags & WITH_DROP_FFS) | |
580 | truncated_write_size = drop_ffs(nand, p_buffer, | |
581 | &write_size); | |
582 | #endif | |
583 | ||
584 | rval = nand_write(nand, offset, &truncated_write_size, | |
585 | p_buffer); | |
47fc18f1 LW |
586 | offset += write_size; |
587 | p_buffer += write_size; | |
588 | } | |
dfbf617f | 589 | |
dfbf617f | 590 | if (rval != 0) { |
378adfcd | 591 | printf ("NAND write to offset %llx failed %d\n", |
4b070809 | 592 | offset, rval); |
dfbf617f SW |
593 | *length -= left_to_write; |
594 | return rval; | |
595 | } | |
596 | ||
597 | left_to_write -= write_size; | |
dfbf617f SW |
598 | } |
599 | ||
600 | return 0; | |
601 | } | |
602 | ||
603 | /** | |
604 | * nand_read_skip_bad: | |
605 | * | |
606 | * Read image from NAND flash. | |
607 | * Blocks that are marked bad are skipped and the next block is readen | |
608 | * instead as long as the image is short enough to fit even after skipping the | |
609 | * bad blocks. | |
610 | * | |
611 | * @param nand NAND device | |
612 | * @param offset offset in flash | |
613 | * @param length buffer length, on return holds remaining bytes to read | |
614 | * @param buffer buffer to write to | |
615 | * @return 0 in case of success | |
616 | */ | |
378adfcd | 617 | int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, |
dfbf617f SW |
618 | u_char *buffer) |
619 | { | |
620 | int rval; | |
621 | size_t left_to_read = *length; | |
dfbf617f | 622 | u_char *p_buffer = buffer; |
f9a52541 | 623 | int need_skip; |
dfbf617f | 624 | |
f9a52541 SW |
625 | if ((offset & (nand->writesize - 1)) != 0) { |
626 | printf ("Attempt to read non page aligned data\n"); | |
627 | *length = 0; | |
628 | return -EINVAL; | |
629 | } | |
dfbf617f | 630 | |
f9a52541 SW |
631 | need_skip = check_skip_len(nand, offset, *length); |
632 | if (need_skip < 0) { | |
dfbf617f | 633 | printf ("Attempt to read outside the flash area\n"); |
f9a52541 | 634 | *length = 0; |
dfbf617f SW |
635 | return -EINVAL; |
636 | } | |
637 | ||
f9a52541 | 638 | if (!need_skip) { |
dfbf617f | 639 | rval = nand_read (nand, offset, length, buffer); |
3ebf70db VG |
640 | if (!rval || rval == -EUCLEAN) |
641 | return 0; | |
f9a52541 SW |
642 | |
643 | *length = 0; | |
3ebf70db VG |
644 | printf ("NAND read from offset %llx failed %d\n", |
645 | offset, rval); | |
2077e348 | 646 | return rval; |
dfbf617f SW |
647 | } |
648 | ||
649 | while (left_to_read > 0) { | |
650 | size_t block_offset = offset & (nand->erasesize - 1); | |
651 | size_t read_length; | |
652 | ||
1fc1d9ae GB |
653 | WATCHDOG_RESET (); |
654 | ||
dfbf617f | 655 | if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) { |
378adfcd | 656 | printf ("Skipping bad block 0x%08llx\n", |
dfbf617f SW |
657 | offset & ~(nand->erasesize - 1)); |
658 | offset += nand->erasesize - block_offset; | |
659 | continue; | |
660 | } | |
661 | ||
662 | if (left_to_read < (nand->erasesize - block_offset)) | |
663 | read_length = left_to_read; | |
664 | else | |
665 | read_length = nand->erasesize - block_offset; | |
666 | ||
667 | rval = nand_read (nand, offset, &read_length, p_buffer); | |
3ebf70db | 668 | if (rval && rval != -EUCLEAN) { |
378adfcd | 669 | printf ("NAND read from offset %llx failed %d\n", |
4b070809 | 670 | offset, rval); |
dfbf617f SW |
671 | *length -= left_to_read; |
672 | return rval; | |
673 | } | |
674 | ||
675 | left_to_read -= read_length; | |
676 | offset += read_length; | |
677 | p_buffer += read_length; | |
678 | } | |
679 | ||
680 | return 0; | |
681 | } |