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 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 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 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 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 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 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 313151c06ecSScott Wood static int raw_access(struct mtd_info *mtd, ulong addr, loff_t off, 3142dc3c483SBoris Brezillon ulong count, int read, int no_verify) 3152e192b24SSimon Glass { 3162e192b24SSimon Glass int ret = 0; 3172e192b24SSimon Glass 3182e192b24SSimon Glass while (count--) { 3192e192b24SSimon Glass /* Raw access */ 3202e192b24SSimon Glass mtd_oob_ops_t ops = { 3212e192b24SSimon Glass .datbuf = (u8 *)addr, 322151c06ecSScott Wood .oobbuf = ((u8 *)addr) + mtd->writesize, 323151c06ecSScott Wood .len = mtd->writesize, 324151c06ecSScott Wood .ooblen = mtd->oobsize, 3252e192b24SSimon Glass .mode = MTD_OPS_RAW 3262e192b24SSimon Glass }; 3272e192b24SSimon Glass 3282e192b24SSimon Glass if (read) { 329151c06ecSScott Wood ret = mtd_read_oob(mtd, off, &ops); 3302e192b24SSimon Glass } else { 331151c06ecSScott Wood ret = mtd_write_oob(mtd, off, &ops); 3322dc3c483SBoris Brezillon if (!ret && !no_verify) 333151c06ecSScott Wood ret = nand_verify_page_oob(mtd, &ops, off); 3342e192b24SSimon Glass } 3352e192b24SSimon Glass 3362e192b24SSimon Glass if (ret) { 3372e192b24SSimon Glass printf("%s: error at offset %llx, ret %d\n", 3382e192b24SSimon Glass __func__, (long long)off, ret); 3392e192b24SSimon Glass break; 3402e192b24SSimon Glass } 3412e192b24SSimon Glass 342151c06ecSScott Wood addr += mtd->writesize + mtd->oobsize; 343151c06ecSScott Wood off += mtd->writesize; 3442e192b24SSimon Glass } 3452e192b24SSimon Glass 3462e192b24SSimon Glass return ret; 3472e192b24SSimon Glass } 3482e192b24SSimon Glass 3492e192b24SSimon Glass /* Adjust a chip/partition size down for bad blocks so we don't 3502e192b24SSimon Glass * read/write past the end of a chip/partition by accident. 3512e192b24SSimon Glass */ 3522e192b24SSimon Glass static void adjust_size_for_badblocks(loff_t *size, loff_t offset, int dev) 3532e192b24SSimon Glass { 3542e192b24SSimon Glass /* We grab the nand info object here fresh because this is usually 3552e192b24SSimon Glass * called after arg_off_size() which can change the value of dev. 3562e192b24SSimon Glass */ 357ad92dff2SMugunthan V N struct mtd_info *mtd = get_nand_dev_by_index(dev); 3582e192b24SSimon Glass loff_t maxoffset = offset + *size; 3592e192b24SSimon Glass int badblocks = 0; 3602e192b24SSimon Glass 3612e192b24SSimon Glass /* count badblocks in NAND from offset to offset + size */ 362151c06ecSScott Wood for (; offset < maxoffset; offset += mtd->erasesize) { 363151c06ecSScott Wood if (nand_block_isbad(mtd, offset)) 3642e192b24SSimon Glass badblocks++; 3652e192b24SSimon Glass } 3662e192b24SSimon Glass /* adjust size if any bad blocks found */ 3672e192b24SSimon Glass if (badblocks) { 368151c06ecSScott Wood *size -= badblocks * mtd->erasesize; 3692e192b24SSimon Glass printf("size adjusted to 0x%llx (%d bad blocks)\n", 3702e192b24SSimon Glass (unsigned long long)*size, badblocks); 3712e192b24SSimon Glass } 3722e192b24SSimon Glass } 3732e192b24SSimon Glass 3742e192b24SSimon Glass static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 3752e192b24SSimon Glass { 3762e192b24SSimon Glass int i, ret = 0; 3772e192b24SSimon Glass ulong addr; 3782e192b24SSimon Glass loff_t off, size, maxsize; 3792e192b24SSimon Glass char *cmd, *s; 380151c06ecSScott Wood struct mtd_info *mtd; 3812e192b24SSimon Glass #ifdef CONFIG_SYS_NAND_QUIET 3822e192b24SSimon Glass int quiet = CONFIG_SYS_NAND_QUIET; 3832e192b24SSimon Glass #else 3842e192b24SSimon Glass int quiet = 0; 3852e192b24SSimon Glass #endif 386*00caae6dSSimon Glass const char *quiet_str = env_get("quiet"); 3872e192b24SSimon Glass int dev = nand_curr_device; 3882e192b24SSimon Glass int repeat = flag & CMD_FLAG_REPEAT; 3892e192b24SSimon Glass 3902e192b24SSimon Glass /* at least two arguments please */ 3912e192b24SSimon Glass if (argc < 2) 3922e192b24SSimon Glass goto usage; 3932e192b24SSimon Glass 3942e192b24SSimon Glass if (quiet_str) 3952e192b24SSimon Glass quiet = simple_strtoul(quiet_str, NULL, 0) != 0; 3962e192b24SSimon Glass 3972e192b24SSimon Glass cmd = argv[1]; 3982e192b24SSimon Glass 3992e192b24SSimon Glass /* Only "dump" is repeatable. */ 4002e192b24SSimon Glass if (repeat && strcmp(cmd, "dump")) 4012e192b24SSimon Glass return 0; 4022e192b24SSimon Glass 4032e192b24SSimon Glass if (strcmp(cmd, "info") == 0) { 4042e192b24SSimon Glass 4052e192b24SSimon Glass putc('\n'); 406ad92dff2SMugunthan V N for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) 4072e192b24SSimon Glass nand_print_and_set_info(i); 4082e192b24SSimon Glass return 0; 4092e192b24SSimon Glass } 4102e192b24SSimon Glass 4112e192b24SSimon Glass if (strcmp(cmd, "device") == 0) { 4122e192b24SSimon Glass if (argc < 3) { 4132e192b24SSimon Glass putc('\n'); 4142e192b24SSimon Glass if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE) 4152e192b24SSimon Glass puts("no devices available\n"); 4162e192b24SSimon Glass else 4172e192b24SSimon Glass nand_print_and_set_info(dev); 4182e192b24SSimon Glass return 0; 4192e192b24SSimon Glass } 4202e192b24SSimon Glass 4212e192b24SSimon Glass dev = (int)simple_strtoul(argv[2], NULL, 10); 4222e192b24SSimon Glass set_dev(dev); 4232e192b24SSimon Glass 4242e192b24SSimon Glass return 0; 4252e192b24SSimon Glass } 4262e192b24SSimon Glass 4272e192b24SSimon Glass #ifdef CONFIG_ENV_OFFSET_OOB 4282e192b24SSimon Glass /* this command operates only on the first nand device */ 4292e192b24SSimon Glass if (strcmp(cmd, "env.oob") == 0) 4302e192b24SSimon Glass return do_nand_env_oob(cmdtp, argc - 1, argv + 1); 4312e192b24SSimon Glass #endif 4322e192b24SSimon Glass 4332e192b24SSimon Glass /* The following commands operate on the current device, unless 4342e192b24SSimon Glass * overridden by a partition specifier. Note that if somehow the 4352e192b24SSimon Glass * current device is invalid, it will have to be changed to a valid 4362e192b24SSimon Glass * one before these commands can run, even if a partition specifier 4372e192b24SSimon Glass * for another device is to be used. 4382e192b24SSimon Glass */ 439ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(dev); 440ad92dff2SMugunthan V N if (!mtd) { 4412e192b24SSimon Glass puts("\nno devices available\n"); 4422e192b24SSimon Glass return 1; 4432e192b24SSimon Glass } 4442e192b24SSimon Glass 4452e192b24SSimon Glass if (strcmp(cmd, "bad") == 0) { 4462e192b24SSimon Glass printf("\nDevice %d bad blocks:\n", dev); 447151c06ecSScott Wood for (off = 0; off < mtd->size; off += mtd->erasesize) 448151c06ecSScott Wood if (nand_block_isbad(mtd, off)) 4492e192b24SSimon Glass printf(" %08llx\n", (unsigned long long)off); 4502e192b24SSimon Glass return 0; 4512e192b24SSimon Glass } 4522e192b24SSimon Glass 4532e192b24SSimon Glass /* 4542e192b24SSimon Glass * Syntax is: 4552e192b24SSimon Glass * 0 1 2 3 4 4562e192b24SSimon Glass * nand erase [clean] [off size] 4572e192b24SSimon Glass */ 4582e192b24SSimon Glass if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) { 4592e192b24SSimon Glass nand_erase_options_t opts; 4602e192b24SSimon Glass /* "clean" at index 2 means request to write cleanmarker */ 4612e192b24SSimon Glass int clean = argc > 2 && !strcmp("clean", argv[2]); 4622e192b24SSimon Glass int scrub_yes = argc > 2 && !strcmp("-y", argv[2]); 4632e192b24SSimon Glass int o = (clean || scrub_yes) ? 3 : 2; 4642e192b24SSimon Glass int scrub = !strncmp(cmd, "scrub", 5); 4652e192b24SSimon Glass int spread = 0; 4662e192b24SSimon Glass int args = 2; 4672e192b24SSimon Glass const char *scrub_warn = 4682e192b24SSimon Glass "Warning: " 4692e192b24SSimon Glass "scrub option will erase all factory set bad blocks!\n" 4702e192b24SSimon Glass " " 4712e192b24SSimon Glass "There is no reliable way to recover them.\n" 4722e192b24SSimon Glass " " 4732e192b24SSimon Glass "Use this command only for testing purposes if you\n" 4742e192b24SSimon Glass " " 4752e192b24SSimon Glass "are sure of what you are doing!\n" 4762e192b24SSimon Glass "\nReally scrub this NAND flash? <y/N>\n"; 4772e192b24SSimon Glass 4782e192b24SSimon Glass if (cmd[5] != 0) { 4792e192b24SSimon Glass if (!strcmp(&cmd[5], ".spread")) { 4802e192b24SSimon Glass spread = 1; 4812e192b24SSimon Glass } else if (!strcmp(&cmd[5], ".part")) { 4822e192b24SSimon Glass args = 1; 4832e192b24SSimon Glass } else if (!strcmp(&cmd[5], ".chip")) { 4842e192b24SSimon Glass args = 0; 4852e192b24SSimon Glass } else { 4862e192b24SSimon Glass goto usage; 4872e192b24SSimon Glass } 4882e192b24SSimon Glass } 4892e192b24SSimon Glass 4902e192b24SSimon Glass /* 4912e192b24SSimon Glass * Don't allow missing arguments to cause full chip/partition 4922e192b24SSimon Glass * erases -- easy to do accidentally, e.g. with a misspelled 4932e192b24SSimon Glass * variable name. 4942e192b24SSimon Glass */ 4952e192b24SSimon Glass if (argc != o + args) 4962e192b24SSimon Glass goto usage; 4972e192b24SSimon Glass 4982e192b24SSimon Glass printf("\nNAND %s: ", cmd); 4992e192b24SSimon Glass /* skip first two or three arguments, look for offset and size */ 5002e192b24SSimon Glass if (mtd_arg_off_size(argc - o, argv + o, &dev, &off, &size, 5012e192b24SSimon Glass &maxsize, MTD_DEV_TYPE_NAND, 502ad92dff2SMugunthan V N mtd->size) != 0) 5032e192b24SSimon Glass return 1; 5042e192b24SSimon Glass 5052e192b24SSimon Glass if (set_dev(dev)) 5062e192b24SSimon Glass return 1; 5072e192b24SSimon Glass 508ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(dev); 5092e192b24SSimon Glass 5102e192b24SSimon Glass memset(&opts, 0, sizeof(opts)); 5112e192b24SSimon Glass opts.offset = off; 5122e192b24SSimon Glass opts.length = size; 5132e192b24SSimon Glass opts.jffs2 = clean; 5142e192b24SSimon Glass opts.quiet = quiet; 5152e192b24SSimon Glass opts.spread = spread; 5162e192b24SSimon Glass 5172e192b24SSimon Glass if (scrub) { 5182e192b24SSimon Glass if (scrub_yes) { 5192e192b24SSimon Glass opts.scrub = 1; 5202e192b24SSimon Glass } else { 5212e192b24SSimon Glass puts(scrub_warn); 5222e192b24SSimon Glass if (confirm_yesno()) { 5232e192b24SSimon Glass opts.scrub = 1; 5242e192b24SSimon Glass } else { 5252e192b24SSimon Glass puts("scrub aborted\n"); 5262e192b24SSimon Glass return 1; 5272e192b24SSimon Glass } 5282e192b24SSimon Glass } 5292e192b24SSimon Glass } 530151c06ecSScott Wood ret = nand_erase_opts(mtd, &opts); 5312e192b24SSimon Glass printf("%s\n", ret ? "ERROR" : "OK"); 5322e192b24SSimon Glass 5332e192b24SSimon Glass return ret == 0 ? 0 : 1; 5342e192b24SSimon Glass } 5352e192b24SSimon Glass 5362e192b24SSimon Glass if (strncmp(cmd, "dump", 4) == 0) { 5372e192b24SSimon Glass if (argc < 3) 5382e192b24SSimon Glass goto usage; 5392e192b24SSimon Glass 5402e192b24SSimon Glass off = (int)simple_strtoul(argv[2], NULL, 16); 541151c06ecSScott Wood ret = nand_dump(mtd, off, !strcmp(&cmd[4], ".oob"), repeat); 5422e192b24SSimon Glass 5432e192b24SSimon Glass return ret == 0 ? 1 : 0; 5442e192b24SSimon Glass } 5452e192b24SSimon Glass 5462e192b24SSimon Glass if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { 5472e192b24SSimon Glass size_t rwsize; 5482e192b24SSimon Glass ulong pagecount = 1; 5492e192b24SSimon Glass int read; 5502e192b24SSimon Glass int raw = 0; 5512dc3c483SBoris Brezillon int no_verify = 0; 5522e192b24SSimon Glass 5532e192b24SSimon Glass if (argc < 4) 5542e192b24SSimon Glass goto usage; 5552e192b24SSimon Glass 5562e192b24SSimon Glass addr = (ulong)simple_strtoul(argv[2], NULL, 16); 5572e192b24SSimon Glass 5582e192b24SSimon Glass read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ 5592e192b24SSimon Glass printf("\nNAND %s: ", read ? "read" : "write"); 5602e192b24SSimon Glass 5612e192b24SSimon Glass s = strchr(cmd, '.'); 5622e192b24SSimon Glass 5632dc3c483SBoris Brezillon if (s && !strncmp(s, ".raw", 4)) { 5642e192b24SSimon Glass raw = 1; 5652e192b24SSimon Glass 5662dc3c483SBoris Brezillon if (!strcmp(s, ".raw.noverify")) 5672dc3c483SBoris Brezillon no_verify = 1; 5682dc3c483SBoris Brezillon 5692e192b24SSimon Glass if (mtd_arg_off(argv[3], &dev, &off, &size, &maxsize, 5702e192b24SSimon Glass MTD_DEV_TYPE_NAND, 571ad92dff2SMugunthan V N mtd->size)) 5722e192b24SSimon Glass return 1; 5732e192b24SSimon Glass 5742e192b24SSimon Glass if (set_dev(dev)) 5752e192b24SSimon Glass return 1; 5762e192b24SSimon Glass 577ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(dev); 5782e192b24SSimon Glass 5792e192b24SSimon Glass if (argc > 4 && !str2long(argv[4], &pagecount)) { 5802e192b24SSimon Glass printf("'%s' is not a number\n", argv[4]); 5812e192b24SSimon Glass return 1; 5822e192b24SSimon Glass } 5832e192b24SSimon Glass 584151c06ecSScott Wood if (pagecount * mtd->writesize > size) { 5852e192b24SSimon Glass puts("Size exceeds partition or device limit\n"); 5862e192b24SSimon Glass return -1; 5872e192b24SSimon Glass } 5882e192b24SSimon Glass 589151c06ecSScott Wood rwsize = pagecount * (mtd->writesize + mtd->oobsize); 5902e192b24SSimon Glass } else { 5912e192b24SSimon Glass if (mtd_arg_off_size(argc - 3, argv + 3, &dev, &off, 5922e192b24SSimon Glass &size, &maxsize, 5932e192b24SSimon Glass MTD_DEV_TYPE_NAND, 594ad92dff2SMugunthan V N mtd->size) != 0) 5952e192b24SSimon Glass return 1; 5962e192b24SSimon Glass 5972e192b24SSimon Glass if (set_dev(dev)) 5982e192b24SSimon Glass return 1; 5992e192b24SSimon Glass 6002e192b24SSimon Glass /* size is unspecified */ 6012e192b24SSimon Glass if (argc < 5) 6022e192b24SSimon Glass adjust_size_for_badblocks(&size, off, dev); 6032e192b24SSimon Glass rwsize = size; 6042e192b24SSimon Glass } 6052e192b24SSimon Glass 606ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(dev); 6072e192b24SSimon Glass 6082e192b24SSimon Glass if (!s || !strcmp(s, ".jffs2") || 6092e192b24SSimon Glass !strcmp(s, ".e") || !strcmp(s, ".i")) { 6102e192b24SSimon Glass if (read) 611151c06ecSScott Wood ret = nand_read_skip_bad(mtd, off, &rwsize, 6122e192b24SSimon Glass NULL, maxsize, 6132e192b24SSimon Glass (u_char *)addr); 6142e192b24SSimon Glass else 615151c06ecSScott Wood ret = nand_write_skip_bad(mtd, off, &rwsize, 6162e192b24SSimon Glass NULL, maxsize, 6172e192b24SSimon Glass (u_char *)addr, 6182e192b24SSimon Glass WITH_WR_VERIFY); 6192e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TRIMFFS 6202e192b24SSimon Glass } else if (!strcmp(s, ".trimffs")) { 6212e192b24SSimon Glass if (read) { 6222e192b24SSimon Glass printf("Unknown nand command suffix '%s'\n", s); 6232e192b24SSimon Glass return 1; 6242e192b24SSimon Glass } 625151c06ecSScott Wood ret = nand_write_skip_bad(mtd, off, &rwsize, NULL, 6262e192b24SSimon Glass maxsize, (u_char *)addr, 6272e192b24SSimon Glass WITH_DROP_FFS | WITH_WR_VERIFY); 6282e192b24SSimon Glass #endif 6292e192b24SSimon Glass } else if (!strcmp(s, ".oob")) { 6302e192b24SSimon Glass /* out-of-band data */ 6312e192b24SSimon Glass mtd_oob_ops_t ops = { 6322e192b24SSimon Glass .oobbuf = (u8 *)addr, 6332e192b24SSimon Glass .ooblen = rwsize, 6342e192b24SSimon Glass .mode = MTD_OPS_RAW 6352e192b24SSimon Glass }; 6362e192b24SSimon Glass 6372e192b24SSimon Glass if (read) 638151c06ecSScott Wood ret = mtd_read_oob(mtd, off, &ops); 6392e192b24SSimon Glass else 640151c06ecSScott Wood ret = mtd_write_oob(mtd, off, &ops); 6412e192b24SSimon Glass } else if (raw) { 6422dc3c483SBoris Brezillon ret = raw_access(mtd, addr, off, pagecount, read, 6432dc3c483SBoris Brezillon no_verify); 6442e192b24SSimon Glass } else { 6452e192b24SSimon Glass printf("Unknown nand command suffix '%s'.\n", s); 6462e192b24SSimon Glass return 1; 6472e192b24SSimon Glass } 6482e192b24SSimon Glass 6492e192b24SSimon Glass printf(" %zu bytes %s: %s\n", rwsize, 6502e192b24SSimon Glass read ? "read" : "written", ret ? "ERROR" : "OK"); 6512e192b24SSimon Glass 6522e192b24SSimon Glass return ret == 0 ? 0 : 1; 6532e192b24SSimon Glass } 6542e192b24SSimon Glass 6552e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TORTURE 6562e192b24SSimon Glass if (strcmp(cmd, "torture") == 0) { 6571866be7dSMax Krummenacher loff_t endoff; 6581866be7dSMax Krummenacher unsigned int failed = 0, passed = 0; 6591866be7dSMax Krummenacher 6602e192b24SSimon Glass if (argc < 3) 6612e192b24SSimon Glass goto usage; 6622e192b24SSimon Glass 6632e192b24SSimon Glass if (!str2off(argv[2], &off)) { 6642e192b24SSimon Glass puts("Offset is not a valid number\n"); 6652e192b24SSimon Glass return 1; 6662e192b24SSimon Glass } 6672e192b24SSimon Glass 6681866be7dSMax Krummenacher size = mtd->erasesize; 6691866be7dSMax Krummenacher if (argc > 3) { 6701866be7dSMax Krummenacher if (!str2off(argv[3], &size)) { 6711866be7dSMax Krummenacher puts("Size is not a valid number\n"); 6721866be7dSMax Krummenacher return 1; 6731866be7dSMax Krummenacher } 6741866be7dSMax Krummenacher } 6752e192b24SSimon Glass 6761866be7dSMax Krummenacher endoff = off + size; 6771866be7dSMax Krummenacher if (endoff > mtd->size) { 6781866be7dSMax Krummenacher puts("Arguments beyond end of NAND\n"); 6791866be7dSMax Krummenacher return 1; 6801866be7dSMax Krummenacher } 6811866be7dSMax Krummenacher 6821866be7dSMax Krummenacher off = round_down(off, mtd->erasesize); 6831866be7dSMax Krummenacher endoff = round_up(endoff, mtd->erasesize); 6841866be7dSMax Krummenacher size = endoff - off; 6851866be7dSMax Krummenacher printf("\nNAND torture: device %d offset 0x%llx size 0x%llx (block size 0x%x)\n", 6861866be7dSMax Krummenacher dev, off, size, mtd->erasesize); 6871866be7dSMax Krummenacher while (off < endoff) { 6881866be7dSMax Krummenacher ret = nand_torture(mtd, off); 6891866be7dSMax Krummenacher if (ret) { 6901866be7dSMax Krummenacher failed++; 6911866be7dSMax Krummenacher printf(" block at 0x%llx failed\n", off); 6921866be7dSMax Krummenacher } else { 6931866be7dSMax Krummenacher passed++; 6941866be7dSMax Krummenacher } 6951866be7dSMax Krummenacher off += mtd->erasesize; 6961866be7dSMax Krummenacher } 6971866be7dSMax Krummenacher printf(" Passed: %u, failed: %u\n", passed, failed); 6981866be7dSMax Krummenacher return failed != 0; 6992e192b24SSimon Glass } 7002e192b24SSimon Glass #endif 7012e192b24SSimon Glass 7022e192b24SSimon Glass if (strcmp(cmd, "markbad") == 0) { 7032e192b24SSimon Glass argc -= 2; 7042e192b24SSimon Glass argv += 2; 7052e192b24SSimon Glass 7062e192b24SSimon Glass if (argc <= 0) 7072e192b24SSimon Glass goto usage; 7082e192b24SSimon Glass 7092e192b24SSimon Glass while (argc > 0) { 7102e192b24SSimon Glass addr = simple_strtoul(*argv, NULL, 16); 7112e192b24SSimon Glass 712151c06ecSScott Wood if (mtd_block_markbad(mtd, addr)) { 7132e192b24SSimon Glass printf("block 0x%08lx NOT marked " 7142e192b24SSimon Glass "as bad! ERROR %d\n", 7152e192b24SSimon Glass addr, ret); 7162e192b24SSimon Glass ret = 1; 7172e192b24SSimon Glass } else { 7182e192b24SSimon Glass printf("block 0x%08lx successfully " 7192e192b24SSimon Glass "marked as bad\n", 7202e192b24SSimon Glass addr); 7212e192b24SSimon Glass } 7222e192b24SSimon Glass --argc; 7232e192b24SSimon Glass ++argv; 7242e192b24SSimon Glass } 7252e192b24SSimon Glass return ret; 7262e192b24SSimon Glass } 7272e192b24SSimon Glass 7282e192b24SSimon Glass if (strcmp(cmd, "biterr") == 0) { 7292e192b24SSimon Glass /* todo */ 7302e192b24SSimon Glass return 1; 7312e192b24SSimon Glass } 7322e192b24SSimon Glass 7332e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK 7342e192b24SSimon Glass if (strcmp(cmd, "lock") == 0) { 7352e192b24SSimon Glass int tight = 0; 7362e192b24SSimon Glass int status = 0; 7372e192b24SSimon Glass if (argc == 3) { 7382e192b24SSimon Glass if (!strcmp("tight", argv[2])) 7392e192b24SSimon Glass tight = 1; 7402e192b24SSimon Glass if (!strcmp("status", argv[2])) 7412e192b24SSimon Glass status = 1; 7422e192b24SSimon Glass } 7432e192b24SSimon Glass if (status) { 744151c06ecSScott Wood do_nand_status(mtd); 7452e192b24SSimon Glass } else { 746151c06ecSScott Wood if (!nand_lock(mtd, tight)) { 7472e192b24SSimon Glass puts("NAND flash successfully locked\n"); 7482e192b24SSimon Glass } else { 7492e192b24SSimon Glass puts("Error locking NAND flash\n"); 7502e192b24SSimon Glass return 1; 7512e192b24SSimon Glass } 7522e192b24SSimon Glass } 7532e192b24SSimon Glass return 0; 7542e192b24SSimon Glass } 7552e192b24SSimon Glass 7562e192b24SSimon Glass if (strncmp(cmd, "unlock", 5) == 0) { 7572e192b24SSimon Glass int allexcept = 0; 7582e192b24SSimon Glass 7592e192b24SSimon Glass s = strchr(cmd, '.'); 7602e192b24SSimon Glass 7612e192b24SSimon Glass if (s && !strcmp(s, ".allexcept")) 7622e192b24SSimon Glass allexcept = 1; 7632e192b24SSimon Glass 7642e192b24SSimon Glass if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &off, &size, 7652e192b24SSimon Glass &maxsize, MTD_DEV_TYPE_NAND, 766ad92dff2SMugunthan V N mtd->size) < 0) 7672e192b24SSimon Glass return 1; 7682e192b24SSimon Glass 7692e192b24SSimon Glass if (set_dev(dev)) 7702e192b24SSimon Glass return 1; 7712e192b24SSimon Glass 772ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(dev); 773ad92dff2SMugunthan V N 774ad92dff2SMugunthan V N if (!nand_unlock(mtd, off, size, allexcept)) { 7752e192b24SSimon Glass puts("NAND flash successfully unlocked\n"); 7762e192b24SSimon Glass } else { 7772e192b24SSimon Glass puts("Error unlocking NAND flash, " 7782e192b24SSimon Glass "write and erase will probably fail\n"); 7792e192b24SSimon Glass return 1; 7802e192b24SSimon Glass } 7812e192b24SSimon Glass return 0; 7822e192b24SSimon Glass } 7832e192b24SSimon Glass #endif 7842e192b24SSimon Glass 7852e192b24SSimon Glass usage: 7862e192b24SSimon Glass return CMD_RET_USAGE; 7872e192b24SSimon Glass } 7882e192b24SSimon Glass 7892e192b24SSimon Glass #ifdef CONFIG_SYS_LONGHELP 7902e192b24SSimon Glass static char nand_help_text[] = 7912e192b24SSimon Glass "info - show available NAND devices\n" 7922e192b24SSimon Glass "nand device [dev] - show or set current device\n" 7932e192b24SSimon Glass "nand read - addr off|partition size\n" 7942e192b24SSimon Glass "nand write - addr off|partition size\n" 7952e192b24SSimon Glass " read/write 'size' bytes starting at offset 'off'\n" 7962e192b24SSimon Glass " to/from memory address 'addr', skipping bad blocks.\n" 7972e192b24SSimon Glass "nand read.raw - addr off|partition [count]\n" 7982dc3c483SBoris Brezillon "nand write.raw[.noverify] - addr off|partition [count]\n" 7992e192b24SSimon Glass " Use read.raw/write.raw to avoid ECC and access the flash as-is.\n" 8002e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TRIMFFS 8012e192b24SSimon Glass "nand write.trimffs - addr off|partition size\n" 8022e192b24SSimon Glass " write 'size' bytes starting at offset 'off' from memory address\n" 8032e192b24SSimon Glass " 'addr', skipping bad blocks and dropping any pages at the end\n" 8042e192b24SSimon Glass " of eraseblocks that contain only 0xFF\n" 8052e192b24SSimon Glass #endif 8062e192b24SSimon Glass "nand erase[.spread] [clean] off size - erase 'size' bytes " 8072e192b24SSimon Glass "from offset 'off'\n" 8082e192b24SSimon Glass " With '.spread', erase enough for given file size, otherwise,\n" 8092e192b24SSimon Glass " 'size' includes skipped bad blocks.\n" 8102e192b24SSimon Glass "nand erase.part [clean] partition - erase entire mtd partition'\n" 8112e192b24SSimon Glass "nand erase.chip [clean] - erase entire chip'\n" 8122e192b24SSimon Glass "nand bad - show bad blocks\n" 8132e192b24SSimon Glass "nand dump[.oob] off - dump page\n" 8142e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TORTURE 8151866be7dSMax Krummenacher "nand torture off - torture one block at offset\n" 8161866be7dSMax Krummenacher "nand torture off [size] - torture blocks from off to off+size\n" 8172e192b24SSimon Glass #endif 8182e192b24SSimon Glass "nand scrub [-y] off size | scrub.part partition | scrub.chip\n" 8192e192b24SSimon Glass " really clean NAND erasing bad blocks (UNSAFE)\n" 8202e192b24SSimon Glass "nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n" 8212e192b24SSimon Glass "nand biterr off - make a bit error at offset (UNSAFE)" 8222e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK 8232e192b24SSimon Glass "\n" 8242e192b24SSimon Glass "nand lock [tight] [status]\n" 8252e192b24SSimon Glass " bring nand to lock state or display locked pages\n" 8262e192b24SSimon Glass "nand unlock[.allexcept] [offset] [size] - unlock section" 8272e192b24SSimon Glass #endif 8282e192b24SSimon Glass #ifdef CONFIG_ENV_OFFSET_OOB 8292e192b24SSimon Glass "\n" 8302e192b24SSimon Glass "nand env.oob - environment offset in OOB of block 0 of" 8312e192b24SSimon Glass " first device.\n" 8322e192b24SSimon Glass "nand env.oob set off|partition - set enviromnent offset\n" 8332e192b24SSimon Glass "nand env.oob get - get environment offset" 8342e192b24SSimon Glass #endif 8352e192b24SSimon Glass ""; 8362e192b24SSimon Glass #endif 8372e192b24SSimon Glass 8382e192b24SSimon Glass U_BOOT_CMD( 8392e192b24SSimon Glass nand, CONFIG_SYS_MAXARGS, 1, do_nand, 8402e192b24SSimon Glass "NAND sub-system", nand_help_text 8412e192b24SSimon Glass ); 8422e192b24SSimon Glass 843151c06ecSScott Wood static int nand_load_image(cmd_tbl_t *cmdtp, struct mtd_info *mtd, 8442e192b24SSimon Glass ulong offset, ulong addr, char *cmd) 8452e192b24SSimon Glass { 8462e192b24SSimon Glass int r; 8472e192b24SSimon Glass char *s; 8482e192b24SSimon Glass size_t cnt; 8492e192b24SSimon Glass #if defined(CONFIG_IMAGE_FORMAT_LEGACY) 8502e192b24SSimon Glass image_header_t *hdr; 8512e192b24SSimon Glass #endif 8522e192b24SSimon Glass #if defined(CONFIG_FIT) 8532e192b24SSimon Glass const void *fit_hdr = NULL; 8542e192b24SSimon Glass #endif 8552e192b24SSimon Glass 8562e192b24SSimon Glass s = strchr(cmd, '.'); 8572e192b24SSimon Glass if (s != NULL && 8582e192b24SSimon Glass (strcmp(s, ".jffs2") && strcmp(s, ".e") && strcmp(s, ".i"))) { 8592e192b24SSimon Glass printf("Unknown nand load suffix '%s'\n", s); 8602e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX); 8612e192b24SSimon Glass return 1; 8622e192b24SSimon Glass } 8632e192b24SSimon Glass 864151c06ecSScott Wood printf("\nLoading from %s, offset 0x%lx\n", mtd->name, offset); 8652e192b24SSimon Glass 866151c06ecSScott Wood cnt = mtd->writesize; 867151c06ecSScott Wood r = nand_read_skip_bad(mtd, offset, &cnt, NULL, mtd->size, 8682e192b24SSimon Glass (u_char *)addr); 8692e192b24SSimon Glass if (r) { 8702e192b24SSimon Glass puts("** Read error\n"); 8712e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_HDR_READ); 8722e192b24SSimon Glass return 1; 8732e192b24SSimon Glass } 8742e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_HDR_READ); 8752e192b24SSimon Glass 8762e192b24SSimon Glass switch (genimg_get_format ((void *)addr)) { 8772e192b24SSimon Glass #if defined(CONFIG_IMAGE_FORMAT_LEGACY) 8782e192b24SSimon Glass case IMAGE_FORMAT_LEGACY: 8792e192b24SSimon Glass hdr = (image_header_t *)addr; 8802e192b24SSimon Glass 8812e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); 8822e192b24SSimon Glass image_print_contents (hdr); 8832e192b24SSimon Glass 8842e192b24SSimon Glass cnt = image_get_image_size (hdr); 8852e192b24SSimon Glass break; 8862e192b24SSimon Glass #endif 8872e192b24SSimon Glass #if defined(CONFIG_FIT) 8882e192b24SSimon Glass case IMAGE_FORMAT_FIT: 8892e192b24SSimon Glass fit_hdr = (const void *)addr; 8902e192b24SSimon Glass puts ("Fit image detected...\n"); 8912e192b24SSimon Glass 8922e192b24SSimon Glass cnt = fit_get_size (fit_hdr); 8932e192b24SSimon Glass break; 8942e192b24SSimon Glass #endif 8952e192b24SSimon Glass default: 8962e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_TYPE); 8972e192b24SSimon Glass puts ("** Unknown image type\n"); 8982e192b24SSimon Glass return 1; 8992e192b24SSimon Glass } 9002e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); 9012e192b24SSimon Glass 902151c06ecSScott Wood r = nand_read_skip_bad(mtd, offset, &cnt, NULL, mtd->size, 9032e192b24SSimon Glass (u_char *)addr); 9042e192b24SSimon Glass if (r) { 9052e192b24SSimon Glass puts("** Read error\n"); 9062e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_READ); 9072e192b24SSimon Glass return 1; 9082e192b24SSimon Glass } 9092e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_READ); 9102e192b24SSimon Glass 9112e192b24SSimon Glass #if defined(CONFIG_FIT) 9122e192b24SSimon Glass /* This cannot be done earlier, we need complete FIT image in RAM first */ 9132e192b24SSimon Glass if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) { 9142e192b24SSimon Glass if (!fit_check_format (fit_hdr)) { 9152e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_FIT_READ); 9162e192b24SSimon Glass puts ("** Bad FIT image format\n"); 9172e192b24SSimon Glass return 1; 9182e192b24SSimon Glass } 9192e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_FIT_READ_OK); 9202e192b24SSimon Glass fit_print_contents (fit_hdr); 9212e192b24SSimon Glass } 9222e192b24SSimon Glass #endif 9232e192b24SSimon Glass 9242e192b24SSimon Glass /* Loading ok, update default load address */ 9252e192b24SSimon Glass 9262e192b24SSimon Glass load_addr = addr; 9272e192b24SSimon Glass 9282e192b24SSimon Glass return bootm_maybe_autostart(cmdtp, cmd); 9292e192b24SSimon Glass } 9302e192b24SSimon Glass 9312e192b24SSimon Glass static int do_nandboot(cmd_tbl_t *cmdtp, int flag, int argc, 9322e192b24SSimon Glass char * const argv[]) 9332e192b24SSimon Glass { 9342e192b24SSimon Glass char *boot_device = NULL; 9352e192b24SSimon Glass int idx; 9362e192b24SSimon Glass ulong addr, offset = 0; 937ad92dff2SMugunthan V N struct mtd_info *mtd; 9382e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS) 9392e192b24SSimon Glass struct mtd_device *dev; 9402e192b24SSimon Glass struct part_info *part; 9412e192b24SSimon Glass u8 pnum; 9422e192b24SSimon Glass 9432e192b24SSimon Glass if (argc >= 2) { 9442e192b24SSimon Glass char *p = (argc == 2) ? argv[1] : argv[2]; 9452e192b24SSimon Glass if (!(str2long(p, &addr)) && (mtdparts_init() == 0) && 9462e192b24SSimon Glass (find_dev_and_part(p, &dev, &pnum, &part) == 0)) { 9472e192b24SSimon Glass if (dev->id->type != MTD_DEV_TYPE_NAND) { 9482e192b24SSimon Glass puts("Not a NAND device\n"); 9492e192b24SSimon Glass return 1; 9502e192b24SSimon Glass } 9512e192b24SSimon Glass if (argc > 3) 9522e192b24SSimon Glass goto usage; 9532e192b24SSimon Glass if (argc == 3) 9542e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 9552e192b24SSimon Glass else 9562e192b24SSimon Glass addr = CONFIG_SYS_LOAD_ADDR; 957ad92dff2SMugunthan V N 958ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(dev->id->num); 959ad92dff2SMugunthan V N return nand_load_image(cmdtp, mtd, part->offset, 960ad92dff2SMugunthan V N addr, argv[0]); 9612e192b24SSimon Glass } 9622e192b24SSimon Glass } 9632e192b24SSimon Glass #endif 9642e192b24SSimon Glass 9652e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_PART); 9662e192b24SSimon Glass switch (argc) { 9672e192b24SSimon Glass case 1: 9682e192b24SSimon Glass addr = CONFIG_SYS_LOAD_ADDR; 969*00caae6dSSimon Glass boot_device = env_get("bootdevice"); 9702e192b24SSimon Glass break; 9712e192b24SSimon Glass case 2: 9722e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 973*00caae6dSSimon Glass boot_device = env_get("bootdevice"); 9742e192b24SSimon Glass break; 9752e192b24SSimon Glass case 3: 9762e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 9772e192b24SSimon Glass boot_device = argv[2]; 9782e192b24SSimon Glass break; 9792e192b24SSimon Glass case 4: 9802e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 9812e192b24SSimon Glass boot_device = argv[2]; 9822e192b24SSimon Glass offset = simple_strtoul(argv[3], NULL, 16); 9832e192b24SSimon Glass break; 9842e192b24SSimon Glass default: 9852e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS) 9862e192b24SSimon Glass usage: 9872e192b24SSimon Glass #endif 9882e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX); 9892e192b24SSimon Glass return CMD_RET_USAGE; 9902e192b24SSimon Glass } 9912e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_SUFFIX); 9922e192b24SSimon Glass 9932e192b24SSimon Glass if (!boot_device) { 9942e192b24SSimon Glass puts("\n** No boot device **\n"); 9952e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_BOOT_DEVICE); 9962e192b24SSimon Glass return 1; 9972e192b24SSimon Glass } 9982e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_BOOT_DEVICE); 9992e192b24SSimon Glass 10002e192b24SSimon Glass idx = simple_strtoul(boot_device, NULL, 16); 10012e192b24SSimon Glass 1002ad92dff2SMugunthan V N mtd = get_nand_dev_by_index(idx); 1003ad92dff2SMugunthan V N if (!mtd) { 10042e192b24SSimon Glass printf("\n** Device %d not available\n", idx); 10052e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_AVAILABLE); 10062e192b24SSimon Glass return 1; 10072e192b24SSimon Glass } 10082e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_AVAILABLE); 10092e192b24SSimon Glass 1010ad92dff2SMugunthan V N return nand_load_image(cmdtp, mtd, offset, addr, argv[0]); 10112e192b24SSimon Glass } 10122e192b24SSimon Glass 10132e192b24SSimon Glass U_BOOT_CMD(nboot, 4, 1, do_nandboot, 10142e192b24SSimon Glass "boot from NAND device", 10152e192b24SSimon Glass "[partition] | [[[loadAddr] dev] offset]" 10162e192b24SSimon Glass ); 1017