[U-Boot] [PATCH] Add 'sf update' command to do smart SPI flash update

Simon Glass sjg at chromium.org
Fri Aug 19 15:47:04 CEST 2011


This adds a new SPI flash command which only rewrites blocks if the contents
need to change. This can speed up SPI flash programming when much of the
data is unchanged from what is already there.

Signed-off-by: Simon Glass <sjg at chromium.org>
---
 common/cmd_sf.c |   75 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 73 insertions(+), 2 deletions(-)

diff --git a/common/cmd_sf.c b/common/cmd_sf.c
index 11a491d..4a807eb 100644
--- a/common/cmd_sf.c
+++ b/common/cmd_sf.c
@@ -6,6 +6,7 @@
  */
 
 #include <common.h>
+#include <malloc.h>
 #include <spi_flash.h>
 
 #include <asm/io.h>
@@ -109,6 +110,70 @@ static int do_spi_flash_probe(int argc, char * const argv[])
 	return 0;
 }
 
+/**
+ * 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
+ */
+static int spi_flash_update(struct spi_flash *flash, u32 offset,
+		size_t len, const char *buf)
+{
+	const char *oper;
+	int err = 0;
+	char *cmp_buf = NULL;
+	size_t cmp_size = 0;
+	size_t todo;	/* number of bytes to do in this pass */
+	size_t skipped, written;		/* statistics */
+
+	for (skipped = written = 0; !err && len > 0;
+			buf += todo, offset += todo, len -= todo) {
+		todo = min(len, flash->sector_size);
+		debug("offset=%#x, sector_size=%#x, todo=%#x\n",
+		      offset, flash->sector_size, todo);
+		if (!err) {
+			oper = "malloc";
+			if (todo > cmp_size) {
+				if (cmp_buf)
+					free(cmp_buf);
+				cmp_buf = malloc(todo);
+				cmp_size = todo;
+			}
+			err = cmp_buf == NULL;
+		}
+		if (!err) {
+			oper = "read";
+			err = spi_flash_read(flash, offset, todo, cmp_buf);
+		}
+		if (!err) {
+			if (0 == memcmp(cmp_buf, buf, todo)) {
+				debug("Skip region %x size %x: no change\n",
+				      offset, todo);
+				skipped += todo;
+				continue;
+			}
+		}
+		if (!err) {
+			oper = "erase";
+			err = spi_flash_erase(flash, offset, todo);
+		}
+		if (!err) {
+			oper = "write";
+			err = spi_flash_write(flash, offset, todo, buf);
+			written += todo;
+		}
+	}
+	printf("%d bytes written, %d bytes skipped\n", written, skipped);
+	if (err)
+		printf("SPI flash %s failed\n", oper);
+	if (cmp_buf)
+		free(cmp_buf);
+	return err;
+}
+
 static int do_spi_flash_read_write(int argc, char * const argv[])
 {
 	unsigned long addr;
@@ -137,7 +202,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 +270,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);
@@ -229,4 +297,7 @@ U_BOOT_CMD(
 	"				  at `addr' to flash at `offset'\n"
 	"sf erase offset [+]len		- erase `len' bytes from `offset'\n"
 	"				  `+len' round up `len' to block size"
+	"sf update addr offset len	- erase and write `len' bytes from "
+		"memory\n"
+	"				  at `addr' to flash at `offset'\n"
 );
-- 
1.7.3.1



More information about the U-Boot mailing list