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