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