@@ -385,8 +385,7 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv)
}
state = mhi_get_mhi_state(mhi_cntrl);
- ee = mhi_cntrl->ee;
- mhi_cntrl->ee = mhi_get_exec_env(mhi_cntrl);
+ ee = mhi_get_exec_env(mhi_cntrl);
dev_dbg(dev, "local ee:%s device ee:%s dev_state:%s\n",
TO_MHI_EXEC_STR(mhi_cntrl->ee), TO_MHI_EXEC_STR(ee),
TO_MHI_STATE_STR(state));
@@ -399,7 +398,9 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv)
goto exit_intvec;
}
- if (mhi_cntrl->ee == MHI_EE_RDDM && mhi_cntrl->ee != ee) {
+ if (ee == MHI_EE_RDDM && mhi_cntrl->ee != ee) {
+ mhi_cntrl->ee = MHI_EE_RDDM;
+
/* prevent clients from queueing any more packets */
pm_state = mhi_tryset_pm_state(mhi_cntrl,
MHI_PM_SYS_ERR_DETECT);
@@ -794,11 +795,6 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
st = DEV_ST_TRANSITION_MISSION_MODE;
break;
case MHI_EE_RDDM:
- mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_RDDM);
- write_lock_irq(&mhi_cntrl->pm_lock);
- mhi_cntrl->ee = event;
- write_unlock_irq(&mhi_cntrl->pm_lock);
- wake_up_all(&mhi_cntrl->state_event);
break;
default:
dev_err(dev,
@@ -377,22 +377,35 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
{
struct mhi_event *mhi_event;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ enum mhi_ee_type ee = MHI_EE_MAX;
int i, ret;
dev_dbg(dev, "Processing Mission Mode transition\n");
write_lock_irq(&mhi_cntrl->pm_lock);
if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state))
- mhi_cntrl->ee = mhi_get_exec_env(mhi_cntrl);
+ ee = mhi_get_exec_env(mhi_cntrl);
write_unlock_irq(&mhi_cntrl->pm_lock);
- if (!MHI_IN_MISSION_MODE(mhi_cntrl->ee))
+ if (!MHI_IN_MISSION_MODE(ee)) {
+ dev_err(dev, "Invalid EE for Mission Mode: %s\n",
+ TO_MHI_EXEC_STR(ee));
return -EIO;
+ }
- wake_up_all(&mhi_cntrl->state_event);
-
+ /*
+ * let controller prepare for mission mode before making the execution
+ * environment change so as to defer core driver activity
+ */
mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_MISSION_MODE);
+ /* ready the core driver for mission mode */
+ write_lock_irq(&mhi_cntrl->pm_lock);
+ mhi_cntrl->ee = ee;
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+
+ wake_up_all(&mhi_cntrl->state_event);
+
/* Force MHI to be in M0 state before continuing */
ret = __mhi_device_get_sync(mhi_cntrl);
if (ret)
In current design, whenever the "bhi" interrupt is fired, the execution environment is updated. This can cause race conditions and impede any ongoing power up or power down processing. For example, if a power down is in progress and the host has updated the execution environment to a local "disabled" state, any BHI interrupt could replace it with the execution environment from the BHI EE register. Another example would be that the device can enter mission mode while the device creation for SBL is still ongoing leading to multiple attempts at opening the same channel. Ensure that EE changes are handled only from appropriate places and occur one after another. For RDDM, handle it as a critical event directly from the interrupt handler and remove RDDM EE change event from the control event ring to prevent the driver from issuing multiple callbacks. SBL handling requires no change. For AMSS/mission mode, hold off the update until the client is notified with a status callback. To sum it up, ensure client readiness before processing mission mode and move to an error state as soon as possible to avoid a bad state. Signed-off-by: Bhaumik Bhatt <bbhatt@codeaurora.org> --- drivers/bus/mhi/core/main.c | 12 ++++-------- drivers/bus/mhi/core/pm.c | 21 +++++++++++++++++---- 2 files changed, 21 insertions(+), 12 deletions(-)