12e192b24SSimon Glass /*
22e192b24SSimon Glass * Driver for NAND support, Rick Bronson
32e192b24SSimon Glass * borrowed heavily from:
42e192b24SSimon Glass * (c) 1999 Machine Vision Holdings, Inc.
52e192b24SSimon Glass * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
62e192b24SSimon Glass *
72e192b24SSimon Glass * Ported 'dynenv' to 'nand env.oob' command
82e192b24SSimon Glass * (C) 2010 Nanometrics, Inc.
92e192b24SSimon Glass * 'dynenv' -- Dynamic environment offset in NAND OOB
102e192b24SSimon Glass * (C) Copyright 2006-2007 OpenMoko, Inc.
112e192b24SSimon Glass * Added 16-bit nand support
122e192b24SSimon Glass * (C) 2004 Texas Instruments
132e192b24SSimon Glass *
142e192b24SSimon Glass * Copyright 2010, 2012 Freescale Semiconductor
152e192b24SSimon Glass * The portions of this file whose copyright is held by Freescale and which
162e192b24SSimon Glass * are not considered a derived work of GPL v2-only code may be distributed
172e192b24SSimon Glass * and/or modified under the terms of the GNU General Public License as
182e192b24SSimon Glass * published by the Free Software Foundation; either version 2 of the
192e192b24SSimon Glass * License, or (at your option) any later version.
202e192b24SSimon Glass */
212e192b24SSimon Glass
222e192b24SSimon Glass #include <common.h>
232e192b24SSimon Glass #include <linux/mtd/mtd.h>
242e192b24SSimon Glass #include <command.h>
252e192b24SSimon Glass #include <console.h>
262e192b24SSimon Glass #include <watchdog.h>
272e192b24SSimon Glass #include <malloc.h>
282e192b24SSimon Glass #include <asm/byteorder.h>
292e192b24SSimon Glass #include <jffs2/jffs2.h>
302e192b24SSimon Glass #include <nand.h>
312e192b24SSimon Glass
322e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS)
332e192b24SSimon Glass
342e192b24SSimon Glass /* partition handling routines */
352e192b24SSimon Glass int mtdparts_init(void);
362e192b24SSimon Glass int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num);
372e192b24SSimon Glass int find_dev_and_part(const char *id, struct mtd_device **dev,
382e192b24SSimon Glass u8 *part_num, struct part_info **part);
392e192b24SSimon Glass #endif
402e192b24SSimon Glass
nand_dump(struct mtd_info * mtd,ulong off,int only_oob,int repeat)41151c06ecSScott Wood static int nand_dump(struct mtd_info *mtd, ulong off, int only_oob,
42151c06ecSScott Wood int repeat)
432e192b24SSimon Glass {
442e192b24SSimon Glass int i;
452e192b24SSimon Glass u_char *datbuf, *oobbuf, *p;
462e192b24SSimon Glass static loff_t last;
472e192b24SSimon Glass int ret = 0;
482e192b24SSimon Glass
492e192b24SSimon Glass if (repeat)
50151c06ecSScott Wood off = last + mtd->writesize;
512e192b24SSimon Glass
522e192b24SSimon Glass last = off;
532e192b24SSimon Glass
54151c06ecSScott Wood datbuf = memalign(ARCH_DMA_MINALIGN, mtd->writesize);
552e192b24SSimon Glass if (!datbuf) {
562e192b24SSimon Glass puts("No memory for page buffer\n");
572e192b24SSimon Glass return 1;
582e192b24SSimon Glass }
592e192b24SSimon Glass
60151c06ecSScott Wood oobbuf = memalign(ARCH_DMA_MINALIGN, mtd->oobsize);
612e192b24SSimon Glass if (!oobbuf) {
622e192b24SSimon Glass puts("No memory for page buffer\n");
632e192b24SSimon Glass ret = 1;
642e192b24SSimon Glass goto free_dat;
652e192b24SSimon Glass }
66151c06ecSScott Wood off &= ~(mtd->writesize - 1);
672e192b24SSimon Glass loff_t addr = (loff_t) off;
682e192b24SSimon Glass struct mtd_oob_ops ops;
692e192b24SSimon Glass memset(&ops, 0, sizeof(ops));
702e192b24SSimon Glass ops.datbuf = datbuf;
712e192b24SSimon Glass ops.oobbuf = oobbuf;
72151c06ecSScott Wood ops.len = mtd->writesize;
73151c06ecSScott Wood ops.ooblen = mtd->oobsize;
742e192b24SSimon Glass ops.mode = MTD_OPS_RAW;
75151c06ecSScott Wood i = mtd_read_oob(mtd, addr, &ops);
762e192b24SSimon Glass if (i < 0) {
772e192b24SSimon Glass printf("Error (%d) reading page %08lx\n", i, off);
782e192b24SSimon Glass ret = 1;
792e192b24SSimon Glass goto free_all;
802e192b24SSimon Glass }
812e192b24SSimon Glass printf("Page %08lx dump:\n", off);
822e192b24SSimon Glass
832e192b24SSimon Glass if (!only_oob) {
84151c06ecSScott Wood i = mtd->writesize >> 4;
852e192b24SSimon Glass p = datbuf;
862e192b24SSimon Glass
872e192b24SSimon Glass while (i--) {
882e192b24SSimon Glass printf("\t%02x %02x %02x %02x %02x %02x %02x %02x"
892e192b24SSimon Glass " %02x %02x %02x %02x %02x %02x %02x %02x\n",
902e192b24SSimon Glass p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
912e192b24SSimon Glass p[8], p[9], p[10], p[11], p[12], p[13], p[14],
922e192b24SSimon Glass p[15]);
932e192b24SSimon Glass p += 16;
942e192b24SSimon Glass }
952e192b24SSimon Glass }
962e192b24SSimon Glass
972e192b24SSimon Glass puts("OOB:\n");
98151c06ecSScott Wood i = mtd->oobsize >> 3;
992e192b24SSimon Glass p = oobbuf;
1002e192b24SSimon Glass while (i--) {
1012e192b24SSimon Glass printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n",
1022e192b24SSimon Glass p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
1032e192b24SSimon Glass p += 8;
1042e192b24SSimon Glass }
1052e192b24SSimon Glass
1062e192b24SSimon Glass free_all:
1072e192b24SSimon Glass free(oobbuf);
1082e192b24SSimon Glass free_dat:
1092e192b24SSimon Glass free(datbuf);
1102e192b24SSimon Glass
1112e192b24SSimon Glass return ret;
1122e192b24SSimon Glass }
1132e192b24SSimon Glass
1142e192b24SSimon Glass /* ------------------------------------------------------------------------- */
1152e192b24SSimon Glass
set_dev(int dev)1162e192b24SSimon Glass static int set_dev(int dev)
1172e192b24SSimon Glass {
118ad92dff2SMugunthan V N struct mtd_info *mtd = get_nand_dev_by_index(dev);
119ad92dff2SMugunthan V N
120ad92dff2SMugunthan V N if (!mtd)
121ad92dff2SMugunthan V N return -ENODEV;
1222e192b24SSimon Glass
1232e192b24SSimon Glass if (nand_curr_device == dev)
1242e192b24SSimon Glass return 0;
1252e192b24SSimon Glass
126ad92dff2SMugunthan V N printf("Device %d: %s", dev, mtd->name);
1272e192b24SSimon Glass puts("... is now current device\n");
1282e192b24SSimon Glass nand_curr_device = dev;
1292e192b24SSimon Glass
1302e192b24SSimon Glass #ifdef CONFIG_SYS_NAND_SELECT_DEVICE
1316308e71bSGrygorii Strashko board_nand_select_device(mtd_to_nand(mtd), dev);
1322e192b24SSimon Glass #endif
1332e192b24SSimon Glass
1342e192b24SSimon Glass return 0;
1352e192b24SSimon Glass }
1362e192b24SSimon Glass
1372e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK
print_status(ulong start,ulong end,ulong erasesize,int status)1382e192b24SSimon Glass static void print_status(ulong start, ulong end, ulong erasesize, int status)
1392e192b24SSimon Glass {
1402e192b24SSimon Glass /*
1412e192b24SSimon Glass * Micron NAND flash (e.g. MT29F4G08ABADAH4) BLOCK LOCK READ STATUS is
1422e192b24SSimon Glass * not the same as others. Instead of bit 1 being lock, it is
1432e192b24SSimon Glass * #lock_tight. To make the driver support either format, ignore bit 1
1442e192b24SSimon Glass * and use only bit 0 and bit 2.
1452e192b24SSimon Glass */
1462e192b24SSimon Glass printf("%08lx - %08lx: %08lx blocks %s%s%s\n",
1472e192b24SSimon Glass start,
1482e192b24SSimon Glass end - 1,
1492e192b24SSimon Glass (end - start) / erasesize,
1502e192b24SSimon Glass ((status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""),
1512e192b24SSimon Glass (!(status & NAND_LOCK_STATUS_UNLOCK) ? "LOCK " : ""),
1522e192b24SSimon Glass ((status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : ""));
1532e192b24SSimon Glass }
1542e192b24SSimon Glass
do_nand_status(struct mtd_info * mtd)155151c06ecSScott Wood static void do_nand_status(struct mtd_info *mtd)
1562e192b24SSimon Glass {
1572e192b24SSimon Glass ulong block_start = 0;
1582e192b24SSimon Glass ulong off;
1592e192b24SSimon Glass int last_status = -1;
1602e192b24SSimon Glass
16117cb4b8fSScott Wood struct nand_chip *nand_chip = mtd_to_nand(mtd);
1622e192b24SSimon Glass /* check the WP bit */
163151c06ecSScott Wood nand_chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
1642e192b24SSimon Glass printf("device is %swrite protected\n",
165151c06ecSScott Wood (nand_chip->read_byte(mtd) & 0x80 ?
1662e192b24SSimon Glass "NOT " : ""));
1672e192b24SSimon Glass
168151c06ecSScott Wood for (off = 0; off < mtd->size; off += mtd->erasesize) {
169151c06ecSScott Wood int s = nand_get_lock_status(mtd, off);
1702e192b24SSimon Glass
1712e192b24SSimon Glass /* print message only if status has changed */
1722e192b24SSimon Glass if (s != last_status && off != 0) {
173151c06ecSScott Wood print_status(block_start, off, mtd->erasesize,
1742e192b24SSimon Glass last_status);
1752e192b24SSimon Glass block_start = off;
1762e192b24SSimon Glass }
1772e192b24SSimon Glass last_status = s;
1782e192b24SSimon Glass }
1792e192b24SSimon Glass /* Print the last block info */
180151c06ecSScott Wood print_status(block_start, off, mtd->erasesize, last_status);
1812e192b24SSimon Glass }
1822e192b24SSimon Glass #endif
1832e192b24SSimon Glass
1842e192b24SSimon Glass #ifdef CONFIG_ENV_OFFSET_OOB
1852e192b24SSimon Glass unsigned long nand_env_oob_offset;
1862e192b24SSimon Glass
do_nand_env_oob(cmd_tbl_t * cmdtp,int argc,char * const argv[])1872e192b24SSimon Glass int do_nand_env_oob(cmd_tbl_t *cmdtp, int argc, char *const argv[])
1882e192b24SSimon Glass {
1892e192b24SSimon Glass int ret;
1902e192b24SSimon Glass uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)];
191ad92dff2SMugunthan V N struct mtd_info *mtd = get_nand_dev_by_index(0);
1922e192b24SSimon Glass char *cmd = argv[1];
1932e192b24SSimon Glass
1948b7d5124SScott Wood if (CONFIG_SYS_MAX_NAND_DEVICE == 0 || !mtd) {
1952e192b24SSimon Glass puts("no devices available\n");
1962e192b24SSimon Glass return 1;
1972e192b24SSimon Glass }
1982e192b24SSimon Glass
1992e192b24SSimon Glass set_dev(0);
2002e192b24SSimon Glass
2012e192b24SSimon Glass if (!strcmp(cmd, "get")) {
202151c06ecSScott Wood ret = get_nand_env_oob(mtd, &nand_env_oob_offset);
2032e192b24SSimon Glass if (ret)
2042e192b24SSimon Glass return 1;
2052e192b24SSimon Glass
2062e192b24SSimon Glass printf("0x%08lx\n", nand_env_oob_offset);
2072e192b24SSimon Glass } else if (!strcmp(cmd, "set")) {
2082e192b24SSimon Glass loff_t addr;
2092e192b24SSimon Glass loff_t maxsize;
2102e192b24SSimon Glass struct mtd_oob_ops ops;
2112e192b24SSimon Glass int idx = 0;
2122e192b24SSimon Glass
2132e192b24SSimon Glass if (argc < 3)
2142e192b24SSimon Glass goto usage;
2152e192b24SSimon Glass
216ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(idx);
2172e192b24SSimon Glass /* We don't care about size, or maxsize. */
2182e192b24SSimon Glass if (mtd_arg_off(argv[2], &idx, &addr, &maxsize, &maxsize,
219ad92dff2SMugunthan V N MTD_DEV_TYPE_NAND, mtd->size)) {
2202e192b24SSimon Glass puts("Offset or partition name expected\n");
2212e192b24SSimon Glass return 1;
2222e192b24SSimon Glass }
2232e192b24SSimon Glass if (set_dev(idx)) {
2242e192b24SSimon Glass puts("Offset or partition name expected\n");
2252e192b24SSimon Glass return 1;
2262e192b24SSimon Glass }
2272e192b24SSimon Glass
2282e192b24SSimon Glass if (idx != 0) {
2292e192b24SSimon Glass puts("Partition not on first NAND device\n");
2302e192b24SSimon Glass return 1;
2312e192b24SSimon Glass }
2322e192b24SSimon Glass
233151c06ecSScott Wood if (mtd->oobavail < ENV_OFFSET_SIZE) {
2342e192b24SSimon Glass printf("Insufficient available OOB bytes:\n"
2352e192b24SSimon Glass "%d OOB bytes available but %d required for "
2362e192b24SSimon Glass "env.oob support\n",
237151c06ecSScott Wood mtd->oobavail, ENV_OFFSET_SIZE);
2382e192b24SSimon Glass return 1;
2392e192b24SSimon Glass }
2402e192b24SSimon Glass
241151c06ecSScott Wood if ((addr & (mtd->erasesize - 1)) != 0) {
2422e192b24SSimon Glass printf("Environment offset must be block-aligned\n");
2432e192b24SSimon Glass return 1;
2442e192b24SSimon Glass }
2452e192b24SSimon Glass
2462e192b24SSimon Glass ops.datbuf = NULL;
2472e192b24SSimon Glass ops.mode = MTD_OOB_AUTO;
2482e192b24SSimon Glass ops.ooboffs = 0;
2492e192b24SSimon Glass ops.ooblen = ENV_OFFSET_SIZE;
2502e192b24SSimon Glass ops.oobbuf = (void *) oob_buf;
2512e192b24SSimon Glass
2522e192b24SSimon Glass oob_buf[0] = ENV_OOB_MARKER;
253151c06ecSScott Wood oob_buf[1] = addr / mtd->erasesize;
2542e192b24SSimon Glass
255151c06ecSScott Wood ret = mtd->write_oob(mtd, ENV_OFFSET_SIZE, &ops);
2562e192b24SSimon Glass if (ret) {
2572e192b24SSimon Glass printf("Error writing OOB block 0\n");
2582e192b24SSimon Glass return ret;
2592e192b24SSimon Glass }
2602e192b24SSimon Glass
261151c06ecSScott Wood ret = get_nand_env_oob(mtd, &nand_env_oob_offset);
2622e192b24SSimon Glass if (ret) {
2632e192b24SSimon Glass printf("Error reading env offset in OOB\n");
2642e192b24SSimon Glass return ret;
2652e192b24SSimon Glass }
2662e192b24SSimon Glass
2672e192b24SSimon Glass if (addr != nand_env_oob_offset) {
2682e192b24SSimon Glass printf("Verification of env offset in OOB failed: "
2692e192b24SSimon Glass "0x%08llx expected but got 0x%08lx\n",
2702e192b24SSimon Glass (unsigned long long)addr, nand_env_oob_offset);
2712e192b24SSimon Glass return 1;
2722e192b24SSimon Glass }
2732e192b24SSimon Glass } else {
2742e192b24SSimon Glass goto usage;
2752e192b24SSimon Glass }
2762e192b24SSimon Glass
2772e192b24SSimon Glass return ret;
2782e192b24SSimon Glass
2792e192b24SSimon Glass usage:
2802e192b24SSimon Glass return CMD_RET_USAGE;
2812e192b24SSimon Glass }
2822e192b24SSimon Glass
2832e192b24SSimon Glass #endif
2842e192b24SSimon Glass
nand_print_and_set_info(int idx)2852e192b24SSimon Glass static void nand_print_and_set_info(int idx)
2862e192b24SSimon Glass {
287ad92dff2SMugunthan V N struct mtd_info *mtd;
288ad92dff2SMugunthan V N struct nand_chip *chip;
2892e192b24SSimon Glass
290ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(idx);
291ad92dff2SMugunthan V N if (!mtd)
292ad92dff2SMugunthan V N return;
293ad92dff2SMugunthan V N
294ad92dff2SMugunthan V N chip = mtd_to_nand(mtd);
2952e192b24SSimon Glass printf("Device %d: ", idx);
2962e192b24SSimon Glass if (chip->numchips > 1)
2972e192b24SSimon Glass printf("%dx ", chip->numchips);
2982e192b24SSimon Glass printf("%s, sector size %u KiB\n",
299151c06ecSScott Wood mtd->name, mtd->erasesize >> 10);
300151c06ecSScott Wood printf(" Page size %8d b\n", mtd->writesize);
301151c06ecSScott Wood printf(" OOB size %8d b\n", mtd->oobsize);
302151c06ecSScott Wood printf(" Erase size %8d b\n", mtd->erasesize);
3032e192b24SSimon Glass printf(" subpagesize %8d b\n", chip->subpagesize);
30466dc09c5SLothar Waßmann printf(" options 0x%08x\n", chip->options);
30566dc09c5SLothar Waßmann printf(" bbt options 0x%08x\n", chip->bbt_options);
3062e192b24SSimon Glass
3072e192b24SSimon Glass /* Set geometry info */
308018f5303SSimon Glass env_set_hex("nand_writesize", mtd->writesize);
309018f5303SSimon Glass env_set_hex("nand_oobsize", mtd->oobsize);
310018f5303SSimon Glass env_set_hex("nand_erasesize", mtd->erasesize);
3112e192b24SSimon Glass }
3122e192b24SSimon Glass
mtd_is_aligned_with_block_size(struct mtd_info * mtd,u64 size)313*6aef6166SJon Lin static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
314*6aef6166SJon Lin {
315*6aef6166SJon Lin return !do_div(size, mtd->erasesize);
316*6aef6166SJon Lin }
317*6aef6166SJon Lin
raw_access(struct mtd_info * mtd,ulong addr,loff_t off,ulong count,int read,int no_verify)318151c06ecSScott Wood static int raw_access(struct mtd_info *mtd, ulong addr, loff_t off,
3192dc3c483SBoris Brezillon ulong count, int read, int no_verify)
3202e192b24SSimon Glass {
3212e192b24SSimon Glass int ret = 0;
3222e192b24SSimon Glass
323*6aef6166SJon Lin while (count && (off + mtd->writesize) <= mtd->size) {
324*6aef6166SJon Lin /*
325*6aef6166SJon Lin * Skipping bad block interfaces meets practical usage
326*6aef6166SJon Lin * requirements
327*6aef6166SJon Lin */
328*6aef6166SJon Lin if (mtd_is_aligned_with_block_size(mtd, off) &&
329*6aef6166SJon Lin mtd_block_isbad(mtd, off)) {
330*6aef6166SJon Lin printf("raw_access skip bad block 0x%08llx\n",
331*6aef6166SJon Lin off & ~(mtd->erasesize - 1));
332*6aef6166SJon Lin off += mtd->erasesize;
333*6aef6166SJon Lin continue;
334*6aef6166SJon Lin }
335*6aef6166SJon Lin
3362e192b24SSimon Glass /* Raw access */
3372e192b24SSimon Glass mtd_oob_ops_t ops = {
3382e192b24SSimon Glass .datbuf = (u8 *)addr,
339151c06ecSScott Wood .oobbuf = ((u8 *)addr) + mtd->writesize,
340151c06ecSScott Wood .len = mtd->writesize,
341151c06ecSScott Wood .ooblen = mtd->oobsize,
3422e192b24SSimon Glass .mode = MTD_OPS_RAW
3432e192b24SSimon Glass };
3442e192b24SSimon Glass
3452e192b24SSimon Glass if (read) {
346151c06ecSScott Wood ret = mtd_read_oob(mtd, off, &ops);
3472e192b24SSimon Glass } else {
348151c06ecSScott Wood ret = mtd_write_oob(mtd, off, &ops);
3492dc3c483SBoris Brezillon if (!ret && !no_verify)
350151c06ecSScott Wood ret = nand_verify_page_oob(mtd, &ops, off);
3512e192b24SSimon Glass }
3522e192b24SSimon Glass
3532e192b24SSimon Glass if (ret) {
3542e192b24SSimon Glass printf("%s: error at offset %llx, ret %d\n",
3552e192b24SSimon Glass __func__, (long long)off, ret);
3562e192b24SSimon Glass break;
3572e192b24SSimon Glass }
3582e192b24SSimon Glass
359151c06ecSScott Wood addr += mtd->writesize + mtd->oobsize;
360151c06ecSScott Wood off += mtd->writesize;
361*6aef6166SJon Lin count--;
3622e192b24SSimon Glass }
3632e192b24SSimon Glass
3642e192b24SSimon Glass return ret;
3652e192b24SSimon Glass }
3662e192b24SSimon Glass
3672e192b24SSimon Glass /* Adjust a chip/partition size down for bad blocks so we don't
3682e192b24SSimon Glass * read/write past the end of a chip/partition by accident.
3692e192b24SSimon Glass */
adjust_size_for_badblocks(loff_t * size,loff_t offset,int dev)3702e192b24SSimon Glass static void adjust_size_for_badblocks(loff_t *size, loff_t offset, int dev)
3712e192b24SSimon Glass {
3722e192b24SSimon Glass /* We grab the nand info object here fresh because this is usually
3732e192b24SSimon Glass * called after arg_off_size() which can change the value of dev.
3742e192b24SSimon Glass */
375ad92dff2SMugunthan V N struct mtd_info *mtd = get_nand_dev_by_index(dev);
3762e192b24SSimon Glass loff_t maxoffset = offset + *size;
3772e192b24SSimon Glass int badblocks = 0;
3782e192b24SSimon Glass
3792e192b24SSimon Glass /* count badblocks in NAND from offset to offset + size */
380151c06ecSScott Wood for (; offset < maxoffset; offset += mtd->erasesize) {
381151c06ecSScott Wood if (nand_block_isbad(mtd, offset))
3822e192b24SSimon Glass badblocks++;
3832e192b24SSimon Glass }
3842e192b24SSimon Glass /* adjust size if any bad blocks found */
3852e192b24SSimon Glass if (badblocks) {
386151c06ecSScott Wood *size -= badblocks * mtd->erasesize;
3872e192b24SSimon Glass printf("size adjusted to 0x%llx (%d bad blocks)\n",
3882e192b24SSimon Glass (unsigned long long)*size, badblocks);
3892e192b24SSimon Glass }
3902e192b24SSimon Glass }
3912e192b24SSimon Glass
do_nand(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])3922e192b24SSimon Glass static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
3932e192b24SSimon Glass {
3942e192b24SSimon Glass int i, ret = 0;
3952e192b24SSimon Glass ulong addr;
3962e192b24SSimon Glass loff_t off, size, maxsize;
3972e192b24SSimon Glass char *cmd, *s;
398151c06ecSScott Wood struct mtd_info *mtd;
3992e192b24SSimon Glass #ifdef CONFIG_SYS_NAND_QUIET
4002e192b24SSimon Glass int quiet = CONFIG_SYS_NAND_QUIET;
4012e192b24SSimon Glass #else
4022e192b24SSimon Glass int quiet = 0;
4032e192b24SSimon Glass #endif
40400caae6dSSimon Glass const char *quiet_str = env_get("quiet");
4052e192b24SSimon Glass int dev = nand_curr_device;
4062e192b24SSimon Glass int repeat = flag & CMD_FLAG_REPEAT;
4072e192b24SSimon Glass
4082e192b24SSimon Glass /* at least two arguments please */
4092e192b24SSimon Glass if (argc < 2)
4102e192b24SSimon Glass goto usage;
4112e192b24SSimon Glass
4122e192b24SSimon Glass if (quiet_str)
4132e192b24SSimon Glass quiet = simple_strtoul(quiet_str, NULL, 0) != 0;
4142e192b24SSimon Glass
4152e192b24SSimon Glass cmd = argv[1];
4162e192b24SSimon Glass
4172e192b24SSimon Glass /* Only "dump" is repeatable. */
4182e192b24SSimon Glass if (repeat && strcmp(cmd, "dump"))
4192e192b24SSimon Glass return 0;
4202e192b24SSimon Glass
4212e192b24SSimon Glass if (strcmp(cmd, "info") == 0) {
4222e192b24SSimon Glass
4232e192b24SSimon Glass putc('\n');
424ad92dff2SMugunthan V N for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
4252e192b24SSimon Glass nand_print_and_set_info(i);
4262e192b24SSimon Glass return 0;
4272e192b24SSimon Glass }
4282e192b24SSimon Glass
4292e192b24SSimon Glass if (strcmp(cmd, "device") == 0) {
4302e192b24SSimon Glass if (argc < 3) {
4312e192b24SSimon Glass putc('\n');
4322e192b24SSimon Glass if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE)
4332e192b24SSimon Glass puts("no devices available\n");
4342e192b24SSimon Glass else
4352e192b24SSimon Glass nand_print_and_set_info(dev);
4362e192b24SSimon Glass return 0;
4372e192b24SSimon Glass }
4382e192b24SSimon Glass
4392e192b24SSimon Glass dev = (int)simple_strtoul(argv[2], NULL, 10);
4402e192b24SSimon Glass set_dev(dev);
4412e192b24SSimon Glass
4422e192b24SSimon Glass return 0;
4432e192b24SSimon Glass }
4442e192b24SSimon Glass
4452e192b24SSimon Glass #ifdef CONFIG_ENV_OFFSET_OOB
4462e192b24SSimon Glass /* this command operates only on the first nand device */
4472e192b24SSimon Glass if (strcmp(cmd, "env.oob") == 0)
4482e192b24SSimon Glass return do_nand_env_oob(cmdtp, argc - 1, argv + 1);
4492e192b24SSimon Glass #endif
4502e192b24SSimon Glass
4512e192b24SSimon Glass /* The following commands operate on the current device, unless
4522e192b24SSimon Glass * overridden by a partition specifier. Note that if somehow the
4532e192b24SSimon Glass * current device is invalid, it will have to be changed to a valid
4542e192b24SSimon Glass * one before these commands can run, even if a partition specifier
4552e192b24SSimon Glass * for another device is to be used.
4562e192b24SSimon Glass */
457ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(dev);
458ad92dff2SMugunthan V N if (!mtd) {
4592e192b24SSimon Glass puts("\nno devices available\n");
4602e192b24SSimon Glass return 1;
4612e192b24SSimon Glass }
4622e192b24SSimon Glass
4632e192b24SSimon Glass if (strcmp(cmd, "bad") == 0) {
4642e192b24SSimon Glass printf("\nDevice %d bad blocks:\n", dev);
465151c06ecSScott Wood for (off = 0; off < mtd->size; off += mtd->erasesize)
466151c06ecSScott Wood if (nand_block_isbad(mtd, off))
4672e192b24SSimon Glass printf(" %08llx\n", (unsigned long long)off);
4682e192b24SSimon Glass return 0;
4692e192b24SSimon Glass }
4702e192b24SSimon Glass
4712e192b24SSimon Glass /*
4722e192b24SSimon Glass * Syntax is:
4732e192b24SSimon Glass * 0 1 2 3 4
4742e192b24SSimon Glass * nand erase [clean] [off size]
4752e192b24SSimon Glass */
4762e192b24SSimon Glass if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) {
4772e192b24SSimon Glass nand_erase_options_t opts;
4782e192b24SSimon Glass /* "clean" at index 2 means request to write cleanmarker */
4792e192b24SSimon Glass int clean = argc > 2 && !strcmp("clean", argv[2]);
4802e192b24SSimon Glass int scrub_yes = argc > 2 && !strcmp("-y", argv[2]);
4812e192b24SSimon Glass int o = (clean || scrub_yes) ? 3 : 2;
4822e192b24SSimon Glass int scrub = !strncmp(cmd, "scrub", 5);
4832e192b24SSimon Glass int spread = 0;
4842e192b24SSimon Glass int args = 2;
4852e192b24SSimon Glass const char *scrub_warn =
4862e192b24SSimon Glass "Warning: "
4872e192b24SSimon Glass "scrub option will erase all factory set bad blocks!\n"
4882e192b24SSimon Glass " "
4892e192b24SSimon Glass "There is no reliable way to recover them.\n"
4902e192b24SSimon Glass " "
4912e192b24SSimon Glass "Use this command only for testing purposes if you\n"
4922e192b24SSimon Glass " "
4932e192b24SSimon Glass "are sure of what you are doing!\n"
4942e192b24SSimon Glass "\nReally scrub this NAND flash? <y/N>\n";
4952e192b24SSimon Glass
4962e192b24SSimon Glass if (cmd[5] != 0) {
4972e192b24SSimon Glass if (!strcmp(&cmd[5], ".spread")) {
4982e192b24SSimon Glass spread = 1;
4992e192b24SSimon Glass } else if (!strcmp(&cmd[5], ".part")) {
5002e192b24SSimon Glass args = 1;
5012e192b24SSimon Glass } else if (!strcmp(&cmd[5], ".chip")) {
5022e192b24SSimon Glass args = 0;
5032e192b24SSimon Glass } else {
5042e192b24SSimon Glass goto usage;
5052e192b24SSimon Glass }
5062e192b24SSimon Glass }
5072e192b24SSimon Glass
5082e192b24SSimon Glass /*
5092e192b24SSimon Glass * Don't allow missing arguments to cause full chip/partition
5102e192b24SSimon Glass * erases -- easy to do accidentally, e.g. with a misspelled
5112e192b24SSimon Glass * variable name.
5122e192b24SSimon Glass */
5132e192b24SSimon Glass if (argc != o + args)
5142e192b24SSimon Glass goto usage;
5152e192b24SSimon Glass
5162e192b24SSimon Glass printf("\nNAND %s: ", cmd);
5172e192b24SSimon Glass /* skip first two or three arguments, look for offset and size */
5182e192b24SSimon Glass if (mtd_arg_off_size(argc - o, argv + o, &dev, &off, &size,
5192e192b24SSimon Glass &maxsize, MTD_DEV_TYPE_NAND,
520ad92dff2SMugunthan V N mtd->size) != 0)
5212e192b24SSimon Glass return 1;
5222e192b24SSimon Glass
5232e192b24SSimon Glass if (set_dev(dev))
5242e192b24SSimon Glass return 1;
5252e192b24SSimon Glass
526ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(dev);
5272e192b24SSimon Glass
5282e192b24SSimon Glass memset(&opts, 0, sizeof(opts));
5292e192b24SSimon Glass opts.offset = off;
5302e192b24SSimon Glass opts.length = size;
5312e192b24SSimon Glass opts.jffs2 = clean;
5322e192b24SSimon Glass opts.quiet = quiet;
5332e192b24SSimon Glass opts.spread = spread;
5342e192b24SSimon Glass
5352e192b24SSimon Glass if (scrub) {
5362e192b24SSimon Glass if (scrub_yes) {
5372e192b24SSimon Glass opts.scrub = 1;
5382e192b24SSimon Glass } else {
5392e192b24SSimon Glass puts(scrub_warn);
5402e192b24SSimon Glass if (confirm_yesno()) {
5412e192b24SSimon Glass opts.scrub = 1;
5422e192b24SSimon Glass } else {
5432e192b24SSimon Glass puts("scrub aborted\n");
5442e192b24SSimon Glass return 1;
5452e192b24SSimon Glass }
5462e192b24SSimon Glass }
5472e192b24SSimon Glass }
548151c06ecSScott Wood ret = nand_erase_opts(mtd, &opts);
5492e192b24SSimon Glass printf("%s\n", ret ? "ERROR" : "OK");
5502e192b24SSimon Glass
5512e192b24SSimon Glass return ret == 0 ? 0 : 1;
5522e192b24SSimon Glass }
5532e192b24SSimon Glass
5542e192b24SSimon Glass if (strncmp(cmd, "dump", 4) == 0) {
5552e192b24SSimon Glass if (argc < 3)
5562e192b24SSimon Glass goto usage;
5572e192b24SSimon Glass
5582e192b24SSimon Glass off = (int)simple_strtoul(argv[2], NULL, 16);
559151c06ecSScott Wood ret = nand_dump(mtd, off, !strcmp(&cmd[4], ".oob"), repeat);
5602e192b24SSimon Glass
5612e192b24SSimon Glass return ret == 0 ? 1 : 0;
5622e192b24SSimon Glass }
5632e192b24SSimon Glass
5642e192b24SSimon Glass if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
5652e192b24SSimon Glass size_t rwsize;
5662e192b24SSimon Glass ulong pagecount = 1;
5672e192b24SSimon Glass int read;
5682e192b24SSimon Glass int raw = 0;
5692dc3c483SBoris Brezillon int no_verify = 0;
5702e192b24SSimon Glass
5712e192b24SSimon Glass if (argc < 4)
5722e192b24SSimon Glass goto usage;
5732e192b24SSimon Glass
5742e192b24SSimon Glass addr = (ulong)simple_strtoul(argv[2], NULL, 16);
5752e192b24SSimon Glass
5762e192b24SSimon Glass read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
5772e192b24SSimon Glass printf("\nNAND %s: ", read ? "read" : "write");
5782e192b24SSimon Glass
5792e192b24SSimon Glass s = strchr(cmd, '.');
5802e192b24SSimon Glass
5812dc3c483SBoris Brezillon if (s && !strncmp(s, ".raw", 4)) {
5822e192b24SSimon Glass raw = 1;
5832e192b24SSimon Glass
5842dc3c483SBoris Brezillon if (!strcmp(s, ".raw.noverify"))
5852dc3c483SBoris Brezillon no_verify = 1;
5862dc3c483SBoris Brezillon
5872e192b24SSimon Glass if (mtd_arg_off(argv[3], &dev, &off, &size, &maxsize,
5882e192b24SSimon Glass MTD_DEV_TYPE_NAND,
589ad92dff2SMugunthan V N mtd->size))
5902e192b24SSimon Glass return 1;
5912e192b24SSimon Glass
5922e192b24SSimon Glass if (set_dev(dev))
5932e192b24SSimon Glass return 1;
5942e192b24SSimon Glass
595ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(dev);
5962e192b24SSimon Glass
5972e192b24SSimon Glass if (argc > 4 && !str2long(argv[4], &pagecount)) {
5982e192b24SSimon Glass printf("'%s' is not a number\n", argv[4]);
5992e192b24SSimon Glass return 1;
6002e192b24SSimon Glass }
6012e192b24SSimon Glass
6025f293671SJon Lin if (strncmp(cmd, "readbyte", 8) == 0 || strncmp(cmd, "writebyte", 9) == 0) {
6035f293671SJon Lin if (pagecount % (mtd->writesize + mtd->oobsize)) {
6045f293671SJon Lin printf("Count=%ld should be aligned with (writesize + oobsize)\n", pagecount);
6055f293671SJon Lin return -1;
6065f293671SJon Lin }
6075f293671SJon Lin pagecount = pagecount / (mtd->writesize + mtd->oobsize);
6085f293671SJon Lin }
6095f293671SJon Lin
610151c06ecSScott Wood if (pagecount * mtd->writesize > size) {
6112e192b24SSimon Glass puts("Size exceeds partition or device limit\n");
6122e192b24SSimon Glass return -1;
6132e192b24SSimon Glass }
6142e192b24SSimon Glass
615151c06ecSScott Wood rwsize = pagecount * (mtd->writesize + mtd->oobsize);
6162e192b24SSimon Glass } else {
6172e192b24SSimon Glass if (mtd_arg_off_size(argc - 3, argv + 3, &dev, &off,
6182e192b24SSimon Glass &size, &maxsize,
6192e192b24SSimon Glass MTD_DEV_TYPE_NAND,
620ad92dff2SMugunthan V N mtd->size) != 0)
6212e192b24SSimon Glass return 1;
6222e192b24SSimon Glass
6232e192b24SSimon Glass if (set_dev(dev))
6242e192b24SSimon Glass return 1;
6252e192b24SSimon Glass
6262e192b24SSimon Glass /* size is unspecified */
6272e192b24SSimon Glass if (argc < 5)
6282e192b24SSimon Glass adjust_size_for_badblocks(&size, off, dev);
6292e192b24SSimon Glass rwsize = size;
6302e192b24SSimon Glass }
6312e192b24SSimon Glass
632ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(dev);
6332e192b24SSimon Glass
6342e192b24SSimon Glass if (!s || !strcmp(s, ".jffs2") ||
6352e192b24SSimon Glass !strcmp(s, ".e") || !strcmp(s, ".i")) {
6362e192b24SSimon Glass if (read)
637151c06ecSScott Wood ret = nand_read_skip_bad(mtd, off, &rwsize,
6382e192b24SSimon Glass NULL, maxsize,
6392e192b24SSimon Glass (u_char *)addr);
6402e192b24SSimon Glass else
641151c06ecSScott Wood ret = nand_write_skip_bad(mtd, off, &rwsize,
6422e192b24SSimon Glass NULL, maxsize,
6432e192b24SSimon Glass (u_char *)addr,
6442e192b24SSimon Glass WITH_WR_VERIFY);
6452e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TRIMFFS
6462e192b24SSimon Glass } else if (!strcmp(s, ".trimffs")) {
6472e192b24SSimon Glass if (read) {
6482e192b24SSimon Glass printf("Unknown nand command suffix '%s'\n", s);
6492e192b24SSimon Glass return 1;
6502e192b24SSimon Glass }
651151c06ecSScott Wood ret = nand_write_skip_bad(mtd, off, &rwsize, NULL,
6522e192b24SSimon Glass maxsize, (u_char *)addr,
6532e192b24SSimon Glass WITH_DROP_FFS | WITH_WR_VERIFY);
6542e192b24SSimon Glass #endif
6552e192b24SSimon Glass } else if (!strcmp(s, ".oob")) {
6562e192b24SSimon Glass /* out-of-band data */
6572e192b24SSimon Glass mtd_oob_ops_t ops = {
6582e192b24SSimon Glass .oobbuf = (u8 *)addr,
6592e192b24SSimon Glass .ooblen = rwsize,
6602e192b24SSimon Glass .mode = MTD_OPS_RAW
6612e192b24SSimon Glass };
6622e192b24SSimon Glass
6632e192b24SSimon Glass if (read)
664151c06ecSScott Wood ret = mtd_read_oob(mtd, off, &ops);
6652e192b24SSimon Glass else
666151c06ecSScott Wood ret = mtd_write_oob(mtd, off, &ops);
6672e192b24SSimon Glass } else if (raw) {
6682dc3c483SBoris Brezillon ret = raw_access(mtd, addr, off, pagecount, read,
6692dc3c483SBoris Brezillon no_verify);
6702e192b24SSimon Glass } else {
6712e192b24SSimon Glass printf("Unknown nand command suffix '%s'.\n", s);
6722e192b24SSimon Glass return 1;
6732e192b24SSimon Glass }
6742e192b24SSimon Glass
6752e192b24SSimon Glass printf(" %zu bytes %s: %s\n", rwsize,
6762e192b24SSimon Glass read ? "read" : "written", ret ? "ERROR" : "OK");
6772e192b24SSimon Glass
6782e192b24SSimon Glass return ret == 0 ? 0 : 1;
6792e192b24SSimon Glass }
6802e192b24SSimon Glass
6812e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TORTURE
6822e192b24SSimon Glass if (strcmp(cmd, "torture") == 0) {
6831866be7dSMax Krummenacher loff_t endoff;
6841866be7dSMax Krummenacher unsigned int failed = 0, passed = 0;
6851866be7dSMax Krummenacher
6862e192b24SSimon Glass if (argc < 3)
6872e192b24SSimon Glass goto usage;
6882e192b24SSimon Glass
6892e192b24SSimon Glass if (!str2off(argv[2], &off)) {
6902e192b24SSimon Glass puts("Offset is not a valid number\n");
6912e192b24SSimon Glass return 1;
6922e192b24SSimon Glass }
6932e192b24SSimon Glass
6941866be7dSMax Krummenacher size = mtd->erasesize;
6951866be7dSMax Krummenacher if (argc > 3) {
6961866be7dSMax Krummenacher if (!str2off(argv[3], &size)) {
6971866be7dSMax Krummenacher puts("Size is not a valid number\n");
6981866be7dSMax Krummenacher return 1;
6991866be7dSMax Krummenacher }
7001866be7dSMax Krummenacher }
7012e192b24SSimon Glass
7021866be7dSMax Krummenacher endoff = off + size;
7031866be7dSMax Krummenacher if (endoff > mtd->size) {
7041866be7dSMax Krummenacher puts("Arguments beyond end of NAND\n");
7051866be7dSMax Krummenacher return 1;
7061866be7dSMax Krummenacher }
7071866be7dSMax Krummenacher
7081866be7dSMax Krummenacher off = round_down(off, mtd->erasesize);
7091866be7dSMax Krummenacher endoff = round_up(endoff, mtd->erasesize);
7101866be7dSMax Krummenacher size = endoff - off;
7111866be7dSMax Krummenacher printf("\nNAND torture: device %d offset 0x%llx size 0x%llx (block size 0x%x)\n",
7121866be7dSMax Krummenacher dev, off, size, mtd->erasesize);
7131866be7dSMax Krummenacher while (off < endoff) {
7141866be7dSMax Krummenacher ret = nand_torture(mtd, off);
7151866be7dSMax Krummenacher if (ret) {
7161866be7dSMax Krummenacher failed++;
7171866be7dSMax Krummenacher printf(" block at 0x%llx failed\n", off);
7181866be7dSMax Krummenacher } else {
7191866be7dSMax Krummenacher passed++;
7201866be7dSMax Krummenacher }
7211866be7dSMax Krummenacher off += mtd->erasesize;
7221866be7dSMax Krummenacher }
7231866be7dSMax Krummenacher printf(" Passed: %u, failed: %u\n", passed, failed);
7241866be7dSMax Krummenacher return failed != 0;
7252e192b24SSimon Glass }
7262e192b24SSimon Glass #endif
7272e192b24SSimon Glass
7282e192b24SSimon Glass if (strcmp(cmd, "markbad") == 0) {
7292e192b24SSimon Glass argc -= 2;
7302e192b24SSimon Glass argv += 2;
7312e192b24SSimon Glass
7322e192b24SSimon Glass if (argc <= 0)
7332e192b24SSimon Glass goto usage;
7342e192b24SSimon Glass
7352e192b24SSimon Glass while (argc > 0) {
7362e192b24SSimon Glass addr = simple_strtoul(*argv, NULL, 16);
7372e192b24SSimon Glass
738151c06ecSScott Wood if (mtd_block_markbad(mtd, addr)) {
7392e192b24SSimon Glass printf("block 0x%08lx NOT marked "
7402e192b24SSimon Glass "as bad! ERROR %d\n",
7412e192b24SSimon Glass addr, ret);
7422e192b24SSimon Glass ret = 1;
7432e192b24SSimon Glass } else {
7442e192b24SSimon Glass printf("block 0x%08lx successfully "
7452e192b24SSimon Glass "marked as bad\n",
7462e192b24SSimon Glass addr);
7472e192b24SSimon Glass }
7482e192b24SSimon Glass --argc;
7492e192b24SSimon Glass ++argv;
7502e192b24SSimon Glass }
7512e192b24SSimon Glass return ret;
7522e192b24SSimon Glass }
7532e192b24SSimon Glass
7542e192b24SSimon Glass if (strcmp(cmd, "biterr") == 0) {
7552e192b24SSimon Glass /* todo */
7562e192b24SSimon Glass return 1;
7572e192b24SSimon Glass }
7582e192b24SSimon Glass
7592e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK
7602e192b24SSimon Glass if (strcmp(cmd, "lock") == 0) {
7612e192b24SSimon Glass int tight = 0;
7622e192b24SSimon Glass int status = 0;
7632e192b24SSimon Glass if (argc == 3) {
7642e192b24SSimon Glass if (!strcmp("tight", argv[2]))
7652e192b24SSimon Glass tight = 1;
7662e192b24SSimon Glass if (!strcmp("status", argv[2]))
7672e192b24SSimon Glass status = 1;
7682e192b24SSimon Glass }
7692e192b24SSimon Glass if (status) {
770151c06ecSScott Wood do_nand_status(mtd);
7712e192b24SSimon Glass } else {
772151c06ecSScott Wood if (!nand_lock(mtd, tight)) {
7732e192b24SSimon Glass puts("NAND flash successfully locked\n");
7742e192b24SSimon Glass } else {
7752e192b24SSimon Glass puts("Error locking NAND flash\n");
7762e192b24SSimon Glass return 1;
7772e192b24SSimon Glass }
7782e192b24SSimon Glass }
7792e192b24SSimon Glass return 0;
7802e192b24SSimon Glass }
7812e192b24SSimon Glass
7822e192b24SSimon Glass if (strncmp(cmd, "unlock", 5) == 0) {
7832e192b24SSimon Glass int allexcept = 0;
7842e192b24SSimon Glass
7852e192b24SSimon Glass s = strchr(cmd, '.');
7862e192b24SSimon Glass
7872e192b24SSimon Glass if (s && !strcmp(s, ".allexcept"))
7882e192b24SSimon Glass allexcept = 1;
7892e192b24SSimon Glass
7902e192b24SSimon Glass if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &off, &size,
7912e192b24SSimon Glass &maxsize, MTD_DEV_TYPE_NAND,
792ad92dff2SMugunthan V N mtd->size) < 0)
7932e192b24SSimon Glass return 1;
7942e192b24SSimon Glass
7952e192b24SSimon Glass if (set_dev(dev))
7962e192b24SSimon Glass return 1;
7972e192b24SSimon Glass
798ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(dev);
799ad92dff2SMugunthan V N
800ad92dff2SMugunthan V N if (!nand_unlock(mtd, off, size, allexcept)) {
8012e192b24SSimon Glass puts("NAND flash successfully unlocked\n");
8022e192b24SSimon Glass } else {
8032e192b24SSimon Glass puts("Error unlocking NAND flash, "
8042e192b24SSimon Glass "write and erase will probably fail\n");
8052e192b24SSimon Glass return 1;
8062e192b24SSimon Glass }
8072e192b24SSimon Glass return 0;
8082e192b24SSimon Glass }
8092e192b24SSimon Glass #endif
8102e192b24SSimon Glass
8112e192b24SSimon Glass usage:
8122e192b24SSimon Glass return CMD_RET_USAGE;
8132e192b24SSimon Glass }
8142e192b24SSimon Glass
8152e192b24SSimon Glass #ifdef CONFIG_SYS_LONGHELP
8162e192b24SSimon Glass static char nand_help_text[] =
8172e192b24SSimon Glass "info - show available NAND devices\n"
8182e192b24SSimon Glass "nand device [dev] - show or set current device\n"
8192e192b24SSimon Glass "nand read - addr off|partition size\n"
8202e192b24SSimon Glass "nand write - addr off|partition size\n"
8212e192b24SSimon Glass " read/write 'size' bytes starting at offset 'off'\n"
8222e192b24SSimon Glass " to/from memory address 'addr', skipping bad blocks.\n"
8232e192b24SSimon Glass "nand read.raw - addr off|partition [count]\n"
8242dc3c483SBoris Brezillon "nand write.raw[.noverify] - addr off|partition [count]\n"
825*6aef6166SJon Lin " Use read.raw/write.raw to avoid ECC and access the flash as-is,\n"
826*6aef6166SJon Lin " and skip bad\n"
8272e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TRIMFFS
8282e192b24SSimon Glass "nand write.trimffs - addr off|partition size\n"
8292e192b24SSimon Glass " write 'size' bytes starting at offset 'off' from memory address\n"
8302e192b24SSimon Glass " 'addr', skipping bad blocks and dropping any pages at the end\n"
8312e192b24SSimon Glass " of eraseblocks that contain only 0xFF\n"
8322e192b24SSimon Glass #endif
8332e192b24SSimon Glass "nand erase[.spread] [clean] off size - erase 'size' bytes "
8342e192b24SSimon Glass "from offset 'off'\n"
8352e192b24SSimon Glass " With '.spread', erase enough for given file size, otherwise,\n"
8362e192b24SSimon Glass " 'size' includes skipped bad blocks.\n"
8372e192b24SSimon Glass "nand erase.part [clean] partition - erase entire mtd partition'\n"
8382e192b24SSimon Glass "nand erase.chip [clean] - erase entire chip'\n"
8392e192b24SSimon Glass "nand bad - show bad blocks\n"
8402e192b24SSimon Glass "nand dump[.oob] off - dump page\n"
8412e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TORTURE
8421866be7dSMax Krummenacher "nand torture off - torture one block at offset\n"
8431866be7dSMax Krummenacher "nand torture off [size] - torture blocks from off to off+size\n"
8442e192b24SSimon Glass #endif
8452e192b24SSimon Glass "nand scrub [-y] off size | scrub.part partition | scrub.chip\n"
8462e192b24SSimon Glass " really clean NAND erasing bad blocks (UNSAFE)\n"
8472e192b24SSimon Glass "nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n"
8482e192b24SSimon Glass "nand biterr off - make a bit error at offset (UNSAFE)"
8492e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK
8502e192b24SSimon Glass "\n"
8512e192b24SSimon Glass "nand lock [tight] [status]\n"
8522e192b24SSimon Glass " bring nand to lock state or display locked pages\n"
8532e192b24SSimon Glass "nand unlock[.allexcept] [offset] [size] - unlock section"
8542e192b24SSimon Glass #endif
8552e192b24SSimon Glass #ifdef CONFIG_ENV_OFFSET_OOB
8562e192b24SSimon Glass "\n"
8572e192b24SSimon Glass "nand env.oob - environment offset in OOB of block 0 of"
8582e192b24SSimon Glass " first device.\n"
8592e192b24SSimon Glass "nand env.oob set off|partition - set enviromnent offset\n"
8602e192b24SSimon Glass "nand env.oob get - get environment offset"
8612e192b24SSimon Glass #endif
8622e192b24SSimon Glass "";
8632e192b24SSimon Glass #endif
8642e192b24SSimon Glass
8652e192b24SSimon Glass U_BOOT_CMD(
8662e192b24SSimon Glass nand, CONFIG_SYS_MAXARGS, 1, do_nand,
8672e192b24SSimon Glass "NAND sub-system", nand_help_text
8682e192b24SSimon Glass );
8692e192b24SSimon Glass
nand_load_image(cmd_tbl_t * cmdtp,struct mtd_info * mtd,ulong offset,ulong addr,char * cmd)870151c06ecSScott Wood static int nand_load_image(cmd_tbl_t *cmdtp, struct mtd_info *mtd,
8712e192b24SSimon Glass ulong offset, ulong addr, char *cmd)
8722e192b24SSimon Glass {
8732e192b24SSimon Glass int r;
8742e192b24SSimon Glass char *s;
8752e192b24SSimon Glass size_t cnt;
8762e192b24SSimon Glass #if defined(CONFIG_IMAGE_FORMAT_LEGACY)
8772e192b24SSimon Glass image_header_t *hdr;
8782e192b24SSimon Glass #endif
8792e192b24SSimon Glass #if defined(CONFIG_FIT)
8802e192b24SSimon Glass const void *fit_hdr = NULL;
8812e192b24SSimon Glass #endif
8822e192b24SSimon Glass
8832e192b24SSimon Glass s = strchr(cmd, '.');
8842e192b24SSimon Glass if (s != NULL &&
8852e192b24SSimon Glass (strcmp(s, ".jffs2") && strcmp(s, ".e") && strcmp(s, ".i"))) {
8862e192b24SSimon Glass printf("Unknown nand load suffix '%s'\n", s);
8872e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX);
8882e192b24SSimon Glass return 1;
8892e192b24SSimon Glass }
8902e192b24SSimon Glass
891151c06ecSScott Wood printf("\nLoading from %s, offset 0x%lx\n", mtd->name, offset);
8922e192b24SSimon Glass
893151c06ecSScott Wood cnt = mtd->writesize;
894151c06ecSScott Wood r = nand_read_skip_bad(mtd, offset, &cnt, NULL, mtd->size,
8952e192b24SSimon Glass (u_char *)addr);
8962e192b24SSimon Glass if (r) {
8972e192b24SSimon Glass puts("** Read error\n");
8982e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_HDR_READ);
8992e192b24SSimon Glass return 1;
9002e192b24SSimon Glass }
9012e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_HDR_READ);
9022e192b24SSimon Glass
9032e192b24SSimon Glass switch (genimg_get_format ((void *)addr)) {
9042e192b24SSimon Glass #if defined(CONFIG_IMAGE_FORMAT_LEGACY)
9052e192b24SSimon Glass case IMAGE_FORMAT_LEGACY:
9062e192b24SSimon Glass hdr = (image_header_t *)addr;
9072e192b24SSimon Glass
9082e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_TYPE);
9092e192b24SSimon Glass image_print_contents (hdr);
9102e192b24SSimon Glass
9112e192b24SSimon Glass cnt = image_get_image_size (hdr);
9122e192b24SSimon Glass break;
9132e192b24SSimon Glass #endif
9142e192b24SSimon Glass #if defined(CONFIG_FIT)
9152e192b24SSimon Glass case IMAGE_FORMAT_FIT:
9162e192b24SSimon Glass fit_hdr = (const void *)addr;
9172e192b24SSimon Glass puts ("Fit image detected...\n");
9182e192b24SSimon Glass
9192e192b24SSimon Glass cnt = fit_get_size (fit_hdr);
9202e192b24SSimon Glass break;
9212e192b24SSimon Glass #endif
9222e192b24SSimon Glass default:
9232e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_TYPE);
9242e192b24SSimon Glass puts ("** Unknown image type\n");
9252e192b24SSimon Glass return 1;
9262e192b24SSimon Glass }
9272e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_TYPE);
9282e192b24SSimon Glass
929151c06ecSScott Wood r = nand_read_skip_bad(mtd, offset, &cnt, NULL, mtd->size,
9302e192b24SSimon Glass (u_char *)addr);
9312e192b24SSimon Glass if (r) {
9322e192b24SSimon Glass puts("** Read error\n");
9332e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_READ);
9342e192b24SSimon Glass return 1;
9352e192b24SSimon Glass }
9362e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_READ);
9372e192b24SSimon Glass
9382e192b24SSimon Glass #if defined(CONFIG_FIT)
9392e192b24SSimon Glass /* This cannot be done earlier, we need complete FIT image in RAM first */
9402e192b24SSimon Glass if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) {
9412e192b24SSimon Glass if (!fit_check_format (fit_hdr)) {
9422e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_FIT_READ);
9432e192b24SSimon Glass puts ("** Bad FIT image format\n");
9442e192b24SSimon Glass return 1;
9452e192b24SSimon Glass }
9462e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_FIT_READ_OK);
9472e192b24SSimon Glass fit_print_contents (fit_hdr);
9482e192b24SSimon Glass }
9492e192b24SSimon Glass #endif
9502e192b24SSimon Glass
9512e192b24SSimon Glass /* Loading ok, update default load address */
9522e192b24SSimon Glass
9532e192b24SSimon Glass load_addr = addr;
9542e192b24SSimon Glass
9552e192b24SSimon Glass return bootm_maybe_autostart(cmdtp, cmd);
9562e192b24SSimon Glass }
9572e192b24SSimon Glass
do_nandboot(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])9582e192b24SSimon Glass static int do_nandboot(cmd_tbl_t *cmdtp, int flag, int argc,
9592e192b24SSimon Glass char * const argv[])
9602e192b24SSimon Glass {
9612e192b24SSimon Glass char *boot_device = NULL;
9622e192b24SSimon Glass int idx;
9632e192b24SSimon Glass ulong addr, offset = 0;
964ad92dff2SMugunthan V N struct mtd_info *mtd;
9652e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS)
9662e192b24SSimon Glass struct mtd_device *dev;
9672e192b24SSimon Glass struct part_info *part;
9682e192b24SSimon Glass u8 pnum;
9692e192b24SSimon Glass
9702e192b24SSimon Glass if (argc >= 2) {
9712e192b24SSimon Glass char *p = (argc == 2) ? argv[1] : argv[2];
9722e192b24SSimon Glass if (!(str2long(p, &addr)) && (mtdparts_init() == 0) &&
9732e192b24SSimon Glass (find_dev_and_part(p, &dev, &pnum, &part) == 0)) {
9742e192b24SSimon Glass if (dev->id->type != MTD_DEV_TYPE_NAND) {
9752e192b24SSimon Glass puts("Not a NAND device\n");
9762e192b24SSimon Glass return 1;
9772e192b24SSimon Glass }
9782e192b24SSimon Glass if (argc > 3)
9792e192b24SSimon Glass goto usage;
9802e192b24SSimon Glass if (argc == 3)
9812e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16);
9822e192b24SSimon Glass else
9832e192b24SSimon Glass addr = CONFIG_SYS_LOAD_ADDR;
984ad92dff2SMugunthan V N
985ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(dev->id->num);
986ad92dff2SMugunthan V N return nand_load_image(cmdtp, mtd, part->offset,
987ad92dff2SMugunthan V N addr, argv[0]);
9882e192b24SSimon Glass }
9892e192b24SSimon Glass }
9902e192b24SSimon Glass #endif
9912e192b24SSimon Glass
9922e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_PART);
9932e192b24SSimon Glass switch (argc) {
9942e192b24SSimon Glass case 1:
9952e192b24SSimon Glass addr = CONFIG_SYS_LOAD_ADDR;
99600caae6dSSimon Glass boot_device = env_get("bootdevice");
9972e192b24SSimon Glass break;
9982e192b24SSimon Glass case 2:
9992e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16);
100000caae6dSSimon Glass boot_device = env_get("bootdevice");
10012e192b24SSimon Glass break;
10022e192b24SSimon Glass case 3:
10032e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16);
10042e192b24SSimon Glass boot_device = argv[2];
10052e192b24SSimon Glass break;
10062e192b24SSimon Glass case 4:
10072e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16);
10082e192b24SSimon Glass boot_device = argv[2];
10092e192b24SSimon Glass offset = simple_strtoul(argv[3], NULL, 16);
10102e192b24SSimon Glass break;
10112e192b24SSimon Glass default:
10122e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS)
10132e192b24SSimon Glass usage:
10142e192b24SSimon Glass #endif
10152e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX);
10162e192b24SSimon Glass return CMD_RET_USAGE;
10172e192b24SSimon Glass }
10182e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_SUFFIX);
10192e192b24SSimon Glass
10202e192b24SSimon Glass if (!boot_device) {
10212e192b24SSimon Glass puts("\n** No boot device **\n");
10222e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_BOOT_DEVICE);
10232e192b24SSimon Glass return 1;
10242e192b24SSimon Glass }
10252e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_BOOT_DEVICE);
10262e192b24SSimon Glass
10272e192b24SSimon Glass idx = simple_strtoul(boot_device, NULL, 16);
10282e192b24SSimon Glass
1029ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(idx);
1030ad92dff2SMugunthan V N if (!mtd) {
10312e192b24SSimon Glass printf("\n** Device %d not available\n", idx);
10322e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_AVAILABLE);
10332e192b24SSimon Glass return 1;
10342e192b24SSimon Glass }
10352e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_AVAILABLE);
10362e192b24SSimon Glass
1037ad92dff2SMugunthan V N return nand_load_image(cmdtp, mtd, offset, addr, argv[0]);
10382e192b24SSimon Glass }
10392e192b24SSimon Glass
10402e192b24SSimon Glass U_BOOT_CMD(nboot, 4, 1, do_nandboot,
10412e192b24SSimon Glass "boot from NAND device",
10422e192b24SSimon Glass "[partition] | [[[loadAddr] dev] offset]"
10432e192b24SSimon Glass );
1044