Commit 0c6b20a1 authored by Manivannan Sadhasivam's avatar Manivannan Sadhasivam Committed by Greg Kroah-Hartman
Browse files

bus: mhi: core: Add support for MHI suspend and resume



Add support for MHI suspend and resume states. While at it, the
mhi_notify() function needs to be exported as well.

Signed-off-by: default avatarManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Link: https://lore.kernel.org/r/20200427075829.9304-2-manivannan.sadhasivam@linaro.org


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 48a4cc95
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -267,7 +267,7 @@ int mhi_destroy_device(struct device *dev, void *data)
	return 0;
}

static void mhi_notify(struct mhi_device *mhi_dev, enum mhi_callback cb_reason)
void mhi_notify(struct mhi_device *mhi_dev, enum mhi_callback cb_reason)
{
	struct mhi_driver *mhi_drv;

@@ -279,6 +279,7 @@ static void mhi_notify(struct mhi_device *mhi_dev, enum mhi_callback cb_reason)
	if (mhi_drv->status_cb)
		mhi_drv->status_cb(mhi_dev, cb_reason);
}
EXPORT_SYMBOL_GPL(mhi_notify);

/* Bind MHI channels to MHI devices */
void mhi_create_devices(struct mhi_controller *mhi_cntrl)
+143 −0
Original line number Diff line number Diff line
@@ -669,6 +669,149 @@ void mhi_pm_st_worker(struct work_struct *work)
	}
}

int mhi_pm_suspend(struct mhi_controller *mhi_cntrl)
{
	struct mhi_chan *itr, *tmp;
	struct device *dev = &mhi_cntrl->mhi_dev->dev;
	enum mhi_pm_state new_state;
	int ret;

	if (mhi_cntrl->pm_state == MHI_PM_DISABLE)
		return -EINVAL;

	if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
		return -EIO;

	/* Return busy if there are any pending resources */
	if (atomic_read(&mhi_cntrl->dev_wake))
		return -EBUSY;

	/* Take MHI out of M2 state */
	read_lock_bh(&mhi_cntrl->pm_lock);
	mhi_cntrl->wake_get(mhi_cntrl, false);
	read_unlock_bh(&mhi_cntrl->pm_lock);

	ret = wait_event_timeout(mhi_cntrl->state_event,
				 mhi_cntrl->dev_state == MHI_STATE_M0 ||
				 mhi_cntrl->dev_state == MHI_STATE_M1 ||
				 MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
				 msecs_to_jiffies(mhi_cntrl->timeout_ms));

	read_lock_bh(&mhi_cntrl->pm_lock);
	mhi_cntrl->wake_put(mhi_cntrl, false);
	read_unlock_bh(&mhi_cntrl->pm_lock);

	if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
		dev_err(dev,
			"Could not enter M0/M1 state");
		return -EIO;
	}

	write_lock_irq(&mhi_cntrl->pm_lock);

	if (atomic_read(&mhi_cntrl->dev_wake)) {
		write_unlock_irq(&mhi_cntrl->pm_lock);
		return -EBUSY;
	}

	dev_info(dev, "Allowing M3 transition\n");
	new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3_ENTER);
	if (new_state != MHI_PM_M3_ENTER) {
		write_unlock_irq(&mhi_cntrl->pm_lock);
		dev_err(dev,
			"Error setting to PM state: %s from: %s\n",
			to_mhi_pm_state_str(MHI_PM_M3_ENTER),
			to_mhi_pm_state_str(mhi_cntrl->pm_state));
		return -EIO;
	}

	/* Set MHI to M3 and wait for completion */
	mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M3);
	write_unlock_irq(&mhi_cntrl->pm_lock);
	dev_info(dev, "Wait for M3 completion\n");

	ret = wait_event_timeout(mhi_cntrl->state_event,
				 mhi_cntrl->dev_state == MHI_STATE_M3 ||
				 MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
				 msecs_to_jiffies(mhi_cntrl->timeout_ms));

	if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
		dev_err(dev,
			"Did not enter M3 state, MHI state: %s, PM state: %s\n",
			TO_MHI_STATE_STR(mhi_cntrl->dev_state),
			to_mhi_pm_state_str(mhi_cntrl->pm_state));
		return -EIO;
	}

	/* Notify clients about entering LPM */
	list_for_each_entry_safe(itr, tmp, &mhi_cntrl->lpm_chans, node) {
		mutex_lock(&itr->mutex);
		if (itr->mhi_dev)
			mhi_notify(itr->mhi_dev, MHI_CB_LPM_ENTER);
		mutex_unlock(&itr->mutex);
	}

	return 0;
}
EXPORT_SYMBOL_GPL(mhi_pm_suspend);

int mhi_pm_resume(struct mhi_controller *mhi_cntrl)
{
	struct mhi_chan *itr, *tmp;
	struct device *dev = &mhi_cntrl->mhi_dev->dev;
	enum mhi_pm_state cur_state;
	int ret;

	dev_info(dev, "Entered with PM state: %s, MHI state: %s\n",
		 to_mhi_pm_state_str(mhi_cntrl->pm_state),
		 TO_MHI_STATE_STR(mhi_cntrl->dev_state));

	if (mhi_cntrl->pm_state == MHI_PM_DISABLE)
		return 0;

	if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
		return -EIO;

	/* Notify clients about exiting LPM */
	list_for_each_entry_safe(itr, tmp, &mhi_cntrl->lpm_chans, node) {
		mutex_lock(&itr->mutex);
		if (itr->mhi_dev)
			mhi_notify(itr->mhi_dev, MHI_CB_LPM_EXIT);
		mutex_unlock(&itr->mutex);
	}

	write_lock_irq(&mhi_cntrl->pm_lock);
	cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3_EXIT);
	if (cur_state != MHI_PM_M3_EXIT) {
		write_unlock_irq(&mhi_cntrl->pm_lock);
		dev_info(dev,
			 "Error setting to PM state: %s from: %s\n",
			 to_mhi_pm_state_str(MHI_PM_M3_EXIT),
			 to_mhi_pm_state_str(mhi_cntrl->pm_state));
		return -EIO;
	}

	/* Set MHI to M0 and wait for completion */
	mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M0);
	write_unlock_irq(&mhi_cntrl->pm_lock);

	ret = wait_event_timeout(mhi_cntrl->state_event,
				 mhi_cntrl->dev_state == MHI_STATE_M0 ||
				 MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
				 msecs_to_jiffies(mhi_cntrl->timeout_ms));

	if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
		dev_err(dev,
			"Did not enter M0 state, MHI state: %s, PM state: %s\n",
			TO_MHI_STATE_STR(mhi_cntrl->dev_state),
			to_mhi_pm_state_str(mhi_cntrl->pm_state));
		return -EIO;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(mhi_pm_resume);

int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl)
{
	int ret;
+19 −0
Original line number Diff line number Diff line
@@ -568,6 +568,13 @@ void mhi_driver_unregister(struct mhi_driver *mhi_drv);
void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl,
		       enum mhi_state state);

/**
 * mhi_notify - Notify the MHI client driver about client device status
 * @mhi_dev: MHI device instance
 * @cb_reason: MHI callback reason
 */
void mhi_notify(struct mhi_device *mhi_dev, enum mhi_callback cb_reason);

/**
 * mhi_prepare_for_power_up - Do pre-initialization before power up.
 *                            This is optional, call this before power up if
@@ -604,6 +611,18 @@ void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful);
 */
void mhi_unprepare_after_power_down(struct mhi_controller *mhi_cntrl);

/**
 * mhi_pm_suspend - Move MHI into a suspended state
 * @mhi_cntrl: MHI controller
 */
int mhi_pm_suspend(struct mhi_controller *mhi_cntrl);

/**
 * mhi_pm_resume - Resume MHI from suspended state
 * @mhi_cntrl: MHI controller
 */
int mhi_pm_resume(struct mhi_controller *mhi_cntrl);

/**
 * mhi_download_rddm_img - Download ramdump image from device for
 *                         debugging purpose.