diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index cc31ed191943..cb57f23f7875 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -946,11 +946,13 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, mhi_event->mhi_cntrl = mhi_cntrl; spin_lock_init(&mhi_event->lock); - if (mhi_event->data_type == MHI_ER_CTRL) - tasklet_init(&mhi_event->task, mhi_ctrl_ev_task, - (ulong)mhi_event); + + if (mhi_event->priority == MHI_ER_PRIORITY_HI_SLEEP) + INIT_WORK(&mhi_event->work, mhi_process_ev_work); else - tasklet_init(&mhi_event->task, mhi_ev_task, + tasklet_init(&mhi_event->task, + (mhi_event->data_type == MHI_ER_CTRL) ? + mhi_ctrl_ev_task : mhi_ev_task, (ulong)mhi_event); } diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h index 78c33c00393c..b88330658270 100644 --- a/drivers/bus/mhi/host/internal.h +++ b/drivers/bus/mhi/host/internal.h @@ -201,6 +201,7 @@ struct mhi_event { struct mhi_ring ring; struct db_cfg db_cfg; struct tasklet_struct task; + struct work_struct work; spinlock_t lock; int (*process_event)(struct mhi_controller *mhi_cntrl, struct mhi_event *mhi_event, @@ -359,6 +360,8 @@ void mhi_reset_chan(struct mhi_controller *mhi_cntrl, /* Event processing methods */ void mhi_ctrl_ev_task(unsigned long data); void mhi_ev_task(unsigned long data); +void mhi_process_ev_work(struct work_struct *work); +void mhi_process_sleeping_events(struct mhi_controller *mhi_cntrl); int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, struct mhi_event *mhi_event, u32 event_quota); int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c index 116f86685851..ec9c7e12f9ba 100644 --- a/drivers/bus/mhi/host/main.c +++ b/drivers/bus/mhi/host/main.c @@ -426,6 +426,30 @@ void mhi_create_devices(struct mhi_controller *mhi_cntrl) } } +void mhi_process_sleeping_events(struct mhi_controller *mhi_cntrl) +{ + struct mhi_event *mhi_event; + struct mhi_event_ctxt *er_ctxt; + struct mhi_ring *ev_ring; + int i; + + mhi_event = mhi_cntrl->mhi_event; + for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { + if (mhi_event->offload_ev || mhi_event->priority != + MHI_ER_PRIORITY_HI_SLEEP) + continue; + + er_ctxt = &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; + ev_ring = &mhi_event->ring; + + /* Only proceed if event ring has pending events */ + if (ev_ring->rp == mhi_to_virtual(ev_ring, er_ctxt->rp)) + continue; + + queue_work(mhi_cntrl->hiprio_wq, &mhi_event->work); + } +} + irqreturn_t mhi_irq_handler(int irq_number, void *priv) { struct mhi_event *mhi_event = priv; @@ -467,6 +491,9 @@ irqreturn_t mhi_irq_handler(int irq_number, void *priv) case MHI_ER_PRIORITY_DEFAULT_NOSLEEP: tasklet_schedule(&mhi_event->task); break; + case MHI_ER_PRIORITY_HI_SLEEP: + queue_work(mhi_cntrl->hiprio_wq, &mhi_event->work); + break; default: dev_dbg(dev, "skip unknown priority event\n"); break; @@ -1095,6 +1122,24 @@ void mhi_ctrl_ev_task(unsigned long data) } } +void mhi_process_ev_work(struct work_struct *work) +{ + struct mhi_event *mhi_event = container_of(work, struct mhi_event, + work); + struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl; + struct device *dev = mhi_cntrl->cntrl_dev; + + dev_dbg(dev, "Enter with pm_state:%s MHI_STATE:%s ee:%s\n", + to_mhi_pm_state_str(mhi_cntrl->pm_state), + mhi_state_str(mhi_cntrl->dev_state), + TO_MHI_EXEC_STR(mhi_cntrl->ee)); + + if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state))) + return; + + mhi_event->process_event(mhi_cntrl, mhi_event, U32_MAX); +} + static bool mhi_is_ring_full(struct mhi_controller *mhi_cntrl, struct mhi_ring *ring) { diff --git a/drivers/bus/mhi/host/pm.c b/drivers/bus/mhi/host/pm.c index cda374bfc9ef..03c6a3529127 100644 --- a/drivers/bus/mhi/host/pm.c +++ b/drivers/bus/mhi/host/pm.c @@ -432,6 +432,8 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl) read_unlock_bh(&mhi_cntrl->pm_lock); + mhi_process_sleeping_events(mhi_cntrl); + /* * The MHI devices are only created when the client device switches its * Execution Environment (EE) to either SBL or AMSS states @@ -497,7 +499,10 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl) if (mhi_event->offload_ev) continue; disable_irq(mhi_cntrl->irq[mhi_event->irq]); - tasklet_kill(&mhi_event->task); + if (mhi_event->priority == MHI_ER_PRIORITY_HI_SLEEP) + cancel_work_sync(&mhi_event->work); + else + tasklet_kill(&mhi_event->task); } /* Release lock and wait for all pending threads to complete */ @@ -630,7 +635,10 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { if (mhi_event->offload_ev) continue; - tasklet_kill(&mhi_event->task); + if (mhi_event->priority == MHI_ER_PRIORITY_HI_SLEEP) + cancel_work_sync(&mhi_event->work); + else + tasklet_kill(&mhi_event->task); } /* Release lock and wait for all pending threads to complete */ @@ -766,6 +774,9 @@ void mhi_pm_st_worker(struct work_struct *work) write_lock_irq(&mhi_cntrl->pm_lock); mhi_cntrl->ee = MHI_EE_SBL; write_unlock_irq(&mhi_cntrl->pm_lock); + + mhi_process_sleeping_events(mhi_cntrl); + /* * The MHI devices are only created when the client * device switches its Execution Environment (EE) to diff --git a/include/linux/mhi.h b/include/linux/mhi.h index 7c182fda4d7c..3dc3ef2bca65 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -202,10 +202,12 @@ enum mhi_er_data_type { * enum mhi_er_priority - Event ring processing priority * @MHI_ER_PRIORITY_DEFAULT_NOSLEEP: processed by tasklet * @MHI_ER_PRIORITY_HI_NOSLEEP: processed by hi-priority tasklet + * @MHI_ER_PRIORITY_HI_SLEEP: processed by hi-priority wq */ enum mhi_er_priority { MHI_ER_PRIORITY_DEFAULT_NOSLEEP, MHI_ER_PRIORITY_HI_NOSLEEP, + MHI_ER_PRIORITY_HI_SLEEP, }; /**