From 6f55f77a77fa8fe861f6fa93ac550b61004f3862 Mon Sep 17 00:00:00 2001 From: Bhaumik Bhatt Date: Wed, 18 Sep 2019 19:25:55 -0700 Subject: [PATCH] mhi: core: handle pm error state transition within fast suspend/resume During fast suspend/resume, the power management states in MHI can change to any error state. Allow for it by re-checking and handling it such that pm states are not lost or over-written in the process. Change-Id: I264ae096d20b9752837624dc3a4b3ea6131ad9a6 Signed-off-by: Bhaumik Bhatt --- drivers/bus/mhi/core/mhi_pm.c | 41 +++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/drivers/bus/mhi/core/mhi_pm.c b/drivers/bus/mhi/core/mhi_pm.c index a7c297c8d0b6..ab5b5604fe87 100644 --- a/drivers/bus/mhi/core/mhi_pm.c +++ b/drivers/bus/mhi/core/mhi_pm.c @@ -1186,11 +1186,17 @@ int mhi_pm_fast_suspend(struct mhi_controller *mhi_cntrl, bool notify_client) enum MHI_PM_STATE new_state; struct mhi_chan *itr, *tmp; - if (mhi_cntrl->pm_state == MHI_PM_DISABLE) + read_lock_bh(&mhi_cntrl->pm_lock); + if (mhi_cntrl->pm_state == MHI_PM_DISABLE) { + read_unlock_bh(&mhi_cntrl->pm_lock); return -EINVAL; + } - if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) + if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { + read_unlock_bh(&mhi_cntrl->pm_lock); return -EIO; + } + read_unlock_bh(&mhi_cntrl->pm_lock); /* do a quick check to see if any pending votes to keep us busy */ if (atomic_read(&mhi_cntrl->pending_pkts)) { @@ -1218,6 +1224,12 @@ int mhi_pm_fast_suspend(struct mhi_controller *mhi_cntrl, bool notify_client) goto error_suspend; } + /* check pm state before saving it */ + if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { + ret = -EIO; + goto error_suspend; + } + /* anytime after this, we will resume thru runtime pm framework */ MHI_LOG("Allowing Fast M3 transition\n"); @@ -1365,17 +1377,23 @@ int mhi_pm_fast_resume(struct mhi_controller *mhi_cntrl, bool notify_client) struct mhi_event *mhi_event; int i; + read_lock_bh(&mhi_cntrl->pm_lock); MHI_LOG("Entered with pm_state:%s dev_state:%s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state), TO_MHI_STATE_STR(mhi_cntrl->dev_state)); - if (mhi_cntrl->pm_state == MHI_PM_DISABLE) + if (mhi_cntrl->pm_state == MHI_PM_DISABLE) { + read_unlock_bh(&mhi_cntrl->pm_lock); return 0; + } - if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) + if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { + read_unlock_bh(&mhi_cntrl->pm_lock); return -EIO; + } MHI_ASSERT(mhi_cntrl->pm_state != MHI_PM_M3, "mhi_pm_state != M3"); + read_unlock_bh(&mhi_cntrl->pm_lock); /* notify any clients we're about to exit lpm */ if (notify_client) { @@ -1389,10 +1407,19 @@ int mhi_pm_fast_resume(struct mhi_controller *mhi_cntrl, bool notify_client) /* do not process control events */ tasklet_disable(&mhi_cntrl->mhi_event->task); + /* check if we entered error state again before doing the restore */ write_lock_irq(&mhi_cntrl->pm_lock); + + if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { + write_unlock_irq(&mhi_cntrl->pm_lock); + tasklet_enable(&mhi_cntrl->mhi_event->task); + return -EIO; + } + /* restore the states */ mhi_cntrl->pm_state = mhi_cntrl->saved_pm_state; mhi_cntrl->dev_state = mhi_cntrl->saved_dev_state; + write_unlock_irq(&mhi_cntrl->pm_lock); switch (mhi_cntrl->pm_state) { @@ -1404,6 +1431,12 @@ int mhi_pm_fast_resume(struct mhi_controller *mhi_cntrl, bool notify_client) mhi_cntrl->wake_get(mhi_cntrl, true); mhi_cntrl->wake_put(mhi_cntrl, true); read_unlock_bh(&mhi_cntrl->pm_lock); + break; + default: + MHI_ERR("Unexpected PM state:%s after restore\n", + to_mhi_pm_state_str(mhi_cntrl->pm_state)); + tasklet_enable(&mhi_cntrl->mhi_event->task); + return -EIO; } /* now it's safe to process ctrl events */