wl1251: use ELP wakeup interrupt instead of polling
authorGrazvydas Ignotas <notasas@gmail.com>
Wed, 27 Oct 2010 21:39:40 +0000 (00:39 +0300)
committerGrazvydas Ignotas <notasas@gmail.com>
Sat, 5 Mar 2011 21:18:01 +0000 (23:18 +0200)
The chip can generate an interrupt on ELP wakeup, make use of it instead
of polling the status register. This reduces wakeup time to ~7ms from
~8-20ms (the wait loop was sleeping, so could take up to 20ms depending
on how system timer is configured). Note that DEBUG_PSM does not report
this accurately as it only measures jiffies.

This patch also moves atomic portion of interrupt processing to main.c
to avoid code duplication.

Signed-off-by: Grazvydas Ignotas <notasas@gmail.com>
drivers/net/wireless/wl12xx/wl1251.h
drivers/net/wireless/wl12xx/wl1251_main.c
drivers/net/wireless/wl12xx/wl1251_ps.c
drivers/net/wireless/wl12xx/wl1251_reg.h
drivers/net/wireless/wl12xx/wl1251_sdio.c
drivers/net/wireless/wl12xx/wl1251_spi.c

index 46dd160..85e4640 100644 (file)
@@ -359,6 +359,7 @@ struct wl1251 {
        bool elp;
 
        struct delayed_work elp_work;
        bool elp;
 
        struct delayed_work elp_work;
+       struct completion *elp_compl;
 
        /* we can be in psm, but not in elp, we have to differentiate */
        bool psm;
 
        /* we can be in psm, but not in elp, we have to differentiate */
        bool psm;
@@ -394,6 +395,7 @@ int wl1251_free_hw(struct wl1251 *wl);
 int wl1251_init_ieee80211(struct wl1251 *wl);
 void wl1251_enable_interrupts(struct wl1251 *wl);
 void wl1251_disable_interrupts(struct wl1251 *wl);
 int wl1251_init_ieee80211(struct wl1251 *wl);
 void wl1251_enable_interrupts(struct wl1251 *wl);
 void wl1251_disable_interrupts(struct wl1251 *wl);
+void wl1251_irq(struct wl1251 *wl);
 
 #define DEFAULT_HW_GEN_MODULATION_TYPE    CCK_LONG /* Long Preamble */
 #define DEFAULT_HW_GEN_TX_RATE          RATE_2MBPS
 
 #define DEFAULT_HW_GEN_MODULATION_TYPE    CCK_LONG /* Long Preamble */
 #define DEFAULT_HW_GEN_TX_RATE          RATE_2MBPS
index 8b3e78e..fcc6829 100644 (file)
@@ -323,6 +323,25 @@ out:
        mutex_unlock(&wl->mutex);
 }
 
        mutex_unlock(&wl->mutex);
 }
 
+/* Atomic irq handler */
+void wl1251_irq(struct wl1251 *wl)
+{
+       unsigned long flags;
+
+       wl1251_debug(DEBUG_IRQ, "IRQ");
+
+       /* complete the ELP completion */
+       spin_lock_irqsave(&wl->wl_lock, flags);
+       if (wl->elp_compl) {
+               complete(wl->elp_compl);
+               wl->elp_compl = NULL;
+       }
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+       ieee80211_queue_work(wl->hw, &wl->irq_work);
+}
+EXPORT_SYMBOL_GPL(wl1251_irq);
+
 static void wl1251_convert_filters(struct wl1251 *wl,
                                  unsigned int mac80211_filters)
 {
 static void wl1251_convert_filters(struct wl1251 *wl,
                                  unsigned int mac80211_filters)
 {
index f5b90a1..4e4c661 100644 (file)
@@ -67,8 +67,10 @@ void wl1251_ps_elp_sleep(struct wl1251 *wl)
 
 int wl1251_ps_elp_wakeup(struct wl1251 *wl)
 {
 
 int wl1251_ps_elp_wakeup(struct wl1251 *wl)
 {
-       unsigned long timeout, start;
+       DECLARE_COMPLETION_ONSTACK(compl);
+       unsigned long start;
        u32 elp_reg;
        u32 elp_reg;
+       int ret;
 
        if (delayed_work_pending(&wl->elp_work))
                cancel_delayed_work(&wl->elp_work);
 
        if (delayed_work_pending(&wl->elp_work))
                cancel_delayed_work(&wl->elp_work);
@@ -79,31 +81,42 @@ int wl1251_ps_elp_wakeup(struct wl1251 *wl)
        wl1251_debug(DEBUG_PSM, "waking up chip from elp");
 
        start = jiffies;
        wl1251_debug(DEBUG_PSM, "waking up chip from elp");
 
        start = jiffies;
-       timeout = jiffies + msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT);
-
-       wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP);
 
        elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
 
 
        elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
 
-       /*
-        * FIXME: we should wait for irq from chip but, as a temporary
-        * solution to simplify locking, let's poll instead
-        */
-       while (!(elp_reg & ELPCTRL_WLAN_READY)) {
-               if (time_after(jiffies, timeout)) {
-                       wl1251_error("elp wakeup timeout");
-                       return -ETIMEDOUT;
+       wl->elp_compl = &compl;
+       wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR,
+                        ELPCTRL_WAKE_UP | ELPCTRL_IRQ_SRC);
+
+       elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
+       if (!(elp_reg & ELPCTRL_WLAN_READY)) {
+               ret = wait_for_completion_timeout(
+                       &compl, msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT));
+               if (ret == 0) {
+                       wl1251_error("ELP wakeup timeout");
+                       ret = -ETIMEDOUT;
+                       goto err;
+               } else if (ret < 0) {
+                       wl1251_error("ELP wakeup completion error.");
+                       goto err;
                }
                }
-               msleep(1);
-               elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
        }
 
        }
 
+       wl->elp_compl = NULL;
+
+       /* clear IRQ_SRC to switch irq source back from WLAN_RDY to HOST_IRQ */
+       wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP);
+
        wl1251_debug(DEBUG_PSM, "wakeup time: %u ms",
                     jiffies_to_msecs(jiffies - start));
 
        wl->elp = false;
 
        return 0;
        wl1251_debug(DEBUG_PSM, "wakeup time: %u ms",
                     jiffies_to_msecs(jiffies - start));
 
        wl->elp = false;
 
        return 0;
+
+err:
+       wl->elp_compl = NULL;
+       return ret;
 }
 
 int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode)
 }
 
 int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode)
index d16edd9..005f115 100644 (file)
@@ -37,7 +37,7 @@
 
 /* ELP register commands */
 #define ELPCTRL_WAKE_UP             0x1
 
 /* ELP register commands */
 #define ELPCTRL_WAKE_UP             0x1
-#define ELPCTRL_WAKE_UP_WLAN_READY  0x5
+#define ELPCTRL_IRQ_SRC             0x4
 #define ELPCTRL_SLEEP               0x0
 /* ELP WLAN_READY bit */
 #define ELPCTRL_WLAN_READY          0x2
 #define ELPCTRL_SLEEP               0x0
 /* ELP WLAN_READY bit */
 #define ELPCTRL_WLAN_READY          0x2
index 0228e97..f1dc7ad 100644 (file)
@@ -54,10 +54,8 @@ static void wl1251_sdio_interrupt(struct sdio_func *func)
 {
        struct wl1251 *wl = sdio_get_drvdata(func);
 
 {
        struct wl1251 *wl = sdio_get_drvdata(func);
 
-       wl1251_debug(DEBUG_IRQ, "IRQ");
-
        /* FIXME should be synchronous for sdio */
        /* FIXME should be synchronous for sdio */
-       ieee80211_queue_work(wl->hw, &wl->irq_work);
+       wl1251_irq(wl);
 }
 
 static const struct sdio_device_id wl1251_devices[] = {
 }
 
 static const struct sdio_device_id wl1251_devices[] = {
@@ -170,7 +168,7 @@ static irqreturn_t wl1251_line_irq(int irq, void *cookie)
 {
        struct wl1251 *wl = cookie;
 
 {
        struct wl1251 *wl = cookie;
 
-       ieee80211_queue_work(wl->hw, &wl->irq_work);
+       wl1251_irq(wl);
 
        return IRQ_HANDLED;
 }
 
        return IRQ_HANDLED;
 }
index 601bde2..113df61 100644 (file)
 #include "wl1251_reg.h"
 #include "wl1251_spi.h"
 
 #include "wl1251_reg.h"
 #include "wl1251_spi.h"
 
-static irqreturn_t wl1251_irq(int irq, void *cookie)
+static irqreturn_t wl1251_line_irq(int irq, void *cookie)
 {
 {
-       struct wl1251 *wl;
-
-       wl1251_debug(DEBUG_IRQ, "IRQ");
-
-       wl = cookie;
+       struct wl1251 *wl = cookie;
 
 
-       ieee80211_queue_work(wl->hw, &wl->irq_work);
+       wl1251_irq(wl);
 
        return IRQ_HANDLED;
 }
 
        return IRQ_HANDLED;
 }
@@ -275,7 +271,7 @@ static int __devinit wl1251_spi_probe(struct spi_device *spi)
 
        wl->use_eeprom = pdata->use_eeprom;
 
 
        wl->use_eeprom = pdata->use_eeprom;
 
-       ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl);
+       ret = request_irq(wl->irq, wl1251_line_irq, 0, DRIVER_NAME, wl);
        if (ret < 0) {
                wl1251_error("request_irq() failed: %d", ret);
                goto out_free;
        if (ret < 0) {
                wl1251_error("request_irq() failed: %d", ret);
                goto out_free;