uboot/cmd/onenand.c
<<
>>
Prefs
   1/*
   2 *  U-Boot command for OneNAND support
   3 *
   4 *  Copyright (C) 2005-2008 Samsung Electronics
   5 *  Kyungmin Park <kyungmin.park@samsung.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <common.h>
  13#include <command.h>
  14#include <malloc.h>
  15
  16#include <linux/compat.h>
  17#include <linux/mtd/mtd.h>
  18#include <linux/mtd/onenand.h>
  19
  20#include <asm/io.h>
  21
  22static struct mtd_info *mtd;
  23
  24static loff_t next_ofs;
  25static loff_t skip_ofs;
  26
  27static int arg_off_size_onenand(int argc, char * const argv[], ulong *off,
  28                                size_t *size)
  29{
  30        if (argc >= 1) {
  31                if (!(str2long(argv[0], off))) {
  32                        printf("'%s' is not a number\n", argv[0]);
  33                        return -1;
  34                }
  35        } else {
  36                *off = 0;
  37        }
  38
  39        if (argc >= 2) {
  40                if (!(str2long(argv[1], (ulong *)size))) {
  41                        printf("'%s' is not a number\n", argv[1]);
  42                        return -1;
  43                }
  44        } else {
  45                *size = mtd->size - *off;
  46        }
  47
  48        if ((*off + *size) > mtd->size) {
  49                printf("total chip size (0x%llx) exceeded!\n", mtd->size);
  50                return -1;
  51        }
  52
  53        if (*size == mtd->size)
  54                puts("whole chip\n");
  55        else
  56                printf("offset 0x%lx, size 0x%x\n", *off, *size);
  57
  58        return 0;
  59}
  60
  61static int onenand_block_read(loff_t from, size_t len,
  62                              size_t *retlen, u_char *buf, int oob)
  63{
  64        struct onenand_chip *this = mtd->priv;
  65        int blocks = (int) len >> this->erase_shift;
  66        int blocksize = (1 << this->erase_shift);
  67        loff_t ofs = from;
  68        struct mtd_oob_ops ops = {
  69                .retlen         = 0,
  70        };
  71        int ret;
  72
  73        if (oob)
  74                ops.ooblen = blocksize;
  75        else
  76                ops.len = blocksize;
  77
  78        while (blocks) {
  79                ret = mtd_block_isbad(mtd, ofs);
  80                if (ret) {
  81                        printk("Bad blocks %d at 0x%x\n",
  82                               (u32)(ofs >> this->erase_shift), (u32)ofs);
  83                        ofs += blocksize;
  84                        continue;
  85                }
  86
  87                if (oob)
  88                        ops.oobbuf = buf;
  89                else
  90                        ops.datbuf = buf;
  91
  92                ops.retlen = 0;
  93                ret = mtd_read_oob(mtd, ofs, &ops);
  94                if (ret) {
  95                        printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
  96                        ofs += blocksize;
  97                        continue;
  98                }
  99                ofs += blocksize;
 100                buf += blocksize;
 101                blocks--;
 102                *retlen += ops.retlen;
 103        }
 104
 105        return 0;
 106}
 107
 108static int onenand_write_oneblock_withoob(loff_t to, const u_char * buf,
 109                                          size_t *retlen)
 110{
 111        struct mtd_oob_ops ops = {
 112                .len = mtd->writesize,
 113                .ooblen = mtd->oobsize,
 114                .mode = MTD_OPS_AUTO_OOB,
 115        };
 116        int page, ret = 0;
 117        for (page = 0; page < (mtd->erasesize / mtd->writesize); page ++) {
 118                ops.datbuf = (u_char *)buf;
 119                buf += mtd->writesize;
 120                ops.oobbuf = (u_char *)buf;
 121                buf += mtd->oobsize;
 122                ret = mtd_write_oob(mtd, to, &ops);
 123                if (ret)
 124                        break;
 125                to += mtd->writesize;
 126        }
 127
 128        *retlen = (ret) ? 0 : mtd->erasesize;
 129        return ret;
 130}
 131
 132static int onenand_block_write(loff_t to, size_t len,
 133                               size_t *retlen, const u_char * buf, int withoob)
 134{
 135        struct onenand_chip *this = mtd->priv;
 136        int blocks = len >> this->erase_shift;
 137        int blocksize = (1 << this->erase_shift);
 138        loff_t ofs;
 139        size_t _retlen = 0;
 140        int ret;
 141
 142        if ((to & (mtd->writesize - 1)) != 0) {
 143                printf("Attempt to write non block-aligned data\n");
 144                *retlen = 0;
 145                return 1;
 146        }
 147
 148        if (to == next_ofs) {
 149                next_ofs = to + len;
 150                to += skip_ofs;
 151        } else {
 152                next_ofs = to + len;
 153                skip_ofs = 0;
 154        }
 155        ofs = to;
 156
 157        while (blocks) {
 158                ret = mtd_block_isbad(mtd, ofs);
 159                if (ret) {
 160                        printk("Bad blocks %d at 0x%x\n",
 161                               (u32)(ofs >> this->erase_shift), (u32)ofs);
 162                        skip_ofs += blocksize;
 163                        goto next;
 164                }
 165
 166                if (!withoob)
 167                        ret = mtd_write(mtd, ofs, blocksize, &_retlen, buf);
 168                else
 169                        ret = onenand_write_oneblock_withoob(ofs, buf, &_retlen);
 170                if (ret) {
 171                        printk("Write failed 0x%x, %d", (u32)ofs, ret);
 172                        skip_ofs += blocksize;
 173                        goto next;
 174                }
 175
 176                buf += blocksize;
 177                blocks--;
 178                *retlen += _retlen;
 179next:
 180                ofs += blocksize;
 181        }
 182
 183        return 0;
 184}
 185
 186static int onenand_block_erase(u32 start, u32 size, int force)
 187{
 188        struct onenand_chip *this = mtd->priv;
 189        struct erase_info instr = {
 190                .callback       = NULL,
 191        };
 192        loff_t ofs;
 193        int ret;
 194        int blocksize = 1 << this->erase_shift;
 195
 196        for (ofs = start; ofs < (start + size); ofs += blocksize) {
 197                ret = mtd_block_isbad(mtd, ofs);
 198                if (ret && !force) {
 199                        printf("Skip erase bad block %d at 0x%x\n",
 200                               (u32)(ofs >> this->erase_shift), (u32)ofs);
 201                        continue;
 202                }
 203
 204                instr.addr = ofs;
 205                instr.len = blocksize;
 206                instr.priv = force;
 207                instr.mtd = mtd;
 208                ret = mtd_erase(mtd, &instr);
 209                if (ret) {
 210                        printf("erase failed block %d at 0x%x\n",
 211                               (u32)(ofs >> this->erase_shift), (u32)ofs);
 212                        continue;
 213                }
 214        }
 215
 216        return 0;
 217}
 218
 219static int onenand_block_test(u32 start, u32 size)
 220{
 221        struct onenand_chip *this = mtd->priv;
 222        struct erase_info instr = {
 223                .callback       = NULL,
 224                .priv           = 0,
 225        };
 226
 227        int blocks;
 228        loff_t ofs;
 229        int blocksize = 1 << this->erase_shift;
 230        int start_block, end_block;
 231        size_t retlen;
 232        u_char *buf;
 233        u_char *verify_buf;
 234        int ret;
 235
 236        buf = malloc(blocksize);
 237        if (!buf) {
 238                printf("Not enough malloc space available!\n");
 239                return -1;
 240        }
 241
 242        verify_buf = malloc(blocksize);
 243        if (!verify_buf) {
 244                printf("Not enough malloc space available!\n");
 245                return -1;
 246        }
 247
 248        start_block = start >> this->erase_shift;
 249        end_block = (start + size) >> this->erase_shift;
 250
 251        /* Protect boot-loader from badblock testing */
 252        if (start_block < 2)
 253                start_block = 2;
 254
 255        if (end_block > (mtd->size >> this->erase_shift))
 256                end_block = mtd->size >> this->erase_shift;
 257
 258        blocks = start_block;
 259        ofs = start;
 260        while (blocks < end_block) {
 261                printf("\rTesting block %d at 0x%x", (u32)(ofs >> this->erase_shift), (u32)ofs);
 262
 263                ret = mtd_block_isbad(mtd, ofs);
 264                if (ret) {
 265                        printf("Skip erase bad block %d at 0x%x\n",
 266                               (u32)(ofs >> this->erase_shift), (u32)ofs);
 267                        goto next;
 268                }
 269
 270                instr.addr = ofs;
 271                instr.len = blocksize;
 272                ret = mtd_erase(mtd, &instr);
 273                if (ret) {
 274                        printk("Erase failed 0x%x, %d\n", (u32)ofs, ret);
 275                        goto next;
 276                }
 277
 278                ret = mtd_write(mtd, ofs, blocksize, &retlen, buf);
 279                if (ret) {
 280                        printk("Write failed 0x%x, %d\n", (u32)ofs, ret);
 281                        goto next;
 282                }
 283
 284                ret = mtd_read(mtd, ofs, blocksize, &retlen, verify_buf);
 285                if (ret) {
 286                        printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
 287                        goto next;
 288                }
 289
 290                if (memcmp(buf, verify_buf, blocksize))
 291                        printk("\nRead/Write test failed at 0x%x\n", (u32)ofs);
 292
 293next:
 294                ofs += blocksize;
 295                blocks++;
 296        }
 297        printf("...Done\n");
 298
 299        free(buf);
 300        free(verify_buf);
 301
 302        return 0;
 303}
 304
 305static int onenand_dump(struct mtd_info *mtd, ulong off, int only_oob)
 306{
 307        int i;
 308        u_char *datbuf, *oobbuf, *p;
 309        struct mtd_oob_ops ops;
 310        loff_t addr;
 311
 312        datbuf = malloc(mtd->writesize + mtd->oobsize);
 313        oobbuf = malloc(mtd->oobsize);
 314        if (!datbuf || !oobbuf) {
 315                puts("No memory for page buffer\n");
 316                return 1;
 317        }
 318        off &= ~(mtd->writesize - 1);
 319        addr = (loff_t) off;
 320        memset(&ops, 0, sizeof(ops));
 321        ops.datbuf = datbuf;
 322        ops.oobbuf = oobbuf;
 323        ops.len = mtd->writesize;
 324        ops.ooblen = mtd->oobsize;
 325        ops.retlen = 0;
 326        i = mtd_read_oob(mtd, addr, &ops);
 327        if (i < 0) {
 328                printf("Error (%d) reading page %08lx\n", i, off);
 329                free(datbuf);
 330                free(oobbuf);
 331                return 1;
 332        }
 333        printf("Page %08lx dump:\n", off);
 334        i = mtd->writesize >> 4;
 335        p = datbuf;
 336
 337        while (i--) {
 338                if (!only_oob)
 339                        printf("\t%02x %02x %02x %02x %02x %02x %02x %02x"
 340                               "  %02x %02x %02x %02x %02x %02x %02x %02x\n",
 341                               p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
 342                               p[8], p[9], p[10], p[11], p[12], p[13], p[14],
 343                               p[15]);
 344                p += 16;
 345        }
 346        puts("OOB:\n");
 347        i = mtd->oobsize >> 3;
 348        p = oobbuf;
 349
 350        while (i--) {
 351                printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n",
 352                       p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
 353                p += 8;
 354        }
 355        free(datbuf);
 356        free(oobbuf);
 357
 358        return 0;
 359}
 360
 361static int do_onenand_info(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 362{
 363        printf("%s\n", mtd->name);
 364        return 0;
 365}
 366
 367static int do_onenand_bad(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 368{
 369        ulong ofs;
 370
 371        mtd = &onenand_mtd;
 372        /* Currently only one OneNAND device is supported */
 373        printf("\nDevice %d bad blocks:\n", 0);
 374        for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) {
 375                if (mtd_block_isbad(mtd, ofs))
 376                        printf("  %08x\n", (u32)ofs);
 377        }
 378
 379        return 0;
 380}
 381
 382static int do_onenand_read(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 383{
 384        char *s;
 385        int oob = 0;
 386        ulong addr, ofs;
 387        size_t len;
 388        int ret = 0;
 389        size_t retlen = 0;
 390
 391        if (argc < 3)
 392                return CMD_RET_USAGE;
 393
 394        s = strchr(argv[0], '.');
 395        if ((s != NULL) && (!strcmp(s, ".oob")))
 396                oob = 1;
 397
 398        addr = (ulong)simple_strtoul(argv[1], NULL, 16);
 399
 400        printf("\nOneNAND read: ");
 401        if (arg_off_size_onenand(argc - 2, argv + 2, &ofs, &len) != 0)
 402                return 1;
 403
 404        ret = onenand_block_read(ofs, len, &retlen, (u8 *)addr, oob);
 405
 406        printf(" %d bytes read: %s\n", retlen, ret ? "ERROR" : "OK");
 407
 408        return ret == 0 ? 0 : 1;
 409}
 410
 411static int do_onenand_write(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 412{
 413        ulong addr, ofs;
 414        size_t len;
 415        int ret = 0, withoob = 0;
 416        size_t retlen = 0;
 417
 418        if (argc < 3)
 419                return CMD_RET_USAGE;
 420
 421        if (strncmp(argv[0] + 6, "yaffs", 5) == 0)
 422                withoob = 1;
 423
 424        addr = (ulong)simple_strtoul(argv[1], NULL, 16);
 425
 426        printf("\nOneNAND write: ");
 427        if (arg_off_size_onenand(argc - 2, argv + 2, &ofs, &len) != 0)
 428                return 1;
 429
 430        ret = onenand_block_write(ofs, len, &retlen, (u8 *)addr, withoob);
 431
 432        printf(" %d bytes written: %s\n", retlen, ret ? "ERROR" : "OK");
 433
 434        return ret == 0 ? 0 : 1;
 435}
 436
 437static int do_onenand_erase(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 438{
 439        ulong ofs;
 440        int ret = 0;
 441        size_t len;
 442        int force;
 443
 444        /*
 445         * Syntax is:
 446         *   0       1     2       3    4
 447         *   onenand erase [force] [off size]
 448         */
 449        argc--;
 450        argv++;
 451        if (argc)
 452        {
 453                if (!strcmp("force", argv[0]))
 454                {
 455                        force = 1;
 456                        argc--;
 457                        argv++;
 458                }
 459        }
 460        printf("\nOneNAND erase: ");
 461
 462        /* skip first two or three arguments, look for offset and size */
 463        if (arg_off_size_onenand(argc, argv, &ofs, &len) != 0)
 464                return 1;
 465
 466        ret = onenand_block_erase(ofs, len, force);
 467
 468        printf("%s\n", ret ? "ERROR" : "OK");
 469
 470        return ret == 0 ? 0 : 1;
 471}
 472
 473static int do_onenand_test(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 474{
 475        ulong ofs;
 476        int ret = 0;
 477        size_t len;
 478
 479        /*
 480         * Syntax is:
 481         *   0       1     2       3    4
 482         *   onenand test [force] [off size]
 483         */
 484
 485        printf("\nOneNAND test: ");
 486
 487        /* skip first two or three arguments, look for offset and size */
 488        if (arg_off_size_onenand(argc - 1, argv + 1, &ofs, &len) != 0)
 489                return 1;
 490
 491        ret = onenand_block_test(ofs, len);
 492
 493        printf("%s\n", ret ? "ERROR" : "OK");
 494
 495        return ret == 0 ? 0 : 1;
 496}
 497
 498static int do_onenand_dump(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 499{
 500        ulong ofs;
 501        int ret = 0;
 502        char *s;
 503
 504        if (argc < 2)
 505                return CMD_RET_USAGE;
 506
 507        s = strchr(argv[0], '.');
 508        ofs = (int)simple_strtoul(argv[1], NULL, 16);
 509
 510        if (s != NULL && strcmp(s, ".oob") == 0)
 511                ret = onenand_dump(mtd, ofs, 1);
 512        else
 513                ret = onenand_dump(mtd, ofs, 0);
 514
 515        return ret == 0 ? 1 : 0;
 516}
 517
 518static int do_onenand_markbad(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 519{
 520        int ret = 0;
 521        ulong addr;
 522
 523        argc -= 2;
 524        argv += 2;
 525
 526        if (argc <= 0)
 527                return CMD_RET_USAGE;
 528
 529        while (argc > 0) {
 530                addr = simple_strtoul(*argv, NULL, 16);
 531
 532                if (mtd_block_markbad(mtd, addr)) {
 533                        printf("block 0x%08lx NOT marked "
 534                                "as bad! ERROR %d\n",
 535                                addr, ret);
 536                        ret = 1;
 537                } else {
 538                        printf("block 0x%08lx successfully "
 539                                "marked as bad\n",
 540                                addr);
 541                }
 542                --argc;
 543                ++argv;
 544        }
 545        return ret;
 546}
 547
 548static cmd_tbl_t cmd_onenand_sub[] = {
 549        U_BOOT_CMD_MKENT(info, 1, 0, do_onenand_info, "", ""),
 550        U_BOOT_CMD_MKENT(bad, 1, 0, do_onenand_bad, "", ""),
 551        U_BOOT_CMD_MKENT(read, 4, 0, do_onenand_read, "", ""),
 552        U_BOOT_CMD_MKENT(write, 4, 0, do_onenand_write, "", ""),
 553        U_BOOT_CMD_MKENT(write.yaffs, 4, 0, do_onenand_write, "", ""),
 554        U_BOOT_CMD_MKENT(erase, 3, 0, do_onenand_erase, "", ""),
 555        U_BOOT_CMD_MKENT(test, 3, 0, do_onenand_test, "", ""),
 556        U_BOOT_CMD_MKENT(dump, 2, 0, do_onenand_dump, "", ""),
 557        U_BOOT_CMD_MKENT(markbad, CONFIG_SYS_MAXARGS, 0, do_onenand_markbad, "", ""),
 558};
 559
 560#ifdef CONFIG_NEEDS_MANUAL_RELOC
 561void onenand_reloc(void) {
 562        fixup_cmdtable(cmd_onenand_sub, ARRAY_SIZE(cmd_onenand_sub));
 563}
 564#endif
 565
 566static int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 567{
 568        cmd_tbl_t *c;
 569
 570        if (argc < 2)
 571                return CMD_RET_USAGE;
 572
 573        mtd = &onenand_mtd;
 574
 575        /* Strip off leading 'onenand' command argument */
 576        argc--;
 577        argv++;
 578
 579        c = find_cmd_tbl(argv[0], &cmd_onenand_sub[0], ARRAY_SIZE(cmd_onenand_sub));
 580
 581        if (c)
 582                return c->cmd(cmdtp, flag, argc, argv);
 583        else
 584                return CMD_RET_USAGE;
 585}
 586
 587U_BOOT_CMD(
 588        onenand,        CONFIG_SYS_MAXARGS,     1,      do_onenand,
 589        "OneNAND sub-system",
 590        "info - show available OneNAND devices\n"
 591        "onenand bad - show bad blocks\n"
 592        "onenand read[.oob] addr off size\n"
 593        "onenand write[.yaffs] addr off size\n"
 594        "    read/write 'size' bytes starting at offset 'off'\n"
 595        "    to/from memory address 'addr', skipping bad blocks.\n"
 596        "onenand erase [force] [off size] - erase 'size' bytes from\n"
 597        "onenand test [off size] - test 'size' bytes from\n"
 598        "    offset 'off' (entire device if not specified)\n"
 599        "onenand dump[.oob] off - dump page\n"
 600        "onenand markbad off [...] - mark bad block(s) at offset (UNSAFE)"
 601);
 602