Merge branch 'next' of https://source.denx.de/u-boot/custodians/u-boot-sh into next
[platform/kernel/u-boot.git] / cmd / sf.c
index 286906c..11b9c25 100644 (file)
--- a/cmd/sf.c
+++ b/cmd/sf.c
@@ -1,24 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Command for accessing SPI flash.
  *
  * Copyright (C) 2008 Atmel Corporation
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
+#include <command.h>
+#include <display_options.h>
 #include <div64.h>
 #include <dm.h>
+#include <log.h>
 #include <malloc.h>
 #include <mapmem.h>
 #include <spi.h>
 #include <spi_flash.h>
+#include <asm/cache.h>
 #include <jffs2/jffs2.h>
 #include <linux/mtd/mtd.h>
 
 #include <asm/io.h>
 #include <dm/device-internal.h>
 
+#include "legacy-mtd-utils.h"
+
 static struct spi_flash *flash;
 
 /*
@@ -49,7 +54,7 @@ static int sf_parse_len_arg(char *arg, ulong *len)
                ++arg;
        }
 
-       len_arg = simple_strtoul(arg, &ep, 16);
+       len_arg = hextoul(arg, &ep);
        if (ep == arg || *ep != '\0')
                return -1;
 
@@ -67,7 +72,7 @@ static int sf_parse_len_arg(char *arg, ulong *len)
  *
  * @param len          amount of bytes currently processed
  * @param start_ms     start time of processing in ms
- * @return bytes per second if OK, 0 on error
+ * Return: bytes per second if OK, 0 on error
  */
 static ulong bytes_per_second(unsigned int len, ulong start_ms)
 {
@@ -78,18 +83,18 @@ static ulong bytes_per_second(unsigned int len, ulong start_ms)
                return 1024 * len / max(get_timer(start_ms), 1UL);
 }
 
-static int do_spi_flash_probe(int argc, char * const argv[])
+static int do_spi_flash_probe(int argc, char *const argv[])
 {
        unsigned int bus = CONFIG_SF_DEFAULT_BUS;
        unsigned int cs = CONFIG_SF_DEFAULT_CS;
+       /* In DM mode, defaults speed and mode will be taken from DT */
        unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
        unsigned int mode = CONFIG_SF_DEFAULT_MODE;
        char *endp;
-#ifdef CONFIG_DM_SPI_FLASH
+       bool use_dt = true;
+#if CONFIG_IS_ENABLED(DM_SPI_FLASH)
        struct udevice *new, *bus_dev;
        int ret;
-       /* In DM mode defaults will be taken from DT */
-       speed = 0, mode = 0;
 #else
        struct spi_flash *new;
 #endif
@@ -113,42 +118,44 @@ static int do_spi_flash_probe(int argc, char * const argv[])
                speed = simple_strtoul(argv[2], &endp, 0);
                if (*argv[2] == 0 || *endp != 0)
                        return -1;
+               use_dt = false;
        }
        if (argc >= 4) {
-               mode = simple_strtoul(argv[3], &endp, 16);
+               mode = hextoul(argv[3], &endp);
                if (*argv[3] == 0 || *endp != 0)
                        return -1;
+               use_dt = false;
        }
 
-#ifdef CONFIG_DM_SPI_FLASH
+#if CONFIG_IS_ENABLED(DM_SPI_FLASH)
        /* Remove the old device, otherwise probe will just be a nop */
        ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new);
        if (!ret) {
-               device_remove(new);
-               device_unbind(new);
+               device_remove(new, DM_REMOVE_NORMAL);
        }
        flash = NULL;
-       ret = spi_flash_probe_bus_cs(bus, cs, speed, mode, &new);
-       if (ret) {
+       if (use_dt) {
+               spi_flash_probe_bus_cs(bus, cs, &new);
+               flash = dev_get_uclass_priv(new);
+       } else {
+               flash = spi_flash_probe(bus, cs, speed, mode);
+       }
+
+       if (!flash) {
                printf("Failed to initialize SPI flash at %u:%u (error %d)\n",
                       bus, cs, ret);
                return 1;
        }
-
-       flash = dev_get_uclass_priv(new);
 #else
        if (flash)
                spi_flash_free(flash);
 
        new = spi_flash_probe(bus, cs, speed, mode);
        flash = new;
-
        if (!new) {
                printf("Failed to initialize SPI flash at %u:%u\n", bus, cs);
                return 1;
        }
-
-       flash = new;
 #endif
 
        return 0;
@@ -166,22 +173,24 @@ static int do_spi_flash_probe(int argc, char * const argv[])
  * @param buf          buffer to write from
  * @param cmp_buf      read buffer to use to compare data
  * @param skipped      Count of skipped data (incremented by this function)
- * @return NULL if OK, else a string containing the stage which failed
+ * Return: NULL if OK, else a string containing the stage which failed
  */
 static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset,
                size_t len, const char *buf, char *cmp_buf, size_t *skipped)
 {
        char *ptr = (char *)buf;
+       u32 start_offset = offset % flash->sector_size;
+       u32 read_offset = offset - start_offset;
 
-       debug("offset=%#x, sector_size=%#x, len=%#zx\n",
-             offset, flash->sector_size, len);
+       debug("offset=%#x+%#x, sector_size=%#x, len=%#zx\n",
+             read_offset, start_offset, flash->sector_size, len);
        /* Read the entire sector so to allow for rewriting */
-       if (spi_flash_read(flash, offset, flash->sector_size, cmp_buf))
+       if (spi_flash_read(flash, read_offset, flash->sector_size, cmp_buf))
                return "read";
        /* Compare only what is meaningful (len) */
-       if (memcmp(cmp_buf, buf, len) == 0) {
-               debug("Skip region %x size %zx: no change\n",
-                     offset, len);
+       if (memcmp(cmp_buf + start_offset, buf, len) == 0) {
+               debug("Skip region %x+%x size %zx: no change\n",
+                     start_offset, read_offset, len);
                *skipped += len;
                return NULL;
        }
@@ -190,7 +199,7 @@ static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset,
                return "erase";
        /* If it's a partial sector, copy the data into the temp-buffer */
        if (len != flash->sector_size) {
-               memcpy(cmp_buf, buf, len);
+               memcpy(cmp_buf + start_offset, buf, len);
                ptr = cmp_buf;
        }
        /* Write one complete sector */
@@ -208,7 +217,7 @@ static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset,
  * @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
+ * Return: 0 if ok, 1 on error
  */
 static int spi_flash_update(struct spi_flash *flash, u32 offset,
                size_t len, const char *buf)
@@ -231,6 +240,8 @@ static int spi_flash_update(struct spi_flash *flash, u32 offset,
 
                for (; buf < end && !err_oper; buf += todo, offset += todo) {
                        todo = min_t(size_t, end - buf, flash->sector_size);
+                       todo = min_t(size_t, end - buf,
+                                    flash->sector_size - (offset % flash->sector_size));
                        if (get_timer(last_update) > 100) {
                                printf("   \rUpdating, %zu%% %lu B/s",
                                       100 - (end - buf) / scale,
@@ -260,7 +271,7 @@ static int spi_flash_update(struct spi_flash *flash, u32 offset,
        return 0;
 }
 
-static int do_spi_flash_read_write(int argc, char * const argv[])
+static int do_spi_flash_read_write(int argc, char *const argv[])
 {
        unsigned long addr;
        void *buf;
@@ -270,27 +281,33 @@ static int do_spi_flash_read_write(int argc, char * const argv[])
        loff_t offset, len, maxsize;
 
        if (argc < 3)
-               return -1;
+               return CMD_RET_USAGE;
 
-       addr = simple_strtoul(argv[1], &endp, 16);
+       addr = hextoul(argv[1], &endp);
        if (*argv[1] == 0 || *endp != 0)
-               return -1;
+               return CMD_RET_USAGE;
 
        if (mtd_arg_off_size(argc - 2, &argv[2], &dev, &offset, &len,
                             &maxsize, MTD_DEV_TYPE_NOR, flash->size))
-               return -1;
+               return CMD_RET_FAILURE;
 
        /* Consistency checking */
        if (offset + len > flash->size) {
                printf("ERROR: attempting %s past flash size (%#x)\n",
                       argv[0], flash->size);
-               return 1;
+               return CMD_RET_FAILURE;
+       }
+
+       if (strncmp(argv[0], "read", 4) != 0 && flash->flash_is_unlocked &&
+           !flash->flash_is_unlocked(flash, offset, len)) {
+               printf("ERROR: flash area is locked\n");
+               return CMD_RET_FAILURE;
        }
 
        buf = map_physmem(addr, len, MAP_WRBACK);
-       if (!buf) {
+       if (!buf && addr) {
                puts("Failed to map physical memory\n");
-               return 1;
+               return CMD_RET_FAILURE;
        }
 
        if (strcmp(argv[0], "update") == 0) {
@@ -315,10 +332,10 @@ static int do_spi_flash_read_write(int argc, char * const argv[])
 
        unmap_physmem(buf, len);
 
-       return ret == 0 ? 0 : 1;
+       return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
 }
 
-static int do_spi_flash_erase(int argc, char * const argv[])
+static int do_spi_flash_erase(int argc, char *const argv[])
 {
        int ret;
        int dev = 0;
@@ -326,31 +343,40 @@ static int do_spi_flash_erase(int argc, char * const argv[])
        ulong size;
 
        if (argc < 3)
-               return -1;
+               return CMD_RET_USAGE;
 
        if (mtd_arg_off(argv[1], &dev, &offset, &len, &maxsize,
                        MTD_DEV_TYPE_NOR, flash->size))
-               return -1;
+               return CMD_RET_FAILURE;
 
        ret = sf_parse_len_arg(argv[2], &size);
        if (ret != 1)
-               return -1;
+               return CMD_RET_USAGE;
 
        /* Consistency checking */
        if (offset + size > flash->size) {
                printf("ERROR: attempting %s past flash size (%#x)\n",
                       argv[0], flash->size);
-               return 1;
+               return CMD_RET_FAILURE;
+       }
+
+       if (flash->flash_is_unlocked &&
+           !flash->flash_is_unlocked(flash, offset, len)) {
+               printf("ERROR: flash area is locked\n");
+               return CMD_RET_FAILURE;
        }
 
        ret = spi_flash_erase(flash, offset, size);
-       printf("SF: %zu bytes @ %#x Erased: %s\n", (size_t)size, (u32)offset,
-              ret ? "ERROR" : "OK");
+       printf("SF: %zu bytes @ %#x Erased: ", (size_t)size, (u32)offset);
+       if (ret)
+               printf("ERROR %d\n", ret);
+       else
+               printf("OK\n");
 
-       return ret == 0 ? 0 : 1;
+       return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
 }
 
-static int do_spi_protect(int argc, char * const argv[])
+static int do_spi_protect(int argc, char *const argv[])
 {
        int ret = 0;
        loff_t start, len;
@@ -381,7 +407,6 @@ static int do_spi_protect(int argc, char * const argv[])
        return ret == 0 ? 0 : 1;
 }
 
-#ifdef CONFIG_CMD_SF_TEST
 enum {
        STAGE_ERASE,
        STAGE_CHECK,
@@ -391,7 +416,7 @@ enum {
        STAGE_COUNT,
 };
 
-static char *stage_name[STAGE_COUNT] = {
+static const char *stage_name[STAGE_COUNT] = {
        "erase",
        "check",
        "write",
@@ -415,7 +440,7 @@ static void show_time(struct test_info *test, int stage)
                do_div(speed, test->time_ms[stage] * 1024);
        bps = speed * 8;
 
-       printf("%d %s: %d ticks, %d KiB/s %d.%03d Mbps\n", stage,
+       printf("%d %s: %u ticks, %d KiB/s %d.%03d Mbps\n", stage,
               stage_name[stage], test->time_ms[stage],
               (int)speed, bps / 1000, bps % 1000);
 }
@@ -436,26 +461,28 @@ static void spi_test_next_stage(struct test_info *test)
  * @param len          Size of data to read/write
  * @param offset       Offset within flash to check
  * @param vbuf         Verification buffer
- * @return 0 if ok, -1 on error
+ * Return: 0 if ok, -1 on error
  */
 static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len,
                           ulong offset, uint8_t *vbuf)
 {
        struct test_info test;
-       int i;
+       int err, i;
 
        printf("SPI flash test:\n");
        memset(&test, '\0', sizeof(test));
        test.base_ms = get_timer(0);
        test.bytes = len;
-       if (spi_flash_erase(flash, offset, len)) {
-               printf("Erase failed\n");
+       err = spi_flash_erase(flash, offset, len);
+       if (err) {
+               printf("Erase failed (err = %d)\n", err);
                return -1;
        }
        spi_test_next_stage(&test);
 
-       if (spi_flash_read(flash, offset, len, vbuf)) {
-               printf("Check read failed\n");
+       err = spi_flash_read(flash, offset, len, vbuf);
+       if (err) {
+               printf("Check read failed (err = %d)\n", err);
                return -1;
        }
        for (i = 0; i < len; i++) {
@@ -468,15 +495,17 @@ static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len,
        }
        spi_test_next_stage(&test);
 
-       if (spi_flash_write(flash, offset, len, buf)) {
-               printf("Write failed\n");
+       err = spi_flash_write(flash, offset, len, buf);
+       if (err) {
+               printf("Write failed (err = %d)\n", err);
                return -1;
        }
        memset(vbuf, '\0', len);
        spi_test_next_stage(&test);
 
-       if (spi_flash_read(flash, offset, len, vbuf)) {
-               printf("Read failed\n");
+       err = spi_flash_read(flash, offset, len, vbuf);
+       if (err) {
+               printf("Read failed (ret = %d)\n", err);
                return -1;
        }
        spi_test_next_stage(&test);
@@ -499,7 +528,7 @@ static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len,
        return 0;
 }
 
-static int do_spi_flash_test(int argc, char * const argv[])
+static int do_spi_flash_test(int argc, char *const argv[])
 {
        unsigned long offset;
        unsigned long len;
@@ -510,10 +539,10 @@ static int do_spi_flash_test(int argc, char * const argv[])
 
        if (argc < 3)
                return -1;
-       offset = simple_strtoul(argv[1], &endp, 16);
+       offset = hextoul(argv[1], &endp);
        if (*argv[1] == 0 || *endp != 0)
                return -1;
-       len = simple_strtoul(argv[2], &endp, 16);
+       len = hextoul(argv[2], &endp);
        if (*argv[2] == 0 || *endp != 0)
                return -1;
 
@@ -529,7 +558,7 @@ static int do_spi_flash_test(int argc, char * const argv[])
                return 1;
        }
 
-       from = map_sysmem(CONFIG_SYS_TEXT_BASE, 0);
+       from = map_sysmem(CONFIG_TEXT_BASE, 0);
        memcpy(buf, from, len);
        ret = spi_flash_test(flash, buf, len, offset, vbuf);
        free(vbuf);
@@ -541,31 +570,28 @@ static int do_spi_flash_test(int argc, char * const argv[])
 
        return 0;
 }
-#endif /* CONFIG_CMD_SF_TEST */
 
-static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc,
-                       char * const argv[])
+static int do_spi_flash(struct cmd_tbl *cmdtp, int flag, int argc,
+                       char *const argv[])
 {
        const char *cmd;
        int ret;
 
        /* need at least two arguments */
        if (argc < 2)
-               goto usage;
+               return CMD_RET_USAGE;
 
        cmd = argv[1];
        --argc;
        ++argv;
 
-       if (strcmp(cmd, "probe") == 0) {
-               ret = do_spi_flash_probe(argc, argv);
-               goto done;
-       }
+       if (strcmp(cmd, "probe") == 0)
+               return do_spi_flash_probe(argc, argv);
 
        /* The remaining commands require a selected device */
        if (!flash) {
                puts("No SPI flash selected. Please run `sf probe'\n");
-               return 1;
+               return CMD_RET_FAILURE;
        }
 
        if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0 ||
@@ -575,31 +601,16 @@ static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc,
                ret = do_spi_flash_erase(argc, argv);
        else if (strcmp(cmd, "protect") == 0)
                ret = do_spi_protect(argc, argv);
-#ifdef CONFIG_CMD_SF_TEST
-       else if (!strcmp(cmd, "test"))
+       else if (IS_ENABLED(CONFIG_CMD_SF_TEST) && !strcmp(cmd, "test"))
                ret = do_spi_flash_test(argc, argv);
-#endif
        else
-               ret = -1;
-
-done:
-       if (ret != -1)
-               return ret;
+               ret = CMD_RET_USAGE;
 
-usage:
-       return CMD_RET_USAGE;
+       return ret;
 }
 
-#ifdef CONFIG_CMD_SF_TEST
-#define SF_TEST_HELP "\nsf test offset len             " \
-               "- run a very basic destructive test"
-#else
-#define SF_TEST_HELP
-#endif
-
-U_BOOT_CMD(
-       sf,     5,      1,      do_spi_flash,
-       "SPI flash sub-system",
+#ifdef CONFIG_SYS_LONGHELP
+static const char long_help[] =
        "probe [[bus:]cs] [hz] [mode]   - init flash device on given SPI bus\n"
        "                                 and chip select\n"
        "sf read addr offset|partition len      - read `len' bytes starting at\n"
@@ -615,6 +626,14 @@ U_BOOT_CMD(
        "                                         at `addr' to flash at `offset'\n"
        "                                         or to start of mtd `partition'\n"
        "sf protect lock/unlock sector len      - protect/unprotect 'len' bytes starting\n"
-       "                                         at address 'sector'\n"
-       SF_TEST_HELP
+       "                                         at address 'sector'"
+#ifdef CONFIG_CMD_SF_TEST
+       "\nsf test offset len           - run a very basic destructive test"
+#endif
+#endif /* CONFIG_SYS_LONGHELP */
+       ;
+
+U_BOOT_CMD(
+       sf,     5,      1,      do_spi_flash,
+       "SPI flash sub-system", long_help
 );