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 struct net_device *ndev = wil_to_ndev(wil); 25 26 wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system"); 27 28 if (!(ndev->flags & IFF_UP)) { 29 /* can always sleep when down */ 30 wil_dbg_pm(wil, "Interface is down\n"); 31 goto out; 32 } 33 if (test_bit(wil_status_resetting, wil->status)) { 34 wil_dbg_pm(wil, "Delay suspend when resetting\n"); 35 rc = -EBUSY; 36 goto out; 37 } 38 if (wil->recovery_state != fw_recovery_idle) { 39 wil_dbg_pm(wil, "Delay suspend during recovery\n"); 40 rc = -EBUSY; 41 goto out; 42 } 43 44 /* interface is running */ 45 switch (wdev->iftype) { 46 case NL80211_IFTYPE_MONITOR: 47 case NL80211_IFTYPE_STATION: 48 case NL80211_IFTYPE_P2P_CLIENT: 49 if (test_bit(wil_status_fwconnecting, wil->status)) { 50 wil_dbg_pm(wil, "Delay suspend when connecting\n"); 51 rc = -EBUSY; 52 goto out; 53 } 54 break; 55 /* AP-like interface - can't suspend */ 56 default: 57 wil_dbg_pm(wil, "AP-like interface\n"); 58 rc = -EBUSY; 59 break; 60 } 61 62 out: 63 wil_dbg_pm(wil, "can_suspend: %s => %s (%d)\n", 64 is_runtime ? "runtime" : "system", rc ? "No" : "Yes", rc); 65 66 if (rc) 67 wil->suspend_stats.rejected_by_host++; 68 69 return rc; 70 } 71 72 static int wil_resume_keep_radio_on(struct wil6210_priv *wil) 73 { 74 int rc = 0; 75 76 /* wil_status_resuming will be cleared when getting 77 * WMI_TRAFFIC_RESUME_EVENTID 78 */ 79 set_bit(wil_status_resuming, wil->status); 80 clear_bit(wil_status_suspended, wil->status); 81 wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); 82 wil_unmask_irq(wil); 83 84 wil6210_bus_request(wil, wil->bus_request_kbps_pre_suspend); 85 86 /* Send WMI resume request to the device */ 87 rc = wmi_resume(wil); 88 if (rc) { 89 wil_err(wil, "device failed to resume (%d)\n", rc); 90 if (no_fw_recovery) 91 goto out; 92 rc = wil_down(wil); 93 if (rc) { 94 wil_err(wil, "wil_down failed (%d)\n", rc); 95 goto out; 96 } 97 rc = wil_up(wil); 98 if (rc) { 99 wil_err(wil, "wil_up failed (%d)\n", rc); 100 goto out; 101 } 102 } 103 104 /* Wake all queues */ 105 if (test_bit(wil_status_fwconnected, wil->status)) 106 wil_update_net_queues_bh(wil, NULL, false); 107 108 out: 109 if (rc) 110 set_bit(wil_status_suspended, wil->status); 111 return rc; 112 } 113 114 static int wil_suspend_keep_radio_on(struct wil6210_priv *wil) 115 { 116 int rc = 0; 117 unsigned long start, data_comp_to; 118 119 wil_dbg_pm(wil, "suspend keep radio on\n"); 120 121 /* Prevent handling of new tx and wmi commands */ 122 set_bit(wil_status_suspending, wil->status); 123 wil_update_net_queues_bh(wil, NULL, true); 124 125 if (!wil_is_tx_idle(wil)) { 126 wil_dbg_pm(wil, "Pending TX data, reject suspend\n"); 127 wil->suspend_stats.rejected_by_host++; 128 goto reject_suspend; 129 } 130 131 if (!wil_is_rx_idle(wil)) { 132 wil_dbg_pm(wil, "Pending RX data, reject suspend\n"); 133 wil->suspend_stats.rejected_by_host++; 134 goto reject_suspend; 135 } 136 137 if (!wil_is_wmi_idle(wil)) { 138 wil_dbg_pm(wil, "Pending WMI events, reject suspend\n"); 139 wil->suspend_stats.rejected_by_host++; 140 goto reject_suspend; 141 } 142 143 /* Send WMI suspend request to the device */ 144 rc = wmi_suspend(wil); 145 if (rc) { 146 wil_dbg_pm(wil, "wmi_suspend failed, reject suspend (%d)\n", 147 rc); 148 goto reject_suspend; 149 } 150 151 /* Wait for completion of the pending RX packets */ 152 start = jiffies; 153 data_comp_to = jiffies + msecs_to_jiffies(WIL_DATA_COMPLETION_TO_MS); 154 if (test_bit(wil_status_napi_en, wil->status)) { 155 while (!wil_is_rx_idle(wil)) { 156 if (time_after(jiffies, data_comp_to)) { 157 if (wil_is_rx_idle(wil)) 158 break; 159 wil_err(wil, 160 "TO waiting for idle RX, suspend failed\n"); 161 wil->suspend_stats.failed_suspends++; 162 goto resume_after_fail; 163 } 164 wil_dbg_ratelimited(wil, "rx vring is not empty -> NAPI\n"); 165 napi_synchronize(&wil->napi_rx); 166 msleep(20); 167 } 168 } 169 170 /* In case of pending WMI events, reject the suspend 171 * and resume the device. 172 * This can happen if the device sent the WMI events before 173 * approving the suspend. 174 */ 175 if (!wil_is_wmi_idle(wil)) { 176 wil_err(wil, "suspend failed due to pending WMI events\n"); 177 wil->suspend_stats.failed_suspends++; 178 goto resume_after_fail; 179 } 180 181 wil_mask_irq(wil); 182 183 /* Disable device reset on PERST */ 184 wil_s(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); 185 186 if (wil->platform_ops.suspend) { 187 rc = wil->platform_ops.suspend(wil->platform_handle, true); 188 if (rc) { 189 wil_err(wil, "platform device failed to suspend (%d)\n", 190 rc); 191 wil->suspend_stats.failed_suspends++; 192 wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); 193 wil_unmask_irq(wil); 194 goto resume_after_fail; 195 } 196 } 197 198 /* Save the current bus request to return to the same in resume */ 199 wil->bus_request_kbps_pre_suspend = wil->bus_request_kbps; 200 wil6210_bus_request(wil, 0); 201 202 set_bit(wil_status_suspended, wil->status); 203 clear_bit(wil_status_suspending, wil->status); 204 205 return rc; 206 207 resume_after_fail: 208 set_bit(wil_status_resuming, wil->status); 209 clear_bit(wil_status_suspending, wil->status); 210 rc = wmi_resume(wil); 211 /* if resume succeeded, reject the suspend */ 212 if (!rc) { 213 rc = -EBUSY; 214 if (test_bit(wil_status_fwconnected, wil->status)) 215 wil_update_net_queues_bh(wil, NULL, false); 216 } 217 return rc; 218 219 reject_suspend: 220 clear_bit(wil_status_suspending, wil->status); 221 if (test_bit(wil_status_fwconnected, wil->status)) 222 wil_update_net_queues_bh(wil, NULL, false); 223 return -EBUSY; 224 } 225 226 static int wil_suspend_radio_off(struct wil6210_priv *wil) 227 { 228 int rc = 0; 229 struct net_device *ndev = wil_to_ndev(wil); 230 231 wil_dbg_pm(wil, "suspend radio off\n"); 232 233 /* if netif up, hardware is alive, shut it down */ 234 if (ndev->flags & IFF_UP) { 235 rc = wil_down(wil); 236 if (rc) { 237 wil_err(wil, "wil_down : %d\n", rc); 238 goto out; 239 } 240 } 241 242 /* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */ 243 wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n"); 244 wil_disable_irq(wil); 245 246 if (wil->platform_ops.suspend) { 247 rc = wil->platform_ops.suspend(wil->platform_handle, false); 248 if (rc) { 249 wil_enable_irq(wil); 250 goto out; 251 } 252 } 253 254 set_bit(wil_status_suspended, wil->status); 255 256 out: 257 wil_dbg_pm(wil, "suspend radio off: %d\n", rc); 258 259 return rc; 260 } 261 262 static int wil_resume_radio_off(struct wil6210_priv *wil) 263 { 264 int rc = 0; 265 struct net_device *ndev = wil_to_ndev(wil); 266 267 wil_dbg_pm(wil, "Enabling PCIe IRQ\n"); 268 wil_enable_irq(wil); 269 /* if netif up, bring hardware up 270 * During open(), IFF_UP set after actual device method 271 * invocation. This prevent recursive call to wil_up() 272 * wil_status_suspended will be cleared in wil_reset 273 */ 274 if (ndev->flags & IFF_UP) 275 rc = wil_up(wil); 276 else 277 clear_bit(wil_status_suspended, wil->status); 278 279 return rc; 280 } 281 282 int wil_suspend(struct wil6210_priv *wil, bool is_runtime) 283 { 284 int rc = 0; 285 struct net_device *ndev = wil_to_ndev(wil); 286 bool keep_radio_on = ndev->flags & IFF_UP && 287 wil->keep_radio_on_during_sleep; 288 289 wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system"); 290 291 if (test_bit(wil_status_suspended, wil->status)) { 292 wil_dbg_pm(wil, "trying to suspend while suspended\n"); 293 return 0; 294 } 295 296 if (!keep_radio_on) 297 rc = wil_suspend_radio_off(wil); 298 else 299 rc = wil_suspend_keep_radio_on(wil); 300 301 wil_dbg_pm(wil, "suspend: %s => %d\n", 302 is_runtime ? "runtime" : "system", rc); 303 304 if (!rc) 305 wil->suspend_stats.suspend_start_time = ktime_get(); 306 307 return rc; 308 } 309 310 int wil_resume(struct wil6210_priv *wil, bool is_runtime) 311 { 312 int rc = 0; 313 struct net_device *ndev = wil_to_ndev(wil); 314 bool keep_radio_on = ndev->flags & IFF_UP && 315 wil->keep_radio_on_during_sleep; 316 unsigned long long suspend_time_usec = 0; 317 318 wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system"); 319 320 if (wil->platform_ops.resume) { 321 rc = wil->platform_ops.resume(wil->platform_handle, 322 keep_radio_on); 323 if (rc) { 324 wil_err(wil, "platform_ops.resume : %d\n", rc); 325 goto out; 326 } 327 } 328 329 if (keep_radio_on) 330 rc = wil_resume_keep_radio_on(wil); 331 else 332 rc = wil_resume_radio_off(wil); 333 334 if (rc) 335 goto out; 336 337 suspend_time_usec = 338 ktime_to_us(ktime_sub(ktime_get(), 339 wil->suspend_stats.suspend_start_time)); 340 wil->suspend_stats.total_suspend_time += suspend_time_usec; 341 if (suspend_time_usec < wil->suspend_stats.min_suspend_time) 342 wil->suspend_stats.min_suspend_time = suspend_time_usec; 343 if (suspend_time_usec > wil->suspend_stats.max_suspend_time) 344 wil->suspend_stats.max_suspend_time = suspend_time_usec; 345 346 out: 347 wil_dbg_pm(wil, "resume: %s => %d, suspend time %lld usec\n", 348 is_runtime ? "runtime" : "system", rc, suspend_time_usec); 349 return rc; 350 } 351