162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2014,2017 Qualcomm Atheros, Inc. 462306a36Sopenharmony_ci * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "wil6210.h" 862306a36Sopenharmony_ci#include <linux/jiffies.h> 962306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define WIL6210_AUTOSUSPEND_DELAY_MS (1000) 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic void wil_pm_wake_connected_net_queues(struct wil6210_priv *wil) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci int i; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci mutex_lock(&wil->vif_mutex); 1862306a36Sopenharmony_ci for (i = 0; i < GET_MAX_VIFS(wil); i++) { 1962306a36Sopenharmony_ci struct wil6210_vif *vif = wil->vifs[i]; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci if (vif && test_bit(wil_vif_fwconnected, vif->status)) 2262306a36Sopenharmony_ci wil_update_net_queues_bh(wil, vif, NULL, false); 2362306a36Sopenharmony_ci } 2462306a36Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic void wil_pm_stop_all_net_queues(struct wil6210_priv *wil) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci int i; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci mutex_lock(&wil->vif_mutex); 3262306a36Sopenharmony_ci for (i = 0; i < GET_MAX_VIFS(wil); i++) { 3362306a36Sopenharmony_ci struct wil6210_vif *vif = wil->vifs[i]; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci if (vif) 3662306a36Sopenharmony_ci wil_update_net_queues_bh(wil, vif, NULL, true); 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic bool 4262306a36Sopenharmony_ciwil_can_suspend_vif(struct wil6210_priv *wil, struct wil6210_vif *vif, 4362306a36Sopenharmony_ci bool is_runtime) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct wireless_dev *wdev = vif_to_wdev(vif); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci switch (wdev->iftype) { 4862306a36Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 4962306a36Sopenharmony_ci wil_dbg_pm(wil, "Sniffer\n"); 5062306a36Sopenharmony_ci return false; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* for STA-like interface, don't runtime suspend */ 5362306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 5462306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_CLIENT: 5562306a36Sopenharmony_ci if (test_bit(wil_vif_fwconnecting, vif->status)) { 5662306a36Sopenharmony_ci wil_dbg_pm(wil, "Delay suspend when connecting\n"); 5762306a36Sopenharmony_ci return false; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci if (is_runtime) { 6062306a36Sopenharmony_ci wil_dbg_pm(wil, "STA-like interface\n"); 6162306a36Sopenharmony_ci return false; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci /* AP-like interface - can't suspend */ 6562306a36Sopenharmony_ci default: 6662306a36Sopenharmony_ci wil_dbg_pm(wil, "AP-like interface\n"); 6762306a36Sopenharmony_ci return false; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return true; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ciint wil_can_suspend(struct wil6210_priv *wil, bool is_runtime) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci int rc = 0, i; 7662306a36Sopenharmony_ci bool wmi_only = test_bit(WMI_FW_CAPABILITY_WMI_ONLY, 7762306a36Sopenharmony_ci wil->fw_capabilities); 7862306a36Sopenharmony_ci bool active_ifaces; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system"); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (wmi_only || debug_fw) { 8362306a36Sopenharmony_ci wil_dbg_pm(wil, "Deny any suspend - %s mode\n", 8462306a36Sopenharmony_ci wmi_only ? "wmi_only" : "debug_fw"); 8562306a36Sopenharmony_ci rc = -EBUSY; 8662306a36Sopenharmony_ci goto out; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci if (is_runtime && !wil->platform_ops.suspend) { 8962306a36Sopenharmony_ci rc = -EBUSY; 9062306a36Sopenharmony_ci goto out; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci mutex_lock(&wil->vif_mutex); 9462306a36Sopenharmony_ci active_ifaces = wil_has_active_ifaces(wil, true, false); 9562306a36Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (!active_ifaces) { 9862306a36Sopenharmony_ci /* can always sleep when down */ 9962306a36Sopenharmony_ci wil_dbg_pm(wil, "Interface is down\n"); 10062306a36Sopenharmony_ci goto out; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci if (test_bit(wil_status_resetting, wil->status)) { 10362306a36Sopenharmony_ci wil_dbg_pm(wil, "Delay suspend when resetting\n"); 10462306a36Sopenharmony_ci rc = -EBUSY; 10562306a36Sopenharmony_ci goto out; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci if (wil->recovery_state != fw_recovery_idle) { 10862306a36Sopenharmony_ci wil_dbg_pm(wil, "Delay suspend during recovery\n"); 10962306a36Sopenharmony_ci rc = -EBUSY; 11062306a36Sopenharmony_ci goto out; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* interface is running */ 11462306a36Sopenharmony_ci mutex_lock(&wil->vif_mutex); 11562306a36Sopenharmony_ci for (i = 0; i < GET_MAX_VIFS(wil); i++) { 11662306a36Sopenharmony_ci struct wil6210_vif *vif = wil->vifs[i]; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (!vif) 11962306a36Sopenharmony_ci continue; 12062306a36Sopenharmony_ci if (!wil_can_suspend_vif(wil, vif, is_runtime)) { 12162306a36Sopenharmony_ci rc = -EBUSY; 12262306a36Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 12362306a36Sopenharmony_ci goto out; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ciout: 12962306a36Sopenharmony_ci wil_dbg_pm(wil, "can_suspend: %s => %s (%d)\n", 13062306a36Sopenharmony_ci is_runtime ? "runtime" : "system", rc ? "No" : "Yes", rc); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (rc) 13362306a36Sopenharmony_ci wil->suspend_stats.rejected_by_host++; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return rc; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int wil_resume_keep_radio_on(struct wil6210_priv *wil) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci int rc = 0; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* wil_status_resuming will be cleared when getting 14362306a36Sopenharmony_ci * WMI_TRAFFIC_RESUME_EVENTID 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci set_bit(wil_status_resuming, wil->status); 14662306a36Sopenharmony_ci clear_bit(wil_status_suspended, wil->status); 14762306a36Sopenharmony_ci wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); 14862306a36Sopenharmony_ci wil_unmask_irq(wil); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci wil6210_bus_request(wil, wil->bus_request_kbps_pre_suspend); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Send WMI resume request to the device */ 15362306a36Sopenharmony_ci rc = wmi_resume(wil); 15462306a36Sopenharmony_ci if (rc) { 15562306a36Sopenharmony_ci wil_err(wil, "device failed to resume (%d)\n", rc); 15662306a36Sopenharmony_ci if (no_fw_recovery) 15762306a36Sopenharmony_ci goto out; 15862306a36Sopenharmony_ci rc = wil_down(wil); 15962306a36Sopenharmony_ci if (rc) { 16062306a36Sopenharmony_ci wil_err(wil, "wil_down failed (%d)\n", rc); 16162306a36Sopenharmony_ci goto out; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci rc = wil_up(wil); 16462306a36Sopenharmony_ci if (rc) { 16562306a36Sopenharmony_ci wil_err(wil, "wil_up failed (%d)\n", rc); 16662306a36Sopenharmony_ci goto out; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* Wake all queues */ 17162306a36Sopenharmony_ci wil_pm_wake_connected_net_queues(wil); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ciout: 17462306a36Sopenharmony_ci if (rc) 17562306a36Sopenharmony_ci set_bit(wil_status_suspended, wil->status); 17662306a36Sopenharmony_ci return rc; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int wil_suspend_keep_radio_on(struct wil6210_priv *wil) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci int rc = 0; 18262306a36Sopenharmony_ci unsigned long data_comp_to; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci wil_dbg_pm(wil, "suspend keep radio on\n"); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* Prevent handling of new tx and wmi commands */ 18762306a36Sopenharmony_ci rc = down_write_trylock(&wil->mem_lock); 18862306a36Sopenharmony_ci if (!rc) { 18962306a36Sopenharmony_ci wil_err(wil, 19062306a36Sopenharmony_ci "device is busy. down_write_trylock failed, returned (0x%x)\n", 19162306a36Sopenharmony_ci rc); 19262306a36Sopenharmony_ci wil->suspend_stats.rejected_by_host++; 19362306a36Sopenharmony_ci return -EBUSY; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci set_bit(wil_status_suspending, wil->status); 19762306a36Sopenharmony_ci up_write(&wil->mem_lock); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci wil_pm_stop_all_net_queues(wil); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (!wil_is_tx_idle(wil)) { 20262306a36Sopenharmony_ci wil_dbg_pm(wil, "Pending TX data, reject suspend\n"); 20362306a36Sopenharmony_ci wil->suspend_stats.rejected_by_host++; 20462306a36Sopenharmony_ci goto reject_suspend; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!wil->txrx_ops.is_rx_idle(wil)) { 20862306a36Sopenharmony_ci wil_dbg_pm(wil, "Pending RX data, reject suspend\n"); 20962306a36Sopenharmony_ci wil->suspend_stats.rejected_by_host++; 21062306a36Sopenharmony_ci goto reject_suspend; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (!wil_is_wmi_idle(wil)) { 21462306a36Sopenharmony_ci wil_dbg_pm(wil, "Pending WMI events, reject suspend\n"); 21562306a36Sopenharmony_ci wil->suspend_stats.rejected_by_host++; 21662306a36Sopenharmony_ci goto reject_suspend; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Send WMI suspend request to the device */ 22062306a36Sopenharmony_ci rc = wmi_suspend(wil); 22162306a36Sopenharmony_ci if (rc) { 22262306a36Sopenharmony_ci wil_dbg_pm(wil, "wmi_suspend failed, reject suspend (%d)\n", 22362306a36Sopenharmony_ci rc); 22462306a36Sopenharmony_ci goto reject_suspend; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Wait for completion of the pending RX packets */ 22862306a36Sopenharmony_ci data_comp_to = jiffies + msecs_to_jiffies(WIL_DATA_COMPLETION_TO_MS); 22962306a36Sopenharmony_ci if (test_bit(wil_status_napi_en, wil->status)) { 23062306a36Sopenharmony_ci while (!wil->txrx_ops.is_rx_idle(wil)) { 23162306a36Sopenharmony_ci if (time_after(jiffies, data_comp_to)) { 23262306a36Sopenharmony_ci if (wil->txrx_ops.is_rx_idle(wil)) 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci wil_err(wil, 23562306a36Sopenharmony_ci "TO waiting for idle RX, suspend failed\n"); 23662306a36Sopenharmony_ci wil->suspend_stats.r_on.failed_suspends++; 23762306a36Sopenharmony_ci goto resume_after_fail; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci wil_dbg_ratelimited(wil, "rx vring is not empty -> NAPI\n"); 24062306a36Sopenharmony_ci napi_synchronize(&wil->napi_rx); 24162306a36Sopenharmony_ci msleep(20); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* In case of pending WMI events, reject the suspend 24662306a36Sopenharmony_ci * and resume the device. 24762306a36Sopenharmony_ci * This can happen if the device sent the WMI events before 24862306a36Sopenharmony_ci * approving the suspend. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ci if (!wil_is_wmi_idle(wil)) { 25162306a36Sopenharmony_ci wil_err(wil, "suspend failed due to pending WMI events\n"); 25262306a36Sopenharmony_ci wil->suspend_stats.r_on.failed_suspends++; 25362306a36Sopenharmony_ci goto resume_after_fail; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci wil_mask_irq(wil); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Disable device reset on PERST */ 25962306a36Sopenharmony_ci wil_s(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (wil->platform_ops.suspend) { 26262306a36Sopenharmony_ci rc = wil->platform_ops.suspend(wil->platform_handle, true); 26362306a36Sopenharmony_ci if (rc) { 26462306a36Sopenharmony_ci wil_err(wil, "platform device failed to suspend (%d)\n", 26562306a36Sopenharmony_ci rc); 26662306a36Sopenharmony_ci wil->suspend_stats.r_on.failed_suspends++; 26762306a36Sopenharmony_ci wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); 26862306a36Sopenharmony_ci wil_unmask_irq(wil); 26962306a36Sopenharmony_ci goto resume_after_fail; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Save the current bus request to return to the same in resume */ 27462306a36Sopenharmony_ci wil->bus_request_kbps_pre_suspend = wil->bus_request_kbps; 27562306a36Sopenharmony_ci wil6210_bus_request(wil, 0); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci set_bit(wil_status_suspended, wil->status); 27862306a36Sopenharmony_ci clear_bit(wil_status_suspending, wil->status); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return rc; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ciresume_after_fail: 28362306a36Sopenharmony_ci set_bit(wil_status_resuming, wil->status); 28462306a36Sopenharmony_ci clear_bit(wil_status_suspending, wil->status); 28562306a36Sopenharmony_ci rc = wmi_resume(wil); 28662306a36Sopenharmony_ci /* if resume succeeded, reject the suspend */ 28762306a36Sopenharmony_ci if (!rc) { 28862306a36Sopenharmony_ci rc = -EBUSY; 28962306a36Sopenharmony_ci wil_pm_wake_connected_net_queues(wil); 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci return rc; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cireject_suspend: 29462306a36Sopenharmony_ci clear_bit(wil_status_suspending, wil->status); 29562306a36Sopenharmony_ci wil_pm_wake_connected_net_queues(wil); 29662306a36Sopenharmony_ci return -EBUSY; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int wil_suspend_radio_off(struct wil6210_priv *wil) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci int rc = 0; 30262306a36Sopenharmony_ci bool active_ifaces; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci wil_dbg_pm(wil, "suspend radio off\n"); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci rc = down_write_trylock(&wil->mem_lock); 30762306a36Sopenharmony_ci if (!rc) { 30862306a36Sopenharmony_ci wil_err(wil, 30962306a36Sopenharmony_ci "device is busy. down_write_trylock failed, returned (0x%x)\n", 31062306a36Sopenharmony_ci rc); 31162306a36Sopenharmony_ci wil->suspend_stats.rejected_by_host++; 31262306a36Sopenharmony_ci return -EBUSY; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci set_bit(wil_status_suspending, wil->status); 31662306a36Sopenharmony_ci up_write(&wil->mem_lock); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* if netif up, hardware is alive, shut it down */ 31962306a36Sopenharmony_ci mutex_lock(&wil->vif_mutex); 32062306a36Sopenharmony_ci active_ifaces = wil_has_active_ifaces(wil, true, false); 32162306a36Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (active_ifaces) { 32462306a36Sopenharmony_ci rc = wil_down(wil); 32562306a36Sopenharmony_ci if (rc) { 32662306a36Sopenharmony_ci wil_err(wil, "wil_down : %d\n", rc); 32762306a36Sopenharmony_ci wil->suspend_stats.r_off.failed_suspends++; 32862306a36Sopenharmony_ci goto out; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */ 33362306a36Sopenharmony_ci wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n"); 33462306a36Sopenharmony_ci wil_disable_irq(wil); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (wil->platform_ops.suspend) { 33762306a36Sopenharmony_ci rc = wil->platform_ops.suspend(wil->platform_handle, false); 33862306a36Sopenharmony_ci if (rc) { 33962306a36Sopenharmony_ci wil_enable_irq(wil); 34062306a36Sopenharmony_ci wil->suspend_stats.r_off.failed_suspends++; 34162306a36Sopenharmony_ci goto out; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci set_bit(wil_status_suspended, wil->status); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ciout: 34862306a36Sopenharmony_ci clear_bit(wil_status_suspending, wil->status); 34962306a36Sopenharmony_ci wil_dbg_pm(wil, "suspend radio off: %d\n", rc); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return rc; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int wil_resume_radio_off(struct wil6210_priv *wil) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci int rc = 0; 35762306a36Sopenharmony_ci bool active_ifaces; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci wil_dbg_pm(wil, "Enabling PCIe IRQ\n"); 36062306a36Sopenharmony_ci wil_enable_irq(wil); 36162306a36Sopenharmony_ci /* if any netif up, bring hardware up 36262306a36Sopenharmony_ci * During open(), IFF_UP set after actual device method 36362306a36Sopenharmony_ci * invocation. This prevent recursive call to wil_up() 36462306a36Sopenharmony_ci * wil_status_suspended will be cleared in wil_reset 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci mutex_lock(&wil->vif_mutex); 36762306a36Sopenharmony_ci active_ifaces = wil_has_active_ifaces(wil, true, false); 36862306a36Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 36962306a36Sopenharmony_ci if (active_ifaces) 37062306a36Sopenharmony_ci rc = wil_up(wil); 37162306a36Sopenharmony_ci else 37262306a36Sopenharmony_ci clear_bit(wil_status_suspended, wil->status); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return rc; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ciint wil_suspend(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci int rc = 0; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system"); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (test_bit(wil_status_suspended, wil->status)) { 38462306a36Sopenharmony_ci wil_dbg_pm(wil, "trying to suspend while suspended\n"); 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (!keep_radio_on) 38962306a36Sopenharmony_ci rc = wil_suspend_radio_off(wil); 39062306a36Sopenharmony_ci else 39162306a36Sopenharmony_ci rc = wil_suspend_keep_radio_on(wil); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci wil_dbg_pm(wil, "suspend: %s => %d\n", 39462306a36Sopenharmony_ci is_runtime ? "runtime" : "system", rc); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return rc; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ciint wil_resume(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci int rc = 0; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system"); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (wil->platform_ops.resume) { 40662306a36Sopenharmony_ci rc = wil->platform_ops.resume(wil->platform_handle, 40762306a36Sopenharmony_ci keep_radio_on); 40862306a36Sopenharmony_ci if (rc) { 40962306a36Sopenharmony_ci wil_err(wil, "platform_ops.resume : %d\n", rc); 41062306a36Sopenharmony_ci goto out; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (keep_radio_on) 41562306a36Sopenharmony_ci rc = wil_resume_keep_radio_on(wil); 41662306a36Sopenharmony_ci else 41762306a36Sopenharmony_ci rc = wil_resume_radio_off(wil); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ciout: 42062306a36Sopenharmony_ci wil_dbg_pm(wil, "resume: %s => %d\n", is_runtime ? "runtime" : "system", 42162306a36Sopenharmony_ci rc); 42262306a36Sopenharmony_ci return rc; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_civoid wil_pm_runtime_allow(struct wil6210_priv *wil) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci pm_runtime_put_noidle(dev); 43062306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(dev, WIL6210_AUTOSUSPEND_DELAY_MS); 43162306a36Sopenharmony_ci pm_runtime_use_autosuspend(dev); 43262306a36Sopenharmony_ci pm_runtime_allow(dev); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_civoid wil_pm_runtime_forbid(struct wil6210_priv *wil) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci pm_runtime_forbid(dev); 44062306a36Sopenharmony_ci pm_runtime_get_noresume(dev); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ciint wil_pm_runtime_get(struct wil6210_priv *wil) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci int rc; 44662306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci rc = pm_runtime_resume_and_get(dev); 44962306a36Sopenharmony_ci if (rc < 0) { 45062306a36Sopenharmony_ci wil_err(wil, "pm_runtime_resume_and_get() failed, rc = %d\n", rc); 45162306a36Sopenharmony_ci return rc; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_civoid wil_pm_runtime_put(struct wil6210_priv *wil) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 46262306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev); 46362306a36Sopenharmony_ci} 464