pm.c (9047B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Mac80211 power management API for ST-Ericsson CW1200 drivers 4 * 5 * Copyright (c) 2011, ST-Ericsson 6 * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> 7 */ 8 9#include <linux/module.h> 10#include <linux/if_ether.h> 11#include "cw1200.h" 12#include "pm.h" 13#include "sta.h" 14#include "bh.h" 15#include "hwbus.h" 16 17#define CW1200_BEACON_SKIPPING_MULTIPLIER 3 18 19struct cw1200_udp_port_filter { 20 struct wsm_udp_port_filter_hdr hdr; 21 /* Up to 4 filters are allowed. */ 22 struct wsm_udp_port_filter filters[WSM_MAX_FILTER_ELEMENTS]; 23} __packed; 24 25struct cw1200_ether_type_filter { 26 struct wsm_ether_type_filter_hdr hdr; 27 /* Up to 4 filters are allowed. */ 28 struct wsm_ether_type_filter filters[WSM_MAX_FILTER_ELEMENTS]; 29} __packed; 30 31static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = { 32 .hdr.num = 2, 33 .filters = { 34 [0] = { 35 .action = WSM_FILTER_ACTION_FILTER_OUT, 36 .type = WSM_FILTER_PORT_TYPE_DST, 37 .port = __cpu_to_le16(67), /* DHCP Bootps */ 38 }, 39 [1] = { 40 .action = WSM_FILTER_ACTION_FILTER_OUT, 41 .type = WSM_FILTER_PORT_TYPE_DST, 42 .port = __cpu_to_le16(68), /* DHCP Bootpc */ 43 }, 44 } 45}; 46 47static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = { 48 .num = 0, 49}; 50 51#ifndef ETH_P_WAPI 52#define ETH_P_WAPI 0x88B4 53#endif 54 55static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = { 56 .hdr.num = 4, 57 .filters = { 58 [0] = { 59 .action = WSM_FILTER_ACTION_FILTER_IN, 60 .type = __cpu_to_le16(ETH_P_IP), 61 }, 62 [1] = { 63 .action = WSM_FILTER_ACTION_FILTER_IN, 64 .type = __cpu_to_le16(ETH_P_PAE), 65 }, 66 [2] = { 67 .action = WSM_FILTER_ACTION_FILTER_IN, 68 .type = __cpu_to_le16(ETH_P_WAPI), 69 }, 70 [3] = { 71 .action = WSM_FILTER_ACTION_FILTER_IN, 72 .type = __cpu_to_le16(ETH_P_ARP), 73 }, 74 }, 75}; 76 77static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = { 78 .num = 0, 79}; 80 81/* private */ 82struct cw1200_suspend_state { 83 unsigned long bss_loss_tmo; 84 unsigned long join_tmo; 85 unsigned long direct_probe; 86 unsigned long link_id_gc; 87 bool beacon_skipping; 88 u8 prev_ps_mode; 89}; 90 91static void cw1200_pm_stay_awake_tmo(struct timer_list *unused) 92{ 93 /* XXX what's the point of this ? */ 94} 95 96int cw1200_pm_init(struct cw1200_pm_state *pm, 97 struct cw1200_common *priv) 98{ 99 spin_lock_init(&pm->lock); 100 101 timer_setup(&pm->stay_awake, cw1200_pm_stay_awake_tmo, 0); 102 103 return 0; 104} 105 106void cw1200_pm_deinit(struct cw1200_pm_state *pm) 107{ 108 del_timer_sync(&pm->stay_awake); 109} 110 111void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, 112 unsigned long tmo) 113{ 114 long cur_tmo; 115 spin_lock_bh(&pm->lock); 116 cur_tmo = pm->stay_awake.expires - jiffies; 117 if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo) 118 mod_timer(&pm->stay_awake, jiffies + tmo); 119 spin_unlock_bh(&pm->lock); 120} 121 122static long cw1200_suspend_work(struct delayed_work *work) 123{ 124 int ret = cancel_delayed_work(work); 125 long tmo; 126 if (ret > 0) { 127 /* Timer is pending */ 128 tmo = work->timer.expires - jiffies; 129 if (tmo < 0) 130 tmo = 0; 131 } else { 132 tmo = -1; 133 } 134 return tmo; 135} 136 137static int cw1200_resume_work(struct cw1200_common *priv, 138 struct delayed_work *work, 139 unsigned long tmo) 140{ 141 if ((long)tmo < 0) 142 return 1; 143 144 return queue_delayed_work(priv->workqueue, work, tmo); 145} 146 147int cw1200_can_suspend(struct cw1200_common *priv) 148{ 149 if (atomic_read(&priv->bh_rx)) { 150 wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n"); 151 return 0; 152 } 153 return 1; 154} 155EXPORT_SYMBOL_GPL(cw1200_can_suspend); 156 157int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) 158{ 159 struct cw1200_common *priv = hw->priv; 160 struct cw1200_pm_state *pm_state = &priv->pm_state; 161 struct cw1200_suspend_state *state; 162 int ret; 163 164 spin_lock_bh(&pm_state->lock); 165 ret = timer_pending(&pm_state->stay_awake); 166 spin_unlock_bh(&pm_state->lock); 167 if (ret) 168 return -EAGAIN; 169 170 /* Do not suspend when datapath is not idle */ 171 if (priv->tx_queue_stats.num_queued) 172 return -EBUSY; 173 174 /* Make sure there is no configuration requests in progress. */ 175 if (!mutex_trylock(&priv->conf_mutex)) 176 return -EBUSY; 177 178 /* Ensure pending operations are done. 179 * Note also that wow_suspend must return in ~2.5sec, before 180 * watchdog is triggered. 181 */ 182 if (priv->channel_switch_in_progress) 183 goto revert1; 184 185 /* Do not suspend when join is pending */ 186 if (priv->join_pending) 187 goto revert1; 188 189 /* Do not suspend when scanning */ 190 if (down_trylock(&priv->scan.lock)) 191 goto revert1; 192 193 /* Lock TX. */ 194 wsm_lock_tx_async(priv); 195 196 /* Wait to avoid possible race with bh code. 197 * But do not wait too long... 198 */ 199 if (wait_event_timeout(priv->bh_evt_wq, 200 !priv->hw_bufs_used, HZ / 10) <= 0) 201 goto revert2; 202 203 /* Set UDP filter */ 204 wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr); 205 206 /* Set ethernet frame type filter */ 207 wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr); 208 209 /* Allocate state */ 210 state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL); 211 if (!state) 212 goto revert3; 213 214 /* Change to legacy PS while going to suspend */ 215 if (!priv->vif->p2p && 216 priv->join_status == CW1200_JOIN_STATUS_STA && 217 priv->powersave_mode.mode != WSM_PSM_PS) { 218 state->prev_ps_mode = priv->powersave_mode.mode; 219 priv->powersave_mode.mode = WSM_PSM_PS; 220 cw1200_set_pm(priv, &priv->powersave_mode); 221 if (wait_event_interruptible_timeout(priv->ps_mode_switch_done, 222 !priv->ps_mode_switch_in_progress, 1*HZ) <= 0) { 223 goto revert4; 224 } 225 } 226 227 /* Store delayed work states. */ 228 state->bss_loss_tmo = 229 cw1200_suspend_work(&priv->bss_loss_work); 230 state->join_tmo = 231 cw1200_suspend_work(&priv->join_timeout); 232 state->direct_probe = 233 cw1200_suspend_work(&priv->scan.probe_work); 234 state->link_id_gc = 235 cw1200_suspend_work(&priv->link_id_gc_work); 236 237 cancel_delayed_work_sync(&priv->clear_recent_scan_work); 238 atomic_set(&priv->recent_scan, 0); 239 240 /* Enable beacon skipping */ 241 if (priv->join_status == CW1200_JOIN_STATUS_STA && 242 priv->join_dtim_period && 243 !priv->has_multicast_subscription) { 244 state->beacon_skipping = true; 245 wsm_set_beacon_wakeup_period(priv, 246 priv->join_dtim_period, 247 CW1200_BEACON_SKIPPING_MULTIPLIER * priv->join_dtim_period); 248 } 249 250 /* Stop serving thread */ 251 if (cw1200_bh_suspend(priv)) 252 goto revert5; 253 254 ret = timer_pending(&priv->mcast_timeout); 255 if (ret) 256 goto revert6; 257 258 /* Store suspend state */ 259 pm_state->suspend_state = state; 260 261 /* Enable IRQ wake */ 262 ret = priv->hwbus_ops->power_mgmt(priv->hwbus_priv, true); 263 if (ret) { 264 wiphy_err(priv->hw->wiphy, 265 "PM request failed: %d. WoW is disabled.\n", ret); 266 cw1200_wow_resume(hw); 267 return -EBUSY; 268 } 269 270 /* Force resume if event is coming from the device. */ 271 if (atomic_read(&priv->bh_rx)) { 272 cw1200_wow_resume(hw); 273 return -EAGAIN; 274 } 275 276 return 0; 277 278revert6: 279 WARN_ON(cw1200_bh_resume(priv)); 280revert5: 281 cw1200_resume_work(priv, &priv->bss_loss_work, 282 state->bss_loss_tmo); 283 cw1200_resume_work(priv, &priv->join_timeout, 284 state->join_tmo); 285 cw1200_resume_work(priv, &priv->scan.probe_work, 286 state->direct_probe); 287 cw1200_resume_work(priv, &priv->link_id_gc_work, 288 state->link_id_gc); 289revert4: 290 kfree(state); 291revert3: 292 wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); 293 wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off); 294revert2: 295 wsm_unlock_tx(priv); 296 up(&priv->scan.lock); 297revert1: 298 mutex_unlock(&priv->conf_mutex); 299 return -EBUSY; 300} 301 302int cw1200_wow_resume(struct ieee80211_hw *hw) 303{ 304 struct cw1200_common *priv = hw->priv; 305 struct cw1200_pm_state *pm_state = &priv->pm_state; 306 struct cw1200_suspend_state *state; 307 308 state = pm_state->suspend_state; 309 pm_state->suspend_state = NULL; 310 311 /* Disable IRQ wake */ 312 priv->hwbus_ops->power_mgmt(priv->hwbus_priv, false); 313 314 /* Scan.lock must be released before BH is resumed other way 315 * in case when BSS_LOST command arrived the processing of the 316 * command will be delayed. 317 */ 318 up(&priv->scan.lock); 319 320 /* Resume BH thread */ 321 WARN_ON(cw1200_bh_resume(priv)); 322 323 /* Restores previous PS mode */ 324 if (!priv->vif->p2p && priv->join_status == CW1200_JOIN_STATUS_STA) { 325 priv->powersave_mode.mode = state->prev_ps_mode; 326 cw1200_set_pm(priv, &priv->powersave_mode); 327 } 328 329 if (state->beacon_skipping) { 330 wsm_set_beacon_wakeup_period(priv, priv->beacon_int * 331 priv->join_dtim_period > 332 MAX_BEACON_SKIP_TIME_MS ? 1 : 333 priv->join_dtim_period, 0); 334 state->beacon_skipping = false; 335 } 336 337 /* Resume delayed work */ 338 cw1200_resume_work(priv, &priv->bss_loss_work, 339 state->bss_loss_tmo); 340 cw1200_resume_work(priv, &priv->join_timeout, 341 state->join_tmo); 342 cw1200_resume_work(priv, &priv->scan.probe_work, 343 state->direct_probe); 344 cw1200_resume_work(priv, &priv->link_id_gc_work, 345 state->link_id_gc); 346 347 /* Remove UDP port filter */ 348 wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); 349 350 /* Remove ethernet frame type filter */ 351 wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off); 352 353 /* Unlock datapath */ 354 wsm_unlock_tx(priv); 355 356 /* Unlock configuration mutex */ 357 mutex_unlock(&priv->conf_mutex); 358 359 /* Free memory */ 360 kfree(state); 361 362 return 0; 363}