bf4ed97f13ddb5cc76fa543daa94bd1603a2eac9
[openwrt/openwrt.git] / package / boot / uboot-mediatek / patches / 100-13-cmd-add-a-new-command-for-NAND-flash-debugging.patch
1 From 88271cb3ae9c68dc200d627653df96fc557c2a64 Mon Sep 17 00:00:00 2001
2 From: Weijie Gao <weijie.gao@mediatek.com>
3 Date: Mon, 25 Jul 2022 10:55:35 +0800
4 Subject: [PATCH 47/71] cmd: add a new command for NAND flash debugging
5
6 Add a command 'nand-ext' for NAND flash debugging:
7 - Dump a page with oob, with optional raw read support
8 - Display all bad blocks
9 - Mark a block as bad block
10 - Set a bitflip on a page
11 - Erase
12 - Read / write data from/to any offset with any size
13 - Read / write pages with oob
14 - Erase, read and write support skip bad block or forced mode, support
15 raw mode, supporot auto-oob mode
16 - Supports operating on a specific partition
17 - No need to specify NAND device name
18
19 Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
20 ---
21 cmd/Kconfig | 8 +
22 cmd/Makefile | 1 +
23 cmd/nand-ext.c | 1062 ++++++++++++++++++++++++++++++++++++++++++++++++
24 3 files changed, 1071 insertions(+)
25 create mode 100644 cmd/nand-ext.c
26
27 --- a/cmd/Kconfig
28 +++ b/cmd/Kconfig
29 @@ -1260,6 +1260,14 @@ config CMD_NAND_TORTURE
30
31 endif # CMD_NAND
32
33 +config CMD_NAND_EXT
34 + bool "nand - extended nand utility for debugging"
35 + depends on !CMD_NAND
36 + default y if MTD_RAW_NAND || MTD_SPI_NAND || MTK_SPI_NAND
37 + select MTD_PARTITIONS
38 + help
39 + NAND flash R/W and debugging support.
40 +
41 config CMD_NMBM
42 depends on NMBM_MTD
43 bool "nmbm"
44 --- a/cmd/Makefile
45 +++ b/cmd/Makefile
46 @@ -114,6 +114,7 @@ obj-y += legacy-mtd-utils.o
47 endif
48 obj-$(CONFIG_CMD_MUX) += mux.o
49 obj-$(CONFIG_CMD_NAND) += nand.o
50 +obj-$(CONFIG_CMD_NAND_EXT) += nand-ext.o
51 obj-$(CONFIG_CMD_NMBM) += nmbm.o
52 obj-$(CONFIG_CMD_NET) += net.o
53 obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o
54 --- /dev/null
55 +++ b/cmd/nand-ext.c
56 @@ -0,0 +1,1062 @@
57 +// SPDX-License-Identifier: GPL-2.0
58 +/*
59 + * Copyright (C) 2021 MediaTek Inc. All Rights Reserved.
60 + *
61 + * Author: Weijie Gao <weijie.gao@mediatek.com>
62 + */
63 +
64 +#include <command.h>
65 +#include <stdbool.h>
66 +#include <malloc.h>
67 +#include <mtd.h>
68 +#include <dm/devres.h>
69 +#include <linux/types.h>
70 +#include <linux/mtd/mtd.h>
71 +
72 +static struct mtd_info *curr_dev;
73 +
74 +static void mtd_show_parts(struct mtd_info *mtd, int level)
75 +{
76 + struct mtd_info *part;
77 + int i;
78 +
79 + list_for_each_entry(part, &mtd->partitions, node) {
80 + for (i = 0; i < level; i++)
81 + printf("\t");
82 + printf(" - 0x%012llx-0x%012llx : \"%s\"\n",
83 + part->offset, part->offset + part->size, part->name);
84 +
85 + mtd_show_parts(part, level + 1);
86 + }
87 +}
88 +
89 +static void mtd_show_device(struct mtd_info *mtd)
90 +{
91 + /* Device */
92 + printf("* %s\n", mtd->name);
93 +#if defined(CONFIG_DM)
94 + if (mtd->dev) {
95 + printf(" - device: %s\n", mtd->dev->name);
96 + printf(" - parent: %s\n", mtd->dev->parent->name);
97 + printf(" - driver: %s\n", mtd->dev->driver->name);
98 + }
99 +#endif
100 +
101 + /* MTD device information */
102 + printf(" - type: ");
103 + switch (mtd->type) {
104 + case MTD_NANDFLASH:
105 + printf("NAND flash\n");
106 + break;
107 + case MTD_MLCNANDFLASH:
108 + printf("MLC NAND flash\n");
109 + break;
110 + case MTD_ABSENT:
111 + default:
112 + printf("Not supported\n");
113 + break;
114 + }
115 +
116 + printf(" - block size: 0x%x bytes\n", mtd->erasesize);
117 + printf(" - page size: 0x%x bytes\n", mtd->writesize);
118 + printf(" - OOB size: %u bytes\n", mtd->oobsize);
119 + printf(" - OOB available: %u bytes\n", mtd->oobavail);
120 +
121 + if (mtd->ecc_strength) {
122 + printf(" - ECC strength: %u bits\n", mtd->ecc_strength);
123 + printf(" - ECC step size: %u bytes\n", mtd->ecc_step_size);
124 + printf(" - bitflip threshold: %u bits\n",
125 + mtd->bitflip_threshold);
126 + }
127 +
128 + printf(" - 0x%012llx-0x%012llx : \"%s\"\n",
129 + mtd->offset, mtd->offset + mtd->size, mtd->name);
130 +
131 + /* MTD partitions, if any */
132 + mtd_show_parts(mtd, 1);
133 +}
134 +
135 +static int do_nand_list(struct cmd_tbl *cmdtp, int flag, int argc,
136 + char *const argv[])
137 +{
138 + struct mtd_info *mtd;
139 + int dev_nb = 0;
140 +
141 + /* Ensure all devices (and their partitions) are probed */
142 + mtd_probe_devices();
143 +
144 + printf("List of NAND devices:\n");
145 + mtd_for_each_device(mtd) {
146 + if (mtd->type != MTD_NANDFLASH && mtd->type != MTD_MLCNANDFLASH)
147 + continue;
148 +
149 + if (!mtd_is_partition(mtd))
150 + mtd_show_device(mtd);
151 +
152 + dev_nb++;
153 + }
154 +
155 + if (!dev_nb)
156 + printf("No NAND MTD device found\n");
157 +
158 + return CMD_RET_SUCCESS;
159 +}
160 +
161 +static struct mtd_info *nand_get_curr_dev(void)
162 +{
163 + struct mtd_info *mtd, *first_dev = NULL;
164 + int err, dev_nb = 0;
165 +
166 + if (curr_dev) {
167 + mtd = get_mtd_device(curr_dev, -1);
168 + if (!IS_ERR_OR_NULL(mtd)) {
169 + __put_mtd_device(mtd);
170 + return mtd;
171 + }
172 +
173 + curr_dev = NULL;
174 + }
175 +
176 + /* Ensure all devices (and their partitions) are probed */
177 + mtd_probe_devices();
178 +
179 + mtd_for_each_device(mtd) {
180 + if (mtd->type != MTD_NANDFLASH && mtd->type != MTD_MLCNANDFLASH)
181 + continue;
182 +
183 + if (!mtd_is_partition(mtd)) {
184 + if (!first_dev)
185 + first_dev = mtd;
186 + dev_nb++;
187 + }
188 + }
189 +
190 + if (!dev_nb) {
191 + printf("No NAND MTD device found\n");
192 + return NULL;
193 + }
194 +
195 + if (dev_nb > 1) {
196 + printf("No active NAND MTD device specified\n");
197 + return NULL;
198 + }
199 +
200 + err = __get_mtd_device(first_dev);
201 + if (err) {
202 + printf("Failed to get MTD device '%s': err %d\n",
203 + first_dev->name, err);
204 + return NULL;
205 + }
206 +
207 + curr_dev = first_dev;
208 +
209 + printf("'%s' is now active device\n", first_dev->name);
210 +
211 + return curr_dev;
212 +}
213 +
214 +static struct mtd_info *nand_get_part(struct mtd_info *master,
215 + const char *name)
216 +{
217 + struct mtd_info *slave;
218 +
219 + list_for_each_entry(slave, &master->partitions, node) {
220 + if (!strcmp(slave->name, name))
221 + return slave;
222 + }
223 +
224 + return NULL;
225 +}
226 +
227 +static int do_nand_info(struct cmd_tbl *cmdtp, int flag, int argc,
228 + char *const argv[])
229 +{
230 + struct mtd_info *mtd = nand_get_curr_dev();
231 +
232 + if (!mtd)
233 + return CMD_RET_FAILURE;
234 +
235 + mtd_show_device(mtd);
236 +
237 + return 0;
238 +}
239 +
240 +static int do_nand_select(struct cmd_tbl *cmdtp, int flag, int argc,
241 + char *const argv[])
242 +{
243 + struct mtd_info *mtd, *old;
244 +
245 + if (argc < 2) {
246 + printf("MTD device name must be specified\n");
247 + return CMD_RET_USAGE;
248 + }
249 +
250 + mtd = get_mtd_device_nm(argv[1]);
251 + if (!mtd) {
252 + printf("MTD device '%s' not found\n", argv[1]);
253 + return CMD_RET_FAILURE;
254 + }
255 +
256 + if (mtd_is_partition(mtd)) {
257 + printf("Error: '%s' is a MTD partition\n", argv[1]);
258 + __put_mtd_device(mtd);
259 + return CMD_RET_FAILURE;
260 + }
261 +
262 + if (mtd->type != MTD_NANDFLASH && mtd->type != MTD_MLCNANDFLASH) {
263 + printf("Error: '%s' is not a NAND device\n", argv[1]);
264 + __put_mtd_device(mtd);
265 + return CMD_RET_FAILURE;
266 + }
267 +
268 + if (mtd == curr_dev) {
269 + __put_mtd_device(mtd);
270 + return CMD_RET_SUCCESS;
271 + }
272 +
273 + if (curr_dev) {
274 + old = get_mtd_device(curr_dev, -1);
275 + if (!IS_ERR_OR_NULL(old)) {
276 + __put_mtd_device(old);
277 + __put_mtd_device(curr_dev);
278 + }
279 +
280 + curr_dev = NULL;
281 + }
282 +
283 + curr_dev = mtd;
284 +
285 + printf("'%s' is now active device\n", curr_dev->name);
286 +
287 + return CMD_RET_SUCCESS;
288 +}
289 +
290 +static void dump_buf(const u8 *data, size_t size, u64 addr)
291 +{
292 + const u8 *p = data;
293 + u32 i, chklen;
294 +
295 + while (size) {
296 + chklen = 16;
297 + if (chklen > size)
298 + chklen = (u32)size;
299 +
300 + printf("%08llx: ", addr);
301 +
302 + for (i = 0; i < chklen; i++) {
303 + if (i && (i % 4 == 0))
304 + printf(" ");
305 +
306 + printf("%02x ", p[i]);
307 + }
308 +
309 + for (i = chklen; i < 16; i++) {
310 + if (i && (i % 4 == 0))
311 + printf(" ");
312 +
313 + printf(" ");
314 + }
315 + printf(" ");
316 +
317 + for (i = 0; i < chklen; i++) {
318 + if (p[i] < 32 || p[i] >= 0x7f)
319 + printf(".");
320 + else
321 + printf("%c", p[i]);
322 + }
323 + printf("\n");
324 +
325 + p += chklen;
326 + size -= chklen;
327 + addr += chklen;
328 + }
329 +}
330 +
331 +static int do_nand_dump(struct cmd_tbl *cmdtp, int flag, int argc,
332 + char *const argv[])
333 +{
334 + struct mtd_info *mtd = nand_get_curr_dev();
335 + struct mtd_oob_ops io_op = {};
336 + bool raw = false;
337 + int ret;
338 + u64 off;
339 + u8 *buf;
340 +
341 + if (!mtd)
342 + return CMD_RET_FAILURE;
343 +
344 + if (strstr(argv[0], ".raw"))
345 + raw = true;
346 +
347 + if (argc < 2) {
348 + printf("Dump offset must be specified\n");
349 + return CMD_RET_USAGE;
350 + }
351 +
352 + off = simple_strtoull(argv[1], NULL, 0);
353 + if (off >= mtd->size) {
354 + printf("Offset 0x%llx is larger than flash size\n", off);
355 + return CMD_RET_FAILURE;
356 + }
357 +
358 + off &= ~(u64)mtd->writesize_mask;
359 +
360 + buf = malloc(mtd->writesize + mtd->oobsize);
361 + if (!buf) {
362 + printf("Failed to allocate buffer\n");
363 + return CMD_RET_FAILURE;
364 + }
365 +
366 + io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB;
367 + io_op.len = mtd->writesize;
368 + io_op.datbuf = buf;
369 + io_op.ooblen = mtd->oobsize;
370 + io_op.oobbuf = buf + mtd->writesize;
371 +
372 + ret = mtd_read_oob(mtd, off, &io_op);
373 + if (ret < 0 && ret != -EUCLEAN && ret != -EBADMSG) {
374 + printf("Failed to read page at 0x%llx, err %d\n", off, ret);
375 + free(buf);
376 + return CMD_RET_FAILURE;
377 + }
378 +
379 + printf("Dump of %spage at 0x%llx:\n", raw ? "raw " : "", off);
380 + dump_buf(buf, mtd->writesize, off);
381 +
382 + printf("\n");
383 + printf("OOB:\n");
384 + dump_buf(buf + mtd->writesize, mtd->oobsize, 0);
385 +
386 + free(buf);
387 +
388 + return CMD_RET_SUCCESS;
389 +}
390 +
391 +static int do_nand_bad(struct cmd_tbl *cmdtp, int flag, int argc,
392 + char *const argv[])
393 +{
394 + struct mtd_info *mtd = nand_get_curr_dev();
395 + u64 off = 0;
396 +
397 + if (!mtd)
398 + return CMD_RET_FAILURE;
399 +
400 + while (off < mtd->size) {
401 + if (mtd_block_isbad(mtd, off))
402 + printf("\t%08llx\n", off);
403 +
404 + off += mtd->erasesize;
405 + }
406 +
407 + return 0;
408 +}
409 +
410 +static int do_nand_markbad(struct cmd_tbl *cmdtp, int flag, int argc,
411 + char *const argv[])
412 +{
413 + struct mtd_info *mtd = nand_get_curr_dev();
414 + u64 off;
415 + int ret;
416 +
417 + if (!mtd)
418 + return CMD_RET_FAILURE;
419 +
420 + if (argc < 2) {
421 + printf("Missing address within a block to be marked bad\n");
422 + return CMD_RET_USAGE;
423 + }
424 +
425 + off = simple_strtoull(argv[1], NULL, 0);
426 + if (off >= mtd->size) {
427 + printf("Offset 0x%llx is larger than flash size\n", off);
428 + return CMD_RET_FAILURE;
429 + }
430 +
431 + off &= ~(u64)mtd->erasesize_mask;
432 +
433 + ret = mtd_block_markbad(mtd, off);
434 +
435 + if (!ret)
436 + printf("Block at 0x%08llx has been marked bad\n", off);
437 + else
438 + printf("Failed to mark bad block at 0x%08llx\n", off);
439 +
440 + return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
441 +}
442 +
443 +static int do_nand_bitflip(struct cmd_tbl *cmdtp, int flag, int argc,
444 + char *const argv[])
445 +{
446 + struct mtd_info *mtd = nand_get_curr_dev();
447 + struct mtd_oob_ops io_op = {};
448 + u32 col, bit;
449 + bool res;
450 + u64 off;
451 + u8 *buf;
452 + int ret;
453 +
454 + if (!mtd)
455 + return CMD_RET_FAILURE;
456 +
457 + if (argc < 2) {
458 + printf("Missing address to generate bitflip\n");
459 + return CMD_RET_USAGE;
460 + }
461 +
462 + off = simple_strtoull(argv[1], NULL, 0);
463 + if (off >= mtd->size) {
464 + printf("Offset 0x%llx is larger than flash size\n", off);
465 + return CMD_RET_FAILURE;
466 + }
467 +
468 + if (argc < 3) {
469 + printf("Missing column address\n");
470 + return CMD_RET_USAGE;
471 + }
472 +
473 + col = simple_strtoul(argv[2], NULL, 0);
474 + if (col >= mtd->writesize + mtd->oobsize) {
475 + printf("Column address must be less than %u\n",
476 + mtd->writesize + mtd->oobsize);
477 + return CMD_RET_FAILURE;
478 + }
479 +
480 + if (argc < 4) {
481 + printf("Missing bit position\n");
482 + return CMD_RET_USAGE;
483 + }
484 +
485 + bit = simple_strtoul(argv[3], NULL, 0);
486 + if (bit > 7) {
487 + printf("Bit position must be less than 8\n");
488 + return CMD_RET_FAILURE;
489 + }
490 +
491 + off &= ~(u64)mtd->writesize_mask;
492 +
493 + buf = malloc(mtd->writesize + mtd->oobsize);
494 + if (!buf) {
495 + printf("Failed to allocate buffer\n");
496 + return CMD_RET_FAILURE;
497 + }
498 +
499 + io_op.mode = MTD_OPS_RAW;
500 + io_op.len = mtd->writesize;
501 + io_op.datbuf = buf;
502 + io_op.ooblen = mtd->oobsize;
503 + io_op.oobbuf = buf + mtd->writesize;
504 +
505 + ret = mtd_read_oob(mtd, off, &io_op);
506 + if (ret < 0) {
507 + printf("Failed to read page at 0x%llx, err %d\n", off, ret);
508 + free(buf);
509 + return CMD_RET_FAILURE;
510 + }
511 +
512 + if (!(buf[col] & (1 << bit))) {
513 + printf("Bit %u at byte %u is already zero\n", bit, col);
514 + free(buf);
515 + return CMD_RET_FAILURE;
516 + }
517 +
518 + buf[col] &= ~(1 << bit);
519 +
520 + memset(&io_op, 0, sizeof(io_op));
521 + io_op.mode = MTD_OPS_RAW;
522 + io_op.len = mtd->writesize;
523 + io_op.datbuf = buf;
524 + io_op.ooblen = mtd->oobsize;
525 + io_op.oobbuf = buf + mtd->writesize;
526 +
527 + ret = mtd_write_oob(mtd, off, &io_op);
528 +
529 + if (ret < 0) {
530 + printf("Failed to write page at 0x%llx, err %d\n", off, ret);
531 + return CMD_RET_FAILURE;
532 + }
533 +
534 + memset(&io_op, 0, sizeof(io_op));
535 + io_op.mode = MTD_OPS_RAW;
536 + io_op.len = mtd->writesize;
537 + io_op.datbuf = buf;
538 + io_op.ooblen = mtd->oobsize;
539 + io_op.oobbuf = buf + mtd->writesize;
540 +
541 + ret = mtd_read_oob(mtd, off, &io_op);
542 + if (ret < 0) {
543 + printf("Failed to read page at 0x%llx, err %d\n", off, ret);
544 + free(buf);
545 + return CMD_RET_FAILURE;
546 + }
547 +
548 + res = (buf[col] & (1 << bit)) == 0;
549 + free(buf);
550 +
551 + if (res) {
552 + printf("Bit %u at byte %u has been changed to 0\n", bit, col);
553 + return CMD_RET_SUCCESS;
554 + }
555 +
556 + printf("Failed to change bit %u at byte %u to 0\n", bit, col);
557 + return CMD_RET_FAILURE;
558 +}
559 +
560 +static int do_nand_erase(struct cmd_tbl *cmdtp, int flag, int argc,
561 + char *const argv[])
562 +{
563 + struct mtd_info *mtd = nand_get_curr_dev(), *part;
564 + bool spread = false, force = false;
565 + u64 off, size, end, limit;
566 + struct erase_info ei;
567 + char *ends;
568 + int ret;
569 +
570 + if (!mtd)
571 + return CMD_RET_FAILURE;
572 +
573 + if (strstr(argv[0], ".spread"))
574 + spread = true;
575 +
576 + if (strstr(argv[0], ".force"))
577 + force = true;
578 +
579 + if (spread && force) {
580 + printf("spread and force must not be set at the same time\n");
581 + return CMD_RET_FAILURE;
582 + }
583 +
584 + if (argc < 2) {
585 + printf("Erase start offset/partition must be specified\n");
586 + return CMD_RET_USAGE;
587 + }
588 +
589 + part = nand_get_part(mtd, argv[1]);
590 + if (part) {
591 + off = part->offset;
592 +
593 + if (argc < 3)
594 + size = part->size;
595 + else
596 + size = simple_strtoull(argv[2], NULL, 0);
597 +
598 + if (size > part->size) {
599 + printf("Erase end offset is larger than partition size\n");
600 + printf("Erase size reduced to 0x%llx\n", part->size);
601 +
602 + size = part->size;
603 + }
604 +
605 + limit = off + part->size;
606 + } else {
607 + off = simple_strtoull(argv[1], &ends, 0);
608 +
609 + if (ends == argv[1] || *ends) {
610 + printf("Partition '%s' not found\n", argv[1]);
611 + return CMD_RET_FAILURE;
612 + }
613 +
614 + if (off >= mtd->size) {
615 + printf("Offset 0x%llx is larger than flash size\n", off);
616 + return CMD_RET_FAILURE;
617 + }
618 +
619 + if (argc < 3) {
620 + printf("Erase size offset must be specified\n");
621 + return CMD_RET_USAGE;
622 + }
623 +
624 + size = simple_strtoull(argv[2], NULL, 0);
625 +
626 + if (off + size > mtd->size) {
627 + printf("Erase end offset is larger than flash size\n");
628 +
629 + size = mtd->size - off;
630 + printf("Erase size reduced to 0x%llx\n", size);
631 + }
632 +
633 + limit = mtd->size;
634 + }
635 +
636 + end = off + size;
637 + off &= ~(u64)mtd->erasesize_mask;
638 + end = (end + mtd->erasesize_mask) & (~(u64)mtd->erasesize_mask);
639 + size = end - off;
640 +
641 + printf("Erasing from 0x%llx to 0x%llx, size 0x%llx ...\n",
642 + off, end - 1, end - off);
643 +
644 + while (size && off < limit) {
645 + if (mtd_block_isbad(mtd, off)) {
646 + printf("Bad block at 0x%llx", off);
647 +
648 + if (spread) {
649 + printf(" ... skipped\n");
650 + off += mtd->erasesize;
651 + continue;
652 + }
653 +
654 + if (!force) {
655 + printf(" ... aborted\n");
656 + return CMD_RET_FAILURE;
657 + }
658 +
659 + printf(" ... will be force erased\n");
660 + }
661 +
662 + memset(&ei, 0, sizeof(ei));
663 +
664 + ei.mtd = mtd;
665 + ei.addr = off;
666 + ei.len = mtd->erasesize;
667 + ei.scrub = force;
668 +
669 + ret = mtd_erase(mtd, &ei);
670 + if (ret) {
671 + printf("Erase failed at 0x%llx\n", off);
672 + return CMD_RET_FAILURE;
673 + }
674 +
675 + off += mtd->erasesize;
676 + size -= mtd->erasesize;
677 + }
678 +
679 + printf("Succeeded\n");
680 +
681 + return CMD_RET_SUCCESS;
682 +}
683 +
684 +static bool is_empty_page(const u8 *buf, size_t size)
685 +{
686 + size_t i;
687 +
688 + for (i = 0; i < size; i++) {
689 + if (buf[i] != 0xff)
690 + return false;
691 + }
692 +
693 + return true;
694 +}
695 +
696 +static int do_nand_io_normal(int argc, char *const argv[])
697 +{
698 + struct mtd_info *mtd = nand_get_curr_dev(), *part;
699 + bool spread = false, force = false, raw = false, writeff = false;
700 + bool read = false, checkbad = true;
701 + struct mtd_oob_ops io_op = {};
702 + size_t size, padding, chksz;
703 + uintptr_t addr;
704 + u64 off, offp;
705 + char *ends;
706 + u8 *buf;
707 + int ret;
708 +
709 + if (!mtd)
710 + return CMD_RET_FAILURE;
711 +
712 + if (!strncmp(argv[0], "read", 4))
713 + read = true;
714 +
715 + if (strstr(argv[0], ".spread"))
716 + spread = true;
717 +
718 + if (strstr(argv[0], ".force"))
719 + force = true;
720 +
721 + if (strstr(argv[0], ".raw"))
722 + raw = true;
723 +
724 + if (strstr(argv[0], ".ff"))
725 + writeff = true;
726 +
727 + if (spread && force) {
728 + printf("spread and force must not be set at the same time\n");
729 + return CMD_RET_FAILURE;
730 + }
731 +
732 + if (argc < 2) {
733 + printf("Data address must be specified\n");
734 + return CMD_RET_USAGE;
735 + }
736 +
737 + addr = simple_strtoul(argv[1], NULL, 0);
738 +
739 + if (argc < 3) {
740 + printf("Flash address/partition must be specified\n");
741 + return CMD_RET_USAGE;
742 + }
743 +
744 + part = nand_get_part(mtd, argv[2]);
745 + if (part) {
746 + if (argc < 4) {
747 + off = 0;
748 + } else {
749 + off = simple_strtoull(argv[3], NULL, 0);
750 + if (off + part->offset >= part->size) {
751 + printf("Offset is larger than partition size\n");
752 + return CMD_RET_FAILURE;
753 + }
754 + }
755 +
756 + if (argc < 5) {
757 + size = part->size - off;
758 + } else {
759 + size = simple_strtoul(argv[4], NULL, 0);
760 + if (off + size > part->size) {
761 + printf("Data size is too large\n");
762 + return CMD_RET_FAILURE;
763 + }
764 + }
765 +
766 + off += part->offset;
767 + } else {
768 + off = simple_strtoull(argv[2], &ends, 0);
769 +
770 + if (ends == argv[1] || *ends) {
771 + printf("Partition '%s' not found\n", argv[2]);
772 + return CMD_RET_FAILURE;
773 + }
774 +
775 + if (off >= mtd->size) {
776 + printf("Offset 0x%llx is larger than flash size\n", off);
777 + return CMD_RET_FAILURE;
778 + }
779 +
780 + if (argc < 4) {
781 + printf("Data size must be specified\n");
782 + return CMD_RET_USAGE;
783 + }
784 +
785 + size = simple_strtoul(argv[3], NULL, 0);
786 + if (off + size > mtd->size) {
787 + printf("Data size is too large\n");
788 + return CMD_RET_FAILURE;
789 + }
790 + }
791 +
792 + buf = malloc(mtd->writesize);
793 + if (!buf) {
794 + printf("Failed to allocate buffer\n");
795 + return CMD_RET_FAILURE;
796 + }
797 +
798 + printf("%s from 0x%llx to 0x%llx, size 0x%zx ...\n",
799 + read ? "Reading" : "Writing", off, off + size - 1, size);
800 +
801 + while (size && off < mtd->size) {
802 + if (checkbad || !(off & mtd->erasesize_mask)) {
803 + offp = off & ~(u64)mtd->erasesize_mask;
804 +
805 + if (mtd_block_isbad(mtd, offp)) {
806 + printf("Bad block at 0x%llx", offp);
807 +
808 + if (spread) {
809 + printf(" ... skipped\n");
810 + off += mtd->erasesize;
811 + checkbad = true;
812 + continue;
813 + }
814 +
815 + if (!force) {
816 + printf(" ... aborted\n");
817 + goto err_out;
818 + }
819 +
820 + printf(" ... continue\n");
821 + }
822 +
823 + checkbad = false;
824 + }
825 +
826 + padding = off & mtd->writesize_mask;
827 + chksz = mtd->writesize - padding;
828 + chksz = min_t(size_t, chksz, size);
829 +
830 + offp = off & ~(u64)mtd->writesize_mask;
831 +
832 + memset(&io_op, 0, sizeof(io_op));
833 + io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB;
834 + io_op.len = mtd->writesize;
835 +
836 + if (chksz < mtd->writesize)
837 + io_op.datbuf = buf;
838 + else
839 + io_op.datbuf = (void *)addr;
840 +
841 + if (read) {
842 + ret = mtd_read_oob(mtd, offp, &io_op);
843 + if (ret && ret != -EUCLEAN && ret != -EBADMSG)
844 + goto io_err;
845 +
846 + if (chksz < mtd->writesize)
847 + memcpy((void *)addr, buf + padding, chksz);
848 + } else {
849 + if (chksz < mtd->writesize) {
850 + memset(buf, 0xff, mtd->writesize);
851 + memcpy(buf + padding, (void *)addr, chksz);
852 + }
853 +
854 + if (is_empty_page(io_op.datbuf, io_op.len) && !writeff)
855 + ret = 0;
856 + else
857 + ret = mtd_write_oob(mtd, offp, &io_op);
858 +
859 + if (ret)
860 + goto io_err;
861 + }
862 +
863 + size -= chksz;
864 + addr += chksz;
865 + off += chksz;
866 + }
867 +
868 + if (!size) {
869 + printf("Succeeded\n");
870 + ret = CMD_RET_SUCCESS;
871 + goto out;
872 + }
873 +
874 + printf("0x%zx byte%s remained for %s\n", size, size > 1 ? "s" : "",
875 + read ? "read" : "write");
876 + goto err_out;
877 +
878 +io_err:
879 + printf("%s error %d at 0x%llx\n", read ? "Read" : "Write", ret, offp);
880 +
881 +err_out:
882 + ret = CMD_RET_FAILURE;
883 +
884 +out:
885 + free(buf);
886 + return ret;
887 +}
888 +
889 +static int do_nand_io_page(int argc, char *const argv[])
890 +{
891 + struct mtd_info *mtd = nand_get_curr_dev(), *part;
892 + bool spread = false, force = false, raw = false, autooob = false;
893 + bool read = false, checkbad = true, writeff = false;
894 + struct mtd_oob_ops io_op = {};
895 + uintptr_t addr;
896 + u64 off, offp;
897 + char *ends;
898 + u32 count;
899 + int ret;
900 +
901 + if (!mtd)
902 + return CMD_RET_FAILURE;
903 +
904 + if (!strncmp(argv[0], "read", 4))
905 + read = true;
906 +
907 + if (strstr(argv[0], ".spread"))
908 + spread = true;
909 +
910 + if (strstr(argv[0], ".force"))
911 + force = true;
912 +
913 + if (strstr(argv[0], ".raw"))
914 + raw = true;
915 +
916 + if (strstr(argv[0], ".auto"))
917 + autooob = true;
918 +
919 + if (spread && force) {
920 + printf("spread and force must not be set at the same time\n");
921 + return CMD_RET_FAILURE;
922 + }
923 +
924 + if (raw && autooob) {
925 + printf("raw and auto must not be set at the same time\n");
926 + return CMD_RET_FAILURE;
927 + }
928 +
929 + if (argc < 2) {
930 + printf("Data address must be specified\n");
931 + return CMD_RET_USAGE;
932 + }
933 +
934 + addr = simple_strtoul(argv[1], NULL, 0);
935 +
936 + if (argc < 3) {
937 + printf("Flash address/partition must be specified\n");
938 + return CMD_RET_USAGE;
939 + }
940 +
941 + part = nand_get_part(mtd, argv[2]);
942 + if (part) {
943 + if (argc < 4) {
944 + printf("Partition offset / page count must be specified\n");
945 + return CMD_RET_USAGE;
946 + }
947 +
948 + if (argc < 5) {
949 + off = 0;
950 +
951 + count = simple_strtoul(argv[3], NULL, 0);
952 + if (part->offset + count * mtd->writesize > part->size) {
953 + printf("Page count exceeds partition size\n");
954 + return CMD_RET_FAILURE;
955 + }
956 + } else {
957 + off = simple_strtoull(argv[3], NULL, 0);
958 + if (off >= part->size) {
959 + printf("Offset 0x%llx is larger than partition size\n", off);
960 + return CMD_RET_FAILURE;
961 + }
962 +
963 + off &= ~(u64)mtd->writesize_mask;
964 +
965 + count = simple_strtoul(argv[4], NULL, 0);
966 + if (part->offset + off + count * mtd->writesize > part->size) {
967 + printf("Page count exceeds partition size\n");
968 + return CMD_RET_FAILURE;
969 + }
970 + }
971 +
972 + off += part->offset;
973 + } else {
974 + off = simple_strtoull(argv[2], &ends, 0);
975 +
976 + if (ends == argv[1] || *ends) {
977 + printf("Partition '%s' not found\n", argv[2]);
978 + return CMD_RET_FAILURE;
979 + }
980 +
981 + if (off >= mtd->size) {
982 + printf("Offset 0x%llx is larger than flash size\n", off);
983 + return CMD_RET_FAILURE;
984 + }
985 +
986 + off &= ~(u64)mtd->writesize_mask;
987 +
988 + if (argc < 4) {
989 + printf("Page count must be specified\n");
990 + return CMD_RET_USAGE;
991 + }
992 +
993 + count = simple_strtoul(argv[3], NULL, 0);
994 + if (off + count * mtd->writesize > mtd->size) {
995 + printf("Page count exceeds flash size\n");
996 + return CMD_RET_FAILURE;
997 + }
998 + }
999 +
1000 + printf("%s from 0x%llx to 0x%llx (+%u), count %u ...\n",
1001 + read ? "Reading" : "Writing", off,
1002 + off + count * mtd->writesize - 1, mtd->oobsize, count);
1003 +
1004 + while (count && off < mtd->size) {
1005 + if (checkbad || !(off & mtd->erasesize_mask)) {
1006 + offp = off & ~(u64)mtd->erasesize_mask;
1007 +
1008 + if (mtd_block_isbad(mtd, offp)) {
1009 + printf("Bad block at 0x%llx", offp);
1010 +
1011 + if (spread) {
1012 + printf(" ... skipped\n");
1013 + off += mtd->erasesize;
1014 + checkbad = true;
1015 + continue;
1016 + }
1017 +
1018 + if (!force) {
1019 + printf(" ... aborted\n");
1020 + return CMD_RET_FAILURE;
1021 + }
1022 +
1023 + printf(" ... continue\n");
1024 + }
1025 +
1026 + checkbad = false;
1027 + }
1028 +
1029 + memset(&io_op, 0, sizeof(io_op));
1030 +
1031 + if (raw)
1032 + io_op.mode = MTD_OPS_RAW;
1033 + else if (autooob)
1034 + io_op.mode = MTD_OPS_AUTO_OOB;
1035 + else
1036 + io_op.mode = MTD_OPS_PLACE_OOB;
1037 +
1038 + io_op.len = mtd->writesize;
1039 + io_op.ooblen = mtd->oobsize;
1040 + io_op.datbuf = (void *)addr;
1041 + io_op.oobbuf = io_op.datbuf + mtd->writesize;
1042 +
1043 + if (read) {
1044 + ret = mtd_read_oob(mtd, off, &io_op);
1045 + if (ret && ret != -EUCLEAN && ret != -EBADMSG)
1046 + goto io_err;
1047 + } else {
1048 + if (is_empty_page((void *)addr, mtd->writesize + mtd->oobsize) && !writeff)
1049 + ret = 0;
1050 + else
1051 + ret = mtd_write_oob(mtd, off, &io_op);
1052 +
1053 + if (ret)
1054 + goto io_err;
1055 + }
1056 +
1057 + count--;
1058 + addr += mtd->writesize + mtd->oobsize;
1059 + off += mtd->writesize;
1060 + }
1061 +
1062 + if (!count) {
1063 + printf("Succeeded\n");
1064 + return CMD_RET_SUCCESS;
1065 + }
1066 +
1067 + printf("%u page%s remained for %s\n", count, count > 1 ? "s" : "",
1068 + read ? "read" : "write");
1069 + return CMD_RET_FAILURE;
1070 +
1071 +io_err:
1072 + printf("%s error %d at 0x%llx\n", read ? "Read" : "Write", ret, off);
1073 + return CMD_RET_FAILURE;
1074 +}
1075 +
1076 +static int do_nand_io(struct cmd_tbl *cmdtp, int flag, int argc,
1077 + char *const argv[])
1078 +{
1079 + if (strstr(argv[0], ".oob"))
1080 + return do_nand_io_page(argc, argv);
1081 +
1082 + return do_nand_io_normal(argc, argv);
1083 +}
1084 +
1085 +#ifdef CONFIG_SYS_LONGHELP
1086 +static char nand_help_text[] =
1087 + "- NAND flash R/W and debugging utility\n"
1088 + "nand list\n"
1089 + "nand info - Show active NAND devices\n"
1090 + "nand select <name> - Select active NAND devices\n"
1091 + "nand dump[.raw] <off>\n"
1092 + "nand bad\n"
1093 + "nand markbad <off>\n"
1094 + "nand bitflip <off> <col> <bit>\n"
1095 + "nand erase[.spread|.force] [<off> <size>|<part> [<size>]]\n"
1096 + "nand read[.spread|.force][.raw] <addr> <off> <size>\n"
1097 + " <addr> <part> [<off> [<size>]]\n"
1098 + "nand write[.spread|.force][.raw][.ff] <addr> <off> <size>\n"
1099 + " <addr> <part> [<off> [<size>]]\n"
1100 + "nand read.oob[.spread|.force][.raw|.auto] <addr> <off> <count>\n"
1101 + " <addr> <part> [<off>] <count>\n"
1102 + "nand write.oob[.spread|.force][.raw|.auto][.ff] <addr> <off> <count>\n"
1103 + " <addr> <part> [<off>] <count>\n";
1104 +#endif
1105 +
1106 +U_BOOT_CMD_WITH_SUBCMDS(nand, "NAND utility",
1107 + nand_help_text,
1108 + U_BOOT_SUBCMD_MKENT(list, 1, 0, do_nand_list),
1109 + U_BOOT_SUBCMD_MKENT(info, 1, 0, do_nand_info),
1110 + U_BOOT_SUBCMD_MKENT(select, 2, 0, do_nand_select),
1111 + U_BOOT_SUBCMD_MKENT(dump, 2, 0, do_nand_dump),
1112 + U_BOOT_SUBCMD_MKENT(bad, 1, 0, do_nand_bad),
1113 + U_BOOT_SUBCMD_MKENT(markbad, 2, 0, do_nand_markbad),
1114 + U_BOOT_SUBCMD_MKENT(bitflip, 4, 0, do_nand_bitflip),
1115 + U_BOOT_SUBCMD_MKENT(erase, 3, 0, do_nand_erase),
1116 + U_BOOT_SUBCMD_MKENT(read, 5, 0, do_nand_io),
1117 + U_BOOT_SUBCMD_MKENT(write, 5, 0, do_nand_io)
1118 +);