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