diff options
author | Ido Yariv <ido@wizery.com> | 2010-09-30 07:28:26 -0400 |
---|---|---|
committer | Luciano Coelho <luciano.coelho@nokia.com> | 2010-10-05 09:27:24 -0400 |
commit | 5c57a901dc96fc81d0041282269b43542f170d2a (patch) | |
tree | 7ee87d336cf3b85b6429cba098329ac784e96092 /drivers/net | |
parent | 65836112fc24bdf009554481b36b6ba0a690b855 (diff) |
wl1271: Handle large SPI transfers
The HW supports up to 4095 bytes transfers via SPI. The SPI read & write
operations do not handle larger transfers, causing the HW to stall in such
cases.
Fix this by fragmenting large transfers into smaller chunks, and
transferring each one separately.
Signed-off-by: Ido Yariv <ido@wizery.com>
Tested-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_spi.c | 140 |
1 files changed, 86 insertions, 54 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index 75cbf36146e2..ef801680773f 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c | |||
@@ -63,6 +63,11 @@ | |||
63 | ((WL1271_BUSY_WORD_LEN - 4) / sizeof(u32)) | 63 | ((WL1271_BUSY_WORD_LEN - 4) / sizeof(u32)) |
64 | #define HW_ACCESS_WSPI_INIT_CMD_MASK 0 | 64 | #define HW_ACCESS_WSPI_INIT_CMD_MASK 0 |
65 | 65 | ||
66 | /* HW limitation: maximum possible chunk size is 4095 bytes */ | ||
67 | #define WSPI_MAX_CHUNK_SIZE 4092 | ||
68 | |||
69 | #define WSPI_MAX_NUM_OF_CHUNKS (WL1271_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) | ||
70 | |||
66 | static inline struct spi_device *wl_to_spi(struct wl1271 *wl) | 71 | static inline struct spi_device *wl_to_spi(struct wl1271 *wl) |
67 | { | 72 | { |
68 | return wl->if_priv; | 73 | return wl->if_priv; |
@@ -202,90 +207,117 @@ static int wl1271_spi_read_busy(struct wl1271 *wl) | |||
202 | static void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf, | 207 | static void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf, |
203 | size_t len, bool fixed) | 208 | size_t len, bool fixed) |
204 | { | 209 | { |
205 | struct spi_transfer t[3]; | 210 | struct spi_transfer t[2]; |
206 | struct spi_message m; | 211 | struct spi_message m; |
207 | u32 *busy_buf; | 212 | u32 *busy_buf; |
208 | u32 *cmd; | 213 | u32 *cmd; |
214 | u32 chunk_len; | ||
209 | 215 | ||
210 | cmd = &wl->buffer_cmd; | 216 | while (len > 0) { |
211 | busy_buf = wl->buffer_busyword; | 217 | chunk_len = min((size_t)WSPI_MAX_CHUNK_SIZE, len); |
212 | 218 | ||
213 | *cmd = 0; | 219 | cmd = &wl->buffer_cmd; |
214 | *cmd |= WSPI_CMD_READ; | 220 | busy_buf = wl->buffer_busyword; |
215 | *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; | ||
216 | *cmd |= addr & WSPI_CMD_BYTE_ADDR; | ||
217 | 221 | ||
218 | if (fixed) | 222 | *cmd = 0; |
219 | *cmd |= WSPI_CMD_FIXED; | 223 | *cmd |= WSPI_CMD_READ; |
224 | *cmd |= (chunk_len << WSPI_CMD_BYTE_LENGTH_OFFSET) & | ||
225 | WSPI_CMD_BYTE_LENGTH; | ||
226 | *cmd |= addr & WSPI_CMD_BYTE_ADDR; | ||
220 | 227 | ||
221 | spi_message_init(&m); | 228 | if (fixed) |
222 | memset(t, 0, sizeof(t)); | 229 | *cmd |= WSPI_CMD_FIXED; |
223 | 230 | ||
224 | t[0].tx_buf = cmd; | 231 | spi_message_init(&m); |
225 | t[0].len = 4; | 232 | memset(t, 0, sizeof(t)); |
226 | t[0].cs_change = true; | ||
227 | spi_message_add_tail(&t[0], &m); | ||
228 | 233 | ||
229 | /* Busy and non busy words read */ | 234 | t[0].tx_buf = cmd; |
230 | t[1].rx_buf = busy_buf; | 235 | t[0].len = 4; |
231 | t[1].len = WL1271_BUSY_WORD_LEN; | 236 | t[0].cs_change = true; |
232 | t[1].cs_change = true; | 237 | spi_message_add_tail(&t[0], &m); |
233 | spi_message_add_tail(&t[1], &m); | ||
234 | 238 | ||
235 | spi_sync(wl_to_spi(wl), &m); | 239 | /* Busy and non busy words read */ |
240 | t[1].rx_buf = busy_buf; | ||
241 | t[1].len = WL1271_BUSY_WORD_LEN; | ||
242 | t[1].cs_change = true; | ||
243 | spi_message_add_tail(&t[1], &m); | ||
236 | 244 | ||
237 | if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1) && | 245 | spi_sync(wl_to_spi(wl), &m); |
238 | wl1271_spi_read_busy(wl)) { | ||
239 | memset(buf, 0, len); | ||
240 | return; | ||
241 | } | ||
242 | 246 | ||
243 | spi_message_init(&m); | 247 | if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1) && |
244 | memset(t, 0, sizeof(t)); | 248 | wl1271_spi_read_busy(wl)) { |
249 | memset(buf, 0, chunk_len); | ||
250 | return; | ||
251 | } | ||
245 | 252 | ||
246 | t[0].rx_buf = buf; | 253 | spi_message_init(&m); |
247 | t[0].len = len; | 254 | memset(t, 0, sizeof(t)); |
248 | t[0].cs_change = true; | ||
249 | spi_message_add_tail(&t[0], &m); | ||
250 | 255 | ||
251 | spi_sync(wl_to_spi(wl), &m); | 256 | t[0].rx_buf = buf; |
257 | t[0].len = chunk_len; | ||
258 | t[0].cs_change = true; | ||
259 | spi_message_add_tail(&t[0], &m); | ||
260 | |||
261 | spi_sync(wl_to_spi(wl), &m); | ||
252 | 262 | ||
253 | wl1271_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd)); | 263 | wl1271_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd)); |
254 | wl1271_dump(DEBUG_SPI, "spi_read buf <- ", buf, len); | 264 | wl1271_dump(DEBUG_SPI, "spi_read buf <- ", buf, chunk_len); |
265 | |||
266 | if (!fixed) | ||
267 | addr += chunk_len; | ||
268 | buf += chunk_len; | ||
269 | len -= chunk_len; | ||
270 | } | ||
255 | } | 271 | } |
256 | 272 | ||
257 | static void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf, | 273 | static void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf, |
258 | size_t len, bool fixed) | 274 | size_t len, bool fixed) |
259 | { | 275 | { |
260 | struct spi_transfer t[2]; | 276 | struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS]; |
261 | struct spi_message m; | 277 | struct spi_message m; |
278 | u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; | ||
262 | u32 *cmd; | 279 | u32 *cmd; |
280 | u32 chunk_len; | ||
281 | int i; | ||
263 | 282 | ||
264 | cmd = &wl->buffer_cmd; | 283 | WARN_ON(len > WL1271_AGGR_BUFFER_SIZE); |
265 | |||
266 | *cmd = 0; | ||
267 | *cmd |= WSPI_CMD_WRITE; | ||
268 | *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; | ||
269 | *cmd |= addr & WSPI_CMD_BYTE_ADDR; | ||
270 | |||
271 | if (fixed) | ||
272 | *cmd |= WSPI_CMD_FIXED; | ||
273 | 284 | ||
274 | spi_message_init(&m); | 285 | spi_message_init(&m); |
275 | memset(t, 0, sizeof(t)); | 286 | memset(t, 0, sizeof(t)); |
276 | 287 | ||
277 | t[0].tx_buf = cmd; | 288 | cmd = &commands[0]; |
278 | t[0].len = sizeof(*cmd); | 289 | i = 0; |
279 | spi_message_add_tail(&t[0], &m); | 290 | while (len > 0) { |
291 | chunk_len = min((size_t)WSPI_MAX_CHUNK_SIZE, len); | ||
280 | 292 | ||
281 | t[1].tx_buf = buf; | 293 | *cmd = 0; |
282 | t[1].len = len; | 294 | *cmd |= WSPI_CMD_WRITE; |
283 | spi_message_add_tail(&t[1], &m); | 295 | *cmd |= (chunk_len << WSPI_CMD_BYTE_LENGTH_OFFSET) & |
296 | WSPI_CMD_BYTE_LENGTH; | ||
297 | *cmd |= addr & WSPI_CMD_BYTE_ADDR; | ||
284 | 298 | ||
285 | spi_sync(wl_to_spi(wl), &m); | 299 | if (fixed) |
300 | *cmd |= WSPI_CMD_FIXED; | ||
301 | |||
302 | t[i].tx_buf = cmd; | ||
303 | t[i].len = sizeof(*cmd); | ||
304 | spi_message_add_tail(&t[i++], &m); | ||
305 | |||
306 | t[i].tx_buf = buf; | ||
307 | t[i].len = chunk_len; | ||
308 | spi_message_add_tail(&t[i++], &m); | ||
286 | 309 | ||
287 | wl1271_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd)); | 310 | wl1271_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd)); |
288 | wl1271_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); | 311 | wl1271_dump(DEBUG_SPI, "spi_write buf -> ", buf, chunk_len); |
312 | |||
313 | if (!fixed) | ||
314 | addr += chunk_len; | ||
315 | buf += chunk_len; | ||
316 | len -= chunk_len; | ||
317 | cmd++; | ||
318 | } | ||
319 | |||
320 | spi_sync(wl_to_spi(wl), &m); | ||
289 | } | 321 | } |
290 | 322 | ||
291 | static irqreturn_t wl1271_irq(int irq, void *cookie) | 323 | static irqreturn_t wl1271_irq(int irq, void *cookie) |