]> git.karo-electronics.de Git - linux-beck.git/commitdiff
mtd: spi-nor: simplify write loop
authorMichal Suchanek <hramrach@gmail.com>
Fri, 6 May 2016 00:31:54 +0000 (17:31 -0700)
committerBrian Norris <computersforpeace@gmail.com>
Thu, 2 Jun 2016 00:22:57 +0000 (17:22 -0700)
The spi-nor write loop assumes that what is passed to the hardware
driver write() is what gets written.

When write() writes less than page size at once data is dropped on the
floor. Check the amount of data writen and exit if it does not match
requested amount.

Signed-off-by: Michal Suchanek <hramrach@gmail.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Tested-by Cyrille Pitchen <cyrille.pitchen@atmel.com>
Acked-by: Michal Suchanek <hramrach@gmail.com>
Tested-by: Michal Suchanek <hramrach@gmail.com>
drivers/mtd/spi-nor/spi-nor.c

index 34dd95373e77293eb885993e0fbcb14d1a7692f6..fe55b48be5b3c52d911cc7e45ad5905d6882970c 100644 (file)
@@ -1129,8 +1129,8 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
        size_t *retlen, const u_char *buf)
 {
        struct spi_nor *nor = mtd_to_spi_nor(mtd);
-       u32 page_offset, page_size, i;
-       int ret;
+       size_t page_offset, page_remain, i;
+       ssize_t ret;
 
        dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
 
@@ -1138,45 +1138,37 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
        if (ret)
                return ret;
 
-       write_enable(nor);
-
-       page_offset = to & (nor->page_size - 1);
+       for (i = 0; i < len; ) {
+               ssize_t written;
 
-       /* do all the bytes fit onto one page? */
-       if (page_offset + len <= nor->page_size) {
-               ret = nor->write(nor, to, len, buf);
-               if (ret < 0)
-                       goto write_err;
-               *retlen += ret;
-       } else {
+               page_offset = (to + i) & (nor->page_size - 1);
+               WARN_ONCE(page_offset,
+                         "Writing at offset %zu into a NOR page. Writing partial pages may decrease reliability and increase wear of NOR flash.",
+                         page_offset);
                /* the size of data remaining on the first page */
-               page_size = nor->page_size - page_offset;
-               ret = nor->write(nor, to, page_size, buf);
+               page_remain = min_t(size_t,
+                                   nor->page_size - page_offset, len - i);
+
+               write_enable(nor);
+               ret = nor->write(nor, to + i, page_remain, buf + i);
                if (ret < 0)
                        goto write_err;
-               *retlen += ret;
-
-               /* write everything in nor->page_size chunks */
-               for (i = ret; i < len; ) {
-                       page_size = len - i;
-                       if (page_size > nor->page_size)
-                               page_size = nor->page_size;
-
-                       ret = spi_nor_wait_till_ready(nor);
-                       if (ret)
-                               goto write_err;
+               written = ret;
 
-                       write_enable(nor);
-
-                       ret = nor->write(nor, to + i, page_size, buf + i);
-                       if (ret < 0)
-                               goto write_err;
-                       *retlen += ret;
-                       i += ret;
+               ret = spi_nor_wait_till_ready(nor);
+               if (ret)
+                       goto write_err;
+               *retlen += written;
+               i += written;
+               if (written != page_remain) {
+                       dev_err(nor->dev,
+                               "While writing %zu bytes written %zd bytes\n",
+                               page_remain, written);
+                       ret = -EIO;
+                       goto write_err;
                }
        }
 
-       ret = spi_nor_wait_till_ready(nor);
 write_err:
        spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
        return ret;