ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/uboot/common/cmd_spi_flash.c b/marvell/uboot/common/cmd_spi_flash.c
new file mode 100644
index 0000000..d7a57cc
--- /dev/null
+++ b/marvell/uboot/common/cmd_spi_flash.c
@@ -0,0 +1,905 @@
+/*
+ * Command for accessing SPI flash.
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <spi.h>
+#include <spi_flash_chip.h>
+//#include <spi_nand.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+
+#ifndef CONFIG_SF_DEFAULT_SPEED
+# define CONFIG_SF_DEFAULT_SPEED	1000000
+#endif
+#ifndef CONFIG_SF_DEFAULT_MODE
+# define CONFIG_SF_DEFAULT_MODE		SPI_MODE_3
+#endif
+#ifndef CONFIG_SF_DEFAULT_CS
+# define CONFIG_SF_DEFAULT_CS		0
+#endif
+#ifndef CONFIG_SF_DEFAULT_BUS
+# define CONFIG_SF_DEFAULT_BUS		0
+#endif
+
+static int bit_count(u_char n)
+{
+	u_char c = 0;
+
+	for (c = 0; n; ++c)
+		n &= (n - 1);
+	return c ;
+}
+
+static int raw_access(struct spi_flash_chip *chip, const char *addr,
+			loff_t off, ulong count, int read)
+{
+	struct mtd_info *mtd = chip->mtd;
+	int ret = 0;
+
+	while (count--) {
+		/* Raw access */
+		struct mtd_oob_ops ops = {
+			.datbuf = (u8 *)addr,
+			.oobbuf = ((u8 *)addr) + mtd->writesize,
+			.len = mtd->writesize,
+			.ooblen = mtd->oobsize,
+			.mode = MTD_OPS_RAW,
+		};
+
+		if (read)
+			ret = mtd_read_oob(mtd, off, &ops);
+		else
+			ret = mtd_write_oob(mtd, off, &ops);
+
+		if (ret) {
+			printf("%s: error at offset %llx, ret %d\n",
+				__func__, (long long)off, ret);
+			break;
+		}
+
+		addr += mtd->writesize + mtd->oobsize;
+		off += mtd->writesize;
+	}
+
+	return ret;
+}
+
+/*
+ * This function computes the length argument for the erase command.
+ * The length on which the command is to operate can be given in two forms:
+ * 1. <cmd> offset len  - operate on <'offset',  'len')
+ * 2. <cmd> offset +len - operate on <'offset',  'round_up(len)')
+ * If the second form is used and the length doesn't fall on the
+ * sector boundary, than it will be adjusted to the next sector boundary.
+ * If it isn't in the flash, the function will fail (return -1).
+ * Input:
+ *    arg: length specification (i.e. both command arguments)
+ * Output:
+ *    len: computed length for operation
+ * Return:
+ *    1: success
+ *   -1: failure (bad format, bad address).
+ */
+static int sf_parse_len_arg(struct spi_flash_chip *chip, char *arg, ulong *len)
+{
+	struct mtd_info *mtd = chip->mtd;
+	char *ep;
+	char round_up_len; /* indicates if the "+length" form used */
+	ulong len_arg;
+
+	round_up_len = 0;
+	if (*arg == '+') {
+		round_up_len = 1;
+		++arg;
+	}
+
+	len_arg = simple_strtoul(arg, &ep, 16);
+	if (ep == arg || *ep != '\0')
+		return -1;
+
+	if (round_up_len && mtd->erasesize > 0)
+		*len = ROUND(len_arg, mtd->erasesize);
+	else
+		*len = len_arg;
+
+	return 1;
+}
+
+static int spi_flash_dump(struct spi_flash_chip *chip, ulong off,
+			int only_oob, int repeat)
+{
+	struct mtd_info *mtd = chip->mtd;
+	struct mtd_oob_ops ops;
+	int i;
+	u_char *datbuf, *oobbuf, *p;
+	static loff_t last;
+	int ret = 0;
+	size_t retlen;
+
+	if (repeat)
+		off = last + mtd->writesize;
+
+	last = off;
+
+	datbuf = memalign(ARCH_DMA_MINALIGN, mtd->writesize);
+	if (!datbuf) {
+		puts("No memory for page buffer\n");
+		return 1;
+	}
+
+	if (mtd->oobsize) {
+		oobbuf = memalign(ARCH_DMA_MINALIGN, mtd->oobsize);
+		if (!oobbuf) {
+			puts("No memory for page buffer\n");
+			ret = 1;
+			goto free_dat;
+		}
+	}
+
+	off &= ~(mtd->writesize - 1);
+	loff_t addr = (loff_t) off;
+	if (mtd->oobsize) {
+		memset(&ops, 0, sizeof(ops));
+		ops.datbuf = datbuf;
+		ops.oobbuf = oobbuf;
+		ops.len = mtd->writesize;
+		ops.ooblen = mtd->oobsize;
+		ops.mode = MTD_OPS_RAW;
+		i = mtd_read_oob(mtd, addr, &ops);
+		if (i < 0 && i != -EUCLEAN) {
+			printf("Error (%d) reading page %08lx\n", i, off);
+			ret = 1;
+			goto free_all;
+		}
+	} else {
+		ret = mtd_read(mtd, addr, mtd->writesize, &retlen, datbuf);
+		if ((ret < 0 && ret != -EUCLEAN) || retlen != mtd->writesize)
+			return -1;
+	}
+	printf("Page %08lx dump:\n", off);
+
+	if (!only_oob) {
+		i = mtd->writesize >> 4;
+		p = datbuf;
+
+		while (i--) {
+			printf("\t%02x %02x %02x %02x %02x %02x %02x %02x"
+			       "  %02x %02x %02x %02x %02x %02x %02x %02x\n",
+			       p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
+			       p[8], p[9], p[10], p[11], p[12], p[13], p[14],
+			       p[15]);
+			p += 16;
+		}
+	}
+
+	if (mtd->oobsize) {
+		puts("OOB:\n");
+		i = mtd->oobsize >> 3;
+		p = oobbuf;
+		while (i--) {
+			printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n",
+			p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+			p += 8;
+		}
+	}
+
+free_all:
+	if (mtd->oobsize)
+		free(oobbuf);
+free_dat:
+	free(datbuf);
+
+	return ret;
+}
+
+static int spi_flash_mark_biterr(struct spi_flash_chip *chip, ulong off, int bad_ct)
+{
+	struct mtd_info *mtd = chip->mtd;
+	int i, j, k, m, bit_ct;
+	u_char *datbuf, *oobbuf, *p;
+	int ret = 0;
+
+	datbuf = memalign(ARCH_DMA_MINALIGN, mtd->writesize);
+	if (!datbuf) {
+		puts("No memory for page buffer\n");
+		return 1;
+	}
+
+	oobbuf = memalign(ARCH_DMA_MINALIGN, mtd->oobsize);
+	if (!oobbuf) {
+		puts("No memory for page buffer\n");
+		ret = 1;
+		goto free_dat;
+	}
+	off &= ~(mtd->writesize - 1);
+	loff_t addr = (loff_t) off;
+	struct mtd_oob_ops ops;
+	memset(&ops, 0, sizeof(ops));
+	ops.datbuf = datbuf;
+	ops.oobbuf = oobbuf;
+	ops.len = mtd->writesize;
+	ops.ooblen = mtd->oobsize;
+	ops.mode = MTD_OPS_RAW;
+	ret = mtd_read_oob(mtd, addr, &ops);
+	if (ret < 0 && ret != -EUCLEAN) {
+		printf("Error (%d) reading page %08lx\n", i, off);
+		ret = 1;
+		goto free_all;
+	}
+
+	i = 0;
+	bit_ct = 0;
+	while (1) {
+		j = bit_count(datbuf[i]);
+		if (bit_ct + j <= bad_ct) {
+			datbuf[i] = 0;
+		} else {
+			j = bad_ct -bit_ct;
+			m = k = 0;
+			while (1) {
+				if (datbuf[i] & (1 << k)) {
+					datbuf[i] &= ~(1 << k);
+					if (++m >= j)
+						break;
+				}
+				k++;
+			}
+		}
+
+		bit_ct += j;
+		if (bit_ct >= bad_ct)
+			break;
+
+		if (++i >= mtd->writesize) {
+			printf("No enough bit set in this page\n");
+			ret = 1;
+			goto free_all;
+		}
+	}
+
+	ret = mtd_write_oob(mtd, addr, &ops);
+	if (ret) {
+		printf("%s: write error at offset %llx, ret %d\n",
+			__func__, (long long)addr, ret);
+		ret = 1;
+	}
+
+free_all:
+	free(oobbuf);
+free_dat:
+	free(datbuf);
+
+	return ret;
+}
+
+struct spi_flash_chip *spi_flash_chip_probe(int dev)
+{
+	struct spi_flash_chip *chip;
+	int ret;
+
+	ret = spi_flash_select_dev(dev);
+	if (ret)
+		chip = NULL;
+	else
+		chip = cur_sf_chip;
+	return chip;
+}
+
+void spi_flash_chip_free(struct spi_flash_chip *chip)
+{
+}
+
+
+int spi_flash_cmd_write_yaffs(struct spi_flash_chip *chip, u32 offset,
+		size_t len, const void *buf)
+{
+	struct mtd_info *mtd = chip->mtd;
+	size_t retlen;
+	size_t leftlen = len;
+	size_t writelen;
+	size_t block_len, block_off;
+	u_char *p_buffer = buf;
+	loff_t block_start;
+	u32 writeoffset;
+	int ret = 0;
+	bool end;
+
+	while (leftlen > 0) {
+		if (offset >= chip->size)
+			return -1;
+
+		writeoffset = offset;
+		writelen = 0;
+		end = false;
+		if (mtd->type == MTD_NANDFLASH) {
+			while ((writelen < leftlen) && !end) {
+				block_start = offset & ~(loff_t)(mtd->erasesize - 1);
+				block_off = offset & (mtd->erasesize - 1);
+				block_len = mtd->erasesize - block_off;
+
+				if (!mtd_block_isbad(mtd, block_start))
+					writelen += block_len;
+				else
+					end = true;
+				offset += block_len;
+			}
+		} else
+			writelen = leftlen;
+
+		if (writelen) {
+			int page, pages;
+			size_t pagesize = mtd->writesize;
+			size_t pagesize_oob = pagesize + mtd->oobsize;
+			struct mtd_oob_ops ops;
+
+			writelen = min(writelen, leftlen);
+
+			ops.len = pagesize;
+			ops.ooblen = mtd->oobsize;
+			ops.mode = MTD_OPS_AUTO_OOB;
+			ops.ooboffs = 0;
+
+			pages = writelen / pagesize_oob;
+			for (page = 0; page < pages; page++) {
+				ops.datbuf = p_buffer;
+				ops.oobbuf = ops.datbuf + pagesize;
+
+				ret = mtd_write_oob(mtd, writeoffset, &ops);
+				if (ret != 0)
+					break;
+
+				writeoffset += pagesize;
+				p_buffer += pagesize_oob;
+			}
+
+			leftlen -= writelen;
+		}
+	}
+
+	return ret;
+}
+
+
+int spi_flash_cmd_write_ops(struct spi_flash_chip *chip, u32 offset,
+		size_t len, const void *buf)
+{
+	struct mtd_info *mtd = chip->mtd;
+	size_t retlen;
+	size_t leftlen = len;
+	size_t writelen;
+	size_t block_len, block_off;
+	loff_t block_start;
+	u32 writeoffset;
+	int ret = 0;
+	bool end;
+
+	while (leftlen > 0) {
+		if (offset >= chip->size)
+			return -1;
+
+		writeoffset = offset;
+		writelen = 0;
+		end = false;
+		if (mtd->type == MTD_NANDFLASH) {
+			while ((writelen < leftlen) && !end) {
+				block_start = offset & ~(loff_t)(mtd->erasesize - 1);
+				block_off = offset & (mtd->erasesize - 1);
+				block_len = mtd->erasesize - block_off;
+
+				if (!mtd_block_isbad(mtd, block_start))
+					writelen += block_len;
+				else
+					end = true;
+				offset += block_len;
+			}
+		} else
+			writelen = leftlen;
+
+		if (writelen) {
+			writelen = min(writelen, leftlen);
+			ret = mtd_write(mtd, writeoffset, writelen, &retlen, buf + (len - leftlen));
+			if (ret || writelen != retlen)
+				return -1;
+			leftlen -= writelen;
+		}
+	}
+
+	return ret;
+}
+
+int spi_flash_cmd_erase_ops(struct spi_flash_chip *chip, u32 offset, size_t len, bool spread)
+{
+	struct mtd_info *mtd = chip->mtd;
+	struct erase_info instr = {
+		.callback	= NULL,
+	};
+	size_t leftlen;
+	size_t eraselen;
+	u32 eraseoffset;
+	u64 endaddr;
+	int ret = 0;
+	bool end;
+
+	if (offset & (mtd->erasesize - 1)) {
+		printf("%s: Unaligned address\n", __func__);
+		return -EINVAL;
+	}
+
+	if (len & (mtd->erasesize - 1)) {
+		printf("%s: warn: len=0x%x not block aligned\n", __func__, len);
+		len = roundup(len, mtd->erasesize);
+	}
+
+	leftlen = len;
+	endaddr = offset + len;
+	if (endaddr > chip->size) {
+		printf("%s: Erase past end of device\n", __func__);
+		return -EINVAL;
+	}
+
+	while (leftlen > 0) {
+		if (offset >= chip->size)
+			return -1;
+
+		eraseoffset = offset;
+		eraselen = 0;
+		end = false;
+		if (mtd->type == MTD_NANDFLASH) {
+			while ((eraselen < leftlen) && !end) {
+				if (!mtd_block_isbad(mtd, offset))
+					eraselen += mtd->erasesize;
+				else
+					end = true;
+				offset += mtd->erasesize;
+			}
+		} else {
+			eraselen = len;
+			offset += len;
+		}
+		if (eraselen) {
+			instr.addr = eraseoffset;
+			instr.len = eraselen;
+			instr.mtd = mtd;
+			ret = mtd_erase(mtd, &instr);
+			if (ret)
+				return -1;
+		}
+		if (spread)
+			leftlen -= eraselen;
+		else
+			leftlen = endaddr - offset;
+	}
+
+	return ret;
+}
+
+int spi_flash_cmd_read_ops(struct spi_flash_chip *chip, u32 offset,
+		size_t len, void *data)
+{
+	struct mtd_info *mtd = chip->mtd;
+	size_t retlen;
+	size_t leftlen = len;
+	size_t readlen;
+	size_t block_len, block_off;
+	loff_t block_start;
+	u32 readoffset;
+	int ret = 0;
+	bool end;
+
+	while (leftlen > 0) {
+		if (offset >= chip->size)
+			return -1;
+
+		readoffset = offset;
+		readlen = 0;
+		end = false;
+		if (mtd->type == MTD_NANDFLASH) {
+			while ((readlen < leftlen) && !end) {
+				block_start = offset & ~(loff_t)(mtd->erasesize - 1);
+				block_off = offset & (mtd->erasesize - 1);
+				block_len = mtd->erasesize - block_off;
+
+				if (!mtd_block_isbad(mtd, block_start))
+					readlen += block_len;
+				else
+					end = true;
+				offset += block_len;
+			}
+		} else
+			readlen = leftlen;
+		if (readlen) {
+			readlen = min(readlen, leftlen);
+			ret = mtd_read(mtd, readoffset, readlen, &retlen, data + (len - leftlen));
+			if ((ret < 0 && ret != -EUCLEAN) || readlen != retlen)
+				return -1;
+
+			leftlen -= readlen;
+		}
+	}
+	return ret;
+}
+
+/**
+ * Update an area of SPI flash by erasing and writing any blocks which need
+ * to change. Existing blocks with the correct data are left unchanged.
+ *
+ * @param flash		flash context pointer
+ * @param offset	flash offset to write
+ * @param len		number of bytes to write
+ * @param buf		buffer to write from
+ * @return 0 if ok, 1 on error
+ */
+static int spi_flash_update(struct spi_flash_chip *chip, u32 offset,
+		size_t len, const char *buf)
+{
+	int ret = 0;
+
+	ret = spi_flash_cmd_erase_ops(chip, offset, len, true);
+	if (ret) {
+		printf("SPI-FLASH: %zu bytes @ %#x Erased: ERROR\n",
+			(size_t)len, (u32)offset);
+		return ret;
+	}
+	ret = spi_flash_cmd_write_ops(chip, offset, len, buf);
+	if (ret) {
+		printf("SPI-FLASH: %zu bytes @ %#x Written: ERROR\n",
+			(size_t)len, (u32)offset);
+	}
+	return ret;
+}
+
+static int do_spi_flash_get_info(struct spi_flash_chip *chip,
+					int argc, char * const argv[])
+{
+	struct mtd_info *mtd = chip->mtd;
+	unsigned long addr;
+	struct spi_flash_info *info;
+	char *endp;
+
+	if (argc < 2)
+		return -1;
+
+	addr = simple_strtoul(argv[1], &endp, 16);
+	if (*argv[1] == 0 || *endp != 0)
+		return -1;
+
+	info = (struct spi_flash_info *)addr;
+	info->totalsize = chip->size;
+	info->erasesize = mtd->erasesize;
+	info->pagesize = mtd->writesize;
+
+#if 0
+	printf("flash info: totalsize=0x%x erase_size=0x%x page_size=0x%x\n",
+		(unsigned)info->totalsize, (unsigned)info->erasesize,
+		(unsigned)info->pagesize);
+#endif
+	return 0;
+}
+
+static int do_spi_flash_read_write(struct spi_flash_chip *chip,
+					int argc, char * const argv[])
+{
+	struct mtd_info *mtd = chip->mtd;
+	unsigned long addr;
+	unsigned long offset;
+	unsigned long len;
+	int data_len;
+	void *buf;
+	char *endp;
+	char *cmd, *s;
+	int ret = 1;
+
+	if (argc < 4)
+		return -1;
+
+	cmd = argv[0];
+	addr = simple_strtoul(argv[1], &endp, 16);
+	if (*argv[1] == 0 || *endp != 0)
+		return -1;
+	offset = simple_strtoul(argv[2], &endp, 16);
+	if (*argv[2] == 0 || *endp != 0)
+		return -1;
+	len = simple_strtoul(argv[3], &endp, 16);
+	if (*argv[3] == 0 || *endp != 0)
+		return -1;
+
+	if (mtd->protect_enabled &&
+	    ((offset <= mtd->protect_start &&
+		offset + len > mtd->protect_start) ||
+	     (offset >= mtd->protect_start &&
+		offset < mtd->protect_end))) {
+		printf("error: protected ared start=0x%x end=0x%x\n",
+			mtd->protect_start, mtd->protect_end);
+		return -EACCES;
+	}
+
+	data_len = len;
+	if (strcmp(argv[0], "update") == 0) {
+		if (offset + len > chip->size) {
+			printf("ERROR: attempting %s past flash size (%#llx)\n",
+			       argv[0], chip->size);
+			return 1;
+		}
+
+		buf = map_physmem(addr, len, MAP_WRBACK);
+		if (!buf) {
+			puts("Failed to map physical memory\n");
+			return 1;
+		}
+
+		ret = spi_flash_update(chip, offset, len, buf);
+		printf("SPI-FLASH: %zu bytes @ %#x Updated: %s\n",
+			(size_t)len, (u32)offset, ret ? "ERROR" : "OK");
+	} else if (strncmp(argv[0], "read", 4) == 0 ||
+			strncmp(argv[0], "write", 5) == 0) {
+		int read;
+		int raw = 0;
+		int yaffs = 0;
+
+		read = strncmp(argv[0], "read", 4) == 0;
+		s = strchr(cmd, '.');
+		if (s && !strcmp(s, ".raw")) {
+			raw = 1;
+
+			if (len * mtd->writesize + offset > chip->size) {
+				puts("ERROR: Offset exceeds device limit\n");
+				return 1;
+			}
+
+			data_len = len * (mtd->writesize + mtd->oobsize);
+		} else if (s && !strcmp(s, ".yaffs")) {
+			yaffs = 1;
+		} else {
+			if (offset + len > chip->size) {
+				printf("ERROR: attempting %s past flash size (%#llx)\n",
+				       argv[0], chip->size);
+				return 1;
+			}
+		}
+
+		buf = map_physmem(addr, data_len, MAP_WRBACK);
+		if (!buf) {
+			puts("Failed to map physical memory\n");
+			return 1;
+		}
+
+		if (!s) {
+			if (read)
+				ret = spi_flash_cmd_read_ops(chip, offset,
+								len, buf);
+			else
+				ret = spi_flash_cmd_write_ops(chip, offset,
+								len, buf);
+		} else if (raw) {
+			ret = raw_access(chip, buf, offset, len, read);
+		} else if (yaffs) {
+			ret = spi_flash_cmd_write_yaffs(chip, offset, len, buf);
+		} else {
+			printf("Unknown spi_flash command suffix '%s'.\n", s);
+			ret = 1;
+			goto exit;
+		}
+
+		if (ret == -EUCLEAN)
+			ret = 0;
+
+		if ( len > 0x8000)
+		printf("SPI-FLASH: %zu %s @ %#x %s: %s\n", (size_t)len,
+		       raw ? "pages" : "bytes", (u32)offset,
+		       read ? "Read" : "Written",
+		       (ret < 0 && ret != -EUCLEAN) ? "ERROR" : "OK");
+	}
+exit:
+	unmap_physmem(buf, data_len);
+	return ret == 0 ? 0 : 1;
+}
+
+static int do_spi_flash_erase(struct spi_flash_chip *chip,
+			int argc, char * const argv[], bool spread)
+{
+	struct mtd_info *mtd = chip->mtd;
+	unsigned long offset;
+	unsigned long len;
+	char *endp;
+	int ret;
+
+	if (argc < 3)
+		return -1;
+
+	offset = simple_strtoul(argv[1], &endp, 16);
+	if (*argv[1] == 0 || *endp != 0)
+		return -1;
+
+	ret = sf_parse_len_arg(chip, argv[2], &len);
+	if (ret != 1)
+		return -1;
+
+	if (mtd->protect_enabled &&
+	    ((offset <= mtd->protect_start &&
+		offset + len > mtd->protect_start) ||
+	     (offset >= mtd->protect_start &&
+		offset < mtd->protect_end))) {
+		printf("error: protected ared start=0x%x end=0x%x\n",
+			mtd->protect_start, mtd->protect_end);
+		return -EACCES;
+	}
+
+	/* Consistency checking */
+	if (offset + len > chip->size) {
+		printf("ERROR: attempting %s past flash size (%#llx)\n",
+		       argv[0], chip->size);
+		return 1;
+	}
+
+	ret = spi_flash_cmd_erase_ops(chip, offset, len, spread);
+	printf("SPI-FLASH: %zu bytes @ %#x Erased: %s\n",
+		(size_t)len, (u32)offset, ret ? "ERROR" : "OK");
+
+	return ret == 0 ? 0 : 1;
+}
+
+static int do_spi_flash_show_bad(struct spi_flash_chip *chip)
+{
+	struct mtd_info *mtd = chip->mtd;
+	loff_t offset;
+
+	for (offset = 0; offset < chip->size; offset += mtd->erasesize) {
+		if(mtd_block_isbad(chip, offset))
+			printf("Bad block at 0x%#llx\n", offset);
+	}
+
+	return 0;
+}
+
+static int do_spi_flash_mark_bad(struct spi_flash_chip *chip,
+				int argc, char * const argv[])
+{
+	int ret;
+	loff_t offset;
+	char *endp;
+
+	if (argc < 2)
+		return -1;
+
+	offset = simple_strtoul(argv[1], &endp, 16);
+	if (*argv[1] == 0 || *endp != 0)
+		return -1;
+
+	ret = mtd_block_markbad(chip->mtd, offset);
+	if (!ret) {
+		printf("SPI-FLASH: 0x%#llx marked as bad block\n", offset);
+	}
+
+	return ret == 0 ? 0 : 1;
+}
+
+static int do_spi_flash_dump(struct spi_flash_chip *chip,
+				int argc, char * const argv[])
+{
+	int ret;
+	loff_t offset;
+	char *endp;
+
+	if (argc < 2)
+		return -1;
+
+	offset = simple_strtoul(argv[1], &endp, 16);
+	if (*argv[1] == 0 || *endp != 0)
+		return -1;
+
+	ret = spi_flash_dump(chip, offset, 0, 0);
+	return ret == 0 ? 0 : 1;
+}
+
+static int do_spi_flash_probe(int argc, char * const argv[])
+{
+	int dev = 0;
+	char *endp;
+
+	if (argc >= 2) {
+		dev = simple_strtoul(argv[1], &endp, 16);
+		if (*argv[1] == 0 || *endp != 0)
+			return -1;
+	}
+
+	return spi_flash_select_dev(dev);
+}
+
+static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc,
+			char * const argv[])
+{
+	struct spi_flash_chip *chip;
+	const char *cmd;
+	int ret;
+
+	/* need at least two arguments */
+	if (argc < 2)
+		goto usage;
+
+	cmd = argv[1];
+	--argc;
+	++argv;
+
+	if (strcmp(cmd, "probe") == 0) {
+		ret = do_spi_flash_probe(argc, argv);
+		goto done;
+	}
+
+	/* The remaining commands require a selected device */
+	chip = cur_sf_chip;
+	if (!chip) {
+		puts("No SPI flash selected. Please run `spi_flash probe'\n");
+		return 1;
+	}
+
+	if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0 ||
+	    strcmp(cmd, "update") == 0)
+		ret = do_spi_flash_read_write(chip, argc, argv);
+	else if (strcmp(cmd, "erase") == 0)
+		ret = do_spi_flash_erase(chip, argc, argv, false);
+	else if (strcmp(cmd, "erase.spread") == 0)
+		ret = do_spi_flash_erase(chip, argc, argv, true);
+	else if (strcmp(cmd, "info") == 0)
+		ret = do_spi_flash_get_info(chip, argc, argv);
+	else if (strcmp(cmd, "bad") == 0)
+		ret = do_spi_flash_show_bad(chip);
+	else if (strcmp(cmd, "markbad") == 0)
+		ret = do_spi_flash_mark_bad(chip, argc, argv);
+	else if (strcmp(cmd, "dump") == 0)
+		ret = do_spi_flash_dump(chip, argc, argv);
+	else if (strcmp(cmd, "biterr") == 0) {
+		int bad_bit, addr;
+
+		argc -= 1;
+		argv += 1;
+
+		if (argc <= 0)
+			goto usage;
+
+		bad_bit = 1;
+		if (argc > 0)
+			addr = (ulong)simple_strtoul(argv[0], NULL, 16);
+		if (argc >= 2)
+			bad_bit = (ulong)simple_strtoul(argv[1], NULL, 16);
+
+		ret = spi_flash_mark_biterr(chip, addr, bad_bit);
+		printf(" %s\n", ret ? "Failed" : "Passed");
+
+		return ret;
+	} else
+		ret = -1;
+
+done:
+	if (ret != -1)
+		return ret;
+
+usage:
+	return CMD_RET_USAGE;
+}
+
+
+U_BOOT_CMD(
+	spi_flash,	5,	1,	do_spi_flash,
+	"SPI NAND/NOR flash sub-system",
+	"probe [[bus:]cs] [hz] [mode]	- init flash device on given SPI bus\n"
+	"				  and chip select\n"
+	"spi_flash read addr offset len	- read `len' bytes starting at\n"
+	"				  `offset' to memory at `addr', skipping bad blocks.\n"
+	"spi_flash write addr offset len	- write `len' bytes from memory\n"
+	"				  at `addr' to flash at `offset', skipping bad blocks.\n"
+	"spi_flash erase[.spread] offset [+]len		- erase `len' bytes from `offset'\n"
+	"				  `+len' round up `len' to block size\n"
+	"				  With '.spread', erase enough for given file size, otherwise,\n"
+	"				  'size' includes skipped bad blocks.\n"
+	"spi_flash info addr - get flash info, and save to addr\n"
+	"spi_flash update addr offset len	- erase and write `len' bytes from memory\n"
+	"				  at `addr' to flash at `offset'\n"
+	"spi_flash bad - show bad blocks\n"
+	"spi_flash markbad offset - mark block include `offset' as bad block\n"
+);