From 25539ada431aeeebf57175481983545cd00db8e5 Mon Sep 17 00:00:00 2001 From: Andrei Danaila Date: Fri, 19 Jun 2015 14:07:15 -0700 Subject: mhi: core: Fix PM state machine sync issue Certain corner cases have been identified in the MHI core power management state machine whereby the driver state can go out of sync with the device. The cases are as follows: During in MHI resume operation the device is not guaranteed to be fully recovered from LPM as the framework requires. This causes RPM suspend to potentially be invoked on a device which is still going through a resume state. During RPM suspend action, if said action is cancelled a timing window exists where previously cached commands are not played back to the device. This causes a rotting command issue, when said commands are never acknowledged causing a non recoverable timeout error on the client channels. During an RPM suspend operation, a timing window exists where the device may be transitioning to M2 when the M3 is set. This causes a device error which is non recoverable except by a full reset. CRs-Fixed: 856202 Change-Id: I232365ba77b02b9aec87fef4cecc3d991243afe2 Signed-off-by: Andrei Danaila --- drivers/platform/msm/mhi/mhi_isr.c | 1 + drivers/platform/msm/mhi/mhi_states.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/platform/msm/mhi/mhi_isr.c b/drivers/platform/msm/mhi/mhi_isr.c index 3d6783ee8ad9..da23eaa3279c 100644 --- a/drivers/platform/msm/mhi/mhi_isr.c +++ b/drivers/platform/msm/mhi/mhi_isr.c @@ -173,6 +173,7 @@ static enum MHI_STATUS mhi_process_event_ring( case MHI_PKT_TYPE_SYS_ERR_EVENT: mhi_log(MHI_MSG_INFO, "MHI System Error Detected. Triggering Reset\n"); + BUG(); if (!mhi_trigger_reset(mhi_dev_ctxt)) mhi_log(MHI_MSG_ERROR, "Failed to reset for SYSERR recovery\n"); diff --git a/drivers/platform/msm/mhi/mhi_states.c b/drivers/platform/msm/mhi/mhi_states.c index 7f7dde6d4e0e..d9bcc3008b02 100644 --- a/drivers/platform/msm/mhi/mhi_states.c +++ b/drivers/platform/msm/mhi/mhi_states.c @@ -231,11 +231,14 @@ static enum MHI_STATUS process_m1_transition( if (r) { mhi_log(MHI_MSG_ERROR, "Failed to remove counter ret %d\n", r); + BUG_ON(mhi_dev_ctxt->dev_info-> + plat_dev->dev.power.runtime_error); } } atomic_set(&mhi_dev_ctxt->flags.m2_transition, 0); mhi_log(MHI_MSG_INFO, "M2 transition complete.\n"); write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); + BUG_ON(atomic_read(&mhi_dev_ctxt->outbound_acks) < 0); return MHI_STATUS_SUCCESS; } @@ -849,6 +852,16 @@ int mhi_initiate_m0(struct mhi_device_ctxt *mhi_dev_ctxt) mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M0); } write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); + r = wait_event_interruptible_timeout( + *mhi_dev_ctxt->mhi_ev_wq.m0_event, + mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || + mhi_dev_ctxt->mhi_state == MHI_STATE_M1, + msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT)); + WARN_ON(!r || -ERESTARTSYS == r); + if (!r || -ERESTARTSYS == r) + mhi_log(MHI_MSG_ERROR, + "Failed to get M0 event ret %d\n", r); + r = 0; } exit: mutex_unlock(&mhi_dev_ctxt->pm_lock); @@ -879,6 +892,7 @@ int mhi_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt) "Failed to set bus freq ret %d\n", r); goto exit; break; + case MHI_STATE_M0: case MHI_STATE_M1: case MHI_STATE_M2: mhi_log(MHI_MSG_INFO, @@ -977,6 +991,7 @@ exit: atomic_inc(&mhi_dev_ctxt->flags.data_pending); write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); ring_all_chan_dbs(mhi_dev_ctxt); + ring_all_cmd_dbs(mhi_dev_ctxt); atomic_dec(&mhi_dev_ctxt->flags.data_pending); mhi_deassert_device_wake(mhi_dev_ctxt); } -- cgit v1.2.3