OneNAND: Bad block aware read/write command support
PATCH v3 1/1 OneNAND: Bad block aware read/write command support
Update OneNAND command to support bad block awareness. Also change the OneNAND command style to better match the NAND version.
---
Scott, I looked at merging this code with the NAND command code, but the differences are too big for my taste.
v3:
- Removed mtd assignment which doesn't work on platforms with problem upon relocation (PPC, MIPS)
v2:
- Removed the bad block marking when an erase/read/write access to a block fails
- More cleanup's
common/cmd_onenand.c | 530 +++++++++++++++++++++++++++++++++++++++----------- 1 files changed, 420 insertions(+), 110 deletions(-)
common/cmd_onenand.c +420 -110
@@ -1,7 +1,7 @@/* * U-Boot command for OneNAND support * -* Copyright (C) 2005-2007 Samsung Electronics +* Copyright (C) 2005-2008 Samsung Electronics * Kyungmin Park <[email protected]> * * This program is free software; you can redistribute it and/or modify
@@ -11,6 +11,7 @@#include <common.h> #include <command.h> +#include <malloc.h> #include <linux/mtd/compat.h> #include <linux/mtd/mtd.h>
@@ -18,159 +19,468 @@#include <asm/io.h> -extern struct mtd_info onenand_mtd; -extern struct onenand_chip onenand_chip; +static struct mtd_info *mtd; + +static loff_t next_ofs; +static loff_t skip_ofs; + +static inline int str2long (char *p,ulong *num) +{ +char *endptr; + + *num =simple_strtoul (p, &endptr,16 ); +return (*p !='\0' && *endptr =='\0' ) ?1 :0 ; +} + +static int arg_off_size (int argc,char *argv[],ulong *off,size_t *size) +{ +if (argc >=1 ) { +if (!(str2long (argv[0 ], off))) { +printf ("'%s' is not a number\n" , argv[0 ]); +return -1 ; + } + }else { + *off =0 ; + } + +if (argc >=2 ) { +if (!(str2long (argv[1 ], (ulong *)size))) { +printf ("'%s' is not a number\n" , argv[1 ]); +return -1 ; + } + }else { + *size = mtd->size - *off; + } + +if ((*off + *size) > mtd->size) { +printf ("total chip size (0x%x) exceeded!\n" , mtd->size); +return -1 ; + } + +if (*size == mtd->size) +puts ("whole chip\n" ); +else +printf ("offset 0x%lx, size 0x%x\n" , *off, *size); + +return 0 ; +} + +static int onenand_block_read (loff_t from,size_t len, +size_t *retlen,u_char *buf,int oob) +{ +struct onenand_chip *this = mtd->priv; +int blocks = (int ) len >> this->erase_shift; +int blocksize = (1 << this->erase_shift); +loff_t ofs = from; +struct mtd_oob_ops ops = { + .retlen =0 , + }; +int ret; + +if (oob) + ops.ooblen = blocksize; +else + ops.len = blocksize; + +while (blocks) { + ret = mtd->block_isbad (mtd, ofs); +if (ret) { +printk ("Bad blocks %d at 0x%x\n" , + (u32)(ofs >> this->erase_shift), (u32 )ofs); + ofs += blocksize; +continue ; + } + +if (oob) + ops.oobbuf = buf; +else + ops.datbuf = buf; + + ops.retlen =0 ; + ret = mtd->read_oob (mtd, ofs, &ops); +if (ret) { +printk ("Read failed 0x%x, %d\n" , (u32 )ofs, ret); + ofs += blocksize; +continue ; + } + ofs += blocksize; + buf += blocksize; + blocks--; + *retlen += ops.retlen; + } + +return 0 ; +} + +static int onenand_block_write (loff_t to,size_t len, +size_t *retlen,const u_char * buf) +{ +struct onenand_chip *this = mtd->priv; +int blocks = len >> this->erase_shift; +int blocksize = (1 << this->erase_shift); +loff_t ofs; +size_t _retlen =0 ; +int ret; + +if (to == next_ofs) { + next_ofs = to + len; + to += skip_ofs; + }else { + next_ofs = to + len; + skip_ofs =0 ; + } + ofs = to; + +while (blocks) { + ret = mtd->block_isbad (mtd, ofs); +if (ret) { +printk ("Bad blocks %d at 0x%x\n" , + (u32)(ofs >> this->erase_shift), (u32 )ofs); + skip_ofs += blocksize; + goto next; + } + + ret = mtd->write (mtd, ofs, blocksize, &_retlen, buf); +if (ret) { +printk ("Write failed 0x%x, %d" , (u32 )ofs, ret); + skip_ofs += blocksize; + goto next; + } + + buf += blocksize; + blocks--; + *retlen += _retlen; +next: + ofs += blocksize; + } + +return 0 ; +} + +static int onenand_block_erase (u32 start,u32 size,int force) +{ +struct onenand_chip *this = mtd->priv; +struct erase_info instr = { + .callback =NULL , + }; +loff_t ofs; +int ret; +int blocksize =1 << this->erase_shift; + +for (ofs = start; ofs < (start + size); ofs += blocksize) { + ret = mtd->block_isbad (mtd, ofs); +if (ret && !force) { +printf ("Skip erase bad block %d at 0x%x\n" , + (u32)(ofs >> this->erase_shift), (u32 )ofs); +continue ; + } + + instr.addr = ofs; + instr.len = blocksize; + instr.priv = force; + instr.mtd = mtd; + ret = mtd->erase (mtd, &instr); +if (ret) { +printf ("erase failed block %d at 0x%x\n" , + (u32)(ofs >> this->erase_shift), (u32 )ofs); +continue ; + } + } + +return 0 ; +} + +static int onenand_block_test (u32 start,u32 size) +{ +struct onenand_chip *this = mtd->priv; +struct erase_info instr = { + .callback =NULL , + .priv =0 , + }; + +int blocks; +loff_t ofs; +int blocksize =1 << this->erase_shift; +int start_block, end_block; +size_t retlen; +u_char *buf; +u_char *verify_buf; +int ret; + + buf =malloc (blocksize); +if (!buf) { +printf ("Not enough malloc space available!\n" ); +return -1 ; + } + + verify_buf =malloc (blocksize); +if (!verify_buf) { +printf ("Not enough malloc space available!\n" ); +return -1 ; + } + + start_block = start >> this->erase_shift; + end_block = (start + size) >> this->erase_shift; + +/* Protect boot-loader from badblock testing */ +if (start_block <2 ) + start_block =2 ; + +if (end_block > (mtd->size >> this->erase_shift)) + end_block = mtd->size >> this->erase_shift; + + blocks = start_block; + ofs = start; +while (blocks < end_block) { +printf ("\rTesting block %d@0x%x" , (u32)(ofs >> this->erase_shift), (u32 )ofs); + + ret = mtd->block_isbad (mtd, ofs); +if (ret) { +printf ("Skip erase bad block %d at 0x%x\n" , + (u32)(ofs >> this->erase_shift), (u32 )ofs); + goto next; + } + + instr.addr = ofs; + instr.len = blocksize; + ret = mtd->erase (mtd, &instr); +if (ret) { +printk ("Erase failed 0x%x, %d\n" , (u32 )ofs, ret); + goto next; + } + + ret = mtd->write (mtd, ofs, blocksize, &retlen, buf); +if (ret) { +printk ("Write failed 0x%x, %d\n" , (u32 )ofs, ret); + goto next; + } + + ret = mtd->read (mtd, ofs, blocksize, &retlen, verify_buf); +if (ret) { +printk ("Read failed 0x%x, %d\n" , (u32 )ofs, ret); + goto next; + } + +if (memcmp (buf, verify_buf, blocksize)) +printk ("\nRead/Write test failed at 0x%x\n" , (u32 )ofs); + +next: + ofs += blocksize; + blocks++; + } +printf ("...Done\n" ); + +free (buf); +free (verify_buf); + +return 0 ; +} + +static int onenand_dump (struct mtd_info *mtd,ulong off,int only_oob) +{ +int i; +u_char *datbuf, *oobbuf, *p; +struct mtd_oob_ops ops; +loff_t addr; + + datbuf =malloc (mtd->writesize + mtd->oobsize); + oobbuf =malloc (mtd->oobsize); +if (!datbuf || !oobbuf) { +puts ("No memory for page buffer\n" ); +return 1 ; + } + off &= ~(mtd->writesize -1 ); + addr = (loff_t ) off; +memset (&ops,0 ,sizeof (ops)); + ops.datbuf = datbuf; + ops.oobbuf = oobbuf;/* must exist, but oob data will be appended to ops.datbuf */ + ops.len = mtd->writesize; + ops.ooblen = mtd->oobsize; + ops.retlen =0 ; + i = mtd->read_oob (mtd, addr, &ops); +if (i <0 ) { +printf ("Error (%d) reading page %08lx\n" , i, off); +free (datbuf); +free (oobbuf); +return 1 ; + } +printf ("Page %08lx dump:\n" , off); + i = mtd->writesize >>4 ; + p = datbuf; + +while (i--) { +if (!only_oob) +printf ("\t%02x %02x %02x %02x %02x %02x %02x %02x" +" %02x %02x %02x %02x %02x %02x %02x %02x\n" , + p[0 ], p[1 ], p[2 ], p[3 ], p[4 ], p[5 ], p[6 ], p[7 ], + p[8 ], p[9 ], p[10 ], p[11 ], p[12 ], p[13 ], p[14 ], + p[15 ]); + p +=16 ; + } +puts ("OOB:\n" ); + i = mtd->oobsize >>3 ; +while (i--) { +printf ("\t%02x %02x %02x %02x %02x %02x %02x %02x\n" , + p[0 ], p[1 ], p[2 ], p[3 ], p[4 ], p[5 ], p[6 ], p[7 ]); + p +=8 ; + } +free (datbuf); +free (oobbuf); + +return 0 ; +}int do_onenand (cmd_tbl_t * cmdtp,int flag,int argc,char *argv[]) { -int ret =0 ; +struct onenand_chip *this; +int blocksize; +ulong addr, ofs; +size_t len, retlen =0 ; +int ret; +char *cmd, *s; + + mtd = &onenand_mtd; + this = mtd->priv; + blocksize = (1 << this->erase_shift); + + cmd = argv[1 ];switch (argc) {case 0 :case 1 : -printf ("Usage:\n%s\n" , cmdtp->usage); -return 1 ; + goto usage;case 2 : -if (strncmp (argv[1 ],"open" ,4 ) ==0 ) { -onenand_init (); +if (strcmp (cmd,"info" ) ==0 ) { +printf ("%s\n" , mtd->name); +return 0 ; + } + +if (strcmp (cmd,"bad" ) ==0 ) { +/* Currently only one OneNAND device is supported */ +printf ("\nDevice %d bad blocks:\n" ,0 ); +for (ofs =0 ; ofs < mtd->size; ofs += mtd->erasesize) { +if (mtd->block_isbad (mtd, ofs)) +printf (" %08x\n" , (u32 )ofs); + } +return 0 ; } -printf ("%s\n" , onenand_mtd.name); -return 0 ;default :/* At least 4 args */ -if (strncmp (argv[1 ],"erase" ,5 ) ==0 ) { -struct erase_info instr = { - .callback =NULL , - }; -ulong start, end; -ulong block; -char *endtail; - -if (strncmp (argv[2 ],"block" ,5 ) ==0 ) { - start =simple_strtoul (argv[3 ],NULL ,10 ); - endtail =strchr (argv[3 ],'-' ); - end =simple_strtoul (endtail +1 ,NULL ,10 ); - }else { - start =simple_strtoul (argv[2 ],NULL ,10 ); - end =simple_strtoul (argv[3 ],NULL ,10 ); - start >>= onenand_chip.erase_shift; - end >>= onenand_chip.erase_shift; -/* Don't include the end block */ - end--; - } +/* +* Syntax is: +* 0 1 2 3 4 +* onenand erase [force] [off size] +*/ +if ((strcmp (cmd,"erase" ) ==0 ) || (strcmp (cmd,"test" ) ==0 )) { +int force = argc >2 && !strcmp ("force" , argv[2 ]); +int o = force ?3 :2 ; +int erase; -if (!end || end <0 ) - end = start; + erase =strcmp (cmd,"erase" ) ==0 ;/* 1 = erase, 0 = test */ +printf ("\nOneNAND %s: " , erase ?"erase" :"test" ); -printf ("Erase block from %lu to %lu\n" , start, end); +/* skip first two or three arguments, look for offset and size */ +if (arg_off_size (argc - o, argv + o, &ofs, &len) !=0 ) +return 1 ; -for (block = start; block <= end; block++) { - instr.addr = block << onenand_chip.erase_shift; - instr.len =1 << onenand_chip.erase_shift; - ret =onenand_erase (&onenand_mtd, &instr); -if (ret) { -printf ("erase failed %lu\n" , block); -break ; - } - } +if (erase) + ret =onenand_block_erase (ofs, len, force); +else + ret =onenand_block_test (ofs, len); -return 0 ; +printf ("%s\n" , ret ?"ERROR" :"OK" ); + +return ret ==0 ?0 :1 ; } -if (strncmp (argv[1 ],"read" ,4 ) ==0 ) { -ulong addr =simple_strtoul (argv[2 ],NULL ,16 ); -ulong ofs =simple_strtoul (argv[3 ],NULL ,16 ); -size_t len =simple_strtoul (argv[4 ],NULL ,16 ); -int oob =strncmp (argv[1 ],"read.oob" ,8 ) ?0 :1 ; -struct mtd_oob_ops ops; +if (strncmp (cmd,"read" ,4 ) ==0 ||strncmp (cmd,"write" ,5 ) ==0 ) { +int read; +int oob =0 ; - ops.mode =MTD_OOB_PLACE ; +if (argc <4 ) + goto usage; -if (oob) { - ops.len =0 ; - ops.datbuf =NULL ; - ops.ooblen = len; - ops.oobbuf = (u_char *) addr; - }else { - ops.len = len; - ops.datbuf = (u_char *) addr; - ops.ooblen =0 ; - ops.oobbuf =NULL ; - } - ops.retlen = ops.oobretlen =0 ; + addr = (ulong )simple_strtoul (argv[2 ],NULL ,16 ); - onenand_mtd.read_oob (&onenand_mtd, ofs, &ops); -printf ("Done\n" ); + read =strncmp (cmd,"read" ,4 ) ==0 ;/* 1 = read, 0 = write */ +printf ("\nOneNAND %s: " , read ?"read" :"write" ); +if (arg_off_size (argc -3 , argv +3 , &ofs, &len) !=0 ) +return 1 ; -return 0 ; - } + s =strchr (cmd,'.' ); +if ((s !=NULL ) && (!strcmp (s,".oob" ))) + oob =1 ; -if (strncmp (argv[1 ],"write" ,5 ) ==0 ) { -ulong addr =simple_strtoul (argv[2 ],NULL ,16 ); -ulong ofs =simple_strtoul (argv[3 ],NULL ,16 ); -size_t len =simple_strtoul (argv[4 ],NULL ,16 ); -size_t retlen =0 ; +if (read) { + ret =onenand_block_read (ofs, len, &retlen, + (u8 *)addr, oob); + }else { + ret =onenand_block_write (ofs, len, &retlen, + (u8 *)addr); + } -onenand_write (&onenand_mtd, ofs, len, &retlen, - (u_char *) addr); -printf ("Done\n" ); +printf (" %d bytes %s: %s\n" , retlen, + read ?"read" :"written" , ret ?"ERROR" :"OK" ); -return 0 ; +return ret ==0 ?0 :1 ; } -if (strncmp (argv[1 ],"block" ,5 ) ==0 ) { -ulong addr =simple_strtoul (argv[2 ],NULL ,16 ); -ulong block =simple_strtoul (argv[3 ],NULL ,10 ); -ulong page =simple_strtoul (argv[4 ],NULL ,10 ); -size_t len =simple_strtol (argv[5 ],NULL ,10 ); -ulong ofs; -int oob =strncmp (argv[1 ],"block.oob" ,9 ) ?0 :1 ; -struct mtd_oob_ops ops; - - ops.mode =MTD_OOB_PLACE ; +if (strcmp (cmd,"markbad" ) ==0 ) { + addr = (ulong )simple_strtoul (argv[2 ],NULL ,16 ); +int ret = mtd->block_markbad (mtd, addr); +if (ret ==0 ) { +printf ("block 0x%08lx successfully marked as bad\n" , + (ulong ) addr); +return 0 ; + }else { +printf ("block 0x%08lx NOT marked as bad! ERROR %d\n" , + (ulong ) addr, ret); + } +return 1 ; + } - ofs = block << onenand_chip.erase_shift; -if (page) - ofs += page << onenand_chip.page_shift; +if (strncmp (cmd,"dump" ,4 ) ==0 ) { +if (argc <3 ) + goto usage; -if (!len) { -if (oob) - ops.ooblen =64 ; -else - ops.len =512 ; - } + s =strchr (cmd,'.' ); + ofs = (int )simple_strtoul (argv[2 ],NULL ,16 ); -if (oob) { - ops.datbuf =NULL ; - ops.oobbuf = (u_char *) addr; - }else { - ops.datbuf = (u_char *) addr; - ops.oobbuf =NULL ; - } - ops.retlen = ops.oobretlen =0 ; +if (s !=NULL &&strcmp (s,".oob" ) ==0 ) + ret =onenand_dump (mtd, ofs,1 ); +else + ret =onenand_dump (mtd, ofs,0 ); -onenand_read_oob (&onenand_mtd, ofs, &ops); -return 0 ; +return ret ==0 ?1 :0 ; }break ; }return 0 ; + +usage: +printf ("Usage:\n%s\n" , cmdtp->usage); +return 1 ; }U_BOOT_CMD ( onenand,6 ,1 , do_onenand,"onenand - OneNAND sub-system\n" , -"info - show available OneNAND devices\n" -"onenand read[.oob] addr ofs len - read data at ofs with len to addr\n" -"onenand write addr ofs len - write data at ofs with len from addr\n" -"onenand erase saddr eaddr - erase block start addr to end addr\n" -"onenand block[.oob] addr block [page] [len] - " -"read data with (block [, page]) to addr" +"info - show available OneNAND devices\n" +"onenand bad - show bad blocks\n" +"onenand read[.oob] addr off size\n" +"onenand write[.oob] addr off size\n" +" read/write 'size' bytes starting at offset 'off'\n" +" to/from memory address 'addr', skipping bad blocks.\n" +"onenand erase [force] [off size] - erase 'size' bytes from\n" +"onenand test [off size] - test 'size' bytes from\n" +" offset 'off' (entire device if not specified)\n" +"onenand dump[.oob] off - dump page\n" +"onenand markbad off - mark bad block at offset (UNSAFE)\n" );
Signature · 1 line
1.6.0.4
PATCH v3 1/1 OneNAND: Bad block aware read/write command support
Quoted 9 lines · Stefan Roese
Update OneNAND command to support bad block awareness. Also change the OneNAND command style to better match the NAND version.
Signed-off-by Stefan Roese <[email protected]>Acked-by Kyungmin Park <[email protected]>---
Scott, I looked at merging this code with the NAND command code, but the differences are too big for my taste.
Understood for now, though long-term we should try to eliminate the differences that aren't rooted in hardware, and abstract into driver code the ones that are.
Applied to u-boot-nand-flash/next (despite a few long lines).
-Scott
PATCH v3 1/1 OneNAND: Bad block aware read/write command support
Quoted 11 lines · Scott Wood
Quoted 9 lines · Stefan Roese
Update OneNAND command to support bad block awareness. Also change the OneNAND command style to better match the NAND version.
Signed-off-by Stefan Roese <[email protected]>Acked-by Kyungmin Park <[email protected]>---
Scott, I looked at merging this code with the NAND command code, but the differences are too big for my taste.
Quoted 3 lines
Understood for now, though long-term we should try to eliminate the differences that aren't rooted in hardware, and abstract into driver code the ones that are.
Ack.
Quoted 1 line
Applied to u-boot-nand-flash/next (despite a few long lines).
Thanks.
Best regards,
Stefan
=====================================================================
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office at denx.de
=====================================================================