wl1271: Implement looped IRQ handling
authorJuuso Oikarinen <juuso.oikarinen@nokia.com>
Mon, 22 Feb 2010 06:38:37 +0000 (08:38 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 9 Mar 2010 20:03:03 +0000 (15:03 -0500)
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>
drivers/net/wireless/wl12xx/wl1271.h
drivers/net/wireless/wl12xx/wl1271_main.c
drivers/net/wireless/wl12xx/wl1271_sdio.c
drivers/net/wireless/wl12xx/wl1271_spi.c

index 7f03f899b2b01c101f2968dbc6254fd7c9a2167c..457ce725027ac3e8316a638efd38f552d1c7f70b 100644 (file)
@@ -371,6 +371,8 @@ struct wl1271 {
 #define WL1271_FLAG_IN_ELP             (6)
 #define WL1271_FLAG_PSM                (7)
 #define WL1271_FLAG_PSM_REQUESTED      (8)
+#define WL1271_FLAG_IRQ_PENDING        (9)
+#define WL1271_FLAG_IRQ_RUNNING       (10)
        unsigned long flags;
 
        struct wl1271_partition_set part;
index a46d323f8a6e927d0d98a0ecbf554076df7068eb..9f741686405310120db588cb73115057d09d1fed 100644 (file)
@@ -406,10 +406,14 @@ static void wl1271_fw_status(struct wl1271 *wl,
                le32_to_cpu(status->fw_localtime);
 }
 
+#define WL1271_IRQ_MAX_LOOPS 10
+
 static void wl1271_irq_work(struct work_struct *work)
 {
        int ret;
        u32 intr;
+       int loopcount = WL1271_IRQ_MAX_LOOPS;
+       unsigned long flags;
        struct wl1271 *wl =
                container_of(work, struct wl1271, irq_work);
 
@@ -417,51 +421,65 @@ static void wl1271_irq_work(struct work_struct *work)
 
        wl1271_debug(DEBUG_IRQ, "IRQ work");
 
-       if (wl->state == WL1271_STATE_OFF)
+       if (unlikely(wl->state == WL1271_STATE_OFF))
                goto out;
 
        ret = wl1271_ps_elp_wakeup(wl, true);
        if (ret < 0)
                goto out;
 
-       wl1271_fw_status(wl, wl->fw_status);
-       intr = le32_to_cpu(wl->fw_status->intr);
-       if (!intr) {
-               wl1271_debug(DEBUG_IRQ, "Zero interrupt received.");
-               goto out_sleep;
-       }
+       spin_lock_irqsave(&wl->wl_lock, flags);
+       while (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags) && loopcount) {
+               clear_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags);
+               spin_unlock_irqrestore(&wl->wl_lock, flags);
+               loopcount--;
 
-       intr &= WL1271_INTR_MASK;
+               wl1271_fw_status(wl, wl->fw_status);
+               intr = le32_to_cpu(wl->fw_status->intr);
+               if (!intr) {
+                       wl1271_debug(DEBUG_IRQ, "Zero interrupt received.");
+                       continue;
+               }
 
-       if (intr & WL1271_ACX_INTR_EVENT_A) {
-               wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
-               wl1271_event_handle(wl, 0);
-       }
+               intr &= WL1271_INTR_MASK;
 
-       if (intr & WL1271_ACX_INTR_EVENT_B) {
-               wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
-               wl1271_event_handle(wl, 1);
-       }
+               if (intr & WL1271_ACX_INTR_DATA) {
+                       wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
 
-       if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
-               wl1271_debug(DEBUG_IRQ,
-                            "WL1271_ACX_INTR_INIT_COMPLETE");
+                       /* check for tx results */
+                       if (wl->fw_status->tx_results_counter !=
+                           (wl->tx_results_count & 0xff))
+                               wl1271_tx_complete(wl);
 
-       if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
-               wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
+                       wl1271_rx(wl, wl->fw_status);
+               }
+
+               if (intr & WL1271_ACX_INTR_EVENT_A) {
+                       wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
+                       wl1271_event_handle(wl, 0);
+               }
+
+               if (intr & WL1271_ACX_INTR_EVENT_B) {
+                       wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
+                       wl1271_event_handle(wl, 1);
+               }
 
-       if (intr & WL1271_ACX_INTR_DATA) {
-               wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
+               if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
+                       wl1271_debug(DEBUG_IRQ,
+                                    "WL1271_ACX_INTR_INIT_COMPLETE");
 
-               /* check for tx results */
-               if (wl->fw_status->tx_results_counter !=
-                   (wl->tx_results_count & 0xff))
-                       wl1271_tx_complete(wl);
+               if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
+                       wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
 
-               wl1271_rx(wl, wl->fw_status);
+               spin_lock_irqsave(&wl->wl_lock, flags);
        }
 
-out_sleep:
+       if (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags))
+               ieee80211_queue_work(wl->hw, &wl->irq_work);
+       else
+               clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
+
        wl1271_ps_elp_sleep(wl);
 
 out:
index 7d7c8504185927181bd6c32b7596d1c406340e8b..1f204db30c27be83a400c86e0c985d713929757c 100644 (file)
@@ -75,7 +75,9 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
                wl->elp_compl = NULL;
        }
 
-       ieee80211_queue_work(wl->hw, &wl->irq_work);
+       if (!test_and_set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags))
+               ieee80211_queue_work(wl->hw, &wl->irq_work);
+       set_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 
        return IRQ_HANDLED;
index 51171d7d5da37a680335a79dd694d8f5bdef950a..ed285fec2a08c466143f10f5ec8e873071e9573d 100644 (file)
@@ -324,7 +324,9 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
                wl->elp_compl = NULL;
        }
 
-       ieee80211_queue_work(wl->hw, &wl->irq_work);
+       if (!test_and_set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags))
+               ieee80211_queue_work(wl->hw, &wl->irq_work);
+       set_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 
        return IRQ_HANDLED;