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;