Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6acfe560 authored by Hemant Kumar's avatar Hemant Kumar
Browse files

mhi: core: Prevent MHI reg read upon endpoint crash



MHI reset is triggered as part of EP shutdown. Shutdown
is blocked until MHI RESET bit is cleared in MHI CTRL reg.
There is a possibility of PCIe link going down during this
time. Since MHI RESET bit never gets cleared after 2 sec
wait_event_timeout reads the MHI CTRL reg and this results
into a memory abort. Fix this issue by using a boolean flag
as condition in wait_event_timeout. This flag is set when
MHI reset is triggered and cleared when reset is done.

Change-Id: I5b1831518255a38cde5b60406b5b9e9386b5477e
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
parent cfab2293
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -1734,9 +1734,15 @@ irqreturn_t mhi_intvec_handlr(int irq_number, void *dev)
{

	struct mhi_controller *mhi_cntrl = dev;
	u32 in_reset = -1;

	/* wake up any events waiting for state change */
	MHI_VERB("Enter\n");
	if (unlikely(mhi_cntrl->initiate_mhi_reset)) {
		mhi_read_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
			MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, &in_reset);
		mhi_cntrl->initiate_mhi_reset = !!in_reset;
	}
	wake_up_all(&mhi_cntrl->state_event);
	MHI_VERB("Exit\n");

+11 −8
Original line number Diff line number Diff line
@@ -167,6 +167,7 @@ void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl,
	if (state == MHI_STATE_RESET) {
		mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
				    MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 1);
		mhi_cntrl->initiate_mhi_reset = true;
	} else {
		mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
				(state << MHICTRL_MHISTATE_SHIFT));
@@ -602,20 +603,18 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,

	/* trigger MHI RESET so device will not access host ddr */
	if (MHI_REG_ACCESS_VALID(prev_state)) {
		u32 in_reset = -1;
		unsigned long timeout = msecs_to_jiffies(mhi_cntrl->timeout_ms);

		MHI_LOG("Trigger device into MHI_RESET\n");

		write_lock_irq(&mhi_cntrl->pm_lock);
		mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
		write_unlock_irq(&mhi_cntrl->pm_lock);

		/* wait for reset to be cleared */
		ret = wait_event_timeout(mhi_cntrl->state_event,
					 mhi_read_reg_field(mhi_cntrl,
						mhi_cntrl->regs, MHICTRL,
						MHICTRL_RESET_MASK,
						MHICTRL_RESET_SHIFT, &in_reset)
					 || !in_reset, timeout);
		if ((!ret || in_reset) && cur_state == MHI_PM_SYS_ERR_PROCESS) {
				!mhi_cntrl->initiate_mhi_reset, timeout);
		if (!ret && cur_state == MHI_PM_SYS_ERR_PROCESS) {
			MHI_CRITICAL("Device failed to exit RESET state\n");
			mutex_unlock(&mhi_cntrl->pm_mutex);
			return;
@@ -625,7 +624,11 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
		 * device cleares INTVEC as part of RESET processing,
		 * re-program it
		 */
		mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0);
		if (!mhi_cntrl->initiate_mhi_reset)
			mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->bhi,
				BHI_INTVEC, 0);

		mhi_cntrl->initiate_mhi_reset = false;
	}

	MHI_LOG("Waiting for all pending event ring processing to complete\n");
+1 −0
Original line number Diff line number Diff line
@@ -399,6 +399,7 @@ struct mhi_controller {
	/* controller specific data */
	const char *name;
	bool power_down;
	bool initiate_mhi_reset;
	void *priv_data;
	void *log_buf;
	struct dentry *dentry;