Merge branch 'sf' of git://git.denx.de/u-boot-blackfin
authorWolfgang Denk <wd@denx.de>
Sat, 1 Oct 2011 19:44:07 +0000 (21:44 +0200)
committerWolfgang Denk <wd@denx.de>
Sat, 1 Oct 2011 19:44:07 +0000 (21:44 +0200)
* 'sf' of git://git.denx.de/u-boot-blackfin:
  sf: eon: add support for EN25Q32B parts
  cmd_sf: add "update" subcommand to do smart SPI flash update

common/cmd_sf.c
drivers/mtd/spi/eon.c

index 27d6e39..c8c547a 100644 (file)
@@ -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'"
 );
index 806b44e..3a421db 100644 (file)
@@ -34,6 +34,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,
                .pages_per_sector = 16,