예제 #1
0
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;
}
예제 #2
0
파일: nand.c 프로젝트: koenkooi/u-boot
/*
 * 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;
}
예제 #3
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;
}
예제 #4
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.
 */
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;
}
예제 #5
0
파일: nand.c 프로젝트: tigergao0113/uboot
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;
}
예제 #6
0
파일: bootm.c 프로젝트: eesuda/u-boot
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;
}
예제 #7
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;
}
예제 #8
0
/*******************************************************************************
 * 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;
}
예제 #9
0
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;
}
예제 #10
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;
}
예제 #11
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 {
예제 #12
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.
 */
#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;
}
예제 #13
0
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);
	}
}
예제 #14
0
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;
}
예제 #15
0
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 {
예제 #16
0
/*******************************************************************************
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;
}
예제 #17
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 */
}
예제 #18
0
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;
}
예제 #19
0
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")) {
예제 #20
0
파일: cmd_nand.c 프로젝트: tojoevan/u-boot
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) {
예제 #21
0
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;
}
예제 #22
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;
}