linux/drivers/net/wireless/ti/wl1251/ps.c
<<
>>
Prefs
   1/*
   2 * This file is part of wl1251
   3 *
   4 * Copyright (C) 2008 Nokia Corporation
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * version 2 as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but
  11 * WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13 * General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program; if not, write to the Free Software
  17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  18 * 02110-1301 USA
  19 *
  20 */
  21
  22#include "reg.h"
  23#include "ps.h"
  24#include "cmd.h"
  25#include "io.h"
  26
  27/* in ms */
  28#define WL1251_WAKEUP_TIMEOUT 100
  29
  30void wl1251_elp_work(struct work_struct *work)
  31{
  32        struct delayed_work *dwork;
  33        struct wl1251 *wl;
  34
  35        dwork = container_of(work, struct delayed_work, work);
  36        wl = container_of(dwork, struct wl1251, elp_work);
  37
  38        wl1251_debug(DEBUG_PSM, "elp work");
  39
  40        mutex_lock(&wl->mutex);
  41
  42        if (wl->elp || wl->station_mode == STATION_ACTIVE_MODE)
  43                goto out;
  44
  45        wl1251_debug(DEBUG_PSM, "chip to elp");
  46        wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
  47        wl->elp = true;
  48
  49out:
  50        mutex_unlock(&wl->mutex);
  51}
  52
  53#define ELP_ENTRY_DELAY  5
  54
  55/* Routines to toggle sleep mode while in ELP */
  56void wl1251_ps_elp_sleep(struct wl1251 *wl)
  57{
  58        unsigned long delay;
  59
  60        if (wl->station_mode != STATION_ACTIVE_MODE) {
  61                delay = msecs_to_jiffies(ELP_ENTRY_DELAY);
  62                ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, delay);
  63        }
  64}
  65
  66int wl1251_ps_elp_wakeup(struct wl1251 *wl)
  67{
  68        unsigned long timeout, start;
  69        u32 elp_reg;
  70
  71        cancel_delayed_work(&wl->elp_work);
  72
  73        if (!wl->elp)
  74                return 0;
  75
  76        wl1251_debug(DEBUG_PSM, "waking up chip from elp");
  77
  78        start = jiffies;
  79        timeout = jiffies + msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT);
  80
  81        wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP);
  82
  83        elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
  84
  85        /*
  86         * FIXME: we should wait for irq from chip but, as a temporary
  87         * solution to simplify locking, let's poll instead
  88         */
  89        while (!(elp_reg & ELPCTRL_WLAN_READY)) {
  90                if (time_after(jiffies, timeout)) {
  91                        wl1251_error("elp wakeup timeout");
  92                        return -ETIMEDOUT;
  93                }
  94                msleep(1);
  95                elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
  96        }
  97
  98        wl1251_debug(DEBUG_PSM, "wakeup time: %u ms",
  99                     jiffies_to_msecs(jiffies - start));
 100
 101        wl->elp = false;
 102
 103        return 0;
 104}
 105
 106int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_station_mode mode)
 107{
 108        int ret;
 109
 110        switch (mode) {
 111        case STATION_POWER_SAVE_MODE:
 112                wl1251_debug(DEBUG_PSM, "entering psm");
 113
 114                /* enable beacon filtering */
 115                ret = wl1251_acx_beacon_filter_opt(wl, true);
 116                if (ret < 0)
 117                        return ret;
 118
 119                ret = wl1251_acx_wake_up_conditions(wl,
 120                                                    WAKE_UP_EVENT_DTIM_BITMAP,
 121                                                    wl->listen_int);
 122                if (ret < 0)
 123                        return ret;
 124
 125                ret = wl1251_acx_bet_enable(wl, WL1251_ACX_BET_ENABLE,
 126                                            WL1251_DEFAULT_BET_CONSECUTIVE);
 127                if (ret < 0)
 128                        return ret;
 129
 130                ret = wl1251_cmd_ps_mode(wl, CHIP_POWER_SAVE_MODE);
 131                if (ret < 0)
 132                        return ret;
 133
 134                ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP);
 135                if (ret < 0)
 136                        return ret;
 137                break;
 138        case STATION_IDLE:
 139                wl1251_debug(DEBUG_PSM, "entering idle");
 140
 141                ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP);
 142                if (ret < 0)
 143                        return ret;
 144
 145                ret = wl1251_cmd_template_set(wl, CMD_DISCONNECT, NULL, 0);
 146                if (ret < 0)
 147                        return ret;
 148                break;
 149        case STATION_ACTIVE_MODE:
 150        default:
 151                wl1251_debug(DEBUG_PSM, "leaving psm");
 152
 153                ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM);
 154                if (ret < 0)
 155                        return ret;
 156
 157                /* disable BET */
 158                ret = wl1251_acx_bet_enable(wl, WL1251_ACX_BET_DISABLE,
 159                                            WL1251_DEFAULT_BET_CONSECUTIVE);
 160                if (ret < 0)
 161                        return ret;
 162
 163                /* disable beacon filtering */
 164                ret = wl1251_acx_beacon_filter_opt(wl, false);
 165                if (ret < 0)
 166                        return ret;
 167
 168                ret = wl1251_acx_wake_up_conditions(wl,
 169                                                    WAKE_UP_EVENT_DTIM_BITMAP,
 170                                                    wl->listen_int);
 171                if (ret < 0)
 172                        return ret;
 173
 174                ret = wl1251_cmd_ps_mode(wl, CHIP_ACTIVE_MODE);
 175                if (ret < 0)
 176                        return ret;
 177
 178                break;
 179        }
 180        wl->station_mode = mode;
 181
 182        return ret;
 183}
 184
 185