xref: /openbmc/linux/drivers/net/wireless/ath/wil6210/pm.c (revision a977d045)
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