diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2016-10-31 06:59:10 -0700 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-10-31 06:59:10 -0700 |
commit | 9178072f99e5921391257cc5846f711c9cf526dc (patch) | |
tree | 53e3eb46fa71d9a09bb30889095017f2814fdc44 /drivers/platform | |
parent | deca532c2980b3514c30dc8bc3f00011ce2ed229 (diff) | |
parent | e1cf6bb611cb9788fc1095a86926b1e1bf70048a (diff) |
Merge "msm: ipa3: Support IPA-USB suspend sequence without remote wake-up"
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/msm/ipa/ipa_clients/ipa_usb.c | 499 | ||||
-rw-r--r-- | drivers/platform/msm/ipa/ipa_v3/ipa_client.c | 45 | ||||
-rw-r--r-- | drivers/platform/msm/ipa/ipa_v3/ipa_i.h | 4 | ||||
-rw-r--r-- | drivers/platform/msm/ipa/ipa_v3/ipa_utils.c | 2 |
4 files changed, 401 insertions, 149 deletions
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c index d18308344431..293371b88ab9 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c @@ -127,6 +127,7 @@ enum ipa3_usb_state { IPA_USB_SUSPEND_REQUESTED, IPA_USB_SUSPEND_IN_PROGRESS, IPA_USB_SUSPENDED, + IPA_USB_SUSPENDED_NO_RWAKEUP, IPA_USB_RESUME_IN_PROGRESS }; @@ -152,6 +153,12 @@ struct finish_suspend_work_context { u32 ul_clnt_hdl; }; +struct ipa3_usb_teth_prot_conn_params { + u32 usb_to_ipa_clnt_hdl; + u32 ipa_to_usb_clnt_hdl; + struct ipa_usb_teth_prot_params params; +}; + /** * Transport type - could be either data tethering or DPL * Each transport has it's own RM resources and statuses @@ -163,6 +170,7 @@ struct ipa3_usb_transport_type_ctx { enum ipa3_usb_state state; struct finish_suspend_work_context finish_suspend_work; struct ipa_usb_xdci_chan_params ch_params; + struct ipa3_usb_teth_prot_conn_params teth_conn_params; }; struct ipa3_usb_smmu_reg_map { @@ -189,14 +197,15 @@ struct ipa3_usb_context { }; enum ipa3_usb_op { - IPA_USB_INIT_TETH_PROT, - IPA_USB_REQUEST_CHANNEL, - IPA_USB_CONNECT, - IPA_USB_DISCONNECT, - IPA_USB_RELEASE_CHANNEL, - IPA_USB_DEINIT_TETH_PROT, - IPA_USB_SUSPEND, - IPA_USB_RESUME + IPA_USB_OP_INIT_TETH_PROT, + IPA_USB_OP_REQUEST_CHANNEL, + IPA_USB_OP_CONNECT, + IPA_USB_OP_DISCONNECT, + IPA_USB_OP_RELEASE_CHANNEL, + IPA_USB_OP_DEINIT_TETH_PROT, + IPA_USB_OP_SUSPEND, + IPA_USB_OP_SUSPEND_NO_RWAKEUP, + IPA_USB_OP_RESUME }; struct ipa3_usb_status_dbg_info { @@ -228,22 +237,24 @@ struct ipa3_usb_context *ipa3_usb_ctx; static char *ipa3_usb_op_to_string(enum ipa3_usb_op op) { switch (op) { - case IPA_USB_INIT_TETH_PROT: - return "IPA_USB_INIT_TETH_PROT"; - case IPA_USB_REQUEST_CHANNEL: - return "IPA_USB_REQUEST_CHANNEL"; - case IPA_USB_CONNECT: - return "IPA_USB_CONNECT"; - case IPA_USB_DISCONNECT: - return "IPA_USB_DISCONNECT"; - case IPA_USB_RELEASE_CHANNEL: - return "IPA_USB_RELEASE_CHANNEL"; - case IPA_USB_DEINIT_TETH_PROT: - return "IPA_USB_DEINIT_TETH_PROT"; - case IPA_USB_SUSPEND: - return "IPA_USB_SUSPEND"; - case IPA_USB_RESUME: - return "IPA_USB_RESUME"; + case IPA_USB_OP_INIT_TETH_PROT: + return "IPA_USB_OP_INIT_TETH_PROT"; + case IPA_USB_OP_REQUEST_CHANNEL: + return "IPA_USB_OP_REQUEST_CHANNEL"; + case IPA_USB_OP_CONNECT: + return "IPA_USB_OP_CONNECT"; + case IPA_USB_OP_DISCONNECT: + return "IPA_USB_OP_DISCONNECT"; + case IPA_USB_OP_RELEASE_CHANNEL: + return "IPA_USB_OP_RELEASE_CHANNEL"; + case IPA_USB_OP_DEINIT_TETH_PROT: + return "IPA_USB_OP_DEINIT_TETH_PROT"; + case IPA_USB_OP_SUSPEND: + return "IPA_USB_OP_SUSPEND"; + case IPA_USB_OP_SUSPEND_NO_RWAKEUP: + return "IPA_USB_OP_SUSPEND_NO_RWAKEUP"; + case IPA_USB_OP_RESUME: + return "IPA_USB_OP_RESUME"; } return "UNSUPPORTED"; @@ -266,6 +277,8 @@ static char *ipa3_usb_state_to_string(enum ipa3_usb_state state) return "IPA_USB_SUSPEND_IN_PROGRESS"; case IPA_USB_SUSPENDED: return "IPA_USB_SUSPENDED"; + case IPA_USB_SUSPENDED_NO_RWAKEUP: + return "IPA_USB_SUSPENDED_NO_RWAKEUP"; case IPA_USB_RESUME_IN_PROGRESS: return "IPA_USB_RESUME_IN_PROGRESS"; } @@ -312,6 +325,7 @@ static bool ipa3_usb_set_state(enum ipa3_usb_state new_state, bool err_permit, if (state == IPA_USB_INITIALIZED || state == IPA_USB_STOPPED || state == IPA_USB_RESUME_IN_PROGRESS || + state == IPA_USB_SUSPENDED_NO_RWAKEUP || /* * In case of failure during suspend request * handling, state is reverted to connected. @@ -327,7 +341,8 @@ static bool ipa3_usb_set_state(enum ipa3_usb_state new_state, bool err_permit, case IPA_USB_STOPPED: if (state == IPA_USB_SUSPEND_IN_PROGRESS || state == IPA_USB_CONNECTED || - state == IPA_USB_SUSPENDED) + state == IPA_USB_SUSPENDED || + state == IPA_USB_SUSPENDED_NO_RWAKEUP) state_legal = true; break; case IPA_USB_SUSPEND_REQUESTED: @@ -354,6 +369,10 @@ static bool ipa3_usb_set_state(enum ipa3_usb_state new_state, bool err_permit, (err_permit && state == IPA_USB_RESUME_IN_PROGRESS)) state_legal = true; break; + case IPA_USB_SUSPENDED_NO_RWAKEUP: + if (state == IPA_USB_CONNECTED) + state_legal = true; + break; case IPA_USB_RESUME_IN_PROGRESS: if (state == IPA_USB_SUSPEND_IN_PROGRESS || state == IPA_USB_SUSPENDED) @@ -418,32 +437,33 @@ static bool ipa3_usb_check_legal_op(enum ipa3_usb_op op, spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags); state = ipa3_usb_ctx->ttype_ctx[ttype].state; switch (op) { - case IPA_USB_INIT_TETH_PROT: + case IPA_USB_OP_INIT_TETH_PROT: if (state == IPA_USB_INVALID || (!is_dpl && state == IPA_USB_INITIALIZED)) is_legal = true; break; - case IPA_USB_REQUEST_CHANNEL: + case IPA_USB_OP_REQUEST_CHANNEL: if (state == IPA_USB_INITIALIZED) is_legal = true; break; - case IPA_USB_CONNECT: + case IPA_USB_OP_CONNECT: if (state == IPA_USB_INITIALIZED || state == IPA_USB_STOPPED) is_legal = true; break; - case IPA_USB_DISCONNECT: + case IPA_USB_OP_DISCONNECT: if (state == IPA_USB_CONNECTED || state == IPA_USB_SUSPEND_IN_PROGRESS || - state == IPA_USB_SUSPENDED) + state == IPA_USB_SUSPENDED || + state == IPA_USB_SUSPENDED_NO_RWAKEUP) is_legal = true; break; - case IPA_USB_RELEASE_CHANNEL: + case IPA_USB_OP_RELEASE_CHANNEL: /* when releasing 1st channel state will be changed already */ if (state == IPA_USB_STOPPED || (!is_dpl && state == IPA_USB_INITIALIZED)) is_legal = true; break; - case IPA_USB_DEINIT_TETH_PROT: + case IPA_USB_OP_DEINIT_TETH_PROT: /* * For data tethering we should allow deinit an inited protocol * always. E.g. rmnet is inited and rndis is connected. @@ -453,13 +473,18 @@ static bool ipa3_usb_check_legal_op(enum ipa3_usb_op op, if (!is_dpl || state == IPA_USB_INITIALIZED) is_legal = true; break; - case IPA_USB_SUSPEND: + case IPA_USB_OP_SUSPEND: if (state == IPA_USB_CONNECTED) is_legal = true; break; - case IPA_USB_RESUME: + case IPA_USB_OP_SUSPEND_NO_RWAKEUP: + if (state == IPA_USB_CONNECTED) + is_legal = true; + break; + case IPA_USB_OP_RESUME: if (state == IPA_USB_SUSPENDED || - state == IPA_USB_SUSPEND_IN_PROGRESS) + state == IPA_USB_SUSPEND_IN_PROGRESS || + state == IPA_USB_SUSPENDED_NO_RWAKEUP) is_legal = true; break; default: @@ -638,6 +663,7 @@ static int ipa3_usb_cons_request_resource_cb_do( ipa3_usb_ctx->ttype_ctx[ttype].state)); switch (ipa3_usb_ctx->ttype_ctx[ttype].state) { case IPA_USB_CONNECTED: + case IPA_USB_SUSPENDED_NO_RWAKEUP: rm_ctx->cons_state = IPA_USB_CONS_GRANTED; result = 0; break; @@ -717,6 +743,7 @@ static int ipa3_usb_cons_release_resource_cb_do( break; case IPA_USB_STOPPED: case IPA_USB_RESUME_IN_PROGRESS: + case IPA_USB_SUSPENDED_NO_RWAKEUP: if (rm_ctx->cons_requested) rm_ctx->cons_requested = false; break; @@ -886,7 +913,7 @@ int ipa_usb_init_teth_prot(enum ipa_usb_teth_prot teth_prot, ttype = IPA3_USB_GET_TTYPE(teth_prot); - if (!ipa3_usb_check_legal_op(IPA_USB_INIT_TETH_PROT, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_INIT_TETH_PROT, ttype)) { IPA_USB_ERR("Illegal operation.\n"); result = -EPERM; goto bad_params; @@ -1204,7 +1231,7 @@ static int ipa3_usb_request_xdci_channel( ttype = IPA3_USB_GET_TTYPE(params->teth_prot); - if (!ipa3_usb_check_legal_op(IPA_USB_REQUEST_CHANNEL, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_REQUEST_CHANNEL, ttype)) { IPA_USB_ERR("Illegal operation\n"); return -EPERM; } @@ -1347,7 +1374,7 @@ static int ipa3_usb_release_xdci_channel(u32 clnt_hdl, return -EINVAL; } - if (!ipa3_usb_check_legal_op(IPA_USB_RELEASE_CHANNEL, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_RELEASE_CHANNEL, ttype)) { IPA_USB_ERR("Illegal operation.\n"); return -EPERM; } @@ -1511,81 +1538,79 @@ static int ipa3_usb_connect_dpl(void) return 0; } -static int ipa3_usb_connect_teth_prot( - struct ipa_usb_xdci_connect_params_internal *params, - enum ipa3_usb_transport_type ttype) +static int ipa3_usb_connect_teth_prot(enum ipa_usb_teth_prot teth_prot) { int result; struct teth_bridge_connect_params teth_bridge_params; + struct ipa3_usb_teth_prot_conn_params *teth_conn_params; + enum ipa3_usb_transport_type ttype; - IPA_USB_DBG("connecting protocol = %d\n", - params->teth_prot); - switch (params->teth_prot) { + IPA_USB_DBG("connecting protocol = %s\n", + ipa3_usb_teth_prot_to_string(teth_prot)); + + ttype = IPA3_USB_GET_TTYPE(teth_prot); + + teth_conn_params = &(ipa3_usb_ctx->ttype_ctx[ttype].teth_conn_params); + + switch (teth_prot) { case IPA_USB_RNDIS: if (ipa3_usb_ctx->teth_prot_ctx[IPA_USB_RNDIS].state == IPA_USB_TETH_PROT_CONNECTED) { IPA_USB_DBG("%s is already connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); break; } ipa3_usb_ctx->ttype_ctx[ttype].user_data = ipa3_usb_ctx->teth_prot_ctx[IPA_USB_RNDIS].user_data; result = rndis_ipa_pipe_connect_notify( - params->usb_to_ipa_clnt_hdl, - params->ipa_to_usb_clnt_hdl, - params->teth_prot_params.max_xfer_size_bytes_to_dev, - params->teth_prot_params.max_packet_number_to_dev, - params->teth_prot_params.max_xfer_size_bytes_to_host, + teth_conn_params->usb_to_ipa_clnt_hdl, + teth_conn_params->ipa_to_usb_clnt_hdl, + teth_conn_params->params.max_xfer_size_bytes_to_dev, + teth_conn_params->params.max_packet_number_to_dev, + teth_conn_params->params.max_xfer_size_bytes_to_host, ipa3_usb_ctx->teth_prot_ctx[IPA_USB_RNDIS]. teth_prot_params.rndis.private); if (result) { IPA_USB_ERR("failed to connect %s.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); ipa3_usb_ctx->ttype_ctx[ttype].user_data = NULL; return result; } ipa3_usb_ctx->teth_prot_ctx[IPA_USB_RNDIS].state = IPA_USB_TETH_PROT_CONNECTED; IPA_USB_DBG("%s is connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); break; case IPA_USB_ECM: if (ipa3_usb_ctx->teth_prot_ctx[IPA_USB_ECM].state == IPA_USB_TETH_PROT_CONNECTED) { IPA_USB_DBG("%s is already connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); break; } ipa3_usb_ctx->ttype_ctx[ttype].user_data = ipa3_usb_ctx->teth_prot_ctx[IPA_USB_ECM].user_data; - result = ecm_ipa_connect(params->usb_to_ipa_clnt_hdl, - params->ipa_to_usb_clnt_hdl, + result = ecm_ipa_connect(teth_conn_params->usb_to_ipa_clnt_hdl, + teth_conn_params->ipa_to_usb_clnt_hdl, ipa3_usb_ctx->teth_prot_ctx[IPA_USB_ECM]. teth_prot_params.ecm.private); if (result) { IPA_USB_ERR("failed to connect %s.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); ipa3_usb_ctx->ttype_ctx[ttype].user_data = NULL; return result; } ipa3_usb_ctx->teth_prot_ctx[IPA_USB_ECM].state = IPA_USB_TETH_PROT_CONNECTED; IPA_USB_DBG("%s is connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); break; case IPA_USB_RMNET: case IPA_USB_MBIM: - if (ipa3_usb_ctx->teth_prot_ctx[params->teth_prot].state == + if (ipa3_usb_ctx->teth_prot_ctx[teth_prot].state == IPA_USB_TETH_PROT_CONNECTED) { IPA_USB_DBG("%s is already connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); break; } result = ipa3_usb_init_teth_bridge(); @@ -1593,14 +1618,14 @@ static int ipa3_usb_connect_teth_prot( return result; ipa3_usb_ctx->ttype_ctx[ttype].user_data = - ipa3_usb_ctx->teth_prot_ctx[params->teth_prot]. + ipa3_usb_ctx->teth_prot_ctx[teth_prot]. user_data; teth_bridge_params.ipa_usb_pipe_hdl = - params->ipa_to_usb_clnt_hdl; + teth_conn_params->ipa_to_usb_clnt_hdl; teth_bridge_params.usb_ipa_pipe_hdl = - params->usb_to_ipa_clnt_hdl; + teth_conn_params->usb_to_ipa_clnt_hdl; teth_bridge_params.tethering_mode = - (params->teth_prot == IPA_USB_RMNET) ? + (teth_prot == IPA_USB_RMNET) ? (TETH_TETHERING_MODE_RMNET):(TETH_TETHERING_MODE_MBIM); teth_bridge_params.client_type = IPA_CLIENT_USB_PROD; result = ipa3_usb_connect_teth_bridge(&teth_bridge_params); @@ -1608,27 +1633,23 @@ static int ipa3_usb_connect_teth_prot( ipa3_usb_ctx->ttype_ctx[ttype].user_data = NULL; return result; } - ipa3_usb_ctx->teth_prot_ctx[params->teth_prot].state = + ipa3_usb_ctx->teth_prot_ctx[teth_prot].state = IPA_USB_TETH_PROT_CONNECTED; ipa3_usb_notify_do(ttype, IPA_USB_DEVICE_READY); IPA_USB_DBG("%s (%s) is connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot), - ipa3_usb_teth_bridge_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot), + ipa3_usb_teth_bridge_prot_to_string(teth_prot)); break; case IPA_USB_DIAG: if (ipa3_usb_ctx->teth_prot_ctx[IPA_USB_DIAG].state == IPA_USB_TETH_PROT_CONNECTED) { IPA_USB_DBG("%s is already connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); break; } ipa3_usb_ctx->ttype_ctx[ttype].user_data = - ipa3_usb_ctx->teth_prot_ctx[params->teth_prot]. - user_data; + ipa3_usb_ctx->teth_prot_ctx[teth_prot].user_data; result = ipa3_usb_connect_dpl(); if (result) { IPA_USB_ERR("Failed connecting DPL result=%d\n", @@ -1640,8 +1661,7 @@ static int ipa3_usb_connect_teth_prot( IPA_USB_TETH_PROT_CONNECTED; ipa3_usb_notify_do(ttype, IPA_USB_DEVICE_READY); IPA_USB_DBG("%s is connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); break; default: IPA_USB_ERR("Invalid tethering protocol\n"); @@ -1775,11 +1795,19 @@ static int ipa3_usb_xdci_connect_internal( ttype = (params->teth_prot == IPA_USB_DIAG) ? IPA_USB_TRANSPORT_DPL : IPA_USB_TRANSPORT_TETH; - if (!ipa3_usb_check_legal_op(IPA_USB_CONNECT, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_CONNECT, ttype)) { IPA_USB_ERR("Illegal operation.\n"); return -EPERM; } + ipa3_usb_ctx->ttype_ctx[ttype].teth_conn_params.ipa_to_usb_clnt_hdl + = params->ipa_to_usb_clnt_hdl; + if (!IPA3_USB_IS_TTYPE_DPL(ttype)) + ipa3_usb_ctx->ttype_ctx[ttype].teth_conn_params. + usb_to_ipa_clnt_hdl = params->usb_to_ipa_clnt_hdl; + ipa3_usb_ctx->ttype_ctx[ttype].teth_conn_params.params + = params->teth_prot_params; + /* Set EE xDCI specific scratch */ result = ipa3_set_usb_max_packet_size(params->max_pkt_size); if (result) { @@ -1816,7 +1844,7 @@ static int ipa3_usb_xdci_connect_internal( if (params->teth_prot != IPA_USB_DIAG) { /* Start UL channel */ - result = ipa3_xdci_connect(params->usb_to_ipa_clnt_hdl, + result = ipa3_xdci_start(params->usb_to_ipa_clnt_hdl, params->usb_to_ipa_xferrscidx, params->usb_to_ipa_xferrscidx_valid); if (result) { @@ -1826,7 +1854,7 @@ static int ipa3_usb_xdci_connect_internal( } /* Start DL/DPL channel */ - result = ipa3_xdci_connect(params->ipa_to_usb_clnt_hdl, + result = ipa3_xdci_start(params->ipa_to_usb_clnt_hdl, params->ipa_to_usb_xferrscidx, params->ipa_to_usb_xferrscidx_valid); if (result) { @@ -1835,7 +1863,7 @@ static int ipa3_usb_xdci_connect_internal( } /* Connect tethering protocol */ - result = ipa3_usb_connect_teth_prot(params, ttype); + result = ipa3_usb_connect_teth_prot(params->teth_prot); if (result) { IPA_USB_ERR("failed to connect teth protocol\n"); goto connect_teth_prot_fail; @@ -2164,6 +2192,70 @@ static int ipa3_usb_check_disconnect_prot(enum ipa_usb_teth_prot teth_prot) return 0; } +/* Assumes lock already acquired */ +static int ipa_usb_xdci_dismiss_channels(u32 ul_clnt_hdl, u32 dl_clnt_hdl, + enum ipa_usb_teth_prot teth_prot) +{ + int result = 0; + enum ipa3_usb_transport_type ttype; + + ttype = IPA3_USB_GET_TTYPE(teth_prot); + + IPA_USB_DBG_LOW("entry\n"); + + /* Reset DL channel */ + result = ipa3_reset_gsi_channel(dl_clnt_hdl); + if (result) { + IPA_USB_ERR("failed to reset DL channel.\n"); + return result; + } + + /* Reset DL event ring */ + result = ipa3_reset_gsi_event_ring(dl_clnt_hdl); + if (result) { + IPA_USB_ERR("failed to reset DL event ring.\n"); + return result; + } + + if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { + /* Reset UL channel */ + result = ipa3_reset_gsi_channel(ul_clnt_hdl); + if (result) { + IPA_USB_ERR("failed to reset UL channel.\n"); + return result; + } + + /* Reset UL event ring */ + result = ipa3_reset_gsi_event_ring(ul_clnt_hdl); + if (result) { + IPA_USB_ERR("failed to reset UL event ring.\n"); + return result; + } + } + + /* Change state to STOPPED */ + if (!ipa3_usb_set_state(IPA_USB_STOPPED, false, ttype)) + IPA_USB_ERR("failed to change state to stopped\n"); + + if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { + result = ipa3_usb_release_xdci_channel(ul_clnt_hdl, ttype); + if (result) { + IPA_USB_ERR("failed to release UL channel.\n"); + return result; + } + } + + result = ipa3_usb_release_xdci_channel(dl_clnt_hdl, ttype); + if (result) { + IPA_USB_ERR("failed to release DL channel.\n"); + return result; + } + + IPA_USB_DBG_LOW("exit\n"); + + return 0; +} + int ipa_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl, enum ipa_usb_teth_prot teth_prot) { @@ -2175,20 +2267,31 @@ int ipa_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl, mutex_lock(&ipa3_usb_ctx->general_mutex); IPA_USB_DBG_LOW("entry\n"); - if (ipa3_usb_check_disconnect_prot(teth_prot)) { - result = -EINVAL; - goto bad_params; - } ttype = IPA3_USB_GET_TTYPE(teth_prot); - if (!ipa3_usb_check_legal_op(IPA_USB_DISCONNECT, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_DISCONNECT, ttype)) { IPA_USB_ERR("Illegal operation.\n"); result = -EPERM; goto bad_params; } spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags); + if (ipa3_usb_ctx->ttype_ctx[ttype].state == + IPA_USB_SUSPENDED_NO_RWAKEUP) { + spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); + result = ipa_usb_xdci_dismiss_channels(ul_clnt_hdl, dl_clnt_hdl, + teth_prot); + mutex_unlock(&ipa3_usb_ctx->general_mutex); + return result; + } + + if (ipa3_usb_check_disconnect_prot(teth_prot)) { + spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); + result = -EINVAL; + goto bad_params; + } + if (ipa3_usb_ctx->ttype_ctx[ttype].state != IPA_USB_SUSPENDED) { spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); /* Stop DL/DPL channel */ @@ -2227,53 +2330,10 @@ int ipa_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl, } else spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); - /* Reset DL channel */ - result = ipa3_reset_gsi_channel(dl_clnt_hdl); - if (result) { - IPA_USB_ERR("failed to reset DL channel.\n"); - goto bad_params; - } - - /* Reset DL event ring */ - result = ipa3_reset_gsi_event_ring(dl_clnt_hdl); - if (result) { - IPA_USB_ERR("failed to reset DL event ring.\n"); - goto bad_params; - } - - if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { - /* Reset UL channel */ - result = ipa3_reset_gsi_channel(ul_clnt_hdl); - if (result) { - IPA_USB_ERR("failed to reset UL channel.\n"); - goto bad_params; - } - - /* Reset UL event ring */ - result = ipa3_reset_gsi_event_ring(ul_clnt_hdl); - if (result) { - IPA_USB_ERR("failed to reset UL event ring.\n"); - goto bad_params; - } - } - - /* Change state to STOPPED */ - if (!ipa3_usb_set_state(IPA_USB_STOPPED, false, ttype)) - IPA_USB_ERR("failed to change state to stopped\n"); - - if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { - result = ipa3_usb_release_xdci_channel(ul_clnt_hdl, ttype); - if (result) { - IPA_USB_ERR("failed to release UL channel.\n"); - goto bad_params; - } - } - - result = ipa3_usb_release_xdci_channel(dl_clnt_hdl, ttype); - if (result) { - IPA_USB_ERR("failed to release DL channel.\n"); + result = ipa_usb_xdci_dismiss_channels(ul_clnt_hdl, dl_clnt_hdl, + teth_prot); + if (result) goto bad_params; - } /* Disconnect tethering protocol */ result = ipa3_usb_disconnect_teth_prot(teth_prot); @@ -2315,7 +2375,7 @@ int ipa_usb_deinit_teth_prot(enum ipa_usb_teth_prot teth_prot) ttype = IPA3_USB_GET_TTYPE(teth_prot); - if (!ipa3_usb_check_legal_op(IPA_USB_DEINIT_TETH_PROT, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_DEINIT_TETH_PROT, ttype)) { IPA_USB_ERR("Illegal operation.\n"); result = -EPERM; goto bad_params; @@ -2411,25 +2471,104 @@ bad_params: } EXPORT_SYMBOL(ipa_usb_deinit_teth_prot); -int ipa_usb_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl, +/* Assumes lock already acquired */ +static int ipa3_usb_suspend_no_remote_wakeup(u32 ul_clnt_hdl, u32 dl_clnt_hdl, enum ipa_usb_teth_prot teth_prot) { int result = 0; + enum ipa3_usb_transport_type ttype; + + ttype = IPA3_USB_GET_TTYPE(teth_prot); + + if (!ipa3_usb_check_legal_op(IPA_USB_OP_SUSPEND_NO_RWAKEUP, ttype)) { + IPA_USB_ERR("Illegal operation.\n"); + result = -EPERM; + goto fail_exit; + } + + IPA_USB_DBG("Start suspend with no remote wakeup sequence: %s\n", + IPA3_USB_IS_TTYPE_DPL(ttype) ? + "DPL channel":"Data Tethering channels"); + + if (ipa3_usb_check_disconnect_prot(teth_prot)) { + result = -EINVAL; + goto fail_exit; + } + + /* Stop DL/DPL channel */ + result = ipa3_xdci_disconnect(dl_clnt_hdl, false, -1); + if (result) { + IPA_USB_ERR("failed to disconnect DL/DPL channel.\n"); + goto fail_exit; + } + + if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { + /* Stop UL channel */ + result = ipa3_xdci_disconnect(ul_clnt_hdl, true, + ipa3_usb_ctx->qmi_req_id); + if (result) { + IPA_USB_ERR("failed disconnect UL channel\n"); + goto start_dl; + } + ipa3_usb_ctx->qmi_req_id++; + } + + /* Disconnect tethering protocol */ + result = ipa3_usb_disconnect_teth_prot(teth_prot); + if (result) + goto start_ul; + + result = ipa3_usb_release_prod(ttype); + if (result) { + IPA_USB_ERR("failed to release PROD.\n"); + goto connect_teth; + } + + /* Change ipa_usb state to SUSPENDED_NO_RWAKEUP */ + if (!ipa3_usb_set_state(IPA_USB_SUSPENDED_NO_RWAKEUP, false, ttype)) + IPA_USB_ERR("failed to change state to suspend no rwakeup\n"); + + IPA_USB_DBG_LOW("exit\n"); + return 0; + +connect_teth: + (void)ipa3_usb_connect_teth_prot(teth_prot); +start_ul: + if (!IPA3_USB_IS_TTYPE_DPL(ttype)) + (void)ipa3_xdci_connect(ul_clnt_hdl); +start_dl: + (void)ipa3_xdci_connect(dl_clnt_hdl); +fail_exit: + return result; +} + +int ipa_usb_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl, + enum ipa_usb_teth_prot teth_prot, bool with_remote_wakeup) +{ + int result = 0; unsigned long flags; enum ipa3_usb_cons_state curr_cons_state; enum ipa3_usb_transport_type ttype; mutex_lock(&ipa3_usb_ctx->general_mutex); IPA_USB_DBG_LOW("entry\n"); + if (teth_prot > IPA_USB_MAX_TETH_PROT_SIZE) { IPA_USB_ERR("bad parameters.\n"); result = -EINVAL; goto bad_params; } + if (!with_remote_wakeup) { + result = ipa3_usb_suspend_no_remote_wakeup(ul_clnt_hdl, + dl_clnt_hdl, teth_prot); + mutex_unlock(&ipa3_usb_ctx->general_mutex); + return result; + } + ttype = IPA3_USB_GET_TTYPE(teth_prot); - if (!ipa3_usb_check_legal_op(IPA_USB_SUSPEND, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_SUSPEND, ttype)) { IPA_USB_ERR("Illegal operation.\n"); result = -EPERM; goto bad_params; @@ -2538,6 +2677,72 @@ bad_params: } EXPORT_SYMBOL(ipa_usb_xdci_suspend); +/* Assumes lock already acquired */ +static int ipa3_usb_resume_no_remote_wakeup(u32 ul_clnt_hdl, u32 dl_clnt_hdl, + enum ipa_usb_teth_prot teth_prot) +{ + int result = -EFAULT; + enum ipa3_usb_transport_type ttype; + + ttype = IPA3_USB_GET_TTYPE(teth_prot); + + IPA_USB_DBG("Start resume with no remote wakeup sequence: %s\n", + IPA3_USB_IS_TTYPE_DPL(ttype) ? + "DPL channel":"Data Tethering channels"); + + /* Request USB_PROD */ + result = ipa3_usb_request_prod(ttype); + if (result) + goto fail_exit; + + /* Connect tethering protocol */ + result = ipa3_usb_connect_teth_prot(teth_prot); + if (result) { + IPA_USB_ERR("failed to connect teth protocol\n"); + goto release_prod; + } + + if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { + /* Start UL channel */ + result = ipa3_xdci_connect(ul_clnt_hdl); + if (result) { + IPA_USB_ERR("failed to start UL channel.\n"); + goto disconn_teth; + } + } + + /* Start DL/DPL channel */ + result = ipa3_xdci_connect(dl_clnt_hdl); + if (result) { + IPA_USB_ERR("failed to start DL/DPL channel.\n"); + goto stop_ul; + } + + /* Change state to CONNECTED */ + if (!ipa3_usb_set_state(IPA_USB_CONNECTED, false, ttype)) { + IPA_USB_ERR("failed to change state to connected\n"); + result = -EFAULT; + goto stop_dl; + } + + return 0; + +stop_dl: + (void)ipa3_xdci_disconnect(dl_clnt_hdl, false, -1); +stop_ul: + if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { + (void)ipa3_xdci_disconnect(ul_clnt_hdl, true, + ipa3_usb_ctx->qmi_req_id); + ipa3_usb_ctx->qmi_req_id++; + } +disconn_teth: + (void)ipa3_usb_disconnect_teth_prot(teth_prot); +release_prod: + (void)ipa3_usb_release_prod(ttype); +fail_exit: + return result; +} + int ipa_usb_xdci_resume(u32 ul_clnt_hdl, u32 dl_clnt_hdl, enum ipa_usb_teth_prot teth_prot) { @@ -2557,19 +2762,25 @@ int ipa_usb_xdci_resume(u32 ul_clnt_hdl, u32 dl_clnt_hdl, ttype = IPA3_USB_GET_TTYPE(teth_prot); - if (!ipa3_usb_check_legal_op(IPA_USB_RESUME, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_RESUME, ttype)) { IPA_USB_ERR("Illegal operation.\n"); result = -EPERM; goto bad_params; } - IPA_USB_DBG_LOW("Start resume sequence: %s\n", - IPA3_USB_IS_TTYPE_DPL(ttype) ? - "DPL channel" : "Data Tethering channels"); - spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags); prev_state = ipa3_usb_ctx->ttype_ctx[ttype].state; spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); + if (prev_state == IPA_USB_SUSPENDED_NO_RWAKEUP) { + result = ipa3_usb_resume_no_remote_wakeup(ul_clnt_hdl, + dl_clnt_hdl, teth_prot); + mutex_unlock(&ipa3_usb_ctx->general_mutex); + return result; + } + + IPA_USB_DBG("Start resume sequence: %s\n", + IPA3_USB_IS_TTYPE_DPL(ttype) ? + "DPL channel" : "Data Tethering channels"); /* Change state to RESUME_IN_PROGRESS */ if (!ipa3_usb_set_state(IPA_USB_RESUME_IN_PROGRESS, false, ttype)) { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index 8326c3fdd9d1..d176552a3533 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c @@ -61,7 +61,7 @@ int ipa3_enable_data_path(u32 clnt_hdl) !ipa3_should_pipe_be_suspended(ep->client))) { memset(&ep_cfg_ctrl, 0 , sizeof(ep_cfg_ctrl)); ep_cfg_ctrl.ipa_ep_suspend = false; - ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl); + res = ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl); } /* Assign the resource group for pipe */ @@ -101,7 +101,7 @@ int ipa3_disable_data_path(u32 clnt_hdl) if (IPA_CLIENT_IS_CONS(ep->client)) { memset(&ep_cfg_ctrl, 0 , sizeof(struct ipa_ep_cfg_ctrl)); ep_cfg_ctrl.ipa_ep_suspend = true; - ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl); + res = ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl); } udelay(IPA_PKT_FLUSH_TO_US); @@ -1311,7 +1311,46 @@ int ipa3_set_usb_max_packet_size( return 0; } -int ipa3_xdci_connect(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid) +int ipa3_xdci_connect(u32 clnt_hdl) +{ + int result; + struct ipa3_ep_context *ep; + + IPADBG("entry\n"); + + if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || + ipa3_ctx->ep[clnt_hdl].valid == 0) { + IPAERR("Bad parameter.\n"); + return -EINVAL; + } + + ep = &ipa3_ctx->ep[clnt_hdl]; + IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl)); + + result = ipa3_start_gsi_channel(clnt_hdl); + if (result) { + IPAERR("failed to start gsi channel clnt_hdl=%u\n", clnt_hdl); + goto exit; + } + + result = ipa3_enable_data_path(clnt_hdl); + if (result) { + IPAERR("enable data path failed res=%d clnt_hdl=%d.\n", result, + clnt_hdl); + goto stop_ch; + } + + IPADBG("exit\n"); + goto exit; + +stop_ch: + (void)ipa3_stop_gsi_channel(clnt_hdl); +exit: + IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl)); + return result; +} + +int ipa3_xdci_start(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid) { struct ipa3_ep_context *ep; int result = -EFAULT; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index 33be22f98b9d..96cd550d7ef6 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -1482,7 +1482,9 @@ int ipa3_reset_gsi_event_ring(u32 clnt_hdl); int ipa3_set_usb_max_packet_size( enum ipa_usb_max_usb_packet_size usb_max_packet_size); -int ipa3_xdci_connect(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid); +int ipa3_xdci_start(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid); + +int ipa3_xdci_connect(u32 clnt_hdl); int ipa3_xdci_disconnect(u32 clnt_hdl, bool should_force_clear, u32 qmi_req_id); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index c0a6e8b00d71..4ea68ae1e95c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -3522,7 +3522,7 @@ int ipa3_stop_gsi_channel(u32 clnt_hdl) goto end_sequence; IPADBG("Inject a DMA_TASK with 1B packet to IPA and retry\n"); - /* Send a 1B packet DMA_RASK to IPA and try again*/ + /* Send a 1B packet DMA_TASK to IPA and try again */ res = ipa3_inject_dma_task_for_gsi(); if (res) { IPAERR("Failed to inject DMA TASk for GSI\n"); |