aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Wu <aaron.wu@analog.com>2014-08-06 23:43:49 -0400
committerBrian Norris <computersforpeace@gmail.com>2014-08-19 14:53:10 -0400
commit02f8a24e7b1c253ee37edc684200c11300de23f9 (patch)
tree14298126df14c0a3ff697305884ef44d16de3810
parentbd8898db3e03147d9d7ddd48876fb3f3bcbab6c1 (diff)
mtd: gpio_flash: handle case where offset + len exceeds the window size
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>
-rw-r--r--drivers/mtd/maps/gpio-addr-flash.c42
1 files changed, 28 insertions, 14 deletions
diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c
index a4c477b9fdd6..2fb346091af2 100644
--- a/drivers/mtd/maps/gpio-addr-flash.c
+++ b/drivers/mtd/maps/gpio-addr-flash.c
@@ -99,22 +99,28 @@ static map_word gf_read(struct map_info *map, unsigned long ofs)
99 * @from: flash offset to copy from 99 * @from: flash offset to copy from
100 * @len: how much to copy 100 * @len: how much to copy
101 * 101 *
102 * We rely on the MTD layer to chunk up copies such that a single request here 102 * The "from" region may straddle more than one window, so toggle the GPIOs for
103 * will not cross a window size. This allows us to only wiggle the GPIOs once 103 * each window region before reading its data.
104 * before falling back to a normal memcpy. Reading the higher layer code shows
105 * that this is indeed the case, but add a BUG_ON() to future proof.
106 */ 104 */
107static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) 105static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
108{ 106{
109 struct async_state *state = gf_map_info_to_state(map); 107 struct async_state *state = gf_map_info_to_state(map);
110 108
111 gf_set_gpios(state, from); 109 int this_len;
112 110
113 /* BUG if operation crosses the win_size */ 111 while (len) {
114 BUG_ON(!((from + len) % state->win_size <= (from + len))); 112 if ((from % state->win_size) + len > state->win_size)
113 this_len = state->win_size - (from % state->win_size);
114 else
115 this_len = len;
115 116
116 /* operation does not cross the win_size, so one shot it */ 117 gf_set_gpios(state, from);
117 memcpy_fromio(to, map->virt + (from % state->win_size), len); 118 memcpy_fromio(to, map->virt + (from % state->win_size),
119 this_len);
120 len -= this_len;
121 from += this_len;
122 to += this_len;
123 }
118} 124}
119 125
120/** 126/**
@@ -147,13 +153,21 @@ static void gf_copy_to(struct map_info *map, unsigned long to,
147{ 153{
148 struct async_state *state = gf_map_info_to_state(map); 154 struct async_state *state = gf_map_info_to_state(map);
149 155
150 gf_set_gpios(state, to); 156 int this_len;
157
158 while (len) {
159 if ((to % state->win_size) + len > state->win_size)
160 this_len = state->win_size - (to % state->win_size);
161 else
162 this_len = len;
151 163
152 /* BUG if operation crosses the win_size */ 164 gf_set_gpios(state, to);
153 BUG_ON(!((to + len) % state->win_size <= (to + len))); 165 memcpy_toio(map->virt + (to % state->win_size), from, len);
154 166
155 /* operation does not cross the win_size, so one shot it */ 167 len -= this_len;
156 memcpy_toio(map->virt + (to % state->win_size), from, len); 168 to += this_len;
169 from += this_len;
170 }
157} 171}
158 172
159static const char * const part_probe_types[] = { 173static const char * const part_probe_types[] = {