static enum MHI_STATUS process_m1_transition( struct mhi_device_ctxt *mhi_dev_ctxt, enum STATE_TRANSITION cur_work_item) { unsigned long flags = 0; int ret_val = 0; int r = 0; mhi_log(MHI_MSG_INFO, "Processing M1 state transition from state %d\n", mhi_dev_ctxt->mhi_state); write_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); if (!mhi_dev_ctxt->flags.pending_M3) { mhi_log(MHI_MSG_INFO, "Setting M2 Transition flag\n"); atomic_inc(&mhi_dev_ctxt->flags.m2_transition); mhi_dev_ctxt->mhi_state = MHI_STATE_M2; mhi_log(MHI_MSG_INFO, "Allowing transition to M2\n"); mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M2); mhi_dev_ctxt->counters.m1_m2++; } write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); ret_val = mhi_set_bus_request(mhi_dev_ctxt, 0); if (ret_val) mhi_log(MHI_MSG_INFO, "Failed to update bus request\n"); mhi_log(MHI_MSG_INFO, "Debouncing M2\n"); msleep(MHI_M2_DEBOUNCE_TMR_MS); write_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); mhi_log(MHI_MSG_INFO, "Pending acks %d\n", atomic_read(&mhi_dev_ctxt->counters.outbound_acks)); if (atomic_read(&mhi_dev_ctxt->counters.outbound_acks) || mhi_dev_ctxt->flags.pending_M3) { mhi_assert_device_wake(mhi_dev_ctxt); } else { pm_runtime_mark_last_busy( &mhi_dev_ctxt->dev_info->plat_dev->dev); r = pm_request_autosuspend( &mhi_dev_ctxt->dev_info->plat_dev->dev); if (r) { mhi_log(MHI_MSG_ERROR, "Failed to remove counter ret %d\n", r); BUG_ON(mhi_dev_ctxt->dev_info-> plat_dev->dev.power.runtime_error); } } atomic_set(&mhi_dev_ctxt->flags.m2_transition, 0); mhi_log(MHI_MSG_INFO, "M2 transition complete.\n"); write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); BUG_ON(atomic_read(&mhi_dev_ctxt->outbound_acks) < 0); return MHI_STATUS_SUCCESS; }
enum MHI_STATUS mhi_trigger_reset(struct mhi_device_ctxt *mhi_dev_ctxt) { enum MHI_STATUS ret_val; unsigned long flags = 0; mhi_log(MHI_MSG_INFO, "Entered\n"); write_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); mhi_dev_ctxt->mhi_state = MHI_STATE_SYS_ERR; write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); mhi_log(MHI_MSG_INFO, "Setting RESET to MDM.\n"); mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_RESET); mhi_log(MHI_MSG_INFO, "Transitioning state to RESET\n"); ret_val = mhi_init_state_transition(mhi_dev_ctxt, STATE_TRANSITION_RESET); if (MHI_STATUS_SUCCESS != ret_val) mhi_log(MHI_MSG_CRITICAL, "Failed to initiate 0x%x state trans ret %d\n", STATE_TRANSITION_RESET, ret_val); mhi_log(MHI_MSG_INFO, "Exiting\n"); return ret_val; }
int mhi_initiate_m3(mhi_device_ctxt *mhi_dev_ctxt) { unsigned long flags; int r = 0; int abort_m3 = 0; mhi_log(MHI_MSG_INFO, "Entered MHI state %d, Pending M0 %d Pending M3 %d\n", mhi_dev_ctxt->mhi_state, mhi_dev_ctxt->flags.pending_M0, mhi_dev_ctxt->flags.pending_M3); mutex_lock(&mhi_dev_ctxt->pm_lock); switch (mhi_dev_ctxt->mhi_state) { case MHI_STATE_M1: case MHI_STATE_M2: mhi_log(MHI_MSG_INFO, "Triggering wake out of M2\n"); write_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); mhi_dev_ctxt->flags.pending_M3 = 1; mhi_assert_device_wake(mhi_dev_ctxt); write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); r = wait_event_interruptible_timeout(*mhi_dev_ctxt->M0_event, mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || mhi_dev_ctxt->mhi_state == MHI_STATE_M1, msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT)); if (0 == r || -ERESTARTSYS == r) { mhi_log(MHI_MSG_INFO | MHI_DBG_POWER, "MDM failed to come out of M2.\n"); goto exit; } break; case MHI_STATE_M3: mhi_log(MHI_MSG_INFO, "MHI state %d, link state %d.\n", mhi_dev_ctxt->mhi_state, mhi_dev_ctxt->flags.link_up); if (mhi_dev_ctxt->flags.link_up) r = -EPERM; else r = 0; goto exit; case MHI_STATE_RESET: mhi_log(MHI_MSG_INFO, "MHI in RESET turning link off and quitting\n"); mhi_turn_off_pcie_link(mhi_dev_ctxt); r = mhi_set_bus_request(mhi_dev_ctxt, 0); if (r) mhi_log(MHI_MSG_INFO, "Failed to set bus freq ret %d\n", r); goto exit; default: mhi_log(MHI_MSG_INFO, "MHI state %d, link state %d.\n", mhi_dev_ctxt->mhi_state, mhi_dev_ctxt->flags.link_up); break; } while (atomic_read(&mhi_dev_ctxt->counters.outbound_acks)) { mhi_log(MHI_MSG_INFO | MHI_DBG_POWER, "There are still %d acks pending from device\n", atomic_read(&mhi_dev_ctxt->counters.outbound_acks)); __pm_stay_awake(&mhi_dev_ctxt->wake_lock); __pm_relax(&mhi_dev_ctxt->wake_lock); abort_m3 = 1; goto exit; } if (atomic_read(&mhi_dev_ctxt->flags.data_pending)) { abort_m3 = 1; goto exit; } r = hrtimer_cancel(&mhi_dev_ctxt->m1_timer); if (r) mhi_log(MHI_MSG_INFO, "Cancelled M1 timer, timer was active\n"); else mhi_log(MHI_MSG_INFO, "Cancelled M1 timer, timer was not active\n"); write_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); if (mhi_dev_ctxt->flags.pending_M0) { write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); mhi_log(MHI_MSG_INFO, "Pending M0 detected, aborting M3 procedure\n"); r = -EPERM; goto exit; } mhi_dev_ctxt->flags.pending_M3 = 1; atomic_set(&mhi_dev_ctxt->flags.cp_m1_state, 0); mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M3); write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); mhi_log(MHI_MSG_INFO | MHI_DBG_POWER, "Waiting for M3 completion.\n"); r = wait_event_interruptible_timeout(*mhi_dev_ctxt->M3_event, mhi_dev_ctxt->mhi_state == MHI_STATE_M3, msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT)); switch(r) { case 0: mhi_log(MHI_MSG_CRITICAL | MHI_DBG_POWER, "MDM failed to suspend after %d ms\n", MHI_MAX_SUSPEND_TIMEOUT); mhi_dev_ctxt->counters.m3_event_timeouts++; mhi_dev_ctxt->flags.pending_M3 = 0; r = -EAGAIN; goto exit; break; case -ERESTARTSYS: mhi_log(MHI_MSG_CRITICAL | MHI_DBG_POWER, "Going Down...\n"); goto exit; break; default: mhi_log(MHI_MSG_INFO | MHI_DBG_POWER, "M3 completion received\n"); break; } mhi_deassert_device_wake(mhi_dev_ctxt); /* Turn off PCIe link*/ mhi_turn_off_pcie_link(mhi_dev_ctxt); r = mhi_set_bus_request(mhi_dev_ctxt, 0); if (r) mhi_log(MHI_MSG_INFO, "Failed to set bus freq ret %d\n", r); exit: if (abort_m3) { write_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); atomic_inc(&mhi_dev_ctxt->flags.data_pending); write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); ring_all_chan_dbs(mhi_dev_ctxt); atomic_dec(&mhi_dev_ctxt->flags.data_pending); r = -EAGAIN; if(atomic_read(&mhi_dev_ctxt->flags.cp_m1_state)) { write_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); atomic_set(&mhi_dev_ctxt->flags.cp_m1_state, 0); mhi_dev_ctxt->mhi_state = MHI_STATE_M2; mhi_log(MHI_MSG_INFO, "Allowing transition to M2\n"); mhi_reg_write_field(mhi_dev_ctxt->mmio_addr, MHICTRL, MHICTRL_MHISTATE_MASK, MHICTRL_MHISTATE_SHIFT, MHI_STATE_M2); mhi_dev_ctxt->counters.m1_m2++; write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); } } /* We have to be careful here, we are setting a pending_M3 to 0 * even if we did not set it above. This works since the only other * entity that sets this flag must also acquire the pm_lock */ atomic_set(&mhi_dev_ctxt->flags.m3_work_enabled, 0); mhi_dev_ctxt->flags.pending_M3 = 0; mutex_unlock(&mhi_dev_ctxt->pm_lock); return r; }
int mhi_initiate_m0(mhi_device_ctxt *mhi_dev_ctxt) { int r = 0; unsigned long flags; mhi_log(MHI_MSG_INFO, "Entered MHI state %d, Pending M0 %d Pending M3 %d\n", mhi_dev_ctxt->mhi_state, mhi_dev_ctxt->flags.pending_M0, mhi_dev_ctxt->flags.pending_M3); mutex_lock(&mhi_dev_ctxt->pm_lock); /* 1. Wait on M3 event completion */ mhi_log(MHI_MSG_INFO, "Waiting for M0 M1 or M3. Currently %d...\n", mhi_dev_ctxt->mhi_state); r = wait_event_interruptible_timeout(*mhi_dev_ctxt->M3_event, mhi_dev_ctxt->mhi_state == MHI_STATE_M3 || mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || mhi_dev_ctxt->mhi_state == MHI_STATE_M1, msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT)); switch(r) { case 0: mhi_log(MHI_MSG_CRITICAL | MHI_DBG_POWER, "Timeout: State %d after %d ms\n", mhi_dev_ctxt->mhi_state, MHI_MAX_SUSPEND_TIMEOUT); mhi_dev_ctxt->counters.m0_event_timeouts++; r = -ETIME; goto exit; break; case -ERESTARTSYS: mhi_log(MHI_MSG_CRITICAL | MHI_DBG_POWER, "Going Down...\n"); goto exit; break; default: mhi_log(MHI_MSG_INFO | MHI_DBG_POWER, "Wait complete state: %d\n", mhi_dev_ctxt->mhi_state); r = 0; break; } if (mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || mhi_dev_ctxt->mhi_state == MHI_STATE_M1) { mhi_assert_device_wake(mhi_dev_ctxt); mhi_log(MHI_MSG_INFO, "MHI state %d, done\n", mhi_dev_ctxt->mhi_state); goto exit; } else { /* 3. Turn the clocks back on. */ if (MHI_STATUS_SUCCESS != mhi_turn_on_pcie_link(mhi_dev_ctxt)) { mhi_log(MHI_MSG_CRITICAL | MHI_DBG_POWER, "Failed to resume link\n"); r = -EIO; goto exit; } write_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); mhi_log(MHI_MSG_CRITICAL | MHI_DBG_POWER, "Setting M0 ...\n"); if (mhi_dev_ctxt->flags.pending_M3){ mhi_log(MHI_MSG_INFO, "Pending M3 detected, aborting M0 procedure\n"); write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); r = -EPERM; goto exit; } if (mhi_dev_ctxt->flags.link_up) { mhi_dev_ctxt->flags.pending_M0 = 1; mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M0); } write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); } exit: atomic_set(&mhi_dev_ctxt->flags.m0_work_enabled, 0); mutex_unlock(&mhi_dev_ctxt->pm_lock); mhi_log(MHI_MSG_INFO, "Exited...\n"); return r; }
int mhi_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt) { unsigned long flags; int r = 0; int abort_m3 = 0; mhi_log(MHI_MSG_INFO, "Entered MHI state %d, Pending M0 %d Pending M3 %d\n", mhi_dev_ctxt->mhi_state, mhi_dev_ctxt->flags.pending_M0, mhi_dev_ctxt->flags.pending_M3); mutex_lock(&mhi_dev_ctxt->pm_lock); switch (mhi_dev_ctxt->mhi_state) { case MHI_STATE_RESET: mhi_log(MHI_MSG_INFO, "MHI in RESET turning link off and quitting\n"); mhi_turn_off_pcie_link(mhi_dev_ctxt); r = mhi_set_bus_request(mhi_dev_ctxt, 0); if (r) mhi_log(MHI_MSG_INFO, "Failed to set bus freq ret %d\n", r); goto exit; break; case MHI_STATE_M0: case MHI_STATE_M1: case MHI_STATE_M2: mhi_log(MHI_MSG_INFO, "Triggering wake out of M2\n"); write_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); mhi_dev_ctxt->flags.pending_M3 = 1; if ((atomic_read(&mhi_dev_ctxt->flags.m2_transition)) == 0) { mhi_log(MHI_MSG_INFO, "M2 transition not set\n"); mhi_assert_device_wake(mhi_dev_ctxt); } write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); r = wait_event_interruptible_timeout( *mhi_dev_ctxt->mhi_ev_wq.m0_event, mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || mhi_dev_ctxt->mhi_state == MHI_STATE_M1, msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT)); if (0 == r || -ERESTARTSYS == r) { mhi_log(MHI_MSG_INFO, "MDM failed to come out of M2.\n"); r = -EAGAIN; goto exit; } break; case MHI_STATE_M3: mhi_log(MHI_MSG_INFO, "MHI state %d, link state %d.\n", mhi_dev_ctxt->mhi_state, mhi_dev_ctxt->flags.link_up); if (mhi_dev_ctxt->flags.link_up) r = -EAGAIN; else r = 0; goto exit; default: mhi_log(MHI_MSG_INFO, "MHI state %d, link state %d.\n", mhi_dev_ctxt->mhi_state, mhi_dev_ctxt->flags.link_up); break; } while (atomic_read(&mhi_dev_ctxt->counters.outbound_acks)) { mhi_log(MHI_MSG_INFO, "There are still %d acks pending from device\n", atomic_read(&mhi_dev_ctxt->counters.outbound_acks)); __pm_stay_awake(&mhi_dev_ctxt->w_lock); __pm_relax(&mhi_dev_ctxt->w_lock); abort_m3 = 1; r = -EAGAIN; goto exit; } if (atomic_read(&mhi_dev_ctxt->flags.data_pending)) { abort_m3 = 1; r = -EAGAIN; goto exit; } write_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); if (mhi_dev_ctxt->flags.pending_M0) { write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); r = -EAGAIN; goto exit; } mhi_dev_ctxt->flags.pending_M3 = 1; mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M3); write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); mhi_log(MHI_MSG_INFO, "Waiting for M3 completion.\n"); r = wait_event_interruptible_timeout(*mhi_dev_ctxt->mhi_ev_wq.m3_event, mhi_dev_ctxt->mhi_state == MHI_STATE_M3, msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT)); switch (r) { case 0: mhi_log(MHI_MSG_CRITICAL, "MDM failed to suspend after %d ms\n", MHI_MAX_SUSPEND_TIMEOUT); mhi_dev_ctxt->counters.m3_event_timeouts++; mhi_dev_ctxt->flags.pending_M3 = 0; goto exit; break; default: mhi_log(MHI_MSG_INFO, "M3 completion received\n"); break; } mhi_turn_off_pcie_link(mhi_dev_ctxt); r = mhi_set_bus_request(mhi_dev_ctxt, 0); if (r) mhi_log(MHI_MSG_INFO, "Failed to set bus freq ret %d\n", r); exit: if (abort_m3) { write_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); atomic_inc(&mhi_dev_ctxt->flags.data_pending); write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); ring_all_chan_dbs(mhi_dev_ctxt); ring_all_cmd_dbs(mhi_dev_ctxt); atomic_dec(&mhi_dev_ctxt->flags.data_pending); mhi_deassert_device_wake(mhi_dev_ctxt); } mhi_dev_ctxt->flags.pending_M3 = 0; mutex_unlock(&mhi_dev_ctxt->pm_lock); return r; }
int mhi_initiate_m0(struct mhi_device_ctxt *mhi_dev_ctxt) { int r = 0; unsigned long flags; mhi_log(MHI_MSG_INFO, "Entered MHI state %d, Pending M0 %d Pending M3 %d\n", mhi_dev_ctxt->mhi_state, mhi_dev_ctxt->flags.pending_M0, mhi_dev_ctxt->flags.pending_M3); mutex_lock(&mhi_dev_ctxt->pm_lock); mhi_log(MHI_MSG_INFO, "Waiting for M0 M1 or M3. Currently %d...\n", mhi_dev_ctxt->mhi_state); r = wait_event_interruptible_timeout(*mhi_dev_ctxt->mhi_ev_wq.m3_event, mhi_dev_ctxt->mhi_state == MHI_STATE_M3 || mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || mhi_dev_ctxt->mhi_state == MHI_STATE_M1, msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT)); switch (r) { case 0: mhi_log(MHI_MSG_CRITICAL, "Timeout: State %d after %d ms\n", mhi_dev_ctxt->mhi_state, MHI_MAX_SUSPEND_TIMEOUT); mhi_dev_ctxt->counters.m0_event_timeouts++; r = -ETIME; goto exit; break; case -ERESTARTSYS: mhi_log(MHI_MSG_CRITICAL, "Going Down...\n"); goto exit; break; default: mhi_log(MHI_MSG_INFO, "Wait complete state: %d\n", mhi_dev_ctxt->mhi_state); r = 0; break; } if (mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || mhi_dev_ctxt->mhi_state == MHI_STATE_M1) { mhi_assert_device_wake(mhi_dev_ctxt); mhi_log(MHI_MSG_INFO, "MHI state %d, done\n", mhi_dev_ctxt->mhi_state); goto exit; } else { if (MHI_STATUS_SUCCESS != mhi_turn_on_pcie_link(mhi_dev_ctxt)) { mhi_log(MHI_MSG_CRITICAL, "Failed to resume link\n"); r = -EIO; goto exit; } write_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); mhi_log(MHI_MSG_VERBOSE, "Setting M0 ...\n"); if (mhi_dev_ctxt->flags.pending_M3) { mhi_log(MHI_MSG_INFO, "Pending M3 detected, aborting M0 procedure\n"); write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); r = -EPERM; goto exit; } if (mhi_dev_ctxt->flags.link_up) { mhi_dev_ctxt->flags.pending_M0 = 1; mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M0); } write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); r = wait_event_interruptible_timeout( *mhi_dev_ctxt->mhi_ev_wq.m0_event, mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || mhi_dev_ctxt->mhi_state == MHI_STATE_M1, msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT)); WARN_ON(!r || -ERESTARTSYS == r); if (!r || -ERESTARTSYS == r) mhi_log(MHI_MSG_ERROR, "Failed to get M0 event ret %d\n", r); r = 0; } exit: mutex_unlock(&mhi_dev_ctxt->pm_lock); mhi_log(MHI_MSG_INFO, "Exited...\n"); return r; }