diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c index 3bc4d448d8bd..c2d9d1921414 100644 --- a/drivers/platform/msm/mhi_dev/mhi.c +++ b/drivers/platform/msm/mhi_dev/mhi.c @@ -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 @@ send_start_completion_event: 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,12 +2159,6 @@ 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); - rc = mhi_dev_process_ring(ring); - if (rc) { - mhi_log(MHI_MSG_ERROR, - "error processing ring %d\n", ring->id); - goto exit; - } if (ring->id < mhi->ch_ring_start) { mhi_log(MHI_MSG_ERROR, @@ -2153,12 +2167,39 @@ static void mhi_dev_process_ring_pending(struct work_struct *work) } 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 (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); + } + 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"); diff --git a/drivers/platform/msm/mhi_dev/mhi.h b/drivers/platform/msm/mhi_dev/mhi.h index 7fec5e68a001..c477bfa7686b 100644 --- a/drivers/platform/msm/mhi_dev/mhi.h +++ b/drivers/platform/msm/mhi_dev/mhi.h @@ -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 */