MHI_STATUS parse_inbound(mhi_device_ctxt *mhi_dev_ctxt, u32 chan, mhi_xfer_pkt *local_ev_trb_loc, u16 xfer_len) { mhi_client_handle *client_handle; mhi_ring *local_chan_ctxt; mhi_result *result; mhi_cb_info cb_info; client_handle = mhi_dev_ctxt->client_handle_list[chan]; local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan]; if (unlikely(mhi_dev_ctxt->mhi_local_chan_ctxt[chan].rp == mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp)) { mhi_dev_ctxt->mhi_chan_cntr[chan].empty_ring_removal++; mhi_wait_for_mdm(mhi_dev_ctxt); return mhi_send_cmd(mhi_dev_ctxt, MHI_COMMAND_RESET_CHAN, chan); } if (NULL != mhi_dev_ctxt->client_handle_list[chan]) result = &mhi_dev_ctxt->client_handle_list[chan]->result; /* If a client is registered */ if (unlikely(IS_SOFTWARE_CHANNEL(chan))) { MHI_TX_TRB_SET_LEN(TX_TRB_LEN, local_ev_trb_loc, xfer_len); ctxt_del_element(local_chan_ctxt, NULL); if (NULL != client_handle->client_info.mhi_client_cb && (0 == (client_handle->pkt_count % client_handle->cb_mod))) { cb_info.cb_reason = MHI_CB_XFER_SUCCESS; cb_info.result = &client_handle->result; cb_info.result->transaction_status = MHI_STATUS_SUCCESS; client_handle->client_info.mhi_client_cb(&cb_info); } } else { /* IN Hardware channel with no client * registered, we are done with this TRB*/ if (likely(NULL != client_handle)) { ctxt_del_element(local_chan_ctxt, NULL); /* A client is not registred for this IN channel */ } else {/* Hardware Channel, no client registerered, drop data */ recycle_trb_and_ring(mhi_dev_ctxt, &mhi_dev_ctxt->mhi_local_chan_ctxt[chan], MHI_RING_TYPE_XFER_RING, chan); } } return MHI_STATUS_SUCCESS; }
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; }
MHI_STATUS parse_outbound(mhi_device_ctxt *mhi_dev_ctxt, u32 chan, mhi_xfer_pkt *local_ev_trb_loc, u16 xfer_len) { mhi_result *result = NULL; MHI_STATUS ret_val = 0; mhi_client_handle *client_handle = NULL; mhi_ring *local_chan_ctxt = NULL; mhi_cb_info cb_info; local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan]; client_handle = mhi_dev_ctxt->client_handle_list[chan]; /* If ring is empty */ if (mhi_dev_ctxt->mhi_local_chan_ctxt[chan].rp == mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp) { mhi_dev_ctxt->mhi_chan_cntr[chan].empty_ring_removal++; mhi_wait_for_mdm(mhi_dev_ctxt); return mhi_send_cmd(mhi_dev_ctxt, MHI_COMMAND_RESET_CHAN, chan); } if (NULL != client_handle) { result = &mhi_dev_ctxt->client_handle_list[chan]->result; if (NULL != (&client_handle->client_info.mhi_client_cb) && (0 == (client_handle->pkt_count % client_handle->cb_mod))) { cb_info.cb_reason = MHI_CB_XFER_SUCCESS; cb_info.result = &client_handle->result; cb_info.result->transaction_status = MHI_STATUS_SUCCESS; client_handle->client_info.mhi_client_cb(&cb_info); } } ret_val = ctxt_del_element(&mhi_dev_ctxt->mhi_local_chan_ctxt[chan], NULL); atomic_dec(&mhi_dev_ctxt->counters.outbound_acks); mhi_log(MHI_MSG_VERBOSE, "Processed outbound ack chan %d Pending acks %d.\n", chan, atomic_read(&mhi_dev_ctxt->counters.outbound_acks)); return MHI_STATUS_SUCCESS; }
MHI_STATUS parse_cmd_event(mhi_device_ctxt *mhi_dev_ctxt, mhi_event_pkt *ev_pkt) { MHI_STATUS ret_val = MHI_STATUS_SUCCESS; mhi_cmd_pkt *cmd_pkt = NULL; uintptr_t phy_trb_loc = 0; if (NULL != ev_pkt) phy_trb_loc = (uintptr_t)MHI_EV_READ_PTR(EV_PTR, ev_pkt); else return MHI_STATUS_ERROR; cmd_pkt = (mhi_cmd_pkt *)mhi_p2v_addr(mhi_dev_ctxt->mhi_ctrl_seg_info, phy_trb_loc); mhi_log(MHI_MSG_INFO, "Received CMD completion event\n"); switch (MHI_EV_READ_CODE(EV_TRB_CODE, ev_pkt)) { /* Command completion was successful */ case MHI_EVENT_CC_SUCCESS: { u32 chan; MHI_TRB_GET_INFO(CMD_TRB_CHID, cmd_pkt, chan); switch (MHI_TRB_READ_INFO(CMD_TRB_TYPE, cmd_pkt)) { case MHI_PKT_TYPE_NOOP_CMD: mhi_log(MHI_MSG_INFO, "Processed NOOP cmd event\n"); break; case MHI_PKT_TYPE_RESET_CHAN_CMD: if (MHI_STATUS_SUCCESS != reset_chan_cmd(mhi_dev_ctxt, cmd_pkt)) mhi_log(MHI_MSG_INFO, "Failed to process reset cmd\n"); break; case MHI_PKT_TYPE_STOP_CHAN_CMD: { mhi_log(MHI_MSG_INFO, "Processed cmd stop event\n"); if (MHI_STATUS_SUCCESS != ret_val) { mhi_log(MHI_MSG_INFO, "Failed to set chan state\n"); return MHI_STATUS_ERROR; } break; } case MHI_PKT_TYPE_START_CHAN_CMD: { if (MHI_STATUS_SUCCESS != start_chan_cmd(mhi_dev_ctxt, cmd_pkt)) mhi_log(MHI_MSG_INFO, "Failed to process reset cmd\n"); atomic_dec(&mhi_dev_ctxt->start_cmd_pending_ack); wake_up_interruptible(mhi_dev_ctxt->chan_start_complete); break; } default: mhi_log(MHI_MSG_INFO, "Bad cmd type 0x%x\n", MHI_TRB_READ_INFO(CMD_TRB_TYPE, cmd_pkt)); break; } mhi_log(MHI_MSG_INFO, "CMD completion indicated successful\n"); mhi_dev_ctxt->mhi_chan_pend_cmd_ack[chan] = MHI_CMD_NOT_PENDING; break; } default: mhi_log(MHI_MSG_INFO, "Unhandled mhi completion code\n"); break; } mhi_log(MHI_MSG_INFO, "Recycling cmd event\n"); ctxt_del_element(mhi_dev_ctxt->mhi_local_cmd_ctxt, NULL); return MHI_STATUS_SUCCESS; }
MHI_STATUS recycle_trb_and_ring(mhi_device_ctxt *mhi_dev_ctxt, mhi_ring *ring, MHI_RING_TYPE ring_type, u32 ring_index) { MHI_STATUS ret_val = MHI_STATUS_ERROR; u64 db_value = 0; void *removed_element = NULL; void *added_element = NULL; /* TODO This will not cover us for ring_index out of * bounds for cmd or event channels */ if (NULL == mhi_dev_ctxt || NULL == ring || ring_type > (MHI_RING_TYPE_MAX - 1) || ring_index > (MHI_MAX_CHANNELS - 1)) { mhi_log(MHI_MSG_ERROR, "Bad input params\n"); return ret_val; } ret_val = ctxt_del_element(ring, &removed_element); if (MHI_STATUS_SUCCESS != ret_val) { mhi_log(MHI_MSG_ERROR, "Could not remove element from ring\n"); return MHI_STATUS_ERROR; } ret_val = ctxt_add_element(ring, &added_element); if (MHI_STATUS_SUCCESS != ret_val) mhi_log(MHI_MSG_ERROR, "Could not add element to ring\n"); db_value = mhi_v2p_addr(mhi_dev_ctxt->mhi_ctrl_seg_info, (uintptr_t)ring->wp); if (MHI_STATUS_SUCCESS != ret_val) return ret_val; if (MHI_RING_TYPE_XFER_RING == ring_type) { mhi_xfer_pkt *removed_xfer_pkt = (mhi_xfer_pkt *)removed_element; mhi_xfer_pkt *added_xfer_pkt = (mhi_xfer_pkt *)added_element; added_xfer_pkt->data_tx_pkt = *(mhi_tx_pkt *)removed_xfer_pkt; } else if (MHI_RING_TYPE_EVENT_RING == ring_type && mhi_dev_ctxt->counters.m0_m3 > 0 && IS_HARDWARE_CHANNEL(ring_index)) { spinlock_t *lock = NULL; unsigned long flags = 0; #if defined(CONFIG_MACH_LENTISLTE_SKT) || defined(CONFIG_MACH_LENTISLTE_LGT) || defined(CONFIG_MACH_LENTISLTE_KTT) || defined(CONFIG_SEC_KCCAT6_PROJECT) mhi_log(MHI_MSG_VERBOSE, "Updating ev context id %d, value 0x%llx\n", ring_index, db_value); lock = &mhi_dev_ctxt->mhi_ev_spinlock_list[ring_index]; spin_lock_irqsave(lock, flags); db_value = mhi_v2p_addr(mhi_dev_ctxt->mhi_ctrl_seg_info, (uintptr_t)ring->wp); mhi_dev_ctxt->mhi_ev_db_order[ring_index] = 1; mhi_dev_ctxt->mhi_ctrl_seg->mhi_ec_list[ring_index].mhi_event_write_ptr = db_value; #else mhi_log(MHI_MSG_ERROR, "Updating EV_CTXT\n"); lock = &mhi_dev_ctxt->mhi_ev_spinlock_list[ring_index]; spin_lock_irqsave(lock, flags); mhi_dev_ctxt->mhi_ev_db_order[ring_index] = 1; mhi_dev_ctxt->mhi_ctrl_seg->mhi_ec_list[ring_index].mhi_event_write_ptr = db_value; #endif mhi_dev_ctxt->ev_counter[ring_index]++; spin_unlock_irqrestore(lock, flags); } atomic_inc(&mhi_dev_ctxt->flags.data_pending); /* Asserting Device Wake here, will imediately wake mdm */ if ((MHI_STATE_M0 == mhi_dev_ctxt->mhi_state || MHI_STATE_M1 == mhi_dev_ctxt->mhi_state) && mhi_dev_ctxt->flags.link_up) { switch (ring_type) { case MHI_RING_TYPE_CMD_RING: { struct mutex *cmd_mutex = NULL; cmd_mutex = &mhi_dev_ctxt->mhi_cmd_mutex_list[PRIMARY_CMD_RING]; mutex_lock(cmd_mutex); mhi_dev_ctxt->cmd_ring_order = 1; MHI_WRITE_DB(mhi_dev_ctxt, mhi_dev_ctxt->cmd_db_addr, ring_index, db_value); mutex_unlock(cmd_mutex); break; } case MHI_RING_TYPE_EVENT_RING: { spinlock_t *lock = NULL; unsigned long flags = 0; lock = &mhi_dev_ctxt->mhi_ev_spinlock_list[ring_index]; spin_lock_irqsave(lock, flags); mhi_dev_ctxt->mhi_ev_db_order[ring_index] = 1; #if defined(CONFIG_MACH_LENTISLTE_SKT) || defined(CONFIG_MACH_LENTISLTE_LGT) || defined(CONFIG_MACH_LENTISLTE_KTT)|| defined(CONFIG_SEC_KCCAT6_PROJECT) db_value = mhi_v2p_addr(mhi_dev_ctxt->mhi_ctrl_seg_info, (uintptr_t)ring->wp); #endif if ((mhi_dev_ctxt->ev_counter[ring_index] % MHI_EV_DB_INTERVAL) == 0) { MHI_WRITE_DB(mhi_dev_ctxt, mhi_dev_ctxt->event_db_addr, ring_index, db_value); } spin_unlock_irqrestore(lock, flags); break; } case MHI_RING_TYPE_XFER_RING: { unsigned long flags = 0; spin_lock_irqsave(&mhi_dev_ctxt->db_write_lock[ring_index], flags); mhi_dev_ctxt->mhi_chan_db_order[ring_index] = 1; MHI_WRITE_DB(mhi_dev_ctxt, mhi_dev_ctxt->channel_db_addr, ring_index, db_value); spin_unlock_irqrestore(&mhi_dev_ctxt->db_write_lock[ring_index], flags); break; } default: mhi_log(MHI_MSG_ERROR, "Bad ring type\n"); } } atomic_dec(&mhi_dev_ctxt->flags.data_pending); return ret_val; }