forked from Minki/linux
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: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Link: https://lore.kernel.org/r/20200427075829.9304-2-manivannan.sadhasivam@linaro.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
48a4cc9503
commit
0c6b20a1d7
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user