diff options
author | Juuso Oikarinen <juuso.oikarinen@nokia.com> | 2010-02-22 01:38:37 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-03-09 15:03:03 -0500 |
commit | 1e73eb62cec7cf78b7295769b6e51a915518f5a1 (patch) | |
tree | 3a9cafedbe96d9953a9bcaee86bad9510c04dadf /drivers/net/wireless/wl12xx/wl1271_main.c | |
parent | 4aa05917051b01da037a80c3207b48aee252eed2 (diff) |
wl1271: Implement looped IRQ handling
This patch implements looped IRQ handling. In essence, if a new interrupt is
asserted by the FW while the host is processing the previous one, the host
will directly proceed processing the new IRQ without leaving the handling
function.
Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Reviewed-by: Teemu Paasikivi <ext-teemu.3.paasikivi@nokia.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/wl12xx/wl1271_main.c')
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_main.c | 76 |
1 files changed, 47 insertions, 29 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index a46d323f8a6e..9f7416864053 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c | |||
@@ -406,10 +406,14 @@ static void wl1271_fw_status(struct wl1271 *wl, | |||
406 | le32_to_cpu(status->fw_localtime); | 406 | le32_to_cpu(status->fw_localtime); |
407 | } | 407 | } |
408 | 408 | ||
409 | #define WL1271_IRQ_MAX_LOOPS 10 | ||
410 | |||
409 | static void wl1271_irq_work(struct work_struct *work) | 411 | static void wl1271_irq_work(struct work_struct *work) |
410 | { | 412 | { |
411 | int ret; | 413 | int ret; |
412 | u32 intr; | 414 | u32 intr; |
415 | int loopcount = WL1271_IRQ_MAX_LOOPS; | ||
416 | unsigned long flags; | ||
413 | struct wl1271 *wl = | 417 | struct wl1271 *wl = |
414 | container_of(work, struct wl1271, irq_work); | 418 | container_of(work, struct wl1271, irq_work); |
415 | 419 | ||
@@ -417,51 +421,65 @@ static void wl1271_irq_work(struct work_struct *work) | |||
417 | 421 | ||
418 | wl1271_debug(DEBUG_IRQ, "IRQ work"); | 422 | wl1271_debug(DEBUG_IRQ, "IRQ work"); |
419 | 423 | ||
420 | if (wl->state == WL1271_STATE_OFF) | 424 | if (unlikely(wl->state == WL1271_STATE_OFF)) |
421 | goto out; | 425 | goto out; |
422 | 426 | ||
423 | ret = wl1271_ps_elp_wakeup(wl, true); | 427 | ret = wl1271_ps_elp_wakeup(wl, true); |
424 | if (ret < 0) | 428 | if (ret < 0) |
425 | goto out; | 429 | goto out; |
426 | 430 | ||
427 | wl1271_fw_status(wl, wl->fw_status); | 431 | spin_lock_irqsave(&wl->wl_lock, flags); |
428 | intr = le32_to_cpu(wl->fw_status->intr); | 432 | while (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags) && loopcount) { |
429 | if (!intr) { | 433 | clear_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags); |
430 | wl1271_debug(DEBUG_IRQ, "Zero interrupt received."); | 434 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
431 | goto out_sleep; | 435 | loopcount--; |
432 | } | ||
433 | 436 | ||
434 | intr &= WL1271_INTR_MASK; | 437 | wl1271_fw_status(wl, wl->fw_status); |
438 | intr = le32_to_cpu(wl->fw_status->intr); | ||
439 | if (!intr) { | ||
440 | wl1271_debug(DEBUG_IRQ, "Zero interrupt received."); | ||
441 | continue; | ||
442 | } | ||
435 | 443 | ||
436 | if (intr & WL1271_ACX_INTR_EVENT_A) { | 444 | intr &= WL1271_INTR_MASK; |
437 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A"); | ||
438 | wl1271_event_handle(wl, 0); | ||
439 | } | ||
440 | 445 | ||
441 | if (intr & WL1271_ACX_INTR_EVENT_B) { | 446 | if (intr & WL1271_ACX_INTR_DATA) { |
442 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B"); | 447 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); |
443 | wl1271_event_handle(wl, 1); | ||
444 | } | ||
445 | 448 | ||
446 | if (intr & WL1271_ACX_INTR_INIT_COMPLETE) | 449 | /* check for tx results */ |
447 | wl1271_debug(DEBUG_IRQ, | 450 | if (wl->fw_status->tx_results_counter != |
448 | "WL1271_ACX_INTR_INIT_COMPLETE"); | 451 | (wl->tx_results_count & 0xff)) |
452 | wl1271_tx_complete(wl); | ||
449 | 453 | ||
450 | if (intr & WL1271_ACX_INTR_HW_AVAILABLE) | 454 | wl1271_rx(wl, wl->fw_status); |
451 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE"); | 455 | } |
456 | |||
457 | if (intr & WL1271_ACX_INTR_EVENT_A) { | ||
458 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A"); | ||
459 | wl1271_event_handle(wl, 0); | ||
460 | } | ||
461 | |||
462 | if (intr & WL1271_ACX_INTR_EVENT_B) { | ||
463 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B"); | ||
464 | wl1271_event_handle(wl, 1); | ||
465 | } | ||
452 | 466 | ||
453 | if (intr & WL1271_ACX_INTR_DATA) { | 467 | if (intr & WL1271_ACX_INTR_INIT_COMPLETE) |
454 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); | 468 | wl1271_debug(DEBUG_IRQ, |
469 | "WL1271_ACX_INTR_INIT_COMPLETE"); | ||
455 | 470 | ||
456 | /* check for tx results */ | 471 | if (intr & WL1271_ACX_INTR_HW_AVAILABLE) |
457 | if (wl->fw_status->tx_results_counter != | 472 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE"); |
458 | (wl->tx_results_count & 0xff)) | ||
459 | wl1271_tx_complete(wl); | ||
460 | 473 | ||
461 | wl1271_rx(wl, wl->fw_status); | 474 | spin_lock_irqsave(&wl->wl_lock, flags); |
462 | } | 475 | } |
463 | 476 | ||
464 | out_sleep: | 477 | if (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags)) |
478 | ieee80211_queue_work(wl->hw, &wl->irq_work); | ||
479 | else | ||
480 | clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); | ||
481 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
482 | |||
465 | wl1271_ps_elp_sleep(wl); | 483 | wl1271_ps_elp_sleep(wl); |
466 | 484 | ||
467 | out: | 485 | out: |