diff --git a/Kbuild b/Kbuild index 7cc42a030ab4..3760d7539366 100644 --- a/Kbuild +++ b/Kbuild @@ -1081,7 +1081,8 @@ IPA_OBJS := $(IPA_DIR)/dispatcher/src/wlan_ipa_ucfg_api.o \ $(IPA_DIR)/dispatcher/src/wlan_ipa_obj_mgmt_api.o \ $(IPA_DIR)/dispatcher/src/wlan_ipa_tgt_api.o \ $(IPA_DIR)/core/src/wlan_ipa_main.o \ - $(IPA_DIR)/core/src/wlan_ipa_core.o + $(IPA_DIR)/core/src/wlan_ipa_core.o \ + $(IPA_DIR)/core/src/wlan_ipa_rm.o endif ########## CLD TARGET_IF ####### diff --git a/components/ipa/core/inc/wlan_ipa_core.h b/components/ipa/core/inc/wlan_ipa_core.h index d0d281ad92f3..5e0701b842e9 100644 --- a/components/ipa/core/inc/wlan_ipa_core.h +++ b/components/ipa/core/inc/wlan_ipa_core.h @@ -55,6 +55,17 @@ static inline bool wlan_ipa_uc_is_enabled(struct wlan_ipa_config *ipa_cfg) return WLAN_IPA_IS_CONFIG_ENABLED(ipa_cfg, WLAN_IPA_UC_ENABLE_MASK); } +/** + * wlan_ipa_is_rm_enabled() - Is IPA RM enabled? + * @ipa_cfg: IPA config + * + * Return: true if IPA RM is enabled, false otherwise + */ +static inline bool wlan_ipa_is_rm_enabled(struct wlan_ipa_config *ipa_cfg) +{ + return WLAN_IPA_IS_CONFIG_ENABLED(ipa_cfg, WLAN_IPA_RM_ENABLE_MASK); +} + /** * wlan_ipa_setup - IPA initialize and setup * @ipa_ctx: IPA priv obj @@ -65,6 +76,13 @@ static inline bool wlan_ipa_uc_is_enabled(struct wlan_ipa_config *ipa_cfg) QDF_STATUS wlan_ipa_setup(struct wlan_ipa_priv *ipa_ctx, struct wlan_ipa_config *ipa_cfg); +/** + * wlan_ipa_get_obj_context - Get IPA OBJ context + * + * Return: IPA context + */ +struct wlan_ipa_priv *wlan_ipa_get_obj_context(void); + /** * wlan_ipa_cleanup - IPA cleanup * @ipa_ctx: IPA priv obj @@ -72,5 +90,124 @@ QDF_STATUS wlan_ipa_setup(struct wlan_ipa_priv *ipa_ctx, * Return: QDF_STATUS */ QDF_STATUS wlan_ipa_cleanup(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_uc_enable_pipes() - Enable IPA uC pipes + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_uc_enable_pipes(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_uc_disable_pipes() - Disable IPA uC pipes + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_uc_disable_pipes(struct wlan_ipa_priv *ipa_ctx); + +#ifndef CONFIG_IPA_WDI_UNIFIED_API +/** + * wlan_ipa_wdi_rm_request_resource() - IPA WDI request resource + * @ipa_ctx: IPA context + * @res_name: IPA RM resource name + * + * Return: 0 on success, negative errno on error + */ +static inline +int wlan_ipa_wdi_rm_request_resource(struct wlan_ipa_priv *ipa_ctx, + qdf_ipa_rm_resource_name_t res_name) +{ + return qdf_ipa_rm_request_resource(res_name); +} + +/** + * wlan_ipa_wdi_rm_release_resource() - IPA WDI release resource + * @ipa_ctx: IPA context + * @res_name: IPA RM resource name + * + * Return: 0 on success, negative errno on error + */ +static inline +int wlan_ipa_wdi_rm_release_resource(struct wlan_ipa_priv *ipa_ctx, + qdf_ipa_rm_resource_name_t res_name) +{ + return qdf_ipa_rm_release_resource(res_name); +} + +/** + * wlan_ipa_wdi_rm_request() - Request resource from IPA + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_wdi_rm_request(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_wdi_rm_try_release() - Attempt to release IPA resource + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_wdi_rm_try_release(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_wdi_setup_rm() - Setup IPA resource management + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_wdi_setup_rm(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_wdi_destroy_rm() - Destroy IPA resources + * @ipa_ctx: IPA context + * + * Destroys all resources associated with the IPA resource manager + * + * Return: None + */ +void wlan_ipa_wdi_destroy_rm(struct wlan_ipa_priv *ipa_ctx); + +#else /* CONFIG_IPA_WDI_UNIFIED_API */ + +static inline int wlan_ipa_wdi_rm_request_resource( + struct wlan_ipa_priv *ipa_ctx, + qdf_ipa_rm_resource_name_t res_name) +{ + return 0; +} + +static inline int wlan_ipa_wdi_rm_release_resource( + struct wlan_ipa_priv *ipa_ctx, + qdf_ipa_rm_resource_name_t res_name) +{ + return 0; +} + +static inline QDF_STATUS wlan_ipa_wdi_setup_rm(struct wlan_ipa_priv *ipa_ctx) +{ + return 0; +} + +static inline int wlan_ipa_wdi_destroy_rm(struct wlan_ipa_priv *ipa_ctx) +{ + return 0; +} + +static inline QDF_STATUS wlan_ipa_wdi_rm_request(struct wlan_ipa_priv *ipa_ctx) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS wlan_ipa_wdi_rm_try_release(struct wlan_ipa_priv + *ipa_ctx) +{ + return QDF_STATUS_SUCCESS; +} + +#endif /* CONFIG_IPA_WDI_UNIFIED_API */ + #endif /* IPA_OFFLOAD */ #endif /* _WLAN_IPA_CORE_H_ */ diff --git a/components/ipa/core/src/wlan_ipa_core.c b/components/ipa/core/src/wlan_ipa_core.c index a66b0825e506..340eecdf742e 100644 --- a/components/ipa/core/src/wlan_ipa_core.c +++ b/components/ipa/core/src/wlan_ipa_core.c @@ -310,6 +310,74 @@ static inline int wlan_ipa_wdi_teardown_sys_pipe( #endif /* CONFIG_IPA_WDI_UNIFIED_API */ +QDF_STATUS wlan_ipa_uc_enable_pipes(struct wlan_ipa_priv *ipa_ctx) +{ + int result; + + ipa_debug("enter"); + + if (!ipa_ctx->ipa_pipes_down) { + /* + * IPA WDI Pipes are already activated due to + * rm deferred resources grant + */ + ipa_warn("IPA WDI Pipes are already activated"); + goto end; + } + + result = cdp_ipa_enable_pipes(ipa_ctx->dp_soc, + ipa_ctx->dp_pdev); + if (result) { + ipa_err("Enable IPA WDI PIPE failed: ret=%d", result); + return QDF_STATUS_E_FAILURE; + } + + qdf_event_reset(&ipa_ctx->ipa_resource_comp); + ipa_ctx->ipa_pipes_down = false; + + cdp_ipa_enable_autonomy(ipa_ctx->dp_soc, + ipa_ctx->dp_pdev); + +end: + ipa_debug("exit: ipa_pipes_down=%d", ipa_ctx->ipa_pipes_down); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_ipa_uc_disable_pipes(struct wlan_ipa_priv *ipa_ctx) +{ + int result; + + ipa_debug("enter"); + + if (ipa_ctx->ipa_pipes_down) { + /* + * This shouldn't happen : + * IPA WDI Pipes are already deactivated + */ + QDF_ASSERT(0); + ipa_warn("IPA WDI Pipes are already deactivated"); + goto end; + } + + cdp_ipa_disable_autonomy(ipa_ctx->dp_soc, + ipa_ctx->dp_pdev); + + result = cdp_ipa_disable_pipes(ipa_ctx->dp_soc, + ipa_ctx->dp_pdev); + if (result) { + ipa_err("Disable IPA WDI PIPE failed: ret=%d", result); + return QDF_STATUS_E_FAILURE; + } + + ipa_ctx->ipa_pipes_down = true; + +end: + ipa_debug("exit: ipa_pipes_down=%d", ipa_ctx->ipa_pipes_down); + + return QDF_STATUS_SUCCESS; +} + /** * wlan_ipa_alloc_tx_desc_list() - Allocate IPA Tx desc list * @ipa_ctx: IPA context @@ -593,6 +661,10 @@ QDF_STATUS wlan_ipa_setup(struct wlan_ipa_priv *ipa_ctx, qdf_mutex_create(&ipa_ctx->event_lock); qdf_mutex_create(&ipa_ctx->ipa_lock); + status = wlan_ipa_wdi_setup_rm(ipa_ctx); + if (status != QDF_STATUS_SUCCESS) + goto fail_setup_rm; + for (i = 0; i < WLAN_IPA_MAX_SYSBAM_PIPE; i++) qdf_mem_zero(&ipa_ctx->sys_pipe[i], sizeof(struct wlan_ipa_sys_pipe)); @@ -636,7 +708,18 @@ QDF_STATUS wlan_ipa_setup(struct wlan_ipa_priv *ipa_ctx, return QDF_STATUS_SUCCESS; fail_create_sys_pipe: + wlan_ipa_wdi_destroy_rm(ipa_ctx); + +fail_setup_rm: qdf_spinlock_destroy(&ipa_ctx->pm_lock); + qdf_spinlock_destroy(&ipa_ctx->q_lock); + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_context = &ipa_ctx->iface_context[i]; + qdf_spinlock_destroy(&iface_context->interface_lock); + } + qdf_mutex_destroy(&ipa_ctx->event_lock); + qdf_mutex_destroy(&ipa_ctx->ipa_lock); + qdf_list_destroy(&ipa_ctx->pending_event); gp_ipa = NULL; ipa_debug("exit: fail"); @@ -655,6 +738,8 @@ QDF_STATUS wlan_ipa_cleanup(struct wlan_ipa_priv *ipa_ctx) if (wlan_ipa_uc_sta_is_enabled(ipa_ctx->config)) wlan_ipa_teardown_sys_pipe(ipa_ctx); + wlan_ipa_wdi_destroy_rm(ipa_ctx); + qdf_spinlock_destroy(&ipa_ctx->pm_lock); qdf_spinlock_destroy(&ipa_ctx->q_lock); diff --git a/components/ipa/core/src/wlan_ipa_rm.c b/components/ipa/core/src/wlan_ipa_rm.c new file mode 100644 index 000000000000..bbd1d1dc441a --- /dev/null +++ b/components/ipa/core/src/wlan_ipa_rm.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. + * + * Previously licensed under the ISC license by Qualcomm Atheros, Inc. + * + * + * 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. + */ + +/* + * This file was originally distributed by Qualcomm Atheros, Inc. + * under proprietary terms before Copyright ownership was assigned + * to the Linux Foundation. + */ + +/* Include Files */ +#include "wlan_ipa_core.h" +#include "wlan_ipa_main.h" +#include "host_diag_core_event.h" + +/** + * wlan_ipa_rm_notify() - IPA resource manager notifier callback + * @user_data: user data registered with IPA + * @event: the IPA resource manager event that occurred + * @data: the data associated with the event + * + * Return: None + */ +static void wlan_ipa_rm_notify(void *user_data, qdf_ipa_rm_event_t event, + unsigned long data) +{ + struct wlan_ipa_priv *ipa_ctx = user_data; + + if (qdf_unlikely(!ipa_ctx)) + return; + + if (!wlan_ipa_is_rm_enabled(ipa_ctx->config)) + return; + + ipa_debug("Evt: %d", event); + + switch (event) { + case QDF_IPA_RM_RESOURCE_GRANTED: + if (wlan_ipa_uc_is_enabled(ipa_ctx->config)) { + /* RM Notification comes with ISR context + * it should be serialized into work queue to avoid + * ISR sleep problem + */ + ipa_ctx->uc_rm_work.event = event; + qdf_sched_work(0, &ipa_ctx->uc_rm_work.work); + break; + } + qdf_spin_lock_bh(&ipa_ctx->rm_lock); + ipa_ctx->rm_state = WLAN_IPA_RM_GRANTED; + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + ipa_ctx->stats.num_rm_grant++; + break; + + case QDF_IPA_RM_RESOURCE_RELEASED: + ipa_debug("RM Release"); + ipa_ctx->resource_unloading = false; + break; + + default: + ipa_err("Unknown RM Evt: %d", event); + break; + } +} + +/** + * wlan_ipa_rm_cons_release() - WLAN consumer resource release handler + * + * Callback function registered with IPA that is called when IPA wants + * to release the WLAN consumer resource + * + * Return: 0 if the request is granted, negative errno otherwise + */ +static int wlan_ipa_rm_cons_release(void) +{ + return 0; +} + +#ifndef CONFIG_IPA_WDI_UNIFIED_API +/** + * wlan_ipa_wdi_rm_request() - Request resource from IPA + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_wdi_rm_request(struct wlan_ipa_priv *ipa_ctx) +{ + int ret; + + if (!wlan_ipa_is_rm_enabled(ipa_ctx->config)) + return QDF_STATUS_SUCCESS; + + qdf_spin_lock_bh(&ipa_ctx->rm_lock); + + switch (ipa_ctx->rm_state) { + case WLAN_IPA_RM_GRANTED: + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + return QDF_STATUS_SUCCESS; + case WLAN_IPA_RM_GRANT_PENDING: + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + return QDF_STATUS_E_PENDING; + case WLAN_IPA_RM_RELEASED: + ipa_ctx->rm_state = WLAN_IPA_RM_GRANT_PENDING; + break; + } + + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + + ret = qdf_ipa_rm_inactivity_timer_request_resource( + QDF_IPA_RM_RESOURCE_WLAN_PROD); + + qdf_spin_lock_bh(&ipa_ctx->rm_lock); + if (ret == 0) { + ipa_ctx->rm_state = WLAN_IPA_RM_GRANTED; + ipa_ctx->stats.num_rm_grant_imm++; + } + + if (ipa_ctx->wake_lock_released) { + qdf_wake_lock_acquire(&ipa_ctx->wake_lock, + WIFI_POWER_EVENT_WAKELOCK_IPA); + ipa_ctx->wake_lock_released = false; + } + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + + qdf_cancel_delayed_work(&ipa_ctx->wake_lock_work); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_ipa_wdi_rm_try_release(struct wlan_ipa_priv *ipa_ctx) +{ + int ret; + + if (!wlan_ipa_is_rm_enabled(ipa_ctx->config)) + return QDF_STATUS_SUCCESS; + + if (qdf_atomic_read(&ipa_ctx->tx_ref_cnt)) + return QDF_STATUS_E_AGAIN; + + qdf_spin_lock_bh(&ipa_ctx->pm_lock); + + if (!qdf_nbuf_is_queue_empty(&ipa_ctx->pm_queue_head)) { + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + return QDF_STATUS_E_AGAIN; + } + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + + qdf_spin_lock_bh(&ipa_ctx->rm_lock); + switch (ipa_ctx->rm_state) { + case WLAN_IPA_RM_GRANTED: + break; + case WLAN_IPA_RM_GRANT_PENDING: + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + return QDF_STATUS_E_PENDING; + case WLAN_IPA_RM_RELEASED: + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + return QDF_STATUS_SUCCESS; + } + + /* IPA driver returns immediately so set the state here to avoid any + * race condition. + */ + ipa_ctx->rm_state = WLAN_IPA_RM_RELEASED; + ipa_ctx->stats.num_rm_release++; + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + + ret = qdf_ipa_rm_inactivity_timer_release_resource( + QDF_IPA_RM_RESOURCE_WLAN_PROD); + + if (qdf_unlikely(ret != 0)) { + qdf_spin_lock_bh(&ipa_ctx->rm_lock); + ipa_ctx->rm_state = WLAN_IPA_RM_GRANTED; + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + QDF_ASSERT(0); + ipa_warn("rm_inactivity_timer_release_resource ret fail"); + } + + /* + * If wake_lock is released immediately, kernel would try to suspend + * immediately as well, Just avoid ping-pong between suspend-resume + * while there is healthy amount of data transfer going on by + * releasing the wake_lock after some delay. + */ + qdf_sched_delayed_work(&ipa_ctx->wake_lock_work, + msecs_to_jiffies + (WLAN_IPA_RX_INACTIVITY_MSEC_DELAY)); + + return QDF_STATUS_SUCCESS; +} +#endif /* CONFIG_IPA_WDI_UNIFIED_API */ + +/** + * wlan_ipa_rm_cons_request() - WLAN consumer resource request handler + * + * Callback function registered with IPA that is called when IPA wants + * to access the WLAN consumer resource + * + * Return: 0 if the request is granted, negative errno otherwise + */ +static int wlan_ipa_rm_cons_request(void) +{ + struct wlan_ipa_priv *ipa_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + ipa_ctx = wlan_ipa_get_obj_context(); + + if (ipa_ctx->resource_loading) { + ipa_err("IPA resource loading in progress"); + ipa_ctx->pending_cons_req = true; + status = QDF_STATUS_E_PENDING; + } else if (ipa_ctx->resource_unloading) { + ipa_err("IPA resource unloading in progress"); + ipa_ctx->pending_cons_req = true; + status = QDF_STATUS_E_PERM; + } + + return qdf_status_to_os_return(status); +} + +/** + * wlan_ipa_uc_rm_notify_handler() - IPA uC resource notification handler + * @ipa_ctx: IPA context + * @event: IPA RM event + * + * Return: None + */ +static void +wlan_ipa_uc_rm_notify_handler(struct wlan_ipa_priv *ipa_ctx, + qdf_ipa_rm_event_t event) +{ + if (!wlan_ipa_is_rm_enabled(ipa_ctx->config)) + return; + + ipa_debug("event code %d", event); + + switch (event) { + case QDF_IPA_RM_RESOURCE_GRANTED: + /* Differed RM Granted */ + qdf_mutex_acquire(&ipa_ctx->ipa_lock); + if ((!ipa_ctx->resource_unloading) && + (!ipa_ctx->activated_fw_pipe)) { + wlan_ipa_uc_enable_pipes(ipa_ctx); + } + qdf_mutex_release(&ipa_ctx->ipa_lock); + break; + + case QDF_IPA_RM_RESOURCE_RELEASED: + /* Differed RM Released */ + ipa_ctx->resource_unloading = false; + break; + + default: + ipa_err("invalid event code %d", event); + break; + } +} + +/** + * wlan_ipa_uc_rm_notify_defer() - Defer IPA uC notification + * * @data: IPA context + * + * This function is called when a resource manager event is received + * from firmware in interrupt context. This function will defer the + * handling to the OL RX thread + * + * Return: None + */ +static void wlan_ipa_uc_rm_notify_defer(void *data) +{ + struct wlan_ipa_priv *ipa_ctx = data; + qdf_ipa_rm_event_t event; + struct uc_rm_work_struct *uc_rm_work = &ipa_ctx->uc_rm_work; + + event = uc_rm_work->event; + + wlan_ipa_uc_rm_notify_handler(ipa_ctx, event); +} + +/** + * wlan_ipa_wake_lock_timer_func() - Wake lock work handler + * @data: IPA context + * + * When IPA resources are released in wlan_ipa_wdi_rm_try_release() we do + * not want to immediately release the wake lock since the system + * would then potentially try to suspend when there is a healthy data + * rate. Deferred work is scheduled and this function handles the + * work. When this function is called, if the IPA resource is still + * released then we release the wake lock. + * + * Return: None + */ +static void wlan_ipa_wake_lock_timer_func(void *data) +{ + struct wlan_ipa_priv *ipa_ctx = data; + + qdf_spin_lock_bh(&ipa_ctx->rm_lock); + + if (ipa_ctx->rm_state != WLAN_IPA_RM_RELEASED) + goto end; + + ipa_ctx->wake_lock_released = true; + qdf_wake_lock_release(&ipa_ctx->wake_lock, + WIFI_POWER_EVENT_WAKELOCK_IPA); + +end: + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); +} + +#ifndef CONFIG_IPA_WDI_UNIFIED_API +QDF_STATUS wlan_ipa_wdi_setup_rm(struct wlan_ipa_priv *ipa_ctx) +{ + qdf_ipa_rm_create_params_t create_params; + int ret; + + if (!wlan_ipa_is_rm_enabled(ipa_ctx->config)) + return 0; + + qdf_create_work(0, &ipa_ctx->uc_rm_work.work, + wlan_ipa_uc_rm_notify_defer, ipa_ctx); + qdf_mem_set(&create_params, 0, sizeof(create_params)); + create_params.name = QDF_IPA_RM_RESOURCE_WLAN_PROD; + create_params.reg_params.user_data = ipa_ctx; + create_params.reg_params.notify_cb = wlan_ipa_rm_notify; + create_params.floor_voltage = QDF_IPA_VOLTAGE_LEVEL; + + ret = qdf_ipa_rm_create_resource(&create_params); + if (ret) { + ipa_err("Create RM resource failed: %d", ret); + goto setup_rm_fail; + } + + qdf_mem_set(&create_params, 0, sizeof(create_params)); + create_params.name = QDF_IPA_RM_RESOURCE_WLAN_CONS; + create_params.request_resource = wlan_ipa_rm_cons_request; + create_params.release_resource = wlan_ipa_rm_cons_release; + create_params.floor_voltage = QDF_IPA_VOLTAGE_LEVEL; + + ret = qdf_ipa_rm_create_resource(&create_params); + if (ret) { + ipa_err("Create RM CONS resource failed: %d", ret); + goto delete_prod; + } + + qdf_ipa_rm_add_dependency(QDF_IPA_RM_RESOURCE_WLAN_PROD, + QDF_IPA_RM_RESOURCE_APPS_CONS); + + ret = qdf_ipa_rm_inactivity_timer_init(QDF_IPA_RM_RESOURCE_WLAN_PROD, + WLAN_IPA_RX_INACTIVITY_MSEC_DELAY); + if (ret) { + ipa_err("Timer init failed: %d", ret); + goto timer_init_failed; + } + + qdf_wake_lock_create(&ipa_ctx->wake_lock, "wlan_ipa"); + qdf_create_delayed_work(&ipa_ctx->wake_lock_work, + wlan_ipa_wake_lock_timer_func, ipa_ctx); + qdf_spinlock_create(&ipa_ctx->rm_lock); + ipa_ctx->rm_state = WLAN_IPA_RM_RELEASED; + ipa_ctx->wake_lock_released = true; + qdf_atomic_set(&ipa_ctx->tx_ref_cnt, 0); + + return QDF_STATUS_SUCCESS; + +timer_init_failed: + qdf_ipa_rm_delete_resource(QDF_IPA_RM_RESOURCE_APPS_CONS); + +delete_prod: + qdf_ipa_rm_delete_resource(QDF_IPA_RM_RESOURCE_WLAN_PROD); + +setup_rm_fail: + return QDF_STATUS_E_FAILURE; +} + +void wlan_ipa_wdi_destroy_rm(struct wlan_ipa_priv *ipa_ctx) +{ + int ret; + + if (!wlan_ipa_is_rm_enabled(ipa_ctx->config)) + return; + + qdf_cancel_delayed_work(&ipa_ctx->wake_lock_work); + qdf_wake_lock_destroy(&ipa_ctx->wake_lock); + qdf_cancel_work(&ipa_ctx->uc_rm_work.work); + qdf_spinlock_destroy(&ipa_ctx->rm_lock); + + ipa_rm_inactivity_timer_destroy(QDF_IPA_RM_RESOURCE_WLAN_PROD); + + ret = qdf_ipa_rm_delete_resource(QDF_IPA_RM_RESOURCE_WLAN_CONS); + if (ret) + ipa_err("RM CONS resource delete failed %d", ret); + + ret = qdf_ipa_rm_delete_resource(QDF_IPA_RM_RESOURCE_WLAN_PROD); + if (ret) + ipa_err("RM PROD resource delete failed %d", ret); +} +#endif /* CONFIG_IPA_WDI_UNIFIED_API */