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