|
@@ -0,0 +1,1225 @@
|
|
|
+/*
|
|
|
+* Copyright (c) 2017 The Linux Foundation. All rights reserved.
|
|
|
+*
|
|
|
+* Permission to use, copy, modify, and/or distribute this software for
|
|
|
+* any purpose with or without fee is hereby granted, provided that the
|
|
|
+* above copyright notice and this permission notice appear in all
|
|
|
+* copies.
|
|
|
+*
|
|
|
+* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
|
|
+* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
|
|
+* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
|
|
+* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
|
|
+* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
|
|
+* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
|
+* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
+* PERFORMANCE OF THIS SOFTWARE.
|
|
|
+*/
|
|
|
+/**
|
|
|
+ * DOC: Define API's for suspend / resume handling
|
|
|
+ */
|
|
|
+
|
|
|
+#include "wlan_pmo_wow.h"
|
|
|
+#include "wlan_pmo_tgt_api.h"
|
|
|
+#include "wlan_pmo_main.h"
|
|
|
+#include "wlan_pmo_obj_mgmt_public_struct.h"
|
|
|
+#include "wlan_pmo_lphb.h"
|
|
|
+#include "wlan_pmo_suspend_resume.h"
|
|
|
+#include "cdp_txrx_ops.h"
|
|
|
+#include "cdp_txrx_misc.h"
|
|
|
+#include "cdp_txrx_flow_ctrl_legacy.h"
|
|
|
+#include "hif.h"
|
|
|
+#include "htc_api.h"
|
|
|
+#include "wlan_pmo_obj_mgmt_api.h"
|
|
|
+#include <wlan_scan_ucfg_api.h>
|
|
|
+#include "cds_api.h"
|
|
|
+#include "wlan_pmo_static_config.h"
|
|
|
+
|
|
|
+/**
|
|
|
+ * pmo_core_calculate_listen_interval() - Calculate vdev listen interval
|
|
|
+ * @vdev: objmgr vdev handle
|
|
|
+ * @vdev_ctx: pmo vdev priv ctx
|
|
|
+ * @listen_interval: listen interval which is computed for vdev
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS
|
|
|
+ */
|
|
|
+static QDF_STATUS pmo_core_calculate_listen_interval(
|
|
|
+ struct wlan_objmgr_vdev *vdev,
|
|
|
+ struct pmo_vdev_priv_obj *vdev_ctx,
|
|
|
+ uint32_t *listen_interval)
|
|
|
+{
|
|
|
+ uint32_t max_mod_dtim;
|
|
|
+ uint32_t beacon_interval_mod;
|
|
|
+ struct pmo_psoc_cfg *psoc_cfg = &vdev_ctx->pmo_psoc_ctx->psoc_cfg;
|
|
|
+
|
|
|
+ if (psoc_cfg->sta_dynamic_dtim) {
|
|
|
+ *listen_interval = psoc_cfg->sta_dynamic_dtim;
|
|
|
+ } else if ((psoc_cfg->sta_mod_dtim) &&
|
|
|
+ (psoc_cfg->sta_max_li_mod_dtim)) {
|
|
|
+ /*
|
|
|
+ * When the system is in suspend
|
|
|
+ * (maximum beacon will be at 1s == 10)
|
|
|
+ * If maxModulatedDTIM ((MAX_LI_VAL = 10) / AP_DTIM)
|
|
|
+ * equal or larger than MDTIM
|
|
|
+ * (configured in WCNSS_qcom_cfg.ini)
|
|
|
+ * Set LI to MDTIM * AP_DTIM
|
|
|
+ * If Dtim = 2 and Mdtim = 2 then LI is 4
|
|
|
+ * Else
|
|
|
+ * Set LI to maxModulatedDTIM * AP_DTIM
|
|
|
+ */
|
|
|
+ beacon_interval_mod =
|
|
|
+ pmo_core_get_vdev_beacon_interval(vdev) / 100;
|
|
|
+ if (beacon_interval_mod == 0)
|
|
|
+ beacon_interval_mod = 1;
|
|
|
+
|
|
|
+ max_mod_dtim = psoc_cfg->sta_max_li_mod_dtim /
|
|
|
+ (pmo_core_get_vdev_dtim_period(vdev)
|
|
|
+ * beacon_interval_mod);
|
|
|
+
|
|
|
+ if (max_mod_dtim <= 0)
|
|
|
+ max_mod_dtim = 1;
|
|
|
+
|
|
|
+ if (max_mod_dtim >= psoc_cfg->sta_mod_dtim) {
|
|
|
+ *listen_interval =
|
|
|
+ (psoc_cfg->sta_mod_dtim *
|
|
|
+ pmo_core_get_vdev_dtim_period(vdev));
|
|
|
+ } else {
|
|
|
+ *listen_interval =
|
|
|
+ (max_mod_dtim *
|
|
|
+ pmo_core_get_vdev_dtim_period(vdev));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return QDF_STATUS_E_FAULT;
|
|
|
+ }
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pmo_core_set_vdev_suspend_dtim() - set suspend dtim parameters in fw
|
|
|
+ * @psoc: objmgr psoc handle
|
|
|
+ * @vdev: objmgr vdev handle
|
|
|
+ * @vdev_ctx: pmo vdev priv ctx
|
|
|
+ *
|
|
|
+ * Return: none
|
|
|
+ */
|
|
|
+static void pmo_core_set_vdev_suspend_dtim(struct wlan_objmgr_psoc *psoc,
|
|
|
+ struct wlan_objmgr_vdev *vdev,
|
|
|
+ struct pmo_vdev_priv_obj *vdev_ctx)
|
|
|
+{
|
|
|
+ uint32_t listen_interval;
|
|
|
+ QDF_STATUS ret;
|
|
|
+ uint8_t vdev_id;
|
|
|
+ enum pmo_power_save_qpower_mode qpower_config;
|
|
|
+ enum tQDF_ADAPTER_MODE opmode = pmo_core_get_vdev_op_mode(vdev);
|
|
|
+
|
|
|
+ qpower_config = pmo_core_psoc_get_qpower_config(psoc);
|
|
|
+ vdev_id = pmo_vdev_get_id(vdev);
|
|
|
+ if (PMO_VDEV_IN_STA_MODE(opmode) &&
|
|
|
+ pmo_core_get_vdev_dtim_period(vdev) != 0) {
|
|
|
+ /* calculate listen interval */
|
|
|
+ ret = pmo_core_calculate_listen_interval(vdev, vdev_ctx,
|
|
|
+ &listen_interval);
|
|
|
+ if (ret != QDF_STATUS_SUCCESS) {
|
|
|
+ /* even it fails continue fwr will take default LI */
|
|
|
+ pmo_info("Fail to calculate listen interval");
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = pmo_tgt_vdev_update_param_req(vdev,
|
|
|
+ pmo_vdev_param_listen_interval,
|
|
|
+ listen_interval);
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret)) {
|
|
|
+ /* even it fails continue fwr will take default LI */
|
|
|
+ pmo_info("Failed to Set Listen Interval vdevId %d",
|
|
|
+ vdev_id);
|
|
|
+ }
|
|
|
+ pmo_debug("Set Listen Interval vdevId %d Listen Intv %d",
|
|
|
+ vdev_id, listen_interval);
|
|
|
+
|
|
|
+ if (qpower_config) {
|
|
|
+ pmo_debug("disable Qpower in suspend mode!");
|
|
|
+ ret = pmo_tgt_send_vdev_sta_ps_param(vdev,
|
|
|
+ pmo_sta_ps_enable_qpower, 0);
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret))
|
|
|
+ pmo_info("Failed to disable Qpower in suspend mode!");
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = pmo_tgt_vdev_update_param_req(vdev,
|
|
|
+ pmo_vdev_param_dtim_policy,
|
|
|
+ pmo_normal_dtim);
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret))
|
|
|
+ pmo_info("Failed to Set to Normal DTIM vdevId %d",
|
|
|
+ vdev_id);
|
|
|
+
|
|
|
+ /* Set it to Normal DTIM */
|
|
|
+ pmo_core_vdev_set_dtim_policy(vdev, pmo_normal_dtim);
|
|
|
+ pmo_debug("Set DTIM Policy to Normal Dtim vdevId %d", vdev_id);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pmo_core_set_suspend_dtim() - set suspend dtim
|
|
|
+ * @psoc: objmgr psoc handle
|
|
|
+ *
|
|
|
+ * Return: none
|
|
|
+ */
|
|
|
+static void pmo_core_set_suspend_dtim(struct wlan_objmgr_psoc *psoc)
|
|
|
+{
|
|
|
+ uint8_t vdev_id;
|
|
|
+ struct wlan_objmgr_vdev *vdev;
|
|
|
+ struct pmo_vdev_priv_obj *vdev_ctx;
|
|
|
+ bool alt_mdtim_enabled;
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ /* Iterate through VDEV list */
|
|
|
+ for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) {
|
|
|
+ vdev = pmo_psoc_get_vdev(psoc, vdev_id);
|
|
|
+ if (!vdev)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ status = pmo_vdev_get_ref(vdev);
|
|
|
+ if (QDF_IS_STATUS_ERROR(status))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ vdev_ctx = pmo_vdev_get_priv(vdev);
|
|
|
+ qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
|
|
|
+ alt_mdtim_enabled = vdev_ctx->alt_modulated_dtim_enable;
|
|
|
+ qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
|
|
|
+
|
|
|
+ if (!alt_mdtim_enabled)
|
|
|
+ pmo_core_set_vdev_suspend_dtim(psoc, vdev, vdev_ctx);
|
|
|
+
|
|
|
+ pmo_vdev_put_ref(vdev);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pmo_core_update_wow_bus_suspend() - set wow bus suspend flag
|
|
|
+ * @psoc: objmgr psoc handle
|
|
|
+ * @psoc_ctx: pmo psoc priv ctx
|
|
|
+ * @val: true for enable else false
|
|
|
+ * Return: none
|
|
|
+ */
|
|
|
+static inline
|
|
|
+void pmo_core_update_wow_bus_suspend(struct wlan_objmgr_psoc *psoc,
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx, int val)
|
|
|
+{
|
|
|
+ qdf_spin_lock_bh(&psoc_ctx->lock);
|
|
|
+ psoc_ctx->wow.is_wow_bus_suspended = val;
|
|
|
+ qdf_spin_unlock_bh(&psoc_ctx->lock);
|
|
|
+ pmo_tgt_psoc_update_wow_bus_suspend_state(psoc, val);
|
|
|
+}
|
|
|
+
|
|
|
+/* Define for conciseness */
|
|
|
+#define BM_LEN PMO_WOW_MAX_EVENT_BM_LEN
|
|
|
+#define EV_NLO WOW_NLO_SCAN_COMPLETE_EVENT
|
|
|
+#define EV_PWR WOW_CHIP_POWER_FAILURE_DETECT_EVENT
|
|
|
+
|
|
|
+void pmo_core_configure_dynamic_wake_events(struct wlan_objmgr_psoc *psoc)
|
|
|
+{
|
|
|
+ int vdev_id;
|
|
|
+ uint32_t adapter_type;
|
|
|
+ uint32_t enable_mask[BM_LEN];
|
|
|
+ uint32_t disable_mask[BM_LEN];
|
|
|
+ struct wlan_objmgr_vdev *vdev;
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx;
|
|
|
+ bool enable_configured;
|
|
|
+ bool disable_configured;
|
|
|
+
|
|
|
+ /* Iterate through VDEV list */
|
|
|
+ for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) {
|
|
|
+
|
|
|
+ enable_configured = false;
|
|
|
+ disable_configured = false;
|
|
|
+
|
|
|
+ qdf_mem_set(enable_mask, sizeof(uint32_t) * BM_LEN, 0);
|
|
|
+ qdf_mem_set(disable_mask, sizeof(uint32_t) * BM_LEN, 0);
|
|
|
+
|
|
|
+ vdev = pmo_psoc_get_vdev(psoc, vdev_id);
|
|
|
+ if (!vdev)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (ucfg_scan_get_pno_in_progress(vdev)) {
|
|
|
+ if (ucfg_scan_get_pno_match(vdev)) {
|
|
|
+ pmo_set_wow_event_bitmap(EV_NLO,
|
|
|
+ BM_LEN,
|
|
|
+ enable_mask);
|
|
|
+ enable_configured = true;
|
|
|
+ } else {
|
|
|
+ pmo_set_wow_event_bitmap(EV_NLO,
|
|
|
+ BM_LEN,
|
|
|
+ disable_mask);
|
|
|
+ disable_configured = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ adapter_type = pmo_get_vdev_opmode(vdev);
|
|
|
+
|
|
|
+ psoc_ctx = pmo_psoc_get_priv(psoc);
|
|
|
+
|
|
|
+ if (psoc_ctx->psoc_cfg.auto_power_save_fail_mode &&
|
|
|
+ (adapter_type == QDF_STA_MODE ||
|
|
|
+ adapter_type == QDF_P2P_CLIENT_MODE)
|
|
|
+ ) {
|
|
|
+ if (psoc_ctx->is_device_in_low_pwr_mode &&
|
|
|
+ psoc_ctx->is_device_in_low_pwr_mode(vdev_id))
|
|
|
+ pmo_set_wow_event_bitmap(EV_PWR,
|
|
|
+ BM_LEN,
|
|
|
+ enable_mask);
|
|
|
+ pmo_core_enable_wakeup_event(psoc, vdev_id,
|
|
|
+ enable_mask);
|
|
|
+ enable_configured = true;
|
|
|
+ }
|
|
|
+ if (enable_configured)
|
|
|
+ pmo_core_enable_wakeup_event(psoc, vdev_id,
|
|
|
+ enable_mask);
|
|
|
+
|
|
|
+ if (disable_configured)
|
|
|
+ pmo_core_disable_wakeup_event(psoc, vdev_id,
|
|
|
+ disable_mask);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pmo_core_psoc_configure_suspend(): configure suspend req events
|
|
|
+ * @psoc: objmgr psoc
|
|
|
+ *
|
|
|
+ * Responsibility of the caller to take the psoc reference.
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS_SUCCESS for success or error code
|
|
|
+ */
|
|
|
+static QDF_STATUS pmo_core_psoc_configure_suspend(struct wlan_objmgr_psoc *psoc)
|
|
|
+{
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+
|
|
|
+ psoc_ctx = pmo_psoc_get_priv(psoc);
|
|
|
+
|
|
|
+ if (pmo_core_is_wow_applicable(psoc)) {
|
|
|
+ pmo_info("WOW Suspend");
|
|
|
+ pmo_core_apply_lphb(psoc);
|
|
|
+
|
|
|
+ pmo_core_configure_dynamic_wake_events(psoc);
|
|
|
+ pmo_core_update_wow_enable(psoc_ctx, true);
|
|
|
+ pmo_core_update_wow_enable_cmd_sent(psoc_ctx, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ pmo_core_set_suspend_dtim(psoc);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * To handle race between hif_pci_suspend and unpause/pause tx handler.
|
|
|
+ * This happens when host sending WMI_WOW_ENABLE_CMDID to FW and receive
|
|
|
+ * WMI_TX_PAUSE_EVENT with ACTON_UNPAUSE almost at same time.
|
|
|
+ */
|
|
|
+ pmo_core_update_wow_bus_suspend(psoc, psoc_ctx, true);
|
|
|
+
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+QDF_STATUS pmo_core_psoc_user_space_suspend_req(struct wlan_objmgr_psoc *psoc,
|
|
|
+ enum qdf_suspend_type type)
|
|
|
+{
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+
|
|
|
+ status = pmo_psoc_get_ref(psoc);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("pmo cannot get the reference out of psoc");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Suspend all components before sending target suspend command */
|
|
|
+ status = pmo_suspend_all_components(psoc, type);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("Failed to suspend all component");
|
|
|
+ goto dec_psoc_ref;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = pmo_core_psoc_configure_suspend(psoc);
|
|
|
+ if (status != QDF_STATUS_SUCCESS)
|
|
|
+ pmo_err("Failed to configure suspend");
|
|
|
+
|
|
|
+dec_psoc_ref:
|
|
|
+ pmo_psoc_put_ref(psoc);
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pmo_core_set_vdev_resume_dtim() - set resume dtim parameters in fw
|
|
|
+ * @psoc: objmgr psoc handle
|
|
|
+ * @vdev: objmgr vdev handle
|
|
|
+ * @vdev_ctx: pmo vdev priv ctx
|
|
|
+ *
|
|
|
+ * Return: none
|
|
|
+ */
|
|
|
+static void pmo_core_set_vdev_resume_dtim(struct wlan_objmgr_psoc *psoc,
|
|
|
+ struct wlan_objmgr_vdev *vdev,
|
|
|
+ struct pmo_vdev_priv_obj *vdev_ctx)
|
|
|
+{
|
|
|
+ enum pmo_power_save_qpower_mode qpower_config;
|
|
|
+ QDF_STATUS ret;
|
|
|
+ uint8_t vdev_id;
|
|
|
+ enum tQDF_ADAPTER_MODE opmode = pmo_core_get_vdev_op_mode(vdev);
|
|
|
+ uint32_t cfg_data_val = 0;
|
|
|
+
|
|
|
+ qpower_config = pmo_core_psoc_get_qpower_config(psoc);
|
|
|
+ vdev_id = pmo_vdev_get_id(vdev);
|
|
|
+ if ((PMO_VDEV_IN_STA_MODE(opmode)) &&
|
|
|
+ (pmo_core_vdev_get_dtim_policy(vdev) == pmo_normal_dtim)) {
|
|
|
+/*
|
|
|
+ if (!mac) {
|
|
|
+ WMA_LOGE(FL("Failed to get mac context"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if ((wlan_cfg_get_int(mac, WNI_CFG_LISTEN_INTERVAL,
|
|
|
+ &cfg_data_val) != eSIR_SUCCESS)) {
|
|
|
+ pmo_err("Failed to get value for listen interval");
|
|
|
+ cfg_data_val = POWERSAVE_DEFAULT_LISTEN_INTERVAL;
|
|
|
+ }
|
|
|
+*/
|
|
|
+ cfg_data_val = 1;
|
|
|
+ ret = pmo_tgt_vdev_update_param_req(vdev,
|
|
|
+ pmo_vdev_param_listen_interval, cfg_data_val);
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret)) {
|
|
|
+ /* Even it fails continue Fw will take default LI */
|
|
|
+ pmo_err("Failed to Set Listen Interval vdevId %d",
|
|
|
+ vdev_id);
|
|
|
+ }
|
|
|
+ pmo_debug("Set Listen Interval vdevId %d Listen Intv %d",
|
|
|
+ vdev_id, cfg_data_val);
|
|
|
+
|
|
|
+ ret = pmo_tgt_vdev_update_param_req(vdev,
|
|
|
+ pmo_vdev_param_dtim_policy,
|
|
|
+ pmo_stick_dtim);
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret)) {
|
|
|
+ /* Set it back to Stick DTIM */
|
|
|
+ pmo_err("Failed to Set to Stick DTIM vdevId %d",
|
|
|
+ vdev_id);
|
|
|
+ }
|
|
|
+ pmo_core_vdev_set_dtim_policy(vdev, pmo_stick_dtim);
|
|
|
+ pmo_debug("Set DTIM Policy to Stick Dtim vdevId %d", vdev_id);
|
|
|
+
|
|
|
+ if (qpower_config) {
|
|
|
+ pmo_debug("enable Qpower in resume mode!");
|
|
|
+ ret = pmo_tgt_send_vdev_sta_ps_param(vdev,
|
|
|
+ pmo_sta_ps_enable_qpower, qpower_config);
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret))
|
|
|
+ pmo_err("Failed to enable Qpower in resume");
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pmo_core_set_resume_dtim() - set resume time dtim
|
|
|
+ * @psoc: objmgr psoc handle
|
|
|
+ *
|
|
|
+ * Return: none
|
|
|
+ */
|
|
|
+static void pmo_core_set_resume_dtim(struct wlan_objmgr_psoc *psoc)
|
|
|
+{
|
|
|
+ uint8_t vdev_id;
|
|
|
+ struct wlan_objmgr_vdev *vdev;
|
|
|
+ struct pmo_vdev_priv_obj *vdev_ctx;
|
|
|
+ bool alt_mdtim_enabled;
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ /* Iterate through VDEV list */
|
|
|
+ for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) {
|
|
|
+ vdev = pmo_psoc_get_vdev(psoc, vdev_id);
|
|
|
+ if (!vdev)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ status = pmo_vdev_get_ref(vdev);
|
|
|
+ if (QDF_IS_STATUS_ERROR(status))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ vdev_ctx = pmo_vdev_get_priv(vdev);
|
|
|
+ qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
|
|
|
+ alt_mdtim_enabled = vdev_ctx->alt_modulated_dtim_enable;
|
|
|
+ qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
|
|
|
+
|
|
|
+ if (!alt_mdtim_enabled)
|
|
|
+ pmo_core_set_vdev_resume_dtim(psoc, vdev, vdev_ctx);
|
|
|
+
|
|
|
+ pmo_vdev_put_ref(vdev);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pmo_unpause_vdev - unpause all vdev
|
|
|
+ * @psoc: objmgr psoc handle
|
|
|
+ *
|
|
|
+ * unpause all vdev aftter resume/coming out of wow mode
|
|
|
+ *
|
|
|
+ * Return: none
|
|
|
+ */
|
|
|
+static void pmo_unpause_all_vdev(struct wlan_objmgr_psoc *psoc,
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx)
|
|
|
+{
|
|
|
+ uint8_t vdev_id;
|
|
|
+ struct wlan_objmgr_psoc_objmgr *objmgr;
|
|
|
+ struct wlan_objmgr_vdev *vdev;
|
|
|
+
|
|
|
+ /* Iterate through VDEV list */
|
|
|
+ for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) {
|
|
|
+ wlan_psoc_obj_lock(psoc);
|
|
|
+ objmgr = &psoc->soc_objmgr;
|
|
|
+ if (!objmgr->wlan_vdev_list[vdev_id]) {
|
|
|
+ wlan_psoc_obj_unlock(psoc);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ vdev = objmgr->wlan_vdev_list[vdev_id];
|
|
|
+ wlan_psoc_obj_unlock(psoc);
|
|
|
+ if (vdev) {
|
|
|
+#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || defined(QCA_LL_TX_FLOW_CONTROL_V2)
|
|
|
+ /*
|
|
|
+ * When host resume, by default,
|
|
|
+ * unpause all active vdev
|
|
|
+ */
|
|
|
+ if (pmo_core_vdev_get_pause_bitmap(psoc_ctx, vdev_id)) {
|
|
|
+ cdp_fc_vdev_unpause(
|
|
|
+ pmo_core_psoc_get_dp_handle(psoc),
|
|
|
+ pmo_core_vdev_get_dp_handle(vdev),
|
|
|
+ 0xffffffff);
|
|
|
+ if (psoc_ctx->pause_bitmap_notifier)
|
|
|
+ psoc_ctx->pause_bitmap_notifier(vdev_id,
|
|
|
+ 0);
|
|
|
+ }
|
|
|
+#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pmo_core_psoc_configure_resume(): configure events after bus resume
|
|
|
+ * @psoc: objmgr psoc
|
|
|
+ *
|
|
|
+ * Responsibility of the caller to take the psoc reference.
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS_SUCCESS for success or error code
|
|
|
+ */
|
|
|
+static QDF_STATUS pmo_core_psoc_configure_resume(struct wlan_objmgr_psoc *psoc)
|
|
|
+{
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+
|
|
|
+ psoc_ctx = pmo_psoc_get_priv(psoc);
|
|
|
+
|
|
|
+ pmo_core_set_resume_dtim(psoc);
|
|
|
+ pmo_core_update_wow_bus_suspend(psoc, psoc_ctx, false);
|
|
|
+ pmo_unpause_all_vdev(psoc, psoc_ctx);
|
|
|
+
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+QDF_STATUS pmo_core_psoc_user_space_resume_req(struct wlan_objmgr_psoc *psoc,
|
|
|
+ enum qdf_suspend_type type)
|
|
|
+{
|
|
|
+ QDF_STATUS status = QDF_STATUS_SUCCESS;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+
|
|
|
+ status = pmo_psoc_get_ref(psoc);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("pmo cannot get the reference out of psoc");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Resume all components */
|
|
|
+ status = pmo_resume_all_components(psoc, type);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("Failed to resume all the components");
|
|
|
+ goto dec_psoc_ref;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = pmo_core_psoc_configure_resume(psoc);
|
|
|
+ if (status != QDF_STATUS_SUCCESS)
|
|
|
+ pmo_err("Failed to configure resume");
|
|
|
+
|
|
|
+dec_psoc_ref:
|
|
|
+ pmo_psoc_put_ref(psoc);
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pmo_core_enable_wow_in_fw() - enable wow in fw
|
|
|
+ * @psoc: objmgr psoc handle
|
|
|
+ * @psoc_ctx: pmo psoc private ctx
|
|
|
+ * @wow_params: collection of wow enable override parameters
|
|
|
+ *
|
|
|
+ * Return: QDF status
|
|
|
+ */
|
|
|
+static
|
|
|
+QDF_STATUS pmo_core_enable_wow_in_fw(struct wlan_objmgr_psoc *psoc,
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx,
|
|
|
+ struct pmo_wow_enable_params *wow_params)
|
|
|
+{
|
|
|
+ int host_credits, wmi_pending_cmds;
|
|
|
+ struct pmo_wow_cmd_params param = {0};
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+ qdf_event_reset(&psoc_ctx->wow.target_suspend);
|
|
|
+ pmo_core_set_wow_nack(psoc_ctx, false);
|
|
|
+ host_credits = pmo_tgt_psoc_get_host_credits(psoc);
|
|
|
+ wmi_pending_cmds = pmo_tgt_psoc_get_pending_cmnds(psoc);
|
|
|
+ pmo_debug("Credits:%d; Pending_Cmds: %d",
|
|
|
+ host_credits, wmi_pending_cmds);
|
|
|
+
|
|
|
+ param.enable = true;
|
|
|
+ if (wow_params->is_unit_test)
|
|
|
+ param.flags = WMI_WOW_FLAG_UNIT_TEST_ENABLE;
|
|
|
+
|
|
|
+ switch (wow_params->interface_pause) {
|
|
|
+ default:
|
|
|
+ pmo_err("Invalid interface pause setting: %d",
|
|
|
+ wow_params->interface_pause);
|
|
|
+ /* intentional fall-through to default */
|
|
|
+ case PMO_WOW_INTERFACE_PAUSE_DEFAULT:
|
|
|
+ param.can_suspend_link =
|
|
|
+ htc_can_suspend_link(
|
|
|
+ pmo_core_psoc_get_htc_handle(psoc));
|
|
|
+ break;
|
|
|
+ case PMO_WOW_INTERFACE_PAUSE_ENABLE:
|
|
|
+ param.can_suspend_link = true;
|
|
|
+ break;
|
|
|
+ case PMO_WOW_INTERFACE_PAUSE_DISABLE:
|
|
|
+ param.can_suspend_link = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (wow_params->resume_trigger) {
|
|
|
+ default:
|
|
|
+ pmo_err("Invalid resume trigger setting: %d",
|
|
|
+ wow_params->resume_trigger);
|
|
|
+ /* intentional fall-through to default */
|
|
|
+ case PMO_WOW_RESUME_TRIGGER_DEFAULT:
|
|
|
+ case PMO_WOW_RESUME_TRIGGER_GPIO:
|
|
|
+ /*
|
|
|
+ * GPIO is currently implicit. This means you can't actually
|
|
|
+ * force GPIO if a platform's default wake trigger is HTC wakeup
|
|
|
+ */
|
|
|
+ break;
|
|
|
+ case PMO_WOW_RESUME_TRIGGER_HTC_WAKEUP:
|
|
|
+ param.flags |= WMI_WOW_FLAG_DO_HTC_WAKEUP;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = pmo_tgt_psoc_send_wow_enable_req(psoc, ¶m);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("Failed to enable wow in fw");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ pmo_tgt_update_target_suspend_flag(psoc, true);
|
|
|
+
|
|
|
+ if (qdf_wait_single_event(&psoc_ctx->wow.target_suspend,
|
|
|
+ PMO_TGT_SUSPEND_COMPLETE_TIMEOUT)
|
|
|
+ != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("Failed to receive WoW Enable Ack from FW");
|
|
|
+ pmo_err("Credits:%d; Pending_Cmds: %d",
|
|
|
+ pmo_tgt_psoc_get_host_credits(psoc),
|
|
|
+ pmo_tgt_psoc_get_pending_cmnds(psoc));
|
|
|
+ pmo_tgt_update_target_suspend_flag(psoc, false);
|
|
|
+ status = QDF_STATUS_E_FAILURE;
|
|
|
+ QDF_BUG(0);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pmo_core_get_wow_nack(psoc_ctx)) {
|
|
|
+ pmo_err("FW not ready to WOW");
|
|
|
+ pmo_tgt_update_target_suspend_flag(psoc, false);
|
|
|
+ status = QDF_STATUS_E_AGAIN;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ host_credits = pmo_tgt_psoc_get_host_credits(psoc);
|
|
|
+ wmi_pending_cmds = pmo_tgt_psoc_get_pending_cmnds(psoc);
|
|
|
+
|
|
|
+ if (host_credits < PMO_WOW_REQUIRED_CREDITS) {
|
|
|
+ pmo_err("No Credits after HTC ACK:%d, pending_cmds:%d,"
|
|
|
+ "cannot resume back", host_credits, wmi_pending_cmds);
|
|
|
+ htc_dump_counter_info(pmo_core_psoc_get_htc_handle(psoc));
|
|
|
+/*
|
|
|
+ if (!cds_is_driver_recovering())
|
|
|
+ QDF_BUG(0);
|
|
|
+ else
|
|
|
+ pmo_err("SSR in progress, ignore no credit issue");
|
|
|
+*/
|
|
|
+ }
|
|
|
+ pmo_debug("WOW enabled successfully in fw: credits:%d pending_cmds: %d",
|
|
|
+ host_credits, wmi_pending_cmds);
|
|
|
+
|
|
|
+ pmo_core_update_wow_enable_cmd_sent(psoc_ctx, true);
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+QDF_STATUS pmo_core_psoc_suspend_target(struct wlan_objmgr_psoc *psoc,
|
|
|
+ int disable_target_intr)
|
|
|
+{
|
|
|
+ QDF_STATUS status;
|
|
|
+ struct pmo_suspend_params param;
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+
|
|
|
+ psoc_ctx = pmo_psoc_get_priv(psoc);
|
|
|
+
|
|
|
+ qdf_event_reset(&psoc_ctx->wow.target_suspend);
|
|
|
+ param.disable_target_intr = disable_target_intr;
|
|
|
+ status = pmo_tgt_psoc_send_supend_req(psoc, ¶m);
|
|
|
+ if (status != QDF_STATUS_SUCCESS)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ pmo_tgt_update_target_suspend_flag(psoc, true);
|
|
|
+
|
|
|
+ if (qdf_wait_single_event(&psoc_ctx->wow.target_suspend,
|
|
|
+ PMO_TGT_SUSPEND_COMPLETE_TIMEOUT)
|
|
|
+ != QDF_STATUS_SUCCESS) {
|
|
|
+ status = QDF_STATUS_E_TIMEOUT;
|
|
|
+ pmo_err("Failed to get ACK from firmware for pdev suspend");
|
|
|
+ pmo_tgt_update_target_suspend_flag(psoc, false);
|
|
|
+ /* wma_suspend_target_timeout(pmac->sme.enableSelfRecovery); */
|
|
|
+ }
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+QDF_STATUS pmo_core_psoc_bus_suspend_req(struct wlan_objmgr_psoc *psoc,
|
|
|
+ enum qdf_suspend_type type,
|
|
|
+ struct pmo_wow_enable_params *wow_params)
|
|
|
+{
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx;
|
|
|
+ QDF_STATUS status;
|
|
|
+ bool wow_mode_selected = false;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+ if (!psoc) {
|
|
|
+ pmo_err("psoc is NULL");
|
|
|
+ status = QDF_STATUS_E_NULL_VALUE;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!wow_params) {
|
|
|
+ pmo_err("wow_params is NULL");
|
|
|
+ status = QDF_STATUS_E_NULL_VALUE;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = pmo_psoc_get_ref(psoc);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("pmo cannot get the reference out of psoc");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ psoc_ctx = pmo_psoc_get_priv(psoc);
|
|
|
+
|
|
|
+/* TODO - scan manager need to provide the below public api
|
|
|
+ if (wma_check_scan_in_progress(handle)) {
|
|
|
+ pmo_err("Scan in progress. Aborting suspend");
|
|
|
+ status = QDF_STATUS_E_NULL_VALUE;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+*/
|
|
|
+
|
|
|
+ wow_mode_selected = pmo_core_is_wow_enabled(psoc_ctx);
|
|
|
+ pmo_info("wow mode selected %d", wow_mode_selected);
|
|
|
+
|
|
|
+ if (wow_mode_selected)
|
|
|
+ status = pmo_core_enable_wow_in_fw(psoc, psoc_ctx, wow_params);
|
|
|
+ else
|
|
|
+ status = pmo_core_psoc_suspend_target(psoc, 0);
|
|
|
+
|
|
|
+ pmo_psoc_put_ref(psoc);
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef FEATURE_RUNTIME_PM
|
|
|
+QDF_STATUS pmo_core_psoc_bus_runtime_suspend(struct wlan_objmgr_psoc *psoc,
|
|
|
+ pmo_pld_auto_suspend_cb pld_cb)
|
|
|
+{
|
|
|
+ void *hif_ctx;
|
|
|
+ void *dp_soc;
|
|
|
+ void *txrx_pdev;
|
|
|
+ void *htc_ctx;
|
|
|
+ QDF_STATUS status;
|
|
|
+ struct pmo_wow_enable_params wow_params = {0};
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+
|
|
|
+ if (!psoc) {
|
|
|
+ pmo_err("psoc is NULL");
|
|
|
+ status = QDF_STATUS_E_INVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = pmo_psoc_get_ref(psoc);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("pmo cannot get the reference out of psoc");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ hif_ctx = pmo_core_psoc_get_hif_handle(psoc);
|
|
|
+ dp_soc = pmo_core_psoc_get_dp_handle(psoc);
|
|
|
+ txrx_pdev = pmo_core_psoc_get_txrx_handle(psoc);
|
|
|
+ htc_ctx = pmo_core_psoc_get_htc_handle(psoc);
|
|
|
+ if (!hif_ctx || !dp_soc || !txrx_pdev || !htc_ctx) {
|
|
|
+ pmo_err("Invalid hif: %p, dp: %p, txrx: %p, htc: %p",
|
|
|
+ hif_ctx, dp_soc, txrx_pdev, htc_ctx);
|
|
|
+ status = QDF_STATUS_E_INVAL;
|
|
|
+ goto dec_psoc_ref;
|
|
|
+ }
|
|
|
+
|
|
|
+ wow_params.interface_pause = PMO_WOW_INTERFACE_PAUSE_ENABLE;
|
|
|
+ wow_params.resume_trigger = PMO_WOW_RESUME_TRIGGER_GPIO;
|
|
|
+
|
|
|
+ if (hif_pre_runtime_suspend(hif_ctx))
|
|
|
+ goto runtime_failure;
|
|
|
+
|
|
|
+ status = cdp_runtime_suspend(dp_soc, txrx_pdev);
|
|
|
+ if (status != QDF_STATUS_SUCCESS)
|
|
|
+ goto runtime_failure;
|
|
|
+
|
|
|
+ if (htc_runtime_suspend(htc_ctx))
|
|
|
+ goto cdp_runtime_resume;
|
|
|
+
|
|
|
+ status = pmo_tgt_psoc_set_runtime_pm_inprogress(psoc, true);
|
|
|
+ if (status != QDF_STATUS_SUCCESS)
|
|
|
+ goto resume_htc;
|
|
|
+
|
|
|
+ status = pmo_core_psoc_configure_suspend(psoc);
|
|
|
+ if (status != QDF_STATUS_SUCCESS)
|
|
|
+ goto resume_htc;
|
|
|
+
|
|
|
+ status = pmo_core_psoc_bus_suspend_req(psoc, QDF_RUNTIME_SUSPEND,
|
|
|
+ &wow_params);
|
|
|
+ if (status != QDF_STATUS_SUCCESS)
|
|
|
+ goto pmo_resume_configure;
|
|
|
+
|
|
|
+ if (hif_runtime_suspend(hif_ctx))
|
|
|
+ goto pmo_bus_resume;
|
|
|
+
|
|
|
+ if (pld_cb && pld_cb())
|
|
|
+ goto resume_hif;
|
|
|
+
|
|
|
+ hif_process_runtime_suspend_success(hif_ctx);
|
|
|
+
|
|
|
+ goto dec_psoc_ref;
|
|
|
+
|
|
|
+resume_hif:
|
|
|
+ QDF_BUG(!hif_runtime_resume(hif_ctx));
|
|
|
+
|
|
|
+pmo_bus_resume:
|
|
|
+ QDF_BUG(QDF_STATUS_SUCCESS ==
|
|
|
+ pmo_core_psoc_bus_resume_req(psoc, QDF_RUNTIME_SUSPEND));
|
|
|
+
|
|
|
+pmo_resume_configure:
|
|
|
+ QDF_BUG(QDF_STATUS_SUCCESS ==
|
|
|
+ pmo_core_psoc_configure_resume(psoc));
|
|
|
+
|
|
|
+resume_htc:
|
|
|
+ QDF_BUG(QDF_STATUS_SUCCESS ==
|
|
|
+ pmo_tgt_psoc_set_runtime_pm_inprogress(psoc, false));
|
|
|
+
|
|
|
+ QDF_BUG(!htc_runtime_resume(htc_ctx));
|
|
|
+
|
|
|
+cdp_runtime_resume:
|
|
|
+ QDF_BUG(QDF_STATUS_SUCCESS ==
|
|
|
+ cdp_runtime_resume(dp_soc, txrx_pdev));
|
|
|
+
|
|
|
+runtime_failure:
|
|
|
+ hif_process_runtime_suspend_failure(hif_ctx);
|
|
|
+
|
|
|
+dec_psoc_ref:
|
|
|
+ pmo_psoc_put_ref(psoc);
|
|
|
+
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+QDF_STATUS pmo_core_psoc_bus_runtime_resume(struct wlan_objmgr_psoc *psoc,
|
|
|
+ pmo_pld_auto_resume_cb pld_cb)
|
|
|
+{
|
|
|
+ void *hif_ctx;
|
|
|
+ void *dp_soc;
|
|
|
+ void *txrx_pdev;
|
|
|
+ void *htc_ctx;
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+
|
|
|
+ if (!psoc) {
|
|
|
+ pmo_err("psoc is NULL");
|
|
|
+ status = QDF_STATUS_E_INVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = pmo_psoc_get_ref(psoc);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("pmo cannot get the reference out of psoc");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ hif_ctx = pmo_core_psoc_get_hif_handle(psoc);
|
|
|
+ dp_soc = pmo_core_psoc_get_dp_handle(psoc);
|
|
|
+ txrx_pdev = pmo_core_psoc_get_txrx_handle(psoc);
|
|
|
+ htc_ctx = pmo_core_psoc_get_htc_handle(psoc);
|
|
|
+ if (!hif_ctx || !dp_soc || !txrx_pdev || !htc_ctx) {
|
|
|
+ pmo_err("Invalid hif: %p, dp: %p, txrx: %p, htc: %p",
|
|
|
+ hif_ctx, dp_soc, txrx_pdev, htc_ctx);
|
|
|
+ status = QDF_STATUS_E_INVAL;
|
|
|
+ goto dec_psoc_ref;
|
|
|
+ }
|
|
|
+
|
|
|
+ hif_pre_runtime_resume(hif_ctx);
|
|
|
+
|
|
|
+ if (pld_cb)
|
|
|
+ QDF_BUG(!pld_cb());
|
|
|
+
|
|
|
+ QDF_BUG(!hif_runtime_resume(hif_ctx));
|
|
|
+
|
|
|
+ status = pmo_core_psoc_bus_resume_req(psoc, QDF_RUNTIME_SUSPEND);
|
|
|
+ QDF_BUG(status == QDF_STATUS_SUCCESS);
|
|
|
+
|
|
|
+ status = pmo_core_psoc_configure_resume(psoc);
|
|
|
+ QDF_BUG(status == QDF_STATUS_SUCCESS);
|
|
|
+
|
|
|
+ status = pmo_tgt_psoc_set_runtime_pm_inprogress(psoc, false);
|
|
|
+ QDF_BUG(status == QDF_STATUS_SUCCESS);
|
|
|
+
|
|
|
+ QDF_BUG(!htc_runtime_resume(htc_ctx));
|
|
|
+
|
|
|
+ status = cdp_runtime_resume(dp_soc, txrx_pdev);
|
|
|
+ QDF_BUG(status == QDF_STATUS_SUCCESS);
|
|
|
+
|
|
|
+ hif_process_runtime_resume_success(hif_ctx);
|
|
|
+
|
|
|
+dec_psoc_ref:
|
|
|
+ pmo_psoc_put_ref(psoc);
|
|
|
+
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+/**
|
|
|
+ * pmo_core_psoc_send_host_wakeup_ind_to_fw() - send wakeup ind to fw
|
|
|
+ * @psoc: objmgr psoc handle
|
|
|
+ * @psoc_ctx: pmo psoc private context
|
|
|
+ *
|
|
|
+ * Sends host wakeup indication to FW. On receiving this indication,
|
|
|
+ * FW will come out of WOW.
|
|
|
+ *
|
|
|
+ * Return: QDF status
|
|
|
+ */
|
|
|
+static
|
|
|
+QDF_STATUS pmo_core_psoc_send_host_wakeup_ind_to_fw(
|
|
|
+ struct wlan_objmgr_psoc *psoc,
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx)
|
|
|
+{
|
|
|
+ QDF_STATUS status = QDF_STATUS_SUCCESS;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+ qdf_event_reset(&psoc_ctx->wow.target_resume);
|
|
|
+
|
|
|
+ status = pmo_tgt_psoc_send_host_wakeup_ind(psoc);
|
|
|
+ if (status) {
|
|
|
+ status = QDF_STATUS_E_FAILURE;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ pmo_debug("Host wakeup indication sent to fw");
|
|
|
+
|
|
|
+ status = qdf_wait_single_event(&psoc_ctx->wow.target_resume,
|
|
|
+ PMO_RESUME_TIMEOUT);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("Timeout waiting for resume event from FW");
|
|
|
+ pmo_err("Pending commands %d credits %d",
|
|
|
+ pmo_tgt_psoc_get_pending_cmnds(psoc),
|
|
|
+ pmo_tgt_psoc_get_host_credits(psoc));
|
|
|
+ QDF_BUG(0);
|
|
|
+ } else {
|
|
|
+ pmo_debug("Host wakeup received");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (status == QDF_STATUS_SUCCESS)
|
|
|
+ pmo_tgt_update_target_suspend_flag(psoc, false);
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pmo_core_psoc_disable_wow_in_fw() - Disable wow in bus resume context.
|
|
|
+ * @psoc: objmgr psoc handle
|
|
|
+ * @psoc_ctx: pmo psoc private context
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS_SUCCESS for success or error code
|
|
|
+ */
|
|
|
+static
|
|
|
+QDF_STATUS pmo_core_psoc_disable_wow_in_fw(struct wlan_objmgr_psoc *psoc,
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx)
|
|
|
+{
|
|
|
+ QDF_STATUS ret;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+ ret = pmo_core_psoc_send_host_wakeup_ind_to_fw(psoc, psoc_ctx);
|
|
|
+ if (ret != QDF_STATUS_SUCCESS)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ pmo_core_update_wow_enable(psoc_ctx, false);
|
|
|
+ pmo_core_update_wow_enable_cmd_sent(psoc_ctx, false);
|
|
|
+
|
|
|
+ /* To allow the tx pause/unpause events */
|
|
|
+ pmo_core_update_wow_bus_suspend(psoc, psoc_ctx, false);
|
|
|
+ /* Unpause the vdev as we are resuming */
|
|
|
+ pmo_unpause_all_vdev(psoc, psoc_ctx);
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pmo_core_psoc_resume_target() - resume target
|
|
|
+ * @psoc: objmgr psoc handle
|
|
|
+ * @psoc_ctx: pmo psoc private context
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS_SUCCESS for success or error code
|
|
|
+ */
|
|
|
+static
|
|
|
+QDF_STATUS pmo_core_psoc_resume_target(struct wlan_objmgr_psoc *psoc,
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx)
|
|
|
+{
|
|
|
+ QDF_STATUS status = QDF_STATUS_SUCCESS;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+ qdf_event_reset(&psoc_ctx->wow.target_resume);
|
|
|
+
|
|
|
+ status = pmo_tgt_psoc_send_target_resume_req(psoc);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ status = QDF_STATUS_E_FAILURE;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = qdf_wait_single_event(&psoc_ctx->wow.target_resume,
|
|
|
+ PMO_RESUME_TIMEOUT);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_fatal("Timeout waiting for resume event from FW");
|
|
|
+ pmo_fatal("Pending commands %d credits %d",
|
|
|
+ pmo_tgt_psoc_get_pending_cmnds(psoc),
|
|
|
+ pmo_tgt_psoc_get_host_credits(psoc));
|
|
|
+ QDF_BUG(0);
|
|
|
+ } else {
|
|
|
+ pmo_debug("Host wakeup received");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (status == QDF_STATUS_SUCCESS)
|
|
|
+ pmo_tgt_update_target_suspend_flag(psoc, false);
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+QDF_STATUS pmo_core_psoc_bus_resume_req(struct wlan_objmgr_psoc *psoc,
|
|
|
+ enum qdf_suspend_type type)
|
|
|
+{
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx;
|
|
|
+ bool wow_mode;
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+ if (!psoc) {
|
|
|
+ pmo_err("psoc is null");
|
|
|
+ status = QDF_STATUS_E_NULL_VALUE;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = pmo_psoc_get_ref(psoc);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("pmo cannot get the reference out of psoc");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ psoc_ctx = pmo_psoc_get_priv(psoc);
|
|
|
+ wow_mode = pmo_core_is_wow_enabled(psoc_ctx);
|
|
|
+ pmo_info("wow mode %d", wow_mode);
|
|
|
+
|
|
|
+ pmo_core_update_wow_initial_wake_up(psoc_ctx, false);
|
|
|
+
|
|
|
+ if (wow_mode)
|
|
|
+ status = pmo_core_psoc_disable_wow_in_fw(psoc, psoc_ctx);
|
|
|
+ else
|
|
|
+ status = pmo_core_psoc_resume_target(psoc, psoc_ctx);
|
|
|
+
|
|
|
+ pmo_psoc_put_ref(psoc);
|
|
|
+
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+void pmo_core_psoc_target_suspend_acknowledge(void *context, bool wow_nack)
|
|
|
+{
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx;
|
|
|
+ struct wlan_objmgr_psoc *psoc = (struct wlan_objmgr_psoc *)context;
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+ if (!psoc) {
|
|
|
+ pmo_err("psoc is null");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = pmo_psoc_get_ref(psoc);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("Failed to get psoc reference");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ psoc_ctx = pmo_psoc_get_priv(psoc);
|
|
|
+
|
|
|
+ pmo_core_set_wow_nack(psoc_ctx, wow_nack);
|
|
|
+ qdf_event_set(&psoc_ctx->wow.target_suspend);
|
|
|
+ if (wow_nack && !pmo_tgt_psoc_get_runtime_pm_in_progress(psoc)) {
|
|
|
+ qdf_wake_lock_timeout_acquire(&psoc_ctx->wow.wow_wake_lock,
|
|
|
+ PMO_WAKE_LOCK_TIMEOUT);
|
|
|
+ }
|
|
|
+
|
|
|
+ pmo_psoc_put_ref(psoc);
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+}
|
|
|
+
|
|
|
+void pmo_core_psoc_wakeup_host_event_received(struct wlan_objmgr_psoc *psoc)
|
|
|
+{
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+ if (!psoc) {
|
|
|
+ pmo_err("psoc is null");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ psoc_ctx = pmo_psoc_get_priv(psoc);
|
|
|
+ qdf_event_set(&psoc_ctx->wow.target_resume);
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+}
|
|
|
+
|
|
|
+int pmo_core_psoc_is_target_wake_up_received(struct wlan_objmgr_psoc *psoc)
|
|
|
+{
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx;
|
|
|
+ int ret = 0;
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ if (!psoc) {
|
|
|
+ pmo_err("psoc is NULL");
|
|
|
+ ret = -EAGAIN;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = pmo_psoc_get_ref(psoc);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("Failed to get psoc reference");
|
|
|
+ ret = -EAGAIN;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ psoc_ctx = pmo_psoc_get_priv(psoc);
|
|
|
+ if (pmo_core_get_wow_initial_wake_up(psoc_ctx)) {
|
|
|
+ pmo_err("Target initial wake up received try again");
|
|
|
+ ret = -EAGAIN;
|
|
|
+ }
|
|
|
+
|
|
|
+ pmo_psoc_put_ref(psoc);
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int pmo_core_psoc_clear_target_wake_up(struct wlan_objmgr_psoc *psoc)
|
|
|
+{
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx;
|
|
|
+ int ret = 0;
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ if (!psoc) {
|
|
|
+ pmo_err("psoc is NULL");
|
|
|
+ ret = -EAGAIN;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = pmo_psoc_get_ref(psoc);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("Failed to get psoc reference");
|
|
|
+ ret = -EAGAIN;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ psoc_ctx = pmo_psoc_get_priv(psoc);
|
|
|
+ pmo_core_update_wow_initial_wake_up(psoc_ctx, false);
|
|
|
+
|
|
|
+ pmo_psoc_put_ref(psoc);
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+void pmo_core_psoc_handle_initial_wake_up(void *cb_ctx)
|
|
|
+{
|
|
|
+ struct pmo_psoc_priv_obj *psoc_ctx;
|
|
|
+ struct wlan_objmgr_psoc *psoc = (struct wlan_objmgr_psoc *)cb_ctx;
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ PMO_ENTER();
|
|
|
+ if (!psoc) {
|
|
|
+ pmo_err("cb ctx/psoc is null");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = pmo_psoc_get_ref(psoc);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ pmo_err("Failed to get psoc reference");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ psoc_ctx = pmo_psoc_get_priv(psoc);
|
|
|
+ pmo_core_update_wow_initial_wake_up(psoc_ctx, true);
|
|
|
+
|
|
|
+ pmo_psoc_put_ref(psoc);
|
|
|
+
|
|
|
+out:
|
|
|
+ PMO_EXIT();
|
|
|
+}
|
|
|
+
|