2 * U-Boot command for OneNAND support
4 * Copyright (C) 2005-2008 Samsung Electronics
5 * Kyungmin Park <kyungmin.park@samsung.com>
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.
16 #include <linux/mtd/compat.h>
17 #include <linux/mtd/mtd.h>
18 #include <linux/mtd/onenand.h>
22 #if !defined(CONFIG_SYS_64BIT_VSPRINTF)
23 #warning Please define CONFIG_SYS_64BIT_VSPRINTF for correct output!
26 static struct mtd_info *mtd;
28 static loff_t next_ofs;
29 static loff_t skip_ofs;
31 static inline int str2long(char *p, ulong *num)
35 *num = simple_strtoul(p, &endptr, 16);
36 return (*p != '\0' && *endptr == '\0') ? 1 : 0;
39 static int arg_off_size(int argc, char *argv[], ulong *off, size_t *size)
42 if (!(str2long(argv[0], off))) {
43 printf("'%s' is not a number\n", argv[0]);
51 if (!(str2long(argv[1], (ulong *)size))) {
52 printf("'%s' is not a number\n", argv[1]);
56 *size = mtd->size - *off;
59 if ((*off + *size) > mtd->size) {
60 printf("total chip size (0x%llx) exceeded!\n", mtd->size);
64 if (*size == mtd->size)
67 printf("offset 0x%lx, size 0x%x\n", *off, *size);
72 static int onenand_block_read(loff_t from, size_t len,
73 size_t *retlen, u_char *buf, int oob)
75 struct onenand_chip *this = mtd->priv;
76 int blocks = (int) len >> this->erase_shift;
77 int blocksize = (1 << this->erase_shift);
79 struct mtd_oob_ops ops = {
85 ops.ooblen = blocksize;
90 ret = mtd->block_isbad(mtd, ofs);
92 printk("Bad blocks %d at 0x%x\n",
93 (u32)(ofs >> this->erase_shift), (u32)ofs);
104 ret = mtd->read_oob(mtd, ofs, &ops);
106 printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
113 *retlen += ops.retlen;
119 static int onenand_block_write(loff_t to, size_t len,
120 size_t *retlen, const u_char * buf)
122 struct onenand_chip *this = mtd->priv;
123 int blocks = len >> this->erase_shift;
124 int blocksize = (1 << this->erase_shift);
129 if (to == next_ofs) {
139 ret = mtd->block_isbad(mtd, ofs);
141 printk("Bad blocks %d at 0x%x\n",
142 (u32)(ofs >> this->erase_shift), (u32)ofs);
143 skip_ofs += blocksize;
147 ret = mtd->write(mtd, ofs, blocksize, &_retlen, buf);
149 printk("Write failed 0x%x, %d", (u32)ofs, ret);
150 skip_ofs += blocksize;
164 static int onenand_block_erase(u32 start, u32 size, int force)
166 struct onenand_chip *this = mtd->priv;
167 struct erase_info instr = {
172 int blocksize = 1 << this->erase_shift;
174 for (ofs = start; ofs < (start + size); ofs += blocksize) {
175 ret = mtd->block_isbad(mtd, ofs);
177 printf("Skip erase bad block %d at 0x%x\n",
178 (u32)(ofs >> this->erase_shift), (u32)ofs);
183 instr.len = blocksize;
186 ret = mtd->erase(mtd, &instr);
188 printf("erase failed block %d at 0x%x\n",
189 (u32)(ofs >> this->erase_shift), (u32)ofs);
197 static int onenand_block_test(u32 start, u32 size)
199 struct onenand_chip *this = mtd->priv;
200 struct erase_info instr = {
207 int blocksize = 1 << this->erase_shift;
208 int start_block, end_block;
214 buf = malloc(blocksize);
216 printf("Not enough malloc space available!\n");
220 verify_buf = malloc(blocksize);
222 printf("Not enough malloc space available!\n");
226 start_block = start >> this->erase_shift;
227 end_block = (start + size) >> this->erase_shift;
229 /* Protect boot-loader from badblock testing */
233 if (end_block > (mtd->size >> this->erase_shift))
234 end_block = mtd->size >> this->erase_shift;
236 blocks = start_block;
238 while (blocks < end_block) {
239 printf("\rTesting block %d at 0x%x", (u32)(ofs >> this->erase_shift), (u32)ofs);
241 ret = mtd->block_isbad(mtd, ofs);
243 printf("Skip erase bad block %d at 0x%x\n",
244 (u32)(ofs >> this->erase_shift), (u32)ofs);
249 instr.len = blocksize;
250 ret = mtd->erase(mtd, &instr);
252 printk("Erase failed 0x%x, %d\n", (u32)ofs, ret);
256 ret = mtd->write(mtd, ofs, blocksize, &retlen, buf);
258 printk("Write failed 0x%x, %d\n", (u32)ofs, ret);
262 ret = mtd->read(mtd, ofs, blocksize, &retlen, verify_buf);
264 printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
268 if (memcmp(buf, verify_buf, blocksize))
269 printk("\nRead/Write test failed at 0x%x\n", (u32)ofs);
283 static int onenand_dump(struct mtd_info *mtd, ulong off, int only_oob)
286 u_char *datbuf, *oobbuf, *p;
287 struct mtd_oob_ops ops;
290 datbuf = malloc(mtd->writesize + mtd->oobsize);
291 oobbuf = malloc(mtd->oobsize);
292 if (!datbuf || !oobbuf) {
293 puts("No memory for page buffer\n");
296 off &= ~(mtd->writesize - 1);
298 memset(&ops, 0, sizeof(ops));
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;
304 i = mtd->read_oob(mtd, addr, &ops);
306 printf("Error (%d) reading page %08lx\n", i, off);
311 printf("Page %08lx dump:\n", off);
312 i = mtd->writesize >> 4;
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],
325 i = mtd->oobsize >> 3;
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]);
337 int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
339 struct onenand_chip *this;
342 size_t len, retlen = 0;
348 blocksize = (1 << this->erase_shift);
358 if (strcmp(cmd, "info") == 0) {
359 printf("%s\n", mtd->name);
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);
375 /* At least 4 args */
380 * onenand erase [force] [off size]
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;
387 erase = strcmp(cmd, "erase") == 0; /* 1 = erase, 0 = test */
388 printf("\nOneNAND %s: ", erase ? "erase" : "test");
390 /* skip first two or three arguments, look for offset and size */
391 if (arg_off_size(argc - o, argv + o, &ofs, &len) != 0)
395 ret = onenand_block_erase(ofs, len, force);
397 ret = onenand_block_test(ofs, len);
399 printf("%s\n", ret ? "ERROR" : "OK");
401 return ret == 0 ? 0 : 1;
404 if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
411 addr = (ulong)simple_strtoul(argv[2], NULL, 16);
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)
418 s = strchr(cmd, '.');
419 if ((s != NULL) && (!strcmp(s, ".oob")))
423 ret = onenand_block_read(ofs, len, &retlen,
426 ret = onenand_block_write(ofs, len, &retlen,
430 printf(" %d bytes %s: %s\n", retlen,
431 read ? "read" : "written", ret ? "ERROR" : "OK");
433 return ret == 0 ? 0 : 1;
436 if (strcmp(cmd, "markbad") == 0) {
444 addr = simple_strtoul(*argv, NULL, 16);
446 if (mtd->block_markbad(mtd, addr)) {
447 printf("block 0x%08lx NOT marked "
448 "as bad! ERROR %d\n",
452 printf("block 0x%08lx successfully "
462 if (strncmp(cmd, "dump", 4) == 0) {
466 s = strchr(cmd, '.');
467 ofs = (int)simple_strtoul(argv[2], NULL, 16);
469 if (s != NULL && strcmp(s, ".oob") == 0)
470 ret = onenand_dump(mtd, ofs, 1);
472 ret = onenand_dump(mtd, ofs, 0);
474 return ret == 0 ? 1 : 0;
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)"