uboot/common/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 == next_ofs) {
 143                next_ofs = to + len;
 144                to += skip_ofs;
 145        } else {
 146                next_ofs = to + len;
 147                skip_ofs = 0;
 148        }
 149        ofs = to;
 150
 151        while (blocks) {
 152                ret = mtd_block_isbad(mtd, ofs);
 153                if (ret) {
 154                        printk("Bad blocks %d at 0x%x\n",
 155                               (u32)(ofs >> this->erase_shift), (u32)ofs);
 156                        skip_ofs += blocksize;
 157                        goto next;
 158                }
 159
 160                if (!withoob)
 161                        ret = mtd_write(mtd, ofs, blocksize, &_retlen, buf);
 162                else
 163                        ret = onenand_write_oneblock_withoob(ofs, buf, &_retlen);
 164                if (ret) {
 165                        printk("Write failed 0x%x, %d", (u32)ofs, ret);
 166                        skip_ofs += blocksize;
 167                        goto next;
 168                }
 169
 170                buf += blocksize;
 171                blocks--;
 172                *retlen += _retlen;
 173next:
 174                ofs += blocksize;
 175        }
 176
 177        return 0;
 178}
 179
 180static int onenand_block_erase(u32 start, u32 size, int force)
 181{
 182        struct onenand_chip *this = mtd->priv;
 183        struct erase_info instr = {
 184                .callback       = NULL,
 185        };
 186        loff_t ofs;
 187        int ret;
 188        int blocksize = 1 << this->erase_shift;
 189
 190        for (ofs = start; ofs < (start + size); ofs += blocksize) {
 191                ret = mtd_block_isbad(mtd, ofs);
 192                if (ret && !force) {
 193                        printf("Skip erase bad block %d at 0x%x\n",
 194                               (u32)(ofs >> this->erase_shift), (u32)ofs);
 195                        continue;
 196                }
 197
 198                instr.addr = ofs;
 199                instr.len = blocksize;
 200                instr.priv = force;
 201                instr.mtd = mtd;
 202                ret = mtd_erase(mtd, &instr);
 203                if (ret) {
 204                        printf("erase failed block %d at 0x%x\n",
 205                               (u32)(ofs >> this->erase_shift), (u32)ofs);
 206                        continue;
 207                }
 208        }
 209
 210        return 0;
 211}
 212
 213static int onenand_block_test(u32 start, u32 size)
 214{
 215        struct onenand_chip *this = mtd->priv;
 216        struct erase_info instr = {
 217                .callback       = NULL,
 218                .priv           = 0,
 219        };
 220
 221        int blocks;
 222        loff_t ofs;
 223        int blocksize = 1 << this->erase_shift;
 224        int start_block, end_block;
 225        size_t retlen;
 226        u_char *buf;
 227        u_char *verify_buf;
 228        int ret;
 229
 230        buf = malloc(blocksize);
 231        if (!buf) {
 232                printf("Not enough malloc space available!\n");
 233                return -1;
 234        }
 235
 236        verify_buf = malloc(blocksize);
 237        if (!verify_buf) {
 238                printf("Not enough malloc space available!\n");
 239                return -1;
 240        }
 241
 242        start_block = start >> this->erase_shift;
 243        end_block = (start + size) >> this->erase_shift;
 244
 245        /* Protect boot-loader from badblock testing */
 246        if (start_block < 2)
 247                start_block = 2;
 248
 249        if (end_block > (mtd->size >> this->erase_shift))
 250                end_block = mtd->size >> this->erase_shift;
 251
 252        blocks = start_block;
 253        ofs = start;
 254        while (blocks < end_block) {
 255                printf("\rTesting block %d at 0x%x", (u32)(ofs >> this->erase_shift), (u32)ofs);
 256
 257                ret = mtd_block_isbad(mtd, ofs);
 258                if (ret) {
 259                        printf("Skip erase bad block %d at 0x%x\n",
 260                               (u32)(ofs >> this->erase_shift), (u32)ofs);
 261                        goto next;
 262                }
 263
 264                instr.addr = ofs;
 265                instr.len = blocksize;
 266                ret = mtd_erase(mtd, &instr);
 267                if (ret) {
 268                        printk("Erase failed 0x%x, %d\n", (u32)ofs, ret);
 269                        goto next;
 270                }
 271
 272                ret = mtd_write(mtd, ofs, blocksize, &retlen, buf);
 273                if (ret) {
 274                        printk("Write failed 0x%x, %d\n", (u32)ofs, ret);
 275                        goto next;
 276                }
 277
 278                ret = mtd_read(mtd, ofs, blocksize, &retlen, verify_buf);
 279                if (ret) {
 280                        printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
 281                        goto next;
 282                }
 283
 284                if (memcmp(buf, verify_buf, blocksize))
 285                        printk("\nRead/Write test failed at 0x%x\n", (u32)ofs);
 286
 287next:
 288                ofs += blocksize;
 289                blocks++;
 290        }
 291        printf("...Done\n");
 292
 293        free(buf);
 294        free(verify_buf);
 295
 296        return 0;
 297}
 298
 299static int onenand_dump(struct mtd_info *mtd, ulong off, int only_oob)
 300{
 301        int i;
 302        u_char *datbuf, *oobbuf, *p;
 303        struct mtd_oob_ops ops;
 304        loff_t addr;
 305
 306        datbuf = malloc(mtd->writesize + mtd->oobsize);
 307        oobbuf = malloc(mtd->oobsize);
 308        if (!datbuf || !oobbuf) {
 309                puts("No memory for page buffer\n");
 310                return 1;
 311        }
 312        off &= ~(mtd->writesize - 1);
 313        addr = (loff_t) off;
 314        memset(&ops, 0, sizeof(ops));
 315        ops.datbuf = datbuf;
 316        ops.oobbuf = oobbuf;
 317        ops.len = mtd->writesize;
 318        ops.ooblen = mtd->oobsize;
 319        ops.retlen = 0;
 320        i = mtd_read_oob(mtd, addr, &ops);
 321        if (i < 0) {
 322                printf("Error (%d) reading page %08lx\n", i, off);
 323                free(datbuf);
 324                free(oobbuf);
 325                return 1;
 326        }
 327        printf("Page %08lx dump:\n", off);
 328        i = mtd->writesize >> 4;
 329        p = datbuf;
 330
 331        while (i--) {
 332                if (!only_oob)
 333                        printf("\t%02x %02x %02x %02x %02x %02x %02x %02x"
 334                               "  %02x %02x %02x %02x %02x %02x %02x %02x\n",
 335                               p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
 336                               p[8], p[9], p[10], p[11], p[12], p[13], p[14],
 337                               p[15]);
 338                p += 16;
 339        }
 340        puts("OOB:\n");
 341        i = mtd->oobsize >> 3;
 342        p = oobbuf;
 343
 344        while (i--) {
 345                printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n",
 346                       p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
 347                p += 8;
 348        }
 349        free(datbuf);
 350        free(oobbuf);
 351
 352        return 0;
 353}
 354
 355static int do_onenand_info(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 356{
 357        printf("%s\n", mtd->name);
 358        return 0;
 359}
 360
 361static int do_onenand_bad(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 362{
 363        ulong ofs;
 364
 365        mtd = &onenand_mtd;
 366        /* Currently only one OneNAND device is supported */
 367        printf("\nDevice %d bad blocks:\n", 0);
 368        for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) {
 369                if (mtd_block_isbad(mtd, ofs))
 370                        printf("  %08x\n", (u32)ofs);
 371        }
 372
 373        return 0;
 374}
 375
 376static int do_onenand_read(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 377{
 378        char *s;
 379        int oob = 0;
 380        ulong addr, ofs;
 381        size_t len;
 382        int ret = 0;
 383        size_t retlen = 0;
 384
 385        if (argc < 3)
 386                return CMD_RET_USAGE;
 387
 388        s = strchr(argv[0], '.');
 389        if ((s != NULL) && (!strcmp(s, ".oob")))
 390                oob = 1;
 391
 392        addr = (ulong)simple_strtoul(argv[1], NULL, 16);
 393
 394        printf("\nOneNAND read: ");
 395        if (arg_off_size_onenand(argc - 2, argv + 2, &ofs, &len) != 0)
 396                return 1;
 397
 398        ret = onenand_block_read(ofs, len, &retlen, (u8 *)addr, oob);
 399
 400        printf(" %d bytes read: %s\n", retlen, ret ? "ERROR" : "OK");
 401
 402        return ret == 0 ? 0 : 1;
 403}
 404
 405static int do_onenand_write(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 406{
 407        ulong addr, ofs;
 408        size_t len;
 409        int ret = 0, withoob = 0;
 410        size_t retlen = 0;
 411
 412        if (argc < 3)
 413                return CMD_RET_USAGE;
 414
 415        if (strncmp(argv[0] + 6, "yaffs", 5) == 0)
 416                withoob = 1;
 417
 418        addr = (ulong)simple_strtoul(argv[1], NULL, 16);
 419
 420        printf("\nOneNAND write: ");
 421        if (arg_off_size_onenand(argc - 2, argv + 2, &ofs, &len) != 0)
 422                return 1;
 423
 424        ret = onenand_block_write(ofs, len, &retlen, (u8 *)addr, withoob);
 425
 426        printf(" %d bytes written: %s\n", retlen, ret ? "ERROR" : "OK");
 427
 428        return ret == 0 ? 0 : 1;
 429}
 430
 431static int do_onenand_erase(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 432{
 433        ulong ofs;
 434        int ret = 0;
 435        size_t len;
 436        int force;
 437
 438        /*
 439         * Syntax is:
 440         *   0       1     2       3    4
 441         *   onenand erase [force] [off size]
 442         */
 443        argc--;
 444        argv++;
 445        if (argc)
 446        {
 447                if (!strcmp("force", argv[0]))
 448                {
 449                        force = 1;
 450                        argc--;
 451                        argv++;
 452                }
 453        }
 454        printf("\nOneNAND erase: ");
 455
 456        /* skip first two or three arguments, look for offset and size */
 457        if (arg_off_size_onenand(argc, argv, &ofs, &len) != 0)
 458                return 1;
 459
 460        ret = onenand_block_erase(ofs, len, force);
 461
 462        printf("%s\n", ret ? "ERROR" : "OK");
 463
 464        return ret == 0 ? 0 : 1;
 465}
 466
 467static int do_onenand_test(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 468{
 469        ulong ofs;
 470        int ret = 0;
 471        size_t len;
 472
 473        /*
 474         * Syntax is:
 475         *   0       1     2       3    4
 476         *   onenand test [force] [off size]
 477         */
 478
 479        printf("\nOneNAND test: ");
 480
 481        /* skip first two or three arguments, look for offset and size */
 482        if (arg_off_size_onenand(argc - 1, argv + 1, &ofs, &len) != 0)
 483                return 1;
 484
 485        ret = onenand_block_test(ofs, len);
 486
 487        printf("%s\n", ret ? "ERROR" : "OK");
 488
 489        return ret == 0 ? 0 : 1;
 490}
 491
 492static int do_onenand_dump(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 493{
 494        ulong ofs;
 495        int ret = 0;
 496        char *s;
 497
 498        if (argc < 2)
 499                return CMD_RET_USAGE;
 500
 501        s = strchr(argv[0], '.');
 502        ofs = (int)simple_strtoul(argv[1], NULL, 16);
 503
 504        if (s != NULL && strcmp(s, ".oob") == 0)
 505                ret = onenand_dump(mtd, ofs, 1);
 506        else
 507                ret = onenand_dump(mtd, ofs, 0);
 508
 509        return ret == 0 ? 1 : 0;
 510}
 511
 512static int do_onenand_markbad(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 513{
 514        int ret = 0;
 515        ulong addr;
 516
 517        argc -= 2;
 518        argv += 2;
 519
 520        if (argc <= 0)
 521                return CMD_RET_USAGE;
 522
 523        while (argc > 0) {
 524                addr = simple_strtoul(*argv, NULL, 16);
 525
 526                if (mtd_block_markbad(mtd, addr)) {
 527                        printf("block 0x%08lx NOT marked "
 528                                "as bad! ERROR %d\n",
 529                                addr, ret);
 530                        ret = 1;
 531                } else {
 532                        printf("block 0x%08lx successfully "
 533                                "marked as bad\n",
 534                                addr);
 535                }
 536                --argc;
 537                ++argv;
 538        }
 539        return ret;
 540}
 541
 542static cmd_tbl_t cmd_onenand_sub[] = {
 543        U_BOOT_CMD_MKENT(info, 1, 0, do_onenand_info, "", ""),
 544        U_BOOT_CMD_MKENT(bad, 1, 0, do_onenand_bad, "", ""),
 545        U_BOOT_CMD_MKENT(read, 4, 0, do_onenand_read, "", ""),
 546        U_BOOT_CMD_MKENT(write, 4, 0, do_onenand_write, "", ""),
 547        U_BOOT_CMD_MKENT(write.yaffs, 4, 0, do_onenand_write, "", ""),
 548        U_BOOT_CMD_MKENT(erase, 3, 0, do_onenand_erase, "", ""),
 549        U_BOOT_CMD_MKENT(test, 3, 0, do_onenand_test, "", ""),
 550        U_BOOT_CMD_MKENT(dump, 2, 0, do_onenand_dump, "", ""),
 551        U_BOOT_CMD_MKENT(markbad, CONFIG_SYS_MAXARGS, 0, do_onenand_markbad, "", ""),
 552};
 553
 554#ifdef CONFIG_NEEDS_MANUAL_RELOC
 555void onenand_reloc(void) {
 556        fixup_cmdtable(cmd_onenand_sub, ARRAY_SIZE(cmd_onenand_sub));
 557}
 558#endif
 559
 560static int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 561{
 562        cmd_tbl_t *c;
 563
 564        if (argc < 2)
 565                return CMD_RET_USAGE;
 566
 567        mtd = &onenand_mtd;
 568
 569        /* Strip off leading 'onenand' command argument */
 570        argc--;
 571        argv++;
 572
 573        c = find_cmd_tbl(argv[0], &cmd_onenand_sub[0], ARRAY_SIZE(cmd_onenand_sub));
 574
 575        if (c)
 576                return c->cmd(cmdtp, flag, argc, argv);
 577        else
 578                return CMD_RET_USAGE;
 579}
 580
 581U_BOOT_CMD(
 582        onenand,        CONFIG_SYS_MAXARGS,     1,      do_onenand,
 583        "OneNAND sub-system",
 584        "info - show available OneNAND devices\n"
 585        "onenand bad - show bad blocks\n"
 586        "onenand read[.oob] addr off size\n"
 587        "onenand write[.yaffs] addr off size\n"
 588        "    read/write 'size' bytes starting at offset 'off'\n"
 589        "    to/from memory address 'addr', skipping bad blocks.\n"
 590        "onenand erase [force] [off size] - erase 'size' bytes from\n"
 591        "onenand test [off size] - test 'size' bytes from\n"
 592        "    offset 'off' (entire device if not specified)\n"
 593        "onenand dump[.oob] off - dump page\n"
 594        "onenand markbad off [...] - mark bad block(s) at offset (UNSAFE)"
 595);
 596