@ -6,6 +6,7 @@
*/
# include <common.h>
# include <malloc.h>
# include <spi_flash.h>
# include <asm/io.h>
@ -109,6 +110,80 @@ static int do_spi_flash_probe(int argc, char * const argv[])
return 0 ;
}
/**
* Write a block of data to SPI flash , first checking if it is different from
* what is already there .
*
* If the data being written is the same , then * skipped is incremented by len .
*
* @ param flash flash context pointer
* @ param offset flash offset to write
* @ param len number of bytes to write
* @ param buf buffer to write from
* @ param cmp_buf read buffer to use to compare data
* @ param skipped Count of skipped data ( incremented by this function )
* @ return NULL if OK , else a string containing the stage which failed
*/
static const char * spi_flash_update_block ( struct spi_flash * flash , u32 offset ,
size_t len , const char * buf , char * cmp_buf , size_t * skipped )
{
debug ( " offset=%#x, sector_size=%#x, len=%#x \n " ,
offset , flash - > sector_size , len ) ;
if ( spi_flash_read ( flash , offset , len , cmp_buf ) )
return " read " ;
if ( memcmp ( cmp_buf , buf , len ) = = 0 ) {
debug ( " Skip region %x size %x: no change \n " ,
offset , len ) ;
* skipped + = len ;
return NULL ;
}
if ( spi_flash_erase ( flash , offset , len ) )
return " erase " ;
if ( spi_flash_write ( flash , offset , len , buf ) )
return " write " ;
return NULL ;
}
/**
* Update an area of SPI flash by erasing and writing any blocks which need
* to change . Existing blocks with the correct data are left unchanged .
*
* @ param flash flash context pointer
* @ param offset flash offset to write
* @ param len number of bytes to write
* @ param buf buffer to write from
* @ return 0 if ok , 1 on error
*/
static int spi_flash_update ( struct spi_flash * flash , u32 offset ,
size_t len , const char * buf )
{
const char * err_oper = NULL ;
char * cmp_buf ;
const char * end = buf + len ;
size_t todo ; /* number of bytes to do in this pass */
size_t skipped ; /* statistics */
cmp_buf = malloc ( flash - > sector_size ) ;
if ( cmp_buf ) {
for ( skipped = 0 ; buf < end & & ! err_oper ;
buf + = todo , offset + = todo ) {
todo = min ( end - buf , flash - > sector_size ) ;
err_oper = spi_flash_update_block ( flash , offset , todo ,
buf , cmp_buf , & skipped ) ;
}
} else {
err_oper = " malloc " ;
}
free ( cmp_buf ) ;
if ( err_oper ) {
printf ( " SPI flash failed in %s step \n " , err_oper ) ;
return 1 ;
}
printf ( " %zu bytes written, %zu bytes skipped \n " , len - skipped ,
skipped ) ;
return 0 ;
}
static int do_spi_flash_read_write ( int argc , char * const argv [ ] )
{
unsigned long addr ;
@ -137,7 +212,9 @@ static int do_spi_flash_read_write(int argc, char * const argv[])
return 1 ;
}
if ( strcmp ( argv [ 0 ] , " read " ) = = 0 )
if ( strcmp ( argv [ 0 ] , " update " ) = = 0 )
ret = spi_flash_update ( flash , offset , len , buf ) ;
else if ( strcmp ( argv [ 0 ] , " read " ) = = 0 )
ret = spi_flash_read ( flash , offset , len , buf ) ;
else
ret = spi_flash_write ( flash , offset , len , buf ) ;
@ -203,7 +280,8 @@ static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[
return 1 ;
}
if ( strcmp ( cmd , " read " ) = = 0 | | strcmp ( cmd , " write " ) = = 0 )
if ( strcmp ( cmd , " read " ) = = 0 | | strcmp ( cmd , " write " ) = = 0 | |
strcmp ( cmd , " update " ) = = 0 )
ret = do_spi_flash_read_write ( argc , argv ) ;
else if ( strcmp ( cmd , " erase " ) = = 0 )
ret = do_spi_flash_erase ( argc , argv ) ;
@ -228,5 +306,7 @@ U_BOOT_CMD(
" sf write addr offset len - write `len' bytes from memory \n "
" at `addr' to flash at `offset' \n "
" sf erase offset [+]len - erase `len' bytes from `offset' \n "
" `+len' round up `len' to block size "
" `+len' round up `len' to block size \n "
" sf update addr offset len - erase and write `len' bytes from memory \n "
" at `addr' to flash at `offset' "
) ;