Merge "msm: mhi_dev: Wait for pending writes to complete"
diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c
index 905f13a..f1134f8 100644
--- a/drivers/platform/msm/mhi_dev/mhi.c
+++ b/drivers/platform/msm/mhi_dev/mhi.c
@@ -63,6 +63,10 @@
 #define TR_RING_ELEMENT_SZ	sizeof(struct mhi_dev_transfer_ring_element)
 #define RING_ELEMENT_TYPE_SZ	sizeof(union mhi_dev_ring_element_type)
 
+#define MHI_DEV_CH_CLOSE_TIMEOUT_MIN	5000
+#define MHI_DEV_CH_CLOSE_TIMEOUT_MAX	5100
+#define MHI_DEV_CH_CLOSE_TIMEOUT_COUNT	30
+
 uint32_t bhi_imgtxdb;
 enum mhi_msg_level mhi_msg_lvl = MHI_MSG_ERROR;
 enum mhi_msg_level mhi_ipc_msg_lvl = MHI_MSG_VERBOSE;
@@ -913,7 +917,7 @@ static int mhi_dev_process_stop_cmd(struct mhi_dev_ring *ring, uint32_t ch_id,
 		return 0;
 	} else if (mhi->ch_ctx_cache[ch_id].ch_type ==
 			MHI_DEV_CH_TYPE_INBOUND_CHANNEL &&
-			mhi->ch[ch_id].wr_request_active) {
+			(mhi->ch[ch_id].pend_wr_count > 0)) {
 		mhi_log(MHI_MSG_INFO, "Pending inbound transaction\n");
 		return 0;
 	}
@@ -1453,9 +1457,23 @@ static void mhi_dev_transfer_completion_cb(void *mreq)
 	rd_offset = req->rd_offset;
 	ch->curr_ereq->context = ch;
 
+	if (mhi->ch_ctx_cache[ch->ch_id].ch_type ==
+			MHI_DEV_CH_TYPE_INBOUND_CHANNEL)
+		ch->pend_wr_count--;
+
 	dma_unmap_single(&mhi_ctx->pdev->dev, req->dma,
 			req->len, DMA_FROM_DEVICE);
 
+	/*
+	 * Channel got closed with transfers pending
+	 * Do not trigger callback or send cmpl to host
+	 */
+	if (ch->state == MHI_DEV_CH_CLOSED) {
+		mhi_log(MHI_MSG_DBG, "Ch %d closed with %d writes pending\n",
+				ch->ch_id, ch->pend_wr_count + 1);
+		return;
+	}
+
 	/* Trigger client call back */
 	req->client_cb(req);
 
@@ -2009,18 +2027,49 @@ int mhi_dev_channel_isempty(struct mhi_dev_client *handle)
 }
 EXPORT_SYMBOL(mhi_dev_channel_isempty);
 
+bool mhi_dev_channel_has_pending_write(struct mhi_dev_client *handle)
+{
+	struct mhi_dev_channel *ch;
+
+	if (!handle) {
+		mhi_log(MHI_MSG_ERROR, "Invalid channel access\n");
+		return -EINVAL;
+	}
+
+	ch = handle->channel;
+	if (!ch)
+		return -EINVAL;
+
+	return ch->pend_wr_count ? true : false;
+}
+EXPORT_SYMBOL(mhi_dev_channel_has_pending_write);
+
 int mhi_dev_close_channel(struct mhi_dev_client *handle)
 {
 	struct mhi_dev_channel *ch;
+	int count = 0;
 	int rc = 0;
 
 	ch = handle->channel;
 
+	do {
+		if (ch->pend_wr_count) {
+			usleep_range(MHI_DEV_CH_CLOSE_TIMEOUT_MIN,
+					MHI_DEV_CH_CLOSE_TIMEOUT_MAX);
+		} else
+			break;
+	} while (++count < MHI_DEV_CH_CLOSE_TIMEOUT_COUNT);
+
 	mutex_lock(&ch->ch_lock);
+
+	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 (ch->state != MHI_DEV_CH_PENDING_START) {
-		if (ch->ch_type == MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL &&
-					!mhi_dev_channel_isempty(handle)) {
-			mhi_log(MHI_MSG_ERROR,
+		if ((ch->ch_type == MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL &&
+			!mhi_dev_channel_isempty(handle)) || ch->tre_loc) {
+			mhi_log(MHI_MSG_DBG,
 				"Trying to close an active channel (%d)\n",
 				ch->ch_id);
 			rc = -EAGAIN;
@@ -2237,6 +2286,7 @@ int mhi_dev_write_channel(struct mhi_req *wreq)
 	size_t bytes_to_write = 0;
 	size_t bytes_written = 0;
 	uint32_t tre_len = 0, suspend_wait_timeout = 0;
+	bool async_wr_sched = false;
 
 	if (!wreq || !wreq->client || !wreq->buf) {
 		pr_err("%s: invalid parameters\n", __func__);
@@ -2280,12 +2330,12 @@ int mhi_dev_write_channel(struct mhi_req *wreq)
 
 	handle_client = wreq->client;
 	ch = handle_client->channel;
-	ch->wr_request_active = true;
 
 	ring = ch->ring;
 
 	mutex_lock(&ch->ch_lock);
 
+	ch->pend_wr_count++;
 	if (ch->state == MHI_DEV_CH_STOPPED) {
 		mhi_log(MHI_MSG_ERROR,
 			"channel %d already stopped\n", wreq->chan);
@@ -2336,7 +2386,8 @@ int mhi_dev_write_channel(struct mhi_req *wreq)
 					"Error while writing chan (%d) rc %d\n",
 					wreq->chan, rc);
 			goto exit;
-		}
+		} else if (wreq->mode == DMA_ASYNC)
+			async_wr_sched = true;
 		bytes_written += bytes_to_write;
 		usr_buf_remaining -= bytes_to_write;
 
@@ -2376,7 +2427,8 @@ int mhi_dev_write_channel(struct mhi_req *wreq)
 		}
 	}
 exit:
-	ch->wr_request_active = false;
+	if (wreq->mode == DMA_SYNC || !async_wr_sched)
+		ch->pend_wr_count--;
 	mutex_unlock(&ch->ch_lock);
 	mutex_unlock(&mhi_ctx->mhi_write_test);
 	return bytes_written;
diff --git a/drivers/platform/msm/mhi_dev/mhi.h b/drivers/platform/msm/mhi_dev/mhi.h
index 4b6af8f..8f7a8e3 100644
--- a/drivers/platform/msm/mhi_dev/mhi.h
+++ b/drivers/platform/msm/mhi_dev/mhi.h
@@ -461,7 +461,7 @@ struct mhi_dev_channel {
 	uint32_t			tre_bytes_left;
 	/* td size being read/written from/to so far */
 	uint32_t			td_size;
-	bool				wr_request_active;
+	uint32_t			pend_wr_count;
 	bool				skip_td;
 };
 
diff --git a/drivers/platform/msm/mhi_dev/mhi_uci.c b/drivers/platform/msm/mhi_dev/mhi_uci.c
index d045d59..e3221e3 100644
--- a/drivers/platform/msm/mhi_dev/mhi_uci.c
+++ b/drivers/platform/msm/mhi_dev/mhi_uci.c
@@ -47,6 +47,10 @@
 #define MHI_UCI_AT_CTRL_READ_TIMEOUT	msecs_to_jiffies(1000)
 #define MHI_UCI_WRITE_REQ_AVAIL_TIMEOUT msecs_to_jiffies(1000)
 
+#define MHI_UCI_RELEASE_TIMEOUT_MIN	5000
+#define MHI_UCI_RELEASE_TIMEOUT_MAX	5100
+#define MHI_UCI_RELEASE_TIMEOUT_COUNT	30
+
 enum uci_dbg_level {
 	UCI_DBG_VERBOSE = 0x0,
 	UCI_DBG_INFO = 0x1,
@@ -907,41 +911,56 @@ static int mhi_uci_client_release(struct inode *mhi_inode,
 		struct file *file_handle)
 {
 	struct uci_client *uci_handle = file_handle->private_data;
-	int rc = 0;
+	int count = 0;
 
 	if (!uci_handle)
 		return -EINVAL;
 
-	if (atomic_sub_return(1, &uci_handle->ref_count) == 0) {
-		uci_log(UCI_DBG_DBG,
-				"Last client left, closing channel 0x%x\n",
-				iminor(mhi_inode));
-		if (atomic_read(&uci_handle->mhi_chans_open)) {
-			atomic_set(&uci_handle->mhi_chans_open, 0);
-
-			if (!(uci_handle->f_flags & O_SYNC))
-				kfree(uci_handle->wreqs);
-			mutex_lock(&uci_handle->out_chan_lock);
-			rc = mhi_dev_close_channel(uci_handle->out_handle);
-			wake_up(&uci_handle->write_wq);
-			mutex_unlock(&uci_handle->out_chan_lock);
-
-			mutex_lock(&uci_handle->in_chan_lock);
-			rc = mhi_dev_close_channel(uci_handle->in_handle);
-			wake_up(&uci_handle->read_wq);
-			mutex_unlock(&uci_handle->in_chan_lock);
-
-		}
-		atomic_set(&uci_handle->read_data_ready, 0);
-		atomic_set(&uci_handle->write_data_ready, 0);
-		file_handle->private_data = NULL;
-	} else {
-		uci_log(UCI_DBG_DBG,
-			"Client close chan %d, ref count 0x%x\n",
+	if (atomic_sub_return(1, &uci_handle->ref_count)) {
+		uci_log(UCI_DBG_DBG, "Client close chan %d, ref count 0x%x\n",
 			iminor(mhi_inode),
 			atomic_read(&uci_handle->ref_count));
+		return 0;
 	}
-	return rc;
+
+	uci_log(UCI_DBG_DBG,
+			"Last client left, closing channel 0x%x\n",
+			iminor(mhi_inode));
+
+	do {
+		if (mhi_dev_channel_has_pending_write(uci_handle->out_handle))
+			usleep_range(MHI_UCI_RELEASE_TIMEOUT_MIN,
+				MHI_UCI_RELEASE_TIMEOUT_MAX);
+		else
+			break;
+	} while (++count < MHI_UCI_RELEASE_TIMEOUT_COUNT);
+
+	if (count == MHI_UCI_RELEASE_TIMEOUT_COUNT) {
+		uci_log(UCI_DBG_DBG, "Channel %d has pending writes\n",
+			iminor(mhi_inode));
+	}
+
+	if (atomic_read(&uci_handle->mhi_chans_open)) {
+		atomic_set(&uci_handle->mhi_chans_open, 0);
+
+		if (!(uci_handle->f_flags & O_SYNC))
+			kfree(uci_handle->wreqs);
+		mutex_lock(&uci_handle->out_chan_lock);
+		mhi_dev_close_channel(uci_handle->out_handle);
+		wake_up(&uci_handle->write_wq);
+		mutex_unlock(&uci_handle->out_chan_lock);
+
+		mutex_lock(&uci_handle->in_chan_lock);
+		mhi_dev_close_channel(uci_handle->in_handle);
+		wake_up(&uci_handle->read_wq);
+		mutex_unlock(&uci_handle->in_chan_lock);
+	}
+
+	atomic_set(&uci_handle->read_data_ready, 0);
+	atomic_set(&uci_handle->write_data_ready, 0);
+	file_handle->private_data = NULL;
+
+	return 0;
 }
 
 static void  mhi_parse_state(char *buf, int *nbytes, uint32_t info)
diff --git a/include/linux/msm_mhi_dev.h b/include/linux/msm_mhi_dev.h
index f717c10..1605687 100644
--- a/include/linux/msm_mhi_dev.h
+++ b/include/linux/msm_mhi_dev.h
@@ -18,6 +18,8 @@
 
 #define IPA_DMA_SYNC                    1
 #define IPA_DMA_ASYNC                   0
+#define DMA_SYNC                    1
+#define DMA_ASYNC                   0
 
 enum cb_reason {
 	MHI_DEV_TRE_AVAILABLE = 0,
@@ -197,6 +199,13 @@ int mhi_dev_write_channel(struct mhi_req *wreq);
 int mhi_dev_channel_isempty(struct mhi_dev_client *handle);
 
 /**
+* mhi_dev_channel_has_pending_write() - Checks if there are any pending writes
+*					to be completed on inbound channel
+* @handle_client:	Client Handle issued during mhi_dev_open_channel
+*/
+bool mhi_dev_channel_has_pending_write(struct mhi_dev_client *handle);
+
+/**
  * mhi_ctrl_state_info() - Provide MHI state info
  *		@idx: Channel number idx. Look at channel_state_info and
  *		pass the index for the corresponding channel.
@@ -246,6 +255,12 @@ static inline int mhi_dev_channel_isempty(struct mhi_dev_client *handle)
 	return -EINVAL;
 };
 
+static inline bool mhi_dev_channel_has_pending_write
+	(struct mhi_dev_client *handle)
+{
+	return false;
+}
+
 static inline int mhi_ctrl_state_info(uint32_t idx, uint32_t *info)
 {
 	return -EINVAL;