xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/ath/wil6210/pm.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: ISC
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
4*4882a593Smuzhiyun  * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include "wil6210.h"
8*4882a593Smuzhiyun #include <linux/jiffies.h>
9*4882a593Smuzhiyun #include <linux/pm_runtime.h>
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #define WIL6210_AUTOSUSPEND_DELAY_MS (1000)
12*4882a593Smuzhiyun 
wil_pm_wake_connected_net_queues(struct wil6210_priv * wil)13*4882a593Smuzhiyun static void wil_pm_wake_connected_net_queues(struct wil6210_priv *wil)
14*4882a593Smuzhiyun {
15*4882a593Smuzhiyun 	int i;
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun 	mutex_lock(&wil->vif_mutex);
18*4882a593Smuzhiyun 	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
19*4882a593Smuzhiyun 		struct wil6210_vif *vif = wil->vifs[i];
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun 		if (vif && test_bit(wil_vif_fwconnected, vif->status))
22*4882a593Smuzhiyun 			wil_update_net_queues_bh(wil, vif, NULL, false);
23*4882a593Smuzhiyun 	}
24*4882a593Smuzhiyun 	mutex_unlock(&wil->vif_mutex);
25*4882a593Smuzhiyun }
26*4882a593Smuzhiyun 
wil_pm_stop_all_net_queues(struct wil6210_priv * wil)27*4882a593Smuzhiyun static void wil_pm_stop_all_net_queues(struct wil6210_priv *wil)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun 	int i;
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun 	mutex_lock(&wil->vif_mutex);
32*4882a593Smuzhiyun 	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
33*4882a593Smuzhiyun 		struct wil6210_vif *vif = wil->vifs[i];
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun 		if (vif)
36*4882a593Smuzhiyun 			wil_update_net_queues_bh(wil, vif, NULL, true);
37*4882a593Smuzhiyun 	}
38*4882a593Smuzhiyun 	mutex_unlock(&wil->vif_mutex);
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun static bool
wil_can_suspend_vif(struct wil6210_priv * wil,struct wil6210_vif * vif,bool is_runtime)42*4882a593Smuzhiyun wil_can_suspend_vif(struct wil6210_priv *wil, struct wil6210_vif *vif,
43*4882a593Smuzhiyun 		    bool is_runtime)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun 	struct wireless_dev *wdev = vif_to_wdev(vif);
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 	switch (wdev->iftype) {
48*4882a593Smuzhiyun 	case NL80211_IFTYPE_MONITOR:
49*4882a593Smuzhiyun 		wil_dbg_pm(wil, "Sniffer\n");
50*4882a593Smuzhiyun 		return false;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	/* for STA-like interface, don't runtime suspend */
53*4882a593Smuzhiyun 	case NL80211_IFTYPE_STATION:
54*4882a593Smuzhiyun 	case NL80211_IFTYPE_P2P_CLIENT:
55*4882a593Smuzhiyun 		if (test_bit(wil_vif_fwconnecting, vif->status)) {
56*4882a593Smuzhiyun 			wil_dbg_pm(wil, "Delay suspend when connecting\n");
57*4882a593Smuzhiyun 			return false;
58*4882a593Smuzhiyun 		}
59*4882a593Smuzhiyun 		if (is_runtime) {
60*4882a593Smuzhiyun 			wil_dbg_pm(wil, "STA-like interface\n");
61*4882a593Smuzhiyun 			return false;
62*4882a593Smuzhiyun 		}
63*4882a593Smuzhiyun 		break;
64*4882a593Smuzhiyun 	/* AP-like interface - can't suspend */
65*4882a593Smuzhiyun 	default:
66*4882a593Smuzhiyun 		wil_dbg_pm(wil, "AP-like interface\n");
67*4882a593Smuzhiyun 		return false;
68*4882a593Smuzhiyun 	}
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	return true;
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun 
wil_can_suspend(struct wil6210_priv * wil,bool is_runtime)73*4882a593Smuzhiyun int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun 	int rc = 0, i;
76*4882a593Smuzhiyun 	bool wmi_only = test_bit(WMI_FW_CAPABILITY_WMI_ONLY,
77*4882a593Smuzhiyun 				 wil->fw_capabilities);
78*4882a593Smuzhiyun 	bool active_ifaces;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system");
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	if (wmi_only || debug_fw) {
83*4882a593Smuzhiyun 		wil_dbg_pm(wil, "Deny any suspend - %s mode\n",
84*4882a593Smuzhiyun 			   wmi_only ? "wmi_only" : "debug_fw");
85*4882a593Smuzhiyun 		rc = -EBUSY;
86*4882a593Smuzhiyun 		goto out;
87*4882a593Smuzhiyun 	}
88*4882a593Smuzhiyun 	if (is_runtime && !wil->platform_ops.suspend) {
89*4882a593Smuzhiyun 		rc = -EBUSY;
90*4882a593Smuzhiyun 		goto out;
91*4882a593Smuzhiyun 	}
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun 	mutex_lock(&wil->vif_mutex);
94*4882a593Smuzhiyun 	active_ifaces = wil_has_active_ifaces(wil, true, false);
95*4882a593Smuzhiyun 	mutex_unlock(&wil->vif_mutex);
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	if (!active_ifaces) {
98*4882a593Smuzhiyun 		/* can always sleep when down */
99*4882a593Smuzhiyun 		wil_dbg_pm(wil, "Interface is down\n");
100*4882a593Smuzhiyun 		goto out;
101*4882a593Smuzhiyun 	}
102*4882a593Smuzhiyun 	if (test_bit(wil_status_resetting, wil->status)) {
103*4882a593Smuzhiyun 		wil_dbg_pm(wil, "Delay suspend when resetting\n");
104*4882a593Smuzhiyun 		rc = -EBUSY;
105*4882a593Smuzhiyun 		goto out;
106*4882a593Smuzhiyun 	}
107*4882a593Smuzhiyun 	if (wil->recovery_state != fw_recovery_idle) {
108*4882a593Smuzhiyun 		wil_dbg_pm(wil, "Delay suspend during recovery\n");
109*4882a593Smuzhiyun 		rc = -EBUSY;
110*4882a593Smuzhiyun 		goto out;
111*4882a593Smuzhiyun 	}
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	/* interface is running */
114*4882a593Smuzhiyun 	mutex_lock(&wil->vif_mutex);
115*4882a593Smuzhiyun 	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
116*4882a593Smuzhiyun 		struct wil6210_vif *vif = wil->vifs[i];
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 		if (!vif)
119*4882a593Smuzhiyun 			continue;
120*4882a593Smuzhiyun 		if (!wil_can_suspend_vif(wil, vif, is_runtime)) {
121*4882a593Smuzhiyun 			rc = -EBUSY;
122*4882a593Smuzhiyun 			mutex_unlock(&wil->vif_mutex);
123*4882a593Smuzhiyun 			goto out;
124*4882a593Smuzhiyun 		}
125*4882a593Smuzhiyun 	}
126*4882a593Smuzhiyun 	mutex_unlock(&wil->vif_mutex);
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun out:
129*4882a593Smuzhiyun 	wil_dbg_pm(wil, "can_suspend: %s => %s (%d)\n",
130*4882a593Smuzhiyun 		   is_runtime ? "runtime" : "system", rc ? "No" : "Yes", rc);
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	if (rc)
133*4882a593Smuzhiyun 		wil->suspend_stats.rejected_by_host++;
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	return rc;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun 
wil_resume_keep_radio_on(struct wil6210_priv * wil)138*4882a593Smuzhiyun static int wil_resume_keep_radio_on(struct wil6210_priv *wil)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun 	int rc = 0;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	/* wil_status_resuming will be cleared when getting
143*4882a593Smuzhiyun 	 * WMI_TRAFFIC_RESUME_EVENTID
144*4882a593Smuzhiyun 	 */
145*4882a593Smuzhiyun 	set_bit(wil_status_resuming, wil->status);
146*4882a593Smuzhiyun 	clear_bit(wil_status_suspended, wil->status);
147*4882a593Smuzhiyun 	wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
148*4882a593Smuzhiyun 	wil_unmask_irq(wil);
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	wil6210_bus_request(wil, wil->bus_request_kbps_pre_suspend);
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	/* Send WMI resume request to the device */
153*4882a593Smuzhiyun 	rc = wmi_resume(wil);
154*4882a593Smuzhiyun 	if (rc) {
155*4882a593Smuzhiyun 		wil_err(wil, "device failed to resume (%d)\n", rc);
156*4882a593Smuzhiyun 		if (no_fw_recovery)
157*4882a593Smuzhiyun 			goto out;
158*4882a593Smuzhiyun 		rc = wil_down(wil);
159*4882a593Smuzhiyun 		if (rc) {
160*4882a593Smuzhiyun 			wil_err(wil, "wil_down failed (%d)\n", rc);
161*4882a593Smuzhiyun 			goto out;
162*4882a593Smuzhiyun 		}
163*4882a593Smuzhiyun 		rc = wil_up(wil);
164*4882a593Smuzhiyun 		if (rc) {
165*4882a593Smuzhiyun 			wil_err(wil, "wil_up failed (%d)\n", rc);
166*4882a593Smuzhiyun 			goto out;
167*4882a593Smuzhiyun 		}
168*4882a593Smuzhiyun 	}
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	/* Wake all queues */
171*4882a593Smuzhiyun 	wil_pm_wake_connected_net_queues(wil);
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun out:
174*4882a593Smuzhiyun 	if (rc)
175*4882a593Smuzhiyun 		set_bit(wil_status_suspended, wil->status);
176*4882a593Smuzhiyun 	return rc;
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun 
wil_suspend_keep_radio_on(struct wil6210_priv * wil)179*4882a593Smuzhiyun static int wil_suspend_keep_radio_on(struct wil6210_priv *wil)
180*4882a593Smuzhiyun {
181*4882a593Smuzhiyun 	int rc = 0;
182*4882a593Smuzhiyun 	unsigned long data_comp_to;
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	wil_dbg_pm(wil, "suspend keep radio on\n");
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	/* Prevent handling of new tx and wmi commands */
187*4882a593Smuzhiyun 	rc = down_write_trylock(&wil->mem_lock);
188*4882a593Smuzhiyun 	if (!rc) {
189*4882a593Smuzhiyun 		wil_err(wil,
190*4882a593Smuzhiyun 			"device is busy. down_write_trylock failed, returned (0x%x)\n",
191*4882a593Smuzhiyun 			rc);
192*4882a593Smuzhiyun 		wil->suspend_stats.rejected_by_host++;
193*4882a593Smuzhiyun 		return -EBUSY;
194*4882a593Smuzhiyun 	}
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	set_bit(wil_status_suspending, wil->status);
197*4882a593Smuzhiyun 	up_write(&wil->mem_lock);
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	wil_pm_stop_all_net_queues(wil);
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	if (!wil_is_tx_idle(wil)) {
202*4882a593Smuzhiyun 		wil_dbg_pm(wil, "Pending TX data, reject suspend\n");
203*4882a593Smuzhiyun 		wil->suspend_stats.rejected_by_host++;
204*4882a593Smuzhiyun 		goto reject_suspend;
205*4882a593Smuzhiyun 	}
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	if (!wil->txrx_ops.is_rx_idle(wil)) {
208*4882a593Smuzhiyun 		wil_dbg_pm(wil, "Pending RX data, reject suspend\n");
209*4882a593Smuzhiyun 		wil->suspend_stats.rejected_by_host++;
210*4882a593Smuzhiyun 		goto reject_suspend;
211*4882a593Smuzhiyun 	}
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	if (!wil_is_wmi_idle(wil)) {
214*4882a593Smuzhiyun 		wil_dbg_pm(wil, "Pending WMI events, reject suspend\n");
215*4882a593Smuzhiyun 		wil->suspend_stats.rejected_by_host++;
216*4882a593Smuzhiyun 		goto reject_suspend;
217*4882a593Smuzhiyun 	}
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	/* Send WMI suspend request to the device */
220*4882a593Smuzhiyun 	rc = wmi_suspend(wil);
221*4882a593Smuzhiyun 	if (rc) {
222*4882a593Smuzhiyun 		wil_dbg_pm(wil, "wmi_suspend failed, reject suspend (%d)\n",
223*4882a593Smuzhiyun 			   rc);
224*4882a593Smuzhiyun 		goto reject_suspend;
225*4882a593Smuzhiyun 	}
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	/* Wait for completion of the pending RX packets */
228*4882a593Smuzhiyun 	data_comp_to = jiffies + msecs_to_jiffies(WIL_DATA_COMPLETION_TO_MS);
229*4882a593Smuzhiyun 	if (test_bit(wil_status_napi_en, wil->status)) {
230*4882a593Smuzhiyun 		while (!wil->txrx_ops.is_rx_idle(wil)) {
231*4882a593Smuzhiyun 			if (time_after(jiffies, data_comp_to)) {
232*4882a593Smuzhiyun 				if (wil->txrx_ops.is_rx_idle(wil))
233*4882a593Smuzhiyun 					break;
234*4882a593Smuzhiyun 				wil_err(wil,
235*4882a593Smuzhiyun 					"TO waiting for idle RX, suspend failed\n");
236*4882a593Smuzhiyun 				wil->suspend_stats.r_on.failed_suspends++;
237*4882a593Smuzhiyun 				goto resume_after_fail;
238*4882a593Smuzhiyun 			}
239*4882a593Smuzhiyun 			wil_dbg_ratelimited(wil, "rx vring is not empty -> NAPI\n");
240*4882a593Smuzhiyun 			napi_synchronize(&wil->napi_rx);
241*4882a593Smuzhiyun 			msleep(20);
242*4882a593Smuzhiyun 		}
243*4882a593Smuzhiyun 	}
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	/* In case of pending WMI events, reject the suspend
246*4882a593Smuzhiyun 	 * and resume the device.
247*4882a593Smuzhiyun 	 * This can happen if the device sent the WMI events before
248*4882a593Smuzhiyun 	 * approving the suspend.
249*4882a593Smuzhiyun 	 */
250*4882a593Smuzhiyun 	if (!wil_is_wmi_idle(wil)) {
251*4882a593Smuzhiyun 		wil_err(wil, "suspend failed due to pending WMI events\n");
252*4882a593Smuzhiyun 		wil->suspend_stats.r_on.failed_suspends++;
253*4882a593Smuzhiyun 		goto resume_after_fail;
254*4882a593Smuzhiyun 	}
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	wil_mask_irq(wil);
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	/* Disable device reset on PERST */
259*4882a593Smuzhiyun 	wil_s(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	if (wil->platform_ops.suspend) {
262*4882a593Smuzhiyun 		rc = wil->platform_ops.suspend(wil->platform_handle, true);
263*4882a593Smuzhiyun 		if (rc) {
264*4882a593Smuzhiyun 			wil_err(wil, "platform device failed to suspend (%d)\n",
265*4882a593Smuzhiyun 				rc);
266*4882a593Smuzhiyun 			wil->suspend_stats.r_on.failed_suspends++;
267*4882a593Smuzhiyun 			wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
268*4882a593Smuzhiyun 			wil_unmask_irq(wil);
269*4882a593Smuzhiyun 			goto resume_after_fail;
270*4882a593Smuzhiyun 		}
271*4882a593Smuzhiyun 	}
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	/* Save the current bus request to return to the same in resume */
274*4882a593Smuzhiyun 	wil->bus_request_kbps_pre_suspend = wil->bus_request_kbps;
275*4882a593Smuzhiyun 	wil6210_bus_request(wil, 0);
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	set_bit(wil_status_suspended, wil->status);
278*4882a593Smuzhiyun 	clear_bit(wil_status_suspending, wil->status);
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	return rc;
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun resume_after_fail:
283*4882a593Smuzhiyun 	set_bit(wil_status_resuming, wil->status);
284*4882a593Smuzhiyun 	clear_bit(wil_status_suspending, wil->status);
285*4882a593Smuzhiyun 	rc = wmi_resume(wil);
286*4882a593Smuzhiyun 	/* if resume succeeded, reject the suspend */
287*4882a593Smuzhiyun 	if (!rc) {
288*4882a593Smuzhiyun 		rc = -EBUSY;
289*4882a593Smuzhiyun 		wil_pm_wake_connected_net_queues(wil);
290*4882a593Smuzhiyun 	}
291*4882a593Smuzhiyun 	return rc;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun reject_suspend:
294*4882a593Smuzhiyun 	clear_bit(wil_status_suspending, wil->status);
295*4882a593Smuzhiyun 	wil_pm_wake_connected_net_queues(wil);
296*4882a593Smuzhiyun 	return -EBUSY;
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun 
wil_suspend_radio_off(struct wil6210_priv * wil)299*4882a593Smuzhiyun static int wil_suspend_radio_off(struct wil6210_priv *wil)
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun 	int rc = 0;
302*4882a593Smuzhiyun 	bool active_ifaces;
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	wil_dbg_pm(wil, "suspend radio off\n");
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	rc = down_write_trylock(&wil->mem_lock);
307*4882a593Smuzhiyun 	if (!rc) {
308*4882a593Smuzhiyun 		wil_err(wil,
309*4882a593Smuzhiyun 			"device is busy. down_write_trylock failed, returned (0x%x)\n",
310*4882a593Smuzhiyun 			rc);
311*4882a593Smuzhiyun 		wil->suspend_stats.rejected_by_host++;
312*4882a593Smuzhiyun 		return -EBUSY;
313*4882a593Smuzhiyun 	}
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 	set_bit(wil_status_suspending, wil->status);
316*4882a593Smuzhiyun 	up_write(&wil->mem_lock);
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	/* if netif up, hardware is alive, shut it down */
319*4882a593Smuzhiyun 	mutex_lock(&wil->vif_mutex);
320*4882a593Smuzhiyun 	active_ifaces = wil_has_active_ifaces(wil, true, false);
321*4882a593Smuzhiyun 	mutex_unlock(&wil->vif_mutex);
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	if (active_ifaces) {
324*4882a593Smuzhiyun 		rc = wil_down(wil);
325*4882a593Smuzhiyun 		if (rc) {
326*4882a593Smuzhiyun 			wil_err(wil, "wil_down : %d\n", rc);
327*4882a593Smuzhiyun 			wil->suspend_stats.r_off.failed_suspends++;
328*4882a593Smuzhiyun 			goto out;
329*4882a593Smuzhiyun 		}
330*4882a593Smuzhiyun 	}
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	/* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */
333*4882a593Smuzhiyun 	wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n");
334*4882a593Smuzhiyun 	wil_disable_irq(wil);
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	if (wil->platform_ops.suspend) {
337*4882a593Smuzhiyun 		rc = wil->platform_ops.suspend(wil->platform_handle, false);
338*4882a593Smuzhiyun 		if (rc) {
339*4882a593Smuzhiyun 			wil_enable_irq(wil);
340*4882a593Smuzhiyun 			wil->suspend_stats.r_off.failed_suspends++;
341*4882a593Smuzhiyun 			goto out;
342*4882a593Smuzhiyun 		}
343*4882a593Smuzhiyun 	}
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	set_bit(wil_status_suspended, wil->status);
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun out:
348*4882a593Smuzhiyun 	clear_bit(wil_status_suspending, wil->status);
349*4882a593Smuzhiyun 	wil_dbg_pm(wil, "suspend radio off: %d\n", rc);
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 	return rc;
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun 
wil_resume_radio_off(struct wil6210_priv * wil)354*4882a593Smuzhiyun static int wil_resume_radio_off(struct wil6210_priv *wil)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun 	int rc = 0;
357*4882a593Smuzhiyun 	bool active_ifaces;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	wil_dbg_pm(wil, "Enabling PCIe IRQ\n");
360*4882a593Smuzhiyun 	wil_enable_irq(wil);
361*4882a593Smuzhiyun 	/* if any netif up, bring hardware up
362*4882a593Smuzhiyun 	 * During open(), IFF_UP set after actual device method
363*4882a593Smuzhiyun 	 * invocation. This prevent recursive call to wil_up()
364*4882a593Smuzhiyun 	 * wil_status_suspended will be cleared in wil_reset
365*4882a593Smuzhiyun 	 */
366*4882a593Smuzhiyun 	mutex_lock(&wil->vif_mutex);
367*4882a593Smuzhiyun 	active_ifaces = wil_has_active_ifaces(wil, true, false);
368*4882a593Smuzhiyun 	mutex_unlock(&wil->vif_mutex);
369*4882a593Smuzhiyun 	if (active_ifaces)
370*4882a593Smuzhiyun 		rc = wil_up(wil);
371*4882a593Smuzhiyun 	else
372*4882a593Smuzhiyun 		clear_bit(wil_status_suspended, wil->status);
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 	return rc;
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun 
wil_suspend(struct wil6210_priv * wil,bool is_runtime,bool keep_radio_on)377*4882a593Smuzhiyun int wil_suspend(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on)
378*4882a593Smuzhiyun {
379*4882a593Smuzhiyun 	int rc = 0;
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system");
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	if (test_bit(wil_status_suspended, wil->status)) {
384*4882a593Smuzhiyun 		wil_dbg_pm(wil, "trying to suspend while suspended\n");
385*4882a593Smuzhiyun 		return 0;
386*4882a593Smuzhiyun 	}
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	if (!keep_radio_on)
389*4882a593Smuzhiyun 		rc = wil_suspend_radio_off(wil);
390*4882a593Smuzhiyun 	else
391*4882a593Smuzhiyun 		rc = wil_suspend_keep_radio_on(wil);
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	wil_dbg_pm(wil, "suspend: %s => %d\n",
394*4882a593Smuzhiyun 		   is_runtime ? "runtime" : "system", rc);
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 	return rc;
397*4882a593Smuzhiyun }
398*4882a593Smuzhiyun 
wil_resume(struct wil6210_priv * wil,bool is_runtime,bool keep_radio_on)399*4882a593Smuzhiyun int wil_resume(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on)
400*4882a593Smuzhiyun {
401*4882a593Smuzhiyun 	int rc = 0;
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system");
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	if (wil->platform_ops.resume) {
406*4882a593Smuzhiyun 		rc = wil->platform_ops.resume(wil->platform_handle,
407*4882a593Smuzhiyun 					      keep_radio_on);
408*4882a593Smuzhiyun 		if (rc) {
409*4882a593Smuzhiyun 			wil_err(wil, "platform_ops.resume : %d\n", rc);
410*4882a593Smuzhiyun 			goto out;
411*4882a593Smuzhiyun 		}
412*4882a593Smuzhiyun 	}
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	if (keep_radio_on)
415*4882a593Smuzhiyun 		rc = wil_resume_keep_radio_on(wil);
416*4882a593Smuzhiyun 	else
417*4882a593Smuzhiyun 		rc = wil_resume_radio_off(wil);
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun out:
420*4882a593Smuzhiyun 	wil_dbg_pm(wil, "resume: %s => %d\n", is_runtime ? "runtime" : "system",
421*4882a593Smuzhiyun 		   rc);
422*4882a593Smuzhiyun 	return rc;
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun 
wil_pm_runtime_allow(struct wil6210_priv * wil)425*4882a593Smuzhiyun void wil_pm_runtime_allow(struct wil6210_priv *wil)
426*4882a593Smuzhiyun {
427*4882a593Smuzhiyun 	struct device *dev = wil_to_dev(wil);
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun 	pm_runtime_put_noidle(dev);
430*4882a593Smuzhiyun 	pm_runtime_set_autosuspend_delay(dev, WIL6210_AUTOSUSPEND_DELAY_MS);
431*4882a593Smuzhiyun 	pm_runtime_use_autosuspend(dev);
432*4882a593Smuzhiyun 	pm_runtime_allow(dev);
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun 
wil_pm_runtime_forbid(struct wil6210_priv * wil)435*4882a593Smuzhiyun void wil_pm_runtime_forbid(struct wil6210_priv *wil)
436*4882a593Smuzhiyun {
437*4882a593Smuzhiyun 	struct device *dev = wil_to_dev(wil);
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	pm_runtime_forbid(dev);
440*4882a593Smuzhiyun 	pm_runtime_get_noresume(dev);
441*4882a593Smuzhiyun }
442*4882a593Smuzhiyun 
wil_pm_runtime_get(struct wil6210_priv * wil)443*4882a593Smuzhiyun int wil_pm_runtime_get(struct wil6210_priv *wil)
444*4882a593Smuzhiyun {
445*4882a593Smuzhiyun 	int rc;
446*4882a593Smuzhiyun 	struct device *dev = wil_to_dev(wil);
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	rc = pm_runtime_get_sync(dev);
449*4882a593Smuzhiyun 	if (rc < 0) {
450*4882a593Smuzhiyun 		wil_err(wil, "pm_runtime_get_sync() failed, rc = %d\n", rc);
451*4882a593Smuzhiyun 		pm_runtime_put_noidle(dev);
452*4882a593Smuzhiyun 		return rc;
453*4882a593Smuzhiyun 	}
454*4882a593Smuzhiyun 
455*4882a593Smuzhiyun 	return 0;
456*4882a593Smuzhiyun }
457*4882a593Smuzhiyun 
wil_pm_runtime_put(struct wil6210_priv * wil)458*4882a593Smuzhiyun void wil_pm_runtime_put(struct wil6210_priv *wil)
459*4882a593Smuzhiyun {
460*4882a593Smuzhiyun 	struct device *dev = wil_to_dev(wil);
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 	pm_runtime_mark_last_busy(dev);
463*4882a593Smuzhiyun 	pm_runtime_put_autosuspend(dev);
464*4882a593Smuzhiyun }
465