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;