Merge "msm: ipa3: Support IPA-USB suspend sequence without remote wake-up"

This commit is contained in:
Linux Build Service Account 2016-10-31 06:59:10 -07:00 committed by Gerrit - the friendly Code Review server
commit 9178072f99
6 changed files with 408 additions and 152 deletions

View file

@ -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");
result = ipa_usb_xdci_dismiss_channels(ul_clnt_hdl, dl_clnt_hdl,
teth_prot);
if (result)
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");
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,8 +2471,79 @@ 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;
@ -2421,15 +2552,23 @@ int ipa_usb_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
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)) {

View file

@ -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;

View file

@ -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);

View file

@ -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");

View file

@ -497,7 +497,8 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
log_event_dbg("%s: Calling xdci_suspend", __func__);
ret = ipa_usb_xdci_suspend(gsi->d_port.out_channel_handle,
gsi->d_port.in_channel_handle, gsi->prot_id);
gsi->d_port.in_channel_handle, gsi->prot_id,
true);
if (!ret) {
d_port->sm_state = STATE_SUSPENDED;

View file

@ -253,6 +253,7 @@ int ipa_usb_deinit_teth_prot(enum ipa_usb_teth_prot teth_prot);
* @dl_clnt_hdl: client handle previously obtained from
* ipa_usb_xdci_connect() for IN channel
* @teth_prot: tethering protocol
* @with_remote_wakeup: Does host support remote wakeup?
*
* Note: Should not be called from atomic context
* Note: for DPL, the ul will be ignored as irrelevant
@ -260,7 +261,8 @@ int ipa_usb_deinit_teth_prot(enum ipa_usb_teth_prot teth_prot);
* @Return 0 on success, negative on failure
*/
int ipa_usb_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
enum ipa_usb_teth_prot teth_prot);
enum ipa_usb_teth_prot teth_prot,
bool with_remote_wakeup);
/**
* ipa_usb_xdci_resume - Peripheral should call this function to resume
@ -313,7 +315,8 @@ static inline int ipa_usb_deinit_teth_prot(enum ipa_usb_teth_prot teth_prot)
}
static inline int ipa_usb_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
enum ipa_usb_teth_prot teth_prot)
enum ipa_usb_teth_prot teth_prot,
bool with_remote_wakeup)
{
return -EPERM;
}