Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93a61119 authored by Ghanim Fodi's avatar Ghanim Fodi Committed by Gerrit - the friendly Code Review server
Browse files

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



Today IPA-USB uses disconnect sequence when USB initiates
suspend without remote wake-up, and uses suspend sequence
when USB initiates suspend with remote wake-up.
In accordance with USB-GSI driver, changing this behavior
to use suspend sequence in both of the cases. This is to
overcome some USB H/W malfunction when using disconnect
sequence in case of suspend scenario.

Change-Id: Iba43e192905ab37d196aea4b5d00861b9cb7c347
CRs-Fixed: 1068949
Signed-off-by: default avatarGhanim Fodi <gfodi@codeaurora.org>
parent 7bb7d5d6
Loading
Loading
Loading
Loading
+355 −144
Original line number Diff line number Diff line
@@ -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,8 +2471,79 @@ int ipa_usb_deinit_teth_prot(enum ipa_usb_teth_prot teth_prot)
}
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 @@ int ipa_usb_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
}
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)) {
+42 −3
Original line number Diff line number Diff line
@@ -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 */
@@ -113,7 +113,7 @@ int ipa3_disable_data_path(u32 clnt_hdl)

		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);
@@ -1322,7 +1322,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;
+3 −1

File changed.

Preview size limit exceeded, changes collapsed.

+1 −1

File changed.

Preview size limit exceeded, changes collapsed.

+5 −2

File changed.

Preview size limit exceeded, changes collapsed.