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(struct cmd_tbl *cmdtp, int flag, int argc,
 362                           char *const argv[])
 363{
 364        printf("%s\n", mtd->name);
 365        return 0;
 366}
 367
 368static int do_onenand_bad(struct cmd_tbl *cmdtp, int flag, int argc,
 369                          char *const argv[])
 370{
 371        ulong ofs;
 372
 373        mtd = &onenand_mtd;
 374        /* Currently only one OneNAND device is supported */
 375        printf("\nDevice %d bad blocks:\n", 0);
 376        for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) {
 377                if (mtd_block_isbad(mtd, ofs))
 378                        printf("  %08x\n", (u32)ofs);
 379        }
 380
 381        return 0;
 382}
 383
 384static int do_onenand_read(struct cmd_tbl *cmdtp, int flag, int argc,
 385                           char *const argv[])
 386{
 387        char *s;
 388        int oob = 0;
 389        ulong addr, ofs;
 390        size_t len;
 391        int ret = 0;
 392        size_t retlen = 0;
 393
 394        if (argc < 3)
 395                return CMD_RET_USAGE;
 396
 397        s = strchr(argv[0], '.');
 398        if ((s != NULL) && (!strcmp(s, ".oob")))
 399                oob = 1;
 400
 401        addr = (ulong)simple_strtoul(argv[1], NULL, 16);
 402
 403        printf("\nOneNAND read: ");
 404        if (arg_off_size_onenand(argc - 2, argv + 2, &ofs, &len) != 0)
 405                return 1;
 406
 407        ret = onenand_block_read(ofs, len, &retlen, (u8 *)addr, oob);
 408
 409        printf(" %d bytes read: %s\n", retlen, ret ? "ERROR" : "OK");
 410
 411        return ret == 0 ? 0 : 1;
 412}
 413
 414static int do_onenand_write(struct cmd_tbl *cmdtp, int flag, int argc,
 415                            char *const argv[])
 416{
 417        ulong addr, ofs;
 418        size_t len;
 419        int ret = 0, withoob = 0;
 420        size_t retlen = 0;
 421
 422        if (argc < 3)
 423                return CMD_RET_USAGE;
 424
 425        if (strncmp(argv[0] + 6, "yaffs", 5) == 0)
 426                withoob = 1;
 427
 428        addr = (ulong)simple_strtoul(argv[1], NULL, 16);
 429
 430        printf("\nOneNAND write: ");
 431        if (arg_off_size_onenand(argc - 2, argv + 2, &ofs, &len) != 0)
 432                return 1;
 433
 434        ret = onenand_block_write(ofs, len, &retlen, (u8 *)addr, withoob);
 435
 436        printf(" %d bytes written: %s\n", retlen, ret ? "ERROR" : "OK");
 437
 438        return ret == 0 ? 0 : 1;
 439}
 440
 441static int do_onenand_erase(struct cmd_tbl *cmdtp, int flag, int argc,
 442                            char *const argv[])
 443{
 444        ulong ofs;
 445        int ret = 0;
 446        size_t len;
 447        int force;
 448
 449        /*
 450         * Syntax is:
 451         *   0       1     2       3    4
 452         *   onenand erase [force] [off size]
 453         */
 454        argc--;
 455        argv++;
 456        if (argc)
 457        {
 458                if (!strcmp("force", argv[0]))
 459                {
 460                        force = 1;
 461                        argc--;
 462                        argv++;
 463                }
 464        }
 465        printf("\nOneNAND erase: ");
 466
 467        /* skip first two or three arguments, look for offset and size */
 468        if (arg_off_size_onenand(argc, argv, &ofs, &len) != 0)
 469                return 1;
 470
 471        ret = onenand_block_erase(ofs, len, force);
 472
 473        printf("%s\n", ret ? "ERROR" : "OK");
 474
 475        return ret == 0 ? 0 : 1;
 476}
 477
 478static int do_onenand_test(struct cmd_tbl *cmdtp, int flag, int argc,
 479                           char *const argv[])
 480{
 481        ulong ofs;
 482        int ret = 0;
 483        size_t len;
 484
 485        /*
 486         * Syntax is:
 487         *   0       1     2       3    4
 488         *   onenand test [force] [off size]
 489         */
 490
 491        printf("\nOneNAND test: ");
 492
 493        /* skip first two or three arguments, look for offset and size */
 494        if (arg_off_size_onenand(argc - 1, argv + 1, &ofs, &len) != 0)
 495                return 1;
 496
 497        ret = onenand_block_test(ofs, len);
 498
 499        printf("%s\n", ret ? "ERROR" : "OK");
 500
 501        return ret == 0 ? 0 : 1;
 502}
 503
 504static int do_onenand_dump(struct cmd_tbl *cmdtp, int flag, int argc,
 505                           char *const argv[])
 506{
 507        ulong ofs;
 508        int ret = 0;
 509        char *s;
 510
 511        if (argc < 2)
 512                return CMD_RET_USAGE;
 513
 514        s = strchr(argv[0], '.');
 515        ofs = (int)simple_strtoul(argv[1], NULL, 16);
 516
 517        if (s != NULL && strcmp(s, ".oob") == 0)
 518                ret = onenand_dump(mtd, ofs, 1);
 519        else
 520                ret = onenand_dump(mtd, ofs, 0);
 521
 522        return ret == 0 ? 1 : 0;
 523}
 524
 525static int do_onenand_markbad(struct cmd_tbl *cmdtp, int flag, int argc,
 526                              char *const argv[])
 527{
 528        int ret = 0;
 529        ulong addr;
 530
 531        argc -= 2;
 532        argv += 2;
 533
 534        if (argc <= 0)
 535                return CMD_RET_USAGE;
 536
 537        while (argc > 0) {
 538                addr = simple_strtoul(*argv, NULL, 16);
 539
 540                if (mtd_block_markbad(mtd, addr)) {
 541                        printf("block 0x%08lx NOT marked "
 542                                "as bad! ERROR %d\n",
 543                                addr, ret);
 544                        ret = 1;
 545                } else {
 546                        printf("block 0x%08lx successfully "
 547                                "marked as bad\n",
 548                                addr);
 549                }
 550                --argc;
 551                ++argv;
 552        }
 553        return ret;
 554}
 555
 556static struct cmd_tbl cmd_onenand_sub[] = {
 557        U_BOOT_CMD_MKENT(info, 1, 0, do_onenand_info, "", ""),
 558        U_BOOT_CMD_MKENT(bad, 1, 0, do_onenand_bad, "", ""),
 559        U_BOOT_CMD_MKENT(read, 4, 0, do_onenand_read, "", ""),
 560        U_BOOT_CMD_MKENT(write, 4, 0, do_onenand_write, "", ""),
 561        U_BOOT_CMD_MKENT(write.yaffs, 4, 0, do_onenand_write, "", ""),
 562        U_BOOT_CMD_MKENT(erase, 3, 0, do_onenand_erase, "", ""),
 563        U_BOOT_CMD_MKENT(test, 3, 0, do_onenand_test, "", ""),
 564        U_BOOT_CMD_MKENT(dump, 2, 0, do_onenand_dump, "", ""),
 565        U_BOOT_CMD_MKENT(markbad, CONFIG_SYS_MAXARGS, 0, do_onenand_markbad, "", ""),
 566};
 567
 568#ifdef CONFIG_NEEDS_MANUAL_RELOC
 569void onenand_reloc(void) {
 570        fixup_cmdtable(cmd_onenand_sub, ARRAY_SIZE(cmd_onenand_sub));
 571}
 572#endif
 573
 574static int do_onenand(struct cmd_tbl *cmdtp, int flag, int argc,
 575                      char *const argv[])
 576{
 577        struct cmd_tbl *c;
 578
 579        if (argc < 2)
 580                return CMD_RET_USAGE;
 581
 582        mtd = &onenand_mtd;
 583
 584        /* Strip off leading 'onenand' command argument */
 585        argc--;
 586        argv++;
 587
 588        c = find_cmd_tbl(argv[0], &cmd_onenand_sub[0], ARRAY_SIZE(cmd_onenand_sub));
 589
 590        if (c)
 591                return c->cmd(cmdtp, flag, argc, argv);
 592        else
 593                return CMD_RET_USAGE;
 594}
 595
 596U_BOOT_CMD(
 597        onenand,        CONFIG_SYS_MAXARGS,     1,      do_onenand,
 598        "OneNAND sub-system",
 599        "info - show available OneNAND devices\n"
 600        "onenand bad - show bad blocks\n"
 601        "onenand read[.oob] addr off size\n"
 602        "onenand write[.yaffs] addr off size\n"
 603        "    read/write 'size' bytes starting at offset 'off'\n"
 604        "    to/from memory address 'addr', skipping bad blocks.\n"
 605        "onenand erase [force] [off size] - erase 'size' bytes from\n"
 606        "onenand test [off size] - test 'size' bytes from\n"
 607        "    offset 'off' (entire device if not specified)\n"
 608        "onenand dump[.oob] off - dump page\n"
 609        "onenand markbad off [...] - mark bad block(s) at offset (UNSAFE)"
 610);
 611