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

Commit bf72f4d9 authored by Gauri Joshi's avatar Gauri Joshi
Browse files

msm: mhi_dev: stop reading host CH ring when CH is reset



The host can asynchronously reset the channel and this can happen when
there are pending ch interrupts to process on the EP.

The change is to prevent processing the old DB rung by the host by
adding delaying channel reset cmd ack.

Change-Id: I020a9961196867a7cc1ac8e4f309d0a75c38f6f5
Signed-off-by: default avatarSubramanian Ananthanarayanan <skananth@codeaurora.org>
Signed-off-by: default avatarGauri Joshi <gaurjosh@codeaurora.org>
parent 24bf1e88
Loading
Loading
Loading
Loading
+81 −35
Original line number Diff line number Diff line
@@ -1829,6 +1829,44 @@ static int mhi_dev_process_stop_cmd(struct mhi_dev_ring *ring, uint32_t ch_id,
					MHI_CMD_COMPL_CODE_SUCCESS);
}

static void mhi_dev_process_reset_cmd(struct mhi_dev *mhi, int ch_id)
{
	int rc = 0;
	struct mhi_dev_channel *ch;
	struct mhi_addr host_addr;

	ch = &mhi->ch[ch_id];
	/* hard stop and set the channel to stop */
	mhi->ch_ctx_cache[ch_id].ch_state =
				MHI_DEV_CH_STATE_DISABLED;
	mhi->ch[ch_id].state = MHI_DEV_CH_STOPPED;

	if (mhi->use_ipa)
		host_addr.host_pa =
			mhi->ch_ctx_shadow.host_pa +
			(sizeof(struct mhi_dev_ch_ctx) * ch_id);
	else
		host_addr.device_va =
			mhi->ch_ctx_shadow.device_va +
			(sizeof(struct mhi_dev_ch_ctx) * ch_id);

	host_addr.virt_addr =
			&mhi->ch_ctx_cache[ch_id].ch_state;
	host_addr.size = sizeof(enum mhi_dev_ch_ctx_state);

	/* update the channel state in the host */
	mhi_ctx->write_to_host(mhi, &host_addr, NULL,
			MHI_DEV_DMA_SYNC);

	/* send the completion event to the host */
	rc = mhi_dev_send_cmd_comp_event(mhi,
				MHI_CMD_COMPL_CODE_SUCCESS);
	if (rc)
		pr_err("Error sending command completion event\n");

	ch->reset_pending = false;
}

static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi,
			union mhi_dev_ring_element_type *el, void *ctx)
{
@@ -2046,38 +2084,19 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi,
				pr_err("Channel not opened for %d\n", ch_id);
				return;
			}

			ch = &mhi->ch[ch_id];

			mutex_lock(&ch->ch_lock);
			mutex_lock(&ch->ring->event_lock);

			/* hard stop and set the channel to stop */
			mhi->ch_ctx_cache[ch_id].ch_state =
						MHI_DEV_CH_STATE_DISABLED;
			mhi->ch[ch_id].state = MHI_DEV_CH_STOPPED;
			if (MHI_USE_DMA(mhi))
				host_addr.host_pa =
					mhi->ch_ctx_shadow.host_pa +
					(sizeof(struct mhi_dev_ch_ctx) * ch_id);
			else
				host_addr.device_va =
					mhi->ch_ctx_shadow.device_va +
					(sizeof(struct mhi_dev_ch_ctx) * ch_id);

			host_addr.virt_addr =
					&mhi->ch_ctx_cache[ch_id].ch_state;
			host_addr.size = sizeof(enum mhi_dev_ch_ctx_state);

			/* update the channel state in the host */
			mhi_ctx->write_to_host(mhi, &host_addr, NULL,
					MHI_DEV_DMA_SYNC);

			/* send the completion event to the host */
			rc = mhi_dev_send_cmd_comp_event(mhi,
						MHI_CMD_COMPL_CODE_SUCCESS);
			if (rc)
				pr_err("Error sending command completion event\n");
			if (ch->db_pending) {
				mhi_log(MHI_MSG_ERROR,
				"skipping reset cmd ack for channel %d\n",
						ch_id);
				ch->reset_pending = true;
				mutex_unlock(&ch->ring->event_lock);
				mutex_unlock(&ch->ch_lock);
				return;
			}
			mhi_dev_process_reset_cmd(mhi, ch_id);
			mutex_unlock(&ch->ring->event_lock);
			mutex_unlock(&ch->ch_lock);
			mhi_update_state_info_ch(ch_id, MHI_STATE_DISCONNECTED);
@@ -2126,9 +2145,10 @@ static void mhi_dev_process_ring_pending(struct work_struct *work)
	struct list_head *cp, *q;
	struct mhi_dev_ring *ring;
	struct mhi_dev_channel *ch;
	int rc = 0;
	int rc = 0, ch_id;

	mutex_lock(&mhi_ctx->mhi_lock);

	rc = mhi_dev_process_ring(&mhi->ring[mhi->cmd_ring_idx]);
	if (rc) {
		mhi_log(MHI_MSG_ERROR, "error processing command ring\n");
@@ -2139,26 +2159,47 @@ static void mhi_dev_process_ring_pending(struct work_struct *work)
		ring = list_entry(cp, struct mhi_dev_ring, list);
		list_del(cp);
		mhi_log(MHI_MSG_VERBOSE, "processing ring %d\n", ring->id);

		if (ring->id < mhi->ch_ring_start) {
			mhi_log(MHI_MSG_ERROR,
				"ring (%d) is not a channel ring\n", ring->id);
			goto exit;
		}

		ch = &mhi->ch[ring->id - mhi->ch_ring_start];
		mutex_lock(&ch->ch_lock);

		rc = mhi_dev_process_ring(ring);
		if (rc) {
			mhi_log(MHI_MSG_ERROR,
				"error processing ring %d\n", ring->id);
			mutex_unlock(&ch->ch_lock);
			goto exit;
		}
		ch->db_pending = false;

		if (ring->id < mhi->ch_ring_start) {
			mhi_log(MHI_MSG_ERROR,
				"ring (%d) is not a channel ring\n", ring->id);
			goto exit;
		if (ch->reset_pending) {
			/*
			 * The channel might be reset asynchronously by the
			 * host, below reset ack is in  case the channel
			 * was stopped/reset with pending DB.
			 */
			ch_id = ch->ch_id;
			mhi_log(MHI_MSG_VERBOSE,
				"processing pending ch:%d reset\n", ch_id);
			mutex_lock(&ch->ring->event_lock);
			mhi_dev_process_reset_cmd(mhi, ch_id);
			mutex_unlock(&ch->ring->event_lock);
		}

		ch = &mhi->ch[ring->id - mhi->ch_ring_start];
		rc = mhi_dev_mmio_enable_chdb_a7(mhi, ch->ch_id);
		if (rc) {
			mhi_log(MHI_MSG_ERROR,
			"error enabling chdb interrupt for %d\n", ch->ch_id);
			mutex_unlock(&ch->ch_lock);
			goto exit;
		}
		mutex_unlock(&ch->ch_lock);
	}

exit:
@@ -2195,6 +2236,7 @@ static void mhi_dev_queue_channel_db(struct mhi_dev *mhi,
					uint32_t chintr_value, uint32_t ch_num)
{
	struct mhi_dev_ring *ring;
	struct mhi_dev_channel *ch;
	int rc = 0;

	for (; chintr_value; ch_num++, chintr_value >>= 1) {
@@ -2206,6 +2248,10 @@ static void mhi_dev_queue_channel_db(struct mhi_dev *mhi,
			}
			mhi_ring_set_state(ring, RING_STATE_PENDING);
			list_add(&ring->list, &mhi->process_ring_list);
			ch = &mhi->ch[ch_num];
			mutex_lock(&ch->ch_lock);
			ch->db_pending = true;
			mutex_unlock(&ch->ch_lock);
			rc = mhi_dev_mmio_disable_chdb_a7(mhi, ch_num);
			if (rc) {
				pr_err("Error disabling chdb\n");
+2 −0
Original line number Diff line number Diff line
@@ -509,6 +509,8 @@ struct mhi_dev_channel {
	uint32_t			msi_cnt;
	uint32_t			flush_req_cnt;
	bool				skip_td;
	bool				db_pending;
	bool				reset_pending;
};

/* Structure device for mhi dev */