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 { 1181cfce74fSTony Lindgren if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev]) { 1192e192b24SSimon Glass puts("No such device\n"); 1202e192b24SSimon Glass return -1; 1212e192b24SSimon Glass } 1222e192b24SSimon Glass 1232e192b24SSimon Glass if (nand_curr_device == dev) 1242e192b24SSimon Glass return 0; 1252e192b24SSimon Glass 126b616d9b0SScott Wood printf("Device %d: %s", dev, nand_info[dev]->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 131b616d9b0SScott Wood board_nand_select_device(nand_info[dev]->priv, 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)]; 191b616d9b0SScott Wood struct mtd_info *mtd = nand_info[0]; 1922e192b24SSimon Glass char *cmd = argv[1]; 1932e192b24SSimon Glass 194*8b7d5124SScott 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 2162e192b24SSimon Glass /* We don't care about size, or maxsize. */ 2172e192b24SSimon Glass if (mtd_arg_off(argv[2], &idx, &addr, &maxsize, &maxsize, 218b616d9b0SScott Wood MTD_DEV_TYPE_NAND, nand_info[idx]->size)) { 2192e192b24SSimon Glass puts("Offset or partition name expected\n"); 2202e192b24SSimon Glass return 1; 2212e192b24SSimon Glass } 2222e192b24SSimon Glass if (set_dev(idx)) { 2232e192b24SSimon Glass puts("Offset or partition name expected\n"); 2242e192b24SSimon Glass return 1; 2252e192b24SSimon Glass } 2262e192b24SSimon Glass 2272e192b24SSimon Glass if (idx != 0) { 2282e192b24SSimon Glass puts("Partition not on first NAND device\n"); 2292e192b24SSimon Glass return 1; 2302e192b24SSimon Glass } 2312e192b24SSimon Glass 232151c06ecSScott Wood if (mtd->oobavail < ENV_OFFSET_SIZE) { 2332e192b24SSimon Glass printf("Insufficient available OOB bytes:\n" 2342e192b24SSimon Glass "%d OOB bytes available but %d required for " 2352e192b24SSimon Glass "env.oob support\n", 236151c06ecSScott Wood mtd->oobavail, ENV_OFFSET_SIZE); 2372e192b24SSimon Glass return 1; 2382e192b24SSimon Glass } 2392e192b24SSimon Glass 240151c06ecSScott Wood if ((addr & (mtd->erasesize - 1)) != 0) { 2412e192b24SSimon Glass printf("Environment offset must be block-aligned\n"); 2422e192b24SSimon Glass return 1; 2432e192b24SSimon Glass } 2442e192b24SSimon Glass 2452e192b24SSimon Glass ops.datbuf = NULL; 2462e192b24SSimon Glass ops.mode = MTD_OOB_AUTO; 2472e192b24SSimon Glass ops.ooboffs = 0; 2482e192b24SSimon Glass ops.ooblen = ENV_OFFSET_SIZE; 2492e192b24SSimon Glass ops.oobbuf = (void *) oob_buf; 2502e192b24SSimon Glass 2512e192b24SSimon Glass oob_buf[0] = ENV_OOB_MARKER; 252151c06ecSScott Wood oob_buf[1] = addr / mtd->erasesize; 2532e192b24SSimon Glass 254151c06ecSScott Wood ret = mtd->write_oob(mtd, ENV_OFFSET_SIZE, &ops); 2552e192b24SSimon Glass if (ret) { 2562e192b24SSimon Glass printf("Error writing OOB block 0\n"); 2572e192b24SSimon Glass return ret; 2582e192b24SSimon Glass } 2592e192b24SSimon Glass 260151c06ecSScott Wood ret = get_nand_env_oob(mtd, &nand_env_oob_offset); 2612e192b24SSimon Glass if (ret) { 2622e192b24SSimon Glass printf("Error reading env offset in OOB\n"); 2632e192b24SSimon Glass return ret; 2642e192b24SSimon Glass } 2652e192b24SSimon Glass 2662e192b24SSimon Glass if (addr != nand_env_oob_offset) { 2672e192b24SSimon Glass printf("Verification of env offset in OOB failed: " 2682e192b24SSimon Glass "0x%08llx expected but got 0x%08lx\n", 2692e192b24SSimon Glass (unsigned long long)addr, nand_env_oob_offset); 2702e192b24SSimon Glass return 1; 2712e192b24SSimon Glass } 2722e192b24SSimon Glass } else { 2732e192b24SSimon Glass goto usage; 2742e192b24SSimon Glass } 2752e192b24SSimon Glass 2762e192b24SSimon Glass return ret; 2772e192b24SSimon Glass 2782e192b24SSimon Glass usage: 2792e192b24SSimon Glass return CMD_RET_USAGE; 2802e192b24SSimon Glass } 2812e192b24SSimon Glass 2822e192b24SSimon Glass #endif 2832e192b24SSimon Glass 2842e192b24SSimon Glass static void nand_print_and_set_info(int idx) 2852e192b24SSimon Glass { 286b616d9b0SScott Wood struct mtd_info *mtd = nand_info[idx]; 28717cb4b8fSScott Wood struct nand_chip *chip = mtd_to_nand(mtd); 2882e192b24SSimon Glass 2892e192b24SSimon Glass printf("Device %d: ", idx); 2902e192b24SSimon Glass if (chip->numchips > 1) 2912e192b24SSimon Glass printf("%dx ", chip->numchips); 2922e192b24SSimon Glass printf("%s, sector size %u KiB\n", 293151c06ecSScott Wood mtd->name, mtd->erasesize >> 10); 294151c06ecSScott Wood printf(" Page size %8d b\n", mtd->writesize); 295151c06ecSScott Wood printf(" OOB size %8d b\n", mtd->oobsize); 296151c06ecSScott Wood printf(" Erase size %8d b\n", mtd->erasesize); 2972e192b24SSimon Glass printf(" subpagesize %8d b\n", chip->subpagesize); 2982e192b24SSimon Glass printf(" options 0x%8x\n", chip->options); 2992e192b24SSimon Glass printf(" bbt options 0x%8x\n", chip->bbt_options); 3002e192b24SSimon Glass 3012e192b24SSimon Glass /* Set geometry info */ 302151c06ecSScott Wood setenv_hex("nand_writesize", mtd->writesize); 303151c06ecSScott Wood setenv_hex("nand_oobsize", mtd->oobsize); 304151c06ecSScott Wood setenv_hex("nand_erasesize", mtd->erasesize); 3052e192b24SSimon Glass } 3062e192b24SSimon Glass 307151c06ecSScott Wood static int raw_access(struct mtd_info *mtd, ulong addr, loff_t off, 3082dc3c483SBoris Brezillon ulong count, int read, int no_verify) 3092e192b24SSimon Glass { 3102e192b24SSimon Glass int ret = 0; 3112e192b24SSimon Glass 3122e192b24SSimon Glass while (count--) { 3132e192b24SSimon Glass /* Raw access */ 3142e192b24SSimon Glass mtd_oob_ops_t ops = { 3152e192b24SSimon Glass .datbuf = (u8 *)addr, 316151c06ecSScott Wood .oobbuf = ((u8 *)addr) + mtd->writesize, 317151c06ecSScott Wood .len = mtd->writesize, 318151c06ecSScott Wood .ooblen = mtd->oobsize, 3192e192b24SSimon Glass .mode = MTD_OPS_RAW 3202e192b24SSimon Glass }; 3212e192b24SSimon Glass 3222e192b24SSimon Glass if (read) { 323151c06ecSScott Wood ret = mtd_read_oob(mtd, off, &ops); 3242e192b24SSimon Glass } else { 325151c06ecSScott Wood ret = mtd_write_oob(mtd, off, &ops); 3262dc3c483SBoris Brezillon if (!ret && !no_verify) 327151c06ecSScott Wood ret = nand_verify_page_oob(mtd, &ops, off); 3282e192b24SSimon Glass } 3292e192b24SSimon Glass 3302e192b24SSimon Glass if (ret) { 3312e192b24SSimon Glass printf("%s: error at offset %llx, ret %d\n", 3322e192b24SSimon Glass __func__, (long long)off, ret); 3332e192b24SSimon Glass break; 3342e192b24SSimon Glass } 3352e192b24SSimon Glass 336151c06ecSScott Wood addr += mtd->writesize + mtd->oobsize; 337151c06ecSScott Wood off += mtd->writesize; 3382e192b24SSimon Glass } 3392e192b24SSimon Glass 3402e192b24SSimon Glass return ret; 3412e192b24SSimon Glass } 3422e192b24SSimon Glass 3432e192b24SSimon Glass /* Adjust a chip/partition size down for bad blocks so we don't 3442e192b24SSimon Glass * read/write past the end of a chip/partition by accident. 3452e192b24SSimon Glass */ 3462e192b24SSimon Glass static void adjust_size_for_badblocks(loff_t *size, loff_t offset, int dev) 3472e192b24SSimon Glass { 3482e192b24SSimon Glass /* We grab the nand info object here fresh because this is usually 3492e192b24SSimon Glass * called after arg_off_size() which can change the value of dev. 3502e192b24SSimon Glass */ 351b616d9b0SScott Wood struct mtd_info *mtd = nand_info[dev]; 3522e192b24SSimon Glass loff_t maxoffset = offset + *size; 3532e192b24SSimon Glass int badblocks = 0; 3542e192b24SSimon Glass 3552e192b24SSimon Glass /* count badblocks in NAND from offset to offset + size */ 356151c06ecSScott Wood for (; offset < maxoffset; offset += mtd->erasesize) { 357151c06ecSScott Wood if (nand_block_isbad(mtd, offset)) 3582e192b24SSimon Glass badblocks++; 3592e192b24SSimon Glass } 3602e192b24SSimon Glass /* adjust size if any bad blocks found */ 3612e192b24SSimon Glass if (badblocks) { 362151c06ecSScott Wood *size -= badblocks * mtd->erasesize; 3632e192b24SSimon Glass printf("size adjusted to 0x%llx (%d bad blocks)\n", 3642e192b24SSimon Glass (unsigned long long)*size, badblocks); 3652e192b24SSimon Glass } 3662e192b24SSimon Glass } 3672e192b24SSimon Glass 3682e192b24SSimon Glass static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 3692e192b24SSimon Glass { 3702e192b24SSimon Glass int i, ret = 0; 3712e192b24SSimon Glass ulong addr; 3722e192b24SSimon Glass loff_t off, size, maxsize; 3732e192b24SSimon Glass char *cmd, *s; 374151c06ecSScott Wood struct mtd_info *mtd; 3752e192b24SSimon Glass #ifdef CONFIG_SYS_NAND_QUIET 3762e192b24SSimon Glass int quiet = CONFIG_SYS_NAND_QUIET; 3772e192b24SSimon Glass #else 3782e192b24SSimon Glass int quiet = 0; 3792e192b24SSimon Glass #endif 3802e192b24SSimon Glass const char *quiet_str = getenv("quiet"); 3812e192b24SSimon Glass int dev = nand_curr_device; 3822e192b24SSimon Glass int repeat = flag & CMD_FLAG_REPEAT; 3832e192b24SSimon Glass 3842e192b24SSimon Glass /* at least two arguments please */ 3852e192b24SSimon Glass if (argc < 2) 3862e192b24SSimon Glass goto usage; 3872e192b24SSimon Glass 3882e192b24SSimon Glass if (quiet_str) 3892e192b24SSimon Glass quiet = simple_strtoul(quiet_str, NULL, 0) != 0; 3902e192b24SSimon Glass 3912e192b24SSimon Glass cmd = argv[1]; 3922e192b24SSimon Glass 3932e192b24SSimon Glass /* Only "dump" is repeatable. */ 3942e192b24SSimon Glass if (repeat && strcmp(cmd, "dump")) 3952e192b24SSimon Glass return 0; 3962e192b24SSimon Glass 3972e192b24SSimon Glass if (strcmp(cmd, "info") == 0) { 3982e192b24SSimon Glass 3992e192b24SSimon Glass putc('\n'); 4002e192b24SSimon Glass for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { 4014004a818STony Lindgren if (nand_info[i]) 4022e192b24SSimon Glass nand_print_and_set_info(i); 4032e192b24SSimon Glass } 4042e192b24SSimon Glass return 0; 4052e192b24SSimon Glass } 4062e192b24SSimon Glass 4072e192b24SSimon Glass if (strcmp(cmd, "device") == 0) { 4082e192b24SSimon Glass if (argc < 3) { 4092e192b24SSimon Glass putc('\n'); 4102e192b24SSimon Glass if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE) 4112e192b24SSimon Glass puts("no devices available\n"); 4122e192b24SSimon Glass else 4132e192b24SSimon Glass nand_print_and_set_info(dev); 4142e192b24SSimon Glass return 0; 4152e192b24SSimon Glass } 4162e192b24SSimon Glass 4172e192b24SSimon Glass dev = (int)simple_strtoul(argv[2], NULL, 10); 4182e192b24SSimon Glass set_dev(dev); 4192e192b24SSimon Glass 4202e192b24SSimon Glass return 0; 4212e192b24SSimon Glass } 4222e192b24SSimon Glass 4232e192b24SSimon Glass #ifdef CONFIG_ENV_OFFSET_OOB 4242e192b24SSimon Glass /* this command operates only on the first nand device */ 4252e192b24SSimon Glass if (strcmp(cmd, "env.oob") == 0) 4262e192b24SSimon Glass return do_nand_env_oob(cmdtp, argc - 1, argv + 1); 4272e192b24SSimon Glass #endif 4282e192b24SSimon Glass 4292e192b24SSimon Glass /* The following commands operate on the current device, unless 4302e192b24SSimon Glass * overridden by a partition specifier. Note that if somehow the 4312e192b24SSimon Glass * current device is invalid, it will have to be changed to a valid 4322e192b24SSimon Glass * one before these commands can run, even if a partition specifier 4332e192b24SSimon Glass * for another device is to be used. 4342e192b24SSimon Glass */ 4352e192b24SSimon Glass if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || 4364004a818STony Lindgren !nand_info[dev]) { 4372e192b24SSimon Glass puts("\nno devices available\n"); 4382e192b24SSimon Glass return 1; 4392e192b24SSimon Glass } 440b616d9b0SScott Wood mtd = nand_info[dev]; 4412e192b24SSimon Glass 4422e192b24SSimon Glass if (strcmp(cmd, "bad") == 0) { 4432e192b24SSimon Glass printf("\nDevice %d bad blocks:\n", dev); 444151c06ecSScott Wood for (off = 0; off < mtd->size; off += mtd->erasesize) 445151c06ecSScott Wood if (nand_block_isbad(mtd, off)) 4462e192b24SSimon Glass printf(" %08llx\n", (unsigned long long)off); 4472e192b24SSimon Glass return 0; 4482e192b24SSimon Glass } 4492e192b24SSimon Glass 4502e192b24SSimon Glass /* 4512e192b24SSimon Glass * Syntax is: 4522e192b24SSimon Glass * 0 1 2 3 4 4532e192b24SSimon Glass * nand erase [clean] [off size] 4542e192b24SSimon Glass */ 4552e192b24SSimon Glass if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) { 4562e192b24SSimon Glass nand_erase_options_t opts; 4572e192b24SSimon Glass /* "clean" at index 2 means request to write cleanmarker */ 4582e192b24SSimon Glass int clean = argc > 2 && !strcmp("clean", argv[2]); 4592e192b24SSimon Glass int scrub_yes = argc > 2 && !strcmp("-y", argv[2]); 4602e192b24SSimon Glass int o = (clean || scrub_yes) ? 3 : 2; 4612e192b24SSimon Glass int scrub = !strncmp(cmd, "scrub", 5); 4622e192b24SSimon Glass int spread = 0; 4632e192b24SSimon Glass int args = 2; 4642e192b24SSimon Glass const char *scrub_warn = 4652e192b24SSimon Glass "Warning: " 4662e192b24SSimon Glass "scrub option will erase all factory set bad blocks!\n" 4672e192b24SSimon Glass " " 4682e192b24SSimon Glass "There is no reliable way to recover them.\n" 4692e192b24SSimon Glass " " 4702e192b24SSimon Glass "Use this command only for testing purposes if you\n" 4712e192b24SSimon Glass " " 4722e192b24SSimon Glass "are sure of what you are doing!\n" 4732e192b24SSimon Glass "\nReally scrub this NAND flash? <y/N>\n"; 4742e192b24SSimon Glass 4752e192b24SSimon Glass if (cmd[5] != 0) { 4762e192b24SSimon Glass if (!strcmp(&cmd[5], ".spread")) { 4772e192b24SSimon Glass spread = 1; 4782e192b24SSimon Glass } else if (!strcmp(&cmd[5], ".part")) { 4792e192b24SSimon Glass args = 1; 4802e192b24SSimon Glass } else if (!strcmp(&cmd[5], ".chip")) { 4812e192b24SSimon Glass args = 0; 4822e192b24SSimon Glass } else { 4832e192b24SSimon Glass goto usage; 4842e192b24SSimon Glass } 4852e192b24SSimon Glass } 4862e192b24SSimon Glass 4872e192b24SSimon Glass /* 4882e192b24SSimon Glass * Don't allow missing arguments to cause full chip/partition 4892e192b24SSimon Glass * erases -- easy to do accidentally, e.g. with a misspelled 4902e192b24SSimon Glass * variable name. 4912e192b24SSimon Glass */ 4922e192b24SSimon Glass if (argc != o + args) 4932e192b24SSimon Glass goto usage; 4942e192b24SSimon Glass 4952e192b24SSimon Glass printf("\nNAND %s: ", cmd); 4962e192b24SSimon Glass /* skip first two or three arguments, look for offset and size */ 4972e192b24SSimon Glass if (mtd_arg_off_size(argc - o, argv + o, &dev, &off, &size, 4982e192b24SSimon Glass &maxsize, MTD_DEV_TYPE_NAND, 499b616d9b0SScott Wood nand_info[dev]->size) != 0) 5002e192b24SSimon Glass return 1; 5012e192b24SSimon Glass 5022e192b24SSimon Glass if (set_dev(dev)) 5032e192b24SSimon Glass return 1; 5042e192b24SSimon Glass 505b616d9b0SScott Wood mtd = nand_info[dev]; 5062e192b24SSimon Glass 5072e192b24SSimon Glass memset(&opts, 0, sizeof(opts)); 5082e192b24SSimon Glass opts.offset = off; 5092e192b24SSimon Glass opts.length = size; 5102e192b24SSimon Glass opts.jffs2 = clean; 5112e192b24SSimon Glass opts.quiet = quiet; 5122e192b24SSimon Glass opts.spread = spread; 5132e192b24SSimon Glass 5142e192b24SSimon Glass if (scrub) { 5152e192b24SSimon Glass if (scrub_yes) { 5162e192b24SSimon Glass opts.scrub = 1; 5172e192b24SSimon Glass } else { 5182e192b24SSimon Glass puts(scrub_warn); 5192e192b24SSimon Glass if (confirm_yesno()) { 5202e192b24SSimon Glass opts.scrub = 1; 5212e192b24SSimon Glass } else { 5222e192b24SSimon Glass puts("scrub aborted\n"); 5232e192b24SSimon Glass return 1; 5242e192b24SSimon Glass } 5252e192b24SSimon Glass } 5262e192b24SSimon Glass } 527151c06ecSScott Wood ret = nand_erase_opts(mtd, &opts); 5282e192b24SSimon Glass printf("%s\n", ret ? "ERROR" : "OK"); 5292e192b24SSimon Glass 5302e192b24SSimon Glass return ret == 0 ? 0 : 1; 5312e192b24SSimon Glass } 5322e192b24SSimon Glass 5332e192b24SSimon Glass if (strncmp(cmd, "dump", 4) == 0) { 5342e192b24SSimon Glass if (argc < 3) 5352e192b24SSimon Glass goto usage; 5362e192b24SSimon Glass 5372e192b24SSimon Glass off = (int)simple_strtoul(argv[2], NULL, 16); 538151c06ecSScott Wood ret = nand_dump(mtd, off, !strcmp(&cmd[4], ".oob"), repeat); 5392e192b24SSimon Glass 5402e192b24SSimon Glass return ret == 0 ? 1 : 0; 5412e192b24SSimon Glass } 5422e192b24SSimon Glass 5432e192b24SSimon Glass if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { 5442e192b24SSimon Glass size_t rwsize; 5452e192b24SSimon Glass ulong pagecount = 1; 5462e192b24SSimon Glass int read; 5472e192b24SSimon Glass int raw = 0; 5482dc3c483SBoris Brezillon int no_verify = 0; 5492e192b24SSimon Glass 5502e192b24SSimon Glass if (argc < 4) 5512e192b24SSimon Glass goto usage; 5522e192b24SSimon Glass 5532e192b24SSimon Glass addr = (ulong)simple_strtoul(argv[2], NULL, 16); 5542e192b24SSimon Glass 5552e192b24SSimon Glass read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ 5562e192b24SSimon Glass printf("\nNAND %s: ", read ? "read" : "write"); 5572e192b24SSimon Glass 5582e192b24SSimon Glass s = strchr(cmd, '.'); 5592e192b24SSimon Glass 5602dc3c483SBoris Brezillon if (s && !strncmp(s, ".raw", 4)) { 5612e192b24SSimon Glass raw = 1; 5622e192b24SSimon Glass 5632dc3c483SBoris Brezillon if (!strcmp(s, ".raw.noverify")) 5642dc3c483SBoris Brezillon no_verify = 1; 5652dc3c483SBoris Brezillon 5662e192b24SSimon Glass if (mtd_arg_off(argv[3], &dev, &off, &size, &maxsize, 5672e192b24SSimon Glass MTD_DEV_TYPE_NAND, 568b616d9b0SScott Wood nand_info[dev]->size)) 5692e192b24SSimon Glass return 1; 5702e192b24SSimon Glass 5712e192b24SSimon Glass if (set_dev(dev)) 5722e192b24SSimon Glass return 1; 5732e192b24SSimon Glass 574b616d9b0SScott Wood mtd = nand_info[dev]; 5752e192b24SSimon Glass 5762e192b24SSimon Glass if (argc > 4 && !str2long(argv[4], &pagecount)) { 5772e192b24SSimon Glass printf("'%s' is not a number\n", argv[4]); 5782e192b24SSimon Glass return 1; 5792e192b24SSimon Glass } 5802e192b24SSimon Glass 581151c06ecSScott Wood if (pagecount * mtd->writesize > size) { 5822e192b24SSimon Glass puts("Size exceeds partition or device limit\n"); 5832e192b24SSimon Glass return -1; 5842e192b24SSimon Glass } 5852e192b24SSimon Glass 586151c06ecSScott Wood rwsize = pagecount * (mtd->writesize + mtd->oobsize); 5872e192b24SSimon Glass } else { 5882e192b24SSimon Glass if (mtd_arg_off_size(argc - 3, argv + 3, &dev, &off, 5892e192b24SSimon Glass &size, &maxsize, 5902e192b24SSimon Glass MTD_DEV_TYPE_NAND, 591b616d9b0SScott Wood nand_info[dev]->size) != 0) 5922e192b24SSimon Glass return 1; 5932e192b24SSimon Glass 5942e192b24SSimon Glass if (set_dev(dev)) 5952e192b24SSimon Glass return 1; 5962e192b24SSimon Glass 5972e192b24SSimon Glass /* size is unspecified */ 5982e192b24SSimon Glass if (argc < 5) 5992e192b24SSimon Glass adjust_size_for_badblocks(&size, off, dev); 6002e192b24SSimon Glass rwsize = size; 6012e192b24SSimon Glass } 6022e192b24SSimon Glass 603b616d9b0SScott Wood mtd = nand_info[dev]; 6042e192b24SSimon Glass 6052e192b24SSimon Glass if (!s || !strcmp(s, ".jffs2") || 6062e192b24SSimon Glass !strcmp(s, ".e") || !strcmp(s, ".i")) { 6072e192b24SSimon Glass if (read) 608151c06ecSScott Wood ret = nand_read_skip_bad(mtd, off, &rwsize, 6092e192b24SSimon Glass NULL, maxsize, 6102e192b24SSimon Glass (u_char *)addr); 6112e192b24SSimon Glass else 612151c06ecSScott Wood ret = nand_write_skip_bad(mtd, off, &rwsize, 6132e192b24SSimon Glass NULL, maxsize, 6142e192b24SSimon Glass (u_char *)addr, 6152e192b24SSimon Glass WITH_WR_VERIFY); 6162e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TRIMFFS 6172e192b24SSimon Glass } else if (!strcmp(s, ".trimffs")) { 6182e192b24SSimon Glass if (read) { 6192e192b24SSimon Glass printf("Unknown nand command suffix '%s'\n", s); 6202e192b24SSimon Glass return 1; 6212e192b24SSimon Glass } 622151c06ecSScott Wood ret = nand_write_skip_bad(mtd, off, &rwsize, NULL, 6232e192b24SSimon Glass maxsize, (u_char *)addr, 6242e192b24SSimon Glass WITH_DROP_FFS | WITH_WR_VERIFY); 6252e192b24SSimon Glass #endif 6262e192b24SSimon Glass } else if (!strcmp(s, ".oob")) { 6272e192b24SSimon Glass /* out-of-band data */ 6282e192b24SSimon Glass mtd_oob_ops_t ops = { 6292e192b24SSimon Glass .oobbuf = (u8 *)addr, 6302e192b24SSimon Glass .ooblen = rwsize, 6312e192b24SSimon Glass .mode = MTD_OPS_RAW 6322e192b24SSimon Glass }; 6332e192b24SSimon Glass 6342e192b24SSimon Glass if (read) 635151c06ecSScott Wood ret = mtd_read_oob(mtd, off, &ops); 6362e192b24SSimon Glass else 637151c06ecSScott Wood ret = mtd_write_oob(mtd, off, &ops); 6382e192b24SSimon Glass } else if (raw) { 6392dc3c483SBoris Brezillon ret = raw_access(mtd, addr, off, pagecount, read, 6402dc3c483SBoris Brezillon no_verify); 6412e192b24SSimon Glass } else { 6422e192b24SSimon Glass printf("Unknown nand command suffix '%s'.\n", s); 6432e192b24SSimon Glass return 1; 6442e192b24SSimon Glass } 6452e192b24SSimon Glass 6462e192b24SSimon Glass printf(" %zu bytes %s: %s\n", rwsize, 6472e192b24SSimon Glass read ? "read" : "written", ret ? "ERROR" : "OK"); 6482e192b24SSimon Glass 6492e192b24SSimon Glass return ret == 0 ? 0 : 1; 6502e192b24SSimon Glass } 6512e192b24SSimon Glass 6522e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TORTURE 6532e192b24SSimon Glass if (strcmp(cmd, "torture") == 0) { 6541866be7dSMax Krummenacher loff_t endoff; 6551866be7dSMax Krummenacher unsigned int failed = 0, passed = 0; 6561866be7dSMax Krummenacher 6572e192b24SSimon Glass if (argc < 3) 6582e192b24SSimon Glass goto usage; 6592e192b24SSimon Glass 6602e192b24SSimon Glass if (!str2off(argv[2], &off)) { 6612e192b24SSimon Glass puts("Offset is not a valid number\n"); 6622e192b24SSimon Glass return 1; 6632e192b24SSimon Glass } 6642e192b24SSimon Glass 6651866be7dSMax Krummenacher size = mtd->erasesize; 6661866be7dSMax Krummenacher if (argc > 3) { 6671866be7dSMax Krummenacher if (!str2off(argv[3], &size)) { 6681866be7dSMax Krummenacher puts("Size is not a valid number\n"); 6691866be7dSMax Krummenacher return 1; 6701866be7dSMax Krummenacher } 6711866be7dSMax Krummenacher } 6722e192b24SSimon Glass 6731866be7dSMax Krummenacher endoff = off + size; 6741866be7dSMax Krummenacher if (endoff > mtd->size) { 6751866be7dSMax Krummenacher puts("Arguments beyond end of NAND\n"); 6761866be7dSMax Krummenacher return 1; 6771866be7dSMax Krummenacher } 6781866be7dSMax Krummenacher 6791866be7dSMax Krummenacher off = round_down(off, mtd->erasesize); 6801866be7dSMax Krummenacher endoff = round_up(endoff, mtd->erasesize); 6811866be7dSMax Krummenacher size = endoff - off; 6821866be7dSMax Krummenacher printf("\nNAND torture: device %d offset 0x%llx size 0x%llx (block size 0x%x)\n", 6831866be7dSMax Krummenacher dev, off, size, mtd->erasesize); 6841866be7dSMax Krummenacher while (off < endoff) { 6851866be7dSMax Krummenacher ret = nand_torture(mtd, off); 6861866be7dSMax Krummenacher if (ret) { 6871866be7dSMax Krummenacher failed++; 6881866be7dSMax Krummenacher printf(" block at 0x%llx failed\n", off); 6891866be7dSMax Krummenacher } else { 6901866be7dSMax Krummenacher passed++; 6911866be7dSMax Krummenacher } 6921866be7dSMax Krummenacher off += mtd->erasesize; 6931866be7dSMax Krummenacher } 6941866be7dSMax Krummenacher printf(" Passed: %u, failed: %u\n", passed, failed); 6951866be7dSMax Krummenacher return failed != 0; 6962e192b24SSimon Glass } 6972e192b24SSimon Glass #endif 6982e192b24SSimon Glass 6992e192b24SSimon Glass if (strcmp(cmd, "markbad") == 0) { 7002e192b24SSimon Glass argc -= 2; 7012e192b24SSimon Glass argv += 2; 7022e192b24SSimon Glass 7032e192b24SSimon Glass if (argc <= 0) 7042e192b24SSimon Glass goto usage; 7052e192b24SSimon Glass 7062e192b24SSimon Glass while (argc > 0) { 7072e192b24SSimon Glass addr = simple_strtoul(*argv, NULL, 16); 7082e192b24SSimon Glass 709151c06ecSScott Wood if (mtd_block_markbad(mtd, addr)) { 7102e192b24SSimon Glass printf("block 0x%08lx NOT marked " 7112e192b24SSimon Glass "as bad! ERROR %d\n", 7122e192b24SSimon Glass addr, ret); 7132e192b24SSimon Glass ret = 1; 7142e192b24SSimon Glass } else { 7152e192b24SSimon Glass printf("block 0x%08lx successfully " 7162e192b24SSimon Glass "marked as bad\n", 7172e192b24SSimon Glass addr); 7182e192b24SSimon Glass } 7192e192b24SSimon Glass --argc; 7202e192b24SSimon Glass ++argv; 7212e192b24SSimon Glass } 7222e192b24SSimon Glass return ret; 7232e192b24SSimon Glass } 7242e192b24SSimon Glass 7252e192b24SSimon Glass if (strcmp(cmd, "biterr") == 0) { 7262e192b24SSimon Glass /* todo */ 7272e192b24SSimon Glass return 1; 7282e192b24SSimon Glass } 7292e192b24SSimon Glass 7302e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK 7312e192b24SSimon Glass if (strcmp(cmd, "lock") == 0) { 7322e192b24SSimon Glass int tight = 0; 7332e192b24SSimon Glass int status = 0; 7342e192b24SSimon Glass if (argc == 3) { 7352e192b24SSimon Glass if (!strcmp("tight", argv[2])) 7362e192b24SSimon Glass tight = 1; 7372e192b24SSimon Glass if (!strcmp("status", argv[2])) 7382e192b24SSimon Glass status = 1; 7392e192b24SSimon Glass } 7402e192b24SSimon Glass if (status) { 741151c06ecSScott Wood do_nand_status(mtd); 7422e192b24SSimon Glass } else { 743151c06ecSScott Wood if (!nand_lock(mtd, tight)) { 7442e192b24SSimon Glass puts("NAND flash successfully locked\n"); 7452e192b24SSimon Glass } else { 7462e192b24SSimon Glass puts("Error locking NAND flash\n"); 7472e192b24SSimon Glass return 1; 7482e192b24SSimon Glass } 7492e192b24SSimon Glass } 7502e192b24SSimon Glass return 0; 7512e192b24SSimon Glass } 7522e192b24SSimon Glass 7532e192b24SSimon Glass if (strncmp(cmd, "unlock", 5) == 0) { 7542e192b24SSimon Glass int allexcept = 0; 7552e192b24SSimon Glass 7562e192b24SSimon Glass s = strchr(cmd, '.'); 7572e192b24SSimon Glass 7582e192b24SSimon Glass if (s && !strcmp(s, ".allexcept")) 7592e192b24SSimon Glass allexcept = 1; 7602e192b24SSimon Glass 7612e192b24SSimon Glass if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &off, &size, 7622e192b24SSimon Glass &maxsize, MTD_DEV_TYPE_NAND, 763b616d9b0SScott Wood nand_info[dev]->size) < 0) 7642e192b24SSimon Glass return 1; 7652e192b24SSimon Glass 7662e192b24SSimon Glass if (set_dev(dev)) 7672e192b24SSimon Glass return 1; 7682e192b24SSimon Glass 769b616d9b0SScott Wood if (!nand_unlock(nand_info[dev], off, size, allexcept)) { 7702e192b24SSimon Glass puts("NAND flash successfully unlocked\n"); 7712e192b24SSimon Glass } else { 7722e192b24SSimon Glass puts("Error unlocking NAND flash, " 7732e192b24SSimon Glass "write and erase will probably fail\n"); 7742e192b24SSimon Glass return 1; 7752e192b24SSimon Glass } 7762e192b24SSimon Glass return 0; 7772e192b24SSimon Glass } 7782e192b24SSimon Glass #endif 7792e192b24SSimon Glass 7802e192b24SSimon Glass usage: 7812e192b24SSimon Glass return CMD_RET_USAGE; 7822e192b24SSimon Glass } 7832e192b24SSimon Glass 7842e192b24SSimon Glass #ifdef CONFIG_SYS_LONGHELP 7852e192b24SSimon Glass static char nand_help_text[] = 7862e192b24SSimon Glass "info - show available NAND devices\n" 7872e192b24SSimon Glass "nand device [dev] - show or set current device\n" 7882e192b24SSimon Glass "nand read - addr off|partition size\n" 7892e192b24SSimon Glass "nand write - addr off|partition size\n" 7902e192b24SSimon Glass " read/write 'size' bytes starting at offset 'off'\n" 7912e192b24SSimon Glass " to/from memory address 'addr', skipping bad blocks.\n" 7922e192b24SSimon Glass "nand read.raw - addr off|partition [count]\n" 7932dc3c483SBoris Brezillon "nand write.raw[.noverify] - addr off|partition [count]\n" 7942e192b24SSimon Glass " Use read.raw/write.raw to avoid ECC and access the flash as-is.\n" 7952e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TRIMFFS 7962e192b24SSimon Glass "nand write.trimffs - addr off|partition size\n" 7972e192b24SSimon Glass " write 'size' bytes starting at offset 'off' from memory address\n" 7982e192b24SSimon Glass " 'addr', skipping bad blocks and dropping any pages at the end\n" 7992e192b24SSimon Glass " of eraseblocks that contain only 0xFF\n" 8002e192b24SSimon Glass #endif 8012e192b24SSimon Glass "nand erase[.spread] [clean] off size - erase 'size' bytes " 8022e192b24SSimon Glass "from offset 'off'\n" 8032e192b24SSimon Glass " With '.spread', erase enough for given file size, otherwise,\n" 8042e192b24SSimon Glass " 'size' includes skipped bad blocks.\n" 8052e192b24SSimon Glass "nand erase.part [clean] partition - erase entire mtd partition'\n" 8062e192b24SSimon Glass "nand erase.chip [clean] - erase entire chip'\n" 8072e192b24SSimon Glass "nand bad - show bad blocks\n" 8082e192b24SSimon Glass "nand dump[.oob] off - dump page\n" 8092e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TORTURE 8101866be7dSMax Krummenacher "nand torture off - torture one block at offset\n" 8111866be7dSMax Krummenacher "nand torture off [size] - torture blocks from off to off+size\n" 8122e192b24SSimon Glass #endif 8132e192b24SSimon Glass "nand scrub [-y] off size | scrub.part partition | scrub.chip\n" 8142e192b24SSimon Glass " really clean NAND erasing bad blocks (UNSAFE)\n" 8152e192b24SSimon Glass "nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n" 8162e192b24SSimon Glass "nand biterr off - make a bit error at offset (UNSAFE)" 8172e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK 8182e192b24SSimon Glass "\n" 8192e192b24SSimon Glass "nand lock [tight] [status]\n" 8202e192b24SSimon Glass " bring nand to lock state or display locked pages\n" 8212e192b24SSimon Glass "nand unlock[.allexcept] [offset] [size] - unlock section" 8222e192b24SSimon Glass #endif 8232e192b24SSimon Glass #ifdef CONFIG_ENV_OFFSET_OOB 8242e192b24SSimon Glass "\n" 8252e192b24SSimon Glass "nand env.oob - environment offset in OOB of block 0 of" 8262e192b24SSimon Glass " first device.\n" 8272e192b24SSimon Glass "nand env.oob set off|partition - set enviromnent offset\n" 8282e192b24SSimon Glass "nand env.oob get - get environment offset" 8292e192b24SSimon Glass #endif 8302e192b24SSimon Glass ""; 8312e192b24SSimon Glass #endif 8322e192b24SSimon Glass 8332e192b24SSimon Glass U_BOOT_CMD( 8342e192b24SSimon Glass nand, CONFIG_SYS_MAXARGS, 1, do_nand, 8352e192b24SSimon Glass "NAND sub-system", nand_help_text 8362e192b24SSimon Glass ); 8372e192b24SSimon Glass 838151c06ecSScott Wood static int nand_load_image(cmd_tbl_t *cmdtp, struct mtd_info *mtd, 8392e192b24SSimon Glass ulong offset, ulong addr, char *cmd) 8402e192b24SSimon Glass { 8412e192b24SSimon Glass int r; 8422e192b24SSimon Glass char *s; 8432e192b24SSimon Glass size_t cnt; 8442e192b24SSimon Glass #if defined(CONFIG_IMAGE_FORMAT_LEGACY) 8452e192b24SSimon Glass image_header_t *hdr; 8462e192b24SSimon Glass #endif 8472e192b24SSimon Glass #if defined(CONFIG_FIT) 8482e192b24SSimon Glass const void *fit_hdr = NULL; 8492e192b24SSimon Glass #endif 8502e192b24SSimon Glass 8512e192b24SSimon Glass s = strchr(cmd, '.'); 8522e192b24SSimon Glass if (s != NULL && 8532e192b24SSimon Glass (strcmp(s, ".jffs2") && strcmp(s, ".e") && strcmp(s, ".i"))) { 8542e192b24SSimon Glass printf("Unknown nand load suffix '%s'\n", s); 8552e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX); 8562e192b24SSimon Glass return 1; 8572e192b24SSimon Glass } 8582e192b24SSimon Glass 859151c06ecSScott Wood printf("\nLoading from %s, offset 0x%lx\n", mtd->name, offset); 8602e192b24SSimon Glass 861151c06ecSScott Wood cnt = mtd->writesize; 862151c06ecSScott Wood r = nand_read_skip_bad(mtd, offset, &cnt, NULL, mtd->size, 8632e192b24SSimon Glass (u_char *)addr); 8642e192b24SSimon Glass if (r) { 8652e192b24SSimon Glass puts("** Read error\n"); 8662e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_HDR_READ); 8672e192b24SSimon Glass return 1; 8682e192b24SSimon Glass } 8692e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_HDR_READ); 8702e192b24SSimon Glass 8712e192b24SSimon Glass switch (genimg_get_format ((void *)addr)) { 8722e192b24SSimon Glass #if defined(CONFIG_IMAGE_FORMAT_LEGACY) 8732e192b24SSimon Glass case IMAGE_FORMAT_LEGACY: 8742e192b24SSimon Glass hdr = (image_header_t *)addr; 8752e192b24SSimon Glass 8762e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); 8772e192b24SSimon Glass image_print_contents (hdr); 8782e192b24SSimon Glass 8792e192b24SSimon Glass cnt = image_get_image_size (hdr); 8802e192b24SSimon Glass break; 8812e192b24SSimon Glass #endif 8822e192b24SSimon Glass #if defined(CONFIG_FIT) 8832e192b24SSimon Glass case IMAGE_FORMAT_FIT: 8842e192b24SSimon Glass fit_hdr = (const void *)addr; 8852e192b24SSimon Glass puts ("Fit image detected...\n"); 8862e192b24SSimon Glass 8872e192b24SSimon Glass cnt = fit_get_size (fit_hdr); 8882e192b24SSimon Glass break; 8892e192b24SSimon Glass #endif 8902e192b24SSimon Glass default: 8912e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_TYPE); 8922e192b24SSimon Glass puts ("** Unknown image type\n"); 8932e192b24SSimon Glass return 1; 8942e192b24SSimon Glass } 8952e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); 8962e192b24SSimon Glass 897151c06ecSScott Wood r = nand_read_skip_bad(mtd, offset, &cnt, NULL, mtd->size, 8982e192b24SSimon Glass (u_char *)addr); 8992e192b24SSimon Glass if (r) { 9002e192b24SSimon Glass puts("** Read error\n"); 9012e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_READ); 9022e192b24SSimon Glass return 1; 9032e192b24SSimon Glass } 9042e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_READ); 9052e192b24SSimon Glass 9062e192b24SSimon Glass #if defined(CONFIG_FIT) 9072e192b24SSimon Glass /* This cannot be done earlier, we need complete FIT image in RAM first */ 9082e192b24SSimon Glass if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) { 9092e192b24SSimon Glass if (!fit_check_format (fit_hdr)) { 9102e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_FIT_READ); 9112e192b24SSimon Glass puts ("** Bad FIT image format\n"); 9122e192b24SSimon Glass return 1; 9132e192b24SSimon Glass } 9142e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_FIT_READ_OK); 9152e192b24SSimon Glass fit_print_contents (fit_hdr); 9162e192b24SSimon Glass } 9172e192b24SSimon Glass #endif 9182e192b24SSimon Glass 9192e192b24SSimon Glass /* Loading ok, update default load address */ 9202e192b24SSimon Glass 9212e192b24SSimon Glass load_addr = addr; 9222e192b24SSimon Glass 9232e192b24SSimon Glass return bootm_maybe_autostart(cmdtp, cmd); 9242e192b24SSimon Glass } 9252e192b24SSimon Glass 9262e192b24SSimon Glass static int do_nandboot(cmd_tbl_t *cmdtp, int flag, int argc, 9272e192b24SSimon Glass char * const argv[]) 9282e192b24SSimon Glass { 9292e192b24SSimon Glass char *boot_device = NULL; 9302e192b24SSimon Glass int idx; 9312e192b24SSimon Glass ulong addr, offset = 0; 9322e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS) 9332e192b24SSimon Glass struct mtd_device *dev; 9342e192b24SSimon Glass struct part_info *part; 9352e192b24SSimon Glass u8 pnum; 9362e192b24SSimon Glass 9372e192b24SSimon Glass if (argc >= 2) { 9382e192b24SSimon Glass char *p = (argc == 2) ? argv[1] : argv[2]; 9392e192b24SSimon Glass if (!(str2long(p, &addr)) && (mtdparts_init() == 0) && 9402e192b24SSimon Glass (find_dev_and_part(p, &dev, &pnum, &part) == 0)) { 9412e192b24SSimon Glass if (dev->id->type != MTD_DEV_TYPE_NAND) { 9422e192b24SSimon Glass puts("Not a NAND device\n"); 9432e192b24SSimon Glass return 1; 9442e192b24SSimon Glass } 9452e192b24SSimon Glass if (argc > 3) 9462e192b24SSimon Glass goto usage; 9472e192b24SSimon Glass if (argc == 3) 9482e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 9492e192b24SSimon Glass else 9502e192b24SSimon Glass addr = CONFIG_SYS_LOAD_ADDR; 951b616d9b0SScott Wood return nand_load_image(cmdtp, nand_info[dev->id->num], 9522e192b24SSimon Glass part->offset, addr, argv[0]); 9532e192b24SSimon Glass } 9542e192b24SSimon Glass } 9552e192b24SSimon Glass #endif 9562e192b24SSimon Glass 9572e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_PART); 9582e192b24SSimon Glass switch (argc) { 9592e192b24SSimon Glass case 1: 9602e192b24SSimon Glass addr = CONFIG_SYS_LOAD_ADDR; 9612e192b24SSimon Glass boot_device = getenv("bootdevice"); 9622e192b24SSimon Glass break; 9632e192b24SSimon Glass case 2: 9642e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 9652e192b24SSimon Glass boot_device = getenv("bootdevice"); 9662e192b24SSimon Glass break; 9672e192b24SSimon Glass case 3: 9682e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 9692e192b24SSimon Glass boot_device = argv[2]; 9702e192b24SSimon Glass break; 9712e192b24SSimon Glass case 4: 9722e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 9732e192b24SSimon Glass boot_device = argv[2]; 9742e192b24SSimon Glass offset = simple_strtoul(argv[3], NULL, 16); 9752e192b24SSimon Glass break; 9762e192b24SSimon Glass default: 9772e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS) 9782e192b24SSimon Glass usage: 9792e192b24SSimon Glass #endif 9802e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX); 9812e192b24SSimon Glass return CMD_RET_USAGE; 9822e192b24SSimon Glass } 9832e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_SUFFIX); 9842e192b24SSimon Glass 9852e192b24SSimon Glass if (!boot_device) { 9862e192b24SSimon Glass puts("\n** No boot device **\n"); 9872e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_BOOT_DEVICE); 9882e192b24SSimon Glass return 1; 9892e192b24SSimon Glass } 9902e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_BOOT_DEVICE); 9912e192b24SSimon Glass 9922e192b24SSimon Glass idx = simple_strtoul(boot_device, NULL, 16); 9932e192b24SSimon Glass 9944004a818STony Lindgren if (idx < 0 || idx >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[idx]) { 9952e192b24SSimon Glass printf("\n** Device %d not available\n", idx); 9962e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_AVAILABLE); 9972e192b24SSimon Glass return 1; 9982e192b24SSimon Glass } 9992e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_AVAILABLE); 10002e192b24SSimon Glass 1001b616d9b0SScott Wood return nand_load_image(cmdtp, nand_info[idx], offset, addr, argv[0]); 10022e192b24SSimon Glass } 10032e192b24SSimon Glass 10042e192b24SSimon Glass U_BOOT_CMD(nboot, 4, 1, do_nandboot, 10052e192b24SSimon Glass "boot from NAND device", 10062e192b24SSimon Glass "[partition] | [[[loadAddr] dev] offset]" 10072e192b24SSimon Glass ); 1008