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