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 22 static struct mtd_info *mtd; 23 24 static loff_t next_ofs; 25 static loff_t skip_ofs; 26 27 static 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 61 static 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 108 static 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 132 static 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; 173 next: 174 ofs += blocksize; 175 } 176 177 return 0; 178 } 179 180 static 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 213 static 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 287 next: 288 ofs += blocksize; 289 blocks++; 290 } 291 printf("...Done\n"); 292 293 free(buf); 294 free(verify_buf); 295 296 return 0; 297 } 298 299 static 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 355 static 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 361 static 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 376 static 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 405 static 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 431 static 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 467 static 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 492 static 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 512 static 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 542 static 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 555 void onenand_reloc(void) { 556 fixup_cmdtable(cmd_onenand_sub, ARRAY_SIZE(cmd_onenand_sub)); 557 } 558 #endif 559 560 static 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 581 U_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