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

Commit 0448fb29 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "mhi: core: serialize execution environment and power off changes"

parents 7e927e56 26935373
Loading
Loading
Loading
Loading
+78 −3
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <linux/list.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/mhi.h>
@@ -73,6 +74,7 @@ static const char * const mhi_pm_state_str[] = {
	[MHI_PM_BIT_M3] = "M3",
	[MHI_PM_BIT_M3_EXIT] = "M3->M0",
	[MHI_PM_BIT_FW_DL_ERR] = "FW DL Error",
	[MHI_PM_BIT_DEVICE_ERR_DETECT] = "Device Error Detect",
	[MHI_PM_BIT_SYS_ERR_DETECT] = "SYS_ERR Detect",
	[MHI_PM_BIT_SYS_ERR_PROCESS] = "SYS_ERR Process",
	[MHI_PM_BIT_SHUTDOWN_PROCESS] = "SHUTDOWN Process",
@@ -105,6 +107,25 @@ const char *to_mhi_pm_state_str(enum MHI_PM_STATE state)
	return mhi_pm_state_str[index];
}

static void mhi_time_async_cb(struct mhi_device *mhi_dev, u32 sequence,
			      u64 local_time, u64 remote_time)
{
	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;

	MHI_LOG("Time response: seq:%llx local: %llu remote: %llu (ticks)\n",
		sequence, local_time, remote_time);
}

static void mhi_time_us_async_cb(struct mhi_device *mhi_dev, u32 sequence,
				 u64 local_time, u64 remote_time)
{
	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;

	MHI_LOG("Time response: seq:%llx local: %llu remote: %llu (us)\n",
		sequence, LOCAL_TICKS_TO_US(local_time),
		REMOTE_TICKS_TO_US(remote_time));
}

static ssize_t time_show(struct device *dev,
			 struct device_attribute *attr,
			 char *buf)
@@ -117,7 +138,8 @@ static ssize_t time_show(struct device *dev,
	ret = mhi_get_remote_time_sync(mhi_dev, &t_host, &t_device);
	if (ret) {
		MHI_ERR("Failed to obtain time, ret:%d\n", ret);
		return ret;
		return scnprintf(buf, PAGE_SIZE,
				 "Request failed or feature unsupported\n");
	}

	return scnprintf(buf, PAGE_SIZE, "local: %llu remote: %llu (ticks)\n",
@@ -137,7 +159,8 @@ static ssize_t time_us_show(struct device *dev,
	ret = mhi_get_remote_time_sync(mhi_dev, &t_host, &t_device);
	if (ret) {
		MHI_ERR("Failed to obtain time, ret:%d\n", ret);
		return ret;
		return scnprintf(buf, PAGE_SIZE,
				 "Request failed or feature unsupported\n");
	}

	return scnprintf(buf, PAGE_SIZE, "local: %llu remote: %llu (us)\n",
@@ -146,9 +169,59 @@ static ssize_t time_us_show(struct device *dev,
}
static DEVICE_ATTR_RO(time_us);

static ssize_t time_async_show(struct device *dev,
			       struct device_attribute *attr,
			       char *buf)
{
	struct mhi_device *mhi_dev = to_mhi_device(dev);
	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
	u32 seq = prandom_u32();
	int ret;

	if (!seq)
		seq = 1;

	ret = mhi_get_remote_time(mhi_dev, seq, &mhi_time_async_cb);
	if (ret) {
		MHI_ERR("Failed to request time, seq:%llx, ret:%d\n", seq, ret);
		return scnprintf(buf, PAGE_SIZE,
				 "Request failed or feature unsupported\n");
	}

	return scnprintf(buf, PAGE_SIZE,
			 "Requested time asynchronously with seq:%llx\n", seq);
}
static DEVICE_ATTR_RO(time_async);

static ssize_t time_us_async_show(struct device *dev,
				  struct device_attribute *attr,
				  char *buf)
{
	struct mhi_device *mhi_dev = to_mhi_device(dev);
	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
	u32 seq = prandom_u32();
	int ret;

	if (!seq)
		seq = 1;

	ret = mhi_get_remote_time(mhi_dev, seq, &mhi_time_us_async_cb);
	if (ret) {
		MHI_ERR("Failed to request time, seq:%llx, ret:%d\n", seq, ret);
		return scnprintf(buf, PAGE_SIZE,
				 "Request failed or feature unsupported\n");
	}

	return scnprintf(buf, PAGE_SIZE,
			 "Requested time asynchronously with seq:%llx\n", seq);
}
static DEVICE_ATTR_RO(time_us_async);

static struct attribute *mhi_tsync_attrs[] = {
	&dev_attr_time.attr,
	&dev_attr_time_us.attr,
	&dev_attr_time_async.attr,
	&dev_attr_time_us_async.attr,
	NULL,
};

@@ -701,6 +774,8 @@ static int mhi_init_timesync(struct mhi_controller *mhi_cntrl)
		return er_index;
	}

	mhi_tsync->db_support = true;

	time_cfg_offset = time_offset + TIMESYNC_CFG_OFFSET;

	/* advertise host support */
@@ -762,7 +837,7 @@ static int mhi_init_bw_scale(struct mhi_controller *mhi_cntrl)

	/* No ER configured to support BW scale */
	er_index = mhi_get_er_index(mhi_cntrl, MHI_ER_BW_SCALE_ELEMENT_TYPE);
	if (ret < 0)
	if (er_index < 0)
		return er_index;

	bw_cfg_offset += BW_SCALE_CFG_OFFSET;
+7 −2
Original line number Diff line number Diff line
@@ -469,6 +469,7 @@ enum {
	MHI_PM_BIT_M3,
	MHI_PM_BIT_M3_EXIT,
	MHI_PM_BIT_FW_DL_ERR,
	MHI_PM_BIT_DEVICE_ERR_DETECT,
	MHI_PM_BIT_SYS_ERR_DETECT,
	MHI_PM_BIT_SYS_ERR_PROCESS,
	MHI_PM_BIT_SHUTDOWN_PROCESS,
@@ -488,6 +489,8 @@ enum MHI_PM_STATE {
	MHI_PM_M3_EXIT = BIT(MHI_PM_BIT_M3_EXIT),
	/* firmware download failure state */
	MHI_PM_FW_DL_ERR = BIT(MHI_PM_BIT_FW_DL_ERR),
	/* error or shutdown detected or processing state */
	MHI_PM_DEVICE_ERR_DETECT = BIT(MHI_PM_BIT_DEVICE_ERR_DETECT),
	MHI_PM_SYS_ERR_DETECT = BIT(MHI_PM_BIT_SYS_ERR_DETECT),
	MHI_PM_SYS_ERR_PROCESS = BIT(MHI_PM_BIT_SYS_ERR_PROCESS),
	MHI_PM_SHUTDOWN_PROCESS = BIT(MHI_PM_BIT_SHUTDOWN_PROCESS),
@@ -498,8 +501,9 @@ enum MHI_PM_STATE {

#define MHI_REG_ACCESS_VALID(pm_state) ((pm_state & (MHI_PM_POR | MHI_PM_M0 | \
		MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_M3_EXIT | \
		MHI_PM_SYS_ERR_DETECT | MHI_PM_SYS_ERR_PROCESS | \
		MHI_PM_SHUTDOWN_PROCESS | MHI_PM_FW_DL_ERR)))
		MHI_PM_DEVICE_ERR_DETECT | MHI_PM_SYS_ERR_DETECT | \
		MHI_PM_SYS_ERR_PROCESS | MHI_PM_SHUTDOWN_PROCESS | \
		MHI_PM_FW_DL_ERR)))
#define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR)
#define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state >= MHI_PM_LD_ERR_FATAL_DETECT)
#define MHI_DB_ACCESS_VALID(mhi_cntrl) (mhi_cntrl->pm_state & \
@@ -725,6 +729,7 @@ struct tsync_node {
struct mhi_timesync {
	void __iomem *time_reg;
	u32 int_sequence;
	bool db_support;
	spinlock_t lock; /* list protection */
	struct list_head head;
};
+33 −21
Original line number Diff line number Diff line
@@ -1228,6 +1228,10 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
				TO_MHI_EXEC_STR(event));
			switch (event) {
			case MHI_EE_SBL:
				write_lock_irq(&mhi_cntrl->pm_lock);
				mhi_cntrl->ee = MHI_EE_SBL;
				write_unlock_irq(&mhi_cntrl->pm_lock);
				wake_up_all(&mhi_cntrl->state_event);
				st = MHI_ST_TRANSITION_SBL;
				break;
			case MHI_EE_WFW:
@@ -1235,13 +1239,6 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
				st = MHI_ST_TRANSITION_MISSION_MODE;
				break;
			case MHI_EE_RDDM:
				mhi_cntrl->status_cb(mhi_cntrl,
						     mhi_cntrl->priv_data,
						     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:
				MHI_ERR("Unhandled EE event:%s\n",
@@ -1606,13 +1603,17 @@ irqreturn_t mhi_intvec_threaded_handlr(int irq_number, void *dev)
	}

	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);
	MHI_LOG("local ee:%s device ee:%s dev_state:%s\n",
		TO_MHI_EXEC_STR(ee),
		TO_MHI_EXEC_STR(mhi_cntrl->ee),
		TO_MHI_EXEC_STR(ee),
		TO_MHI_STATE_STR(state));

	if (mhi_cntrl->power_down) {
		write_unlock_irq(&mhi_cntrl->pm_lock);
		goto exit_intvec;
	}

	if (state == MHI_STATE_SYS_ERR) {
		MHI_ERR("MHI system error detected\n");
		pm_state = mhi_tryset_pm_state(mhi_cntrl,
@@ -1620,17 +1621,28 @@ irqreturn_t mhi_intvec_threaded_handlr(int irq_number, void *dev)
	}
	write_unlock_irq(&mhi_cntrl->pm_lock);

	/* if device in rddm don't bother processing sys error */
	if (ee == MHI_EE_RDDM) {
		write_lock_irq(&mhi_cntrl->pm_lock);
		if (mhi_cntrl->ee == MHI_EE_RDDM) {
		if (mhi_cntrl->ee != ee) {
			write_unlock_irq(&mhi_cntrl->pm_lock);
			goto exit_intvec;
		}
		mhi_cntrl->ee = MHI_EE_RDDM;
		write_unlock_irq(&mhi_cntrl->pm_lock);

		MHI_ERR("RDDM event occurred!\n");
		mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data,
				     MHI_CB_EE_RDDM);
		wake_up_all(&mhi_cntrl->state_event);
		}

		/* notify critical clients with early notifications */
		mhi_control_error(mhi_cntrl);

		goto exit_intvec;
	}

	if (pm_state == MHI_PM_SYS_ERR_DETECT) {
	/* if device is in RDDM, don't bother processing SYS_ERR */
	if (ee != MHI_EE_RDDM && pm_state == MHI_PM_SYS_ERR_DETECT) {
		wake_up_all(&mhi_cntrl->state_event);

		/* for fatal errors, we let controller decide next step */
@@ -2466,7 +2478,7 @@ int mhi_get_remote_time_sync(struct mhi_device *mhi_dev,
	int ret;

	mutex_lock(&mhi_cntrl->tsync_mutex);
	/* not all devices support time feature */
	/* not all devices support time features */
	if (!mhi_tsync) {
		ret = -EIO;
		goto err_unlock;
@@ -2537,9 +2549,9 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev,
	struct tsync_node *tsync_node;
	int ret;

	/* not all devices support time feature */
	/* not all devices support all time features */
	mutex_lock(&mhi_cntrl->tsync_mutex);
	if (!mhi_tsync) {
	if (!mhi_tsync || !mhi_tsync->db_support) {
		ret = -EIO;
		goto error_unlock;
	}
+64 −46
Original line number Diff line number Diff line
@@ -44,7 +44,8 @@ static void mhi_special_events_pending(struct mhi_controller *mhi_cntrl);
 *     M0 <--> M0
 *     M0 -> FW_DL_ERR
 *     M0 -> M3_ENTER -> M3 -> M3_EXIT --> M0
 * L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS --> POR
 * L1: DEVICE_ERR_DETECT -> SYS_ERR_DETECT
 *     SYS_ERR_DETECT -> SYS_ERR_PROCESS --> POR
 * L2: SHUTDOWN_PROCESS -> LD_ERR_FATAL_DETECT
 *     SHUTDOWN_PROCESS -> DISABLE
 * L3: LD_ERR_FATAL_DETECT <--> LD_ERR_FATAL_DETECT
@@ -60,44 +61,53 @@ static struct mhi_pm_transitions const mhi_state_transitions[] = {
	{
		MHI_PM_POR,
		MHI_PM_POR | MHI_PM_DISABLE | MHI_PM_M0 |
		MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
		MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_FW_DL_ERR |
		MHI_PM_SHUTDOWN_NO_ACCESS
		MHI_PM_DEVICE_ERR_DETECT | MHI_PM_SYS_ERR_DETECT |
		MHI_PM_SHUTDOWN_PROCESS | MHI_PM_LD_ERR_FATAL_DETECT |
		MHI_PM_FW_DL_ERR | MHI_PM_SHUTDOWN_NO_ACCESS
	},
	{
		MHI_PM_M0,
		MHI_PM_M0 | MHI_PM_M2 | MHI_PM_M3_ENTER |
		MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
		MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_FW_DL_ERR |
		MHI_PM_SHUTDOWN_NO_ACCESS
		MHI_PM_DEVICE_ERR_DETECT | MHI_PM_SYS_ERR_DETECT |
		MHI_PM_SHUTDOWN_PROCESS | MHI_PM_LD_ERR_FATAL_DETECT |
		MHI_PM_FW_DL_ERR | MHI_PM_SHUTDOWN_NO_ACCESS
	},
	{
		MHI_PM_M2,
		MHI_PM_M0 | MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
		MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_SHUTDOWN_NO_ACCESS
		MHI_PM_M0 | MHI_PM_DEVICE_ERR_DETECT | MHI_PM_SYS_ERR_DETECT |
		MHI_PM_SHUTDOWN_PROCESS | MHI_PM_LD_ERR_FATAL_DETECT |
		MHI_PM_SHUTDOWN_NO_ACCESS
	},
	{
		MHI_PM_M3_ENTER,
		MHI_PM_M3 | MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
		MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_SHUTDOWN_NO_ACCESS
		MHI_PM_M3 | MHI_PM_DEVICE_ERR_DETECT | MHI_PM_SYS_ERR_DETECT |
		MHI_PM_SHUTDOWN_PROCESS | MHI_PM_LD_ERR_FATAL_DETECT |
		MHI_PM_SHUTDOWN_NO_ACCESS
	},
	{
		MHI_PM_M3,
		MHI_PM_M3_EXIT | MHI_PM_SYS_ERR_DETECT |
		MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_SHUTDOWN_NO_ACCESS
		MHI_PM_M3_EXIT | MHI_PM_DEVICE_ERR_DETECT |
		MHI_PM_SYS_ERR_DETECT | MHI_PM_LD_ERR_FATAL_DETECT |
		MHI_PM_SHUTDOWN_NO_ACCESS
	},
	{
		MHI_PM_M3_EXIT,
		MHI_PM_M0 | MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
		MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_SHUTDOWN_NO_ACCESS
		MHI_PM_M0 | MHI_PM_DEVICE_ERR_DETECT | MHI_PM_SYS_ERR_DETECT |
		MHI_PM_SHUTDOWN_PROCESS | MHI_PM_LD_ERR_FATAL_DETECT |
		MHI_PM_SHUTDOWN_NO_ACCESS
	},
	{
		MHI_PM_FW_DL_ERR,
		MHI_PM_FW_DL_ERR | MHI_PM_SYS_ERR_DETECT |
		MHI_PM_SHUTDOWN_PROCESS | MHI_PM_LD_ERR_FATAL_DETECT |
		MHI_PM_SHUTDOWN_NO_ACCESS
		MHI_PM_FW_DL_ERR | MHI_PM_DEVICE_ERR_DETECT |
		MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
		MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_SHUTDOWN_NO_ACCESS
	},
	/* L1 States */
	{
		MHI_PM_DEVICE_ERR_DETECT,
		MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
		MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_SHUTDOWN_NO_ACCESS
	},
	{
		MHI_PM_SYS_ERR_DETECT,
		MHI_PM_SYS_ERR_PROCESS | MHI_PM_SHUTDOWN_PROCESS |
@@ -461,12 +471,16 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
		ee = mhi_get_exec_env(mhi_cntrl);
	write_unlock_irq(&mhi_cntrl->pm_lock);

	if (!MHI_IN_MISSION_MODE(ee))
	if (!MHI_IN_MISSION_MODE(ee)) {
		MHI_ERR("Invalid EE:%s\n", TO_MHI_EXEC_STR(ee));
		return -EIO;
	}

	mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data,
			     MHI_CB_EE_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);

@@ -573,8 +587,6 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
		mhi_cntrl->ee = MHI_EE_DISABLE_TRANSITION;
		mhi_cntrl->dev_state = MHI_STATE_RESET;
	}
	/* notify controller of power down regardless of state transitions */
	mhi_cntrl->power_down = true;
	write_unlock_irq(&mhi_cntrl->pm_lock);

	/* wake up any threads waiting for state transitions */
@@ -823,6 +835,10 @@ void mhi_process_sys_err(struct mhi_controller *mhi_cntrl)
		return;
	}

	write_lock_irq(&mhi_cntrl->pm_lock);
	mhi_cntrl->power_down = true;
	write_unlock_irq(&mhi_cntrl->pm_lock);

	mhi_queue_disable_transition(mhi_cntrl, MHI_PM_SYS_ERR_PROCESS);
}

@@ -848,14 +864,9 @@ void mhi_pm_st_worker(struct work_struct *work)
			if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state))
				mhi_cntrl->ee = mhi_get_exec_env(mhi_cntrl);
			write_unlock_irq(&mhi_cntrl->pm_lock);
			if (MHI_IN_PBL(mhi_cntrl->ee))
			mhi_fw_load_handler(mhi_cntrl);
			break;
		case MHI_ST_TRANSITION_SBL:
			write_lock_irq(&mhi_cntrl->pm_lock);
			mhi_cntrl->ee = MHI_EE_SBL;
			write_unlock_irq(&mhi_cntrl->pm_lock);
			wake_up_all(&mhi_cntrl->state_event);
			mhi_create_devices(mhi_cntrl);
			break;
		case MHI_ST_TRANSITION_MISSION_MODE:
@@ -942,6 +953,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
	}

	mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0);
	mhi_cntrl->power_down = false;
	mhi_cntrl->pm_state = MHI_PM_POR;
	mhi_cntrl->ee = MHI_EE_MAX;
	current_ee = mhi_get_exec_env(mhi_cntrl);
@@ -969,6 +981,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
	return 0;

error_bhi_offset:
	mhi_cntrl->power_down = true;
	mhi_deinit_free_irq(mhi_cntrl);

error_setup_irq:
@@ -985,7 +998,7 @@ EXPORT_SYMBOL(mhi_async_power_up);
/* Transition MHI into error state and notify critical clients */
void mhi_control_error(struct mhi_controller *mhi_cntrl)
{
	enum MHI_PM_STATE cur_state;
	enum MHI_PM_STATE cur_state, transition_state;
	struct mhi_sfr_info *sfr_info = mhi_cntrl->mhi_sfr;

	MHI_LOG("Enter with pm_state:%s MHI_STATE:%s\n",
@@ -999,13 +1012,18 @@ void mhi_control_error(struct mhi_controller *mhi_cntrl)
		       sfr_info->buf_addr);
	}

	/* link is not down if device is in RDDM */
	transition_state = (mhi_cntrl->ee == MHI_EE_RDDM) ?
		MHI_PM_DEVICE_ERR_DETECT : MHI_PM_LD_ERR_FATAL_DETECT;

	write_lock_irq(&mhi_cntrl->pm_lock);
	cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_LD_ERR_FATAL_DETECT);
	cur_state = mhi_tryset_pm_state(mhi_cntrl, transition_state);
	write_unlock_irq(&mhi_cntrl->pm_lock);

	if (cur_state != MHI_PM_LD_ERR_FATAL_DETECT) {
	/* proceed if we move to device error or are already in error state */
	if (!MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
		MHI_ERR("Failed to transition to state:%s from:%s\n",
			to_mhi_pm_state_str(MHI_PM_LD_ERR_FATAL_DETECT),
			to_mhi_pm_state_str(transition_state),
			to_mhi_pm_state_str(cur_state));
		goto exit_control_error;
	}
@@ -1030,21 +1048,23 @@ void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
	enum MHI_PM_STATE cur_state;
	enum MHI_PM_STATE transition_state = MHI_PM_SHUTDOWN_PROCESS;

	/* if it's not graceful shutdown, force MHI to a linkdown state */
	if (!graceful) {
	mutex_lock(&mhi_cntrl->pm_mutex);

	write_lock_irq(&mhi_cntrl->pm_lock);
	mhi_cntrl->power_down = true;
	/* if it's not graceful shutdown, force MHI to a linkdown state */
	if (!graceful) {
		cur_state = mhi_tryset_pm_state(mhi_cntrl,
						MHI_PM_LD_ERR_FATAL_DETECT);
		write_unlock_irq(&mhi_cntrl->pm_lock);
		mutex_unlock(&mhi_cntrl->pm_mutex);
		if (cur_state != MHI_PM_LD_ERR_FATAL_DETECT)
			MHI_ERR("Failed to move to state:%s from:%s\n",
				to_mhi_pm_state_str(MHI_PM_LD_ERR_FATAL_DETECT),
				to_mhi_pm_state_str(mhi_cntrl->pm_state));

		transition_state = MHI_PM_SHUTDOWN_NO_ACCESS;
	}
	write_unlock_irq(&mhi_cntrl->pm_lock);

	mutex_unlock(&mhi_cntrl->pm_mutex);

	mhi_queue_disable_transition(mhi_cntrl, transition_state);

@@ -1402,6 +1422,9 @@ 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);

	write_lock_irq(&mhi_cntrl->pm_lock);
	/* restore the states */
	mhi_cntrl->pm_state = mhi_cntrl->saved_pm_state;
@@ -1414,19 +1437,14 @@ int mhi_pm_fast_resume(struct mhi_controller *mhi_cntrl, bool notify_client)
		break;
	case MHI_PM_M2:
		read_lock_bh(&mhi_cntrl->pm_lock);
		/*
		 * we're doing a double check of pm_state because by the time we
		 * grab the pm_lock, device may have already initiate a M0 on
		 * its own. If that's the case we should not be toggling device
		 * wake.
		 */
		if (mhi_cntrl->pm_state == MHI_PM_M2) {
		mhi_cntrl->wake_get(mhi_cntrl, true);
		mhi_cntrl->wake_put(mhi_cntrl, true);
		}
		read_unlock_bh(&mhi_cntrl->pm_lock);
	}

	/* now it's safe to process ctrl events */
	tasklet_enable(&mhi_cntrl->mhi_event->task);

	/*
	 * In fast suspend/resume case device is not aware host transition
	 * to suspend state. So, device could be triggering a interrupt while