/** * @brief Add a new state transition work item to the state transition * thread work item list. * * @param mhi_dev_ctxt [IN ] The mhi_dev_ctxt context * @param new_state The state we wish to transition to * * @return MHI_STATUS */ MHI_STATUS mhi_init_state_transition(mhi_device_ctxt *mhi_dev_ctxt, STATE_TRANSITION new_state) { unsigned long flags = 0; MHI_STATUS ret_val = MHI_STATUS_SUCCESS; volatile STATE_TRANSITION *cur_work_item = NULL; s32 nr_avail_work_items = 0; mhi_ring *stt_ring = &mhi_dev_ctxt->state_change_work_item_list.q_info; mhi_state_work_queue *work_q = &mhi_dev_ctxt->state_change_work_item_list; spin_lock_irqsave(work_q->q_lock, flags); nr_avail_work_items = get_nr_avail_ring_elements(stt_ring); if (0 >= nr_avail_work_items) { mhi_log(MHI_MSG_CRITICAL, "No Room left on STT work queue\n"); return MHI_STATUS_ERROR; } mhi_log(MHI_MSG_VERBOSE, "Processing state transition %x\n", new_state); *(STATE_TRANSITION *)stt_ring->wp = new_state; ret_val = ctxt_add_element(stt_ring, (void **)&cur_work_item); wmb(); MHI_ASSERT(MHI_STATUS_SUCCESS == ret_val, "Failed to add selement to STT workqueue\n"); spin_unlock_irqrestore(work_q->q_lock, flags); wake_up_interruptible(mhi_dev_ctxt->state_change_event_handle); return ret_val; }
MHI_STATUS process_stt_work_item(mhi_device_ctxt *mhi_dev_ctxt, STATE_TRANSITION cur_work_item) { MHI_STATUS ret_val = MHI_STATUS_SUCCESS; mhi_log(MHI_MSG_INFO, "Transitioning to %d\n", (int)cur_work_item); switch (cur_work_item) { case STATE_TRANSITION_BHI: ret_val = process_BHI_transition(mhi_dev_ctxt, cur_work_item); break; case STATE_TRANSITION_RESET: ret_val = process_RESET_transition(mhi_dev_ctxt, cur_work_item); break; case STATE_TRANSITION_READY: ret_val = process_READY_transition(mhi_dev_ctxt, cur_work_item); break; case STATE_TRANSITION_SBL: ret_val = process_SBL_transition(mhi_dev_ctxt, cur_work_item); break; case STATE_TRANSITION_AMSS: ret_val = process_AMSS_transition(mhi_dev_ctxt, cur_work_item); break; case STATE_TRANSITION_M0: ret_val = process_M0_transition(mhi_dev_ctxt, cur_work_item); break; case STATE_TRANSITION_M1: ret_val = process_M1_transition(mhi_dev_ctxt, cur_work_item); break; case STATE_TRANSITION_M3: ret_val = process_M3_transition(mhi_dev_ctxt, cur_work_item); break; case STATE_TRANSITION_SYS_ERR: ret_val = process_SYSERR_transition(mhi_dev_ctxt, cur_work_item); break; case STATE_TRANSITION_LINK_DOWN: ret_val = process_LINK_DOWN_transition(mhi_dev_ctxt, cur_work_item); break; case STATE_TRANSITION_WAKE: ret_val = process_WAKE_transition(mhi_dev_ctxt, cur_work_item); break; default: mhi_log(MHI_MSG_ERROR, "Unrecongized state: %d\n", cur_work_item); MHI_ASSERT(0, "Unrecognized state transition\n"); break; } return ret_val; }
int mhi_state_change_thread(void *ctxt) { int r = 0; unsigned long flags = 0; mhi_device_ctxt *mhi_dev_ctxt = (mhi_device_ctxt *)ctxt; STATE_TRANSITION cur_work_item; MHI_STATUS ret_val = MHI_STATUS_SUCCESS; mhi_state_work_queue *work_q = &mhi_dev_ctxt->state_change_work_item_list; volatile mhi_ring *state_change_q = &work_q->q_info; if (NULL == mhi_dev_ctxt) { mhi_log(MHI_MSG_ERROR, "Got bad context, quitting\n"); return -EIO; } for (;;) { r = wait_event_interruptible( *mhi_dev_ctxt->state_change_event_handle, ((work_q->q_info.rp != work_q->q_info.wp) & !mhi_dev_ctxt->st_thread_stopped)); if (r) { mhi_log(MHI_MSG_INFO, "Caught signal %d, quitting\n", r); return 0; } if (mhi_dev_ctxt->flags.kill_threads) { mhi_log(MHI_MSG_INFO, "Caught exit signal, quitting\n"); return 0; } mhi_dev_ctxt->st_thread_stopped = 0; spin_lock_irqsave(work_q->q_lock, flags); cur_work_item = *(STATE_TRANSITION *)(state_change_q->rp); ret_val = ctxt_del_element(&work_q->q_info, NULL); MHI_ASSERT(ret_val == MHI_STATUS_SUCCESS, "Failed to delete element from STT workqueue\n"); spin_unlock_irqrestore(work_q->q_lock, flags); ret_val = process_stt_work_item(mhi_dev_ctxt, cur_work_item); } return 0; }
/** * @brief Function for sending data on an outbound channel. * This function only sends on TRE's worth of * data and may chain the TRE as specified by the caller. * * @param device [IN ] Pointer to mhi context used to send the TRE * @param chan [IN ] Channel number to send the TRE on * @param buf [IN ] Physical address of buffer to be linked to descriptor * @param buf_len [IN ] Length of buffer, which will be populated in the TRE * @param chain [IN ] Specification on whether this TRE should be chained * * @return MHI_STATUS */ MHI_STATUS mhi_queue_xfer(mhi_client_handle *client_handle, uintptr_t buf, size_t buf_len, u32 chain, u32 eob) { mhi_xfer_pkt *pkt_loc; MHI_STATUS ret_val; MHI_CLIENT_CHANNEL chan; mhi_device_ctxt *mhi_dev_ctxt; unsigned long flags; if (NULL == client_handle || !VALID_CHAN_NR(client_handle->chan) || 0 == buf || chain >= MHI_TRE_CHAIN_LIMIT || 0 == buf_len) { mhi_log(MHI_MSG_CRITICAL, "Bad input args\n"); return MHI_STATUS_ERROR; } MHI_ASSERT(VALID_BUF(buf, buf_len), "Client buffer is of invalid length\n"); mhi_dev_ctxt = client_handle->mhi_dev_ctxt; chan = client_handle->chan; /* Bump up the vote for pending data */ read_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); atomic_inc(&mhi_dev_ctxt->flags.data_pending); mhi_dev_ctxt->counters.m1_m0++; if (mhi_dev_ctxt->flags.link_up) mhi_assert_device_wake(mhi_dev_ctxt); read_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); pkt_loc = mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp; pkt_loc->data_tx_pkt.buffer_ptr = buf; if (likely(0 != client_handle->intmod_t)) MHI_TRB_SET_INFO(TX_TRB_BEI, pkt_loc, 1); else MHI_TRB_SET_INFO(TX_TRB_BEI, pkt_loc, 0); MHI_TRB_SET_INFO(TX_TRB_IEOT, pkt_loc, 1); MHI_TRB_SET_INFO(TX_TRB_CHAIN, pkt_loc, chain); MHI_TRB_SET_INFO(TX_TRB_IEOB, pkt_loc, eob); MHI_TRB_SET_INFO(TX_TRB_TYPE, pkt_loc, MHI_PKT_TYPE_TRANSFER); MHI_TX_TRB_SET_LEN(TX_TRB_LEN, pkt_loc, buf_len); if (chan % 2 == 0) { atomic_inc(&mhi_dev_ctxt->counters.outbound_acks); mhi_log(MHI_MSG_VERBOSE, "Queued outbound pkt. Pending Acks %d\n", atomic_read(&mhi_dev_ctxt->counters.outbound_acks)); } /* Add the TRB to the correct transfer ring */ ret_val = ctxt_add_element(&mhi_dev_ctxt->mhi_local_chan_ctxt[chan], (void *)&pkt_loc); if (unlikely(MHI_STATUS_SUCCESS != ret_val)) { mhi_log(MHI_MSG_INFO, "Failed to insert trb in xfer ring\n"); goto error; } mhi_notify_device(mhi_dev_ctxt, chan); atomic_dec(&mhi_dev_ctxt->flags.data_pending); return MHI_STATUS_SUCCESS; error: atomic_dec(&mhi_dev_ctxt->flags.data_pending); return ret_val; }