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

Commit 98e95994 authored by Subramanian Ananthanarayanan's avatar Subramanian Ananthanarayanan Committed by Gauri Joshi
Browse files

msm: mhi: Send Ch cmd ack in async fashion



The CH completion commands are sent in async fashion, whereas the ch
cmd ack event is send in a sync fashion. This can lead to the host
receiving completions post the reset ch ack. These ch completion
events post ch reset would be treated as OOS events.
Making changes to send cmd ack events in async fashion to maintain the
ordering that is being sent to the host.

Change-Id: Ib2d568ea1d69486f3d1ebf4e7b209bba3315eba5
Signed-off-by: default avatarSubramanian Ananthanarayanan <skananth@codeaurora.org>
Signed-off-by: default avatarGauri Joshi <gaurjosh@codeaurora.org>
parent c728c09e
Loading
Loading
Loading
Loading
+181 −28
Original line number Diff line number Diff line
@@ -87,6 +87,10 @@ static int mhi_dev_alloc_evt_buf_evt_req(struct mhi_dev *mhi,
static int mhi_dev_schedule_msi_ipa(struct mhi_dev *mhi,
		struct event_req *ereq);
static void mhi_dev_event_msi_cb(void *req);
static void mhi_dev_cmd_event_msi_cb(void *req);

static int mhi_dev_alloc_cmd_ack_buf_req(struct mhi_dev *mhi);


static struct mhi_dev_uevent_info channel_state_info[MHI_MAX_CHANNELS];
static DECLARE_COMPLETION(read_from_host);
@@ -262,7 +266,7 @@ static int mhi_dev_schedule_msi_ipa(struct mhi_dev *mhi, struct event_req *ereq)
{
	struct ep_pcie_msi_config cfg;
	struct mhi_addr msi_addr;
	struct mhi_dev_channel *ch = ereq->context;
	struct mhi_dev_channel *ch;
	uint64_t evnt_ring_idx = mhi->ev_ring_start + ereq->event_ring;
	struct mhi_dev_ring *ring = &mhi->ring[evnt_ring_idx];
	union mhi_dev_ring_ctx *ctx;
@@ -283,14 +287,23 @@ static int mhi_dev_schedule_msi_ipa(struct mhi_dev *mhi, struct event_req *ereq)
	msi_addr.phy_addr = ring->msi_buf_dma_handle;

	ereq->event_type = SEND_MSI;
	if (!ereq->is_cmd_cpl) {
		ch = ereq->context;
		ereq->msi_cb = mhi_dev_event_msi_cb;

		ch->msi_cnt++;

		mhi_log(MHI_MSG_VERBOSE,
			"Sending MSI %d to 0x%llx as data = 0x%x for ch %d msi_count %d, ereq flush_num %d\n",
		ctx->ev.msivec, msi_addr.host_pa, *ring->msi_buf, ch->ch_id,
			ctx->ev.msivec, msi_addr.host_pa,
			*ring->msi_buf, ch->ch_id,
			ch->msi_cnt, ereq->flush_num);
	} else {
		ereq->msi_cb = mhi_dev_cmd_event_msi_cb;
		mhi_log(MHI_MSG_VERBOSE,
			"Sending MSI %d to 0x%llx as data = 0x%x for cmd ack, ereq flush_num %d\n",
			ctx->ev.msivec, msi_addr.host_pa, *ring->msi_buf,
			ereq->flush_num);
	}

	mhi_ctx->write_to_host(mhi, &msi_addr, ereq, MHI_DEV_DMA_ASYNC);

	return 0;
@@ -314,6 +327,27 @@ static void mhi_dev_event_rd_offset_completion_cb(void *req)
		sizeof(uint64_t), DMA_TO_DEVICE);
}

static void mhi_dev_cmd_event_msi_cb(void *req)
{
	struct mhi_cmd_cmpl_ctx *cmd_ctx;
	struct mhi_dev *mhi;
	struct event_req *ereq = req;
	unsigned long flags;

	mhi_log(MHI_MSG_VERBOSE, "MSI completed for flush req %d\n",
		ereq->flush_num);

	/*Cmd completion handling*/
	mhi = ereq->context;
	cmd_ctx = mhi->cmd_ctx;
	cmd_ctx->cmd_buf_wp += ereq->num_events;
	if (cmd_ctx->cmd_buf_wp == NUM_CMD_EVENTS_DEFAULT)
		cmd_ctx->cmd_buf_wp = 0;
	spin_lock_irqsave(&mhi->lock, flags);
	list_add_tail(&ereq->list, &cmd_ctx->cmd_req_buffers);
	spin_unlock_irqrestore(&mhi->lock, flags);
}

static void mhi_dev_event_msi_cb(void *req)
{
	struct event_req *ereq = req;
@@ -401,7 +435,8 @@ static int mhi_trigger_msi_edma(struct mhi_dev_ring *ring, u32 idx)
}

static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring,
		struct event_req *ereq, uint32_t evt_len)
		struct event_req *ereq, uint32_t evt_len,
		enum mhi_dev_tr_compl_evt_type event_type)
{
	int rc = 0;
	uint64_t evnt_ring_idx = mhi->ev_ring_start + evnt_ring;
@@ -430,25 +465,25 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring,
			return rc;
		}
	}
	ch = ereq->context;
	/* Check the limits of the buffer to be flushed */
	if (ereq->tr_events < ch->tr_events ||
		(ereq->tr_events + ereq->num_events) >
		(ch->tr_events + ch->evt_buf_size)) {
		pr_err("%s: Invalid completion event buffer!\n", __func__);
		mhi_log(MHI_MSG_ERROR,
			"Invalid cmpl evt buf - start %pK, end %pK\n",
			ereq->tr_events, ereq->tr_events + ereq->num_events);
		return -EINVAL;
	}


	mutex_lock(&ring->event_lock);
	mhi_log(MHI_MSG_VERBOSE, "Flushing %d cmpl events of ch %d\n",
			ereq->num_events, ch->ch_id);

	/* add the events */
	ereq->client_cb = mhi_dev_event_buf_completion_cb;
	ereq->is_cmd_cpl = (event_type == SEND_CMD_CMP) ? true:false;
	ereq->event_type = SEND_EVENT_BUFFER;

	if (!ereq->is_cmd_cpl) {
		ch = ereq->context;
		mhi_log(MHI_MSG_VERBOSE, "Flushing %d cmpl events of ch %d\n",
				ereq->num_events, ch->ch_id);
	} else {
		mhi_log(MHI_MSG_VERBOSE,
			"Flushing %d cmpl events of cmd ring\n",
			ereq->num_events);
	}

	rc = mhi_dev_add_element(ring, ereq->tr_events, ereq, evt_len);
	if (rc) {
		pr_err("%s(): error in adding element rc %d\n", __func__, rc);
@@ -506,6 +541,55 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring,
	return rc;
}

static int mhi_dev_flush_cmd_completion_events(struct mhi_dev *mhi,
		union mhi_dev_ring_element_type *el)
{
	struct mhi_cmd_cmpl_ctx *cmd_ctx = mhi->cmd_ctx;
	unsigned long flags;
	struct event_req *flush_ereq;
	union mhi_dev_ring_element_type *compl_ev;
	int rc = 0;

	/*cmd completions are sent on event ring 0 always*/
	if (!mhi->cmd_ctx) {
		if (mhi_dev_alloc_cmd_ack_buf_req(mhi)) {
			mhi_log(MHI_MSG_ERROR, "Alloc cmd ack buff failed");
			return -ENOMEM;
		}
	}
	cmd_ctx = mhi->cmd_ctx;
	if (list_empty(&cmd_ctx->cmd_req_buffers)) {
		mhi_log(MHI_MSG_ERROR, "cmd req buff list is empty");
		return -ENOMEM;
	}

	spin_lock_irqsave(&mhi->lock, flags);
	flush_ereq = container_of(cmd_ctx->cmd_req_buffers.next,
					struct event_req, list);
	list_del_init(&flush_ereq->list);
	flush_ereq->context = mhi;
	spin_unlock_irqrestore(&mhi->lock, flags);

	compl_ev = cmd_ctx->cmd_events + cmd_ctx->cmd_buf_rp;
	memcpy(compl_ev, el, sizeof(union mhi_dev_ring_element_type));
	cmd_ctx->cmd_buf_rp++;
	if (cmd_ctx->cmd_buf_rp == NUM_CMD_EVENTS_DEFAULT)
		cmd_ctx->cmd_buf_rp = 0;
	flush_ereq->tr_events = compl_ev;
	rc = mhi_dev_send_multiple_tr_events(mhi,
				0,
				flush_ereq,
				(1 *
				sizeof(union mhi_dev_ring_element_type)),
				SEND_CMD_CMP);
	if (rc) {
		mhi_log(MHI_MSG_ERROR, "failed to send compl evts\n");
		return rc;
	}

	return rc;
}

static int mhi_dev_flush_transfer_completion_events(struct mhi_dev *mhi,
		struct mhi_dev_channel *ch)
{
@@ -539,11 +623,25 @@ static int mhi_dev_flush_transfer_completion_events(struct mhi_dev *mhi,
		flush_ereq->flush_num = ch->flush_req_cnt;
		mhi_log(MHI_MSG_DBG, "Flush num %d called for ch %d\n",
			ch->flush_req_cnt, ch->ch_id);

		/* Check the limits of the buffer to be flushed */
		if (flush_ereq->tr_events < ch->tr_events ||
			(flush_ereq->tr_events + flush_ereq->num_events) >
			(ch->tr_events + ch->evt_buf_size)) {
			pr_err("%s: Invalid completion event buffer!\n",
				__func__);
			mhi_log(MHI_MSG_ERROR,
				"Invalid cmpl evt buf - start %pK, end %pK\n",
				flush_ereq->tr_events,
				flush_ereq->tr_events + flush_ereq->num_events);
			return -EINVAL;
		}
		rc = mhi_dev_send_multiple_tr_events(mhi,
				mhi->ch_ctx_cache[ch->ch_id].err_indx,
				flush_ereq,
				(flush_ereq->num_events *
				sizeof(union mhi_dev_ring_element_type)));
				sizeof(union mhi_dev_ring_element_type)),
				SEND_EVENT_BUFFER);
		if (rc) {
			mhi_log(MHI_MSG_ERROR, "failed to send compl evts\n");
			break;
@@ -1608,7 +1706,7 @@ int mhi_dev_send_state_change_event(struct mhi_dev *mhi,
	event.evt_state_change.type = MHI_DEV_RING_EL_MHI_STATE_CHG;
	event.evt_state_change.mhistate = state;

	return mhi_dev_send_event(mhi, 0, &event);
	return mhi_dev_flush_cmd_completion_events(mhi, &event);
}
EXPORT_SYMBOL(mhi_dev_send_state_change_event);

@@ -1619,7 +1717,7 @@ int mhi_dev_send_ee_event(struct mhi_dev *mhi, enum mhi_dev_execenv exec_env)
	event.evt_ee_state.type = MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY;
	event.evt_ee_state.execenv = exec_env;

	return mhi_dev_send_event(mhi, 0, &event);
	return mhi_dev_flush_cmd_completion_events(mhi, &event);
}
EXPORT_SYMBOL(mhi_dev_send_ee_event);

@@ -1669,7 +1767,7 @@ static int mhi_dev_send_cmd_comp_event(struct mhi_dev *mhi,
			(size_t) event.evt_cmd_comp.ptr);
	event.evt_cmd_comp.type = MHI_DEV_RING_EL_CMD_COMPLETION_EVT;
	event.evt_cmd_comp.code = code;
	return mhi_dev_send_event(mhi, 0, &event);
	return mhi_dev_flush_cmd_completion_events(mhi, &event);
}

static int mhi_dev_process_stop_cmd(struct mhi_dev_ring *ring, uint32_t ch_id,
@@ -1852,7 +1950,7 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi,
				event.evt_cmd_comp.code =
					MHI_CMD_COMPL_CODE_UNDEFINED;

			rc = mhi_dev_send_event(mhi, 0, &event);
			rc = mhi_dev_flush_cmd_completion_events(mhi, &event);
			if (rc) {
				pr_err("stop event send failed\n");
				return;
@@ -1912,7 +2010,7 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi,
				event.evt_cmd_comp.code =
					MHI_CMD_COMPL_CODE_UNDEFINED;

			rc = mhi_dev_send_event(mhi, 0, &event);
			rc = mhi_dev_flush_cmd_completion_events(mhi, &event);
			if (rc) {
				pr_err("stop event send failed\n");
				return;
@@ -2723,6 +2821,61 @@ static uint32_t mhi_dev_get_evt_ring_size(struct mhi_dev *mhi, uint32_t ch_id)
		mhi->ch_ctx_cache[ch_id].err_indx].ring_size;
}

static int mhi_dev_alloc_cmd_ack_buf_req(struct mhi_dev *mhi)
{
	int rc = 0;
	uint32_t i;
	struct mhi_cmd_cmpl_ctx *cmd_ctx;
	union mhi_dev_ring_element_type *cmd_events;

	mhi->cmd_ctx = kmalloc(sizeof(struct mhi_cmd_cmpl_ctx),
					GFP_KERNEL);
	if (!mhi->cmd_ctx)
		return -ENOMEM;

	cmd_ctx = mhi->cmd_ctx;
	/* Allocate event requests */
	cmd_ctx->ereqs = kcalloc(NUM_CMD_EVENTS_DEFAULT,
						sizeof(*cmd_ctx->ereqs),
						GFP_KERNEL);
	if (!cmd_ctx->ereqs) {
		goto free_ereqs;
		return -ENOMEM;
	}

	/* Allocate buffers to queue transfer completion events */
	cmd_ctx->cmd_events = kcalloc(NUM_CMD_EVENTS_DEFAULT,
							sizeof(*cmd_events),
							GFP_KERNEL);
	if (!cmd_ctx->cmd_events) {
		rc = -ENOMEM;
		goto free_ereqs;
	}

	/* Organize event flush requests into a linked list */
	INIT_LIST_HEAD(&cmd_ctx->cmd_req_buffers);

	for (i = 0; i < NUM_CMD_EVENTS_DEFAULT; ++i) {
		list_add_tail(&cmd_ctx->ereqs[i].list,
					&cmd_ctx->cmd_req_buffers);
	}

	/*
	 * Initialize cmpl event buffer indexes - cmd_buf_rp and
	 * cmd_buf_wp point to the first and last free index available.
	 */
	cmd_ctx->cmd_buf_rp = 0;
	cmd_ctx->cmd_buf_wp = NUM_CMD_EVENTS_DEFAULT - 1;

	return 0;
free_ereqs:
		kfree(mhi->cmd_ctx);
		kfree(cmd_ctx->ereqs);
		cmd_ctx->ereqs = NULL;
		mhi->cmd_ctx = NULL;
		return rc;
}

static int mhi_dev_alloc_evt_buf_evt_req(struct mhi_dev *mhi,
		struct mhi_dev_channel *ch, struct mhi_dev_ring *evt_ring)
{
+17 −1
Original line number Diff line number Diff line
@@ -271,6 +271,8 @@ struct mhi_config {

/* maximum transfer completion events buffer */
#define NUM_TR_EVENTS_DEFAULT			128
#define NUM_CMD_EVENTS_DEFAULT			20


/* Set flush threshold to 80% of event buf size */
#define MHI_CMPL_EVT_FLUSH_THRSHLD(n) ((n * 8) / 10)
@@ -354,7 +356,8 @@ enum mhi_dev_ch_operation {
enum mhi_dev_tr_compl_evt_type {
	SEND_EVENT_BUFFER,
	SEND_EVENT_RD_OFFSET,
	SEND_MSI
	SEND_MSI,
	SEND_CMD_CMP,
};

enum mhi_dev_transfer_type {
@@ -448,6 +451,18 @@ struct event_req {
	void			(*msi_cb)(void *req);
	struct list_head	list;
	u32			flush_num;
	bool		is_cmd_cpl;
};

struct mhi_cmd_cmpl_ctx {
	/* Indices for completion event buffer */
	uint32_t			cmd_buf_rp;
	uint32_t			cmd_buf_wp;
	uint32_t			cmd_buf_size;
	bool				mem_allocated;
	struct list_head	cmd_req_buffers;
	struct event_req		*ereqs;
	union mhi_dev_ring_element_type *cmd_events;
};

struct mhi_dev_channel {
@@ -531,6 +546,7 @@ struct mhi_dev {
	struct mhi_dev_ring		*ring;
	int				mhi_irq;
	struct mhi_dev_channel		*ch;
	struct mhi_cmd_cmpl_ctx			*cmd_ctx;

	int				ctrl_int;
	int				cmd_int;