wl1251: Prepare for idle mode support
[pandora-wifi.git] / drivers / net / wireless / wl12xx / wl1251_ps.c
index fb33ee1..7f14467 100644 (file)
@@ -41,7 +41,7 @@ void wl1251_elp_work(struct work_struct *work)
 
        mutex_lock(&wl->mutex);
 
-       if (wl->elp || !wl->psm)
+       if (wl->elp || wl->station_mode == STATION_ACTIVE_MODE)
                goto out;
 
        wl1251_debug(DEBUG_PSM, "chip to elp");
@@ -59,8 +59,7 @@ void wl1251_ps_elp_sleep(struct wl1251 *wl)
 {
        unsigned long delay;
 
-       if (wl->psm) {
-               cancel_delayed_work(&wl->elp_work);
+       if (wl->station_mode != STATION_ACTIVE_MODE) {
                delay = msecs_to_jiffies(ELP_ENTRY_DELAY);
                ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, delay);
        }
@@ -68,8 +67,13 @@ void wl1251_ps_elp_sleep(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;
+       int ret;
+
+       if (delayed_work_pending(&wl->elp_work))
+               cancel_delayed_work(&wl->elp_work);
 
        if (!wl->elp)
                return 0;
@@ -77,66 +81,45 @@ int wl1251_ps_elp_wakeup(struct wl1251 *wl)
        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);
 
-       /*
-        * 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;
-}
-
-static int wl1251_ps_set_elp(struct wl1251 *wl, bool enable)
-{
-       int ret;
-
-       if (enable) {
-               wl1251_debug(DEBUG_PSM, "sleep auth psm/elp");
-
-               ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP);
-               if (ret < 0)
-                       return ret;
-
-               wl1251_ps_elp_sleep(wl);
-       } else {
-               wl1251_debug(DEBUG_PSM, "sleep auth cam");
-
-               /*
-                * When the target is in ELP, we can only
-                * access the ELP control register. Thus,
-                * we have to wake the target up before
-                * changing the power authorization.
-                */
 
-               wl1251_ps_elp_wakeup(wl);
-
-               ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM);
-               if (ret < 0)
-                       return ret;
-       }
-
-       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_station_mode mode)
 {
        int ret;
 
@@ -160,20 +143,19 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode)
                if (ret < 0)
                        return ret;
 
-               ret = wl1251_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE);
+               ret = wl1251_cmd_ps_mode(wl, CHIP_POWER_SAVE_MODE);
                if (ret < 0)
                        return ret;
 
-               ret = wl1251_ps_set_elp(wl, true);
+               ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP);
                if (ret < 0)
                        return ret;
-
-               wl->psm = 1;
                break;
        case STATION_ACTIVE_MODE:
        default:
                wl1251_debug(DEBUG_PSM, "leaving psm");
-               ret = wl1251_ps_set_elp(wl, false);
+
+               ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM);
                if (ret < 0)
                        return ret;
 
@@ -194,13 +176,13 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode)
                if (ret < 0)
                        return ret;
 
-               ret = wl1251_cmd_ps_mode(wl, STATION_ACTIVE_MODE);
+               ret = wl1251_cmd_ps_mode(wl, CHIP_ACTIVE_MODE);
                if (ret < 0)
                        return ret;
 
-               wl->psm = 0;
                break;
        }
+       wl->station_mode = mode;
 
        return ret;
 }