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"
+);