int readenv(size_t offset, u_char * buf) { size_t end = offset + CONFIG_ENV_RANGE; size_t amount_loaded = 0; size_t blocksize, len; u_char *char_ptr; blocksize = nand_info[0].erasesize; if (!blocksize) return 1; len = min(blocksize, CONFIG_ENV_SIZE); while (amount_loaded < CONFIG_ENV_SIZE && offset < end) { if (nand_block_isbad(&nand_info[0], offset)) { offset += blocksize; } else { char_ptr = &buf[amount_loaded]; if (nand_read_skip_bad(&nand_info[0], offset, &len, char_ptr)) return 1; offset += blocksize; amount_loaded += len; } } if (amount_loaded != CONFIG_ENV_SIZE) return 1; return 0; }
/* * The legacy NAND code saved the environment in the first NAND device i.e., * nand_dev_desc + 0. This is also the behaviour using the new NAND code. */ static int writeenv(size_t offset, u_char *buf) { size_t end = offset + CONFIG_ENV_RANGE; size_t amount_saved = 0; size_t blocksize, len; struct mtd_info *mtd; u_char *char_ptr; mtd = get_nand_dev_by_index(0); if (!mtd) return 1; blocksize = mtd->erasesize; len = min(blocksize, (size_t)CONFIG_ENV_SIZE); while (amount_saved < CONFIG_ENV_SIZE && offset < end) { if (nand_block_isbad(mtd, offset)) { offset += blocksize; } else { char_ptr = &buf[amount_saved]; if (nand_write(mtd, offset, &len, char_ptr)) return 1; offset += blocksize; amount_saved += len; } } if (amount_saved != CONFIG_ENV_SIZE) return 1; return 0; }
static unsigned long long logic_to_phylength ( nand_info_t *nand, unsigned long long phyaddress, /* should be alignment with block size */ unsigned long long logiclength /* switch logic length, should be alignment with block size */ ) { unsigned long long len_incl_bad = 0; unsigned long long len_excl_bad = 0; while (len_excl_bad < logiclength) { if (!nand_block_isbad(nand, phyaddress)) len_excl_bad += nand->erasesize; len_incl_bad += nand->erasesize; phyaddress += nand->erasesize; if (phyaddress >= nand->size) { printf("operation out of nand flash range.\n"); break; } } return len_incl_bad; }
/* * The legacy NAND code saved the environment in the first NAND device i.e., * nand_dev_desc + 0. This is also the behaviour using the new NAND code. */ int writeenv(size_t offset, u_char *buf) { uint64_t end = offset + CONFIG_ENV_RANGE; uint64_t amount_saved = 0; uint64_t blocksize, len; u_char *char_ptr; blocksize = nand_info[0].erasesize; len = min(blocksize, CONFIG_ENV_SIZE); offset = CONFIG_ENV_OFFSET; while (amount_saved < CONFIG_ENV_SIZE && offset < end) { if (nand_block_isbad(&nand_info[0], offset)) { offset += blocksize; } else { char_ptr = &buf[amount_saved]; if (nand_write(&nand_info[0], offset, (size_t *)&len, char_ptr)) return 1; offset += blocksize; amount_saved += len; } } if (amount_saved != CONFIG_ENV_SIZE) return 1; return 0; }
int nand_get_env_offs(void) { size_t offset = 0; #if defined(CONFIG_SKIP_BAD_BLOCK) int i = 0; int sum = 0; size_t blocksize; blocksize = nand_info[0].erasesize; /* Find U-Boot start */ while(i * blocksize < nand_info[0].size) { if (!nand_block_isbad(&nand_info[0], (i * blocksize))) sum += blocksize; else { sum = 0; offset = (i + 1) * blocksize; } i++; if (sum >= CONFIG_UBOOT_SIZE) break; } offset += CONFIG_UBOOT_SIZE; /* Find Env start */ sum = 0; while(i * blocksize < nand_info[0].size) { if (!nand_block_isbad(&nand_info[0], (i * blocksize))) sum += blocksize; else { sum = 0; offset = (i + 1) * blocksize; } i++; if (sum >= CONFIG_ENV_RANGE) break; } #else offset = CONFIG_UBOOT_SIZE; #endif return offset; }
static int do_imls_nand(void) { struct mtd_info *mtd; int nand_dev = nand_curr_device; size_t len; loff_t off; u32 buffer[16]; if (nand_dev < 0 || nand_dev >= CONFIG_SYS_MAX_NAND_DEVICE) { puts("\nNo NAND devices available\n"); return -ENODEV; } printf("\n"); for (nand_dev = 0; nand_dev < CONFIG_SYS_MAX_NAND_DEVICE; nand_dev++) { mtd = nand_info[nand_dev]; if (!mtd->name || !mtd->size) continue; for (off = 0; off < mtd->size; off += mtd->erasesize) { const image_header_t *header; int ret; if (nand_block_isbad(mtd, off)) continue; len = sizeof(buffer); ret = nand_read(mtd, off, &len, (u8 *)buffer); if (ret < 0 && ret != -EUCLEAN) { printf("NAND read error %d at offset %08llX\n", ret, off); continue; } switch (genimg_get_format(buffer)) { #if defined(CONFIG_IMAGE_FORMAT_LEGACY) case IMAGE_FORMAT_LEGACY: header = (const image_header_t *)buffer; len = image_get_image_size(header); nand_imls_legacyimage(mtd, nand_dev, off, len); break; #endif #if defined(CONFIG_FIT) case IMAGE_FORMAT_FIT: len = fit_get_size(buffer); nand_imls_fitimage(mtd, nand_dev, off, len); break; #endif } } } return 0; }
static unsigned long warp_nand_skipbad(nand_info_t * nand, unsigned long off, unsigned long size) { unsigned long end = off + size; while (off < end) { if (!nand_block_isbad(nand, off)) return off; off += nand->erasesize; } return (unsigned long)-1; }
/******************************************************************************* * mvNandBadBlockTest - Prints all bad blocks on NAND memory * * DESCRIPTION: * This routine checks all blocks against bad block table and prints the * block if it is marked as bad. * * INPUT: * None. * * OUTPUT: * None. * * RETURN: * Returns 1 on failure, else 0 *******************************************************************************/ int mvNandBadBlockTest(void) { int testFlag; unsigned int off; testFlag = DIAG_PASS; for (off=0; off < nand_info[0].size; off += nand_info[0].erasesize) { if (nand_block_isbad(&nand_info[0], off)) { printf("\tBad Block: %08x\n", off); } } printf("\tNAND bad-block detection test "); printf((testFlag==DIAG_PASS)?"PASSED\n":"FAIL\n"); return testFlag; }
static int warp_nand_bferase(int saveno) { int ret; unsigned long off, end; nand_info_t *nand = &nand_info[0]; off = warp_savearea[saveno].bootflag_area; end = off + warp_savearea[saveno].bootflag_size; printf("Warp!! bootflag clear NAND: 0x%08lx-0x%08lx\n", off, end - 1); while (off < end) { if (!nand_block_isbad(nand, off)) { if ((ret = nand_erase(nand, off, nand->erasesize)) != 0) { printf("bootflag erase error %d\n", ret); return ret; } } off += nand->erasesize; } return 0; }
static int check_nand_bad_block(nand_info_t * nand) { unsigned int off; unsigned int bad_blk_cnt = 0; for (off = 0; off < nand->size; off += nand->erasesize) { if (nand_block_isbad(nand, off)) { printf(" %08lx\n", (long unsigned int)off); bad_blk_cnt++; if (off <= NAND_PROGMABLE_AREA) { printf("ERROR: NAND bad block exists in programmable area (offset %08lx)\n", (long unsigned int)off); return -1; } } if (bad_blk_cnt >= MAX_BAD_BLK_LIMIT) { printf("ERROR: Bad NAND Block Count exceeds MAX_BAD_BLK_LIMIT (%d)\n", MAX_BAD_BLK_LIMIT); return -1; } } printf("Bad NAND Block Count %d\n", bad_blk_cnt); return 0; }
int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { int i, dev, ret = 0; ulong addr, off; size_t size; char *cmd, *s; nand_info_t *nand; #ifdef CONFIG_SYS_NAND_QUIET int quiet = CONFIG_SYS_NAND_QUIET; #else int quiet = 0; #endif const char *quiet_str = getenv("quiet"); /* at least two arguments please */ if (argc < 2) goto usage; if (quiet_str) quiet = simple_strtoul(quiet_str, NULL, 0) != 0; cmd = argv[1]; if (strcmp(cmd, "info") == 0) { putc('\n'); for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { if (nand_info[i].name) nand_print_info(i); } return 0; } if (strcmp(cmd, "device") == 0) { if (argc < 3) { putc('\n'); if ((nand_curr_device < 0) || (nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE)) puts("no devices available\n"); else nand_print_info(nand_curr_device); return 0; } dev = (int)simple_strtoul(argv[2], NULL, 10); if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev].name) { puts("No such device\n"); return 1; } printf("Device %d: %s", dev, nand_info[dev].name); puts("... is now current device\n"); nand_curr_device = dev; #ifdef CONFIG_SYS_NAND_SELECT_DEVICE /* * Select the chip in the board/cpu specific driver */ board_nand_select_device(nand_info[dev].priv, dev); #endif return 0; } if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 && strncmp(cmd, "dump", 4) != 0 && strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && strcmp(cmd, "biterr") != 0 && strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 #ifdef CONFIG_ENV_OFFSET_OOB && strcmp(cmd, "env.oob") != 0 #endif ) goto usage; #ifdef CONFIG_ENV_OFFSET_OOB /* this command operates only on the first nand device */ if (strcmp(cmd, "env.oob") == 0) { return do_nand_env_oob(cmdtp, &nand_info[0], argc - 1, argv + 1); } #endif /* the following commands operate on the current device */ if (nand_curr_device < 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[nand_curr_device].name) { puts("\nno devices available\n"); return 1; } nand = &nand_info[nand_curr_device]; if (strcmp(cmd, "bad") == 0) { printf("\nDevice %d bad blocks:\n", nand_curr_device); for (off = 0; off < nand->size; off += nand->erasesize) if (nand_block_isbad(nand, off)) printf(" %08lx\n", off); return 0; } /* * Syntax is: * 0 1 2 3 4 * nand erase [clean] [off size] */ if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) { nand_erase_options_t opts; /* "clean" at index 2 means request to write cleanmarker */ int clean = argc > 2 && !strcmp("clean", argv[2]); int o = clean ? 3 : 2; int scrub = !strcmp(cmd, "scrub"); printf("\nNAND %s: ", scrub ? "scrub" : "erase"); /* skip first two or three arguments, look for offset and size */ if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0) return 1; memset(&opts, 0, sizeof(opts)); opts.offset = off; opts.length = size; opts.jffs2 = clean; opts.quiet = quiet; if (scrub) { puts("Warning: " "scrub option will erase all factory set " "bad blocks!\n" " " "There is no reliable way to recover them.\n" " " "Use this command only for testing purposes " "if you\n" " " "are sure of what you are doing!\n" "\nReally scrub this NAND flash? <y/N>\n"); if (getc() == 'y') { puts("y"); if (getc() == '\r') opts.scrub = 1; else { puts("scrub aborted\n"); return -1; } } else { puts("scrub aborted\n"); return -1; } } ret = nand_erase_opts(nand, &opts); printf("%s\n", ret ? "ERROR" : "OK"); return ret == 0 ? 0 : 1; } if (strncmp(cmd, "dump", 4) == 0) { if (argc < 3) goto usage; s = strchr(cmd, '.'); off = (int)simple_strtoul(argv[2], NULL, 16); if (s != NULL && strcmp(s, ".oob") == 0) ret = nand_dump(nand, off, 1); else ret = nand_dump(nand, off, 0); return ret == 0 ? 1 : 0; } if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { int read; if (argc < 4) goto usage; addr = (ulong)simple_strtoul(argv[2], NULL, 16); read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ printf("\nNAND %s: ", read ? "read" : "write"); if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) return 1; s = strchr(cmd, '.'); if (!s || !strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i")) { if (read) ret = nand_read_skip_bad(nand, off, &size, (u_char *)addr); else ret = nand_write_skip_bad(nand, off, &size, (u_char *)addr); } else if (!strcmp(s, ".oob")) { /* out-of-band data */ mtd_oob_ops_t ops = { .oobbuf = (u8 *)addr, .ooblen = size, .mode = MTD_OOB_RAW }; if (read) ret = nand->read_oob(nand, off, &ops); else ret = nand->write_oob(nand, off, &ops); } else {
/* * The legacy NAND code saved the environment in the first NAND device i.e., * nand_dev_desc + 0. This is also the behaviour using the new NAND code. */ #ifdef CFG_ENV_OFFSET_REDUND int saveenv(void) { ulong total; int ret = 0; env_ptr->flags++; total = CFG_ENV_SIZE; if(gd->env_valid == 1) { puts ("Erasing redundant Nand..."); if (nand_erase(&nand_info[0], CFG_ENV_OFFSET_REDUND, CFG_ENV_SIZE)) return 1; puts ("Writing to redundant Nand... "); ret = nand_write(&nand_info[0], CFG_ENV_OFFSET_REDUND, &total, (u_char*) env_ptr); } else { puts ("Erasing Nand..."); if (nand_erase(&nand_info[0], CFG_ENV_OFFSET, CFG_ENV_SIZE)) return 1; puts ("Writing to Nand... "); ret = nand_write(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*) env_ptr); } if (ret || total != CFG_ENV_SIZE) return 1; puts ("done\n"); gd->env_valid = (gd->env_valid == 2 ? 1 : 2); return ret; } #else /* ! CFG_ENV_OFFSET_REDUND */ int saveenv(void) //YWDRIVER_MODI lwj :这里需要注意,还需要加一些代码,否则如果要保存环境变量的地方是坏块的话,就会出问题的。 { //YWDRIVER_MODI 2010/3/5 d48zm modify #if 0 int ret = 0; int blockstart = -1; ulong erasesize_blockalign = nand_info[0].erasesize; u_char* data_ptr = (u_char*)env_ptr; ssize_t offset = CFG_ENV_OFFSET; ssize_t envlen = CFG_ENV_SIZE; ulong writelen = erasesize_blockalign; ulong checklen = erasesize_blockalign; ssize_t boundbegin = CFG_ENV_OFFSET + YW_CFG_NAND_ENV_BOUND - erasesize_blockalign; while ((envlen > 0) && (offset <= boundbegin)){ if (envlen < erasesize_blockalign){ checklen = envlen; writelen = envlen; } /* * new eraseblock, check for bad block(s). Stay in the * loop to be sure if the offset changes because of * a bad block, that the next block that will be * written to is also checked. Thus avoiding errors if * the block(s) after the skipped block(s) is also bad * (number of blocks depending on the blockalign */ while (blockstart != (offset & (~erasesize_blockalign+1))) { blockstart = offset & (~erasesize_blockalign+1); int ret = nand_block_isbad(&nand_info[0], offset); if (ret < 0) { printf("Bad block check failed\n"); return 1; } if (ret == 1) { offset = blockstart + erasesize_blockalign; printf("\rBad block at 0x%lx " "in erase block from " "0x%x will be skipped\n", (long) offset, blockstart); } } printf ("Erasing Nand block at 0x%lx...", offset); if (nand_erase(&nand_info[0], offset, erasesize_blockalign)) return 1; puts ("Writing to Nand block... "); ret = nand_write(&nand_info[0], offset, &writelen, data_ptr); if (ret || writelen != checklen) return 1; envlen -= writelen; data_ptr += writelen; offset += writelen; } #else ulong total; int ret = 0; puts ("Erasing Nand..."); if (nand_erase(&nand_info[0], CFG_ENV_OFFSET, CFG_ENV_SIZE)) return 1; puts ("Writing to Nand... "); total = CFG_ENV_SIZE; ret = nand_write(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr); if (ret || total != CFG_ENV_SIZE) return 1; #endif //YWDRIVER_MODI end puts ("done\n"); return ret; }
int nand_logic_read ( nand_logic_t *nand_logic, unsigned long long offset, /* should be alignment with nand page size */ unsigned int length, /* should be alignment with nand page size */ unsigned char *buf, int withoob ) { unsigned long long phylength; unsigned long long phyaddress; nand_info_t *nand = nand_logic->nand; /* Reject read, which are not page aligned */ if ((offset & (nand->writesize - 1)) || (length & (nand->writesize - 1))) { printf("Attempt to read non page aligned data, " "nand page size: 0x%08x, offset: 0x%08llx, length: 0x%08x\n", nand->writesize, offset, length); return -1; } phylength = logic_to_phylength(nand, nand_logic->address, (offset + length + nand->erasesize - 1) & (~(nand_logic->erasesize - 1))); if ((offset > nand_logic->length) || (length > nand_logic->length) || (phylength > nand_logic->length)) { printf("Attempt to read outside the flash handle area, " "flash handle size: 0x%08llx, offset: 0x%08llx, " "length: 0x%08x, phylength: 0x%08llx\n", nand_logic->length, offset, length, phylength); return -1; } phylength = logic_to_phylength(nand, nand_logic->address, (offset + nand->erasesize - 1) & (~(nand_logic->erasesize - 1))); if(offset & (nand_logic->erasesize - 1)) phyaddress = phylength - nand->erasesize + (offset & (nand_logic->erasesize - 1)) + nand_logic->address; else phyaddress = phylength + nand_logic->address; if (withoob) { unsigned long long block_offset; unsigned long long read_length; while (length > 0) { block_offset = phyaddress & (nand->erasesize - 1); WATCHDOG_RESET (); if (nand_block_isbad (nand, phyaddress & ~(nand_logic->erasesize - 1))) { printf("Skipping bad block 0x%08llx\n", phyaddress & ~(nand_logic->erasesize - 1)); phyaddress += nand->erasesize - block_offset; continue; } if (length < (nand->erasesize - block_offset)) read_length = length; else read_length = nand->erasesize - block_offset; while (read_length > 0) { int ret; struct mtd_oob_ops ops; memset(&ops, 0, sizeof(ops)); ops.datbuf = buf; ops.oobbuf = buf + nand->writesize; ops.len = nand->writesize; ops.ooblen = nand->oobsize; ops.mode = MTD_OOB_RAW; if ((ret = nand->read_oob(nand, phyaddress, &ops)) < 0) { printf("Error (%d) reading page 0x%08llx\n", ret, phyaddress); return -1; } phyaddress += nand->writesize; read_length -= nand->writesize; length -= nand->writesize; buf += nand->writesize + nand->oobsize; } } return 0; } else { return nand_read_skip_bad(nand, phyaddress, &length, buf); } }
static int write_to_ptn(struct fastboot_ptentry *ptn) { int ret = 1; char start[32], length[32]; char wstart[32], wlength[32], addr[32]; char ecc_type[32], write_type[32]; int repeat, repeat_max; char *lock[5] = { "nand", "lock", NULL, NULL, NULL, }; char *unlock[5] = { "nand", "unlock", NULL, NULL, NULL, }; char *write[6] = { "nand", "write", NULL, NULL, NULL, NULL, }; char *ecc[4] = { "nand", "ecc", NULL, NULL, }; char *erase[5] = { "nand", "erase", NULL, NULL, NULL, }; lock[2] = unlock[2] = erase[2] = start; lock[3] = unlock[3] = erase[3] = length; write[1] = write_type; write[2] = addr; write[3] = wstart; write[4] = wlength; printf("flashing '%s'\n", ptn->name); /* Which flavor of write to use */ if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_I) sprintf(write_type, "write.i"); #ifdef CFG_NAND_YAFFS_WRITE else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_YAFFS) sprintf(write_type, "write.yaffs"); #endif else sprintf(write_type, "write"); /* Some flashing requires the nand's ecc to be set */ ecc[2] = ecc_type; if ((ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC) && (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC)) { /* Both can not be true */ printf("Warning can not do hw and sw ecc for partition '%s'\n", ptn->name); printf("Ignoring these flags\n"); } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC) { sprintf(ecc_type, "hw"); #if 0 do_nand(NULL, 0, 3, ecc); #endif } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC) { sprintf(ecc_type, "sw"); #if 0 do_nand(NULL, 0, 3, ecc); #endif } /* Some flashing requires writing the same data in multiple, consecutive flash partitions */ repeat_max = 1; if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK) { if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_CONTIGUOUS_BLOCK) { printf("Warning can not do both 'contiguous block' and 'repeat' writes for for partition '%s'\n", ptn->name); printf("Ignoring repeat flag\n"); } else { repeat_max = ptn->flags & FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK; } } /* Unlock the whole partition instead of trying to manage special cases */ sprintf(length, "0x%x", ptn->length * repeat_max); for (repeat = 0; repeat < repeat_max; repeat++) { sprintf(start, "0x%x", ptn->start + (repeat * ptn->length)); #if 0 do_nand(NULL, 0, 4, unlock); do_nand(NULL, 0, 4, erase); #endif if ((ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_NEXT_GOOD_BLOCK) && (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_CONTIGUOUS_BLOCK)) { /* Both can not be true */ printf("Warning can not do 'next good block' and 'contiguous block' for partition '%s'\n", ptn->name); printf("Ignoring these flags\n"); } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_NEXT_GOOD_BLOCK) { /* Keep writing until you get a good block transfer_buffer should already be aligned */ if (interface.nand_block_size) { unsigned int blocks = download_bytes / interface.nand_block_size; unsigned int i = 0; unsigned int offset = 0; sprintf(wlength, "0x%x", interface.nand_block_size); while (i < blocks) { /* Check for overflow */ if (offset >= ptn->length) break; /* download's address only advance if last write was successful */ sprintf(addr, "0x%x", interface.transfer_buffer + (i * interface.nand_block_size)); /* nand's address always advances */ sprintf(wstart, "0x%x", ptn->start + (repeat * ptn->length) + offset); #if 0 ret = do_nand(NULL, 0, 5, write); #endif if (ret) break; else i++; /* Go to next nand block */ offset += interface.nand_block_size; } } else { printf("Warning nand block size can not be 0 when using 'next good block' for partition '%s'\n", ptn->name); printf("Ignoring write request\n"); } } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_CONTIGUOUS_BLOCK) { #if 0 /* Keep writing until you get a good block transfer_buffer should already be aligned */ if (interface.nand_block_size) { if (0 == nand_curr_device) { nand_info_t *nand; unsigned long off; unsigned int ok_start; nand = &nand_info[nand_curr_device]; printf("\nDevice %d bad blocks:\n", nand_curr_device); /* Initialize the ok_start to the start of the partition Then try to find a block large enough for the download */ ok_start = ptn->start; /* It is assumed that the start and length are multiples of block size */ for (off = ptn->start; off < ptn->start + ptn->length; off += nand->erasesize) { if (nand_block_isbad(nand, off)) { /* Reset the ok_start to the next block */ ok_start = off + nand->erasesize; } /* Check if we have enough blocks */ if ((ok_start - off) >= download_bytes) break; } /* Check if there is enough space */ if (ok_start + download_bytes <= ptn->start + ptn->length) { sprintf(addr, "0x%x", interface.transfer_buffer); sprintf(wstart, "0x%x", ok_start); sprintf(wlength, "0x%x", download_bytes); ret = do_nand(NULL, 0, 5, write); /* Save the results into an environment variable on the format ptn_name + 'offset' ptn_name + 'size' */ if (ret) { /* failed */ save_block_values(ptn, 0, 0); } else { /* success */ save_block_values(ptn, ok_start, download_bytes); } } else { printf("Error could not find enough contiguous space in partition '%s' \n", ptn->name); printf("Ignoring write request\n"); } } else { /* TBD : Generalize flash handling */ printf("Error only handling 1 NAND per board"); printf("Ignoring write request\n"); } } else { printf("Warning nand block size can not be 0 when using 'continuous block' for partition '%s'\n", ptn->name); printf("Ignoring write request\n"); } #endif } else { /* Normal case */ sprintf(addr, "0x%x", interface.transfer_buffer); sprintf(wstart, "0x%x", ptn->start + (repeat * ptn->length)); sprintf(wlength, "0x%x", download_bytes); #ifdef CFG_NAND_YAFFS_WRITE if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_YAFFS) sprintf(wlength, "0x%x", download_bytes_unpadded); #endif #if 0 ret = do_nand(NULL, 0, 5, write); #endif if (0 == repeat) { if (ret) /* failed */ save_block_values(ptn, 0, 0); else /* success */ save_block_values(ptn, ptn->start, download_bytes); } } #if 0 do_nand(NULL, 0, 4, lock); #endif if (ret) break; } return ret; }
int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) { int i, dev, ret = 0; uint32_t addr, cmp_addr; uint64_t off; uint64_t size; char *cmd, *s; nand_info_t *nand; #ifdef CONFIG_SYS_NAND_QUIET int quiet = CONFIG_SYS_NAND_QUIET; #else int quiet = 0; #endif const char *quiet_str = getenv("quiet"); /* at least two arguments please */ if (argc < 2) goto usage; if (quiet_str) quiet = simple_strtoul(quiet_str, NULL, 0) != 0; cmd = argv[1]; if (strcmp(cmd, "info") == 0) { for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { if (nand_info[i].name) nand_print_info(i); } return 0; } if (strcmp(cmd, "device") == 0) { if (argc < 3) { putc('\n'); if ((nand_curr_device < 0) || (nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE)) puts("no devices available\n"); else nand_print_info(nand_curr_device); return 0; } dev = (int)simple_strtoul(argv[2], NULL, 10); if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev].name) { puts("No such device\n"); return 1; } printf("Device %d: %s", dev, nand_info[dev].name); puts("... is now current device\n"); nand_curr_device = dev; #ifdef CONFIG_SYS_NAND_SELECT_DEVICE /* * Select the chip in the board/cpu specific driver */ board_nand_select_device(nand_info[dev].priv, dev); #endif return 0; } if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 && strncmp(cmd, "dump", 4) != 0 && strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && strcmp(cmd, "biterr") != 0 && strcmp(cmd, "pattern") != 0 && strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 ) goto usage; /* the following commands operate on the current device */ if (nand_curr_device < 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[nand_curr_device].name) { puts("\nno devices available\n"); return 1; } nand = &nand_info[nand_curr_device]; if (strcmp(cmd, "bad") == 0) { printf("\nDevice %d bad blocks:\n", nand_curr_device); for (off = 0; off < nand->size; off += nand->erasesize) if (nand_block_isbad(nand, off)) printf(" %12llx\n", off); return 0; } /* * Syntax is: * 0 1 2 3 4 * nand erase [clean] [off size] */ if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) { nand_erase_options_t opts; /* "clean" at index 2 means request to write cleanmarker */ int clean = argc > 2 && !strcmp("clean", argv[2]); int o = clean ? 3 : 2; int scrub = !strcmp(cmd, "scrub"); printf("\nNAND %s: ", scrub ? "scrub" : "erase"); /* skip first two or three arguments, look for offset and size */ if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0) return 1; memset(&opts, 0, sizeof(opts)); opts.offset = off; opts.length = size; opts.jffs2 = clean; opts.quiet = quiet; if (scrub) { puts("Warning: " "scrub option will erase all factory set " "bad blocks!\n" " " "There is no reliable way to recover them.\n" " " "Use this command only for testing purposes " "if you\n" " " "are sure of what you are doing!\n" "\nReally scrub this NAND flash? <y/N>\n"); if (getc() == 'y' && getc() == '\r') { opts.scrub = 1; } else { puts("scrub aborted\n"); return -1; } } ret = nand_erase_opts(nand, &opts); printf("%s\n", ret ? "ERROR" : "OK"); return ret == 0 ? 0 : 1; } if (strncmp(cmd, "dump", 4) == 0) { if (argc < 3) goto usage; s = strchr(cmd, '.'); off = simple_strtoull(argv[2], NULL, 16); if (s != NULL && strcmp(s, ".oob") == 0) ret = nand_dump(nand, off, 1); else ret = nand_dump(nand, off, 0); return ret == 0 ? 1 : 0; } if (strncmp(cmd, "pattern", 4) == 0) { int chunk_num; uint64_t offset, chunk64, cmp64; nand_erase_options_t opts; memset(&opts, 0, sizeof(opts)); // nand pattern addr cmp_addr chunk_size // 0 1 2 3 4 if (argc < 5) goto usage; addr = simple_strtoul(argv[2], NULL, 16); cmp_addr = simple_strtoul(argv[3], NULL, 16); chunk64 = simple_strtoull(argv[4], NULL, 16); cmp64 = chunk64; chunk_num = (int) (nand->size / chunk64); printf("NAND size [0x%llx], chunk size [0x%llx], number of chunks [%d]\n", nand->size, chunk64, chunk_num); printf("Erasing all chip...\n"); opts.offset = 0; opts.length = nand->size; opts.jffs2 = 0; opts.quiet = 0; opts.scrub = 0; if (nand_erase_opts(nand, &opts) != 0) { printf("[FAILED]\n"); return 1; } else { printf("[OK]\n"); } offset = 0; while(offset < nand->size) { printf("0x%010llx:", offset); // printf("\tB:"); // if (nand_block_isbad(nand, i*chunk_size & ~(nand->erasesize - 1))) { // printf("[V]\n"); // continue; // } else { // printf("[X]"); // } // size_include_bad = get_len_incl_bad (nand, offset, chunk_size); // opts.offset = offset; // opts.length = size_include_bad; // opts.quiet = 1; // printf("\tE:"); // if (nand_erase_opts(nand, &opts) != 0) { // printf("[X]\n"); // return 1; // } else { // printf("[V]"); // } printf("\tW:"); chunk64 = cmp64; if (nand_write_skip_bad(nand, offset, &chunk64, (u_char *)addr) != 0) { printf("[X]\n"); return 1; } else { printf("[V]"); } printf("\tR:"); chunk64 = cmp64; if (nand_read_skip_bad(nand, offset, &chunk64, (u_char *)cmp_addr) != 0) { printf("[X]\n"); return 1; } else { printf("[V]"); } printf("\tC:"); if (memcmp((void *)addr, (void *)cmp_addr, cmp64) != 0) { printf("[X]\n"); return 1; } else { printf("[V]"); } printf("\n"); if (ctrlc()) { puts ("\nAbort\n"); return (-1); } offset += get_len_incl_bad (nand, offset, cmp64); } printf("NAND Pattern Test PASSED!\n"); return 0; } if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { int read; if (argc < 4) goto usage; addr = simple_strtoul(argv[2], NULL, 16); read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ printf("\nNAND %s: ", read ? "read" : "write"); if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) return 1; s = strchr(cmd, '.'); if (!s || !strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i")) { if (read) ret = nand_read_skip_bad(nand, off, &size, (u_char *)addr); else ret = nand_write_skip_bad(nand, off, &size, (u_char *)addr); } else if (!strcmp(s, ".oob")) { /* out-of-band data */ mtd_oob_ops_t ops = { .oobbuf = (uint8_t *)addr, .ooblen = size, .mode = MTD_OOB_RAW }; if (read) ret = nand->read_oob(nand, off, &ops); else ret = nand->write_oob(nand, off, &ops); } else {
/******************************************************************************* Reset environment variables. ********************************************************************************/ int resetenv_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { #if defined(CONFIG_ENV_IS_IN_FLASH ) ulong stop_addr; ulong start_addr; #endif #if defined(CONFIG_ENV_IS_IN_NAND) size_t offset = 0; nand_info_t *nand = &nand_info[0]; int sum = 0; #if defined(CONFIG_SKIP_BAD_BLOCK) int i = 0; size_t blocksize; blocksize = nand_info[0].erasesize; while(i * blocksize < nand_info[0].size) { if (!nand_block_isbad(&nand_info[0], (i * blocksize))) sum += blocksize; else { sum = 0; offset = (i + 1) * blocksize; } if (sum >= CONFIG_UBOOT_SIZE) break; i++; } #else offset = CONFIG_ENV_OFFSET; #endif printf("Erasing 0x%x - 0x%x:",CONFIG_UBOOT_SIZE + offset, CONFIG_ENV_RANGE_NAND); nand_erase(nand, CONFIG_UBOOT_SIZE + offset, CONFIG_ENV_RANGE_NAND); puts ("[Done]\n"); #elif defined(CONFIG_ENV_IS_IN_SPI_FLASH) u32 sector = 1; if (CONFIG_ENV_SIZE > CONFIG_ENV_SECT_SIZE) { sector = CONFIG_ENV_SIZE / CONFIG_ENV_SECT_SIZE; if (CONFIG_ENV_SIZE % CONFIG_ENV_SECT_SIZE) sector++; } #ifdef CONFIG_SPI_FLASH_PROTECTION printf("Unprotecting flash:"); spi_flash_protect(flash, 0); printf("\t\t[Done]\n"); #endif printf("Erasing 0x%x - 0x%x:",CONFIG_ENV_OFFSET, CONFIG_ENV_OFFSET + sector * CONFIG_ENV_SECT_SIZE); if(!flash) { flash = spi_flash_probe(0, 0, CONFIG_SF_DEFAULT_SPEED, CONFIG_SF_DEFAULT_MODE); if (!flash) { printf("Failed to probe SPI Flash\n"); set_default_env("!spi_flash_probe() failed"); return 0; } } if (spi_flash_erase(flash, CONFIG_ENV_OFFSET, sector * CONFIG_ENV_SECT_SIZE)) return 1; puts("\t[Done]\n"); #ifdef CONFIG_SPI_FLASH_PROTECTION printf("Protecting flash:"); spi_flash_protect(flash, 1); printf("\t\t[Done]\n"); #endif #elif defined(CONFIG_ENV_IS_IN_FLASH ) start_addr = CONFIG_ENV_ADDR; stop_addr = start_addr + CONFIG_ENV_SIZE - 1; printf("Erasing sector 0x%x:",CONFIG_ENV_OFFSET); flash_sect_protect (0, start_addr, stop_addr); flash_sect_erase (start_addr, stop_addr); flash_sect_protect (1, start_addr, stop_addr); printf("\t[Done]\n"); #endif printf("Warning: Default Environment Variables will take effect Only after RESET\n"); return 0; }
/* * The legacy NAND code saved the environment in the first NAND device i.e., * nand_dev_desc + 0. This is also the behaviour using the new NAND code. */ void env_relocate_spec (void) { #if !defined(ENV_IS_EMBEDDED) //YWDRIVER_MODI 2010/3/5 d48zm modify #if 0 int ret; int blockstart = -1; ulong erasesize_blockalign = nand_info[0].erasesize; u_char* data_ptr = (u_char*)env_ptr; ssize_t offset = CFG_ENV_OFFSET; ssize_t envlen = CFG_ENV_SIZE; ulong readlen = erasesize_blockalign; ulong checklen = erasesize_blockalign; ssize_t boundbegin = CFG_ENV_OFFSET + YW_CFG_NAND_ENV_BOUND - erasesize_blockalign; while ((envlen > 0) && (offset <= boundbegin)){ if (envlen < erasesize_blockalign){ checklen = envlen; readlen = envlen; } /* * new eraseblock, check for bad block(s). Stay in the * loop to be sure if the offset changes because of * a bad block, that the next block that will be * written to is also checked. Thus avoiding errors if * the block(s) after the skipped block(s) is also bad * (number of blocks depending on the blockalign */ while (blockstart != (offset & (~erasesize_blockalign+1))) { blockstart = offset & (~erasesize_blockalign+1); ret = nand_block_isbad(&nand_info[0], offset); if (ret < 0) { printf("Bad block check failed\n"); return 1; } if (ret == 1) { offset = blockstart + erasesize_blockalign; printf("\rBad block at 0x%lx " "in erase block from " "0x%x will be skipped\n", (long) offset, blockstart); } } ret = nand_read(&nand_info[0], offset, &readlen, data_ptr); if (ret || readlen != checklen) return use_default(); envlen -= readlen; data_ptr += readlen; offset += readlen; } if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) return use_default(); #else ulong total; int ret; total = CFG_ENV_SIZE; ret = nand_read(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr); if (ret || total != CFG_ENV_SIZE) return use_default(); if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) return use_default(); #endif //YWDRIVER_MODI 2010/3/5 d48zm modify end #endif /* ! ENV_IS_EMBEDDED */ }
int write_fec(char *infile, int dev, loff_t mtdoffset, struct mtd_info_user *meminfo) { int image = -1; int n, retCode = 0; unsigned char *inbuf, *outbuf; int pagesize = meminfo->writesize; int rdsize = pagesize / FEC_ENCODE_SCALING; if (lseek(dev, mtdoffset, SEEK_SET) != mtdoffset) { perror("lseek error"); return -4; } if ((image = open(infile, O_RDONLY)) < 0) { perror("open error"); return -1; } inbuf = (unsigned char *)MALLOC(rdsize); outbuf = (unsigned char *)MALLOC(pagesize); while ( (n = read(image, inbuf, rdsize)) > 0) { unsigned char *ppos = outbuf; int i, j; erase_buffer(outbuf, pagesize); for (i = 0; i < n; i++) { unsigned char plain = inbuf[i]; unsigned char cypher; for (j = 0; j < 8; j++) { if (plain & 1) cypher = 0x55; else cypher = 0xAA; plain >>= 1; *ppos++ = cypher; } } while (nand_block_isbad(dev, mtdoffset)) { mtdoffset += meminfo->erasesize; if (lseek(dev, mtdoffset, SEEK_SET) != mtdoffset) { perror("lseek error"); retCode = -4; goto out; } } /* Should check bad block first */ if (write(dev, outbuf, pagesize) != pagesize) { perror("Write Error"); retCode = -2; goto out; } mtdoffset += pagesize; } if (n < 0) { perror("Read Error"); retCode = -3; } out: close(image); free(inbuf); free(outbuf); return retCode; }
int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) { int i, dev, ret = 0; ulong addr; loff_t off, size; char *cmd, *s; nand_info_t *nand; #ifdef CONFIG_SYS_NAND_QUIET int quiet = CONFIG_SYS_NAND_QUIET; #else int quiet = 0; #endif const char *quiet_str = getenv("quiet"); #if ((defined CONFIG_AML_NAND_KEY) || (defined MX_REVD) || (defined CONFIG_SECURE_NAND)) int chip_num , tmp_chip_num, error; nand = nand_info[nand_curr_device]; struct mtd_info *mtd =nand; struct aml_nand_chip *aml_chip = mtd_to_nand_chip(nand); #endif /* at least two arguments please */ if (argc < 2) goto usage; if (quiet_str) quiet = simple_strtoul(quiet_str, NULL, 0) != 0; cmd = argv[1]; #ifdef CONFIG_AML_NAND_KEY if (strcmp(cmd, "key") == 0){ aml_chip->key_protect = 1; //force nand key can be erased return 0; } #endif #ifdef CONFIG_SECURE_NAND if (strcmp(cmd, "secure") == 0){ aml_chip->secure_protect = 1; //force nand key can be erased return 0; } #endif #ifdef CONFIG_SECURE_NAND if (strcmp(cmd, "secure") == 0){ aml_chip->secure_protect = 1; //force nand key can be erased return 0; } #endif if(strcmp(cmd, "exist") == 0){ if(nand_info[1]){ printf("nand exist return 0\n"); return 0; } else{ printf("nand exist return 1\n"); return 1; } } #ifdef MX_REVD if (strcmp(cmd, "errstat") == 0){ printk("checking chiprev here\n"); if(aml_chip->err_sts == NAND_CHIP_REVB_HY_ERR){ printk("Must use RevD chip for Hynix 26nm/20nm nand boot without SPI!!!\n"); return NAND_CHIP_REVB_HY_ERR; } return 0; } #endif #ifdef CONFIG_SECURE_NAND if (strcmp(cmd, "secure") == 0){ aml_chip->secure_protect = 1; //force nand key can be erased return 0; } #endif if (strcmp(cmd, "info") == 0) { #ifdef CONFIG_AML_NAND_KEY aml_chip->key_protect = 0; //protect nand key can not be erased #endif #ifdef CONFIG_SECURE_NAND aml_chip->secure_protect = 0; //protect nand secure can not be erased #endif putc('\n'); for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { nand = nand_info[i]; if (!nand) { nand_init(); if (!nand) return -1; } if (nand->name) nand_print_info(i); } return 0; } if (strcmp(cmd, "init") == 0) { nand_init(); return 0; } //cmd for nand test , if nand is ok , then trigger power off if (strcmp(cmd, "test") == 0) { int ret=-1; puts("\ntest the nand flash ***\n"); for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { nand = nand_info[i]; if (!nand) { ret=nand_test_init(); printf("\n***nand_test_init()in NAND DEVICE %d returned:%d***\n ", i,ret); if (ret) return -1; } } return 0; } if (strcmp(cmd, "scrub_detect") == 0) { if (nand_curr_device < 0 || nand_curr_device >= (CONFIG_SYS_MAX_NAND_DEVICE+2)) { puts("\nno devices available\n"); return 1; } nand = nand_info[nand_curr_device]; aml_nand_stupid_dectect_badblock(nand); return 0; } if (strcmp(cmd, "device") == 0) { if (argc < 3) { putc('\n'); if ((nand_curr_device < 0) || (nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE)) puts("no devices available\n"); else nand_print_info(nand_curr_device); return 0; } dev = (int)simple_strtoul(argv[2], NULL, 10); if (dev < 0 || dev >= (CONFIG_SYS_MAX_NAND_DEVICE+1) || !nand_info[dev]->name) { puts("No such device\n"); return 1; } printf("Device %d: %s", dev, nand_info[dev]->name); puts("... is now current device\n"); nand_curr_device = dev; #ifdef CONFIG_SYS_NAND_SELECT_DEVICE /* * Select the chip in the board/cpu specific driver */ board_nand_select_device(nand_info[dev]->priv, dev); #endif return 0; } if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 && strncmp(cmd, "dump", 4) != 0 && strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && strcmp(cmd, "biterr") != 0 && strncmp(cmd, "rom_protect", 11) != 0 && strncmp(cmd, "wr_rd_cmp", 9) != 0 && strncmp(cmd, "rom_write", 9) != 0 && (strncmp(cmd, "rom_read", 8) != 0) && strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 && strcmp(cmd, "factory_info") != 0 && strcmp(cmd, "show_para_page")&& strncmp(cmd, "scrub_safe", 10) != 0) //my_ goto usage; /* the following commands operate on the current device */ if (nand_curr_device < 0 || nand_curr_device >= (CONFIG_SYS_MAX_NAND_DEVICE+2)) { puts("\nno devices available\n"); return 1; } nand = nand_info[nand_curr_device]; if (!nand) return -1; if (strcmp(cmd, "bad") == 0) { printf("\nDevice %d bad blocks:\n", nand_curr_device); for (off = 0; off < nand->size; off += nand->erasesize) if (nand_block_isbad(nand, off)) printf(" %09llx\n", off); return 0; } /* * Syntax is: * 0 1 2 3 4 * nand erase [clean] [off size] */ if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0 || strcmp(cmd, "scrub_safe") == 0) { nand_erase_options_t opts; int argc_cnt = 2; //printk("%s\n", argv[2]); /* if (isstring(argv[2])) { nand = get_mtd_device_nm(argv[2]); if (IS_ERR(nand)){ printf("get nand device err\n"); return 1; } argc_cnt++; } */ /* "clean" at index 2 means request to write cleanmarker */ int clean = argc > argc_cnt && !strcmp("clean", argv[argc_cnt]); if (clean) argc_cnt++; int o = argc_cnt; int scrub = !strncmp(cmd, "scrub",10); int scrub_safe = !strncmp(cmd, "scrub_safe",10); if(scrub_safe) printf("\nNAND %s: ", scrub_safe ? "scrub_safe" : "erase"); else printf("\nNAND %s: ", scrub ? "scrub" : "erase"); if (argv[argc_cnt]) { if(!strcmp(argv[argc_cnt], "whole")) { off = 0; size = nand->size; printf("whole chip.\n"); } } else { /* skip first two or three arguments, look for offset and size */ if ((strcmp(cmd, "erase") == 0) && (argc < 3)) { goto usage; } if ((arg_off_size(argc - o, argv + o, nand, &off, &size) != 0)) { return 1; } } memset(&opts, 0, sizeof(opts)); opts.offset = off; opts.length = size; opts.jffs2 = clean; opts.quiet = quiet; if (scrub) { puts("Warning: " "scrub option will erase all factory set " "bad blocks!\n" " " "There is no reliable way to recover them.\n" " " "Use this command only for testing purposes " "if you\n" " " "are sure of what you are doing!\n" "\nReally scrub this NAND flash? <y/N>\n"); if(nand_protect) { if (getc() == 'y') { puts("y"); if (getc() == '\r') opts.scrub = 1; else { puts("scrub aborted\n"); return -1; } } else { puts("scrub aborted\n"); return -1; } } else { opts.scrub = 1; } } else if(scrub_safe){ puts("Warning: " "scrub_safe option will erase all " "bad blocks except factory bad blocks!\n"); opts.scrub = 2; // indicate scrub_safe } ret = nand_erase_opts(nand, &opts); printf("%s\n", ret ? "ERROR" : "OK"); #ifdef CONFIG_AML_NAND_KEY aml_chip->key_protect = 0; //protect nand key can not be erased #endif #ifdef CONFIG_SECURE_NAND aml_chip->secure_protect = 0; //protect nand secure can not be erased #endif return ret == 0 ? 0 : 1; } if (strncmp(cmd, "dump", 4) == 0) { if (argc < 3) goto usage; s = strchr(cmd, '.'); //off = (loff_t)simple_strtoul(argv[2], NULL, 16); if (!(str2longlong(argv[2], (unsigned long long*)(&off)))) return -1; if (s != NULL && strcmp(s, ".oob") == 0) ret = nand_dump(nand, off, 1); else ret = nand_dump(nand, off, 0); return ret == 0 ? 1 : 0; } if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { int read; if (argc < 4) goto usage; if (isstring(argv[2])) { nand = get_mtd_device_nm(argv[2]); if (IS_ERR(nand)) goto usage; addr = (ulong)simple_strtoul(argv[3], NULL, 16); read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ printf("\nNAND %s: %s ", read ? "read" : "write", argv[2]); if (argc == 4) { extern unsigned int get_mtd_size(char *name); off = 0; size = get_mtd_size(argv[2]); } else { if (arg_off_size(argc - 4, argv + 4, nand, &off, &size) != 0) return 1; } } else { addr = (ulong)simple_strtoul(argv[2], NULL, 16); read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ printf("\nNAND %s: ", read ? "read" : "write"); if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) return 1; } #ifdef CONFIG_AMLROM_NANDBOOT if((read==0) && ((off)<(1024* nand->writesize)) && (nand_curr_device == 0)){ printf("offset 0x%llx in aml-boot area ,abort\n", off); return -1; } #endif s = strchr(cmd, '.'); if (!s || !strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i")) { if (read) ret = nand_read_skip_bad(nand, off, &size, (u_char *)addr, 0); else ret = nand_write_skip_bad(nand, off, &size, (u_char *)addr, 0); } else if (!strcmp(s, ".oob")) { /* out-of-band data */ mtd_oob_ops_t ops = { .oobbuf = (u8 *)addr, .ooblen = size, .mode = MTD_OOB_RAW }; if (read) ret = nand->read_oob(nand, off, &ops); else ret = nand->write_oob(nand, off, &ops); } else if (!strcmp(s, ".raw")) {
static int raw_access(nand_info_t *nand, ulong addr, loff_t off, ulong count, int read) { int ret = 0; while (count--) { /* Raw access */ mtd_oob_ops_t ops = { .datbuf = (u8 *)addr, .oobbuf = ((u8 *)addr) + nand->writesize, .len = nand->writesize, .ooblen = nand->oobsize, .mode = MTD_OOB_RAW }; if (read) ret = nand->read_oob(nand, off, &ops); else ret = nand->write_oob(nand, off, &ops); if (ret) { printf("%s: error at offset %llx, ret %d\n", __func__, (long long)off, ret); break; } addr += nand->writesize + nand->oobsize; off += nand->writesize; } return ret; } int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int i, ret = 0; ulong addr; loff_t off, size, maxsize; char *cmd, *s; nand_info_t *nand; #ifdef CONFIG_SYS_NAND_QUIET int quiet = CONFIG_SYS_NAND_QUIET; #else int quiet = 0; #endif const char *quiet_str = getenv("quiet"); int dev = nand_curr_device; int repeat = flag & CMD_FLAG_REPEAT; /* at least two arguments please */ if (argc < 2) goto usage; if (quiet_str) quiet = simple_strtoul(quiet_str, NULL, 0) != 0; cmd = argv[1]; /* Only "dump" is repeatable. */ if (repeat && strcmp(cmd, "dump")) return 0; if (strcmp(cmd, "info") == 0) { putc('\n'); for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { if (nand_info[i].name) nand_print_and_set_info(i); } return 0; } if (strcmp(cmd, "device") == 0) { if (argc < 3) { putc('\n'); if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE) puts("no devices available\n"); else nand_print_and_set_info(dev); return 0; } dev = (int)simple_strtoul(argv[2], NULL, 10); set_dev(dev); return 0; } #ifdef CONFIG_ENV_OFFSET_OOB /* this command operates only on the first nand device */ if (strcmp(cmd, "env.oob") == 0) return do_nand_env_oob(cmdtp, argc - 1, argv + 1); #endif /* The following commands operate on the current device, unless * overridden by a partition specifier. Note that if somehow the * current device is invalid, it will have to be changed to a valid * one before these commands can run, even if a partition specifier * for another device is to be used. */ if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev].name) { puts("\nno devices available\n"); return 1; } nand = &nand_info[dev]; if (strcmp(cmd, "bad") == 0) { printf("\nDevice %d bad blocks:\n", dev); for (off = 0; off < nand->size; off += nand->erasesize) if (nand_block_isbad(nand, off)) printf(" %08llx\n", (unsigned long long)off); return 0; } /* * Syntax is: * 0 1 2 3 4 * nand erase [clean] [off size] */ if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) { nand_erase_options_t opts; /* "clean" at index 2 means request to write cleanmarker */ int clean = argc > 2 && !strcmp("clean", argv[2]); int scrub_yes = argc > 2 && !strcmp("-y", argv[2]); int o = (clean || scrub_yes) ? 3 : 2; int scrub = !strncmp(cmd, "scrub", 5); int spread = 0; int args = 2; const char *scrub_warn = "Warning: " "scrub option will erase all factory set bad blocks!\n" " " "There is no reliable way to recover them.\n" " " "Use this command only for testing purposes if you\n" " " "are sure of what you are doing!\n" "\nReally scrub this NAND flash? <y/N>\n"; if (cmd[5] != 0) { if (!strcmp(&cmd[5], ".spread")) { spread = 1; } else if (!strcmp(&cmd[5], ".part")) { args = 1; } else if (!strcmp(&cmd[5], ".chip")) { args = 0; } else { goto usage; } } /* * Don't allow missing arguments to cause full chip/partition * erases -- easy to do accidentally, e.g. with a misspelled * variable name. */ if (argc != o + args) goto usage; printf("\nNAND %s: ", cmd); /* skip first two or three arguments, look for offset and size */ if (arg_off_size(argc - o, argv + o, &dev, &off, &size, &maxsize) != 0) return 1; nand = &nand_info[dev]; memset(&opts, 0, sizeof(opts)); opts.offset = off; opts.length = size; opts.jffs2 = clean; opts.quiet = quiet; opts.spread = spread; if (scrub) { if (!scrub_yes) puts(scrub_warn); if (scrub_yes) opts.scrub = 1; else if (getc() == 'y') { puts("y"); if (getc() == '\r') opts.scrub = 1; else { puts("scrub aborted\n"); return -1; } } else { puts("scrub aborted\n"); return -1; } } ret = nand_erase_opts(nand, &opts); printf("%s\n", ret ? "ERROR" : "OK"); return ret == 0 ? 0 : 1; } if (strncmp(cmd, "dump", 4) == 0) { if (argc < 3) goto usage; off = (int)simple_strtoul(argv[2], NULL, 16); ret = nand_dump(nand, off, !strcmp(&cmd[4], ".oob"), repeat); return ret == 0 ? 1 : 0; } if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { size_t rwsize; ulong pagecount = 1; int read; int raw; if (argc < 4) goto usage; addr = (ulong)simple_strtoul(argv[2], NULL, 16); read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ printf("\nNAND %s: ", read ? "read" : "write"); nand = &nand_info[dev]; s = strchr(cmd, '.'); if (s && !strcmp(s, ".raw")) { raw = 1; if (arg_off(argv[3], &dev, &off, &size, &maxsize)) return 1; if (argc > 4 && !str2long(argv[4], &pagecount)) { printf("'%s' is not a number\n", argv[4]); return 1; } if (pagecount * nand->writesize > size) { puts("Size exceeds partition or device limit\n"); return -1; } rwsize = pagecount * (nand->writesize + nand->oobsize); } else { if (arg_off_size(argc - 3, argv + 3, &dev, &off, &size, &maxsize) != 0) return 1; rwsize = size; } if (!s || !strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i")) { if (read) ret = nand_read_skip_bad(nand, off, &rwsize, NULL, maxsize, (u_char *)addr); else ret = nand_write_skip_bad(nand, off, &rwsize, NULL, maxsize, (u_char *)addr, 0); #ifdef CONFIG_CMD_NAND_TRIMFFS } else if (!strcmp(s, ".trimffs")) { if (read) { printf("Unknown nand command suffix '%s'\n", s); return 1; } ret = nand_write_skip_bad(nand, off, &rwsize, NULL, maxsize, (u_char *)addr, WITH_DROP_FFS); #endif #ifdef CONFIG_CMD_NAND_YAFFS } else if (!strcmp(s, ".yaffs")) { if (read) { printf("Unknown nand command suffix '%s'.\n", s); return 1; } ret = nand_write_skip_bad(nand, off, &rwsize, NULL, maxsize, (u_char *)addr, WITH_YAFFS_OOB); #endif } else if (!strcmp(s, ".oob")) { /* out-of-band data */ mtd_oob_ops_t ops = { .oobbuf = (u8 *)addr, .ooblen = rwsize, .mode = MTD_OOB_RAW }; if (read) ret = nand->read_oob(nand, off, &ops); else ret = nand->write_oob(nand, off, &ops); } else if (raw) {
int flash_test(void) { struct nand_chip *nand_chip = nand_info[0].priv; nand_info_t *nand; int count; int ret = 0; /*********************** ERASE - start ***************************************/ struct erase_info instr; ulong start; ulong end = 0x8000000; ulong ofs = 0; ulong block; ulong off; size_t size; int i = 0; int test_passed = 1; int p_test_passed = 1; u_char *read_buff = (u_char *) FLASH_READ_BUF_ADDR; u_char *write_buff = (u_char *) FLASH_WRITE_BUF_ADDR; nand = &nand_info[nand_curr_device]; /* Added to check nand bad block */ ret = check_nand_bad_block(nand); if (ret != 0) { printf("FAILED\n"); return -1; } for (count = 0; count < 3; count++) { start = 283; end = 345; if (count == 1) { start = 1103; end = 1165; } else if (count == 2) { start = 1922; end = 1984; } if (!end || end < 0) end = start; printf("Running Erase Test...Please wait..."); printf("Start Block : %d, End Block : %d\n", (int)start, (int)end); for (i = 0; i < 0x20000; i++) { write_buff[i] = (unsigned char)i; } for (block = start; block <= end; ofs++, block++) { instr.addr = block << nand_chip->phys_erase_shift; instr.len = 1 << nand_chip->phys_erase_shift; off = instr.addr; size = instr.len; ret = nand_erase(nand, off, size); if (ret) { if (!nand_block_isbad(nand, off)) { printf("erase failed at block %d\n", (int)block); p_test_passed = test_passed = 0; } continue; } } /*********************** ERASE - end ***************************************/ if (p_test_passed) { printf("PASSED\n"); } else printf("FAILED\n"); p_test_passed = 1; printf("Running Write test...Please wait..."); printf("Start Block : %d, End Block : %d\n", (int)start, (int)end); for (block = start; block <= end; ofs++, block++) { instr.addr = block << nand_chip->phys_erase_shift; instr.len = 1 << nand_chip->phys_erase_shift; off = instr.addr; size = instr.len; ret = nand_write(nand, off, &size, (u_char *) write_buff); if (ret) { printf("Error writing to NAND: ret = %d\n", ret); p_test_passed = test_passed = 0; continue; } } if (p_test_passed) { printf("PASSED\n"); } else printf("FAILED\n"); p_test_passed = 1; printf("Running Read test...Please wait..."); printf("Start Block : %d, End Block : %d\n", (int)start, (int)end); for (block = start; block <= end; ofs++, block++) { instr.addr = block << nand_chip->phys_erase_shift; instr.len = 1 << nand_chip->phys_erase_shift; off = instr.addr; size = instr.len; nand_read(nand, off, &size, (u_char *) read_buff); for (i = 0; i < 0x20000; i++) { if (read_buff[i] != (unsigned char)i) { if (!nand_block_isbad(nand, off)) { printf("write-read failed at offset 0x%x (0x%x)\n", (unsigned int)(block * 0x20000 + i), read_buff[i]); p_test_passed = test_passed = 0; } break; } } } if (p_test_passed) { printf("PASSED\n"); } else { printf("FAILED\n"); } } /*********************** ERASE - start ***************************************/ printf("Erase nand after completing the Nand write read test... \n"); { struct erase_info instr; ulong start; ulong end = 0x8000000; ulong ofs = 0; ulong block; ulong off, size; int i = 0; u_char *write_buff = (u_char *) FLASH_WRITE_BUF_ADDR; nand = &nand_info[nand_curr_device]; for (count = 0; count < 3; count++) { start = 283; end = 345; if (count == 1) { start = 1103; end = 1165; } else if (count == 2) { start = 1922; end = 1984; } if (!end || end < 0) end = start; printf("Running Erase Test...Please wait..."); printf("Start Block : %d, End Block : %d\n", (int)start, (int)end); for (i = 0; i < 0x20000; i++) { write_buff[i] = (unsigned char)i; } for (block = start; block <= end; ofs++, block++) { instr.addr = block << nand_chip->phys_erase_shift; instr.len = 1 << nand_chip->phys_erase_shift; off = instr.addr; size = instr.len; ret = nand_erase(nand, off, size); if (ret) { if (!nand_block_isbad(nand, off)) { printf("erase failed at block %d\n", (int)block); p_test_passed = test_passed = 0; } continue; } } } } /*********************** ERASE - end ***************************************/ printf("Nand Test Completed... %s\n", ((test_passed) ? "PASS" : "FAIL")); return 0; }
int nandwrite_mlc(char *image_path, int dev, loff_t mtdoffset, struct mtd_info_user *meminfo) { int cnt = 0; int image = -1; int imglen = 0, pagesize, blocksize, badblocks = 0; unsigned int offs; int ret; bool read_next = true; unsigned char *writebuf = NULL; int retCode = 0; uint32_t nblock, npage, skip; int total_blocks, pagesperblock, blockskip; image = open(image_path, O_RDONLY); if (image == -1) { perror("open error"); return -1; } imglen = lseek(image, 0, SEEK_END); lseek (image, 0, SEEK_SET); pagesize = meminfo->writesize; blocksize = meminfo->erasesize; // Check, if length fits into device total_blocks = meminfo->size / blocksize; pagesperblock = blocksize / pagesize; blockskip = (MLC_MAX_IMG_SIZ / pagesize + 1) * CONFIG_PAGE_REPLICATION * CONFIG_BLOCK_REPLICATION / pagesperblock; if ((blockskip * 2) > total_blocks || imglen > MLC_MAX_IMG_SIZ) { show_nand_info(stderr, meminfo); perror("Assigned max image size does not fit into device"); retCode = -2; goto closeall; } // Allocate a buffer big enough to contain all the data for one page writebuf = (unsigned char*)MALLOC(pagesize); erase_buffer(writebuf, pagesize); while ((imglen > 0) && (mtdoffset < meminfo->size)) { int readlen = pagesize; int tinycnt = 0; skip = 0; badblocks = 0; if (read_next) { erase_buffer(writebuf, readlen); /* Read up to one page data */ while (tinycnt < readlen) { cnt = read(image, writebuf + tinycnt, readlen - tinycnt); if (cnt == 0) { /* EOF */ break; } else if (cnt < 0) { perror ("File I/O error on input"); retCode = -3; goto closeall; } tinycnt += cnt; } imglen -= tinycnt; read_next = false; } for (nblock = 0; nblock < CONFIG_BLOCK_REPLICATION; nblock++) { // offs = mtdoffset + nblock * blocksize + skip * blocksize; offs = mtdoffset + skip * blocksize; // skip bad blocks ret = nand_block_isbad(dev, offs); if (ret < 0) { retCode = -5; goto closeall; } else if (ret == 1) { #if 0 loff_t checkblock; char have_copy = 0; if (!quiet) { fprintf(stderr, "Skip bad block at address 0x%x, (block %u)\n", offs, offs / blocksize); } badblocks++; // make sure we have at least one copy for (checkblock = 0; checkblock < CONFIG_BLOCK_REPLICATION; checkblock++) { offs = mtdoffset + checkblock * blocksize + skip * blocksize; ret = nand_block_isbad(dev, offs); if (ret < 0) goto closeall; else if (ret == 0) { have_copy = 1; break; } } if (!have_copy) { printf("Too many bad blocks\n"); goto closeall; } skip += blockskip; continue; #else if (!quiet) { uint32_t block_mask = meminfo->erasesize - 1; printf("Bad block 0x%x\n", (offs & (~block_mask))); } if (++badblocks >= CONFIG_BLOCK_REPLICATION) { printf("Too many bad blocks\n"); retCode = -4; goto closeall; } skip += blockskip; continue; #endif } for (npage = 0; npage < CONFIG_PAGE_REPLICATION; npage++) { offs = mtdoffset + npage * pagesize + skip * blocksize; /* Write out the Page data */ if (pwrite(dev, writebuf, pagesize, offs) != pagesize) { fprintf(stderr, "Bad page for copy %u of block %x for address %x\n", npage, nblock, offs); } } skip += blockskip; read_next = true; } // for nblock mtdoffset += pagesize * CONFIG_PAGE_REPLICATION; } // while (imglen > 0) closeall: if (writebuf) { free(writebuf); } close(image); if (imglen > 0) { fprintf(stderr, "Data was only partially written due to error\n"); } return retCode; }