1 /* 2 * Copyright (c) 2014,2017 Qualcomm Atheros, Inc. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include "wil6210.h" 18 #include <linux/jiffies.h> 19 20 int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime) 21 { 22 int rc = 0; 23 struct wireless_dev *wdev = wil->wdev; 24 25 wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system"); 26 27 if (!netif_running(wil_to_ndev(wil))) { 28 /* can always sleep when down */ 29 wil_dbg_pm(wil, "Interface is down\n"); 30 goto out; 31 } 32 if (test_bit(wil_status_resetting, wil->status)) { 33 wil_dbg_pm(wil, "Delay suspend when resetting\n"); 34 rc = -EBUSY; 35 goto out; 36 } 37 if (wil->recovery_state != fw_recovery_idle) { 38 wil_dbg_pm(wil, "Delay suspend during recovery\n"); 39 rc = -EBUSY; 40 goto out; 41 } 42 43 /* interface is running */ 44 switch (wdev->iftype) { 45 case NL80211_IFTYPE_MONITOR: 46 case NL80211_IFTYPE_STATION: 47 case NL80211_IFTYPE_P2P_CLIENT: 48 if (test_bit(wil_status_fwconnecting, wil->status)) { 49 wil_dbg_pm(wil, "Delay suspend when connecting\n"); 50 rc = -EBUSY; 51 goto out; 52 } 53 break; 54 /* AP-like interface - can't suspend */ 55 default: 56 wil_dbg_pm(wil, "AP-like interface\n"); 57 rc = -EBUSY; 58 break; 59 } 60 61 out: 62 wil_dbg_pm(wil, "can_suspend: %s => %s (%d)\n", 63 is_runtime ? "runtime" : "system", rc ? "No" : "Yes", rc); 64 65 if (rc) 66 wil->suspend_stats.rejected_by_host++; 67 68 return rc; 69 } 70 71 static int wil_resume_keep_radio_on(struct wil6210_priv *wil) 72 { 73 int rc = 0; 74 75 /* wil_status_resuming will be cleared when getting 76 * WMI_TRAFFIC_RESUME_EVENTID 77 */ 78 set_bit(wil_status_resuming, wil->status); 79 clear_bit(wil_status_suspended, wil->status); 80 wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); 81 wil_unmask_irq(wil); 82 83 wil6210_bus_request(wil, wil->bus_request_kbps_pre_suspend); 84 85 /* Send WMI resume request to the device */ 86 rc = wmi_resume(wil); 87 if (rc) { 88 wil_err(wil, "device failed to resume (%d), resetting\n", rc); 89 rc = wil_down(wil); 90 if (rc) { 91 wil_err(wil, "wil_down failed (%d)\n", rc); 92 goto out; 93 } 94 rc = wil_up(wil); 95 if (rc) { 96 wil_err(wil, "wil_up failed (%d)\n", rc); 97 goto out; 98 } 99 } 100 101 /* Wake all queues */ 102 if (test_bit(wil_status_fwconnected, wil->status)) 103 wil_update_net_queues_bh(wil, NULL, false); 104 105 out: 106 if (rc) 107 set_bit(wil_status_suspended, wil->status); 108 return rc; 109 } 110 111 static int wil_suspend_keep_radio_on(struct wil6210_priv *wil) 112 { 113 int rc = 0; 114 unsigned long start, data_comp_to; 115 116 wil_dbg_pm(wil, "suspend keep radio on\n"); 117 118 /* Prevent handling of new tx and wmi commands */ 119 set_bit(wil_status_suspending, wil->status); 120 wil_update_net_queues_bh(wil, NULL, true); 121 122 if (!wil_is_tx_idle(wil)) { 123 wil_dbg_pm(wil, "Pending TX data, reject suspend\n"); 124 wil->suspend_stats.rejected_by_host++; 125 goto reject_suspend; 126 } 127 128 if (!wil_is_rx_idle(wil)) { 129 wil_dbg_pm(wil, "Pending RX data, reject suspend\n"); 130 wil->suspend_stats.rejected_by_host++; 131 goto reject_suspend; 132 } 133 134 if (!wil_is_wmi_idle(wil)) { 135 wil_dbg_pm(wil, "Pending WMI events, reject suspend\n"); 136 wil->suspend_stats.rejected_by_host++; 137 goto reject_suspend; 138 } 139 140 /* Send WMI suspend request to the device */ 141 rc = wmi_suspend(wil); 142 if (rc) { 143 wil_dbg_pm(wil, "wmi_suspend failed, reject suspend (%d)\n", 144 rc); 145 goto reject_suspend; 146 } 147 148 /* Wait for completion of the pending RX packets */ 149 start = jiffies; 150 data_comp_to = jiffies + msecs_to_jiffies(WIL_DATA_COMPLETION_TO_MS); 151 if (test_bit(wil_status_napi_en, wil->status)) { 152 while (!wil_is_rx_idle(wil)) { 153 if (time_after(jiffies, data_comp_to)) { 154 if (wil_is_rx_idle(wil)) 155 break; 156 wil_err(wil, 157 "TO waiting for idle RX, suspend failed\n"); 158 wil->suspend_stats.failed_suspends++; 159 goto resume_after_fail; 160 } 161 wil_dbg_ratelimited(wil, "rx vring is not empty -> NAPI\n"); 162 napi_synchronize(&wil->napi_rx); 163 msleep(20); 164 } 165 } 166 167 /* In case of pending WMI events, reject the suspend 168 * and resume the device. 169 * This can happen if the device sent the WMI events before 170 * approving the suspend. 171 */ 172 if (!wil_is_wmi_idle(wil)) { 173 wil_err(wil, "suspend failed due to pending WMI events\n"); 174 wil->suspend_stats.failed_suspends++; 175 goto resume_after_fail; 176 } 177 178 wil_mask_irq(wil); 179 180 /* Disable device reset on PERST */ 181 wil_s(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); 182 183 if (wil->platform_ops.suspend) { 184 rc = wil->platform_ops.suspend(wil->platform_handle, true); 185 if (rc) { 186 wil_err(wil, "platform device failed to suspend (%d)\n", 187 rc); 188 wil->suspend_stats.failed_suspends++; 189 wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); 190 wil_unmask_irq(wil); 191 goto resume_after_fail; 192 } 193 } 194 195 /* Save the current bus request to return to the same in resume */ 196 wil->bus_request_kbps_pre_suspend = wil->bus_request_kbps; 197 wil6210_bus_request(wil, 0); 198 199 set_bit(wil_status_suspended, wil->status); 200 clear_bit(wil_status_suspending, wil->status); 201 202 return rc; 203 204 resume_after_fail: 205 set_bit(wil_status_resuming, wil->status); 206 clear_bit(wil_status_suspending, wil->status); 207 rc = wmi_resume(wil); 208 /* if resume succeeded, reject the suspend */ 209 if (!rc) { 210 rc = -EBUSY; 211 if (test_bit(wil_status_fwconnected, wil->status)) 212 wil_update_net_queues_bh(wil, NULL, false); 213 } 214 return rc; 215 216 reject_suspend: 217 clear_bit(wil_status_suspending, wil->status); 218 if (test_bit(wil_status_fwconnected, wil->status)) 219 wil_update_net_queues_bh(wil, NULL, false); 220 return -EBUSY; 221 } 222 223 static int wil_suspend_radio_off(struct wil6210_priv *wil) 224 { 225 int rc = 0; 226 struct net_device *ndev = wil_to_ndev(wil); 227 228 wil_dbg_pm(wil, "suspend radio off\n"); 229 230 /* if netif up, hardware is alive, shut it down */ 231 if (ndev->flags & IFF_UP) { 232 rc = wil_down(wil); 233 if (rc) { 234 wil_err(wil, "wil_down : %d\n", rc); 235 goto out; 236 } 237 } 238 239 /* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */ 240 wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n"); 241 wil_disable_irq(wil); 242 243 if (wil->platform_ops.suspend) { 244 rc = wil->platform_ops.suspend(wil->platform_handle, false); 245 if (rc) { 246 wil_enable_irq(wil); 247 goto out; 248 } 249 } 250 251 set_bit(wil_status_suspended, wil->status); 252 253 out: 254 wil_dbg_pm(wil, "suspend radio off: %d\n", rc); 255 256 return rc; 257 } 258 259 static int wil_resume_radio_off(struct wil6210_priv *wil) 260 { 261 int rc = 0; 262 struct net_device *ndev = wil_to_ndev(wil); 263 264 wil_dbg_pm(wil, "Enabling PCIe IRQ\n"); 265 wil_enable_irq(wil); 266 /* if netif up, bring hardware up 267 * During open(), IFF_UP set after actual device method 268 * invocation. This prevent recursive call to wil_up() 269 * wil_status_suspended will be cleared in wil_reset 270 */ 271 if (ndev->flags & IFF_UP) 272 rc = wil_up(wil); 273 else 274 clear_bit(wil_status_suspended, wil->status); 275 276 return rc; 277 } 278 279 int wil_suspend(struct wil6210_priv *wil, bool is_runtime) 280 { 281 int rc = 0; 282 struct net_device *ndev = wil_to_ndev(wil); 283 bool keep_radio_on = ndev->flags & IFF_UP && 284 wil->keep_radio_on_during_sleep; 285 286 wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system"); 287 288 if (test_bit(wil_status_suspended, wil->status)) { 289 wil_dbg_pm(wil, "trying to suspend while suspended\n"); 290 return 0; 291 } 292 293 if (!keep_radio_on) 294 rc = wil_suspend_radio_off(wil); 295 else 296 rc = wil_suspend_keep_radio_on(wil); 297 298 wil_dbg_pm(wil, "suspend: %s => %d\n", 299 is_runtime ? "runtime" : "system", rc); 300 301 return rc; 302 } 303 304 int wil_resume(struct wil6210_priv *wil, bool is_runtime) 305 { 306 int rc = 0; 307 struct net_device *ndev = wil_to_ndev(wil); 308 bool keep_radio_on = ndev->flags & IFF_UP && 309 wil->keep_radio_on_during_sleep; 310 311 wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system"); 312 313 if (wil->platform_ops.resume) { 314 rc = wil->platform_ops.resume(wil->platform_handle, 315 keep_radio_on); 316 if (rc) { 317 wil_err(wil, "platform_ops.resume : %d\n", rc); 318 goto out; 319 } 320 } 321 322 if (keep_radio_on) 323 rc = wil_resume_keep_radio_on(wil); 324 else 325 rc = wil_resume_radio_off(wil); 326 327 out: 328 wil_dbg_pm(wil, "resume: %s => %d\n", 329 is_runtime ? "runtime" : "system", rc); 330 return rc; 331 } 332