1*2e192b24SSimon Glass /* 2*2e192b24SSimon Glass * Driver for NAND support, Rick Bronson 3*2e192b24SSimon Glass * borrowed heavily from: 4*2e192b24SSimon Glass * (c) 1999 Machine Vision Holdings, Inc. 5*2e192b24SSimon Glass * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> 6*2e192b24SSimon Glass * 7*2e192b24SSimon Glass * Ported 'dynenv' to 'nand env.oob' command 8*2e192b24SSimon Glass * (C) 2010 Nanometrics, Inc. 9*2e192b24SSimon Glass * 'dynenv' -- Dynamic environment offset in NAND OOB 10*2e192b24SSimon Glass * (C) Copyright 2006-2007 OpenMoko, Inc. 11*2e192b24SSimon Glass * Added 16-bit nand support 12*2e192b24SSimon Glass * (C) 2004 Texas Instruments 13*2e192b24SSimon Glass * 14*2e192b24SSimon Glass * Copyright 2010, 2012 Freescale Semiconductor 15*2e192b24SSimon Glass * The portions of this file whose copyright is held by Freescale and which 16*2e192b24SSimon Glass * are not considered a derived work of GPL v2-only code may be distributed 17*2e192b24SSimon Glass * and/or modified under the terms of the GNU General Public License as 18*2e192b24SSimon Glass * published by the Free Software Foundation; either version 2 of the 19*2e192b24SSimon Glass * License, or (at your option) any later version. 20*2e192b24SSimon Glass */ 21*2e192b24SSimon Glass 22*2e192b24SSimon Glass #include <common.h> 23*2e192b24SSimon Glass #include <linux/mtd/mtd.h> 24*2e192b24SSimon Glass #include <command.h> 25*2e192b24SSimon Glass #include <console.h> 26*2e192b24SSimon Glass #include <watchdog.h> 27*2e192b24SSimon Glass #include <malloc.h> 28*2e192b24SSimon Glass #include <asm/byteorder.h> 29*2e192b24SSimon Glass #include <jffs2/jffs2.h> 30*2e192b24SSimon Glass #include <nand.h> 31*2e192b24SSimon Glass 32*2e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS) 33*2e192b24SSimon Glass 34*2e192b24SSimon Glass /* partition handling routines */ 35*2e192b24SSimon Glass int mtdparts_init(void); 36*2e192b24SSimon Glass int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num); 37*2e192b24SSimon Glass int find_dev_and_part(const char *id, struct mtd_device **dev, 38*2e192b24SSimon Glass u8 *part_num, struct part_info **part); 39*2e192b24SSimon Glass #endif 40*2e192b24SSimon Glass 41*2e192b24SSimon Glass static int nand_dump(nand_info_t *nand, ulong off, int only_oob, int repeat) 42*2e192b24SSimon Glass { 43*2e192b24SSimon Glass int i; 44*2e192b24SSimon Glass u_char *datbuf, *oobbuf, *p; 45*2e192b24SSimon Glass static loff_t last; 46*2e192b24SSimon Glass int ret = 0; 47*2e192b24SSimon Glass 48*2e192b24SSimon Glass if (repeat) 49*2e192b24SSimon Glass off = last + nand->writesize; 50*2e192b24SSimon Glass 51*2e192b24SSimon Glass last = off; 52*2e192b24SSimon Glass 53*2e192b24SSimon Glass datbuf = memalign(ARCH_DMA_MINALIGN, nand->writesize); 54*2e192b24SSimon Glass if (!datbuf) { 55*2e192b24SSimon Glass puts("No memory for page buffer\n"); 56*2e192b24SSimon Glass return 1; 57*2e192b24SSimon Glass } 58*2e192b24SSimon Glass 59*2e192b24SSimon Glass oobbuf = memalign(ARCH_DMA_MINALIGN, nand->oobsize); 60*2e192b24SSimon Glass if (!oobbuf) { 61*2e192b24SSimon Glass puts("No memory for page buffer\n"); 62*2e192b24SSimon Glass ret = 1; 63*2e192b24SSimon Glass goto free_dat; 64*2e192b24SSimon Glass } 65*2e192b24SSimon Glass off &= ~(nand->writesize - 1); 66*2e192b24SSimon Glass loff_t addr = (loff_t) off; 67*2e192b24SSimon Glass struct mtd_oob_ops ops; 68*2e192b24SSimon Glass memset(&ops, 0, sizeof(ops)); 69*2e192b24SSimon Glass ops.datbuf = datbuf; 70*2e192b24SSimon Glass ops.oobbuf = oobbuf; 71*2e192b24SSimon Glass ops.len = nand->writesize; 72*2e192b24SSimon Glass ops.ooblen = nand->oobsize; 73*2e192b24SSimon Glass ops.mode = MTD_OPS_RAW; 74*2e192b24SSimon Glass i = mtd_read_oob(nand, addr, &ops); 75*2e192b24SSimon Glass if (i < 0) { 76*2e192b24SSimon Glass printf("Error (%d) reading page %08lx\n", i, off); 77*2e192b24SSimon Glass ret = 1; 78*2e192b24SSimon Glass goto free_all; 79*2e192b24SSimon Glass } 80*2e192b24SSimon Glass printf("Page %08lx dump:\n", off); 81*2e192b24SSimon Glass 82*2e192b24SSimon Glass if (!only_oob) { 83*2e192b24SSimon Glass i = nand->writesize >> 4; 84*2e192b24SSimon Glass p = datbuf; 85*2e192b24SSimon Glass 86*2e192b24SSimon Glass while (i--) { 87*2e192b24SSimon Glass printf("\t%02x %02x %02x %02x %02x %02x %02x %02x" 88*2e192b24SSimon Glass " %02x %02x %02x %02x %02x %02x %02x %02x\n", 89*2e192b24SSimon Glass p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], 90*2e192b24SSimon Glass p[8], p[9], p[10], p[11], p[12], p[13], p[14], 91*2e192b24SSimon Glass p[15]); 92*2e192b24SSimon Glass p += 16; 93*2e192b24SSimon Glass } 94*2e192b24SSimon Glass } 95*2e192b24SSimon Glass 96*2e192b24SSimon Glass puts("OOB:\n"); 97*2e192b24SSimon Glass i = nand->oobsize >> 3; 98*2e192b24SSimon Glass p = oobbuf; 99*2e192b24SSimon Glass while (i--) { 100*2e192b24SSimon Glass printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n", 101*2e192b24SSimon Glass p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); 102*2e192b24SSimon Glass p += 8; 103*2e192b24SSimon Glass } 104*2e192b24SSimon Glass 105*2e192b24SSimon Glass free_all: 106*2e192b24SSimon Glass free(oobbuf); 107*2e192b24SSimon Glass free_dat: 108*2e192b24SSimon Glass free(datbuf); 109*2e192b24SSimon Glass 110*2e192b24SSimon Glass return ret; 111*2e192b24SSimon Glass } 112*2e192b24SSimon Glass 113*2e192b24SSimon Glass /* ------------------------------------------------------------------------- */ 114*2e192b24SSimon Glass 115*2e192b24SSimon Glass static int set_dev(int dev) 116*2e192b24SSimon Glass { 117*2e192b24SSimon Glass if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || 118*2e192b24SSimon Glass !nand_info[dev].name) { 119*2e192b24SSimon Glass puts("No such device\n"); 120*2e192b24SSimon Glass return -1; 121*2e192b24SSimon Glass } 122*2e192b24SSimon Glass 123*2e192b24SSimon Glass if (nand_curr_device == dev) 124*2e192b24SSimon Glass return 0; 125*2e192b24SSimon Glass 126*2e192b24SSimon Glass printf("Device %d: %s", dev, nand_info[dev].name); 127*2e192b24SSimon Glass puts("... is now current device\n"); 128*2e192b24SSimon Glass nand_curr_device = dev; 129*2e192b24SSimon Glass 130*2e192b24SSimon Glass #ifdef CONFIG_SYS_NAND_SELECT_DEVICE 131*2e192b24SSimon Glass board_nand_select_device(nand_info[dev].priv, dev); 132*2e192b24SSimon Glass #endif 133*2e192b24SSimon Glass 134*2e192b24SSimon Glass return 0; 135*2e192b24SSimon Glass } 136*2e192b24SSimon Glass 137*2e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK 138*2e192b24SSimon Glass static void print_status(ulong start, ulong end, ulong erasesize, int status) 139*2e192b24SSimon Glass { 140*2e192b24SSimon Glass /* 141*2e192b24SSimon Glass * Micron NAND flash (e.g. MT29F4G08ABADAH4) BLOCK LOCK READ STATUS is 142*2e192b24SSimon Glass * not the same as others. Instead of bit 1 being lock, it is 143*2e192b24SSimon Glass * #lock_tight. To make the driver support either format, ignore bit 1 144*2e192b24SSimon Glass * and use only bit 0 and bit 2. 145*2e192b24SSimon Glass */ 146*2e192b24SSimon Glass printf("%08lx - %08lx: %08lx blocks %s%s%s\n", 147*2e192b24SSimon Glass start, 148*2e192b24SSimon Glass end - 1, 149*2e192b24SSimon Glass (end - start) / erasesize, 150*2e192b24SSimon Glass ((status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""), 151*2e192b24SSimon Glass (!(status & NAND_LOCK_STATUS_UNLOCK) ? "LOCK " : ""), 152*2e192b24SSimon Glass ((status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : "")); 153*2e192b24SSimon Glass } 154*2e192b24SSimon Glass 155*2e192b24SSimon Glass static void do_nand_status(nand_info_t *nand) 156*2e192b24SSimon Glass { 157*2e192b24SSimon Glass ulong block_start = 0; 158*2e192b24SSimon Glass ulong off; 159*2e192b24SSimon Glass int last_status = -1; 160*2e192b24SSimon Glass 161*2e192b24SSimon Glass struct nand_chip *nand_chip = nand->priv; 162*2e192b24SSimon Glass /* check the WP bit */ 163*2e192b24SSimon Glass nand_chip->cmdfunc(nand, NAND_CMD_STATUS, -1, -1); 164*2e192b24SSimon Glass printf("device is %swrite protected\n", 165*2e192b24SSimon Glass (nand_chip->read_byte(nand) & 0x80 ? 166*2e192b24SSimon Glass "NOT " : "")); 167*2e192b24SSimon Glass 168*2e192b24SSimon Glass for (off = 0; off < nand->size; off += nand->erasesize) { 169*2e192b24SSimon Glass int s = nand_get_lock_status(nand, off); 170*2e192b24SSimon Glass 171*2e192b24SSimon Glass /* print message only if status has changed */ 172*2e192b24SSimon Glass if (s != last_status && off != 0) { 173*2e192b24SSimon Glass print_status(block_start, off, nand->erasesize, 174*2e192b24SSimon Glass last_status); 175*2e192b24SSimon Glass block_start = off; 176*2e192b24SSimon Glass } 177*2e192b24SSimon Glass last_status = s; 178*2e192b24SSimon Glass } 179*2e192b24SSimon Glass /* Print the last block info */ 180*2e192b24SSimon Glass print_status(block_start, off, nand->erasesize, last_status); 181*2e192b24SSimon Glass } 182*2e192b24SSimon Glass #endif 183*2e192b24SSimon Glass 184*2e192b24SSimon Glass #ifdef CONFIG_ENV_OFFSET_OOB 185*2e192b24SSimon Glass unsigned long nand_env_oob_offset; 186*2e192b24SSimon Glass 187*2e192b24SSimon Glass int do_nand_env_oob(cmd_tbl_t *cmdtp, int argc, char *const argv[]) 188*2e192b24SSimon Glass { 189*2e192b24SSimon Glass int ret; 190*2e192b24SSimon Glass uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; 191*2e192b24SSimon Glass nand_info_t *nand = &nand_info[0]; 192*2e192b24SSimon Glass char *cmd = argv[1]; 193*2e192b24SSimon Glass 194*2e192b24SSimon Glass if (CONFIG_SYS_MAX_NAND_DEVICE == 0 || !nand->name) { 195*2e192b24SSimon Glass puts("no devices available\n"); 196*2e192b24SSimon Glass return 1; 197*2e192b24SSimon Glass } 198*2e192b24SSimon Glass 199*2e192b24SSimon Glass set_dev(0); 200*2e192b24SSimon Glass 201*2e192b24SSimon Glass if (!strcmp(cmd, "get")) { 202*2e192b24SSimon Glass ret = get_nand_env_oob(nand, &nand_env_oob_offset); 203*2e192b24SSimon Glass if (ret) 204*2e192b24SSimon Glass return 1; 205*2e192b24SSimon Glass 206*2e192b24SSimon Glass printf("0x%08lx\n", nand_env_oob_offset); 207*2e192b24SSimon Glass } else if (!strcmp(cmd, "set")) { 208*2e192b24SSimon Glass loff_t addr; 209*2e192b24SSimon Glass loff_t maxsize; 210*2e192b24SSimon Glass struct mtd_oob_ops ops; 211*2e192b24SSimon Glass int idx = 0; 212*2e192b24SSimon Glass 213*2e192b24SSimon Glass if (argc < 3) 214*2e192b24SSimon Glass goto usage; 215*2e192b24SSimon Glass 216*2e192b24SSimon Glass /* We don't care about size, or maxsize. */ 217*2e192b24SSimon Glass if (mtd_arg_off(argv[2], &idx, &addr, &maxsize, &maxsize, 218*2e192b24SSimon Glass MTD_DEV_TYPE_NAND, nand_info[idx].size)) { 219*2e192b24SSimon Glass puts("Offset or partition name expected\n"); 220*2e192b24SSimon Glass return 1; 221*2e192b24SSimon Glass } 222*2e192b24SSimon Glass if (set_dev(idx)) { 223*2e192b24SSimon Glass puts("Offset or partition name expected\n"); 224*2e192b24SSimon Glass return 1; 225*2e192b24SSimon Glass } 226*2e192b24SSimon Glass 227*2e192b24SSimon Glass if (idx != 0) { 228*2e192b24SSimon Glass puts("Partition not on first NAND device\n"); 229*2e192b24SSimon Glass return 1; 230*2e192b24SSimon Glass } 231*2e192b24SSimon Glass 232*2e192b24SSimon Glass if (nand->oobavail < ENV_OFFSET_SIZE) { 233*2e192b24SSimon Glass printf("Insufficient available OOB bytes:\n" 234*2e192b24SSimon Glass "%d OOB bytes available but %d required for " 235*2e192b24SSimon Glass "env.oob support\n", 236*2e192b24SSimon Glass nand->oobavail, ENV_OFFSET_SIZE); 237*2e192b24SSimon Glass return 1; 238*2e192b24SSimon Glass } 239*2e192b24SSimon Glass 240*2e192b24SSimon Glass if ((addr & (nand->erasesize - 1)) != 0) { 241*2e192b24SSimon Glass printf("Environment offset must be block-aligned\n"); 242*2e192b24SSimon Glass return 1; 243*2e192b24SSimon Glass } 244*2e192b24SSimon Glass 245*2e192b24SSimon Glass ops.datbuf = NULL; 246*2e192b24SSimon Glass ops.mode = MTD_OOB_AUTO; 247*2e192b24SSimon Glass ops.ooboffs = 0; 248*2e192b24SSimon Glass ops.ooblen = ENV_OFFSET_SIZE; 249*2e192b24SSimon Glass ops.oobbuf = (void *) oob_buf; 250*2e192b24SSimon Glass 251*2e192b24SSimon Glass oob_buf[0] = ENV_OOB_MARKER; 252*2e192b24SSimon Glass oob_buf[1] = addr / nand->erasesize; 253*2e192b24SSimon Glass 254*2e192b24SSimon Glass ret = nand->write_oob(nand, ENV_OFFSET_SIZE, &ops); 255*2e192b24SSimon Glass if (ret) { 256*2e192b24SSimon Glass printf("Error writing OOB block 0\n"); 257*2e192b24SSimon Glass return ret; 258*2e192b24SSimon Glass } 259*2e192b24SSimon Glass 260*2e192b24SSimon Glass ret = get_nand_env_oob(nand, &nand_env_oob_offset); 261*2e192b24SSimon Glass if (ret) { 262*2e192b24SSimon Glass printf("Error reading env offset in OOB\n"); 263*2e192b24SSimon Glass return ret; 264*2e192b24SSimon Glass } 265*2e192b24SSimon Glass 266*2e192b24SSimon Glass if (addr != nand_env_oob_offset) { 267*2e192b24SSimon Glass printf("Verification of env offset in OOB failed: " 268*2e192b24SSimon Glass "0x%08llx expected but got 0x%08lx\n", 269*2e192b24SSimon Glass (unsigned long long)addr, nand_env_oob_offset); 270*2e192b24SSimon Glass return 1; 271*2e192b24SSimon Glass } 272*2e192b24SSimon Glass } else { 273*2e192b24SSimon Glass goto usage; 274*2e192b24SSimon Glass } 275*2e192b24SSimon Glass 276*2e192b24SSimon Glass return ret; 277*2e192b24SSimon Glass 278*2e192b24SSimon Glass usage: 279*2e192b24SSimon Glass return CMD_RET_USAGE; 280*2e192b24SSimon Glass } 281*2e192b24SSimon Glass 282*2e192b24SSimon Glass #endif 283*2e192b24SSimon Glass 284*2e192b24SSimon Glass static void nand_print_and_set_info(int idx) 285*2e192b24SSimon Glass { 286*2e192b24SSimon Glass nand_info_t *nand = &nand_info[idx]; 287*2e192b24SSimon Glass struct nand_chip *chip = nand->priv; 288*2e192b24SSimon Glass 289*2e192b24SSimon Glass printf("Device %d: ", idx); 290*2e192b24SSimon Glass if (chip->numchips > 1) 291*2e192b24SSimon Glass printf("%dx ", chip->numchips); 292*2e192b24SSimon Glass printf("%s, sector size %u KiB\n", 293*2e192b24SSimon Glass nand->name, nand->erasesize >> 10); 294*2e192b24SSimon Glass printf(" Page size %8d b\n", nand->writesize); 295*2e192b24SSimon Glass printf(" OOB size %8d b\n", nand->oobsize); 296*2e192b24SSimon Glass printf(" Erase size %8d b\n", nand->erasesize); 297*2e192b24SSimon Glass printf(" subpagesize %8d b\n", chip->subpagesize); 298*2e192b24SSimon Glass printf(" options 0x%8x\n", chip->options); 299*2e192b24SSimon Glass printf(" bbt options 0x%8x\n", chip->bbt_options); 300*2e192b24SSimon Glass 301*2e192b24SSimon Glass /* Set geometry info */ 302*2e192b24SSimon Glass setenv_hex("nand_writesize", nand->writesize); 303*2e192b24SSimon Glass setenv_hex("nand_oobsize", nand->oobsize); 304*2e192b24SSimon Glass setenv_hex("nand_erasesize", nand->erasesize); 305*2e192b24SSimon Glass } 306*2e192b24SSimon Glass 307*2e192b24SSimon Glass static int raw_access(nand_info_t *nand, ulong addr, loff_t off, ulong count, 308*2e192b24SSimon Glass int read) 309*2e192b24SSimon Glass { 310*2e192b24SSimon Glass int ret = 0; 311*2e192b24SSimon Glass 312*2e192b24SSimon Glass while (count--) { 313*2e192b24SSimon Glass /* Raw access */ 314*2e192b24SSimon Glass mtd_oob_ops_t ops = { 315*2e192b24SSimon Glass .datbuf = (u8 *)addr, 316*2e192b24SSimon Glass .oobbuf = ((u8 *)addr) + nand->writesize, 317*2e192b24SSimon Glass .len = nand->writesize, 318*2e192b24SSimon Glass .ooblen = nand->oobsize, 319*2e192b24SSimon Glass .mode = MTD_OPS_RAW 320*2e192b24SSimon Glass }; 321*2e192b24SSimon Glass 322*2e192b24SSimon Glass if (read) { 323*2e192b24SSimon Glass ret = mtd_read_oob(nand, off, &ops); 324*2e192b24SSimon Glass } else { 325*2e192b24SSimon Glass ret = mtd_write_oob(nand, off, &ops); 326*2e192b24SSimon Glass if (!ret) 327*2e192b24SSimon Glass ret = nand_verify_page_oob(nand, &ops, off); 328*2e192b24SSimon Glass } 329*2e192b24SSimon Glass 330*2e192b24SSimon Glass if (ret) { 331*2e192b24SSimon Glass printf("%s: error at offset %llx, ret %d\n", 332*2e192b24SSimon Glass __func__, (long long)off, ret); 333*2e192b24SSimon Glass break; 334*2e192b24SSimon Glass } 335*2e192b24SSimon Glass 336*2e192b24SSimon Glass addr += nand->writesize + nand->oobsize; 337*2e192b24SSimon Glass off += nand->writesize; 338*2e192b24SSimon Glass } 339*2e192b24SSimon Glass 340*2e192b24SSimon Glass return ret; 341*2e192b24SSimon Glass } 342*2e192b24SSimon Glass 343*2e192b24SSimon Glass /* Adjust a chip/partition size down for bad blocks so we don't 344*2e192b24SSimon Glass * read/write past the end of a chip/partition by accident. 345*2e192b24SSimon Glass */ 346*2e192b24SSimon Glass static void adjust_size_for_badblocks(loff_t *size, loff_t offset, int dev) 347*2e192b24SSimon Glass { 348*2e192b24SSimon Glass /* We grab the nand info object here fresh because this is usually 349*2e192b24SSimon Glass * called after arg_off_size() which can change the value of dev. 350*2e192b24SSimon Glass */ 351*2e192b24SSimon Glass nand_info_t *nand = &nand_info[dev]; 352*2e192b24SSimon Glass loff_t maxoffset = offset + *size; 353*2e192b24SSimon Glass int badblocks = 0; 354*2e192b24SSimon Glass 355*2e192b24SSimon Glass /* count badblocks in NAND from offset to offset + size */ 356*2e192b24SSimon Glass for (; offset < maxoffset; offset += nand->erasesize) { 357*2e192b24SSimon Glass if (nand_block_isbad(nand, offset)) 358*2e192b24SSimon Glass badblocks++; 359*2e192b24SSimon Glass } 360*2e192b24SSimon Glass /* adjust size if any bad blocks found */ 361*2e192b24SSimon Glass if (badblocks) { 362*2e192b24SSimon Glass *size -= badblocks * nand->erasesize; 363*2e192b24SSimon Glass printf("size adjusted to 0x%llx (%d bad blocks)\n", 364*2e192b24SSimon Glass (unsigned long long)*size, badblocks); 365*2e192b24SSimon Glass } 366*2e192b24SSimon Glass } 367*2e192b24SSimon Glass 368*2e192b24SSimon Glass static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 369*2e192b24SSimon Glass { 370*2e192b24SSimon Glass int i, ret = 0; 371*2e192b24SSimon Glass ulong addr; 372*2e192b24SSimon Glass loff_t off, size, maxsize; 373*2e192b24SSimon Glass char *cmd, *s; 374*2e192b24SSimon Glass nand_info_t *nand; 375*2e192b24SSimon Glass #ifdef CONFIG_SYS_NAND_QUIET 376*2e192b24SSimon Glass int quiet = CONFIG_SYS_NAND_QUIET; 377*2e192b24SSimon Glass #else 378*2e192b24SSimon Glass int quiet = 0; 379*2e192b24SSimon Glass #endif 380*2e192b24SSimon Glass const char *quiet_str = getenv("quiet"); 381*2e192b24SSimon Glass int dev = nand_curr_device; 382*2e192b24SSimon Glass int repeat = flag & CMD_FLAG_REPEAT; 383*2e192b24SSimon Glass 384*2e192b24SSimon Glass /* at least two arguments please */ 385*2e192b24SSimon Glass if (argc < 2) 386*2e192b24SSimon Glass goto usage; 387*2e192b24SSimon Glass 388*2e192b24SSimon Glass if (quiet_str) 389*2e192b24SSimon Glass quiet = simple_strtoul(quiet_str, NULL, 0) != 0; 390*2e192b24SSimon Glass 391*2e192b24SSimon Glass cmd = argv[1]; 392*2e192b24SSimon Glass 393*2e192b24SSimon Glass /* Only "dump" is repeatable. */ 394*2e192b24SSimon Glass if (repeat && strcmp(cmd, "dump")) 395*2e192b24SSimon Glass return 0; 396*2e192b24SSimon Glass 397*2e192b24SSimon Glass if (strcmp(cmd, "info") == 0) { 398*2e192b24SSimon Glass 399*2e192b24SSimon Glass putc('\n'); 400*2e192b24SSimon Glass for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { 401*2e192b24SSimon Glass if (nand_info[i].name) 402*2e192b24SSimon Glass nand_print_and_set_info(i); 403*2e192b24SSimon Glass } 404*2e192b24SSimon Glass return 0; 405*2e192b24SSimon Glass } 406*2e192b24SSimon Glass 407*2e192b24SSimon Glass if (strcmp(cmd, "device") == 0) { 408*2e192b24SSimon Glass if (argc < 3) { 409*2e192b24SSimon Glass putc('\n'); 410*2e192b24SSimon Glass if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE) 411*2e192b24SSimon Glass puts("no devices available\n"); 412*2e192b24SSimon Glass else 413*2e192b24SSimon Glass nand_print_and_set_info(dev); 414*2e192b24SSimon Glass return 0; 415*2e192b24SSimon Glass } 416*2e192b24SSimon Glass 417*2e192b24SSimon Glass dev = (int)simple_strtoul(argv[2], NULL, 10); 418*2e192b24SSimon Glass set_dev(dev); 419*2e192b24SSimon Glass 420*2e192b24SSimon Glass return 0; 421*2e192b24SSimon Glass } 422*2e192b24SSimon Glass 423*2e192b24SSimon Glass #ifdef CONFIG_ENV_OFFSET_OOB 424*2e192b24SSimon Glass /* this command operates only on the first nand device */ 425*2e192b24SSimon Glass if (strcmp(cmd, "env.oob") == 0) 426*2e192b24SSimon Glass return do_nand_env_oob(cmdtp, argc - 1, argv + 1); 427*2e192b24SSimon Glass #endif 428*2e192b24SSimon Glass 429*2e192b24SSimon Glass /* The following commands operate on the current device, unless 430*2e192b24SSimon Glass * overridden by a partition specifier. Note that if somehow the 431*2e192b24SSimon Glass * current device is invalid, it will have to be changed to a valid 432*2e192b24SSimon Glass * one before these commands can run, even if a partition specifier 433*2e192b24SSimon Glass * for another device is to be used. 434*2e192b24SSimon Glass */ 435*2e192b24SSimon Glass if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || 436*2e192b24SSimon Glass !nand_info[dev].name) { 437*2e192b24SSimon Glass puts("\nno devices available\n"); 438*2e192b24SSimon Glass return 1; 439*2e192b24SSimon Glass } 440*2e192b24SSimon Glass nand = &nand_info[dev]; 441*2e192b24SSimon Glass 442*2e192b24SSimon Glass if (strcmp(cmd, "bad") == 0) { 443*2e192b24SSimon Glass printf("\nDevice %d bad blocks:\n", dev); 444*2e192b24SSimon Glass for (off = 0; off < nand->size; off += nand->erasesize) 445*2e192b24SSimon Glass if (nand_block_isbad(nand, off)) 446*2e192b24SSimon Glass printf(" %08llx\n", (unsigned long long)off); 447*2e192b24SSimon Glass return 0; 448*2e192b24SSimon Glass } 449*2e192b24SSimon Glass 450*2e192b24SSimon Glass /* 451*2e192b24SSimon Glass * Syntax is: 452*2e192b24SSimon Glass * 0 1 2 3 4 453*2e192b24SSimon Glass * nand erase [clean] [off size] 454*2e192b24SSimon Glass */ 455*2e192b24SSimon Glass if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) { 456*2e192b24SSimon Glass nand_erase_options_t opts; 457*2e192b24SSimon Glass /* "clean" at index 2 means request to write cleanmarker */ 458*2e192b24SSimon Glass int clean = argc > 2 && !strcmp("clean", argv[2]); 459*2e192b24SSimon Glass int scrub_yes = argc > 2 && !strcmp("-y", argv[2]); 460*2e192b24SSimon Glass int o = (clean || scrub_yes) ? 3 : 2; 461*2e192b24SSimon Glass int scrub = !strncmp(cmd, "scrub", 5); 462*2e192b24SSimon Glass int spread = 0; 463*2e192b24SSimon Glass int args = 2; 464*2e192b24SSimon Glass const char *scrub_warn = 465*2e192b24SSimon Glass "Warning: " 466*2e192b24SSimon Glass "scrub option will erase all factory set bad blocks!\n" 467*2e192b24SSimon Glass " " 468*2e192b24SSimon Glass "There is no reliable way to recover them.\n" 469*2e192b24SSimon Glass " " 470*2e192b24SSimon Glass "Use this command only for testing purposes if you\n" 471*2e192b24SSimon Glass " " 472*2e192b24SSimon Glass "are sure of what you are doing!\n" 473*2e192b24SSimon Glass "\nReally scrub this NAND flash? <y/N>\n"; 474*2e192b24SSimon Glass 475*2e192b24SSimon Glass if (cmd[5] != 0) { 476*2e192b24SSimon Glass if (!strcmp(&cmd[5], ".spread")) { 477*2e192b24SSimon Glass spread = 1; 478*2e192b24SSimon Glass } else if (!strcmp(&cmd[5], ".part")) { 479*2e192b24SSimon Glass args = 1; 480*2e192b24SSimon Glass } else if (!strcmp(&cmd[5], ".chip")) { 481*2e192b24SSimon Glass args = 0; 482*2e192b24SSimon Glass } else { 483*2e192b24SSimon Glass goto usage; 484*2e192b24SSimon Glass } 485*2e192b24SSimon Glass } 486*2e192b24SSimon Glass 487*2e192b24SSimon Glass /* 488*2e192b24SSimon Glass * Don't allow missing arguments to cause full chip/partition 489*2e192b24SSimon Glass * erases -- easy to do accidentally, e.g. with a misspelled 490*2e192b24SSimon Glass * variable name. 491*2e192b24SSimon Glass */ 492*2e192b24SSimon Glass if (argc != o + args) 493*2e192b24SSimon Glass goto usage; 494*2e192b24SSimon Glass 495*2e192b24SSimon Glass printf("\nNAND %s: ", cmd); 496*2e192b24SSimon Glass /* skip first two or three arguments, look for offset and size */ 497*2e192b24SSimon Glass if (mtd_arg_off_size(argc - o, argv + o, &dev, &off, &size, 498*2e192b24SSimon Glass &maxsize, MTD_DEV_TYPE_NAND, 499*2e192b24SSimon Glass nand_info[dev].size) != 0) 500*2e192b24SSimon Glass return 1; 501*2e192b24SSimon Glass 502*2e192b24SSimon Glass if (set_dev(dev)) 503*2e192b24SSimon Glass return 1; 504*2e192b24SSimon Glass 505*2e192b24SSimon Glass nand = &nand_info[dev]; 506*2e192b24SSimon Glass 507*2e192b24SSimon Glass memset(&opts, 0, sizeof(opts)); 508*2e192b24SSimon Glass opts.offset = off; 509*2e192b24SSimon Glass opts.length = size; 510*2e192b24SSimon Glass opts.jffs2 = clean; 511*2e192b24SSimon Glass opts.quiet = quiet; 512*2e192b24SSimon Glass opts.spread = spread; 513*2e192b24SSimon Glass 514*2e192b24SSimon Glass if (scrub) { 515*2e192b24SSimon Glass if (scrub_yes) { 516*2e192b24SSimon Glass opts.scrub = 1; 517*2e192b24SSimon Glass } else { 518*2e192b24SSimon Glass puts(scrub_warn); 519*2e192b24SSimon Glass if (confirm_yesno()) { 520*2e192b24SSimon Glass opts.scrub = 1; 521*2e192b24SSimon Glass } else { 522*2e192b24SSimon Glass puts("scrub aborted\n"); 523*2e192b24SSimon Glass return 1; 524*2e192b24SSimon Glass } 525*2e192b24SSimon Glass } 526*2e192b24SSimon Glass } 527*2e192b24SSimon Glass ret = nand_erase_opts(nand, &opts); 528*2e192b24SSimon Glass printf("%s\n", ret ? "ERROR" : "OK"); 529*2e192b24SSimon Glass 530*2e192b24SSimon Glass return ret == 0 ? 0 : 1; 531*2e192b24SSimon Glass } 532*2e192b24SSimon Glass 533*2e192b24SSimon Glass if (strncmp(cmd, "dump", 4) == 0) { 534*2e192b24SSimon Glass if (argc < 3) 535*2e192b24SSimon Glass goto usage; 536*2e192b24SSimon Glass 537*2e192b24SSimon Glass off = (int)simple_strtoul(argv[2], NULL, 16); 538*2e192b24SSimon Glass ret = nand_dump(nand, off, !strcmp(&cmd[4], ".oob"), repeat); 539*2e192b24SSimon Glass 540*2e192b24SSimon Glass return ret == 0 ? 1 : 0; 541*2e192b24SSimon Glass } 542*2e192b24SSimon Glass 543*2e192b24SSimon Glass if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { 544*2e192b24SSimon Glass size_t rwsize; 545*2e192b24SSimon Glass ulong pagecount = 1; 546*2e192b24SSimon Glass int read; 547*2e192b24SSimon Glass int raw = 0; 548*2e192b24SSimon Glass 549*2e192b24SSimon Glass if (argc < 4) 550*2e192b24SSimon Glass goto usage; 551*2e192b24SSimon Glass 552*2e192b24SSimon Glass addr = (ulong)simple_strtoul(argv[2], NULL, 16); 553*2e192b24SSimon Glass 554*2e192b24SSimon Glass read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ 555*2e192b24SSimon Glass printf("\nNAND %s: ", read ? "read" : "write"); 556*2e192b24SSimon Glass 557*2e192b24SSimon Glass s = strchr(cmd, '.'); 558*2e192b24SSimon Glass 559*2e192b24SSimon Glass if (s && !strcmp(s, ".raw")) { 560*2e192b24SSimon Glass raw = 1; 561*2e192b24SSimon Glass 562*2e192b24SSimon Glass if (mtd_arg_off(argv[3], &dev, &off, &size, &maxsize, 563*2e192b24SSimon Glass MTD_DEV_TYPE_NAND, 564*2e192b24SSimon Glass nand_info[dev].size)) 565*2e192b24SSimon Glass return 1; 566*2e192b24SSimon Glass 567*2e192b24SSimon Glass if (set_dev(dev)) 568*2e192b24SSimon Glass return 1; 569*2e192b24SSimon Glass 570*2e192b24SSimon Glass nand = &nand_info[dev]; 571*2e192b24SSimon Glass 572*2e192b24SSimon Glass if (argc > 4 && !str2long(argv[4], &pagecount)) { 573*2e192b24SSimon Glass printf("'%s' is not a number\n", argv[4]); 574*2e192b24SSimon Glass return 1; 575*2e192b24SSimon Glass } 576*2e192b24SSimon Glass 577*2e192b24SSimon Glass if (pagecount * nand->writesize > size) { 578*2e192b24SSimon Glass puts("Size exceeds partition or device limit\n"); 579*2e192b24SSimon Glass return -1; 580*2e192b24SSimon Glass } 581*2e192b24SSimon Glass 582*2e192b24SSimon Glass rwsize = pagecount * (nand->writesize + nand->oobsize); 583*2e192b24SSimon Glass } else { 584*2e192b24SSimon Glass if (mtd_arg_off_size(argc - 3, argv + 3, &dev, &off, 585*2e192b24SSimon Glass &size, &maxsize, 586*2e192b24SSimon Glass MTD_DEV_TYPE_NAND, 587*2e192b24SSimon Glass nand_info[dev].size) != 0) 588*2e192b24SSimon Glass return 1; 589*2e192b24SSimon Glass 590*2e192b24SSimon Glass if (set_dev(dev)) 591*2e192b24SSimon Glass return 1; 592*2e192b24SSimon Glass 593*2e192b24SSimon Glass /* size is unspecified */ 594*2e192b24SSimon Glass if (argc < 5) 595*2e192b24SSimon Glass adjust_size_for_badblocks(&size, off, dev); 596*2e192b24SSimon Glass rwsize = size; 597*2e192b24SSimon Glass } 598*2e192b24SSimon Glass 599*2e192b24SSimon Glass nand = &nand_info[dev]; 600*2e192b24SSimon Glass 601*2e192b24SSimon Glass if (!s || !strcmp(s, ".jffs2") || 602*2e192b24SSimon Glass !strcmp(s, ".e") || !strcmp(s, ".i")) { 603*2e192b24SSimon Glass if (read) 604*2e192b24SSimon Glass ret = nand_read_skip_bad(nand, off, &rwsize, 605*2e192b24SSimon Glass NULL, maxsize, 606*2e192b24SSimon Glass (u_char *)addr); 607*2e192b24SSimon Glass else 608*2e192b24SSimon Glass ret = nand_write_skip_bad(nand, off, &rwsize, 609*2e192b24SSimon Glass NULL, maxsize, 610*2e192b24SSimon Glass (u_char *)addr, 611*2e192b24SSimon Glass WITH_WR_VERIFY); 612*2e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TRIMFFS 613*2e192b24SSimon Glass } else if (!strcmp(s, ".trimffs")) { 614*2e192b24SSimon Glass if (read) { 615*2e192b24SSimon Glass printf("Unknown nand command suffix '%s'\n", s); 616*2e192b24SSimon Glass return 1; 617*2e192b24SSimon Glass } 618*2e192b24SSimon Glass ret = nand_write_skip_bad(nand, off, &rwsize, NULL, 619*2e192b24SSimon Glass maxsize, (u_char *)addr, 620*2e192b24SSimon Glass WITH_DROP_FFS | WITH_WR_VERIFY); 621*2e192b24SSimon Glass #endif 622*2e192b24SSimon Glass } else if (!strcmp(s, ".oob")) { 623*2e192b24SSimon Glass /* out-of-band data */ 624*2e192b24SSimon Glass mtd_oob_ops_t ops = { 625*2e192b24SSimon Glass .oobbuf = (u8 *)addr, 626*2e192b24SSimon Glass .ooblen = rwsize, 627*2e192b24SSimon Glass .mode = MTD_OPS_RAW 628*2e192b24SSimon Glass }; 629*2e192b24SSimon Glass 630*2e192b24SSimon Glass if (read) 631*2e192b24SSimon Glass ret = mtd_read_oob(nand, off, &ops); 632*2e192b24SSimon Glass else 633*2e192b24SSimon Glass ret = mtd_write_oob(nand, off, &ops); 634*2e192b24SSimon Glass } else if (raw) { 635*2e192b24SSimon Glass ret = raw_access(nand, addr, off, pagecount, read); 636*2e192b24SSimon Glass } else { 637*2e192b24SSimon Glass printf("Unknown nand command suffix '%s'.\n", s); 638*2e192b24SSimon Glass return 1; 639*2e192b24SSimon Glass } 640*2e192b24SSimon Glass 641*2e192b24SSimon Glass printf(" %zu bytes %s: %s\n", rwsize, 642*2e192b24SSimon Glass read ? "read" : "written", ret ? "ERROR" : "OK"); 643*2e192b24SSimon Glass 644*2e192b24SSimon Glass return ret == 0 ? 0 : 1; 645*2e192b24SSimon Glass } 646*2e192b24SSimon Glass 647*2e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TORTURE 648*2e192b24SSimon Glass if (strcmp(cmd, "torture") == 0) { 649*2e192b24SSimon Glass if (argc < 3) 650*2e192b24SSimon Glass goto usage; 651*2e192b24SSimon Glass 652*2e192b24SSimon Glass if (!str2off(argv[2], &off)) { 653*2e192b24SSimon Glass puts("Offset is not a valid number\n"); 654*2e192b24SSimon Glass return 1; 655*2e192b24SSimon Glass } 656*2e192b24SSimon Glass 657*2e192b24SSimon Glass printf("\nNAND torture: device %d offset 0x%llx size 0x%x\n", 658*2e192b24SSimon Glass dev, off, nand->erasesize); 659*2e192b24SSimon Glass ret = nand_torture(nand, off); 660*2e192b24SSimon Glass printf(" %s\n", ret ? "Failed" : "Passed"); 661*2e192b24SSimon Glass 662*2e192b24SSimon Glass return ret == 0 ? 0 : 1; 663*2e192b24SSimon Glass } 664*2e192b24SSimon Glass #endif 665*2e192b24SSimon Glass 666*2e192b24SSimon Glass if (strcmp(cmd, "markbad") == 0) { 667*2e192b24SSimon Glass argc -= 2; 668*2e192b24SSimon Glass argv += 2; 669*2e192b24SSimon Glass 670*2e192b24SSimon Glass if (argc <= 0) 671*2e192b24SSimon Glass goto usage; 672*2e192b24SSimon Glass 673*2e192b24SSimon Glass while (argc > 0) { 674*2e192b24SSimon Glass addr = simple_strtoul(*argv, NULL, 16); 675*2e192b24SSimon Glass 676*2e192b24SSimon Glass if (mtd_block_markbad(nand, addr)) { 677*2e192b24SSimon Glass printf("block 0x%08lx NOT marked " 678*2e192b24SSimon Glass "as bad! ERROR %d\n", 679*2e192b24SSimon Glass addr, ret); 680*2e192b24SSimon Glass ret = 1; 681*2e192b24SSimon Glass } else { 682*2e192b24SSimon Glass printf("block 0x%08lx successfully " 683*2e192b24SSimon Glass "marked as bad\n", 684*2e192b24SSimon Glass addr); 685*2e192b24SSimon Glass } 686*2e192b24SSimon Glass --argc; 687*2e192b24SSimon Glass ++argv; 688*2e192b24SSimon Glass } 689*2e192b24SSimon Glass return ret; 690*2e192b24SSimon Glass } 691*2e192b24SSimon Glass 692*2e192b24SSimon Glass if (strcmp(cmd, "biterr") == 0) { 693*2e192b24SSimon Glass /* todo */ 694*2e192b24SSimon Glass return 1; 695*2e192b24SSimon Glass } 696*2e192b24SSimon Glass 697*2e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK 698*2e192b24SSimon Glass if (strcmp(cmd, "lock") == 0) { 699*2e192b24SSimon Glass int tight = 0; 700*2e192b24SSimon Glass int status = 0; 701*2e192b24SSimon Glass if (argc == 3) { 702*2e192b24SSimon Glass if (!strcmp("tight", argv[2])) 703*2e192b24SSimon Glass tight = 1; 704*2e192b24SSimon Glass if (!strcmp("status", argv[2])) 705*2e192b24SSimon Glass status = 1; 706*2e192b24SSimon Glass } 707*2e192b24SSimon Glass if (status) { 708*2e192b24SSimon Glass do_nand_status(nand); 709*2e192b24SSimon Glass } else { 710*2e192b24SSimon Glass if (!nand_lock(nand, tight)) { 711*2e192b24SSimon Glass puts("NAND flash successfully locked\n"); 712*2e192b24SSimon Glass } else { 713*2e192b24SSimon Glass puts("Error locking NAND flash\n"); 714*2e192b24SSimon Glass return 1; 715*2e192b24SSimon Glass } 716*2e192b24SSimon Glass } 717*2e192b24SSimon Glass return 0; 718*2e192b24SSimon Glass } 719*2e192b24SSimon Glass 720*2e192b24SSimon Glass if (strncmp(cmd, "unlock", 5) == 0) { 721*2e192b24SSimon Glass int allexcept = 0; 722*2e192b24SSimon Glass 723*2e192b24SSimon Glass s = strchr(cmd, '.'); 724*2e192b24SSimon Glass 725*2e192b24SSimon Glass if (s && !strcmp(s, ".allexcept")) 726*2e192b24SSimon Glass allexcept = 1; 727*2e192b24SSimon Glass 728*2e192b24SSimon Glass if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &off, &size, 729*2e192b24SSimon Glass &maxsize, MTD_DEV_TYPE_NAND, 730*2e192b24SSimon Glass nand_info[dev].size) < 0) 731*2e192b24SSimon Glass return 1; 732*2e192b24SSimon Glass 733*2e192b24SSimon Glass if (set_dev(dev)) 734*2e192b24SSimon Glass return 1; 735*2e192b24SSimon Glass 736*2e192b24SSimon Glass if (!nand_unlock(&nand_info[dev], off, size, allexcept)) { 737*2e192b24SSimon Glass puts("NAND flash successfully unlocked\n"); 738*2e192b24SSimon Glass } else { 739*2e192b24SSimon Glass puts("Error unlocking NAND flash, " 740*2e192b24SSimon Glass "write and erase will probably fail\n"); 741*2e192b24SSimon Glass return 1; 742*2e192b24SSimon Glass } 743*2e192b24SSimon Glass return 0; 744*2e192b24SSimon Glass } 745*2e192b24SSimon Glass #endif 746*2e192b24SSimon Glass 747*2e192b24SSimon Glass usage: 748*2e192b24SSimon Glass return CMD_RET_USAGE; 749*2e192b24SSimon Glass } 750*2e192b24SSimon Glass 751*2e192b24SSimon Glass #ifdef CONFIG_SYS_LONGHELP 752*2e192b24SSimon Glass static char nand_help_text[] = 753*2e192b24SSimon Glass "info - show available NAND devices\n" 754*2e192b24SSimon Glass "nand device [dev] - show or set current device\n" 755*2e192b24SSimon Glass "nand read - addr off|partition size\n" 756*2e192b24SSimon Glass "nand write - addr off|partition size\n" 757*2e192b24SSimon Glass " read/write 'size' bytes starting at offset 'off'\n" 758*2e192b24SSimon Glass " to/from memory address 'addr', skipping bad blocks.\n" 759*2e192b24SSimon Glass "nand read.raw - addr off|partition [count]\n" 760*2e192b24SSimon Glass "nand write.raw - addr off|partition [count]\n" 761*2e192b24SSimon Glass " Use read.raw/write.raw to avoid ECC and access the flash as-is.\n" 762*2e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TRIMFFS 763*2e192b24SSimon Glass "nand write.trimffs - addr off|partition size\n" 764*2e192b24SSimon Glass " write 'size' bytes starting at offset 'off' from memory address\n" 765*2e192b24SSimon Glass " 'addr', skipping bad blocks and dropping any pages at the end\n" 766*2e192b24SSimon Glass " of eraseblocks that contain only 0xFF\n" 767*2e192b24SSimon Glass #endif 768*2e192b24SSimon Glass "nand erase[.spread] [clean] off size - erase 'size' bytes " 769*2e192b24SSimon Glass "from offset 'off'\n" 770*2e192b24SSimon Glass " With '.spread', erase enough for given file size, otherwise,\n" 771*2e192b24SSimon Glass " 'size' includes skipped bad blocks.\n" 772*2e192b24SSimon Glass "nand erase.part [clean] partition - erase entire mtd partition'\n" 773*2e192b24SSimon Glass "nand erase.chip [clean] - erase entire chip'\n" 774*2e192b24SSimon Glass "nand bad - show bad blocks\n" 775*2e192b24SSimon Glass "nand dump[.oob] off - dump page\n" 776*2e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_TORTURE 777*2e192b24SSimon Glass "nand torture off - torture block at offset\n" 778*2e192b24SSimon Glass #endif 779*2e192b24SSimon Glass "nand scrub [-y] off size | scrub.part partition | scrub.chip\n" 780*2e192b24SSimon Glass " really clean NAND erasing bad blocks (UNSAFE)\n" 781*2e192b24SSimon Glass "nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n" 782*2e192b24SSimon Glass "nand biterr off - make a bit error at offset (UNSAFE)" 783*2e192b24SSimon Glass #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK 784*2e192b24SSimon Glass "\n" 785*2e192b24SSimon Glass "nand lock [tight] [status]\n" 786*2e192b24SSimon Glass " bring nand to lock state or display locked pages\n" 787*2e192b24SSimon Glass "nand unlock[.allexcept] [offset] [size] - unlock section" 788*2e192b24SSimon Glass #endif 789*2e192b24SSimon Glass #ifdef CONFIG_ENV_OFFSET_OOB 790*2e192b24SSimon Glass "\n" 791*2e192b24SSimon Glass "nand env.oob - environment offset in OOB of block 0 of" 792*2e192b24SSimon Glass " first device.\n" 793*2e192b24SSimon Glass "nand env.oob set off|partition - set enviromnent offset\n" 794*2e192b24SSimon Glass "nand env.oob get - get environment offset" 795*2e192b24SSimon Glass #endif 796*2e192b24SSimon Glass ""; 797*2e192b24SSimon Glass #endif 798*2e192b24SSimon Glass 799*2e192b24SSimon Glass U_BOOT_CMD( 800*2e192b24SSimon Glass nand, CONFIG_SYS_MAXARGS, 1, do_nand, 801*2e192b24SSimon Glass "NAND sub-system", nand_help_text 802*2e192b24SSimon Glass ); 803*2e192b24SSimon Glass 804*2e192b24SSimon Glass static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, 805*2e192b24SSimon Glass ulong offset, ulong addr, char *cmd) 806*2e192b24SSimon Glass { 807*2e192b24SSimon Glass int r; 808*2e192b24SSimon Glass char *s; 809*2e192b24SSimon Glass size_t cnt; 810*2e192b24SSimon Glass #if defined(CONFIG_IMAGE_FORMAT_LEGACY) 811*2e192b24SSimon Glass image_header_t *hdr; 812*2e192b24SSimon Glass #endif 813*2e192b24SSimon Glass #if defined(CONFIG_FIT) 814*2e192b24SSimon Glass const void *fit_hdr = NULL; 815*2e192b24SSimon Glass #endif 816*2e192b24SSimon Glass 817*2e192b24SSimon Glass s = strchr(cmd, '.'); 818*2e192b24SSimon Glass if (s != NULL && 819*2e192b24SSimon Glass (strcmp(s, ".jffs2") && strcmp(s, ".e") && strcmp(s, ".i"))) { 820*2e192b24SSimon Glass printf("Unknown nand load suffix '%s'\n", s); 821*2e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX); 822*2e192b24SSimon Glass return 1; 823*2e192b24SSimon Glass } 824*2e192b24SSimon Glass 825*2e192b24SSimon Glass printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset); 826*2e192b24SSimon Glass 827*2e192b24SSimon Glass cnt = nand->writesize; 828*2e192b24SSimon Glass r = nand_read_skip_bad(nand, offset, &cnt, NULL, nand->size, 829*2e192b24SSimon Glass (u_char *)addr); 830*2e192b24SSimon Glass if (r) { 831*2e192b24SSimon Glass puts("** Read error\n"); 832*2e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_HDR_READ); 833*2e192b24SSimon Glass return 1; 834*2e192b24SSimon Glass } 835*2e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_HDR_READ); 836*2e192b24SSimon Glass 837*2e192b24SSimon Glass switch (genimg_get_format ((void *)addr)) { 838*2e192b24SSimon Glass #if defined(CONFIG_IMAGE_FORMAT_LEGACY) 839*2e192b24SSimon Glass case IMAGE_FORMAT_LEGACY: 840*2e192b24SSimon Glass hdr = (image_header_t *)addr; 841*2e192b24SSimon Glass 842*2e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); 843*2e192b24SSimon Glass image_print_contents (hdr); 844*2e192b24SSimon Glass 845*2e192b24SSimon Glass cnt = image_get_image_size (hdr); 846*2e192b24SSimon Glass break; 847*2e192b24SSimon Glass #endif 848*2e192b24SSimon Glass #if defined(CONFIG_FIT) 849*2e192b24SSimon Glass case IMAGE_FORMAT_FIT: 850*2e192b24SSimon Glass fit_hdr = (const void *)addr; 851*2e192b24SSimon Glass puts ("Fit image detected...\n"); 852*2e192b24SSimon Glass 853*2e192b24SSimon Glass cnt = fit_get_size (fit_hdr); 854*2e192b24SSimon Glass break; 855*2e192b24SSimon Glass #endif 856*2e192b24SSimon Glass default: 857*2e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_TYPE); 858*2e192b24SSimon Glass puts ("** Unknown image type\n"); 859*2e192b24SSimon Glass return 1; 860*2e192b24SSimon Glass } 861*2e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); 862*2e192b24SSimon Glass 863*2e192b24SSimon Glass r = nand_read_skip_bad(nand, offset, &cnt, NULL, nand->size, 864*2e192b24SSimon Glass (u_char *)addr); 865*2e192b24SSimon Glass if (r) { 866*2e192b24SSimon Glass puts("** Read error\n"); 867*2e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_READ); 868*2e192b24SSimon Glass return 1; 869*2e192b24SSimon Glass } 870*2e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_READ); 871*2e192b24SSimon Glass 872*2e192b24SSimon Glass #if defined(CONFIG_FIT) 873*2e192b24SSimon Glass /* This cannot be done earlier, we need complete FIT image in RAM first */ 874*2e192b24SSimon Glass if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) { 875*2e192b24SSimon Glass if (!fit_check_format (fit_hdr)) { 876*2e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_FIT_READ); 877*2e192b24SSimon Glass puts ("** Bad FIT image format\n"); 878*2e192b24SSimon Glass return 1; 879*2e192b24SSimon Glass } 880*2e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_FIT_READ_OK); 881*2e192b24SSimon Glass fit_print_contents (fit_hdr); 882*2e192b24SSimon Glass } 883*2e192b24SSimon Glass #endif 884*2e192b24SSimon Glass 885*2e192b24SSimon Glass /* Loading ok, update default load address */ 886*2e192b24SSimon Glass 887*2e192b24SSimon Glass load_addr = addr; 888*2e192b24SSimon Glass 889*2e192b24SSimon Glass return bootm_maybe_autostart(cmdtp, cmd); 890*2e192b24SSimon Glass } 891*2e192b24SSimon Glass 892*2e192b24SSimon Glass static int do_nandboot(cmd_tbl_t *cmdtp, int flag, int argc, 893*2e192b24SSimon Glass char * const argv[]) 894*2e192b24SSimon Glass { 895*2e192b24SSimon Glass char *boot_device = NULL; 896*2e192b24SSimon Glass int idx; 897*2e192b24SSimon Glass ulong addr, offset = 0; 898*2e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS) 899*2e192b24SSimon Glass struct mtd_device *dev; 900*2e192b24SSimon Glass struct part_info *part; 901*2e192b24SSimon Glass u8 pnum; 902*2e192b24SSimon Glass 903*2e192b24SSimon Glass if (argc >= 2) { 904*2e192b24SSimon Glass char *p = (argc == 2) ? argv[1] : argv[2]; 905*2e192b24SSimon Glass if (!(str2long(p, &addr)) && (mtdparts_init() == 0) && 906*2e192b24SSimon Glass (find_dev_and_part(p, &dev, &pnum, &part) == 0)) { 907*2e192b24SSimon Glass if (dev->id->type != MTD_DEV_TYPE_NAND) { 908*2e192b24SSimon Glass puts("Not a NAND device\n"); 909*2e192b24SSimon Glass return 1; 910*2e192b24SSimon Glass } 911*2e192b24SSimon Glass if (argc > 3) 912*2e192b24SSimon Glass goto usage; 913*2e192b24SSimon Glass if (argc == 3) 914*2e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 915*2e192b24SSimon Glass else 916*2e192b24SSimon Glass addr = CONFIG_SYS_LOAD_ADDR; 917*2e192b24SSimon Glass return nand_load_image(cmdtp, &nand_info[dev->id->num], 918*2e192b24SSimon Glass part->offset, addr, argv[0]); 919*2e192b24SSimon Glass } 920*2e192b24SSimon Glass } 921*2e192b24SSimon Glass #endif 922*2e192b24SSimon Glass 923*2e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_PART); 924*2e192b24SSimon Glass switch (argc) { 925*2e192b24SSimon Glass case 1: 926*2e192b24SSimon Glass addr = CONFIG_SYS_LOAD_ADDR; 927*2e192b24SSimon Glass boot_device = getenv("bootdevice"); 928*2e192b24SSimon Glass break; 929*2e192b24SSimon Glass case 2: 930*2e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 931*2e192b24SSimon Glass boot_device = getenv("bootdevice"); 932*2e192b24SSimon Glass break; 933*2e192b24SSimon Glass case 3: 934*2e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 935*2e192b24SSimon Glass boot_device = argv[2]; 936*2e192b24SSimon Glass break; 937*2e192b24SSimon Glass case 4: 938*2e192b24SSimon Glass addr = simple_strtoul(argv[1], NULL, 16); 939*2e192b24SSimon Glass boot_device = argv[2]; 940*2e192b24SSimon Glass offset = simple_strtoul(argv[3], NULL, 16); 941*2e192b24SSimon Glass break; 942*2e192b24SSimon Glass default: 943*2e192b24SSimon Glass #if defined(CONFIG_CMD_MTDPARTS) 944*2e192b24SSimon Glass usage: 945*2e192b24SSimon Glass #endif 946*2e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX); 947*2e192b24SSimon Glass return CMD_RET_USAGE; 948*2e192b24SSimon Glass } 949*2e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_SUFFIX); 950*2e192b24SSimon Glass 951*2e192b24SSimon Glass if (!boot_device) { 952*2e192b24SSimon Glass puts("\n** No boot device **\n"); 953*2e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_BOOT_DEVICE); 954*2e192b24SSimon Glass return 1; 955*2e192b24SSimon Glass } 956*2e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_BOOT_DEVICE); 957*2e192b24SSimon Glass 958*2e192b24SSimon Glass idx = simple_strtoul(boot_device, NULL, 16); 959*2e192b24SSimon Glass 960*2e192b24SSimon Glass if (idx < 0 || idx >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[idx].name) { 961*2e192b24SSimon Glass printf("\n** Device %d not available\n", idx); 962*2e192b24SSimon Glass bootstage_error(BOOTSTAGE_ID_NAND_AVAILABLE); 963*2e192b24SSimon Glass return 1; 964*2e192b24SSimon Glass } 965*2e192b24SSimon Glass bootstage_mark(BOOTSTAGE_ID_NAND_AVAILABLE); 966*2e192b24SSimon Glass 967*2e192b24SSimon Glass return nand_load_image(cmdtp, &nand_info[idx], offset, addr, argv[0]); 968*2e192b24SSimon Glass } 969*2e192b24SSimon Glass 970*2e192b24SSimon Glass U_BOOT_CMD(nboot, 4, 1, do_nandboot, 971*2e192b24SSimon Glass "boot from NAND device", 972*2e192b24SSimon Glass "[partition] | [[[loadAddr] dev] offset]" 973*2e192b24SSimon Glass ); 974