uboot/cmd/sf.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Command for accessing SPI flash.
   4 *
   5 * Copyright (C) 2008 Atmel Corporation
   6 */
   7
   8#include <common.h>
   9#include <command.h>
  10#include <div64.h>
  11#include <dm.h>
  12#include <flash.h>
  13#include <log.h>
  14#include <malloc.h>
  15#include <mapmem.h>
  16#include <spi.h>
  17#include <spi_flash.h>
  18#include <asm/cache.h>
  19#include <jffs2/jffs2.h>
  20#include <linux/mtd/mtd.h>
  21
  22#include <asm/io.h>
  23#include <dm/device-internal.h>
  24
  25#include "legacy-mtd-utils.h"
  26
  27static struct spi_flash *flash;
  28
  29/*
  30 * This function computes the length argument for the erase command.
  31 * The length on which the command is to operate can be given in two forms:
  32 * 1. <cmd> offset len  - operate on <'offset',  'len')
  33 * 2. <cmd> offset +len - operate on <'offset',  'round_up(len)')
  34 * If the second form is used and the length doesn't fall on the
  35 * sector boundary, than it will be adjusted to the next sector boundary.
  36 * If it isn't in the flash, the function will fail (return -1).
  37 * Input:
  38 *    arg: length specification (i.e. both command arguments)
  39 * Output:
  40 *    len: computed length for operation
  41 * Return:
  42 *    1: success
  43 *   -1: failure (bad format, bad address).
  44 */
  45static int sf_parse_len_arg(char *arg, ulong *len)
  46{
  47        char *ep;
  48        char round_up_len; /* indicates if the "+length" form used */
  49        ulong len_arg;
  50
  51        round_up_len = 0;
  52        if (*arg == '+') {
  53                round_up_len = 1;
  54                ++arg;
  55        }
  56
  57        len_arg = hextoul(arg, &ep);
  58        if (ep == arg || *ep != '\0')
  59                return -1;
  60
  61        if (round_up_len && flash->sector_size > 0)
  62                *len = ROUND(len_arg, flash->sector_size);
  63        else
  64                *len = len_arg;
  65
  66        return 1;
  67}
  68
  69/**
  70 * This function takes a byte length and a delta unit of time to compute the
  71 * approximate bytes per second
  72 *
  73 * @param len           amount of bytes currently processed
  74 * @param start_ms      start time of processing in ms
  75 * @return bytes per second if OK, 0 on error
  76 */
  77static ulong bytes_per_second(unsigned int len, ulong start_ms)
  78{
  79        /* less accurate but avoids overflow */
  80        if (len >= ((unsigned int) -1) / 1024)
  81                return len / (max(get_timer(start_ms) / 1024, 1UL));
  82        else
  83                return 1024 * len / max(get_timer(start_ms), 1UL);
  84}
  85
  86static int do_spi_flash_probe(int argc, char *const argv[])
  87{
  88        unsigned int bus = CONFIG_SF_DEFAULT_BUS;
  89        unsigned int cs = CONFIG_SF_DEFAULT_CS;
  90        /* In DM mode, defaults speed and mode will be taken from DT */
  91        unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
  92        unsigned int mode = CONFIG_SF_DEFAULT_MODE;
  93        char *endp;
  94#if CONFIG_IS_ENABLED(DM_SPI_FLASH)
  95        struct udevice *new, *bus_dev;
  96        int ret;
  97#else
  98        struct spi_flash *new;
  99#endif
 100
 101        if (argc >= 2) {
 102                cs = simple_strtoul(argv[1], &endp, 0);
 103                if (*argv[1] == 0 || (*endp != 0 && *endp != ':'))
 104                        return -1;
 105                if (*endp == ':') {
 106                        if (endp[1] == 0)
 107                                return -1;
 108
 109                        bus = cs;
 110                        cs = simple_strtoul(endp + 1, &endp, 0);
 111                        if (*endp != 0)
 112                                return -1;
 113                }
 114        }
 115
 116        if (argc >= 3) {
 117                speed = simple_strtoul(argv[2], &endp, 0);
 118                if (*argv[2] == 0 || *endp != 0)
 119                        return -1;
 120        }
 121        if (argc >= 4) {
 122                mode = hextoul(argv[3], &endp);
 123                if (*argv[3] == 0 || *endp != 0)
 124                        return -1;
 125        }
 126
 127#if CONFIG_IS_ENABLED(DM_SPI_FLASH)
 128        /* Remove the old device, otherwise probe will just be a nop */
 129        ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new);
 130        if (!ret) {
 131                device_remove(new, DM_REMOVE_NORMAL);
 132        }
 133        flash = NULL;
 134        ret = spi_flash_probe_bus_cs(bus, cs, speed, mode, &new);
 135        if (ret) {
 136                printf("Failed to initialize SPI flash at %u:%u (error %d)\n",
 137                       bus, cs, ret);
 138                return 1;
 139        }
 140
 141        flash = dev_get_uclass_priv(new);
 142#else
 143        if (flash)
 144                spi_flash_free(flash);
 145
 146        new = spi_flash_probe(bus, cs, speed, mode);
 147        flash = new;
 148        if (!new) {
 149                printf("Failed to initialize SPI flash at %u:%u\n", bus, cs);
 150                return 1;
 151        }
 152#endif
 153
 154        return 0;
 155}
 156
 157/**
 158 * Write a block of data to SPI flash, first checking if it is different from
 159 * what is already there.
 160 *
 161 * If the data being written is the same, then *skipped is incremented by len.
 162 *
 163 * @param flash         flash context pointer
 164 * @param offset        flash offset to write
 165 * @param len           number of bytes to write
 166 * @param buf           buffer to write from
 167 * @param cmp_buf       read buffer to use to compare data
 168 * @param skipped       Count of skipped data (incremented by this function)
 169 * @return NULL if OK, else a string containing the stage which failed
 170 */
 171static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset,
 172                size_t len, const char *buf, char *cmp_buf, size_t *skipped)
 173{
 174        char *ptr = (char *)buf;
 175
 176        debug("offset=%#x, sector_size=%#x, len=%#zx\n",
 177              offset, flash->sector_size, len);
 178        /* Read the entire sector so to allow for rewriting */
 179        if (spi_flash_read(flash, offset, flash->sector_size, cmp_buf))
 180                return "read";
 181        /* Compare only what is meaningful (len) */
 182        if (memcmp(cmp_buf, buf, len) == 0) {
 183                debug("Skip region %x size %zx: no change\n",
 184                      offset, len);
 185                *skipped += len;
 186                return NULL;
 187        }
 188        /* Erase the entire sector */
 189        if (spi_flash_erase(flash, offset, flash->sector_size))
 190                return "erase";
 191        /* If it's a partial sector, copy the data into the temp-buffer */
 192        if (len != flash->sector_size) {
 193                memcpy(cmp_buf, buf, len);
 194                ptr = cmp_buf;
 195        }
 196        /* Write one complete sector */
 197        if (spi_flash_write(flash, offset, flash->sector_size, ptr))
 198                return "write";
 199
 200        return NULL;
 201}
 202
 203/**
 204 * Update an area of SPI flash by erasing and writing any blocks which need
 205 * to change. Existing blocks with the correct data are left unchanged.
 206 *
 207 * @param flash         flash context pointer
 208 * @param offset        flash offset to write
 209 * @param len           number of bytes to write
 210 * @param buf           buffer to write from
 211 * @return 0 if ok, 1 on error
 212 */
 213static int spi_flash_update(struct spi_flash *flash, u32 offset,
 214                size_t len, const char *buf)
 215{
 216        const char *err_oper = NULL;
 217        char *cmp_buf;
 218        const char *end = buf + len;
 219        size_t todo;            /* number of bytes to do in this pass */
 220        size_t skipped = 0;     /* statistics */
 221        const ulong start_time = get_timer(0);
 222        size_t scale = 1;
 223        const char *start_buf = buf;
 224        ulong delta;
 225
 226        if (end - buf >= 200)
 227                scale = (end - buf) / 100;
 228        cmp_buf = memalign(ARCH_DMA_MINALIGN, flash->sector_size);
 229        if (cmp_buf) {
 230                ulong last_update = get_timer(0);
 231
 232                for (; buf < end && !err_oper; buf += todo, offset += todo) {
 233                        todo = min_t(size_t, end - buf, flash->sector_size);
 234                        if (get_timer(last_update) > 100) {
 235                                printf("   \rUpdating, %zu%% %lu B/s",
 236                                       100 - (end - buf) / scale,
 237                                        bytes_per_second(buf - start_buf,
 238                                                         start_time));
 239                                last_update = get_timer(0);
 240                        }
 241                        err_oper = spi_flash_update_block(flash, offset, todo,
 242                                        buf, cmp_buf, &skipped);
 243                }
 244        } else {
 245                err_oper = "malloc";
 246        }
 247        free(cmp_buf);
 248        putc('\r');
 249        if (err_oper) {
 250                printf("SPI flash failed in %s step\n", err_oper);
 251                return 1;
 252        }
 253
 254        delta = get_timer(start_time);
 255        printf("%zu bytes written, %zu bytes skipped", len - skipped,
 256               skipped);
 257        printf(" in %ld.%lds, speed %ld B/s\n",
 258               delta / 1000, delta % 1000, bytes_per_second(len, start_time));
 259
 260        return 0;
 261}
 262
 263static int do_spi_flash_read_write(int argc, char *const argv[])
 264{
 265        unsigned long addr;
 266        void *buf;
 267        char *endp;
 268        int ret = 1;
 269        int dev = 0;
 270        loff_t offset, len, maxsize;
 271
 272        if (argc < 3)
 273                return -1;
 274
 275        addr = hextoul(argv[1], &endp);
 276        if (*argv[1] == 0 || *endp != 0)
 277                return -1;
 278
 279        if (mtd_arg_off_size(argc - 2, &argv[2], &dev, &offset, &len,
 280                             &maxsize, MTD_DEV_TYPE_NOR, flash->size))
 281                return -1;
 282
 283        /* Consistency checking */
 284        if (offset + len > flash->size) {
 285                printf("ERROR: attempting %s past flash size (%#x)\n",
 286                       argv[0], flash->size);
 287                return 1;
 288        }
 289
 290        buf = map_physmem(addr, len, MAP_WRBACK);
 291        if (!buf && addr) {
 292                puts("Failed to map physical memory\n");
 293                return 1;
 294        }
 295
 296        if (strcmp(argv[0], "update") == 0) {
 297                ret = spi_flash_update(flash, offset, len, buf);
 298        } else if (strncmp(argv[0], "read", 4) == 0 ||
 299                        strncmp(argv[0], "write", 5) == 0) {
 300                int read;
 301
 302                read = strncmp(argv[0], "read", 4) == 0;
 303                if (read)
 304                        ret = spi_flash_read(flash, offset, len, buf);
 305                else
 306                        ret = spi_flash_write(flash, offset, len, buf);
 307
 308                printf("SF: %zu bytes @ %#x %s: ", (size_t)len, (u32)offset,
 309                       read ? "Read" : "Written");
 310                if (ret)
 311                        printf("ERROR %d\n", ret);
 312                else
 313                        printf("OK\n");
 314        }
 315
 316        unmap_physmem(buf, len);
 317
 318        return ret == 0 ? 0 : 1;
 319}
 320
 321static int do_spi_flash_erase(int argc, char *const argv[])
 322{
 323        int ret;
 324        int dev = 0;
 325        loff_t offset, len, maxsize;
 326        ulong size;
 327
 328        if (argc < 3)
 329                return -1;
 330
 331        if (mtd_arg_off(argv[1], &dev, &offset, &len, &maxsize,
 332                        MTD_DEV_TYPE_NOR, flash->size))
 333                return -1;
 334
 335        ret = sf_parse_len_arg(argv[2], &size);
 336        if (ret != 1)
 337                return -1;
 338
 339        /* Consistency checking */
 340        if (offset + size > flash->size) {
 341                printf("ERROR: attempting %s past flash size (%#x)\n",
 342                       argv[0], flash->size);
 343                return 1;
 344        }
 345
 346        ret = spi_flash_erase(flash, offset, size);
 347        printf("SF: %zu bytes @ %#x Erased: ", (size_t)size, (u32)offset);
 348        if (ret)
 349                printf("ERROR %d\n", ret);
 350        else
 351                printf("OK\n");
 352
 353        return ret == 0 ? 0 : 1;
 354}
 355
 356static int do_spi_protect(int argc, char *const argv[])
 357{
 358        int ret = 0;
 359        loff_t start, len;
 360        bool prot = false;
 361
 362        if (argc != 4)
 363                return -1;
 364
 365        if (!str2off(argv[2], &start)) {
 366                puts("start sector is not a valid number\n");
 367                return 1;
 368        }
 369
 370        if (!str2off(argv[3], &len)) {
 371                puts("len is not a valid number\n");
 372                return 1;
 373        }
 374
 375        if (strcmp(argv[1], "lock") == 0)
 376                prot = true;
 377        else if (strcmp(argv[1], "unlock") == 0)
 378                prot = false;
 379        else
 380                return -1;  /* Unknown parameter */
 381
 382        ret = spi_flash_protect(flash, start, len, prot);
 383
 384        return ret == 0 ? 0 : 1;
 385}
 386
 387#ifdef CONFIG_CMD_SF_TEST
 388enum {
 389        STAGE_ERASE,
 390        STAGE_CHECK,
 391        STAGE_WRITE,
 392        STAGE_READ,
 393
 394        STAGE_COUNT,
 395};
 396
 397static char *stage_name[STAGE_COUNT] = {
 398        "erase",
 399        "check",
 400        "write",
 401        "read",
 402};
 403
 404struct test_info {
 405        int stage;
 406        int bytes;
 407        unsigned base_ms;
 408        unsigned time_ms[STAGE_COUNT];
 409};
 410
 411static void show_time(struct test_info *test, int stage)
 412{
 413        uint64_t speed; /* KiB/s */
 414        int bps;        /* Bits per second */
 415
 416        speed = (long long)test->bytes * 1000;
 417        if (test->time_ms[stage])
 418                do_div(speed, test->time_ms[stage] * 1024);
 419        bps = speed * 8;
 420
 421        printf("%d %s: %u ticks, %d KiB/s %d.%03d Mbps\n", stage,
 422               stage_name[stage], test->time_ms[stage],
 423               (int)speed, bps / 1000, bps % 1000);
 424}
 425
 426static void spi_test_next_stage(struct test_info *test)
 427{
 428        test->time_ms[test->stage] = get_timer(test->base_ms);
 429        show_time(test, test->stage);
 430        test->base_ms = get_timer(0);
 431        test->stage++;
 432}
 433
 434/**
 435 * Run a test on the SPI flash
 436 *
 437 * @param flash         SPI flash to use
 438 * @param buf           Source buffer for data to write
 439 * @param len           Size of data to read/write
 440 * @param offset        Offset within flash to check
 441 * @param vbuf          Verification buffer
 442 * @return 0 if ok, -1 on error
 443 */
 444static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len,
 445                           ulong offset, uint8_t *vbuf)
 446{
 447        struct test_info test;
 448        int err, i;
 449
 450        printf("SPI flash test:\n");
 451        memset(&test, '\0', sizeof(test));
 452        test.base_ms = get_timer(0);
 453        test.bytes = len;
 454        err = spi_flash_erase(flash, offset, len);
 455        if (err) {
 456                printf("Erase failed (err = %d)\n", err);
 457                return -1;
 458        }
 459        spi_test_next_stage(&test);
 460
 461        err = spi_flash_read(flash, offset, len, vbuf);
 462        if (err) {
 463                printf("Check read failed (err = %d)\n", err);
 464                return -1;
 465        }
 466        for (i = 0; i < len; i++) {
 467                if (vbuf[i] != 0xff) {
 468                        printf("Check failed at %d\n", i);
 469                        print_buffer(i, vbuf + i, 1,
 470                                     min_t(uint, len - i, 0x40), 0);
 471                        return -1;
 472                }
 473        }
 474        spi_test_next_stage(&test);
 475
 476        err = spi_flash_write(flash, offset, len, buf);
 477        if (err) {
 478                printf("Write failed (err = %d)\n", err);
 479                return -1;
 480        }
 481        memset(vbuf, '\0', len);
 482        spi_test_next_stage(&test);
 483
 484        err = spi_flash_read(flash, offset, len, vbuf);
 485        if (err) {
 486                printf("Read failed (ret = %d)\n", err);
 487                return -1;
 488        }
 489        spi_test_next_stage(&test);
 490
 491        for (i = 0; i < len; i++) {
 492                if (buf[i] != vbuf[i]) {
 493                        printf("Verify failed at %d, good data:\n", i);
 494                        print_buffer(i, buf + i, 1,
 495                                     min_t(uint, len - i, 0x40), 0);
 496                        printf("Bad data:\n");
 497                        print_buffer(i, vbuf + i, 1,
 498                                     min_t(uint, len - i, 0x40), 0);
 499                        return -1;
 500                }
 501        }
 502        printf("Test passed\n");
 503        for (i = 0; i < STAGE_COUNT; i++)
 504                show_time(&test, i);
 505
 506        return 0;
 507}
 508
 509static int do_spi_flash_test(int argc, char *const argv[])
 510{
 511        unsigned long offset;
 512        unsigned long len;
 513        uint8_t *buf, *from;
 514        char *endp;
 515        uint8_t *vbuf;
 516        int ret;
 517
 518        if (argc < 3)
 519                return -1;
 520        offset = hextoul(argv[1], &endp);
 521        if (*argv[1] == 0 || *endp != 0)
 522                return -1;
 523        len = hextoul(argv[2], &endp);
 524        if (*argv[2] == 0 || *endp != 0)
 525                return -1;
 526
 527        vbuf = memalign(ARCH_DMA_MINALIGN, len);
 528        if (!vbuf) {
 529                printf("Cannot allocate memory (%lu bytes)\n", len);
 530                return 1;
 531        }
 532        buf = memalign(ARCH_DMA_MINALIGN, len);
 533        if (!buf) {
 534                free(vbuf);
 535                printf("Cannot allocate memory (%lu bytes)\n", len);
 536                return 1;
 537        }
 538
 539        from = map_sysmem(CONFIG_SYS_TEXT_BASE, 0);
 540        memcpy(buf, from, len);
 541        ret = spi_flash_test(flash, buf, len, offset, vbuf);
 542        free(vbuf);
 543        free(buf);
 544        if (ret) {
 545                printf("Test failed\n");
 546                return 1;
 547        }
 548
 549        return 0;
 550}
 551#endif /* CONFIG_CMD_SF_TEST */
 552
 553static int do_spi_flash(struct cmd_tbl *cmdtp, int flag, int argc,
 554                        char *const argv[])
 555{
 556        const char *cmd;
 557        int ret;
 558
 559        /* need at least two arguments */
 560        if (argc < 2)
 561                goto usage;
 562
 563        cmd = argv[1];
 564        --argc;
 565        ++argv;
 566
 567        if (strcmp(cmd, "probe") == 0) {
 568                ret = do_spi_flash_probe(argc, argv);
 569                goto done;
 570        }
 571
 572        /* The remaining commands require a selected device */
 573        if (!flash) {
 574                puts("No SPI flash selected. Please run `sf probe'\n");
 575                return 1;
 576        }
 577
 578        if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0 ||
 579            strcmp(cmd, "update") == 0)
 580                ret = do_spi_flash_read_write(argc, argv);
 581        else if (strcmp(cmd, "erase") == 0)
 582                ret = do_spi_flash_erase(argc, argv);
 583        else if (strcmp(cmd, "protect") == 0)
 584                ret = do_spi_protect(argc, argv);
 585#ifdef CONFIG_CMD_SF_TEST
 586        else if (!strcmp(cmd, "test"))
 587                ret = do_spi_flash_test(argc, argv);
 588#endif
 589        else
 590                ret = -1;
 591
 592done:
 593        if (ret != -1)
 594                return ret;
 595
 596usage:
 597        return CMD_RET_USAGE;
 598}
 599
 600#ifdef CONFIG_CMD_SF_TEST
 601#define SF_TEST_HELP "\nsf test offset len              " \
 602                "- run a very basic destructive test"
 603#else
 604#define SF_TEST_HELP
 605#endif
 606
 607U_BOOT_CMD(
 608        sf,     5,      1,      do_spi_flash,
 609        "SPI flash sub-system",
 610        "probe [[bus:]cs] [hz] [mode]   - init flash device on given SPI bus\n"
 611        "                                 and chip select\n"
 612        "sf read addr offset|partition len      - read `len' bytes starting at\n"
 613        "                                         `offset' or from start of mtd\n"
 614        "                                         `partition'to memory at `addr'\n"
 615        "sf write addr offset|partition len     - write `len' bytes from memory\n"
 616        "                                         at `addr' to flash at `offset'\n"
 617        "                                         or to start of mtd `partition'\n"
 618        "sf erase offset|partition [+]len       - erase `len' bytes from `offset'\n"
 619        "                                         or from start of mtd `partition'\n"
 620        "                                        `+len' round up `len' to block size\n"
 621        "sf update addr offset|partition len    - erase and write `len' bytes from memory\n"
 622        "                                         at `addr' to flash at `offset'\n"
 623        "                                         or to start of mtd `partition'\n"
 624        "sf protect lock/unlock sector len      - protect/unprotect 'len' bytes starting\n"
 625        "                                         at address 'sector'\n"
 626        SF_TEST_HELP
 627);
 628