summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoic Poulain <loic.poulain@linaro.org>2021-03-10 12:28:47 +0100
committerLoic Poulain <loic.poulain@linaro.org>2021-04-19 10:01:08 +0200
commit580b405f572c3ea268ea581c8f5c32dd6301e05c (patch)
tree4455ccde336446f1b7724e26a5bbae6c7b17c32e
parent35e008a8ab151dae6ad1f6b5b9d69d26416169d0 (diff)
bus: mhi: Command completion workaround
Some buggy hardwares (e.g sdx24) may report the current command ring wp pointer instead of the command completion pointer. It's obviously wrong, causing completion timeout. We can however deal with that situation by completing the cmd n-1 element, which is what the device actually completes. Signed-off-by: Loic Poulain <loic.poulain@linaro.org>
-rw-r--r--drivers/bus/mhi/core/main.c19
1 files changed, 19 insertions, 0 deletions
diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
index 55aa7db11e4f..0f1febf7716b 100644
--- a/drivers/bus/mhi/core/main.c
+++ b/drivers/bus/mhi/core/main.c
@@ -740,6 +740,7 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
{
dma_addr_t ptr = MHI_TRE_GET_EV_PTR(tre);
struct mhi_cmd *cmd_ring = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING];
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
struct mhi_ring *mhi_ring = &cmd_ring->ring;
struct mhi_tre *cmd_pkt;
struct mhi_chan *mhi_chan;
@@ -747,6 +748,24 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
cmd_pkt = mhi_to_virtual(mhi_ring, ptr);
+ if (unlikely(cmd_pkt == mhi_ring->wp)) {
+ /*
+ * Some buggy hardwares (e.g sdx24) sometimes report the current
+ * command ring wp pointer instead of the command completion
+ * pointer. It's obviously wrong, causing completion timeout. We
+ * can however deal with that situation by completing the cmd
+ * n-1 element.
+ */
+ void *ring_ptr = (void *)cmd_pkt - mhi_ring->el_size;
+
+ if (ring_ptr < mhi_ring->base)
+ ring_ptr += mhi_ring->len;
+
+ cmd_pkt = ring_ptr;
+
+ dev_warn(dev, "Bad completion pointer (ptr == ring_wp)\n");
+ }
+
chan = MHI_TRE_GET_CMD_CHID(cmd_pkt);
mhi_chan = &mhi_cntrl->mhi_chan[chan];
write_lock_bh(&mhi_chan->lock);