Loading drivers/bus/mhi/controllers/mhi_qcom.c +21 −0 Original line number Diff line number Diff line Loading @@ -525,6 +525,22 @@ static int mhi_qcom_power_up(struct mhi_controller *mhi_cntrl) return ret; } static void mhi_qcom_fatal_worker(struct work_struct *work) { struct mhi_dev *mhi_dev = container_of(work, struct mhi_dev, fatal_worker); struct device *dev = &mhi_dev->pci_dev->dev; struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); int ret; mhi_power_down(mhi_cntrl, true); ret = mhi_qcom_power_up(mhi_cntrl); if (ret) MHI_ERR("Power up failure after SYS ERROR in PBL, ret:%d\n", ret); } static int mhi_runtime_get(struct mhi_controller *mhi_cntrl, void *priv) { struct mhi_dev *mhi_dev = priv; Loading Loading @@ -569,6 +585,10 @@ static void mhi_status_cb(struct mhi_controller *mhi_cntrl, pm_runtime_put(dev); mhi_arch_mission_mode_enter(mhi_cntrl); break; case MHI_CB_FATAL_ERROR: MHI_CNTRL_ERR("Perform power cycle due to SYS ERROR in PBL\n"); schedule_work(&mhi_dev->fatal_worker); break; default: MHI_CNTRL_LOG("Unhandled cb:0x%x\n", reason); } Loading Loading @@ -782,6 +802,7 @@ static struct mhi_controller *mhi_register_controller(struct pci_dev *pci_dev) goto error_register; INIT_WORK(&mhi_cntrl->reg_write_work, mhi_reg_write_work); INIT_WORK(&mhi_dev->fatal_worker, mhi_qcom_fatal_worker); mhi_cntrl->reg_write_q = kcalloc(REG_WRITE_QUEUE_LEN, sizeof(*mhi_cntrl->reg_write_q), Loading drivers/bus/mhi/controllers/mhi_qcom.h +1 −0 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ struct mhi_dev { dma_addr_t iova_start; dma_addr_t iova_stop; enum mhi_suspend_mode suspend_mode; struct work_struct fatal_worker; /* hardware info */ u32 serial_num; Loading drivers/bus/mhi/core/mhi_init.c +39 −8 Original line number Diff line number Diff line Loading @@ -1967,7 +1967,8 @@ static int mhi_driver_remove(struct device *dev) MHI_CH_STATE_DISABLED, MHI_CH_STATE_DISABLED }; int dir; int dir, ret; bool interrupted = false; /* control device has no work to do */ if (mhi_dev->dev_type == MHI_CONTROLLER_TYPE) Loading @@ -1975,11 +1976,11 @@ static int mhi_driver_remove(struct device *dev) MHI_LOG("Removing device for chan:%s\n", mhi_dev->chan_name); /* reset both channels */ /* move both channels to suspended state and disallow processing */ for (dir = 0; dir < 2; dir++) { mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; if (!mhi_chan) if (!mhi_chan || mhi_chan->offload_ch) continue; /* wake all threads waiting for completion */ Loading @@ -1988,15 +1989,45 @@ static int mhi_driver_remove(struct device *dev) complete_all(&mhi_chan->completion); write_unlock_irq(&mhi_chan->lock); /* move channel state to disable, no more processing */ mutex_lock(&mhi_chan->mutex); write_lock_irq(&mhi_chan->lock); if (mhi_chan->ch_state != MHI_CH_STATE_DISABLED) { ch_state[dir] = mhi_chan->ch_state; mhi_chan->ch_state = MHI_CH_STATE_SUSPENDED; } write_unlock_irq(&mhi_chan->lock); mutex_unlock(&mhi_chan->mutex); } /* wait for each channel to close and reset both channels */ for (dir = 0; dir < 2; dir++) { mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; if (!mhi_chan || mhi_chan->offload_ch) continue; /* unbind request from userspace, wait for channel reset */ if (!(mhi_cntrl->power_down || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) && ch_state[dir] != MHI_CH_STATE_DISABLED && !interrupted) { MHI_ERR("Channel %s busy, wait for it to be reset\n", mhi_dev->chan_name); ret = wait_event_interruptible(mhi_cntrl->state_event, mhi_chan->ch_state == MHI_CH_STATE_DISABLED || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)); if (unlikely(ret)) interrupted = true; } /* update channel state as an error can exit above wait */ mutex_lock(&mhi_chan->mutex); write_lock_irq(&mhi_chan->lock); ch_state[dir] = mhi_chan->ch_state; write_unlock_irq(&mhi_chan->lock); /* reset the channel */ if (!mhi_chan->offload_ch) /* reset channel if it was left enabled */ if (ch_state[dir] != MHI_CH_STATE_DISABLED) mhi_reset_chan(mhi_cntrl, mhi_chan); mutex_unlock(&mhi_chan->mutex); Loading @@ -2014,7 +2045,7 @@ static int mhi_driver_remove(struct device *dev) mutex_lock(&mhi_chan->mutex); if (ch_state[dir] == MHI_CH_STATE_ENABLED && if (ch_state[dir] != MHI_CH_STATE_DISABLED && !mhi_chan->offload_ch) mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan); Loading drivers/bus/mhi/core/mhi_main.c +27 −3 Original line number Diff line number Diff line Loading @@ -1259,6 +1259,22 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, st = MHI_ST_TRANSITION_MISSION_MODE; break; case MHI_EE_RDDM: if (mhi_cntrl->ee == MHI_EE_RDDM || mhi_cntrl->power_down) break; MHI_ERR("RDDM event occurred!\n"); write_lock_irq(&mhi_cntrl->pm_lock); mhi_cntrl->ee = MHI_EE_RDDM; write_unlock_irq(&mhi_cntrl->pm_lock); /* notify critical clients */ mhi_control_error(mhi_cntrl); mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data, MHI_CB_EE_RDDM); wake_up_all(&mhi_cntrl->state_event); break; default: MHI_ERR("Unhandled EE event:%s\n", Loading Loading @@ -2038,19 +2054,22 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, { int ret; bool in_mission_mode = false; bool notify = false; MHI_LOG("Entered: unprepare channel:%d\n", mhi_chan->chan); /* no more processing events for this channel */ mutex_lock(&mhi_chan->mutex); write_lock_irq(&mhi_chan->lock); if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) { if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED && mhi_chan->ch_state != MHI_CH_STATE_SUSPENDED) { MHI_LOG("chan:%d is already disabled\n", mhi_chan->chan); write_unlock_irq(&mhi_chan->lock); mutex_unlock(&mhi_chan->mutex); return; } if (mhi_chan->ch_state == MHI_CH_STATE_SUSPENDED) notify = true; mhi_chan->ch_state = MHI_CH_STATE_DISABLED; write_unlock_irq(&mhi_chan->lock); Loading Loading @@ -2091,6 +2110,10 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, if (!mhi_chan->offload_ch) { mhi_reset_chan(mhi_cntrl, mhi_chan); mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan); /* notify waiters to proceed with unbinding channel */ if (notify) wake_up_all(&mhi_cntrl->state_event); } MHI_LOG("chan:%d successfully resetted\n", mhi_chan->chan); mutex_unlock(&mhi_chan->mutex); Loading Loading @@ -2361,7 +2384,8 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, mhi_chan->chan, cmd == MHI_CMD_START_CHAN ? "START" : "STOP"); /* if channel is not active state state do not allow to state change */ if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) { if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED && mhi_chan->ch_state != MHI_CH_STATE_SUSPENDED) { ret = -EINVAL; MHI_LOG("channel:%d is not in active state, ch_state%d\n", mhi_chan->chan, mhi_chan->ch_state); Loading drivers/bus/mhi/core/mhi_pm.c +21 −0 Original line number Diff line number Diff line Loading @@ -1487,6 +1487,26 @@ int mhi_pm_fast_resume(struct mhi_controller *mhi_cntrl, bool notify_client) return -EIO; } if (mhi_cntrl->rddm_supported) { if (mhi_get_exec_env(mhi_cntrl) == MHI_EE_RDDM && !mhi_cntrl->power_down) { mhi_cntrl->ee = MHI_EE_RDDM; write_unlock_irq(&mhi_cntrl->pm_lock); MHI_ERR("RDDM event occurred!\n"); /* notify critical clients with early notifications */ mhi_control_error(mhi_cntrl); mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data, MHI_CB_EE_RDDM); wake_up_all(&mhi_cntrl->state_event); tasklet_enable(&mhi_cntrl->mhi_event->task); goto exit_pm_fast_resume; } } /* restore the states */ mhi_cntrl->pm_state = mhi_cntrl->saved_pm_state; mhi_cntrl->dev_state = mhi_cntrl->saved_dev_state; Loading Loading @@ -1530,6 +1550,7 @@ int mhi_pm_fast_resume(struct mhi_controller *mhi_cntrl, bool notify_client) /* schedules worker if any special purpose events need to be handled */ mhi_special_events_pending(mhi_cntrl); exit_pm_fast_resume: MHI_LOG("Exit 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)); Loading Loading
drivers/bus/mhi/controllers/mhi_qcom.c +21 −0 Original line number Diff line number Diff line Loading @@ -525,6 +525,22 @@ static int mhi_qcom_power_up(struct mhi_controller *mhi_cntrl) return ret; } static void mhi_qcom_fatal_worker(struct work_struct *work) { struct mhi_dev *mhi_dev = container_of(work, struct mhi_dev, fatal_worker); struct device *dev = &mhi_dev->pci_dev->dev; struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); int ret; mhi_power_down(mhi_cntrl, true); ret = mhi_qcom_power_up(mhi_cntrl); if (ret) MHI_ERR("Power up failure after SYS ERROR in PBL, ret:%d\n", ret); } static int mhi_runtime_get(struct mhi_controller *mhi_cntrl, void *priv) { struct mhi_dev *mhi_dev = priv; Loading Loading @@ -569,6 +585,10 @@ static void mhi_status_cb(struct mhi_controller *mhi_cntrl, pm_runtime_put(dev); mhi_arch_mission_mode_enter(mhi_cntrl); break; case MHI_CB_FATAL_ERROR: MHI_CNTRL_ERR("Perform power cycle due to SYS ERROR in PBL\n"); schedule_work(&mhi_dev->fatal_worker); break; default: MHI_CNTRL_LOG("Unhandled cb:0x%x\n", reason); } Loading Loading @@ -782,6 +802,7 @@ static struct mhi_controller *mhi_register_controller(struct pci_dev *pci_dev) goto error_register; INIT_WORK(&mhi_cntrl->reg_write_work, mhi_reg_write_work); INIT_WORK(&mhi_dev->fatal_worker, mhi_qcom_fatal_worker); mhi_cntrl->reg_write_q = kcalloc(REG_WRITE_QUEUE_LEN, sizeof(*mhi_cntrl->reg_write_q), Loading
drivers/bus/mhi/controllers/mhi_qcom.h +1 −0 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ struct mhi_dev { dma_addr_t iova_start; dma_addr_t iova_stop; enum mhi_suspend_mode suspend_mode; struct work_struct fatal_worker; /* hardware info */ u32 serial_num; Loading
drivers/bus/mhi/core/mhi_init.c +39 −8 Original line number Diff line number Diff line Loading @@ -1967,7 +1967,8 @@ static int mhi_driver_remove(struct device *dev) MHI_CH_STATE_DISABLED, MHI_CH_STATE_DISABLED }; int dir; int dir, ret; bool interrupted = false; /* control device has no work to do */ if (mhi_dev->dev_type == MHI_CONTROLLER_TYPE) Loading @@ -1975,11 +1976,11 @@ static int mhi_driver_remove(struct device *dev) MHI_LOG("Removing device for chan:%s\n", mhi_dev->chan_name); /* reset both channels */ /* move both channels to suspended state and disallow processing */ for (dir = 0; dir < 2; dir++) { mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; if (!mhi_chan) if (!mhi_chan || mhi_chan->offload_ch) continue; /* wake all threads waiting for completion */ Loading @@ -1988,15 +1989,45 @@ static int mhi_driver_remove(struct device *dev) complete_all(&mhi_chan->completion); write_unlock_irq(&mhi_chan->lock); /* move channel state to disable, no more processing */ mutex_lock(&mhi_chan->mutex); write_lock_irq(&mhi_chan->lock); if (mhi_chan->ch_state != MHI_CH_STATE_DISABLED) { ch_state[dir] = mhi_chan->ch_state; mhi_chan->ch_state = MHI_CH_STATE_SUSPENDED; } write_unlock_irq(&mhi_chan->lock); mutex_unlock(&mhi_chan->mutex); } /* wait for each channel to close and reset both channels */ for (dir = 0; dir < 2; dir++) { mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; if (!mhi_chan || mhi_chan->offload_ch) continue; /* unbind request from userspace, wait for channel reset */ if (!(mhi_cntrl->power_down || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) && ch_state[dir] != MHI_CH_STATE_DISABLED && !interrupted) { MHI_ERR("Channel %s busy, wait for it to be reset\n", mhi_dev->chan_name); ret = wait_event_interruptible(mhi_cntrl->state_event, mhi_chan->ch_state == MHI_CH_STATE_DISABLED || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)); if (unlikely(ret)) interrupted = true; } /* update channel state as an error can exit above wait */ mutex_lock(&mhi_chan->mutex); write_lock_irq(&mhi_chan->lock); ch_state[dir] = mhi_chan->ch_state; write_unlock_irq(&mhi_chan->lock); /* reset the channel */ if (!mhi_chan->offload_ch) /* reset channel if it was left enabled */ if (ch_state[dir] != MHI_CH_STATE_DISABLED) mhi_reset_chan(mhi_cntrl, mhi_chan); mutex_unlock(&mhi_chan->mutex); Loading @@ -2014,7 +2045,7 @@ static int mhi_driver_remove(struct device *dev) mutex_lock(&mhi_chan->mutex); if (ch_state[dir] == MHI_CH_STATE_ENABLED && if (ch_state[dir] != MHI_CH_STATE_DISABLED && !mhi_chan->offload_ch) mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan); Loading
drivers/bus/mhi/core/mhi_main.c +27 −3 Original line number Diff line number Diff line Loading @@ -1259,6 +1259,22 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, st = MHI_ST_TRANSITION_MISSION_MODE; break; case MHI_EE_RDDM: if (mhi_cntrl->ee == MHI_EE_RDDM || mhi_cntrl->power_down) break; MHI_ERR("RDDM event occurred!\n"); write_lock_irq(&mhi_cntrl->pm_lock); mhi_cntrl->ee = MHI_EE_RDDM; write_unlock_irq(&mhi_cntrl->pm_lock); /* notify critical clients */ mhi_control_error(mhi_cntrl); mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data, MHI_CB_EE_RDDM); wake_up_all(&mhi_cntrl->state_event); break; default: MHI_ERR("Unhandled EE event:%s\n", Loading Loading @@ -2038,19 +2054,22 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, { int ret; bool in_mission_mode = false; bool notify = false; MHI_LOG("Entered: unprepare channel:%d\n", mhi_chan->chan); /* no more processing events for this channel */ mutex_lock(&mhi_chan->mutex); write_lock_irq(&mhi_chan->lock); if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) { if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED && mhi_chan->ch_state != MHI_CH_STATE_SUSPENDED) { MHI_LOG("chan:%d is already disabled\n", mhi_chan->chan); write_unlock_irq(&mhi_chan->lock); mutex_unlock(&mhi_chan->mutex); return; } if (mhi_chan->ch_state == MHI_CH_STATE_SUSPENDED) notify = true; mhi_chan->ch_state = MHI_CH_STATE_DISABLED; write_unlock_irq(&mhi_chan->lock); Loading Loading @@ -2091,6 +2110,10 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, if (!mhi_chan->offload_ch) { mhi_reset_chan(mhi_cntrl, mhi_chan); mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan); /* notify waiters to proceed with unbinding channel */ if (notify) wake_up_all(&mhi_cntrl->state_event); } MHI_LOG("chan:%d successfully resetted\n", mhi_chan->chan); mutex_unlock(&mhi_chan->mutex); Loading Loading @@ -2361,7 +2384,8 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, mhi_chan->chan, cmd == MHI_CMD_START_CHAN ? "START" : "STOP"); /* if channel is not active state state do not allow to state change */ if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) { if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED && mhi_chan->ch_state != MHI_CH_STATE_SUSPENDED) { ret = -EINVAL; MHI_LOG("channel:%d is not in active state, ch_state%d\n", mhi_chan->chan, mhi_chan->ch_state); Loading
drivers/bus/mhi/core/mhi_pm.c +21 −0 Original line number Diff line number Diff line Loading @@ -1487,6 +1487,26 @@ int mhi_pm_fast_resume(struct mhi_controller *mhi_cntrl, bool notify_client) return -EIO; } if (mhi_cntrl->rddm_supported) { if (mhi_get_exec_env(mhi_cntrl) == MHI_EE_RDDM && !mhi_cntrl->power_down) { mhi_cntrl->ee = MHI_EE_RDDM; write_unlock_irq(&mhi_cntrl->pm_lock); MHI_ERR("RDDM event occurred!\n"); /* notify critical clients with early notifications */ mhi_control_error(mhi_cntrl); mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data, MHI_CB_EE_RDDM); wake_up_all(&mhi_cntrl->state_event); tasklet_enable(&mhi_cntrl->mhi_event->task); goto exit_pm_fast_resume; } } /* restore the states */ mhi_cntrl->pm_state = mhi_cntrl->saved_pm_state; mhi_cntrl->dev_state = mhi_cntrl->saved_dev_state; Loading Loading @@ -1530,6 +1550,7 @@ int mhi_pm_fast_resume(struct mhi_controller *mhi_cntrl, bool notify_client) /* schedules worker if any special purpose events need to be handled */ mhi_special_events_pending(mhi_cntrl); exit_pm_fast_resume: MHI_LOG("Exit 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)); Loading