]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
mtd: gpio_flash: handle case where offset + len exceeds the window size
authorAaron Wu <aaron.wu@analog.com>
Thu, 7 Aug 2014 03:43:49 +0000 (11:43 +0800)
committerBrian Norris <computersforpeace@gmail.com>
Tue, 19 Aug 2014 18:53:10 +0000 (11:53 -0700)
Fix the bug in handling gpio flash read/write when offset + len
from MTD exceeds the window size

Signed-off-by: Aaron Wu <Aaron.wu@analog.com>
[Brian: made some commentary edits. Also note that the BUG_ON() was
provably false for all non-negative inputs (since x % y <= x), so we
dropped it.]

Signed-off-by: Brian Norris <computersforpeace@gmail.com>
drivers/mtd/maps/gpio-addr-flash.c

index a4c477b9fdd600cefc6b48e0dd67472c08d611f5..2fb346091af2827b2ae1d238f511f641d00acde2 100644 (file)
@@ -99,22 +99,28 @@ static map_word gf_read(struct map_info *map, unsigned long ofs)
  *     @from: flash offset to copy from
  *     @len:  how much to copy
  *
- * We rely on the MTD layer to chunk up copies such that a single request here
- * will not cross a window size.  This allows us to only wiggle the GPIOs once
- * before falling back to a normal memcpy.  Reading the higher layer code shows
- * that this is indeed the case, but add a BUG_ON() to future proof.
+ * The "from" region may straddle more than one window, so toggle the GPIOs for
+ * each window region before reading its data.
  */
 static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
 {
        struct async_state *state = gf_map_info_to_state(map);
 
-       gf_set_gpios(state, from);
+       int this_len;
 
-       /* BUG if operation crosses the win_size */
-       BUG_ON(!((from + len) % state->win_size <= (from + len)));
+       while (len) {
+               if ((from % state->win_size) + len > state->win_size)
+                       this_len = state->win_size - (from % state->win_size);
+               else
+                       this_len = len;
 
-       /* operation does not cross the win_size, so one shot it */
-       memcpy_fromio(to, map->virt + (from % state->win_size), len);
+               gf_set_gpios(state, from);
+               memcpy_fromio(to, map->virt + (from % state->win_size),
+                        this_len);
+               len -= this_len;
+               from += this_len;
+               to += this_len;
+       }
 }
 
 /**
@@ -147,13 +153,21 @@ static void gf_copy_to(struct map_info *map, unsigned long to,
 {
        struct async_state *state = gf_map_info_to_state(map);
 
-       gf_set_gpios(state, to);
+       int this_len;
+
+       while (len) {
+               if ((to % state->win_size) + len > state->win_size)
+                       this_len = state->win_size - (to % state->win_size);
+               else
+                       this_len = len;
 
-       /* BUG if operation crosses the win_size */
-       BUG_ON(!((to + len) % state->win_size <= (to + len)));
+               gf_set_gpios(state, to);
+               memcpy_toio(map->virt + (to % state->win_size), from, len);
 
-       /* operation does not cross the win_size, so one shot it */
-       memcpy_toio(map->virt + (to % state->win_size), from, len);
+               len -= this_len;
+               to += this_len;
+               from += this_len;
+       }
 }
 
 static const char * const part_probe_types[] = {