Loading drivers/platform/msm/mhi_dev/mhi.c +140 −53 Original line number Diff line number Diff line Loading @@ -476,6 +476,22 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring, if (!ereq->is_cmd_cpl) { ch = ereq->context; /* * Take Channel ring event lock to prevent sending * completion command while the channel is getting * reset/stopped. * Abort sending completion event if channel has moved to * stopped state. */ mutex_lock(&ch->ring->event_lock); if (ch->state == MHI_DEV_CH_STOPPED || ch->state == MHI_DEV_CH_PENDING_STOP) { mhi_log(MHI_MSG_ERROR, "Ch:%d is in %d state, abort sending completion evnt\n" , ch->ch_id, ch->state); rc = -ENXIO; goto exit; } mhi_log(MHI_MSG_VERBOSE, "Flushing %d cmpl events of ch %d\n", ereq->num_events, ch->ch_id); } else { Loading @@ -487,8 +503,7 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring, 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); mutex_unlock(&ring->event_lock); return rc; goto exit; } ring->ring_ctx_shadow->ev.rp = (ring->rd_offset * Loading Loading @@ -537,6 +552,9 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring, pr_err("%s: error sending in msi\n", __func__); } exit: if (!ereq->is_cmd_cpl) mutex_unlock(&ch->ring->event_lock); mutex_unlock(&ring->event_lock); return rc; } Loading Loading @@ -597,18 +615,21 @@ static int mhi_dev_flush_transfer_completion_events(struct mhi_dev *mhi, unsigned long flags; struct event_req *flush_ereq; do { /* * Channel got stopped or closed with transfers pending * Do not send completion events to host */ if (ch->state == MHI_DEV_CH_CLOSED || ch->state == MHI_DEV_CH_STOPPED) { mhi_log(MHI_MSG_DBG, "Ch %d closed with %d writes pending\n", mhi_log(MHI_MSG_DBG, "Ch %d closed with %d writes pending\n", ch->ch_id, ch->pend_wr_count + 1); return -ENODEV; rc = -ENODEV; break; } do { spin_lock_irqsave(&mhi->lock, flags); if (list_empty(&ch->flush_event_req_buffers)) { spin_unlock_irqrestore(&mhi->lock, flags); Loading @@ -634,7 +655,8 @@ static int mhi_dev_flush_transfer_completion_events(struct mhi_dev *mhi, "Invalid cmpl evt buf - start %pK, end %pK\n", flush_ereq->tr_events, flush_ereq->tr_events + flush_ereq->num_events); return -EINVAL; rc = -EINVAL; break; } rc = mhi_dev_send_multiple_tr_events(mhi, mhi->ch_ctx_cache[ch->ch_id].err_indx, Loading Loading @@ -1599,7 +1621,7 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring, } } mutex_lock(&mhi->mhi_event_lock); mutex_lock(&ring->event_lock); /* add the ring element */ mhi_dev_add_element(ring, el, NULL, 0); Loading Loading @@ -1632,7 +1654,6 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring, */ wmb(); mutex_unlock(&mhi->mhi_event_lock); mhi_log(MHI_MSG_VERBOSE, "event sent:\n"); mhi_log(MHI_MSG_VERBOSE, "evnt ptr : 0x%llx\n", el->evt_tr_comp.ptr); mhi_log(MHI_MSG_VERBOSE, "evnt len : 0x%x\n", el->evt_tr_comp.len); Loading @@ -1645,6 +1666,7 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring, else rc = ep_pcie_trigger_msi(mhi_ctx->phandle, ctx->ev.msivec); mutex_unlock(&ring->event_lock); return rc; } Loading Loading @@ -1811,6 +1833,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) { Loading Loading @@ -1972,6 +2032,7 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi, ch = &mhi->ch[ch_id]; mutex_lock(&ch->ch_lock); mutex_lock(&ch->ring->event_lock); mhi->ch[ch_id].state = MHI_DEV_CH_PENDING_STOP; rc = mhi_dev_process_stop_cmd( Loading @@ -1980,6 +2041,7 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi, if (rc) pr_err("stop event send failed\n"); mutex_unlock(&ch->ring->event_lock); mutex_unlock(&ch->ch_lock); mhi_update_state_info_ch(ch_id, MHI_STATE_DISCONNECTED); /* Trigger callback to clients */ Loading Loading @@ -2026,37 +2088,20 @@ 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); /* 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"); mutex_lock(&ch->ring->event_lock); 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); mhi_dev_trigger_cb(ch_id); Loading Loading @@ -2104,9 +2149,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"); Loading @@ -2117,26 +2163,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: Loading Loading @@ -2173,6 +2240,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) { Loading @@ -2184,6 +2252,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"); Loading Loading @@ -2361,6 +2433,7 @@ static void mhi_dev_transfer_completion_cb(void *mreq) /* Trigger client call back */ req->client_cb(req); mutex_lock(&ch->ch_lock); /* Flush read completions to host */ if (snd_cmpl && mhi_ctx->ch_ctx_cache[ch->ch_id].ch_type == MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL) { Loading @@ -2371,7 +2444,7 @@ static void mhi_dev_transfer_completion_cb(void *mreq) "Failed to flush read completions to host\n"); } } mutex_unlock(&ch->ch_lock); if (ch->state == MHI_DEV_CH_PENDING_STOP) { ch->state = MHI_DEV_CH_STOPPED; rc = mhi_dev_process_stop_cmd(ch->ring, ch->ch_id, mhi_ctx); Loading Loading @@ -3066,7 +3139,8 @@ void mhi_dev_close_channel(struct mhi_dev_client *handle) ch = handle->channel; do { if (ch->pend_wr_count) { if (ch->pend_wr_count && !list_empty(&ch->event_req_buffers)) { usleep_range(MHI_DEV_CH_CLOSE_TIMEOUT_MIN, MHI_DEV_CH_CLOSE_TIMEOUT_MAX); } else Loading @@ -3078,6 +3152,9 @@ void mhi_dev_close_channel(struct mhi_dev_client *handle) if (ch->pend_wr_count) mhi_log(MHI_MSG_ERROR, "%d writes pending for channel %d\n", ch->pend_wr_count, ch->ch_id); if (!list_empty(&ch->event_req_buffers)) mhi_log(MHI_MSG_ERROR, "%d pending flush for channel %d\n", ch->pend_wr_count, ch->ch_id); if (ch->state != MHI_DEV_CH_PENDING_START) if ((ch->ch_type == MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL && Loading @@ -3089,14 +3166,24 @@ void mhi_dev_close_channel(struct mhi_dev_client *handle) ch->state = MHI_DEV_CH_CLOSED; ch->active_client = NULL; kfree(ch->ereqs); mhi_log(MHI_MSG_INFO, "MEM_DEALLOC:ch:%d size:%d EREQ\n", ch->ch_id, ch->evt_req_size); kfree(ch->tr_events); mhi_log(MHI_MSG_INFO, "MEM_DEALLOC:ch:%d size:%d TR_EVENTS\n", ch->ch_id, ch->evt_buf_size); ch->evt_buf_size = 0; ch->evt_req_size = 0; ch->ereqs = NULL; ch->tr_events = NULL; kfree(handle); mhi_log(MHI_MSG_INFO, "MEM_ALLOC:ch:%d size:%d CLNT_HANDLE\n", ch->ch_id, sizeof(struct mhi_dev_client)); mutex_unlock(&ch->ch_lock); return; } EXPORT_SYMBOL(mhi_dev_close_channel); Loading drivers/platform/msm/mhi_dev/mhi.h +2 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading drivers/platform/msm/mhi_dev/mhi_uci.c +62 −32 Original line number Diff line number Diff line Loading @@ -375,6 +375,7 @@ struct uci_client { struct mhi_uci_ctxt_t *uci_ctxt; struct mutex in_chan_lock; struct mutex out_chan_lock; struct mutex client_lock; spinlock_t req_lock; unsigned int f_flags; /* Pointer to dynamically allocated mhi_req structs */ Loading Loading @@ -557,15 +558,25 @@ static struct mhi_req *mhi_uci_get_req(struct uci_client *uci_handle) return req; } static void mhi_uci_put_req(struct uci_client *uci_handle, struct mhi_req *req) static int mhi_uci_put_req(struct uci_client *uci_handle, struct mhi_req *req) { unsigned long flags; spin_lock_irqsave(&uci_handle->req_lock, flags); if (req->is_stale) { uci_log(UCI_DBG_VERBOSE, "Got stale completion for ch %d, ignoring\n", req->chan); spin_unlock_irqrestore(&uci_handle->req_lock, flags); return -EINVAL; } /* Remove from in-use list and add back to free list */ list_del_init(&req->list); list_add_tail(&req->list, &uci_handle->req_list); spin_unlock_irqrestore(&uci_handle->req_lock, flags); return 0; } static void mhi_uci_write_completion_cb(void *req) Loading @@ -581,17 +592,12 @@ static void mhi_uci_write_completion_cb(void *req) * the stale flag and return. The ureq was added to * the free list when client called release function. */ if (ureq->is_stale) { uci_log(UCI_DBG_VERBOSE, "Got stale completion for ch %d\n", ureq->chan); ureq->is_stale = false; if (mhi_uci_put_req(uci_handle, ureq)) return; } if (uci_handle->write_done) complete(uci_handle->write_done); mhi_uci_put_req(uci_handle, ureq); /* Write queue may be waiting for write request structs */ wake_up(&uci_handle->write_wq); } Loading @@ -601,19 +607,19 @@ static void mhi_uci_read_completion_cb(void *req) struct mhi_req *ureq = req; struct uci_client *uci_handle; if (ureq->is_stale) { uci_log(UCI_DBG_VERBOSE, "Got stale completion for ch %d, ignoring\n", ureq->chan); return; } uci_handle = (struct uci_client *)ureq->context; uci_handle->pkt_loc = (void *)ureq->buf; uci_handle->pkt_size = ureq->transfer_len; mhi_uci_put_req(uci_handle, ureq); /* * If this is a delayed read completion, just clear * the stale flag and return. The ureq was added to * the free list when client called release function. */ if (mhi_uci_put_req(uci_handle, ureq)) return; complete(&uci_handle->read_done); } Loading Loading @@ -782,6 +788,8 @@ static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait) if (!uci_handle) return -ENODEV; mutex_lock(&uci_handle->client_lock); poll_wait(file, &uci_handle->read_wq, wait); poll_wait(file, &uci_handle->write_wq, wait); /* Loading @@ -789,8 +797,10 @@ static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait) * to poll are in connected state and return with the * appropriate mask if channels are disconnected. */ if (!mhi_uci_are_channels_connected(uci_handle)) { if (!atomic_read(&uci_handle->mhi_chans_open) || !mhi_uci_are_channels_connected(uci_handle)) { mask = POLLHUP; mutex_unlock(&uci_handle->client_lock); return mask; } mask = uci_handle->at_ctrl_mask; Loading @@ -810,6 +820,8 @@ static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait) uci_log(UCI_DBG_VERBOSE, "Client attempted to poll chan %d, returning mask 0x%x\n", uci_handle->in_chan, mask); mutex_unlock(&uci_handle->client_lock); return mask; } Loading Loading @@ -1069,6 +1081,7 @@ static int mhi_uci_client_open(struct inode *mhi_inode, return -EINVAL; } mutex_lock(&uci_handle->client_lock); uci_log(UCI_DBG_DBG, "Client opened struct device node 0x%x, ref count 0x%x\n", iminor(mhi_inode), atomic_read(&uci_handle->ref_count)); Loading @@ -1076,6 +1089,7 @@ static int mhi_uci_client_open(struct inode *mhi_inode, if (!uci_handle) { atomic_dec(&uci_handle->ref_count); uci_log(UCI_DBG_DBG, "No memory, returning failure\n"); mutex_unlock(&uci_handle->client_lock); return -ENOMEM; } uci_handle->uci_ctxt = &uci_ctxt; Loading @@ -1093,11 +1107,13 @@ static int mhi_uci_client_open(struct inode *mhi_inode, uci_log(UCI_DBG_INFO, "Closing failed channel\n"); } mutex_unlock(&uci_handle->client_lock); return rc; } } } file_handle->private_data = uci_handle; mutex_unlock(&uci_handle->client_lock); return 0; Loading @@ -1110,14 +1126,17 @@ static int mhi_uci_client_release(struct inode *mhi_inode, const struct chan_attr *in_chan_attr; int count = 0, i; struct mhi_req *ureq; unsigned long flags; if (!uci_handle) return -EINVAL; mutex_lock(&uci_handle->client_lock); in_chan_attr = uci_handle->in_chan_attr; if (!in_chan_attr) { uci_log(UCI_DBG_ERROR, "Null channel attributes for chan %d\n", uci_handle->in_chan); mutex_unlock(&uci_handle->client_lock); return -EINVAL; } Loading @@ -1125,6 +1144,7 @@ static int mhi_uci_client_release(struct inode *mhi_inode, uci_log(UCI_DBG_DBG, "Client close chan %d, ref count 0x%x\n", iminor(mhi_inode), atomic_read(&uci_handle->ref_count)); mutex_unlock(&uci_handle->client_lock); return 0; } Loading Loading @@ -1162,8 +1182,12 @@ static int mhi_uci_client_release(struct inode *mhi_inode, * to client if the transfer completes later. */ count = 0; spin_lock_irqsave(&uci_handle->req_lock, flags); if (!(uci_handle->f_flags & O_SYNC)) { while (!(list_empty(&uci_handle->in_use_list))) { ureq = container_of(uci_handle->in_use_list.next, ureq = container_of( uci_handle->in_use_list.next, struct mhi_req, list); list_del_init(&ureq->list); ureq->is_stale = true; Loading @@ -1173,6 +1197,8 @@ static int mhi_uci_client_release(struct inode *mhi_inode, list_add_tail(&ureq->list, &uci_handle->req_list); count++; } } spin_unlock_irqrestore(&uci_handle->req_lock, flags); if (count) uci_log(UCI_DBG_DBG, "Client %d closed with %d transfers pending\n", Loading @@ -1188,6 +1214,7 @@ static int mhi_uci_client_release(struct inode *mhi_inode, atomic_set(&uci_handle->read_data_ready, 0); atomic_set(&uci_handle->write_data_ready, 0); file_handle->private_data = NULL; mutex_unlock(&uci_handle->client_lock); return 0; } Loading Loading @@ -1670,6 +1697,7 @@ static int mhi_register_client(struct uci_client *mhi_client, int index) mutex_init(&mhi_client->in_chan_lock); mutex_init(&mhi_client->out_chan_lock); mutex_init(&mhi_client->client_lock); spin_lock_init(&mhi_client->req_lock); /* Init the completion event for AT ctrl read */ init_completion(&mhi_client->at_ctrl_read_done); Loading Loading @@ -2069,7 +2097,8 @@ static void mhi_uci_at_ctrl_client_cb(struct mhi_dev_client_cb_data *cb_data) mhi_dev_close_channel(client->out_handle); mhi_dev_close_channel(client->in_handle); /* Add back reqs for in-use list, if any, to free list */ /* Add back reqs from in-use list, if any, to free list */ if (!(client->f_flags & O_SYNC)) { while (!(list_empty(&client->in_use_list))) { ureq = container_of(client->in_use_list.next, struct mhi_req, list); Loading @@ -2077,6 +2106,7 @@ static void mhi_uci_at_ctrl_client_cb(struct mhi_dev_client_cb_data *cb_data) /* Add to in-use list */ list_add_tail(&ureq->list, &client->req_list); } } for (i = 0; i < (client->in_chan_attr->nr_trbs); i++) { kfree(client->in_buf_list[i].addr); Loading Loading
drivers/platform/msm/mhi_dev/mhi.c +140 −53 Original line number Diff line number Diff line Loading @@ -476,6 +476,22 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring, if (!ereq->is_cmd_cpl) { ch = ereq->context; /* * Take Channel ring event lock to prevent sending * completion command while the channel is getting * reset/stopped. * Abort sending completion event if channel has moved to * stopped state. */ mutex_lock(&ch->ring->event_lock); if (ch->state == MHI_DEV_CH_STOPPED || ch->state == MHI_DEV_CH_PENDING_STOP) { mhi_log(MHI_MSG_ERROR, "Ch:%d is in %d state, abort sending completion evnt\n" , ch->ch_id, ch->state); rc = -ENXIO; goto exit; } mhi_log(MHI_MSG_VERBOSE, "Flushing %d cmpl events of ch %d\n", ereq->num_events, ch->ch_id); } else { Loading @@ -487,8 +503,7 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring, 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); mutex_unlock(&ring->event_lock); return rc; goto exit; } ring->ring_ctx_shadow->ev.rp = (ring->rd_offset * Loading Loading @@ -537,6 +552,9 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring, pr_err("%s: error sending in msi\n", __func__); } exit: if (!ereq->is_cmd_cpl) mutex_unlock(&ch->ring->event_lock); mutex_unlock(&ring->event_lock); return rc; } Loading Loading @@ -597,18 +615,21 @@ static int mhi_dev_flush_transfer_completion_events(struct mhi_dev *mhi, unsigned long flags; struct event_req *flush_ereq; do { /* * Channel got stopped or closed with transfers pending * Do not send completion events to host */ if (ch->state == MHI_DEV_CH_CLOSED || ch->state == MHI_DEV_CH_STOPPED) { mhi_log(MHI_MSG_DBG, "Ch %d closed with %d writes pending\n", mhi_log(MHI_MSG_DBG, "Ch %d closed with %d writes pending\n", ch->ch_id, ch->pend_wr_count + 1); return -ENODEV; rc = -ENODEV; break; } do { spin_lock_irqsave(&mhi->lock, flags); if (list_empty(&ch->flush_event_req_buffers)) { spin_unlock_irqrestore(&mhi->lock, flags); Loading @@ -634,7 +655,8 @@ static int mhi_dev_flush_transfer_completion_events(struct mhi_dev *mhi, "Invalid cmpl evt buf - start %pK, end %pK\n", flush_ereq->tr_events, flush_ereq->tr_events + flush_ereq->num_events); return -EINVAL; rc = -EINVAL; break; } rc = mhi_dev_send_multiple_tr_events(mhi, mhi->ch_ctx_cache[ch->ch_id].err_indx, Loading Loading @@ -1599,7 +1621,7 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring, } } mutex_lock(&mhi->mhi_event_lock); mutex_lock(&ring->event_lock); /* add the ring element */ mhi_dev_add_element(ring, el, NULL, 0); Loading Loading @@ -1632,7 +1654,6 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring, */ wmb(); mutex_unlock(&mhi->mhi_event_lock); mhi_log(MHI_MSG_VERBOSE, "event sent:\n"); mhi_log(MHI_MSG_VERBOSE, "evnt ptr : 0x%llx\n", el->evt_tr_comp.ptr); mhi_log(MHI_MSG_VERBOSE, "evnt len : 0x%x\n", el->evt_tr_comp.len); Loading @@ -1645,6 +1666,7 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring, else rc = ep_pcie_trigger_msi(mhi_ctx->phandle, ctx->ev.msivec); mutex_unlock(&ring->event_lock); return rc; } Loading Loading @@ -1811,6 +1833,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) { Loading Loading @@ -1972,6 +2032,7 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi, ch = &mhi->ch[ch_id]; mutex_lock(&ch->ch_lock); mutex_lock(&ch->ring->event_lock); mhi->ch[ch_id].state = MHI_DEV_CH_PENDING_STOP; rc = mhi_dev_process_stop_cmd( Loading @@ -1980,6 +2041,7 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi, if (rc) pr_err("stop event send failed\n"); mutex_unlock(&ch->ring->event_lock); mutex_unlock(&ch->ch_lock); mhi_update_state_info_ch(ch_id, MHI_STATE_DISCONNECTED); /* Trigger callback to clients */ Loading Loading @@ -2026,37 +2088,20 @@ 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); /* 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"); mutex_lock(&ch->ring->event_lock); 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); mhi_dev_trigger_cb(ch_id); Loading Loading @@ -2104,9 +2149,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"); Loading @@ -2117,26 +2163,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: Loading Loading @@ -2173,6 +2240,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) { Loading @@ -2184,6 +2252,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"); Loading Loading @@ -2361,6 +2433,7 @@ static void mhi_dev_transfer_completion_cb(void *mreq) /* Trigger client call back */ req->client_cb(req); mutex_lock(&ch->ch_lock); /* Flush read completions to host */ if (snd_cmpl && mhi_ctx->ch_ctx_cache[ch->ch_id].ch_type == MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL) { Loading @@ -2371,7 +2444,7 @@ static void mhi_dev_transfer_completion_cb(void *mreq) "Failed to flush read completions to host\n"); } } mutex_unlock(&ch->ch_lock); if (ch->state == MHI_DEV_CH_PENDING_STOP) { ch->state = MHI_DEV_CH_STOPPED; rc = mhi_dev_process_stop_cmd(ch->ring, ch->ch_id, mhi_ctx); Loading Loading @@ -3066,7 +3139,8 @@ void mhi_dev_close_channel(struct mhi_dev_client *handle) ch = handle->channel; do { if (ch->pend_wr_count) { if (ch->pend_wr_count && !list_empty(&ch->event_req_buffers)) { usleep_range(MHI_DEV_CH_CLOSE_TIMEOUT_MIN, MHI_DEV_CH_CLOSE_TIMEOUT_MAX); } else Loading @@ -3078,6 +3152,9 @@ void mhi_dev_close_channel(struct mhi_dev_client *handle) if (ch->pend_wr_count) mhi_log(MHI_MSG_ERROR, "%d writes pending for channel %d\n", ch->pend_wr_count, ch->ch_id); if (!list_empty(&ch->event_req_buffers)) mhi_log(MHI_MSG_ERROR, "%d pending flush for channel %d\n", ch->pend_wr_count, ch->ch_id); if (ch->state != MHI_DEV_CH_PENDING_START) if ((ch->ch_type == MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL && Loading @@ -3089,14 +3166,24 @@ void mhi_dev_close_channel(struct mhi_dev_client *handle) ch->state = MHI_DEV_CH_CLOSED; ch->active_client = NULL; kfree(ch->ereqs); mhi_log(MHI_MSG_INFO, "MEM_DEALLOC:ch:%d size:%d EREQ\n", ch->ch_id, ch->evt_req_size); kfree(ch->tr_events); mhi_log(MHI_MSG_INFO, "MEM_DEALLOC:ch:%d size:%d TR_EVENTS\n", ch->ch_id, ch->evt_buf_size); ch->evt_buf_size = 0; ch->evt_req_size = 0; ch->ereqs = NULL; ch->tr_events = NULL; kfree(handle); mhi_log(MHI_MSG_INFO, "MEM_ALLOC:ch:%d size:%d CLNT_HANDLE\n", ch->ch_id, sizeof(struct mhi_dev_client)); mutex_unlock(&ch->ch_lock); return; } EXPORT_SYMBOL(mhi_dev_close_channel); Loading
drivers/platform/msm/mhi_dev/mhi.h +2 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading
drivers/platform/msm/mhi_dev/mhi_uci.c +62 −32 Original line number Diff line number Diff line Loading @@ -375,6 +375,7 @@ struct uci_client { struct mhi_uci_ctxt_t *uci_ctxt; struct mutex in_chan_lock; struct mutex out_chan_lock; struct mutex client_lock; spinlock_t req_lock; unsigned int f_flags; /* Pointer to dynamically allocated mhi_req structs */ Loading Loading @@ -557,15 +558,25 @@ static struct mhi_req *mhi_uci_get_req(struct uci_client *uci_handle) return req; } static void mhi_uci_put_req(struct uci_client *uci_handle, struct mhi_req *req) static int mhi_uci_put_req(struct uci_client *uci_handle, struct mhi_req *req) { unsigned long flags; spin_lock_irqsave(&uci_handle->req_lock, flags); if (req->is_stale) { uci_log(UCI_DBG_VERBOSE, "Got stale completion for ch %d, ignoring\n", req->chan); spin_unlock_irqrestore(&uci_handle->req_lock, flags); return -EINVAL; } /* Remove from in-use list and add back to free list */ list_del_init(&req->list); list_add_tail(&req->list, &uci_handle->req_list); spin_unlock_irqrestore(&uci_handle->req_lock, flags); return 0; } static void mhi_uci_write_completion_cb(void *req) Loading @@ -581,17 +592,12 @@ static void mhi_uci_write_completion_cb(void *req) * the stale flag and return. The ureq was added to * the free list when client called release function. */ if (ureq->is_stale) { uci_log(UCI_DBG_VERBOSE, "Got stale completion for ch %d\n", ureq->chan); ureq->is_stale = false; if (mhi_uci_put_req(uci_handle, ureq)) return; } if (uci_handle->write_done) complete(uci_handle->write_done); mhi_uci_put_req(uci_handle, ureq); /* Write queue may be waiting for write request structs */ wake_up(&uci_handle->write_wq); } Loading @@ -601,19 +607,19 @@ static void mhi_uci_read_completion_cb(void *req) struct mhi_req *ureq = req; struct uci_client *uci_handle; if (ureq->is_stale) { uci_log(UCI_DBG_VERBOSE, "Got stale completion for ch %d, ignoring\n", ureq->chan); return; } uci_handle = (struct uci_client *)ureq->context; uci_handle->pkt_loc = (void *)ureq->buf; uci_handle->pkt_size = ureq->transfer_len; mhi_uci_put_req(uci_handle, ureq); /* * If this is a delayed read completion, just clear * the stale flag and return. The ureq was added to * the free list when client called release function. */ if (mhi_uci_put_req(uci_handle, ureq)) return; complete(&uci_handle->read_done); } Loading Loading @@ -782,6 +788,8 @@ static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait) if (!uci_handle) return -ENODEV; mutex_lock(&uci_handle->client_lock); poll_wait(file, &uci_handle->read_wq, wait); poll_wait(file, &uci_handle->write_wq, wait); /* Loading @@ -789,8 +797,10 @@ static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait) * to poll are in connected state and return with the * appropriate mask if channels are disconnected. */ if (!mhi_uci_are_channels_connected(uci_handle)) { if (!atomic_read(&uci_handle->mhi_chans_open) || !mhi_uci_are_channels_connected(uci_handle)) { mask = POLLHUP; mutex_unlock(&uci_handle->client_lock); return mask; } mask = uci_handle->at_ctrl_mask; Loading @@ -810,6 +820,8 @@ static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait) uci_log(UCI_DBG_VERBOSE, "Client attempted to poll chan %d, returning mask 0x%x\n", uci_handle->in_chan, mask); mutex_unlock(&uci_handle->client_lock); return mask; } Loading Loading @@ -1069,6 +1081,7 @@ static int mhi_uci_client_open(struct inode *mhi_inode, return -EINVAL; } mutex_lock(&uci_handle->client_lock); uci_log(UCI_DBG_DBG, "Client opened struct device node 0x%x, ref count 0x%x\n", iminor(mhi_inode), atomic_read(&uci_handle->ref_count)); Loading @@ -1076,6 +1089,7 @@ static int mhi_uci_client_open(struct inode *mhi_inode, if (!uci_handle) { atomic_dec(&uci_handle->ref_count); uci_log(UCI_DBG_DBG, "No memory, returning failure\n"); mutex_unlock(&uci_handle->client_lock); return -ENOMEM; } uci_handle->uci_ctxt = &uci_ctxt; Loading @@ -1093,11 +1107,13 @@ static int mhi_uci_client_open(struct inode *mhi_inode, uci_log(UCI_DBG_INFO, "Closing failed channel\n"); } mutex_unlock(&uci_handle->client_lock); return rc; } } } file_handle->private_data = uci_handle; mutex_unlock(&uci_handle->client_lock); return 0; Loading @@ -1110,14 +1126,17 @@ static int mhi_uci_client_release(struct inode *mhi_inode, const struct chan_attr *in_chan_attr; int count = 0, i; struct mhi_req *ureq; unsigned long flags; if (!uci_handle) return -EINVAL; mutex_lock(&uci_handle->client_lock); in_chan_attr = uci_handle->in_chan_attr; if (!in_chan_attr) { uci_log(UCI_DBG_ERROR, "Null channel attributes for chan %d\n", uci_handle->in_chan); mutex_unlock(&uci_handle->client_lock); return -EINVAL; } Loading @@ -1125,6 +1144,7 @@ static int mhi_uci_client_release(struct inode *mhi_inode, uci_log(UCI_DBG_DBG, "Client close chan %d, ref count 0x%x\n", iminor(mhi_inode), atomic_read(&uci_handle->ref_count)); mutex_unlock(&uci_handle->client_lock); return 0; } Loading Loading @@ -1162,8 +1182,12 @@ static int mhi_uci_client_release(struct inode *mhi_inode, * to client if the transfer completes later. */ count = 0; spin_lock_irqsave(&uci_handle->req_lock, flags); if (!(uci_handle->f_flags & O_SYNC)) { while (!(list_empty(&uci_handle->in_use_list))) { ureq = container_of(uci_handle->in_use_list.next, ureq = container_of( uci_handle->in_use_list.next, struct mhi_req, list); list_del_init(&ureq->list); ureq->is_stale = true; Loading @@ -1173,6 +1197,8 @@ static int mhi_uci_client_release(struct inode *mhi_inode, list_add_tail(&ureq->list, &uci_handle->req_list); count++; } } spin_unlock_irqrestore(&uci_handle->req_lock, flags); if (count) uci_log(UCI_DBG_DBG, "Client %d closed with %d transfers pending\n", Loading @@ -1188,6 +1214,7 @@ static int mhi_uci_client_release(struct inode *mhi_inode, atomic_set(&uci_handle->read_data_ready, 0); atomic_set(&uci_handle->write_data_ready, 0); file_handle->private_data = NULL; mutex_unlock(&uci_handle->client_lock); return 0; } Loading Loading @@ -1670,6 +1697,7 @@ static int mhi_register_client(struct uci_client *mhi_client, int index) mutex_init(&mhi_client->in_chan_lock); mutex_init(&mhi_client->out_chan_lock); mutex_init(&mhi_client->client_lock); spin_lock_init(&mhi_client->req_lock); /* Init the completion event for AT ctrl read */ init_completion(&mhi_client->at_ctrl_read_done); Loading Loading @@ -2069,7 +2097,8 @@ static void mhi_uci_at_ctrl_client_cb(struct mhi_dev_client_cb_data *cb_data) mhi_dev_close_channel(client->out_handle); mhi_dev_close_channel(client->in_handle); /* Add back reqs for in-use list, if any, to free list */ /* Add back reqs from in-use list, if any, to free list */ if (!(client->f_flags & O_SYNC)) { while (!(list_empty(&client->in_use_list))) { ureq = container_of(client->in_use_list.next, struct mhi_req, list); Loading @@ -2077,6 +2106,7 @@ static void mhi_uci_at_ctrl_client_cb(struct mhi_dev_client_cb_data *cb_data) /* Add to in-use list */ list_add_tail(&ureq->list, &client->req_list); } } for (i = 0; i < (client->in_chan_attr->nr_trbs); i++) { kfree(client->in_buf_list[i].addr); Loading