mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-12 07:37:31 -04:00
msm: mhi_dev: Workqueue to handle host wakeup in M3+D0
After the change <a260532fb4a3> ("msm: mhi_dev: Handle host wakeup in
M3/D0"), if a wakeup host request is received during M3+D0, wait for
D3 hot/cold before sending the host wakeup request inside a mutex lock.
Regression: In mhi_sm_dev_event_manager, the mhi_sm_ctx->mhi_state_lock
mutex is held while calling mhi_sm_wakeup_host. This means waiting for
mhi_sm_ctx->d_state to transition to MHI_SM_EP_PCIE_D3_HOT_STATE or
MHI_SM_EP_PCIE_D3_COLD_STATE with the lock held. This prevents
mhi_sm_pcie_event_manager from processing the D3 hot/cold event because
it also waits for the same lock.
Create a separate workqueue to wait for D3 hot/cold before waking up
the host if a wakeup request is received in M3+D0.
Change-Id: I1d3eff63b1968f5ded0d0c84a0fa71e893d74d45
Signed-off-by: Sumit Kumar <quic_sumk@quicinc.com>
This commit is contained in:
parent
a92f080134
commit
90f5b1c30a
2 changed files with 77 additions and 42 deletions
|
|
@ -32,6 +32,8 @@
|
|||
/* Maximum wait time for D state transitions to D3hot */
|
||||
#define M3_DO_WAKEUP_TIMEOUT_MS 2500
|
||||
|
||||
static void wait_d3_and_wakeup(struct work_struct *work);
|
||||
static int mhi_dev_sm_get_mhi_pcie_states(uint32_t *mstate, uint32_t *dstate);
|
||||
|
||||
static inline const char *mhi_sm_dev_event_str(enum mhi_dev_event state)
|
||||
{
|
||||
|
|
@ -238,6 +240,8 @@ struct mhi_sm_dev {
|
|||
struct mutex mhi_state_lock;
|
||||
bool syserr_occurred;
|
||||
struct workqueue_struct *mhi_sm_wq;
|
||||
struct workqueue_struct *mhi_wake_wq;
|
||||
struct work_struct mhi_wake_work;
|
||||
atomic_t pending_device_events;
|
||||
atomic_t pending_pcie_events;
|
||||
struct mhi_sm_stats stats;
|
||||
|
|
@ -742,10 +746,9 @@ exit:
|
|||
* Return: 0:success
|
||||
* negative: failure
|
||||
*/
|
||||
static int mhi_sm_wakeup_host(enum mhi_dev_event event)
|
||||
static int mhi_sm_wakeup_host(void)
|
||||
{
|
||||
int res = 0;
|
||||
int timeout = 0;
|
||||
enum ep_pcie_event pcie_event;
|
||||
|
||||
MHI_SM_FUNC_ENTRY();
|
||||
|
|
@ -757,33 +760,10 @@ static int mhi_sm_wakeup_host(enum mhi_dev_event event)
|
|||
MHI_SM_ERR("Failed switching to M0 state\n");
|
||||
} else if (mhi_sm_ctx->mhi_state == MHI_DEV_M3_STATE) {
|
||||
/*
|
||||
* Handle host wakeup in M3 + D0 states.
|
||||
*
|
||||
* When a MHI WAKE request is received while device is in D0,
|
||||
* wait for D3 and wakeup the host using inband PME.
|
||||
* If the MHI state changes to M0 while waiting for D3,
|
||||
* exit, since both MHI and the device are in active state.
|
||||
* Check and send D3_HOT to enable waking up the host
|
||||
* using inband PME if the host is in D3_HOT state, otherwise
|
||||
* send D3_COLD to wake up the host.
|
||||
*/
|
||||
if (mhi_sm_ctx->d_state == MHI_SM_EP_PCIE_D0_STATE) {
|
||||
timeout = ktime_add_ms(ktime_get(), M3_DO_WAKEUP_TIMEOUT_MS);
|
||||
while (1) {
|
||||
/* Received M0 */
|
||||
if (mhi_sm_ctx->mhi_state == MHI_DEV_M0_STATE)
|
||||
goto exit;
|
||||
/* Received D3 state */
|
||||
if (mhi_sm_ctx->d_state == MHI_SM_EP_PCIE_D3_HOT_STATE ||
|
||||
mhi_sm_ctx->d_state == MHI_SM_EP_PCIE_D3_COLD_STATE)
|
||||
goto wakeup_host;
|
||||
if (ktime_after(ktime_get(), timeout)) {
|
||||
MHI_SM_ERR(mhi->vf_id,
|
||||
"M3, D0 wakeup host is not supported %d\n", res);
|
||||
goto exit;
|
||||
}
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
}
|
||||
wakeup_host:
|
||||
/* Received D3hot or D3cold, send the wakeup request */
|
||||
if (mhi_sm_ctx->d_state == MHI_SM_EP_PCIE_D3_HOT_STATE)
|
||||
pcie_event = EP_PCIE_EVENT_PM_D3_HOT;
|
||||
else
|
||||
|
|
@ -934,9 +914,7 @@ static void mhi_sm_dev_event_manager(struct work_struct *work)
|
|||
break;
|
||||
case MHI_DEV_EVENT_HW_ACC_WAKEUP:
|
||||
case MHI_DEV_EVENT_CORE_WAKEUP:
|
||||
res = mhi_sm_wakeup_host(chg_event->event);
|
||||
if (res)
|
||||
MHI_SM_ERR("Failed to wakeup MHI host\n");
|
||||
queue_work(mhi_sm_ctx->mhi_wake_wq, &mhi_sm_ctx->mhi_wake_work);
|
||||
break;
|
||||
case MHI_DEV_EVENT_CTRL_TRIG:
|
||||
case MHI_DEV_EVENT_M1_STATE:
|
||||
|
|
@ -1147,9 +1125,19 @@ int mhi_dev_sm_init(struct mhi_dev *mhi_dev)
|
|||
if (!mhi_sm_ctx->mhi_sm_wq) {
|
||||
MHI_SM_ERR("Failed to create singlethread_workqueue: sm_wq\n");
|
||||
res = -ENOMEM;
|
||||
goto fail_init_wq;
|
||||
goto fail_init_sm_wq;
|
||||
}
|
||||
|
||||
if (!mhi_sm_ctx->mhi_wake_wq)
|
||||
mhi_sm_ctx->mhi_wake_wq = alloc_workqueue(
|
||||
"mhi_wake_wq", WQ_HIGHPRI | WQ_UNBOUND, 1);
|
||||
if (!mhi_sm_ctx->mhi_wake_wq) {
|
||||
MHI_SM_ERR("Failed to create singlethread_workqueue: wake_wq\n");
|
||||
res = -ENOMEM;
|
||||
goto fail_init_wake_wq;
|
||||
}
|
||||
INIT_WORK(&mhi_sm_ctx->mhi_wake_work, wait_d3_and_wakeup);
|
||||
|
||||
mutex_init(&mhi_sm_ctx->mhi_state_lock);
|
||||
mhi_sm_ctx->mhi_dev = mhi_dev;
|
||||
mhi_sm_ctx->mhi_state = MHI_DEV_RESET_STATE;
|
||||
|
|
@ -1162,7 +1150,10 @@ int mhi_dev_sm_init(struct mhi_dev *mhi_dev)
|
|||
MHI_SM_FUNC_EXIT();
|
||||
return 0;
|
||||
|
||||
fail_init_wq:
|
||||
fail_init_wake_wq:
|
||||
flush_workqueue(mhi_sm_ctx->mhi_sm_wq);
|
||||
destroy_workqueue(mhi_sm_ctx->mhi_sm_wq);
|
||||
fail_init_sm_wq:
|
||||
mhi_sm_ctx = NULL;
|
||||
mhi_sm_debugfs_destroy();
|
||||
return res;
|
||||
|
|
@ -1190,20 +1181,20 @@ int mhi_dev_sm_exit(struct mhi_dev *mhi_dev)
|
|||
EXPORT_SYMBOL(mhi_dev_sm_exit);
|
||||
|
||||
/**
|
||||
* mhi_dev_sm_get_mhi_state() -Get current MHI state.
|
||||
* mhi_dev_sm_get_mhi_pcie_states() -Get current MHI and Pcie states.
|
||||
* @state: return param
|
||||
*
|
||||
* Returns the current MHI state of the state machine.
|
||||
* Returns the current MHI and PCIe states of the state machine.
|
||||
*
|
||||
* Return: 0 success
|
||||
* -EINVAL: invalid param
|
||||
* -EFAULT: state machine isn't initialized
|
||||
*/
|
||||
int mhi_dev_sm_get_mhi_state(enum mhi_dev_state *state)
|
||||
static int mhi_dev_sm_get_mhi_pcie_states(uint32_t *mstate, uint32_t *dstate)
|
||||
{
|
||||
MHI_SM_FUNC_ENTRY();
|
||||
|
||||
if (!state) {
|
||||
if (!mstate || !dstate) {
|
||||
MHI_SM_ERR("Fail: Null argument\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -1211,15 +1202,60 @@ int mhi_dev_sm_get_mhi_state(enum mhi_dev_state *state)
|
|||
MHI_SM_ERR("Fail: MHI SM is not initialized\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
*state = mhi_sm_ctx->mhi_state;
|
||||
mutex_lock(&mhi_sm_ctx->mhi_state_lock);
|
||||
*mstate = mhi_sm_ctx->mhi_state;
|
||||
*dstate = mhi_sm_ctx->d_state;
|
||||
mutex_unlock(&mhi_sm_ctx->mhi_state_lock);
|
||||
MHI_SM_DBG("state machine states are: %s and %s\n",
|
||||
mhi_sm_mstate_str(*state),
|
||||
mhi_sm_dstate_str(mhi_sm_ctx->d_state));
|
||||
mhi_sm_mstate_str(*mstate),
|
||||
mhi_sm_dstate_str(*dstate));
|
||||
|
||||
MHI_SM_FUNC_EXIT();
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mhi_dev_sm_get_mhi_state);
|
||||
|
||||
static void wait_d3_and_wakeup(struct work_struct *work)
|
||||
{
|
||||
struct mhi_sm_dev *mhi_sm_ctx = container_of(work, struct mhi_sm_dev, mhi_wake_work);
|
||||
enum mhi_dev_state mstate;
|
||||
enum mhi_sm_ep_pcie_state dstate;
|
||||
ktime_t timeout = 0;
|
||||
|
||||
if (mhi_dev_sm_get_mhi_pcie_states(&mstate, &dstate)) {
|
||||
MHI_SM_ERR("Unable to read states\n");
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Handle host wakeup in M3 + D0 states.
|
||||
* When a MHI WAKE request is received while device is in D0,
|
||||
* wait for D3 and wakeup the host using inband PME.
|
||||
* If the MHI state changes to M0 while waiting for D3,
|
||||
* exit, since both MHI and the device are in active state
|
||||
*/
|
||||
if (dstate == MHI_SM_EP_PCIE_D0_STATE) {
|
||||
timeout = ktime_add_ms(ktime_get(), M3_DO_WAKEUP_TIMEOUT_MS);
|
||||
while (1) {
|
||||
mhi_dev_sm_get_mhi_pcie_states(&mstate, &dstate);
|
||||
if (mstate == MHI_DEV_M0_STATE) {
|
||||
MHI_SM_DBG("M0 state received\n");
|
||||
return;
|
||||
}
|
||||
if (dstate == MHI_SM_EP_PCIE_D3_HOT_STATE ||
|
||||
dstate == MHI_SM_EP_PCIE_D3_COLD_STATE) {
|
||||
MHI_SM_DBG("D3 state received\n");
|
||||
goto send_host_wakeup;
|
||||
}
|
||||
if (ktime_after(ktime_get(), timeout)) {
|
||||
MHI_SM_ERR("Neither received D3 nor M0 in stipulated time\n");
|
||||
return;
|
||||
}
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
}
|
||||
send_host_wakeup:
|
||||
if (dstate == MHI_SM_EP_PCIE_D3_HOT_STATE || dstate == MHI_SM_EP_PCIE_D3_COLD_STATE)
|
||||
mhi_sm_wakeup_host();
|
||||
}
|
||||
|
||||
/**
|
||||
* mhi_dev_sm_set_ready() -Set MHI state to ready.
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ int mhi_dev_sm_init(struct mhi_dev *dev);
|
|||
int mhi_dev_sm_exit(struct mhi_dev *dev);
|
||||
int mhi_dev_sm_set_ready(void);
|
||||
int mhi_dev_notify_sm_event(enum mhi_dev_event event);
|
||||
int mhi_dev_sm_get_mhi_state(enum mhi_dev_state *state);
|
||||
int mhi_dev_sm_syserr(void);
|
||||
void mhi_dev_sm_pcie_handler(struct ep_pcie_notify *notify);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue