// SPDX-License-Identifier: ISC /* * Copyright (c) 2014,2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
*/
/* Prevent handling of new tx and wmi commands */
rc = down_write_trylock(&wil->mem_lock); if (!rc) {
wil_err(wil, "device is busy. down_write_trylock failed, returned (0x%x)\n",
rc);
wil->suspend_stats.rejected_by_host++; return -EBUSY;
}
/* Send WMI suspend request to the device */
rc = wmi_suspend(wil); if (rc) {
wil_dbg_pm(wil, "wmi_suspend failed, reject suspend (%d)\n",
rc); goto reject_suspend;
}
/* Wait for completion of the pending RX packets */
data_comp_to = jiffies + msecs_to_jiffies(WIL_DATA_COMPLETION_TO_MS); if (test_bit(wil_status_napi_en, wil->status)) { while (!wil->txrx_ops.is_rx_idle(wil)) { if (time_after(jiffies, data_comp_to)) { if (wil->txrx_ops.is_rx_idle(wil)) break;
wil_err(wil, "TO waiting for idle RX, suspend failed\n");
wil->suspend_stats.r_on.failed_suspends++; goto resume_after_fail;
}
wil_dbg_ratelimited(wil, "rx vring is not empty -> NAPI\n");
napi_synchronize(&wil->napi_rx);
msleep(20);
}
}
/* In case of pending WMI events, reject the suspend * and resume the device. * This can happen if the device sent the WMI events before * approving the suspend.
*/ if (!wil_is_wmi_idle(wil)) {
wil_err(wil, "suspend failed due to pending WMI events\n");
wil->suspend_stats.r_on.failed_suspends++; goto resume_after_fail;
}
wil_mask_irq(wil);
/* Disable device reset on PERST */
wil_s(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
if (wil->platform_ops.suspend) {
rc = wil->platform_ops.suspend(wil->platform_handle, true); if (rc) {
wil_err(wil, "platform device failed to suspend (%d)\n",
rc);
wil->suspend_stats.r_on.failed_suspends++;
wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
wil_unmask_irq(wil); goto resume_after_fail;
}
}
/* Save the current bus request to return to the same in resume */
wil->bus_request_kbps_pre_suspend = wil->bus_request_kbps;
wil6210_bus_request(wil, 0);
/* if netif up, hardware is alive, shut it down */
mutex_lock(&wil->vif_mutex);
active_ifaces = wil_has_active_ifaces(wil, true, false);
mutex_unlock(&wil->vif_mutex);
if (active_ifaces) {
rc = wil_down(wil); if (rc) {
wil_err(wil, "wil_down : %d\n", rc);
wil->suspend_stats.r_off.failed_suspends++; goto out;
}
}
/* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */
wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n");
wil_disable_irq(wil);
if (wil->platform_ops.suspend) {
rc = wil->platform_ops.suspend(wil->platform_handle, false); if (rc) {
wil_enable_irq(wil);
wil->suspend_stats.r_off.failed_suspends++; goto out;
}
}
set_bit(wil_status_suspended, wil->status);
out:
clear_bit(wil_status_suspending, wil->status);
wil_dbg_pm(wil, "suspend radio off: %d\n", rc);
wil_dbg_pm(wil, "Enabling PCIe IRQ\n");
wil_enable_irq(wil); /* if any netif up, bring hardware up * During open(), IFF_UP set after actual device method * invocation. This prevent recursive call to wil_up() * wil_status_suspended will be cleared in wil_reset
*/
mutex_lock(&wil->vif_mutex);
active_ifaces = wil_has_active_ifaces(wil, true, false);
mutex_unlock(&wil->vif_mutex); if (active_ifaces)
rc = wil_up(wil); else
clear_bit(wil_status_suspended, wil->status);
return rc;
}
int wil_suspend(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on)
{ int rc = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.