bus: mhi: core: Fix race while handling SYS_ERR at power up
authorManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Thu, 16 Dec 2021 08:12:24 +0000 (13:42 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 27 Jan 2022 10:02:58 +0000 (11:02 +0100)
commit d651ce8e917fa1bf6cfab8dca74c512edffc35d3 upstream.

During SYS_ERR condition, as a response to the MHI_RESET from host, some
devices tend to issue BHI interrupt without clearing the SYS_ERR state in
the device. This creates a race condition and causes a failure in booting
up the device.

The issue is seen on the Sierra Wireless EM9191 modem during SYS_ERR
handling in mhi_async_power_up(). Once the host detects that the device
is in SYS_ERR state, it issues MHI_RESET and waits for the device to
process the reset request. During this time, the device triggers the BHI
interrupt to the host without clearing SYS_ERR condition. So the host
starts handling the SYS_ERR condition again.

To fix this issue, let's register the IRQ handler only after handling the
SYS_ERR check to avoid getting spurious IRQs from the device.

Fixes: e18d4e9fa79b ("bus: mhi: core: Handle syserr during power_up")
Cc: stable@vger.kernel.org
Reported-by: Aleksander Morgado <aleksander@aleksander.es>
Tested-by: Aleksander Morgado <aleksander@aleksander.es>
Tested-by: Thomas Perrot <thomas.perrot@bootlin.com>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Link: https://lore.kernel.org/r/20211216081227.237749-8-manivannan.sadhasivam@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/bus/mhi/core/pm.c

index 547e6e7..bb9a204 100644 (file)
@@ -1053,7 +1053,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
        enum mhi_ee_type current_ee;
        enum dev_st_transition next_state;
        struct device *dev = &mhi_cntrl->mhi_dev->dev;
-       u32 val;
+       u32 interval_us = 25000; /* poll register field every 25 milliseconds */
        int ret;
 
        dev_info(dev, "Requested to power ON\n");
@@ -1070,10 +1070,6 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
        mutex_lock(&mhi_cntrl->pm_mutex);
        mhi_cntrl->pm_state = MHI_PM_DISABLE;
 
-       ret = mhi_init_irq_setup(mhi_cntrl);
-       if (ret)
-               goto error_setup_irq;
-
        /* Setup BHI INTVEC */
        write_lock_irq(&mhi_cntrl->pm_lock);
        mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0);
@@ -1087,7 +1083,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
                dev_err(dev, "%s is not a valid EE for power on\n",
                        TO_MHI_EXEC_STR(current_ee));
                ret = -EIO;
-               goto error_async_power_up;
+               goto error_exit;
        }
 
        state = mhi_get_mhi_state(mhi_cntrl);
@@ -1096,20 +1092,12 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
 
        if (state == MHI_STATE_SYS_ERR) {
                mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
-               ret = wait_event_timeout(mhi_cntrl->state_event,
-                               MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state) ||
-                                       mhi_read_reg_field(mhi_cntrl,
-                                                          mhi_cntrl->regs,
-                                                          MHICTRL,
-                                                          MHICTRL_RESET_MASK,
-                                                          MHICTRL_RESET_SHIFT,
-                                                          &val) ||
-                                       !val,
-                               msecs_to_jiffies(mhi_cntrl->timeout_ms));
-               if (!ret) {
-                       ret = -EIO;
+               ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
+                                MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 0,
+                                interval_us);
+               if (ret) {
                        dev_info(dev, "Failed to reset MHI due to syserr state\n");
-                       goto error_async_power_up;
+                       goto error_exit;
                }
 
                /*
@@ -1119,6 +1107,10 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
                mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0);
        }
 
+       ret = mhi_init_irq_setup(mhi_cntrl);
+       if (ret)
+               goto error_exit;
+
        /* Transition to next state */
        next_state = MHI_IN_PBL(current_ee) ?
                DEV_ST_TRANSITION_PBL : DEV_ST_TRANSITION_READY;
@@ -1131,10 +1123,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
 
        return 0;
 
-error_async_power_up:
-       mhi_deinit_free_irq(mhi_cntrl);
-
-error_setup_irq:
+error_exit:
        mhi_cntrl->pm_state = MHI_PM_DISABLE;
        mutex_unlock(&mhi_cntrl->pm_mutex);