From b17ffbbf400500ad424b05394ec4459e55bad614 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Wed, 29 Jun 2016 12:16:55 +0530 Subject: msm: ipa: disconnect sequence change for USB 2.0 For USB 2.0 there is a requirement to not to flush the USB endpoints after the pipes are disconnected. Otherwise this can result into NOC errors. Make a change to modify the disconnect sequence as below. 1) USB driver first disable the pipes 2) New API is provided from IPA to disable the endpoint. 3) As part of disable, make sure pipes are empty and reset the pipes. 4) USB resets its BAM and flushes the ep. 5) USB then disconnects both IPA and USB pipes. Change-Id: I917f025678e6abb03058d5be4ec42d9e6d76835f CRs-Fixed: 1038623 Acked-by: Chaitanya Pratapa Acked-by: Mohammed Javid Signed-off-by: Utkarsh Saxena --- drivers/platform/msm/ipa/ipa_api.c | 18 +++++ drivers/platform/msm/ipa/ipa_api.h | 2 + drivers/platform/msm/ipa/ipa_v2/ipa_client.c | 114 +++++++++++++++++++++++---- drivers/platform/msm/ipa/ipa_v2/ipa_i.h | 6 ++ drivers/platform/msm/ipa/ipa_v2/ipa_utils.c | 1 + drivers/platform/msm/ipa/ipa_v3/ipa_utils.c | 1 + 6 files changed, 127 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c index 208a4ce1e40e..7a9307294a6d 100644 --- a/drivers/platform/msm/ipa/ipa_api.c +++ b/drivers/platform/msm/ipa/ipa_api.c @@ -369,6 +369,24 @@ int ipa_reset_endpoint(u32 clnt_hdl) } EXPORT_SYMBOL(ipa_reset_endpoint); +/** +* ipa_disable_endpoint() - Disable an endpoint from IPA perspective +* @clnt_hdl: [in] IPA client handle +* +* Returns: 0 on success, negative on failure +* +* Note: Should not be called from atomic context +*/ +int ipa_disable_endpoint(u32 clnt_hdl) +{ + int ret; + + IPA_API_DISPATCH_RETURN(ipa_disable_endpoint, clnt_hdl); + + return ret; +} +EXPORT_SYMBOL(ipa_disable_endpoint); + /** * ipa_cfg_ep - IPA end-point configuration diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h index 862bdc475025..3c2471dd11dd 100644 --- a/drivers/platform/msm/ipa/ipa_api.h +++ b/drivers/platform/msm/ipa/ipa_api.h @@ -26,6 +26,8 @@ struct ipa_api_controller { int (*ipa_clear_endpoint_delay)(u32 clnt_hdl); + int (*ipa_disable_endpoint)(u32 clnt_hdl); + int (*ipa_cfg_ep)(u32 clnt_hdl, const struct ipa_ep_cfg *ipa_ep_cfg); int (*ipa_cfg_ep_nat)(u32 clnt_hdl, diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_client.c b/drivers/platform/msm/ipa/ipa_v2/ipa_client.c index 64246ac4eec0..66e329a03df7 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_client.c @@ -560,22 +560,30 @@ int ipa2_disconnect(u32 clnt_hdl) if (!ep->keep_ipa_awake) IPA_ACTIVE_CLIENTS_INC_EP(client_type); - /* Set Disconnect in Progress flag. */ - spin_lock(&ipa_ctx->disconnect_lock); - ep->disconnect_in_progress = true; - spin_unlock(&ipa_ctx->disconnect_lock); - - /* Notify uc to stop monitoring holb on USB BAM Producer pipe. */ - if (IPA_CLIENT_IS_USB_CONS(ep->client)) { - ipa_uc_monitor_holb(ep->client, false); - IPADBG("Disabling holb monitor for client: %d\n", ep->client); - } + /* For USB 2.0 controller, first the ep will be disabled. + * so this sequence is not needed again when disconnecting the pipe. + */ + if (!ep->ep_disabled) { + /* Set Disconnect in Progress flag. */ + spin_lock(&ipa_ctx->disconnect_lock); + ep->disconnect_in_progress = true; + spin_unlock(&ipa_ctx->disconnect_lock); + + /* Notify uc to stop monitoring holb on USB BAM + * Producer pipe. + */ + if (IPA_CLIENT_IS_USB_CONS(ep->client)) { + ipa_uc_monitor_holb(ep->client, false); + IPADBG("Disabling holb monitor for client: %d\n", + ep->client); + } - result = ipa_disable_data_path(clnt_hdl); - if (result) { - IPAERR("disable data path failed res=%d clnt=%d.\n", result, - clnt_hdl); - return -EPERM; + result = ipa_disable_data_path(clnt_hdl); + if (result) { + IPAERR("disable data path failed res=%d clnt=%d.\n", + result, clnt_hdl); + return -EPERM; + } } result = sps_disconnect(ep->ep_hdl); @@ -783,6 +791,82 @@ int ipa2_clear_endpoint_delay(u32 clnt_hdl) return 0; } +/** + * ipa2_disable_endpoint() - low-level IPA client disable endpoint + * @clnt_hdl: [in] opaque client handle assigned by IPA to client + * + * Should be called by the driver of the peripheral that wants to + * disable the pipe from IPA in BAM-BAM mode. + * + * Returns: 0 on success, negative on failure + * + * Note: Should not be called from atomic context + */ +int ipa2_disable_endpoint(u32 clnt_hdl) +{ + int result; + struct ipa_ep_context *ep; + enum ipa_client_type client_type; + unsigned long bam; + + if (unlikely(!ipa_ctx)) { + IPAERR("IPA driver was not initialized\n"); + return -EINVAL; + } + + if (clnt_hdl >= ipa_ctx->ipa_num_pipes || + ipa_ctx->ep[clnt_hdl].valid == 0) { + IPAERR("bad parm.\n"); + return -EINVAL; + } + + ep = &ipa_ctx->ep[clnt_hdl]; + client_type = ipa2_get_client_mapping(clnt_hdl); + IPA_ACTIVE_CLIENTS_INC_EP(client_type); + + /* Set Disconnect in Progress flag. */ + spin_lock(&ipa_ctx->disconnect_lock); + ep->disconnect_in_progress = true; + spin_unlock(&ipa_ctx->disconnect_lock); + + /* Notify uc to stop monitoring holb on USB BAM Producer pipe. */ + if (IPA_CLIENT_IS_USB_CONS(ep->client)) { + ipa_uc_monitor_holb(ep->client, false); + IPADBG("Disabling holb monitor for client: %d\n", ep->client); + } + + result = ipa_disable_data_path(clnt_hdl); + if (result) { + IPAERR("disable data path failed res=%d clnt=%d.\n", result, + clnt_hdl); + goto fail; + } + + if (IPA_CLIENT_IS_CONS(ep->client)) + bam = ep->connect.source; + else + bam = ep->connect.destination; + + result = sps_pipe_reset(bam, clnt_hdl); + if (result) { + IPAERR("SPS pipe reset failed.\n"); + goto fail; + } + + ep->ep_disabled = true; + + IPA_ACTIVE_CLIENTS_DEC_EP(client_type); + + IPADBG("client (ep: %d) disabled\n", clnt_hdl); + + return 0; + +fail: + IPA_ACTIVE_CLIENTS_DEC_EP(client_type); + return -EPERM; +} + + /** * ipa_sps_connect_safe() - connect endpoint from BAM prespective * @h: [in] sps pipe handle diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h index f94418efc927..5cececc64b8a 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h @@ -553,6 +553,7 @@ struct ipa_ep_context { bool switch_to_intr; int inactive_cycles; u32 eot_in_poll_err; + bool ep_disabled; /* sys MUST be the last element of this struct */ struct ipa_sys_context *sys; @@ -1425,6 +1426,11 @@ int ipa2_reset_endpoint(u32 clnt_hdl); */ int ipa2_clear_endpoint_delay(u32 clnt_hdl); +/* + * Disable ep + */ +int ipa2_disable_endpoint(u32 clnt_hdl); + /* * Configuration */ diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c index 0dd10743a01e..d34312b6c068 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c @@ -4920,6 +4920,7 @@ int ipa2_bind_api_controller(enum ipa_hw_type ipa_hw_type, api_ctrl->ipa_disconnect = ipa2_disconnect; api_ctrl->ipa_reset_endpoint = ipa2_reset_endpoint; api_ctrl->ipa_clear_endpoint_delay = ipa2_clear_endpoint_delay; + api_ctrl->ipa_disable_endpoint = ipa2_disable_endpoint; api_ctrl->ipa_cfg_ep = ipa2_cfg_ep; api_ctrl->ipa_cfg_ep_nat = ipa2_cfg_ep_nat; api_ctrl->ipa_cfg_ep_hdr = ipa2_cfg_ep_hdr; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 21b8cac11d2b..b4a5456be943 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -3029,6 +3029,7 @@ int ipa3_bind_api_controller(enum ipa_hw_type ipa_hw_type, api_ctrl->ipa_disconnect = ipa3_disconnect; api_ctrl->ipa_reset_endpoint = ipa3_reset_endpoint; api_ctrl->ipa_clear_endpoint_delay = ipa3_clear_endpoint_delay; + api_ctrl->ipa_disable_endpoint = NULL; api_ctrl->ipa_cfg_ep = ipa3_cfg_ep; api_ctrl->ipa_cfg_ep_nat = ipa3_cfg_ep_nat; api_ctrl->ipa_cfg_ep_hdr = ipa3_cfg_ep_hdr; -- cgit v1.2.3