[PATCH] cmd: sf: Support unaligned flash updates with 'sf update'

Michael Walle michael at walle.cc
Thu Sep 30 23:21:48 CEST 2021


Am 2021-09-30 18:19, schrieb Frieder Schrempf:
> From: Frieder Schrempf <frieder.schrempf at kontron.de>
> 
> Currently 'sf update' supports only offsets that are aligned to the
> erase block size of the serial flash. Unaligned offsets result in
> something like:
> 
> => sf update ${kernel_addr_r} 0x400 ${filesize}
> device 0 offset 0x400, size 0x11f758
> SPI flash failed in erase step
> 
> In order to support unaligned updates, we simply read the first full
> block and check only the requested part of the block for changes. If
> necessary, the block is erased, the first (unchanged) part of the block
> is written back together with the second part of the block (updated 
> data).
> 
> Signed-off-by: Frieder Schrempf <frieder.schrempf at kontron.de>
> ---
>  cmd/sf.c | 17 +++++++++++------
>  1 file changed, 11 insertions(+), 6 deletions(-)
> 
> diff --git a/cmd/sf.c b/cmd/sf.c
> index eac27ed2d7..c54b4b10bb 100644
> --- a/cmd/sf.c
> +++ b/cmd/sf.c
> @@ -171,30 +171,32 @@ static int do_spi_flash_probe(int argc, char
> *const argv[])
>  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)
>  {
> +	u32 offset_aligned = ALIGN_DOWN(offset, flash->sector_size);
> +	u32 sector_offset = offset - offset_aligned;
>  	char *ptr = (char *)buf;
> 
>  	debug("offset=%#x, sector_size=%#x, len=%#zx\n",
>  	      offset, flash->sector_size, len);
>  	/* Read the entire sector so to allow for rewriting */
> -	if (spi_flash_read(flash, offset, flash->sector_size, cmp_buf))
> +	if (spi_flash_read(flash, offset_aligned, flash->sector_size, 
> cmp_buf))
>  		return "read";
>  	/* Compare only what is meaningful (len) */
> -	if (memcmp(cmp_buf, buf, len) == 0) {
> +	if (memcmp(cmp_buf + sector_offset, buf, len) == 0) {
>  		debug("Skip region %x size %zx: no change\n",
>  		      offset, len);
>  		*skipped += len;
>  		return NULL;
>  	}
>  	/* Erase the entire sector */
> -	if (spi_flash_erase(flash, offset, flash->sector_size))
> +	if (spi_flash_erase(flash, offset_aligned, flash->sector_size))
>  		return "erase";
>  	/* If it's a partial sector, copy the data into the temp-buffer */
>  	if (len != flash->sector_size) {
> -		memcpy(cmp_buf, buf, len);
> +		memcpy(cmp_buf + sector_offset, buf, len);
>  		ptr = cmp_buf;
>  	}
>  	/* Write one complete sector */
> -	if (spi_flash_write(flash, offset, flash->sector_size, ptr))
> +	if (spi_flash_write(flash, offset_aligned, flash->sector_size, ptr))
>  		return "write";
> 
>  	return NULL;
> @@ -230,7 +232,10 @@ static int spi_flash_update(struct spi_flash
> *flash, u32 offset,
>  		ulong last_update = get_timer(0);
> 
>  		for (; buf < end && !err_oper; buf += todo, offset += todo) {
> -			todo = min_t(size_t, end - buf, flash->sector_size);
> +			if (offset % flash->sector_size)

do_div() to avoid linking errors on some archs, I guess.

> +				todo = flash->sector_size - (offset % flash->sector_size);

This is missing the end-buf calculation, no? I.e. if you update just one
sector at an offset and the data you write is smaller than "sector_size 
- offset".

> +			else
> +				todo = min_t(size_t, end - buf, flash->sector_size);
>  			if (get_timer(last_update) > 100) {
>  				printf("   \rUpdating, %zu%% %lu B/s",
>  				       100 - (end - buf) / scale,

-michael


More information about the U-Boot mailing list