From 98e95994cc5e2e684c450b0ba69203a355ca24a2 Mon Sep 17 00:00:00 2001 From: Subramanian Ananthanarayanan Date: Mon, 2 Nov 2020 14:51:05 +0530 Subject: [PATCH] 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: Subramanian Ananthanarayanan Signed-off-by: Gauri Joshi --- drivers/platform/msm/mhi_dev/mhi.c | 209 +++++++++++++++++++++++++---- drivers/platform/msm/mhi_dev/mhi.h | 18 ++- 2 files changed, 198 insertions(+), 29 deletions(-) diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c index 4f5bfc135cbd..0eeef414892c 100644 --- a/drivers/platform/msm/mhi_dev/mhi.c +++ b/drivers/platform/msm/mhi_dev/mhi.c @@ -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; - ereq->msi_cb = mhi_dev_event_msi_cb; + 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, + 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); + } - 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, - ch->msi_cnt, 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 @@ send_start_completion_event: 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 @@ send_start_completion_event: 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) { diff --git a/drivers/platform/msm/mhi_dev/mhi.h b/drivers/platform/msm/mhi_dev/mhi.h index 10e9ca3145dc..7fec5e68a001 100644 --- a/drivers/platform/msm/mhi_dev/mhi.h +++ b/drivers/platform/msm/mhi_dev/mhi.h @@ -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;