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

Commit 181b7e52 authored by Michael Adisumarta's avatar Michael Adisumarta
Browse files

msm: ipa4: fix deadlock on suspend pipes



Fix a deadlock when IPA driver disables IPA clock.
The deadlock occurs when the disable clocks function
is trying to enable the clocks as part of pipe suspend.

Change-Id: I2fc6f2f80ca56ddaff572b0770d60bedf8da887b
CRs-Fixed: 2154685
Signed-off-by: default avatarMichael Adisumarta <madisuma@codeaurora.org>
parent b14f1bf3
Loading
Loading
Loading
Loading
+73 −69
Original line number Diff line number Diff line
@@ -4867,6 +4867,77 @@ static void ipa3_gsi_poll_after_suspend(struct ipa3_ep_context *ep)
	}
}

static int __ipa3_stop_gsi_channel(u32 clnt_hdl)
{
	struct ipa_mem_buffer mem;
	int res = 0;
	int i;
	struct ipa3_ep_context *ep;

	if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
		ipa3_ctx->ep[clnt_hdl].valid == 0) {
		IPAERR("bad parm.\n");
		return -EINVAL;
	}

	ep = &ipa3_ctx->ep[clnt_hdl];
	memset(&mem, 0, sizeof(mem));

	if (IPA_CLIENT_IS_PROD(ep->client)) {
		IPADBG("Calling gsi_stop_channel ch:%lu\n",
			ep->gsi_chan_hdl);
		res = gsi_stop_channel(ep->gsi_chan_hdl);
		IPADBG("gsi_stop_channel ch: %lu returned %d\n",
			ep->gsi_chan_hdl, res);
		return res;
	}

	for (i = 0; i < IPA_GSI_CHANNEL_STOP_MAX_RETRY; i++) {
		IPADBG("Calling gsi_stop_channel ch:%lu\n",
			ep->gsi_chan_hdl);
		res = gsi_stop_channel(ep->gsi_chan_hdl);
		IPADBG("gsi_stop_channel ch: %lu returned %d\n",
			ep->gsi_chan_hdl, res);
		if (res != -GSI_STATUS_AGAIN && res != -GSI_STATUS_TIMED_OUT)
			return res;

		IPADBG("Inject a DMA_TASK with 1B packet to IPA\n");
		/* 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");
			return res;
		}

		/* sleep for short period to flush IPA */
		usleep_range(IPA_GSI_CHANNEL_STOP_SLEEP_MIN_USEC,
			IPA_GSI_CHANNEL_STOP_SLEEP_MAX_USEC);
	}

	IPAERR("Failed  to stop GSI channel with retries\n");
	return -EFAULT;
}

/**
 * ipa3_stop_gsi_channel()- Stops a GSI channel in IPA
 * @chan_hdl: GSI channel handle
 *
 * This function implements the sequence to stop a GSI channel
 * in IPA. This function returns when the channel is in STOP state.
 *
 * Return value: 0 on success, negative otherwise
 */
int ipa3_stop_gsi_channel(u32 clnt_hdl)
{
	int res;

	IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));
	res = __ipa3_stop_gsi_channel(clnt_hdl);
	IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));

	return res;
}

void ipa3_suspend_apps_pipes(bool suspend)
{
	struct ipa_ep_cfg_ctrl cfg;
@@ -4889,7 +4960,7 @@ void ipa3_suspend_apps_pipes(bool suspend)
			ipa_ep_idx);
		if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
			if (suspend) {
				res = ipa3_stop_gsi_channel(ipa_ep_idx);
				res = __ipa3_stop_gsi_channel(ipa_ep_idx);
				if (res) {
					IPAERR("failed to stop LAN channel\n");
					ipa_assert();
@@ -4923,7 +4994,7 @@ void ipa3_suspend_apps_pipes(bool suspend)
			ipa_ep_idx);
		if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
			if (suspend) {
				res = ipa3_stop_gsi_channel(ipa_ep_idx);
				res = __ipa3_stop_gsi_channel(ipa_ep_idx);
				if (res) {
					IPAERR("failed to stop WAN channel\n");
					ipa_assert();
@@ -5013,73 +5084,6 @@ int ipa3_inject_dma_task_for_gsi(void)
	return 0;
}

/**
 * ipa3_stop_gsi_channel()- Stops a GSI channel in IPA
 * @chan_hdl: GSI channel handle
 *
 * This function implements the sequence to stop a GSI channel
 * in IPA. This function returns when the channel is is STOP state.
 *
 * Return value: 0 on success, negative otherwise
 */
int ipa3_stop_gsi_channel(u32 clnt_hdl)
{
	struct ipa_mem_buffer mem;
	int res = 0;
	int i;
	struct ipa3_ep_context *ep;

	if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
		ipa3_ctx->ep[clnt_hdl].valid == 0) {
		IPAERR("bad parm.\n");
		return -EINVAL;
	}

	ep = &ipa3_ctx->ep[clnt_hdl];

	IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));

	memset(&mem, 0, sizeof(mem));

	if (IPA_CLIENT_IS_PROD(ep->client)) {
		IPADBG("Calling gsi_stop_channel ch:%lu\n",
			ep->gsi_chan_hdl);
		res = gsi_stop_channel(ep->gsi_chan_hdl);
		IPADBG("gsi_stop_channel ch: %lu returned %d\n",
			ep->gsi_chan_hdl, res);
		goto end_sequence;
	}

	for (i = 0; i < IPA_GSI_CHANNEL_STOP_MAX_RETRY; i++) {
		IPADBG("Calling gsi_stop_channel ch:%lu\n",
			ep->gsi_chan_hdl);
		res = gsi_stop_channel(ep->gsi_chan_hdl);
		IPADBG("gsi_stop_channel ch: %lu returned %d\n",
			ep->gsi_chan_hdl, res);
		if (res != -GSI_STATUS_AGAIN && res != -GSI_STATUS_TIMED_OUT)
			goto end_sequence;

		IPADBG("Inject a DMA_TASK with 1B packet to IPA\n");
		/* 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");
			goto end_sequence;
		}

		/* sleep for short period to flush IPA */
		usleep_range(IPA_GSI_CHANNEL_STOP_SLEEP_MIN_USEC,
			IPA_GSI_CHANNEL_STOP_SLEEP_MAX_USEC);
	}

	IPAERR("Failed  to stop GSI channel with retries\n");
	res = -EFAULT;
end_sequence:
	IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));

	return res;
}

static int ipa3_load_single_fw(const struct firmware *firmware,
	const struct elf32_phdr *phdr)
{