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 { 1182e192b24SSimon Glass if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || 119b616d9b0SScott Wood !nand_info[dev]->name) { 1202e192b24SSimon Glass puts("No such device\n"); 1212e192b24SSimon Glass return -1; 1222e192b24SSimon Glass } 1232e192b24SSimon Glass 1242e192b24SSimon Glass if (nand_curr_device == dev) 1252e192b24SSimon Glass return 0; 1262e192b24SSimon Glass 127b616d9b0SScott Wood printf("Device %d: %s", dev, nand_info[dev]->name); 1282e192b24SSimon Glass puts("... is now current device\n"); 1292e192b24SSimon Glass nand_curr_device = dev; 1302e192b24SSimon Glass 1312e192b24SSimon Glass #ifdef CONFIG_SYS_NAND_SELECT_DEVICE 132b616d9b0SScott Wood board_nand_select_device(nand_info[dev]->priv, dev); 1332e192b24SSimon Glass #endif 1342e192b24SSimon Glass 1352e192b24SSimon Glass return 0; 1362e192b24SSimon Glass } 1372e192b24SSimon Glass 1382e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK 1392e192b24SSimon Glass static void print_status(ulong start, ulong end, ulong erasesize, int status) 1402e192b24SSimon Glass { 1412e192b24SSimon Glass /* 1422e192b24SSimon Glass * Micron NAND flash (e.g. MT29F4G08ABADAH4) BLOCK LOCK READ STATUS is 1432e192b24SSimon Glass * not the same as others. Instead of bit 1 being lock, it is 1442e192b24SSimon Glass * #lock_tight. To make the driver support either format, ignore bit 1 1452e192b24SSimon Glass * and use only bit 0 and bit 2. 1462e192b24SSimon Glass */ 1472e192b24SSimon Glass printf("%08lx - %08lx: %08lx blocks %s%s%s\n", 1482e192b24SSimon Glass start, 1492e192b24SSimon Glass end - 1, 1502e192b24SSimon Glass (end - start) / erasesize, 1512e192b24SSimon Glass ((status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""), 1522e192b24SSimon Glass (!(status & NAND_LOCK_STATUS_UNLOCK) ? "LOCK " : ""), 1532e192b24SSimon Glass ((status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : "")); 1542e192b24SSimon Glass } 1552e192b24SSimon Glass 156151c06ecSScott Wood static void do_nand_status(struct mtd_info *mtd) 1572e192b24SSimon Glass { 1582e192b24SSimon Glass ulong block_start = 0; 1592e192b24SSimon Glass ulong off; 1602e192b24SSimon Glass int last_status = -1; 1612e192b24SSimon Glass 162*17cb4b8fSScott Wood struct nand_chip *nand_chip = mtd_to_nand(mtd); 1632e192b24SSimon Glass /* check the WP bit */ 164151c06ecSScott Wood nand_chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); 1652e192b24SSimon Glass printf("device is %swrite protected\n", 166151c06ecSScott Wood (nand_chip->read_byte(mtd) & 0x80 ? 1672e192b24SSimon Glass "NOT " : "")); 1682e192b24SSimon Glass 169151c06ecSScott Wood for (off = 0; off < mtd->size; off += mtd->erasesize) { 170151c06ecSScott Wood int s = nand_get_lock_status(mtd, off); 1712e192b24SSimon Glass 1722e192b24SSimon Glass /* print message only if status has changed */ 1732e192b24SSimon Glass if (s != last_status && off != 0) { 174151c06ecSScott Wood print_status(block_start, off, mtd->erasesize, 1752e192b24SSimon Glass last_status); 1762e192b24SSimon Glass block_start = off; 1772e192b24SSimon Glass } 1782e192b24SSimon Glass last_status = s; 1792e192b24SSimon Glass } 1802e192b24SSimon Glass /* Print the last block info */ 181151c06ecSScott Wood print_status(block_start, off, mtd->erasesize, last_status); 1822e192b24SSimon Glass } 1832e192b24SSimon Glass #endif 1842e192b24SSimon Glass 1852e192b24SSimon Glass #ifdef CONFIG_ENV_OFFSET_OOB 1862e192b24SSimon Glass unsigned long nand_env_oob_offset; 1872e192b24SSimon Glass 1882e192b24SSimon Glass int do_nand_env_oob(cmd_tbl_t *cmdtp, int argc, char *const argv[]) 1892e192b24SSimon Glass { 1902e192b24SSimon Glass int ret; 1912e192b24SSimon Glass uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; 192b616d9b0SScott Wood struct mtd_info *mtd = nand_info[0]; 1932e192b24SSimon Glass char *cmd = argv[1]; 1942e192b24SSimon Glass 195151c06ecSScott Wood if (CONFIG_SYS_MAX_NAND_DEVICE == 0 || !mtd->name) { 1962e192b24SSimon Glass puts("no devices available\n"); 1972e192b24SSimon Glass return 1; 1982e192b24SSimon Glass } 1992e192b24SSimon Glass 2002e192b24SSimon Glass set_dev(0); 2012e192b24SSimon Glass 2022e192b24SSimon Glass if (!strcmp(cmd, "get")) { 203151c06ecSScott Wood ret = get_nand_env_oob(mtd, &nand_env_oob_offset); 2042e192b24SSimon Glass if (ret) 2052e192b24SSimon Glass return 1; 2062e192b24SSimon Glass 2072e192b24SSimon Glass printf("0x%08lx\n", nand_env_oob_offset); 2082e192b24SSimon Glass } else if (!strcmp(cmd, "set")) { 2092e192b24SSimon Glass loff_t addr; 2102e192b24SSimon Glass loff_t maxsize; 2112e192b24SSimon Glass struct mtd_oob_ops ops; 2122e192b24SSimon Glass int idx = 0; 2132e192b24SSimon Glass 2142e192b24SSimon Glass if (argc < 3) 2152e192b24SSimon Glass goto usage; 2162e192b24SSimon Glass 2172e192b24SSimon Glass /* We don't care about size, or maxsize. */ 2182e192b24SSimon Glass if (mtd_arg_off(argv[2], &idx, &addr, &maxsize, &maxsize, 219b616d9b0SScott Wood MTD_DEV_TYPE_NAND, nand_info[idx]->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 { 287b616d9b0SScott Wood struct mtd_info *mtd = nand_info[idx]; 288*17cb4b8fSScott Wood struct nand_chip *chip = mtd_to_nand(mtd); 2892e192b24SSimon Glass 2902e192b24SSimon Glass printf("Device %d: ", idx); 2912e192b24SSimon Glass if (chip->numchips > 1) 2922e192b24SSimon Glass printf("%dx ", chip->numchips); 2932e192b24SSimon Glass printf("%s, sector size %u KiB\n", 294151c06ecSScott Wood mtd->name, mtd->erasesize >> 10); 295151c06ecSScott Wood printf(" Page size %8d b\n", mtd->writesize); 296151c06ecSScott Wood printf(" OOB size %8d b\n", mtd->oobsize); 297151c06ecSScott Wood printf(" Erase size %8d b\n", mtd->erasesize); 2982e192b24SSimon Glass printf(" subpagesize %8d b\n", chip->subpagesize); 2992e192b24SSimon Glass printf(" options 0x%8x\n", chip->options); 3002e192b24SSimon Glass printf(" bbt options 0x%8x\n", chip->bbt_options); 3012e192b24SSimon Glass 3022e192b24SSimon Glass /* Set geometry info */ 303151c06ecSScott Wood setenv_hex("nand_writesize", mtd->writesize); 304151c06ecSScott Wood setenv_hex("nand_oobsize", mtd->oobsize); 305151c06ecSScott Wood setenv_hex("nand_erasesize", mtd->erasesize); 3062e192b24SSimon Glass } 3072e192b24SSimon Glass 308151c06ecSScott Wood static int raw_access(struct mtd_info *mtd, ulong addr, loff_t off, 309151c06ecSScott Wood ulong count, int read) 3102e192b24SSimon Glass { 3112e192b24SSimon Glass int ret = 0; 3122e192b24SSimon Glass 3132e192b24SSimon Glass while (count--) { 3142e192b24SSimon Glass /* Raw access */ 3152e192b24SSimon Glass mtd_oob_ops_t ops = { 3162e192b24SSimon Glass .datbuf = (u8 *)addr, 317151c06ecSScott Wood .oobbuf = ((u8 *)addr) + mtd->writesize, 318151c06ecSScott Wood .len = mtd->writesize, 319151c06ecSScott Wood .ooblen = mtd->oobsize, 3202e192b24SSimon Glass .mode = MTD_OPS_RAW 3212e192b24SSimon Glass }; 3222e192b24SSimon Glass 3232e192b24SSimon Glass if (read) { 324151c06ecSScott Wood ret = mtd_read_oob(mtd, off, &ops); 3252e192b24SSimon Glass } else { 326151c06ecSScott Wood ret = mtd_write_oob(mtd, off, &ops); 3272e192b24SSimon Glass if (!ret) 328151c06ecSScott Wood ret = nand_verify_page_oob(mtd, &ops, off); 3292e192b24SSimon Glass } 3302e192b24SSimon Glass 3312e192b24SSimon Glass if (ret) { 3322e192b24SSimon Glass printf("%s: error at offset %llx, ret %d\n", 3332e192b24SSimon Glass __func__, (long long)off, ret); 3342e192b24SSimon Glass break; 3352e192b24SSimon Glass } 3362e192b24SSimon Glass 337151c06ecSScott Wood addr += mtd->writesize + mtd->oobsize; 338151c06ecSScott Wood off += mtd->writesize; 3392e192b24SSimon Glass } 3402e192b24SSimon Glass 3412e192b24SSimon Glass return ret; 3422e192b24SSimon Glass } 3432e192b24SSimon Glass 3442e192b24SSimon Glass /* Adjust a chip/partition size down for bad blocks so we don't 3452e192b24SSimon Glass * read/write past the end of a chip/partition by accident. 3462e192b24SSimon Glass */ 3472e192b24SSimon Glass static void adjust_size_for_badblocks(loff_t *size, loff_t offset, int dev) 3482e192b24SSimon Glass { 3492e192b24SSimon Glass /* We grab the nand info object here fresh because this is usually 3502e192b24SSimon Glass * called after arg_off_size() which can change the value of dev. 3512e192b24SSimon Glass */ 352b616d9b0SScott Wood struct mtd_info *mtd = nand_info[dev]; 3532e192b24SSimon Glass loff_t maxoffset = offset + *size; 3542e192b24SSimon Glass int badblocks = 0; 3552e192b24SSimon Glass 3562e192b24SSimon Glass /* count badblocks in NAND from offset to offset + size */ 357151c06ecSScott Wood for (; offset < maxoffset; offset += mtd->erasesize) { 358151c06ecSScott Wood if (nand_block_isbad(mtd, offset)) 3592e192b24SSimon Glass badblocks++; 3602e192b24SSimon Glass } 3612e192b24SSimon Glass /* adjust size if any bad blocks found */ 3622e192b24SSimon Glass if (badblocks) { 363151c06ecSScott Wood *size -= badblocks * mtd->erasesize; 3642e192b24SSimon Glass printf("size adjusted to 0x%llx (%d bad blocks)\n", 3652e192b24SSimon Glass (unsigned long long)*size, badblocks); 3662e192b24SSimon Glass } 3672e192b24SSimon Glass } 3682e192b24SSimon Glass 3692e192b24SSimon Glass static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 3702e192b24SSimon Glass { 3712e192b24SSimon Glass int i, ret = 0; 3722e192b24SSimon Glass ulong addr; 3732e192b24SSimon Glass loff_t off, size, maxsize; 3742e192b24SSimon Glass char *cmd, *s; 375151c06ecSScott Wood struct mtd_info *mtd; 3762e192b24SSimon Glass #ifdef CONFIG_SYS_NAND_QUIET 3772e192b24SSimon Glass int quiet = CONFIG_SYS_NAND_QUIET; 3782e192b24SSimon Glass #else 3792e192b24SSimon Glass int quiet = 0; 3802e192b24SSimon Glass #endif 3812e192b24SSimon Glass const char *quiet_str = getenv("quiet"); 3822e192b24SSimon Glass int dev = nand_curr_device; 3832e192b24SSimon Glass int repeat = flag & CMD_FLAG_REPEAT; 3842e192b24SSimon Glass 3852e192b24SSimon Glass /* at least two arguments please */ 3862e192b24SSimon Glass if (argc < 2) 3872e192b24SSimon Glass goto usage; 3882e192b24SSimon Glass 3892e192b24SSimon Glass if (quiet_str) 3902e192b24SSimon Glass quiet = simple_strtoul(quiet_str, NULL, 0) != 0; 3912e192b24SSimon Glass 3922e192b24SSimon Glass cmd = argv[1]; 3932e192b24SSimon Glass 3942e192b24SSimon Glass /* Only "dump" is repeatable. */ 3952e192b24SSimon Glass if (repeat && strcmp(cmd, "dump")) 3962e192b24SSimon Glass return 0; 3972e192b24SSimon Glass 3982e192b24SSimon Glass if (strcmp(cmd, "info") == 0) { 3992e192b24SSimon Glass 4002e192b24SSimon Glass putc('\n'); 4012e192b24SSimon Glass for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { 402b616d9b0SScott Wood if (nand_info[i]->name) 4032e192b24SSimon Glass nand_print_and_set_info(i); 4042e192b24SSimon Glass } 4052e192b24SSimon Glass return 0; 4062e192b24SSimon Glass } 4072e192b24SSimon Glass 4082e192b24SSimon Glass if (strcmp(cmd, "device") == 0) { 4092e192b24SSimon Glass if (argc < 3) { 4102e192b24SSimon Glass putc('\n'); 4112e192b24SSimon Glass if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE) 4122e192b24SSimon Glass puts("no devices available\n"); 4132e192b24SSimon Glass else 4142e192b24SSimon Glass nand_print_and_set_info(dev); 4152e192b24SSimon Glass return 0; 4162e192b24SSimon Glass } 4172e192b24SSimon Glass 4182e192b24SSimon Glass dev = (int)simple_strtoul(argv[2], NULL, 10); 4192e192b24SSimon Glass set_dev(dev); 4202e192b24SSimon Glass 4212e192b24SSimon Glass return 0; 4222e192b24SSimon Glass } 4232e192b24SSimon Glass 4242e192b24SSimon Glass #ifdef CONFIG_ENV_OFFSET_OOB 4252e192b24SSimon Glass /* this command operates only on the first nand device */ 4262e192b24SSimon Glass if (strcmp(cmd, "env.oob") == 0) 4272e192b24SSimon Glass return do_nand_env_oob(cmdtp, argc - 1, argv + 1); 4282e192b24SSimon Glass #endif 4292e192b24SSimon Glass 4302e192b24SSimon Glass /* The following commands operate on the current device, unless 4312e192b24SSimon Glass * overridden by a partition specifier. Note that if somehow the 4322e192b24SSimon Glass * current device is invalid, it will have to be changed to a valid 4332e192b24SSimon Glass * one before these commands can run, even if a partition specifier 4342e192b24SSimon Glass * for another device is to be used. 4352e192b24SSimon Glass */ 4362e192b24SSimon Glass if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || 437b616d9b0SScott Wood !nand_info[dev]->name) { 4382e192b24SSimon Glass puts("\nno devices available\n"); 4392e192b24SSimon Glass return 1; 4402e192b24SSimon Glass } 441b616d9b0SScott Wood mtd = nand_info[dev]; 4422e192b24SSimon Glass 4432e192b24SSimon Glass if (strcmp(cmd, "bad") == 0) { 4442e192b24SSimon Glass printf("\nDevice %d bad blocks:\n", dev); 445151c06ecSScott Wood for (off = 0; off < mtd->size; off += mtd->erasesize) 446151c06ecSScott Wood if (nand_block_isbad(mtd, off)) 4472e192b24SSimon Glass printf(" %08llx\n", (unsigned long long)off); 4482e192b24SSimon Glass return 0; 4492e192b24SSimon Glass } 4502e192b24SSimon Glass 4512e192b24SSimon Glass /* 4522e192b24SSimon Glass * Syntax is: 4532e192b24SSimon Glass * 0 1 2 3 4 4542e192b24SSimon Glass * nand erase [clean] [off size] 4552e192b24SSimon Glass */ 4562e192b24SSimon Glass if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) { 4572e192b24SSimon Glass nand_erase_options_t opts; 4582e192b24SSimon Glass /* "clean" at index 2 means request to write cleanmarker */ 4592e192b24SSimon Glass int clean = argc > 2 && !strcmp("clean", argv[2]); 4602e192b24SSimon Glass int scrub_yes = argc > 2 && !strcmp("-y", argv[2]); 4612e192b24SSimon Glass int o = (clean || scrub_yes) ? 3 : 2; 4622e192b24SSimon Glass int scrub = !strncmp(cmd, "scrub", 5); 4632e192b24SSimon Glass int spread = 0; 4642e192b24SSimon Glass int args = 2; 4652e192b24SSimon Glass const char *scrub_warn = 4662e192b24SSimon Glass "Warning: " 4672e192b24SSimon Glass "scrub option will erase all factory set bad blocks!\n" 4682e192b24SSimon Glass " " 4692e192b24SSimon Glass "There is no reliable way to recover them.\n" 4702e192b24SSimon Glass " " 4712e192b24SSimon Glass "Use this command only for testing purposes if you\n" 4722e192b24SSimon Glass " " 4732e192b24SSimon Glass "are sure of what you are doing!\n" 4742e192b24SSimon Glass "\nReally scrub this NAND flash? <y/N>\n"; 4752e192b24SSimon Glass 4762e192b24SSimon Glass if (cmd[5] != 0) { 4772e192b24SSimon Glass if (!strcmp(&cmd[5], ".spread")) { 4782e192b24SSimon Glass spread = 1; 4792e192b24SSimon Glass } else if (!strcmp(&cmd[5], ".part")) { 4802e192b24SSimon Glass args = 1; 4812e192b24SSimon Glass } else if (!strcmp(&cmd[5], ".chip")) { 4822e192b24SSimon Glass args = 0; 4832e192b24SSimon Glass } else { 4842e192b24SSimon Glass goto usage; 4852e192b24SSimon Glass } 4862e192b24SSimon Glass } 4872e192b24SSimon Glass 4882e192b24SSimon Glass /* 4892e192b24SSimon Glass * Don't allow missing arguments to cause full chip/partition 4902e192b24SSimon Glass * erases -- easy to do accidentally, e.g. with a misspelled 4912e192b24SSimon Glass * variable name. 4922e192b24SSimon Glass */ 4932e192b24SSimon Glass if (argc != o + args) 4942e192b24SSimon Glass goto usage; 4952e192b24SSimon Glass 4962e192b24SSimon Glass printf("\nNAND %s: ", cmd); 4972e192b24SSimon Glass /* skip first two or three arguments, look for offset and size */ 4982e192b24SSimon Glass if (mtd_arg_off_size(argc - o, argv + o, &dev, &off, &size, 4992e192b24SSimon Glass &maxsize, MTD_DEV_TYPE_NAND, 500b616d9b0SScott Wood nand_info[dev]->size) != 0) 5012e192b24SSimon Glass return 1; 5022e192b24SSimon Glass 5032e192b24SSimon Glass if (set_dev(dev)) 5042e192b24SSimon Glass return 1; 5052e192b24SSimon Glass 506b616d9b0SScott Wood mtd = nand_info[dev]; 5072e192b24SSimon Glass 5082e192b24SSimon Glass memset(&opts, 0, sizeof(opts)); 5092e192b24SSimon Glass opts.offset = off; 5102e192b24SSimon Glass opts.length = size; 5112e192b24SSimon Glass opts.jffs2 = clean; 5122e192b24SSimon Glass opts.quiet = quiet; 5132e192b24SSimon Glass opts.spread = spread; 5142e192b24SSimon Glass 5152e192b24SSimon Glass if (scrub) { 5162e192b24SSimon Glass if (scrub_yes) { 5172e192b24SSimon Glass opts.scrub = 1; 5182e192b24SSimon Glass } else { 5192e192b24SSimon Glass puts(scrub_warn); 5202e192b24SSimon Glass if (confirm_yesno()) { 5212e192b24SSimon Glass opts.scrub = 1; 5222e192b24SSimon Glass } else { 5232e192b24SSimon Glass puts("scrub aborted\n"); 5242e192b24SSimon Glass return 1; 5252e192b24SSimon Glass } 5262e192b24SSimon Glass } 5272e192b24SSimon Glass } 528151c06ecSScott Wood ret = nand_erase_opts(mtd, &opts); 5292e192b24SSimon Glass printf("%s\n", ret ? "ERROR" : "OK"); 5302e192b24SSimon Glass 5312e192b24SSimon Glass return ret == 0 ? 0 : 1; 5322e192b24SSimon Glass } 5332e192b24SSimon Glass 5342e192b24SSimon Glass if (strncmp(cmd, "dump", 4) == 0) { 5352e192b24SSimon Glass if (argc < 3) 5362e192b24SSimon Glass goto usage; 5372e192b24SSimon Glass 5382e192b24SSimon Glass off = (int)simple_strtoul(argv[2], NULL, 16); 539151c06ecSScott Wood ret = nand_dump(mtd, off, !strcmp(&cmd[4], ".oob"), repeat); 5402e192b24SSimon Glass 5412e192b24SSimon Glass return ret == 0 ? 1 : 0; 5422e192b24SSimon Glass } 5432e192b24SSimon Glass 5442e192b24SSimon Glass if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { 5452e192b24SSimon Glass size_t rwsize; 5462e192b24SSimon Glass ulong pagecount = 1; 5472e192b24SSimon Glass int read; 5482e192b24SSimon Glass int raw = 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 5602e192b24SSimon Glass if (s && !strcmp(s, ".raw")) { 5612e192b24SSimon Glass raw = 1; 5622e192b24SSimon Glass 5632e192b24SSimon Glass if (mtd_arg_off(argv[3], &dev, &off, &size, &maxsize, 5642e192b24SSimon Glass MTD_DEV_TYPE_NAND, 565b616d9b0SScott Wood nand_info[dev]->size)) 5662e192b24SSimon Glass return 1; 5672e192b24SSimon Glass 5682e192b24SSimon Glass if (set_dev(dev)) 5692e192b24SSimon Glass return 1; 5702e192b24SSimon Glass 571b616d9b0SScott Wood mtd = nand_info[dev]; 5722e192b24SSimon Glass 5732e192b24SSimon Glass if (argc > 4 && !str2long(argv[4], &pagecount)) { 5742e192b24SSimon Glass printf("'%s' is not a number\n", argv[4]); 5752e192b24SSimon Glass return 1; 5762e192b24SSimon Glass } 5772e192b24SSimon Glass 578151c06ecSScott Wood if (pagecount * mtd->writesize > size) { 5792e192b24SSimon Glass puts("Size exceeds partition or device limit\n"); 5802e192b24SSimon Glass return -1; 5812e192b24SSimon Glass } 5822e192b24SSimon Glass 583151c06ecSScott Wood rwsize = pagecount * (mtd->writesize + mtd->oobsize); 5842e192b24SSimon Glass } else { 5852e192b24SSimon Glass if (mtd_arg_off_size(argc - 3, argv + 3, &dev, &off, 5862e192b24SSimon Glass &size, &maxsize, 5872e192b24SSimon Glass MTD_DEV_TYPE_NAND, 588b616d9b0SScott Wood nand_info[dev]->size) != 0) 5892e192b24SSimon Glass return 1; 5902e192b24SSimon Glass 5912e192b24SSimon Glass if (set_dev(dev)) 5922e192b24SSimon Glass return 1; 5932e192b24SSimon Glass 5942e192b24SSimon Glass /* size is unspecified */ 5952e192b24SSimon Glass if (argc < 5) 5962e192b24SSimon Glass adjust_size_for_badblocks(&size, off, dev); 5972e192b24SSimon Glass rwsize = size; 5982e192b24SSimon Glass } 5992e192b24SSimon Glass 600b616d9b0SScott Wood mtd = nand_info[dev]; 6012e192b24SSimon Glass 6022e192b24SSimon Glass if (!s || !strcmp(s, ".jffs2") || 6032e192b24SSimon Glass !strcmp(s, ".e") || !strcmp(s, ".i")) { 6042e192b24SSimon Glass if (read) 605151c06ecSScott Wood ret = nand_read_skip_bad(mtd, off, &rwsize, 6062e192b24SSimon Glass NULL, maxsize, 6072e192b24SSimon Glass (u_char *)addr); 6082e192b24SSimon Glass else 609151c06ecSScott Wood ret = nand_write_skip_bad(mtd, off, &rwsize, 6102e192b24SSimon Glass NULL, maxsize, 6112e192b24SSimon Glass (u_char *)addr, 6122e192b24SSimon Glass WITH_WR_VERIFY); 6132e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TRIMFFS 6142e192b24SSimon Glass } else if (!strcmp(s, ".trimffs")) { 6152e192b24SSimon Glass if (read) { 6162e192b24SSimon Glass printf("Unknown nand command suffix '%s'\n", s); 6172e192b24SSimon Glass return 1; 6182e192b24SSimon Glass } 619151c06ecSScott Wood ret = nand_write_skip_bad(mtd, off, &rwsize, NULL, 6202e192b24SSimon Glass maxsize, (u_char *)addr, 6212e192b24SSimon Glass WITH_DROP_FFS | WITH_WR_VERIFY); 6222e192b24SSimon Glass #endif 6232e192b24SSimon Glass } else if (!strcmp(s, ".oob")) { 6242e192b24SSimon Glass /* out-of-band data */ 6252e192b24SSimon Glass mtd_oob_ops_t ops = { 6262e192b24SSimon Glass .oobbuf = (u8 *)addr, 6272e192b24SSimon Glass .ooblen = rwsize, 6282e192b24SSimon Glass .mode = MTD_OPS_RAW 6292e192b24SSimon Glass }; 6302e192b24SSimon Glass 6312e192b24SSimon Glass if (read) 632151c06ecSScott Wood ret = mtd_read_oob(mtd, off, &ops); 6332e192b24SSimon Glass else 634151c06ecSScott Wood ret = mtd_write_oob(mtd, off, &ops); 6352e192b24SSimon Glass } else if (raw) { 636151c06ecSScott Wood ret = raw_access(mtd, addr, off, pagecount, read); 6372e192b24SSimon Glass } else { 6382e192b24SSimon Glass printf("Unknown nand command suffix '%s'.\n", s); 6392e192b24SSimon Glass return 1; 6402e192b24SSimon Glass } 6412e192b24SSimon Glass 6422e192b24SSimon Glass printf(" %zu bytes %s: %s\n", rwsize, 6432e192b24SSimon Glass read ? "read" : "written", ret ? "ERROR" : "OK"); 6442e192b24SSimon Glass 6452e192b24SSimon Glass return ret == 0 ? 0 : 1; 6462e192b24SSimon Glass } 6472e192b24SSimon Glass 6482e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TORTURE 6492e192b24SSimon Glass if (strcmp(cmd, "torture") == 0) { 6502e192b24SSimon Glass if (argc < 3) 6512e192b24SSimon Glass goto usage; 6522e192b24SSimon Glass 6532e192b24SSimon Glass if (!str2off(argv[2], &off)) { 6542e192b24SSimon Glass puts("Offset is not a valid number\n"); 6552e192b24SSimon Glass return 1; 6562e192b24SSimon Glass } 6572e192b24SSimon Glass 6582e192b24SSimon Glass printf("\nNAND torture: device %d offset 0x%llx size 0x%x\n", 659151c06ecSScott Wood dev, off, mtd->erasesize); 660151c06ecSScott Wood ret = nand_torture(mtd, off); 6612e192b24SSimon Glass printf(" %s\n", ret ? "Failed" : "Passed"); 6622e192b24SSimon Glass 6632e192b24SSimon Glass return ret == 0 ? 0 : 1; 6642e192b24SSimon Glass } 6652e192b24SSimon Glass #endif 6662e192b24SSimon Glass 6672e192b24SSimon Glass if (strcmp(cmd, "markbad") == 0) { 6682e192b24SSimon Glass argc -= 2; 6692e192b24SSimon Glass argv += 2; 6702e192b24SSimon Glass 6712e192b24SSimon Glass if (argc <= 0) 6722e192b24SSimon Glass goto usage; 6732e192b24SSimon Glass 6742e192b24SSimon Glass while (argc > 0) { 6752e192b24SSimon Glass addr = simple_strtoul(*argv, NULL, 16); 6762e192b24SSimon Glass 677151c06ecSScott Wood if (mtd_block_markbad(mtd, addr)) { 6782e192b24SSimon Glass printf("block 0x%08lx NOT marked " 6792e192b24SSimon Glass "as bad! ERROR %d\n", 6802e192b24SSimon Glass addr, ret); 6812e192b24SSimon Glass ret = 1; 6822e192b24SSimon Glass } else { 6832e192b24SSimon Glass printf("block 0x%08lx successfully " 6842e192b24SSimon Glass "marked as bad\n", 6852e192b24SSimon Glass addr); 6862e192b24SSimon Glass } 6872e192b24SSimon Glass --argc; 6882e192b24SSimon Glass ++argv; 6892e192b24SSimon Glass } 6902e192b24SSimon Glass return ret; 6912e192b24SSimon Glass } 6922e192b24SSimon Glass 6932e192b24SSimon Glass if (strcmp(cmd, "biterr") == 0) { 6942e192b24SSimon Glass /* todo */ 6952e192b24SSimon Glass return 1; 6962e192b24SSimon Glass } 6972e192b24SSimon Glass 6982e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK 6992e192b24SSimon Glass if (strcmp(cmd, "lock") == 0) { 7002e192b24SSimon Glass int tight = 0; 7012e192b24SSimon Glass int status = 0; 7022e192b24SSimon Glass if (argc == 3) { 7032e192b24SSimon Glass if (!strcmp("tight", argv[2])) 7042e192b24SSimon Glass tight = 1; 7052e192b24SSimon Glass if (!strcmp("status", argv[2])) 7062e192b24SSimon Glass status = 1; 7072e192b24SSimon Glass } 7082e192b24SSimon Glass if (status) { 709151c06ecSScott Wood do_nand_status(mtd); 7102e192b24SSimon Glass } else { 711151c06ecSScott Wood if (!nand_lock(mtd, tight)) { 7122e192b24SSimon Glass puts("NAND flash successfully locked\n"); 7132e192b24SSimon Glass } else { 7142e192b24SSimon Glass puts("Error locking NAND flash\n"); 7152e192b24SSimon Glass return 1; 7162e192b24SSimon Glass } 7172e192b24SSimon Glass } 7182e192b24SSimon Glass return 0; 7192e192b24SSimon Glass } 7202e192b24SSimon Glass 7212e192b24SSimon Glass if (strncmp(cmd, "unlock", 5) == 0) { 7222e192b24SSimon Glass int allexcept = 0; 7232e192b24SSimon Glass 7242e192b24SSimon Glass s = strchr(cmd, '.'); 7252e192b24SSimon Glass 7262e192b24SSimon Glass if (s && !strcmp(s, ".allexcept")) 7272e192b24SSimon Glass allexcept = 1; 7282e192b24SSimon Glass 7292e192b24SSimon Glass if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &off, &size, 7302e192b24SSimon Glass &maxsize, MTD_DEV_TYPE_NAND, 731b616d9b0SScott Wood nand_info[dev]->size) < 0) 7322e192b24SSimon Glass return 1; 7332e192b24SSimon Glass 7342e192b24SSimon Glass if (set_dev(dev)) 7352e192b24SSimon Glass return 1; 7362e192b24SSimon Glass 737b616d9b0SScott Wood if (!nand_unlock(nand_info[dev], off, size, allexcept)) { 7382e192b24SSimon Glass puts("NAND flash successfully unlocked\n"); 7392e192b24SSimon Glass } else { 7402e192b24SSimon Glass puts("Error unlocking NAND flash, " 7412e192b24SSimon Glass "write and erase will probably fail\n"); 7422e192b24SSimon Glass return 1; 7432e192b24SSimon Glass } 7442e192b24SSimon Glass return 0; 7452e192b24SSimon Glass } 7462e192b24SSimon Glass #endif 7472e192b24SSimon Glass 7482e192b24SSimon Glass usage: 7492e192b24SSimon Glass return CMD_RET_USAGE; 7502e192b24SSimon Glass } 7512e192b24SSimon Glass 7522e192b24SSimon Glass #ifdef CONFIG_SYS_LONGHELP 7532e192b24SSimon Glass static char nand_help_text[] = 7542e192b24SSimon Glass "info - show available NAND devices\n" 7552e192b24SSimon Glass "nand device [dev] - show or set current device\n" 7562e192b24SSimon Glass "nand read - addr off|partition size\n" 7572e192b24SSimon Glass "nand write - addr off|partition size\n" 7582e192b24SSimon Glass " read/write 'size' bytes starting at offset 'off'\n" 7592e192b24SSimon Glass " to/from memory address 'addr', skipping bad blocks.\n" 7602e192b24SSimon Glass "nand read.raw - addr off|partition [count]\n" 7612e192b24SSimon Glass "nand write.raw - addr off|partition [count]\n" 7622e192b24SSimon Glass " Use read.raw/write.raw to avoid ECC and access the flash as-is.\n" 7632e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TRIMFFS 7642e192b24SSimon Glass "nand write.trimffs - addr off|partition size\n" 7652e192b24SSimon Glass " write 'size' bytes starting at offset 'off' from memory address\n" 7662e192b24SSimon Glass " 'addr', skipping bad blocks and dropping any pages at the end\n" 7672e192b24SSimon Glass " of eraseblocks that contain only 0xFF\n" 7682e192b24SSimon Glass #endif 7692e192b24SSimon Glass "nand erase[.spread] [clean] off size - erase 'size' bytes " 7702e192b24SSimon Glass "from offset 'off'\n" 7712e192b24SSimon Glass " With '.spread', erase enough for given file size, otherwise,\n" 7722e192b24SSimon Glass " 'size' includes skipped bad blocks.\n" 7732e192b24SSimon Glass "nand erase.part [clean] partition - erase entire mtd partition'\n" 7742e192b24SSimon Glass "nand erase.chip [clean] - erase entire chip'\n" 7752e192b24SSimon Glass "nand bad - show bad blocks\n" 7762e192b24SSimon Glass "nand dump[.oob] off - dump page\n" 7772e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TORTURE 7782e192b24SSimon Glass "nand torture off - torture block at offset\n" 7792e192b24SSimon Glass #endif 7802e192b24SSimon Glass "nand scrub [-y] off size | scrub.part partition | scrub.chip\n" 7812e192b24SSimon Glass " really clean NAND erasing bad blocks (UNSAFE)\n" 7822e192b24SSimon Glass "nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n" 7832e192b24SSimon Glass "nand biterr off - make a bit error at offset (UNSAFE)" 7842e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK 7852e192b24SSimon Glass "\n" 7862e192b24SSimon Glass "nand lock [tight] [status]\n" 7872e192b24SSimon Glass " bring nand to lock state or display locked pages\n" 7882e192b24SSimon Glass "nand unlock[.allexcept] [offset] [size] - unlock section" 7892e192b24SSimon Glass #endif 7902e192b24SSimon Glass #ifdef CONFIG_ENV_OFFSET_OOB 7912e192b24SSimon Glass "\n" 7922e192b24SSimon Glass "nand env.oob - environment offset in OOB of block 0 of" 7932e192b24SSimon Glass " first device.\n" 7942e192b24SSimon Glass "nand env.oob set off|partition - set enviromnent offset\n" 7952e192b24SSimon Glass "nand env.oob get - get environment offset" 7962e192b24SSimon Glass #endif 7972e192b24SSimon Glass ""; 7982e192b24SSimon Glass #endif 7992e192b24SSimon Glass 8002e192b24SSimon Glass U_BOOT_CMD( 8012e192b24SSimon Glass nand, CONFIG_SYS_MAXARGS, 1, do_nand, 8022e192b24SSimon Glass "NAND sub-system", nand_help_text 8032e192b24SSimon Glass ); 8042e192b24SSimon Glass 805151c06ecSScott Wood static int nand_load_image(cmd_tbl_t *cmdtp, struct mtd_info *mtd, 8062e192b24SSimon Glass ulong offset, ulong addr, char *cmd) 8072e192b24SSimon Glass { 8082e192b24SSimon Glass int r; 8092e192b24SSimon Glass char *s; 8102e192b24SSimon Glass size_t cnt; 8112e192b24SSimon Glass #if defined(CONFIG_IMAGE_FORMAT_LEGACY) 8122e192b24SSimon Glass image_header_t *hdr; 8132e192b24SSimon Glass #endif 8142e192b24SSimon Glass #if defined(CONFIG_FIT) 8152e192b24SSimon Glass const void *fit_hdr = NULL; 8162e192b24SSimon Glass #endif 8172e192b24SSimon Glass 8182e192b24SSimon Glass s = strchr(cmd, '.'); 8192e192b24SSimon Glass if (s != NULL && 8202e192b24SSimon Glass (strcmp(s, ".jffs2") && strcmp(s, ".e") && strcmp(s, ".i"))) { 8212e192b24SSimon Glass printf("Unknown nand load suffix '%s'\n", s); 8222e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX); 8232e192b24SSimon Glass return 1; 8242e192b24SSimon Glass } 8252e192b24SSimon Glass 826151c06ecSScott Wood printf("\nLoading from %s, offset 0x%lx\n", mtd->name, offset); 8272e192b24SSimon Glass 828151c06ecSScott Wood cnt = mtd->writesize; 829151c06ecSScott Wood r = nand_read_skip_bad(mtd, offset, &cnt, NULL, mtd->size, 8302e192b24SSimon Glass (u_char *)addr); 8312e192b24SSimon Glass if (r) { 8322e192b24SSimon Glass puts("** Read error\n"); 8332e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_HDR_READ); 8342e192b24SSimon Glass return 1; 8352e192b24SSimon Glass } 8362e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_HDR_READ); 8372e192b24SSimon Glass 8382e192b24SSimon Glass switch (genimg_get_format ((void *)addr)) { 8392e192b24SSimon Glass #if defined(CONFIG_IMAGE_FORMAT_LEGACY) 8402e192b24SSimon Glass case IMAGE_FORMAT_LEGACY: 8412e192b24SSimon Glass hdr = (image_header_t *)addr; 8422e192b24SSimon Glass 8432e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); 8442e192b24SSimon Glass image_print_contents (hdr); 8452e192b24SSimon Glass 8462e192b24SSimon Glass cnt = image_get_image_size (hdr); 8472e192b24SSimon Glass break; 8482e192b24SSimon Glass #endif 8492e192b24SSimon Glass #if defined(CONFIG_FIT) 8502e192b24SSimon Glass case IMAGE_FORMAT_FIT: 8512e192b24SSimon Glass fit_hdr = (const void *)addr; 8522e192b24SSimon Glass puts ("Fit image detected...\n"); 8532e192b24SSimon Glass 8542e192b24SSimon Glass cnt = fit_get_size (fit_hdr); 8552e192b24SSimon Glass break; 8562e192b24SSimon Glass #endif 8572e192b24SSimon Glass default: 8582e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_TYPE); 8592e192b24SSimon Glass puts ("** Unknown image type\n"); 8602e192b24SSimon Glass return 1; 8612e192b24SSimon Glass } 8622e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); 8632e192b24SSimon Glass 864151c06ecSScott Wood r = nand_read_skip_bad(mtd, offset, &cnt, NULL, mtd->size, 8652e192b24SSimon Glass (u_char *)addr); 8662e192b24SSimon Glass if (r) { 8672e192b24SSimon Glass puts("** Read error\n"); 8682e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_READ); 8692e192b24SSimon Glass return 1; 8702e192b24SSimon Glass } 8712e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_READ); 8722e192b24SSimon Glass 8732e192b24SSimon Glass #if defined(CONFIG_FIT) 8742e192b24SSimon Glass /* This cannot be done earlier, we need complete FIT image in RAM first */ 8752e192b24SSimon Glass if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) { 8762e192b24SSimon Glass if (!fit_check_format (fit_hdr)) { 8772e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_FIT_READ); 8782e192b24SSimon Glass puts ("** Bad FIT image format\n"); 8792e192b24SSimon Glass return 1; 8802e192b24SSimon Glass } 8812e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_FIT_READ_OK); 8822e192b24SSimon Glass fit_print_contents (fit_hdr); 8832e192b24SSimon Glass } 8842e192b24SSimon Glass #endif 8852e192b24SSimon Glass 8862e192b24SSimon Glass /* Loading ok, update default load address */ 8872e192b24SSimon Glass 8882e192b24SSimon Glass load_addr = addr; 8892e192b24SSimon Glass 8902e192b24SSimon Glass return bootm_maybe_autostart(cmdtp, cmd); 8912e192b24SSimon Glass } 8922e192b24SSimon Glass 8932e192b24SSimon Glass static int do_nandboot(cmd_tbl_t *cmdtp, int flag, int argc, 8942e192b24SSimon Glass char * const argv[]) 8952e192b24SSimon Glass { 8962e192b24SSimon Glass char *boot_device = NULL; 8972e192b24SSimon Glass int idx; 8982e192b24SSimon Glass ulong addr, offset = 0; 8992e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS) 9002e192b24SSimon Glass struct mtd_device *dev; 9012e192b24SSimon Glass struct part_info *part; 9022e192b24SSimon Glass u8 pnum; 9032e192b24SSimon Glass 9042e192b24SSimon Glass if (argc >= 2) { 9052e192b24SSimon Glass char *p = (argc == 2) ? argv[1] : argv[2]; 9062e192b24SSimon Glass if (!(str2long(p, &addr)) && (mtdparts_init() == 0) && 9072e192b24SSimon Glass (find_dev_and_part(p, &dev, &pnum, &part) == 0)) { 9082e192b24SSimon Glass if (dev->id->type != MTD_DEV_TYPE_NAND) { 9092e192b24SSimon Glass puts("Not a NAND device\n"); 9102e192b24SSimon Glass return 1; 9112e192b24SSimon Glass } 9122e192b24SSimon Glass if (argc > 3) 9132e192b24SSimon Glass goto usage; 9142e192b24SSimon Glass if (argc == 3) 9152e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 9162e192b24SSimon Glass else 9172e192b24SSimon Glass addr = CONFIG_SYS_LOAD_ADDR; 918b616d9b0SScott Wood return nand_load_image(cmdtp, nand_info[dev->id->num], 9192e192b24SSimon Glass part->offset, addr, argv[0]); 9202e192b24SSimon Glass } 9212e192b24SSimon Glass } 9222e192b24SSimon Glass #endif 9232e192b24SSimon Glass 9242e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_PART); 9252e192b24SSimon Glass switch (argc) { 9262e192b24SSimon Glass case 1: 9272e192b24SSimon Glass addr = CONFIG_SYS_LOAD_ADDR; 9282e192b24SSimon Glass boot_device = getenv("bootdevice"); 9292e192b24SSimon Glass break; 9302e192b24SSimon Glass case 2: 9312e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 9322e192b24SSimon Glass boot_device = getenv("bootdevice"); 9332e192b24SSimon Glass break; 9342e192b24SSimon Glass case 3: 9352e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 9362e192b24SSimon Glass boot_device = argv[2]; 9372e192b24SSimon Glass break; 9382e192b24SSimon Glass case 4: 9392e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 9402e192b24SSimon Glass boot_device = argv[2]; 9412e192b24SSimon Glass offset = simple_strtoul(argv[3], NULL, 16); 9422e192b24SSimon Glass break; 9432e192b24SSimon Glass default: 9442e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS) 9452e192b24SSimon Glass usage: 9462e192b24SSimon Glass #endif 9472e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX); 9482e192b24SSimon Glass return CMD_RET_USAGE; 9492e192b24SSimon Glass } 9502e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_SUFFIX); 9512e192b24SSimon Glass 9522e192b24SSimon Glass if (!boot_device) { 9532e192b24SSimon Glass puts("\n** No boot device **\n"); 9542e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_BOOT_DEVICE); 9552e192b24SSimon Glass return 1; 9562e192b24SSimon Glass } 9572e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_BOOT_DEVICE); 9582e192b24SSimon Glass 9592e192b24SSimon Glass idx = simple_strtoul(boot_device, NULL, 16); 9602e192b24SSimon Glass 961b616d9b0SScott Wood if (idx < 0 || idx >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[idx]->name) { 9622e192b24SSimon Glass printf("\n** Device %d not available\n", idx); 9632e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_AVAILABLE); 9642e192b24SSimon Glass return 1; 9652e192b24SSimon Glass } 9662e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_AVAILABLE); 9672e192b24SSimon Glass 968b616d9b0SScott Wood return nand_load_image(cmdtp, nand_info[idx], offset, addr, argv[0]); 9692e192b24SSimon Glass } 9702e192b24SSimon Glass 9712e192b24SSimon Glass U_BOOT_CMD(nboot, 4, 1, do_nandboot, 9722e192b24SSimon Glass "boot from NAND device", 9732e192b24SSimon Glass "[partition] | [[[loadAddr] dev] offset]" 9742e192b24SSimon Glass ); 975