diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_client.c b/drivers/platform/msm/ipa/ipa_v2/ipa_client.c index 66e329a03df7..74e7394a80b1 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_client.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -95,6 +95,46 @@ int ipa_disable_data_path(u32 clnt_hdl) return res; } +int ipa2_enable_force_clear(u32 request_id, bool throttle_source, + u32 source_pipe_bitmask) +{ + struct ipa_enable_force_clear_datapath_req_msg_v01 req; + int result; + + memset(&req, 0, sizeof(req)); + req.request_id = request_id; + req.source_pipe_bitmask = source_pipe_bitmask; + if (throttle_source) { + req.throttle_source_valid = 1; + req.throttle_source = 1; + } + result = qmi_enable_force_clear_datapath_send(&req); + if (result) { + IPAERR("qmi_enable_force_clear_datapath_send failed %d\n", + result); + return result; + } + + return 0; +} + +int ipa2_disable_force_clear(u32 request_id) +{ + struct ipa_disable_force_clear_datapath_req_msg_v01 req; + int result; + + memset(&req, 0, sizeof(req)); + req.request_id = request_id; + result = qmi_disable_force_clear_datapath_send(&req); + if (result) { + IPAERR("qmi_disable_force_clear_datapath_send failed %d\n", + result); + return result; + } + + return 0; +} + static int ipa2_smmu_map_peer_bam(unsigned long dev) { phys_addr_t base; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h index b45e748b66a6..94d76db6f993 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h @@ -1803,6 +1803,9 @@ void ipa_delete_dflt_flt_rules(u32 ipa_ep_idx); int ipa_enable_data_path(u32 clnt_hdl); int ipa_disable_data_path(u32 clnt_hdl); +int ipa2_enable_force_clear(u32 request_id, bool throttle_source, + u32 source_pipe_bitmask); +int ipa2_disable_force_clear(u32 request_id); int ipa_id_alloc(void *ptr); void *ipa_id_find(u32 id); void ipa_id_remove(u32 id); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c index d88e5a6d3d73..e2ac9bfceed7 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c @@ -39,6 +39,8 @@ #define QMI_SEND_STATS_REQ_TIMEOUT_MS 5000 #define QMI_SEND_REQ_TIMEOUT_MS 60000 +#define QMI_IPA_FORCE_CLEAR_DATAPATH_TIMEOUT_MS 1000 + static struct qmi_handle *ipa_svc_handle; static void ipa_a5_svc_recv_msg(struct work_struct *work); static DECLARE_DELAYED_WORK(work_recv_msg, ipa_a5_svc_recv_msg); @@ -583,7 +585,8 @@ int qmi_enable_force_clear_datapath_send( &req_desc, req, sizeof(*req), - &resp_desc, &resp, sizeof(resp), 0); + &resp_desc, &resp, sizeof(resp), + QMI_IPA_FORCE_CLEAR_DATAPATH_TIMEOUT_MS); if (rc < 0) { IPAWANERR("send req failed %d\n", rc); return rc; @@ -628,7 +631,8 @@ int qmi_disable_force_clear_datapath_send( &req_desc, req, sizeof(*req), - &resp_desc, &resp, sizeof(resp), 0); + &resp_desc, &resp, sizeof(resp), + QMI_IPA_FORCE_CLEAR_DATAPATH_TIMEOUT_MS); if (rc < 0) { IPAWANERR("send req failed %d\n", rc); return rc; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c index 5bda4cb9ed2b..f60669132865 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c @@ -1404,7 +1404,6 @@ int ipa2_disable_wdi_pipe(u32 clnt_hdl) union IpaHwWdiCommonChCmdData_t disable; struct ipa_ep_cfg_ctrl ep_cfg_ctrl; u32 prod_hdl; - int i; if (unlikely(!ipa_ctx)) { IPAERR("IPA driver was not initialized\n"); @@ -1421,28 +1420,6 @@ int ipa2_disable_wdi_pipe(u32 clnt_hdl) if (result) return result; - /* checking rdy_ring_rp_pa matches the rdy_comp_ring_wp_pa on WDI2.0 */ - if (ipa_ctx->ipa_wdi2) { - for (i = 0; i < IPA_UC_FINISH_MAX; i++) { - IPADBG("(%d) rp_value(%u), comp_wp_value(%u)\n", - i, - *ipa_ctx->uc_ctx.rdy_ring_rp_va, - *ipa_ctx->uc_ctx.rdy_comp_ring_wp_va); - if (*ipa_ctx->uc_ctx.rdy_ring_rp_va != - *ipa_ctx->uc_ctx.rdy_comp_ring_wp_va) { - usleep_range(IPA_UC_WAIT_MIN_SLEEP, - IPA_UC_WAII_MAX_SLEEP); - } else { - break; - } - } - /* In case ipa_uc still haven't processed all - * pending descriptors, we have to assert - */ - if (i == IPA_UC_FINISH_MAX) - BUG(); - } - IPADBG("ep=%d\n", clnt_hdl); ep = &ipa_ctx->ep[clnt_hdl]; @@ -1468,6 +1445,11 @@ int ipa2_disable_wdi_pipe(u32 clnt_hdl) * holb on IPA Producer pipe */ if (IPA_CLIENT_IS_PROD(ep->client)) { + + IPADBG("Stopping PROD channel - hdl=%d clnt=%d\n", + clnt_hdl, ep->client); + + /* remove delay on wlan-prod pipe*/ memset(&ep_cfg_ctrl, 0 , sizeof(struct ipa_ep_cfg_ctrl)); ipa2_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl); @@ -1594,6 +1576,8 @@ int ipa2_suspend_wdi_pipe(u32 clnt_hdl) struct ipa_ep_context *ep; union IpaHwWdiCommonChCmdData_t suspend; struct ipa_ep_cfg_ctrl ep_cfg_ctrl; + u32 source_pipe_bitmask = 0; + bool disable_force_clear = false; if (unlikely(!ipa_ctx)) { IPAERR("IPA driver was not initialized\n"); @@ -1623,6 +1607,31 @@ int ipa2_suspend_wdi_pipe(u32 clnt_hdl) suspend.params.ipa_pipe_number = clnt_hdl; if (IPA_CLIENT_IS_PROD(ep->client)) { + /* + * For WDI 2.0 need to ensure pipe will be empty before suspend + * as IPA uC will fail to suspend the pipe otherwise. + */ + if (ipa_ctx->ipa_wdi2) { + source_pipe_bitmask = 1 << + ipa_get_ep_mapping(ep->client); + result = ipa2_enable_force_clear(clnt_hdl, + false, source_pipe_bitmask); + if (result) { + /* + * assuming here modem SSR, AP can remove + * the delay in this case + */ + IPAERR("failed to force clear %d\n", result); + IPAERR("remove delay from SCND reg\n"); + memset(&ep_cfg_ctrl, 0, + sizeof(struct ipa_ep_cfg_ctrl)); + ep_cfg_ctrl.ipa_ep_delay = false; + ep_cfg_ctrl.ipa_ep_suspend = false; + ipa2_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl); + } else { + disable_force_clear = true; + } + } IPADBG("Post suspend event first for IPA Producer\n"); IPADBG("Client: %d clnt_hdl: %d\n", ep->client, clnt_hdl); result = ipa_uc_send_cmd(suspend.raw32b, @@ -1667,6 +1676,9 @@ int ipa2_suspend_wdi_pipe(u32 clnt_hdl) } } + if (disable_force_clear) + ipa2_disable_force_clear(clnt_hdl); + ipa_ctx->tag_process_before_gating = true; IPA_ACTIVE_CLIENTS_DEC_EP(ipa2_get_client_mapping(clnt_hdl)); ep->uc_offload_state &= ~IPA_WDI_RESUMED;