From 8d1af94220f62d2227fc7e633c5cef5f78ec4f4a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 22 Aug 2011 16:03:46 -0700 Subject: cmd_sf: add "update" subcommand to do smart SPI flash update 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 Signed-off-by: Mike Frysinger --- common/cmd_sf.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/common/cmd_sf.c b/common/cmd_sf.c index 27d6e39a18..c8c547ac0b 100644 --- a/common/cmd_sf.c +++ b/common/cmd_sf.c @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -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'" ); -- cgit v1.2.1 From bd75c63a94dc3f2bab02ee59f23ff4131c62709d Mon Sep 17 00:00:00 2001 From: Shaohui Xie Date: Tue, 27 Sep 2011 19:21:34 +0800 Subject: sf: eon: add support for EN25Q32B parts Signed-off-by: Shaohui Xie Signed-off-by: Mike Frysinger --- drivers/mtd/spi/eon.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/mtd/spi/eon.c b/drivers/mtd/spi/eon.c index 806b44e693..3a421db782 100644 --- a/drivers/mtd/spi/eon.c +++ b/drivers/mtd/spi/eon.c @@ -33,6 +33,14 @@ struct eon_spi_flash_params { }; static const struct eon_spi_flash_params eon_spi_flash_table[] = { + { + .idcode1 = 0x16, + .page_size = 256, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_sectors = 1024, + .name = "EN25Q32B", + }, { .idcode1 = 0x18, .page_size = 256, -- cgit v1.2.1