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 19 int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime) 20 { 21 int rc = 0; 22 struct wireless_dev *wdev = wil->wdev; 23 24 wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system"); 25 26 if (!netif_running(wil_to_ndev(wil))) { 27 /* can always sleep when down */ 28 wil_dbg_pm(wil, "Interface is down\n"); 29 goto out; 30 } 31 if (test_bit(wil_status_resetting, wil->status)) { 32 wil_dbg_pm(wil, "Delay suspend when resetting\n"); 33 rc = -EBUSY; 34 goto out; 35 } 36 if (wil->recovery_state != fw_recovery_idle) { 37 wil_dbg_pm(wil, "Delay suspend during recovery\n"); 38 rc = -EBUSY; 39 goto out; 40 } 41 42 /* interface is running */ 43 switch (wdev->iftype) { 44 case NL80211_IFTYPE_MONITOR: 45 case NL80211_IFTYPE_STATION: 46 case NL80211_IFTYPE_P2P_CLIENT: 47 if (test_bit(wil_status_fwconnecting, wil->status)) { 48 wil_dbg_pm(wil, "Delay suspend when connecting\n"); 49 rc = -EBUSY; 50 goto out; 51 } 52 break; 53 /* AP-like interface - can't suspend */ 54 default: 55 wil_dbg_pm(wil, "AP-like interface\n"); 56 rc = -EBUSY; 57 break; 58 } 59 60 out: 61 wil_dbg_pm(wil, "can_suspend: %s => %s (%d)\n", 62 is_runtime ? "runtime" : "system", rc ? "No" : "Yes", rc); 63 64 return rc; 65 } 66 67 int wil_suspend(struct wil6210_priv *wil, bool is_runtime) 68 { 69 int rc = 0; 70 struct net_device *ndev = wil_to_ndev(wil); 71 72 wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system"); 73 74 if (test_bit(wil_status_suspended, wil->status)) { 75 wil_dbg_pm(wil, "trying to suspend while suspended\n"); 76 return 0; 77 } 78 79 /* if netif up, hardware is alive, shut it down */ 80 if (ndev->flags & IFF_UP) { 81 rc = wil_down(wil); 82 if (rc) { 83 wil_err(wil, "wil_down : %d\n", rc); 84 goto out; 85 } 86 } 87 88 /* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */ 89 wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n"); 90 wil_disable_irq(wil); 91 92 if (wil->platform_ops.suspend) { 93 rc = wil->platform_ops.suspend(wil->platform_handle); 94 if (rc) { 95 wil_enable_irq(wil); 96 goto out; 97 } 98 } 99 100 set_bit(wil_status_suspended, wil->status); 101 102 out: 103 wil_dbg_pm(wil, "suspend: %s => %d\n", 104 is_runtime ? "runtime" : "system", rc); 105 106 return rc; 107 } 108 109 int wil_resume(struct wil6210_priv *wil, bool is_runtime) 110 { 111 int rc = 0; 112 struct net_device *ndev = wil_to_ndev(wil); 113 114 wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system"); 115 116 if (wil->platform_ops.resume) { 117 rc = wil->platform_ops.resume(wil->platform_handle); 118 if (rc) { 119 wil_err(wil, "platform_ops.resume : %d\n", rc); 120 goto out; 121 } 122 } 123 124 wil_dbg_pm(wil, "Enabling PCIe IRQ\n"); 125 wil_enable_irq(wil); 126 127 /* if netif up, bring hardware up 128 * During open(), IFF_UP set after actual device method 129 * invocation. This prevent recursive call to wil_up(). 130 * wil_status_suspended will be cleared in wil_reset 131 */ 132 if (ndev->flags & IFF_UP) 133 rc = wil_up(wil); 134 else 135 clear_bit(wil_status_suspended, wil->status); 136 137 out: 138 wil_dbg_pm(wil, "resume: %s => %d\n", 139 is_runtime ? "runtime" : "system", rc); 140 return rc; 141 } 142