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 <cpratapa@qti.qualcomm.com> Acked-by: Mohammed Javid <mjavid@qti.qualcomm.com> Signed-off-by: Utkarsh Saxena <usaxena@codeaurora.org>
This commit is contained in:
parent
0fdb8c8eae
commit
b17ffbbf40
7 changed files with 139 additions and 14 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
/* 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);
|
||||
}
|
||||
/* 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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1095,6 +1095,11 @@ int ipa_reset_endpoint(u32 clnt_hdl);
|
|||
*/
|
||||
int ipa_clear_endpoint_delay(u32 clnt_hdl);
|
||||
|
||||
/*
|
||||
* Disable ep
|
||||
*/
|
||||
int ipa_disable_endpoint(u32 clnt_hdl);
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
|
@ -1461,6 +1466,14 @@ static inline int ipa_clear_endpoint_delay(u32 clnt_hdl)
|
|||
return -EPERM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable ep
|
||||
*/
|
||||
static inline int ipa_disable_endpoint(u32 clnt_hdl)
|
||||
{
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
|
|
Loading…
Add table
Reference in a new issue