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 30 void 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 49 out: 50 mutex_unlock(&wl->mutex); 51 } 52 53 #define ELP_ENTRY_DELAY 5 54 55 /* Routines to toggle sleep mode while in ELP */ 56 void 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 66 int 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 106 int 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