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/mtd/compat.h>
  17#include <linux/mtd/mtd.h>
  18#include <linux/mtd/onenand.h>
  19
  20#include <asm/io.h>
  21
  22#if !defined(CONFIG_SYS_64BIT_VSPRINTF)
  23#warning Please define CONFIG_SYS_64BIT_VSPRINTF for correct output!
  24#endif
  25
  26static struct mtd_info *mtd;
  27
  28static loff_t next_ofs;
  29static loff_t skip_ofs;
  30
  31static inline int str2long(char *p, ulong *num)
  32{
  33        char *endptr;
  34
  35        *num = simple_strtoul(p, &endptr, 16);
  36        return (*p != '\0' && *endptr == '\0') ? 1 : 0;
  37}
  38
  39static int arg_off_size(int argc, char *argv[], ulong *off, size_t *size)
  40{
  41        if (argc >= 1) {
  42                if (!(str2long(argv[0], off))) {
  43                        printf("'%s' is not a number\n", argv[0]);
  44                        return -1;
  45                }
  46        } else {
  47                *off = 0;
  48        }
  49
  50        if (argc >= 2) {
  51                if (!(str2long(argv[1], (ulong *)size))) {
  52                        printf("'%s' is not a number\n", argv[1]);
  53                        return -1;
  54                }
  55        } else {
  56                *size = mtd->size - *off;
  57        }
  58
  59        if ((*off + *size) > mtd->size) {
  60                printf("total chip size (0x%llx) exceeded!\n", mtd->size);
  61                return -1;
  62        }
  63
  64        if (*size == mtd->size)
  65                puts("whole chip\n");
  66        else
  67                printf("offset 0x%lx, size 0x%x\n", *off, *size);
  68
  69        return 0;
  70}
  71
  72static int onenand_block_read(loff_t from, size_t len,
  73                              size_t *retlen, u_char *buf, int oob)
  74{
  75        struct onenand_chip *this = mtd->priv;
  76        int blocks = (int) len >> this->erase_shift;
  77        int blocksize = (1 << this->erase_shift);
  78        loff_t ofs = from;
  79        struct mtd_oob_ops ops = {
  80                .retlen         = 0,
  81        };
  82        int ret;
  83
  84        if (oob)
  85                ops.ooblen = blocksize;
  86        else
  87                ops.len = blocksize;
  88
  89        while (blocks) {
  90                ret = mtd->block_isbad(mtd, ofs);
  91                if (ret) {
  92                        printk("Bad blocks %d at 0x%x\n",
  93                               (u32)(ofs >> this->erase_shift), (u32)ofs);
  94                        ofs += blocksize;
  95                        continue;
  96                }
  97
  98                if (oob)
  99                        ops.oobbuf = buf;
 100                else
 101                        ops.datbuf = buf;
 102
 103                ops.retlen = 0;
 104                ret = mtd->read_oob(mtd, ofs, &ops);
 105                if (ret) {
 106                        printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
 107                        ofs += blocksize;
 108                        continue;
 109                }
 110                ofs += blocksize;
 111                buf += blocksize;
 112                blocks--;
 113                *retlen += ops.retlen;
 114        }
 115
 116        return 0;
 117}
 118
 119static int onenand_block_write(loff_t to, size_t len,
 120                               size_t *retlen, const u_char * buf)
 121{
 122        struct onenand_chip *this = mtd->priv;
 123        int blocks = len >> this->erase_shift;
 124        int blocksize = (1 << this->erase_shift);
 125        loff_t ofs;
 126        size_t _retlen = 0;
 127        int ret;
 128
 129        if (to == next_ofs) {
 130                next_ofs = to + len;
 131                to += skip_ofs;
 132        } else {
 133                next_ofs = to + len;
 134                skip_ofs = 0;
 135        }
 136        ofs = to;
 137
 138        while (blocks) {
 139                ret = mtd->block_isbad(mtd, ofs);
 140                if (ret) {
 141                        printk("Bad blocks %d at 0x%x\n",
 142                               (u32)(ofs >> this->erase_shift), (u32)ofs);
 143                        skip_ofs += blocksize;
 144                        goto next;
 145                }
 146
 147                ret = mtd->write(mtd, ofs, blocksize, &_retlen, buf);
 148                if (ret) {
 149                        printk("Write failed 0x%x, %d", (u32)ofs, ret);
 150                        skip_ofs += blocksize;
 151                        goto next;
 152                }
 153
 154                buf += blocksize;
 155                blocks--;
 156                *retlen += _retlen;
 157next:
 158                ofs += blocksize;
 159        }
 160
 161        return 0;
 162}
 163
 164static int onenand_block_erase(u32 start, u32 size, int force)
 165{
 166        struct onenand_chip *this = mtd->priv;
 167        struct erase_info instr = {
 168                .callback       = NULL,
 169        };
 170        loff_t ofs;
 171        int ret;
 172        int blocksize = 1 << this->erase_shift;
 173
 174        for (ofs = start; ofs < (start + size); ofs += blocksize) {
 175                ret = mtd->block_isbad(mtd, ofs);
 176                if (ret && !force) {
 177                        printf("Skip erase bad block %d at 0x%x\n",
 178                               (u32)(ofs >> this->erase_shift), (u32)ofs);
 179                        continue;
 180                }
 181
 182                instr.addr = ofs;
 183                instr.len = blocksize;
 184                instr.priv = force;
 185                instr.mtd = mtd;
 186                ret = mtd->erase(mtd, &instr);
 187                if (ret) {
 188                        printf("erase failed block %d at 0x%x\n",
 189                               (u32)(ofs >> this->erase_shift), (u32)ofs);
 190                        continue;
 191                }
 192        }
 193
 194        return 0;
 195}
 196
 197static int onenand_block_test(u32 start, u32 size)
 198{
 199        struct onenand_chip *this = mtd->priv;
 200        struct erase_info instr = {
 201                .callback       = NULL,
 202                .priv           = 0,
 203        };
 204
 205        int blocks;
 206        loff_t ofs;
 207        int blocksize = 1 << this->erase_shift;
 208        int start_block, end_block;
 209        size_t retlen;
 210        u_char *buf;
 211        u_char *verify_buf;
 212        int ret;
 213
 214        buf = malloc(blocksize);
 215        if (!buf) {
 216                printf("Not enough malloc space available!\n");
 217                return -1;
 218        }
 219
 220        verify_buf = malloc(blocksize);
 221        if (!verify_buf) {
 222                printf("Not enough malloc space available!\n");
 223                return -1;
 224        }
 225
 226        start_block = start >> this->erase_shift;
 227        end_block = (start + size) >> this->erase_shift;
 228
 229        /* Protect boot-loader from badblock testing */
 230        if (start_block < 2)
 231                start_block = 2;
 232
 233        if (end_block > (mtd->size >> this->erase_shift))
 234                end_block = mtd->size >> this->erase_shift;
 235
 236        blocks = start_block;
 237        ofs = start;
 238        while (blocks < end_block) {
 239                printf("\rTesting block %d at 0x%x", (u32)(ofs >> this->erase_shift), (u32)ofs);
 240
 241                ret = mtd->block_isbad(mtd, ofs);
 242                if (ret) {
 243                        printf("Skip erase bad block %d at 0x%x\n",
 244                               (u32)(ofs >> this->erase_shift), (u32)ofs);
 245                        goto next;
 246                }
 247
 248                instr.addr = ofs;
 249                instr.len = blocksize;
 250                ret = mtd->erase(mtd, &instr);
 251                if (ret) {
 252                        printk("Erase failed 0x%x, %d\n", (u32)ofs, ret);
 253                        goto next;
 254                }
 255
 256                ret = mtd->write(mtd, ofs, blocksize, &retlen, buf);
 257                if (ret) {
 258                        printk("Write failed 0x%x, %d\n", (u32)ofs, ret);
 259                        goto next;
 260                }
 261
 262                ret = mtd->read(mtd, ofs, blocksize, &retlen, verify_buf);
 263                if (ret) {
 264                        printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
 265                        goto next;
 266                }
 267
 268                if (memcmp(buf, verify_buf, blocksize))
 269                        printk("\nRead/Write test failed at 0x%x\n", (u32)ofs);
 270
 271next:
 272                ofs += blocksize;
 273                blocks++;
 274        }
 275        printf("...Done\n");
 276
 277        free(buf);
 278        free(verify_buf);
 279
 280        return 0;
 281}
 282
 283static int onenand_dump(struct mtd_info *mtd, ulong off, int only_oob)
 284{
 285        int i;
 286        u_char *datbuf, *oobbuf, *p;
 287        struct mtd_oob_ops ops;
 288        loff_t addr;
 289
 290        datbuf = malloc(mtd->writesize + mtd->oobsize);
 291        oobbuf = malloc(mtd->oobsize);
 292        if (!datbuf || !oobbuf) {
 293                puts("No memory for page buffer\n");
 294                return 1;
 295        }
 296        off &= ~(mtd->writesize - 1);
 297        addr = (loff_t) off;
 298        memset(&ops, 0, sizeof(ops));
 299        ops.datbuf = datbuf;
 300        ops.oobbuf = oobbuf; /* must exist, but oob data will be appended to ops.datbuf */
 301        ops.len = mtd->writesize;
 302        ops.ooblen = mtd->oobsize;
 303        ops.retlen = 0;
 304        i = mtd->read_oob(mtd, addr, &ops);
 305        if (i < 0) {
 306                printf("Error (%d) reading page %08lx\n", i, off);
 307                free(datbuf);
 308                free(oobbuf);
 309                return 1;
 310        }
 311        printf("Page %08lx dump:\n", off);
 312        i = mtd->writesize >> 4;
 313        p = datbuf;
 314
 315        while (i--) {
 316                if (!only_oob)
 317                        printf("\t%02x %02x %02x %02x %02x %02x %02x %02x"
 318                               "  %02x %02x %02x %02x %02x %02x %02x %02x\n",
 319                               p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
 320                               p[8], p[9], p[10], p[11], p[12], p[13], p[14],
 321                               p[15]);
 322                p += 16;
 323        }
 324        puts("OOB:\n");
 325        i = mtd->oobsize >> 3;
 326        while (i--) {
 327                printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n",
 328                       p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
 329                p += 8;
 330        }
 331        free(datbuf);
 332        free(oobbuf);
 333
 334        return 0;
 335}
 336
 337int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 338{
 339        struct onenand_chip *this;
 340        int blocksize;
 341        ulong addr, ofs;
 342        size_t len, retlen = 0;
 343        int ret = 0;
 344        char *cmd, *s;
 345
 346        mtd = &onenand_mtd;
 347        this = mtd->priv;
 348        blocksize = (1 << this->erase_shift);
 349
 350        cmd = argv[1];
 351
 352        switch (argc) {
 353        case 0:
 354        case 1:
 355                goto usage;
 356
 357        case 2:
 358                if (strcmp(cmd, "info") == 0) {
 359                        printf("%s\n", mtd->name);
 360                        return 0;
 361                }
 362
 363                if (strcmp(cmd, "bad") == 0) {
 364                        /* Currently only one OneNAND device is supported */
 365                        printf("\nDevice %d bad blocks:\n", 0);
 366                        for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) {
 367                                if (mtd->block_isbad(mtd, ofs))
 368                                        printf("  %08x\n", (u32)ofs);
 369                        }
 370
 371                        return 0;
 372                }
 373
 374        default:
 375                /* At least 4 args */
 376
 377                /*
 378                 * Syntax is:
 379                 *   0       1     2       3    4
 380                 *   onenand erase [force] [off size]
 381                 */
 382                if ((strcmp(cmd, "erase") == 0) || (strcmp(cmd, "test") == 0)) {
 383                        int force = argc > 2 && !strcmp("force", argv[2]);
 384                        int o = force ? 3 : 2;
 385                        int erase;
 386
 387                        erase = strcmp(cmd, "erase") == 0; /* 1 = erase, 0 = test */
 388                        printf("\nOneNAND %s: ", erase ? "erase" : "test");
 389
 390                        /* skip first two or three arguments, look for offset and size */
 391                        if (arg_off_size(argc - o, argv + o, &ofs, &len) != 0)
 392                                return 1;
 393
 394                        if (erase)
 395                                ret = onenand_block_erase(ofs, len, force);
 396                        else
 397                                ret = onenand_block_test(ofs, len);
 398
 399                        printf("%s\n", ret ? "ERROR" : "OK");
 400
 401                        return ret == 0 ? 0 : 1;
 402                }
 403
 404                if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
 405                        int read;
 406                        int oob = 0;
 407
 408                        if (argc < 4)
 409                                goto usage;
 410
 411                        addr = (ulong)simple_strtoul(argv[2], NULL, 16);
 412
 413                        read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
 414                        printf("\nOneNAND %s: ", read ? "read" : "write");
 415                        if (arg_off_size(argc - 3, argv + 3, &ofs, &len) != 0)
 416                                return 1;
 417
 418                        s = strchr(cmd, '.');
 419                        if ((s != NULL) && (!strcmp(s, ".oob")))
 420                                oob = 1;
 421
 422                        if (read) {
 423                                ret = onenand_block_read(ofs, len, &retlen,
 424                                                         (u8 *)addr, oob);
 425                        } else {
 426                                ret = onenand_block_write(ofs, len, &retlen,
 427                                                          (u8 *)addr);
 428                        }
 429
 430                        printf(" %d bytes %s: %s\n", retlen,
 431                               read ? "read" : "written", ret ? "ERROR" : "OK");
 432
 433                        return ret == 0 ? 0 : 1;
 434                }
 435
 436                if (strcmp(cmd, "markbad") == 0) {
 437                        argc -= 2;
 438                        argv += 2;
 439
 440                        if (argc <= 0)
 441                                goto usage;
 442
 443                        while (argc > 0) {
 444                                addr = simple_strtoul(*argv, NULL, 16);
 445
 446                                if (mtd->block_markbad(mtd, addr)) {
 447                                        printf("block 0x%08lx NOT marked "
 448                                                "as bad! ERROR %d\n",
 449                                                addr, ret);
 450                                        ret = 1;
 451                                } else {
 452                                        printf("block 0x%08lx successfully "
 453                                                "marked as bad\n",
 454                                                addr);
 455                                }
 456                                --argc;
 457                                ++argv;
 458                        }
 459                        return ret;
 460                }
 461
 462                if (strncmp(cmd, "dump", 4) == 0) {
 463                        if (argc < 3)
 464                                goto usage;
 465
 466                        s = strchr(cmd, '.');
 467                        ofs = (int)simple_strtoul(argv[2], NULL, 16);
 468
 469                        if (s != NULL && strcmp(s, ".oob") == 0)
 470                                ret = onenand_dump(mtd, ofs, 1);
 471                        else
 472                                ret = onenand_dump(mtd, ofs, 0);
 473
 474                        return ret == 0 ? 1 : 0;
 475                }
 476
 477                break;
 478        }
 479
 480        return 0;
 481
 482usage:
 483        cmd_usage(cmdtp);
 484        return 1;
 485}
 486
 487U_BOOT_CMD(
 488        onenand,        CONFIG_SYS_MAXARGS,     1,      do_onenand,
 489        "OneNAND sub-system",
 490        "info - show available OneNAND devices\n"
 491        "onenand bad - show bad blocks\n"
 492        "onenand read[.oob] addr off size\n"
 493        "onenand write[.oob] addr off size\n"
 494        "    read/write 'size' bytes starting at offset 'off'\n"
 495        "    to/from memory address 'addr', skipping bad blocks.\n"
 496        "onenand erase [force] [off size] - erase 'size' bytes from\n"
 497        "onenand test [off size] - test 'size' bytes from\n"
 498        "    offset 'off' (entire device if not specified)\n"
 499        "onenand dump[.oob] off - dump page\n"
 500        "onenand markbad off [...] - mark bad block(s) at offset (UNSAFE)"
 501);
 502