diff options
author | Juuso Oikarinen <juuso.oikarinen@nokia.com> | 2010-02-22 01:38:31 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-03-09 15:03:01 -0500 |
commit | ffb591cd0e32d817bdbd359dead3baa770b999f8 (patch) | |
tree | eb4ff124e93543fce011e634de3c3c0ac677ca2a /drivers/net/wireless | |
parent | 93c5bb68c89eff0cd41afce8ac932d12cc9d7ae8 (diff) |
wl1271: Improvements to the TX path
- Fix a TX result overflow problem that was present in the TX path and visible
with at least linksys AP's (probably any AP with high throughput capability.)
- Optimize TX by writing FW trigger for a group of TX frames instead of
each and every frame.
- Slightly optimize the TX path code.
Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Reviewed-by: Kalle Valo <kalle.valo@nokia.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_main.c | 8 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_tx.c | 38 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_tx.h | 2 |
4 files changed, 29 insertions, 23 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 10135c94e20e..cc974eae009e 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h | |||
@@ -396,10 +396,10 @@ struct wl1271 { | |||
396 | /* Accounting for allocated / available TX blocks on HW */ | 396 | /* Accounting for allocated / available TX blocks on HW */ |
397 | u32 tx_blocks_freed[NUM_TX_QUEUES]; | 397 | u32 tx_blocks_freed[NUM_TX_QUEUES]; |
398 | u32 tx_blocks_available; | 398 | u32 tx_blocks_available; |
399 | u8 tx_results_count; | 399 | u32 tx_results_count; |
400 | 400 | ||
401 | /* Transmitted TX packets counter for chipset interface */ | 401 | /* Transmitted TX packets counter for chipset interface */ |
402 | int tx_packets_count; | 402 | u32 tx_packets_count; |
403 | 403 | ||
404 | /* Time-offset between host and chipset clocks */ | 404 | /* Time-offset between host and chipset clocks */ |
405 | int time_offset; | 405 | int time_offset; |
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 310f58c66231..5cc778f658b9 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c | |||
@@ -453,14 +453,12 @@ static void wl1271_irq_work(struct work_struct *work) | |||
453 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE"); | 453 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE"); |
454 | 454 | ||
455 | if (intr & WL1271_ACX_INTR_DATA) { | 455 | if (intr & WL1271_ACX_INTR_DATA) { |
456 | u8 tx_res_cnt = wl->fw_status->tx_results_counter - | ||
457 | wl->tx_results_count; | ||
458 | |||
459 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); | 456 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); |
460 | 457 | ||
461 | /* check for tx results */ | 458 | /* check for tx results */ |
462 | if (tx_res_cnt) | 459 | if (wl->fw_status->tx_results_counter != |
463 | wl1271_tx_complete(wl, tx_res_cnt); | 460 | (wl->tx_results_count & 0xff)) |
461 | wl1271_tx_complete(wl); | ||
464 | 462 | ||
465 | wl1271_rx(wl, wl->fw_status); | 463 | wl1271_rx(wl, wl->fw_status); |
466 | } | 464 | } |
diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c index 2b7dd9b76fe1..2e057b0e3257 100644 --- a/drivers/net/wireless/wl12xx/wl1271_tx.c +++ b/drivers/net/wireless/wl12xx/wl1271_tx.c | |||
@@ -169,7 +169,6 @@ static int wl1271_tx_send_packet(struct wl1271 *wl, struct sk_buff *skb, | |||
169 | 169 | ||
170 | /* write packet new counter into the write access register */ | 170 | /* write packet new counter into the write access register */ |
171 | wl->tx_packets_count++; | 171 | wl->tx_packets_count++; |
172 | wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); | ||
173 | 172 | ||
174 | desc = (struct wl1271_tx_hw_descr *) skb->data; | 173 | desc = (struct wl1271_tx_hw_descr *) skb->data; |
175 | wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)", | 174 | wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)", |
@@ -244,6 +243,7 @@ void wl1271_tx_work(struct work_struct *work) | |||
244 | struct sk_buff *skb; | 243 | struct sk_buff *skb; |
245 | bool woken_up = false; | 244 | bool woken_up = false; |
246 | u32 sta_rates = 0; | 245 | u32 sta_rates = 0; |
246 | u32 prev_tx_packets_count; | ||
247 | int ret; | 247 | int ret; |
248 | 248 | ||
249 | /* check if the rates supported by the AP have changed */ | 249 | /* check if the rates supported by the AP have changed */ |
@@ -260,6 +260,8 @@ void wl1271_tx_work(struct work_struct *work) | |||
260 | if (unlikely(wl->state == WL1271_STATE_OFF)) | 260 | if (unlikely(wl->state == WL1271_STATE_OFF)) |
261 | goto out; | 261 | goto out; |
262 | 262 | ||
263 | prev_tx_packets_count = wl->tx_packets_count; | ||
264 | |||
263 | /* if rates have changed, re-configure the rate policy */ | 265 | /* if rates have changed, re-configure the rate policy */ |
264 | if (unlikely(sta_rates)) { | 266 | if (unlikely(sta_rates)) { |
265 | wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates); | 267 | wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates); |
@@ -270,7 +272,7 @@ void wl1271_tx_work(struct work_struct *work) | |||
270 | if (!woken_up) { | 272 | if (!woken_up) { |
271 | ret = wl1271_ps_elp_wakeup(wl, false); | 273 | ret = wl1271_ps_elp_wakeup(wl, false); |
272 | if (ret < 0) | 274 | if (ret < 0) |
273 | goto out; | 275 | goto out_ack; |
274 | woken_up = true; | 276 | woken_up = true; |
275 | } | 277 | } |
276 | 278 | ||
@@ -282,10 +284,10 @@ void wl1271_tx_work(struct work_struct *work) | |||
282 | ieee80211_stop_queues(wl->hw); | 284 | ieee80211_stop_queues(wl->hw); |
283 | set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); | 285 | set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); |
284 | skb_queue_head(&wl->tx_queue, skb); | 286 | skb_queue_head(&wl->tx_queue, skb); |
285 | goto out; | 287 | goto out_ack; |
286 | } else if (ret < 0) { | 288 | } else if (ret < 0) { |
287 | dev_kfree_skb(skb); | 289 | dev_kfree_skb(skb); |
288 | goto out; | 290 | goto out_ack; |
289 | } else if (test_and_clear_bit(WL1271_FLAG_TX_QUEUE_STOPPED, | 291 | } else if (test_and_clear_bit(WL1271_FLAG_TX_QUEUE_STOPPED, |
290 | &wl->flags)) { | 292 | &wl->flags)) { |
291 | /* firmware buffer has space, restart queues */ | 293 | /* firmware buffer has space, restart queues */ |
@@ -295,6 +297,11 @@ void wl1271_tx_work(struct work_struct *work) | |||
295 | } | 297 | } |
296 | } | 298 | } |
297 | 299 | ||
300 | out_ack: | ||
301 | /* interrupt the firmware with the new packets */ | ||
302 | if (prev_tx_packets_count != wl->tx_packets_count) | ||
303 | wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); | ||
304 | |||
298 | out: | 305 | out: |
299 | if (woken_up) | 306 | if (woken_up) |
300 | wl1271_ps_elp_sleep(wl); | 307 | wl1271_ps_elp_sleep(wl); |
@@ -311,7 +318,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, | |||
311 | int id = result->id; | 318 | int id = result->id; |
312 | 319 | ||
313 | /* check for id legality */ | 320 | /* check for id legality */ |
314 | if (id >= ACX_TX_DESCRIPTORS || wl->tx_frames[id] == NULL) { | 321 | if (unlikely(id >= ACX_TX_DESCRIPTORS || wl->tx_frames[id] == NULL)) { |
315 | wl1271_warning("TX result illegal id: %d", id); | 322 | wl1271_warning("TX result illegal id: %d", id); |
316 | return; | 323 | return; |
317 | } | 324 | } |
@@ -366,10 +373,11 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, | |||
366 | } | 373 | } |
367 | 374 | ||
368 | /* Called upon reception of a TX complete interrupt */ | 375 | /* Called upon reception of a TX complete interrupt */ |
369 | void wl1271_tx_complete(struct wl1271 *wl, u32 count) | 376 | void wl1271_tx_complete(struct wl1271 *wl) |
370 | { | 377 | { |
371 | struct wl1271_acx_mem_map *memmap = | 378 | struct wl1271_acx_mem_map *memmap = |
372 | (struct wl1271_acx_mem_map *)wl->target_mem_map; | 379 | (struct wl1271_acx_mem_map *)wl->target_mem_map; |
380 | u32 count, fw_counter; | ||
373 | u32 i; | 381 | u32 i; |
374 | 382 | ||
375 | wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count); | 383 | wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count); |
@@ -377,12 +385,18 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count) | |||
377 | /* read the tx results from the chipset */ | 385 | /* read the tx results from the chipset */ |
378 | wl1271_read(wl, le32_to_cpu(memmap->tx_result), | 386 | wl1271_read(wl, le32_to_cpu(memmap->tx_result), |
379 | wl->tx_res_if, sizeof(*wl->tx_res_if), false); | 387 | wl->tx_res_if, sizeof(*wl->tx_res_if), false); |
388 | fw_counter = le32_to_cpu(wl->tx_res_if->tx_result_fw_counter); | ||
389 | |||
390 | /* write host counter to chipset (to ack) */ | ||
391 | wl1271_write32(wl, le32_to_cpu(memmap->tx_result) + | ||
392 | offsetof(struct wl1271_tx_hw_res_if, | ||
393 | tx_result_host_counter), fw_counter); | ||
394 | |||
395 | count = fw_counter - wl->tx_results_count; | ||
380 | 396 | ||
381 | /* verify that the result buffer is not getting overrun */ | 397 | /* verify that the result buffer is not getting overrun */ |
382 | if (count > TX_HW_RESULT_QUEUE_LEN) { | 398 | if (unlikely(count > TX_HW_RESULT_QUEUE_LEN)) |
383 | wl1271_warning("TX result overflow from chipset: %d", count); | 399 | wl1271_warning("TX result overflow from chipset: %d", count); |
384 | count = TX_HW_RESULT_QUEUE_LEN; | ||
385 | } | ||
386 | 400 | ||
387 | /* process the results */ | 401 | /* process the results */ |
388 | for (i = 0; i < count; i++) { | 402 | for (i = 0; i < count; i++) { |
@@ -395,12 +409,6 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count) | |||
395 | 409 | ||
396 | wl->tx_results_count++; | 410 | wl->tx_results_count++; |
397 | } | 411 | } |
398 | |||
399 | /* write host counter to chipset (to ack) */ | ||
400 | wl1271_write32(wl, le32_to_cpu(memmap->tx_result) + | ||
401 | offsetof(struct wl1271_tx_hw_res_if, | ||
402 | tx_result_host_counter), | ||
403 | le32_to_cpu(wl->tx_res_if->tx_result_fw_counter)); | ||
404 | } | 412 | } |
405 | 413 | ||
406 | /* caller must hold wl->mutex */ | 414 | /* caller must hold wl->mutex */ |
diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.h b/drivers/net/wireless/wl12xx/wl1271_tx.h index 17e405a09caa..ca92bd811292 100644 --- a/drivers/net/wireless/wl12xx/wl1271_tx.h +++ b/drivers/net/wireless/wl12xx/wl1271_tx.h | |||
@@ -160,7 +160,7 @@ static inline int wl1271_tx_ac_to_tid(int ac) | |||
160 | } | 160 | } |
161 | 161 | ||
162 | void wl1271_tx_work(struct work_struct *work); | 162 | void wl1271_tx_work(struct work_struct *work); |
163 | void wl1271_tx_complete(struct wl1271 *wl, u32 count); | 163 | void wl1271_tx_complete(struct wl1271 *wl); |
164 | void wl1271_tx_flush(struct wl1271 *wl); | 164 | void wl1271_tx_flush(struct wl1271 *wl); |
165 | 165 | ||
166 | #endif | 166 | #endif |