ps.c revision c3e334d29484423e78009790a3d3fa78da8b43a1
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->psm) 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->psm) { 61 cancel_delayed_work(&wl->elp_work); 62 delay = msecs_to_jiffies(ELP_ENTRY_DELAY); 63 ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, delay); 64 } 65} 66 67int wl1251_ps_elp_wakeup(struct wl1251 *wl) 68{ 69 unsigned long timeout, start; 70 u32 elp_reg; 71 72 if (!wl->elp) 73 return 0; 74 75 wl1251_debug(DEBUG_PSM, "waking up chip from elp"); 76 77 start = jiffies; 78 timeout = jiffies + msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT); 79 80 wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP); 81 82 elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); 83 84 /* 85 * FIXME: we should wait for irq from chip but, as a temporary 86 * solution to simplify locking, let's poll instead 87 */ 88 while (!(elp_reg & ELPCTRL_WLAN_READY)) { 89 if (time_after(jiffies, timeout)) { 90 wl1251_error("elp wakeup timeout"); 91 return -ETIMEDOUT; 92 } 93 msleep(1); 94 elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); 95 } 96 97 wl1251_debug(DEBUG_PSM, "wakeup time: %u ms", 98 jiffies_to_msecs(jiffies - start)); 99 100 wl->elp = false; 101 102 return 0; 103} 104 105static int wl1251_ps_set_elp(struct wl1251 *wl, bool enable) 106{ 107 int ret; 108 109 if (enable) { 110 wl1251_debug(DEBUG_PSM, "sleep auth psm/elp"); 111 112 ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP); 113 if (ret < 0) 114 return ret; 115 116 wl1251_ps_elp_sleep(wl); 117 } else { 118 wl1251_debug(DEBUG_PSM, "sleep auth cam"); 119 120 /* 121 * When the target is in ELP, we can only 122 * access the ELP control register. Thus, 123 * we have to wake the target up before 124 * changing the power authorization. 125 */ 126 127 wl1251_ps_elp_wakeup(wl); 128 129 ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM); 130 if (ret < 0) 131 return ret; 132 } 133 134 return 0; 135} 136 137int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode) 138{ 139 int ret; 140 141 switch (mode) { 142 case STATION_POWER_SAVE_MODE: 143 wl1251_debug(DEBUG_PSM, "entering psm"); 144 145 /* enable beacon filtering */ 146 ret = wl1251_acx_beacon_filter_opt(wl, true); 147 if (ret < 0) 148 return ret; 149 150 ret = wl1251_acx_wake_up_conditions(wl, 151 WAKE_UP_EVENT_DTIM_BITMAP, 152 wl->listen_int); 153 if (ret < 0) 154 return ret; 155 156 ret = wl1251_acx_bet_enable(wl, WL1251_ACX_BET_ENABLE, 157 WL1251_DEFAULT_BET_CONSECUTIVE); 158 if (ret < 0) 159 return ret; 160 161 ret = wl1251_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE); 162 if (ret < 0) 163 return ret; 164 165 ret = wl1251_ps_set_elp(wl, true); 166 if (ret < 0) 167 return ret; 168 169 wl->psm = 1; 170 break; 171 case STATION_ACTIVE_MODE: 172 default: 173 wl1251_debug(DEBUG_PSM, "leaving psm"); 174 ret = wl1251_ps_set_elp(wl, false); 175 if (ret < 0) 176 return ret; 177 178 /* disable BET */ 179 ret = wl1251_acx_bet_enable(wl, WL1251_ACX_BET_DISABLE, 180 WL1251_DEFAULT_BET_CONSECUTIVE); 181 if (ret < 0) 182 return ret; 183 184 /* disable beacon filtering */ 185 ret = wl1251_acx_beacon_filter_opt(wl, false); 186 if (ret < 0) 187 return ret; 188 189 ret = wl1251_acx_wake_up_conditions(wl, 190 WAKE_UP_EVENT_DTIM_BITMAP, 191 wl->listen_int); 192 if (ret < 0) 193 return ret; 194 195 ret = wl1251_cmd_ps_mode(wl, STATION_ACTIVE_MODE); 196 if (ret < 0) 197 return ret; 198 199 wl->psm = 0; 200 break; 201 } 202 203 return ret; 204} 205 206