162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MHI Endpoint bus stack 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2022 Linaro Ltd. 662306a36Sopenharmony_ci * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bitfield.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/dma-direction.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/irq.h> 1562306a36Sopenharmony_ci#include <linux/mhi_ep.h> 1662306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include "internal.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define M0_WAIT_DELAY_MS 100 2162306a36Sopenharmony_ci#define M0_WAIT_COUNT 100 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic DEFINE_IDA(mhi_ep_cntrl_ida); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id); 2662306a36Sopenharmony_cistatic int mhi_ep_destroy_device(struct device *dev, void *data); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int mhi_ep_send_event(struct mhi_ep_cntrl *mhi_cntrl, u32 ring_idx, 2962306a36Sopenharmony_ci struct mhi_ring_element *el, bool bei) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 3262306a36Sopenharmony_ci union mhi_ep_ring_ctx *ctx; 3362306a36Sopenharmony_ci struct mhi_ep_ring *ring; 3462306a36Sopenharmony_ci int ret; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci mutex_lock(&mhi_cntrl->event_lock); 3762306a36Sopenharmony_ci ring = &mhi_cntrl->mhi_event[ring_idx].ring; 3862306a36Sopenharmony_ci ctx = (union mhi_ep_ring_ctx *)&mhi_cntrl->ev_ctx_cache[ring_idx]; 3962306a36Sopenharmony_ci if (!ring->started) { 4062306a36Sopenharmony_ci ret = mhi_ep_ring_start(mhi_cntrl, ring, ctx); 4162306a36Sopenharmony_ci if (ret) { 4262306a36Sopenharmony_ci dev_err(dev, "Error starting event ring (%u)\n", ring_idx); 4362306a36Sopenharmony_ci goto err_unlock; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* Add element to the event ring */ 4862306a36Sopenharmony_ci ret = mhi_ep_ring_add_element(ring, el); 4962306a36Sopenharmony_ci if (ret) { 5062306a36Sopenharmony_ci dev_err(dev, "Error adding element to event ring (%u)\n", ring_idx); 5162306a36Sopenharmony_ci goto err_unlock; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci mutex_unlock(&mhi_cntrl->event_lock); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* 5762306a36Sopenharmony_ci * Raise IRQ to host only if the BEI flag is not set in TRE. Host might 5862306a36Sopenharmony_ci * set this flag for interrupt moderation as per MHI protocol. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci if (!bei) 6162306a36Sopenharmony_ci mhi_cntrl->raise_irq(mhi_cntrl, ring->irq_vector); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return 0; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cierr_unlock: 6662306a36Sopenharmony_ci mutex_unlock(&mhi_cntrl->event_lock); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return ret; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int mhi_ep_send_completion_event(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring, 7262306a36Sopenharmony_ci struct mhi_ring_element *tre, u32 len, enum mhi_ev_ccs code) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct mhi_ring_element *event; 7562306a36Sopenharmony_ci int ret; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL | GFP_DMA); 7862306a36Sopenharmony_ci if (!event) 7962306a36Sopenharmony_ci return -ENOMEM; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci event->ptr = cpu_to_le64(ring->rbase + ring->rd_offset * sizeof(*tre)); 8262306a36Sopenharmony_ci event->dword[0] = MHI_TRE_EV_DWORD0(code, len); 8362306a36Sopenharmony_ci event->dword[1] = MHI_TRE_EV_DWORD1(ring->ch_id, MHI_PKT_TYPE_TX_EVENT); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci ret = mhi_ep_send_event(mhi_cntrl, ring->er_index, event, MHI_TRE_DATA_GET_BEI(tre)); 8662306a36Sopenharmony_ci kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return ret; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ciint mhi_ep_send_state_change_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state state) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct mhi_ring_element *event; 9462306a36Sopenharmony_ci int ret; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL | GFP_DMA); 9762306a36Sopenharmony_ci if (!event) 9862306a36Sopenharmony_ci return -ENOMEM; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci event->dword[0] = MHI_SC_EV_DWORD0(state); 10162306a36Sopenharmony_ci event->dword[1] = MHI_SC_EV_DWORD1(MHI_PKT_TYPE_STATE_CHANGE_EVENT); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0); 10462306a36Sopenharmony_ci kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return ret; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ciint mhi_ep_send_ee_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_ee_type exec_env) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct mhi_ring_element *event; 11262306a36Sopenharmony_ci int ret; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL | GFP_DMA); 11562306a36Sopenharmony_ci if (!event) 11662306a36Sopenharmony_ci return -ENOMEM; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci event->dword[0] = MHI_EE_EV_DWORD0(exec_env); 11962306a36Sopenharmony_ci event->dword[1] = MHI_SC_EV_DWORD1(MHI_PKT_TYPE_EE_EVENT); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0); 12262306a36Sopenharmony_ci kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return ret; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int mhi_ep_send_cmd_comp_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_ev_ccs code) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct mhi_ep_ring *ring = &mhi_cntrl->mhi_cmd->ring; 13062306a36Sopenharmony_ci struct mhi_ring_element *event; 13162306a36Sopenharmony_ci int ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL | GFP_DMA); 13462306a36Sopenharmony_ci if (!event) 13562306a36Sopenharmony_ci return -ENOMEM; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci event->ptr = cpu_to_le64(ring->rbase + ring->rd_offset * sizeof(struct mhi_ring_element)); 13862306a36Sopenharmony_ci event->dword[0] = MHI_CC_EV_DWORD0(code); 13962306a36Sopenharmony_ci event->dword[1] = MHI_CC_EV_DWORD1(MHI_PKT_TYPE_CMD_COMPLETION_EVENT); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0); 14262306a36Sopenharmony_ci kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return ret; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_element *el) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl; 15062306a36Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 15162306a36Sopenharmony_ci struct mhi_result result = {}; 15262306a36Sopenharmony_ci struct mhi_ep_chan *mhi_chan; 15362306a36Sopenharmony_ci struct mhi_ep_ring *ch_ring; 15462306a36Sopenharmony_ci u32 tmp, ch_id; 15562306a36Sopenharmony_ci int ret; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci ch_id = MHI_TRE_GET_CMD_CHID(el); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* Check if the channel is supported by the controller */ 16062306a36Sopenharmony_ci if ((ch_id >= mhi_cntrl->max_chan) || !mhi_cntrl->mhi_chan[ch_id].name) { 16162306a36Sopenharmony_ci dev_dbg(dev, "Channel (%u) not supported!\n", ch_id); 16262306a36Sopenharmony_ci return -ENODEV; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci mhi_chan = &mhi_cntrl->mhi_chan[ch_id]; 16662306a36Sopenharmony_ci ch_ring = &mhi_cntrl->mhi_chan[ch_id].ring; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci switch (MHI_TRE_GET_CMD_TYPE(el)) { 16962306a36Sopenharmony_ci case MHI_PKT_TYPE_START_CHAN_CMD: 17062306a36Sopenharmony_ci dev_dbg(dev, "Received START command for channel (%u)\n", ch_id); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci mutex_lock(&mhi_chan->lock); 17362306a36Sopenharmony_ci /* Initialize and configure the corresponding channel ring */ 17462306a36Sopenharmony_ci if (!ch_ring->started) { 17562306a36Sopenharmony_ci ret = mhi_ep_ring_start(mhi_cntrl, ch_ring, 17662306a36Sopenharmony_ci (union mhi_ep_ring_ctx *)&mhi_cntrl->ch_ctx_cache[ch_id]); 17762306a36Sopenharmony_ci if (ret) { 17862306a36Sopenharmony_ci dev_err(dev, "Failed to start ring for channel (%u)\n", ch_id); 17962306a36Sopenharmony_ci ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, 18062306a36Sopenharmony_ci MHI_EV_CC_UNDEFINED_ERR); 18162306a36Sopenharmony_ci if (ret) 18262306a36Sopenharmony_ci dev_err(dev, "Error sending completion event: %d\n", ret); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci goto err_unlock; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* Set channel state to RUNNING */ 18962306a36Sopenharmony_ci mhi_chan->state = MHI_CH_STATE_RUNNING; 19062306a36Sopenharmony_ci tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg); 19162306a36Sopenharmony_ci tmp &= ~CHAN_CTX_CHSTATE_MASK; 19262306a36Sopenharmony_ci tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_RUNNING); 19362306a36Sopenharmony_ci mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS); 19662306a36Sopenharmony_ci if (ret) { 19762306a36Sopenharmony_ci dev_err(dev, "Error sending command completion event (%u)\n", 19862306a36Sopenharmony_ci MHI_EV_CC_SUCCESS); 19962306a36Sopenharmony_ci goto err_unlock; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci mutex_unlock(&mhi_chan->lock); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* 20562306a36Sopenharmony_ci * Create MHI device only during UL channel start. Since the MHI 20662306a36Sopenharmony_ci * channels operate in a pair, we'll associate both UL and DL 20762306a36Sopenharmony_ci * channels to the same device. 20862306a36Sopenharmony_ci * 20962306a36Sopenharmony_ci * We also need to check for mhi_dev != NULL because, the host 21062306a36Sopenharmony_ci * will issue START_CHAN command during resume and we don't 21162306a36Sopenharmony_ci * destroy the device during suspend. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ci if (!(ch_id % 2) && !mhi_chan->mhi_dev) { 21462306a36Sopenharmony_ci ret = mhi_ep_create_device(mhi_cntrl, ch_id); 21562306a36Sopenharmony_ci if (ret) { 21662306a36Sopenharmony_ci dev_err(dev, "Error creating device for channel (%u)\n", ch_id); 21762306a36Sopenharmony_ci mhi_ep_handle_syserr(mhi_cntrl); 21862306a36Sopenharmony_ci return ret; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* Finally, enable DB for the channel */ 22362306a36Sopenharmony_ci mhi_ep_mmio_enable_chdb(mhi_cntrl, ch_id); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci case MHI_PKT_TYPE_STOP_CHAN_CMD: 22762306a36Sopenharmony_ci dev_dbg(dev, "Received STOP command for channel (%u)\n", ch_id); 22862306a36Sopenharmony_ci if (!ch_ring->started) { 22962306a36Sopenharmony_ci dev_err(dev, "Channel (%u) not opened\n", ch_id); 23062306a36Sopenharmony_ci return -ENODEV; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci mutex_lock(&mhi_chan->lock); 23462306a36Sopenharmony_ci /* Disable DB for the channel */ 23562306a36Sopenharmony_ci mhi_ep_mmio_disable_chdb(mhi_cntrl, ch_id); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* Send channel disconnect status to client drivers */ 23862306a36Sopenharmony_ci if (mhi_chan->xfer_cb) { 23962306a36Sopenharmony_ci result.transaction_status = -ENOTCONN; 24062306a36Sopenharmony_ci result.bytes_xferd = 0; 24162306a36Sopenharmony_ci mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Set channel state to STOP */ 24562306a36Sopenharmony_ci mhi_chan->state = MHI_CH_STATE_STOP; 24662306a36Sopenharmony_ci tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg); 24762306a36Sopenharmony_ci tmp &= ~CHAN_CTX_CHSTATE_MASK; 24862306a36Sopenharmony_ci tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_STOP); 24962306a36Sopenharmony_ci mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS); 25262306a36Sopenharmony_ci if (ret) { 25362306a36Sopenharmony_ci dev_err(dev, "Error sending command completion event (%u)\n", 25462306a36Sopenharmony_ci MHI_EV_CC_SUCCESS); 25562306a36Sopenharmony_ci goto err_unlock; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci mutex_unlock(&mhi_chan->lock); 25962306a36Sopenharmony_ci break; 26062306a36Sopenharmony_ci case MHI_PKT_TYPE_RESET_CHAN_CMD: 26162306a36Sopenharmony_ci dev_dbg(dev, "Received RESET command for channel (%u)\n", ch_id); 26262306a36Sopenharmony_ci if (!ch_ring->started) { 26362306a36Sopenharmony_ci dev_err(dev, "Channel (%u) not opened\n", ch_id); 26462306a36Sopenharmony_ci return -ENODEV; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci mutex_lock(&mhi_chan->lock); 26862306a36Sopenharmony_ci /* Stop and reset the transfer ring */ 26962306a36Sopenharmony_ci mhi_ep_ring_reset(mhi_cntrl, ch_ring); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Send channel disconnect status to client driver */ 27262306a36Sopenharmony_ci if (mhi_chan->xfer_cb) { 27362306a36Sopenharmony_ci result.transaction_status = -ENOTCONN; 27462306a36Sopenharmony_ci result.bytes_xferd = 0; 27562306a36Sopenharmony_ci mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Set channel state to DISABLED */ 27962306a36Sopenharmony_ci mhi_chan->state = MHI_CH_STATE_DISABLED; 28062306a36Sopenharmony_ci tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg); 28162306a36Sopenharmony_ci tmp &= ~CHAN_CTX_CHSTATE_MASK; 28262306a36Sopenharmony_ci tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_DISABLED); 28362306a36Sopenharmony_ci mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS); 28662306a36Sopenharmony_ci if (ret) { 28762306a36Sopenharmony_ci dev_err(dev, "Error sending command completion event (%u)\n", 28862306a36Sopenharmony_ci MHI_EV_CC_SUCCESS); 28962306a36Sopenharmony_ci goto err_unlock; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci mutex_unlock(&mhi_chan->lock); 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci default: 29562306a36Sopenharmony_ci dev_err(dev, "Invalid command received: %lu for channel (%u)\n", 29662306a36Sopenharmony_ci MHI_TRE_GET_CMD_TYPE(el), ch_id); 29762306a36Sopenharmony_ci return -EINVAL; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return 0; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cierr_unlock: 30362306a36Sopenharmony_ci mutex_unlock(&mhi_chan->lock); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return ret; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cibool mhi_ep_queue_is_empty(struct mhi_ep_device *mhi_dev, enum dma_data_direction dir) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct mhi_ep_chan *mhi_chan = (dir == DMA_FROM_DEVICE) ? mhi_dev->dl_chan : 31162306a36Sopenharmony_ci mhi_dev->ul_chan; 31262306a36Sopenharmony_ci struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl; 31362306a36Sopenharmony_ci struct mhi_ep_ring *ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return !!(ring->rd_offset == ring->wr_offset); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_ep_queue_is_empty); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl, 32062306a36Sopenharmony_ci struct mhi_ep_ring *ring, 32162306a36Sopenharmony_ci struct mhi_result *result, 32262306a36Sopenharmony_ci u32 len) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct mhi_ep_chan *mhi_chan = &mhi_cntrl->mhi_chan[ring->ch_id]; 32562306a36Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 32662306a36Sopenharmony_ci size_t tr_len, read_offset, write_offset; 32762306a36Sopenharmony_ci struct mhi_ep_buf_info buf_info = {}; 32862306a36Sopenharmony_ci struct mhi_ring_element *el; 32962306a36Sopenharmony_ci bool tr_done = false; 33062306a36Sopenharmony_ci u32 buf_left; 33162306a36Sopenharmony_ci int ret; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci buf_left = len; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci do { 33662306a36Sopenharmony_ci /* Don't process the transfer ring if the channel is not in RUNNING state */ 33762306a36Sopenharmony_ci if (mhi_chan->state != MHI_CH_STATE_RUNNING) { 33862306a36Sopenharmony_ci dev_err(dev, "Channel not available\n"); 33962306a36Sopenharmony_ci return -ENODEV; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci el = &ring->ring_cache[ring->rd_offset]; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Check if there is data pending to be read from previous read operation */ 34562306a36Sopenharmony_ci if (mhi_chan->tre_bytes_left) { 34662306a36Sopenharmony_ci dev_dbg(dev, "TRE bytes remaining: %u\n", mhi_chan->tre_bytes_left); 34762306a36Sopenharmony_ci tr_len = min(buf_left, mhi_chan->tre_bytes_left); 34862306a36Sopenharmony_ci } else { 34962306a36Sopenharmony_ci mhi_chan->tre_loc = MHI_TRE_DATA_GET_PTR(el); 35062306a36Sopenharmony_ci mhi_chan->tre_size = MHI_TRE_DATA_GET_LEN(el); 35162306a36Sopenharmony_ci mhi_chan->tre_bytes_left = mhi_chan->tre_size; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci tr_len = min(buf_left, mhi_chan->tre_size); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci read_offset = mhi_chan->tre_size - mhi_chan->tre_bytes_left; 35762306a36Sopenharmony_ci write_offset = len - buf_left; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci buf_info.host_addr = mhi_chan->tre_loc + read_offset; 36062306a36Sopenharmony_ci buf_info.dev_addr = result->buf_addr + write_offset; 36162306a36Sopenharmony_ci buf_info.size = tr_len; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci dev_dbg(dev, "Reading %zd bytes from channel (%u)\n", tr_len, ring->ch_id); 36462306a36Sopenharmony_ci ret = mhi_cntrl->read_from_host(mhi_cntrl, &buf_info); 36562306a36Sopenharmony_ci if (ret < 0) { 36662306a36Sopenharmony_ci dev_err(&mhi_chan->mhi_dev->dev, "Error reading from channel\n"); 36762306a36Sopenharmony_ci return ret; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci buf_left -= tr_len; 37162306a36Sopenharmony_ci mhi_chan->tre_bytes_left -= tr_len; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* 37462306a36Sopenharmony_ci * Once the TRE (Transfer Ring Element) of a TD (Transfer Descriptor) has been 37562306a36Sopenharmony_ci * read completely: 37662306a36Sopenharmony_ci * 37762306a36Sopenharmony_ci * 1. Send completion event to the host based on the flags set in TRE. 37862306a36Sopenharmony_ci * 2. Increment the local read offset of the transfer ring. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci if (!mhi_chan->tre_bytes_left) { 38162306a36Sopenharmony_ci /* 38262306a36Sopenharmony_ci * The host will split the data packet into multiple TREs if it can't fit 38362306a36Sopenharmony_ci * the packet in a single TRE. In that case, CHAIN flag will be set by the 38462306a36Sopenharmony_ci * host for all TREs except the last one. 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_ci if (MHI_TRE_DATA_GET_CHAIN(el)) { 38762306a36Sopenharmony_ci /* 38862306a36Sopenharmony_ci * IEOB (Interrupt on End of Block) flag will be set by the host if 38962306a36Sopenharmony_ci * it expects the completion event for all TREs of a TD. 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_ci if (MHI_TRE_DATA_GET_IEOB(el)) { 39262306a36Sopenharmony_ci ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, 39362306a36Sopenharmony_ci MHI_TRE_DATA_GET_LEN(el), 39462306a36Sopenharmony_ci MHI_EV_CC_EOB); 39562306a36Sopenharmony_ci if (ret < 0) { 39662306a36Sopenharmony_ci dev_err(&mhi_chan->mhi_dev->dev, 39762306a36Sopenharmony_ci "Error sending transfer compl. event\n"); 39862306a36Sopenharmony_ci return ret; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci } else { 40262306a36Sopenharmony_ci /* 40362306a36Sopenharmony_ci * IEOT (Interrupt on End of Transfer) flag will be set by the host 40462306a36Sopenharmony_ci * for the last TRE of the TD and expects the completion event for 40562306a36Sopenharmony_ci * the same. 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_ci if (MHI_TRE_DATA_GET_IEOT(el)) { 40862306a36Sopenharmony_ci ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, 40962306a36Sopenharmony_ci MHI_TRE_DATA_GET_LEN(el), 41062306a36Sopenharmony_ci MHI_EV_CC_EOT); 41162306a36Sopenharmony_ci if (ret < 0) { 41262306a36Sopenharmony_ci dev_err(&mhi_chan->mhi_dev->dev, 41362306a36Sopenharmony_ci "Error sending transfer compl. event\n"); 41462306a36Sopenharmony_ci return ret; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci tr_done = true; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci mhi_ep_ring_inc_index(ring); 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci result->bytes_xferd += tr_len; 42562306a36Sopenharmony_ci } while (buf_left && !tr_done); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int mhi_ep_process_ch_ring(struct mhi_ep_ring *ring, struct mhi_ring_element *el) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl; 43362306a36Sopenharmony_ci struct mhi_result result = {}; 43462306a36Sopenharmony_ci u32 len = MHI_EP_DEFAULT_MTU; 43562306a36Sopenharmony_ci struct mhi_ep_chan *mhi_chan; 43662306a36Sopenharmony_ci int ret; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci mhi_chan = &mhi_cntrl->mhi_chan[ring->ch_id]; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* 44162306a36Sopenharmony_ci * Bail out if transfer callback is not registered for the channel. 44262306a36Sopenharmony_ci * This is most likely due to the client driver not loaded at this point. 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_ci if (!mhi_chan->xfer_cb) { 44562306a36Sopenharmony_ci dev_err(&mhi_chan->mhi_dev->dev, "Client driver not available\n"); 44662306a36Sopenharmony_ci return -ENODEV; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (ring->ch_id % 2) { 45062306a36Sopenharmony_ci /* DL channel */ 45162306a36Sopenharmony_ci result.dir = mhi_chan->dir; 45262306a36Sopenharmony_ci mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); 45362306a36Sopenharmony_ci } else { 45462306a36Sopenharmony_ci /* UL channel */ 45562306a36Sopenharmony_ci result.buf_addr = kmem_cache_zalloc(mhi_cntrl->tre_buf_cache, GFP_KERNEL | GFP_DMA); 45662306a36Sopenharmony_ci if (!result.buf_addr) 45762306a36Sopenharmony_ci return -ENOMEM; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci do { 46062306a36Sopenharmony_ci ret = mhi_ep_read_channel(mhi_cntrl, ring, &result, len); 46162306a36Sopenharmony_ci if (ret < 0) { 46262306a36Sopenharmony_ci dev_err(&mhi_chan->mhi_dev->dev, "Failed to read channel\n"); 46362306a36Sopenharmony_ci kmem_cache_free(mhi_cntrl->tre_buf_cache, result.buf_addr); 46462306a36Sopenharmony_ci return ret; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci result.dir = mhi_chan->dir; 46862306a36Sopenharmony_ci mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); 46962306a36Sopenharmony_ci result.bytes_xferd = 0; 47062306a36Sopenharmony_ci memset(result.buf_addr, 0, len); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* Read until the ring becomes empty */ 47362306a36Sopenharmony_ci } while (!mhi_ep_queue_is_empty(mhi_chan->mhi_dev, DMA_TO_DEVICE)); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci kmem_cache_free(mhi_cntrl->tre_buf_cache, result.buf_addr); 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return 0; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci/* TODO: Handle partially formed TDs */ 48262306a36Sopenharmony_ciint mhi_ep_queue_skb(struct mhi_ep_device *mhi_dev, struct sk_buff *skb) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl; 48562306a36Sopenharmony_ci struct mhi_ep_chan *mhi_chan = mhi_dev->dl_chan; 48662306a36Sopenharmony_ci struct device *dev = &mhi_chan->mhi_dev->dev; 48762306a36Sopenharmony_ci struct mhi_ep_buf_info buf_info = {}; 48862306a36Sopenharmony_ci struct mhi_ring_element *el; 48962306a36Sopenharmony_ci u32 buf_left, read_offset; 49062306a36Sopenharmony_ci struct mhi_ep_ring *ring; 49162306a36Sopenharmony_ci enum mhi_ev_ccs code; 49262306a36Sopenharmony_ci size_t tr_len; 49362306a36Sopenharmony_ci u32 tre_len; 49462306a36Sopenharmony_ci int ret; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci buf_left = skb->len; 49762306a36Sopenharmony_ci ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci mutex_lock(&mhi_chan->lock); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci do { 50262306a36Sopenharmony_ci /* Don't process the transfer ring if the channel is not in RUNNING state */ 50362306a36Sopenharmony_ci if (mhi_chan->state != MHI_CH_STATE_RUNNING) { 50462306a36Sopenharmony_ci dev_err(dev, "Channel not available\n"); 50562306a36Sopenharmony_ci ret = -ENODEV; 50662306a36Sopenharmony_ci goto err_exit; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (mhi_ep_queue_is_empty(mhi_dev, DMA_FROM_DEVICE)) { 51062306a36Sopenharmony_ci dev_err(dev, "TRE not available!\n"); 51162306a36Sopenharmony_ci ret = -ENOSPC; 51262306a36Sopenharmony_ci goto err_exit; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci el = &ring->ring_cache[ring->rd_offset]; 51662306a36Sopenharmony_ci tre_len = MHI_TRE_DATA_GET_LEN(el); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci tr_len = min(buf_left, tre_len); 51962306a36Sopenharmony_ci read_offset = skb->len - buf_left; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci buf_info.dev_addr = skb->data + read_offset; 52262306a36Sopenharmony_ci buf_info.host_addr = MHI_TRE_DATA_GET_PTR(el); 52362306a36Sopenharmony_ci buf_info.size = tr_len; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci dev_dbg(dev, "Writing %zd bytes to channel (%u)\n", tr_len, ring->ch_id); 52662306a36Sopenharmony_ci ret = mhi_cntrl->write_to_host(mhi_cntrl, &buf_info); 52762306a36Sopenharmony_ci if (ret < 0) { 52862306a36Sopenharmony_ci dev_err(dev, "Error writing to the channel\n"); 52962306a36Sopenharmony_ci goto err_exit; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci buf_left -= tr_len; 53362306a36Sopenharmony_ci /* 53462306a36Sopenharmony_ci * For all TREs queued by the host for DL channel, only the EOT flag will be set. 53562306a36Sopenharmony_ci * If the packet doesn't fit into a single TRE, send the OVERFLOW event to 53662306a36Sopenharmony_ci * the host so that the host can adjust the packet boundary to next TREs. Else send 53762306a36Sopenharmony_ci * the EOT event to the host indicating the packet boundary. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci if (buf_left) 54062306a36Sopenharmony_ci code = MHI_EV_CC_OVERFLOW; 54162306a36Sopenharmony_ci else 54262306a36Sopenharmony_ci code = MHI_EV_CC_EOT; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, tr_len, code); 54562306a36Sopenharmony_ci if (ret) { 54662306a36Sopenharmony_ci dev_err(dev, "Error sending transfer completion event\n"); 54762306a36Sopenharmony_ci goto err_exit; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci mhi_ep_ring_inc_index(ring); 55162306a36Sopenharmony_ci } while (buf_left); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci mutex_unlock(&mhi_chan->lock); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cierr_exit: 55862306a36Sopenharmony_ci mutex_unlock(&mhi_chan->lock); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return ret; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_ep_queue_skb); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int mhi_ep_cache_host_cfg(struct mhi_ep_cntrl *mhi_cntrl) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci size_t cmd_ctx_host_size, ch_ctx_host_size, ev_ctx_host_size; 56762306a36Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 56862306a36Sopenharmony_ci int ret; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* Update the number of event rings (NER) programmed by the host */ 57162306a36Sopenharmony_ci mhi_ep_mmio_update_ner(mhi_cntrl); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci dev_dbg(dev, "Number of Event rings: %u, HW Event rings: %u\n", 57462306a36Sopenharmony_ci mhi_cntrl->event_rings, mhi_cntrl->hw_event_rings); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci ch_ctx_host_size = sizeof(struct mhi_chan_ctxt) * mhi_cntrl->max_chan; 57762306a36Sopenharmony_ci ev_ctx_host_size = sizeof(struct mhi_event_ctxt) * mhi_cntrl->event_rings; 57862306a36Sopenharmony_ci cmd_ctx_host_size = sizeof(struct mhi_cmd_ctxt) * NR_OF_CMD_RINGS; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* Get the channel context base pointer from host */ 58162306a36Sopenharmony_ci mhi_ep_mmio_get_chc_base(mhi_cntrl); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* Allocate and map memory for caching host channel context */ 58462306a36Sopenharmony_ci ret = mhi_cntrl->alloc_map(mhi_cntrl, mhi_cntrl->ch_ctx_host_pa, 58562306a36Sopenharmony_ci &mhi_cntrl->ch_ctx_cache_phys, 58662306a36Sopenharmony_ci (void __iomem **) &mhi_cntrl->ch_ctx_cache, 58762306a36Sopenharmony_ci ch_ctx_host_size); 58862306a36Sopenharmony_ci if (ret) { 58962306a36Sopenharmony_ci dev_err(dev, "Failed to allocate and map ch_ctx_cache\n"); 59062306a36Sopenharmony_ci return ret; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* Get the event context base pointer from host */ 59462306a36Sopenharmony_ci mhi_ep_mmio_get_erc_base(mhi_cntrl); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* Allocate and map memory for caching host event context */ 59762306a36Sopenharmony_ci ret = mhi_cntrl->alloc_map(mhi_cntrl, mhi_cntrl->ev_ctx_host_pa, 59862306a36Sopenharmony_ci &mhi_cntrl->ev_ctx_cache_phys, 59962306a36Sopenharmony_ci (void __iomem **) &mhi_cntrl->ev_ctx_cache, 60062306a36Sopenharmony_ci ev_ctx_host_size); 60162306a36Sopenharmony_ci if (ret) { 60262306a36Sopenharmony_ci dev_err(dev, "Failed to allocate and map ev_ctx_cache\n"); 60362306a36Sopenharmony_ci goto err_ch_ctx; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* Get the command context base pointer from host */ 60762306a36Sopenharmony_ci mhi_ep_mmio_get_crc_base(mhi_cntrl); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* Allocate and map memory for caching host command context */ 61062306a36Sopenharmony_ci ret = mhi_cntrl->alloc_map(mhi_cntrl, mhi_cntrl->cmd_ctx_host_pa, 61162306a36Sopenharmony_ci &mhi_cntrl->cmd_ctx_cache_phys, 61262306a36Sopenharmony_ci (void __iomem **) &mhi_cntrl->cmd_ctx_cache, 61362306a36Sopenharmony_ci cmd_ctx_host_size); 61462306a36Sopenharmony_ci if (ret) { 61562306a36Sopenharmony_ci dev_err(dev, "Failed to allocate and map cmd_ctx_cache\n"); 61662306a36Sopenharmony_ci goto err_ev_ctx; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* Initialize command ring */ 62062306a36Sopenharmony_ci ret = mhi_ep_ring_start(mhi_cntrl, &mhi_cntrl->mhi_cmd->ring, 62162306a36Sopenharmony_ci (union mhi_ep_ring_ctx *)mhi_cntrl->cmd_ctx_cache); 62262306a36Sopenharmony_ci if (ret) { 62362306a36Sopenharmony_ci dev_err(dev, "Failed to start the command ring\n"); 62462306a36Sopenharmony_ci goto err_cmd_ctx; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci return ret; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cierr_cmd_ctx: 63062306a36Sopenharmony_ci mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->cmd_ctx_host_pa, mhi_cntrl->cmd_ctx_cache_phys, 63162306a36Sopenharmony_ci (void __iomem *) mhi_cntrl->cmd_ctx_cache, cmd_ctx_host_size); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cierr_ev_ctx: 63462306a36Sopenharmony_ci mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->ev_ctx_host_pa, mhi_cntrl->ev_ctx_cache_phys, 63562306a36Sopenharmony_ci (void __iomem *) mhi_cntrl->ev_ctx_cache, ev_ctx_host_size); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cierr_ch_ctx: 63862306a36Sopenharmony_ci mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->ch_ctx_host_pa, mhi_cntrl->ch_ctx_cache_phys, 63962306a36Sopenharmony_ci (void __iomem *) mhi_cntrl->ch_ctx_cache, ch_ctx_host_size); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return ret; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic void mhi_ep_free_host_cfg(struct mhi_ep_cntrl *mhi_cntrl) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci size_t cmd_ctx_host_size, ch_ctx_host_size, ev_ctx_host_size; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci ch_ctx_host_size = sizeof(struct mhi_chan_ctxt) * mhi_cntrl->max_chan; 64962306a36Sopenharmony_ci ev_ctx_host_size = sizeof(struct mhi_event_ctxt) * mhi_cntrl->event_rings; 65062306a36Sopenharmony_ci cmd_ctx_host_size = sizeof(struct mhi_cmd_ctxt) * NR_OF_CMD_RINGS; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->cmd_ctx_host_pa, mhi_cntrl->cmd_ctx_cache_phys, 65362306a36Sopenharmony_ci (void __iomem *) mhi_cntrl->cmd_ctx_cache, cmd_ctx_host_size); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->ev_ctx_host_pa, mhi_cntrl->ev_ctx_cache_phys, 65662306a36Sopenharmony_ci (void __iomem *) mhi_cntrl->ev_ctx_cache, ev_ctx_host_size); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->ch_ctx_host_pa, mhi_cntrl->ch_ctx_cache_phys, 65962306a36Sopenharmony_ci (void __iomem *) mhi_cntrl->ch_ctx_cache, ch_ctx_host_size); 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic void mhi_ep_enable_int(struct mhi_ep_cntrl *mhi_cntrl) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci /* 66562306a36Sopenharmony_ci * Doorbell interrupts are enabled when the corresponding channel gets started. 66662306a36Sopenharmony_ci * Enabling all interrupts here triggers spurious irqs as some of the interrupts 66762306a36Sopenharmony_ci * associated with hw channels always get triggered. 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_ci mhi_ep_mmio_enable_ctrl_interrupt(mhi_cntrl); 67062306a36Sopenharmony_ci mhi_ep_mmio_enable_cmdb_interrupt(mhi_cntrl); 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic int mhi_ep_enable(struct mhi_ep_cntrl *mhi_cntrl) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 67662306a36Sopenharmony_ci enum mhi_state state; 67762306a36Sopenharmony_ci bool mhi_reset; 67862306a36Sopenharmony_ci u32 count = 0; 67962306a36Sopenharmony_ci int ret; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* Wait for Host to set the M0 state */ 68262306a36Sopenharmony_ci do { 68362306a36Sopenharmony_ci msleep(M0_WAIT_DELAY_MS); 68462306a36Sopenharmony_ci mhi_ep_mmio_get_mhi_state(mhi_cntrl, &state, &mhi_reset); 68562306a36Sopenharmony_ci if (mhi_reset) { 68662306a36Sopenharmony_ci /* Clear the MHI reset if host is in reset state */ 68762306a36Sopenharmony_ci mhi_ep_mmio_clear_reset(mhi_cntrl); 68862306a36Sopenharmony_ci dev_info(dev, "Detected Host reset while waiting for M0\n"); 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci count++; 69162306a36Sopenharmony_ci } while (state != MHI_STATE_M0 && count < M0_WAIT_COUNT); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (state != MHI_STATE_M0) { 69462306a36Sopenharmony_ci dev_err(dev, "Host failed to enter M0\n"); 69562306a36Sopenharmony_ci return -ETIMEDOUT; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci ret = mhi_ep_cache_host_cfg(mhi_cntrl); 69962306a36Sopenharmony_ci if (ret) { 70062306a36Sopenharmony_ci dev_err(dev, "Failed to cache host config\n"); 70162306a36Sopenharmony_ci return ret; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci mhi_ep_mmio_set_env(mhi_cntrl, MHI_EE_AMSS); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* Enable all interrupts now */ 70762306a36Sopenharmony_ci mhi_ep_enable_int(mhi_cntrl); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return 0; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic void mhi_ep_cmd_ring_worker(struct work_struct *work) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, cmd_ring_work); 71562306a36Sopenharmony_ci struct mhi_ep_ring *ring = &mhi_cntrl->mhi_cmd->ring; 71662306a36Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 71762306a36Sopenharmony_ci struct mhi_ring_element *el; 71862306a36Sopenharmony_ci int ret; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* Update the write offset for the ring */ 72162306a36Sopenharmony_ci ret = mhi_ep_update_wr_offset(ring); 72262306a36Sopenharmony_ci if (ret) { 72362306a36Sopenharmony_ci dev_err(dev, "Error updating write offset for ring\n"); 72462306a36Sopenharmony_ci return; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* Sanity check to make sure there are elements in the ring */ 72862306a36Sopenharmony_ci if (ring->rd_offset == ring->wr_offset) 72962306a36Sopenharmony_ci return; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* 73262306a36Sopenharmony_ci * Process command ring element till write offset. In case of an error, just try to 73362306a36Sopenharmony_ci * process next element. 73462306a36Sopenharmony_ci */ 73562306a36Sopenharmony_ci while (ring->rd_offset != ring->wr_offset) { 73662306a36Sopenharmony_ci el = &ring->ring_cache[ring->rd_offset]; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci ret = mhi_ep_process_cmd_ring(ring, el); 73962306a36Sopenharmony_ci if (ret && ret != -ENODEV) 74062306a36Sopenharmony_ci dev_err(dev, "Error processing cmd ring element: %zu\n", ring->rd_offset); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci mhi_ep_ring_inc_index(ring); 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic void mhi_ep_ch_ring_worker(struct work_struct *work) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, ch_ring_work); 74962306a36Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 75062306a36Sopenharmony_ci struct mhi_ep_ring_item *itr, *tmp; 75162306a36Sopenharmony_ci struct mhi_ring_element *el; 75262306a36Sopenharmony_ci struct mhi_ep_ring *ring; 75362306a36Sopenharmony_ci struct mhi_ep_chan *chan; 75462306a36Sopenharmony_ci unsigned long flags; 75562306a36Sopenharmony_ci LIST_HEAD(head); 75662306a36Sopenharmony_ci int ret; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci spin_lock_irqsave(&mhi_cntrl->list_lock, flags); 75962306a36Sopenharmony_ci list_splice_tail_init(&mhi_cntrl->ch_db_list, &head); 76062306a36Sopenharmony_ci spin_unlock_irqrestore(&mhi_cntrl->list_lock, flags); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* Process each queued channel ring. In case of an error, just process next element. */ 76362306a36Sopenharmony_ci list_for_each_entry_safe(itr, tmp, &head, node) { 76462306a36Sopenharmony_ci list_del(&itr->node); 76562306a36Sopenharmony_ci ring = itr->ring; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci chan = &mhi_cntrl->mhi_chan[ring->ch_id]; 76862306a36Sopenharmony_ci mutex_lock(&chan->lock); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* 77162306a36Sopenharmony_ci * The ring could've stopped while we waited to grab the (chan->lock), so do 77262306a36Sopenharmony_ci * a sanity check before going further. 77362306a36Sopenharmony_ci */ 77462306a36Sopenharmony_ci if (!ring->started) { 77562306a36Sopenharmony_ci mutex_unlock(&chan->lock); 77662306a36Sopenharmony_ci kfree(itr); 77762306a36Sopenharmony_ci continue; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* Update the write offset for the ring */ 78162306a36Sopenharmony_ci ret = mhi_ep_update_wr_offset(ring); 78262306a36Sopenharmony_ci if (ret) { 78362306a36Sopenharmony_ci dev_err(dev, "Error updating write offset for ring\n"); 78462306a36Sopenharmony_ci mutex_unlock(&chan->lock); 78562306a36Sopenharmony_ci kmem_cache_free(mhi_cntrl->ring_item_cache, itr); 78662306a36Sopenharmony_ci continue; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* Sanity check to make sure there are elements in the ring */ 79062306a36Sopenharmony_ci if (ring->rd_offset == ring->wr_offset) { 79162306a36Sopenharmony_ci mutex_unlock(&chan->lock); 79262306a36Sopenharmony_ci kmem_cache_free(mhi_cntrl->ring_item_cache, itr); 79362306a36Sopenharmony_ci continue; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci el = &ring->ring_cache[ring->rd_offset]; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci dev_dbg(dev, "Processing the ring for channel (%u)\n", ring->ch_id); 79962306a36Sopenharmony_ci ret = mhi_ep_process_ch_ring(ring, el); 80062306a36Sopenharmony_ci if (ret) { 80162306a36Sopenharmony_ci dev_err(dev, "Error processing ring for channel (%u): %d\n", 80262306a36Sopenharmony_ci ring->ch_id, ret); 80362306a36Sopenharmony_ci mutex_unlock(&chan->lock); 80462306a36Sopenharmony_ci kmem_cache_free(mhi_cntrl->ring_item_cache, itr); 80562306a36Sopenharmony_ci continue; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci mutex_unlock(&chan->lock); 80962306a36Sopenharmony_ci kmem_cache_free(mhi_cntrl->ring_item_cache, itr); 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic void mhi_ep_state_worker(struct work_struct *work) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, state_work); 81662306a36Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 81762306a36Sopenharmony_ci struct mhi_ep_state_transition *itr, *tmp; 81862306a36Sopenharmony_ci unsigned long flags; 81962306a36Sopenharmony_ci LIST_HEAD(head); 82062306a36Sopenharmony_ci int ret; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci spin_lock_irqsave(&mhi_cntrl->list_lock, flags); 82362306a36Sopenharmony_ci list_splice_tail_init(&mhi_cntrl->st_transition_list, &head); 82462306a36Sopenharmony_ci spin_unlock_irqrestore(&mhi_cntrl->list_lock, flags); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci list_for_each_entry_safe(itr, tmp, &head, node) { 82762306a36Sopenharmony_ci list_del(&itr->node); 82862306a36Sopenharmony_ci dev_dbg(dev, "Handling MHI state transition to %s\n", 82962306a36Sopenharmony_ci mhi_state_str(itr->state)); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci switch (itr->state) { 83262306a36Sopenharmony_ci case MHI_STATE_M0: 83362306a36Sopenharmony_ci ret = mhi_ep_set_m0_state(mhi_cntrl); 83462306a36Sopenharmony_ci if (ret) 83562306a36Sopenharmony_ci dev_err(dev, "Failed to transition to M0 state\n"); 83662306a36Sopenharmony_ci break; 83762306a36Sopenharmony_ci case MHI_STATE_M3: 83862306a36Sopenharmony_ci ret = mhi_ep_set_m3_state(mhi_cntrl); 83962306a36Sopenharmony_ci if (ret) 84062306a36Sopenharmony_ci dev_err(dev, "Failed to transition to M3 state\n"); 84162306a36Sopenharmony_ci break; 84262306a36Sopenharmony_ci default: 84362306a36Sopenharmony_ci dev_err(dev, "Invalid MHI state transition: %d\n", itr->state); 84462306a36Sopenharmony_ci break; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci kfree(itr); 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic void mhi_ep_queue_channel_db(struct mhi_ep_cntrl *mhi_cntrl, unsigned long ch_int, 85162306a36Sopenharmony_ci u32 ch_idx) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct mhi_ep_ring_item *item; 85462306a36Sopenharmony_ci struct mhi_ep_ring *ring; 85562306a36Sopenharmony_ci bool work = !!ch_int; 85662306a36Sopenharmony_ci LIST_HEAD(head); 85762306a36Sopenharmony_ci u32 i; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* First add the ring items to a local list */ 86062306a36Sopenharmony_ci for_each_set_bit(i, &ch_int, 32) { 86162306a36Sopenharmony_ci /* Channel index varies for each register: 0, 32, 64, 96 */ 86262306a36Sopenharmony_ci u32 ch_id = ch_idx + i; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci ring = &mhi_cntrl->mhi_chan[ch_id].ring; 86562306a36Sopenharmony_ci item = kmem_cache_zalloc(mhi_cntrl->ring_item_cache, GFP_ATOMIC); 86662306a36Sopenharmony_ci if (!item) 86762306a36Sopenharmony_ci return; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci item->ring = ring; 87062306a36Sopenharmony_ci list_add_tail(&item->node, &head); 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci /* Now, splice the local list into ch_db_list and queue the work item */ 87462306a36Sopenharmony_ci if (work) { 87562306a36Sopenharmony_ci spin_lock(&mhi_cntrl->list_lock); 87662306a36Sopenharmony_ci list_splice_tail_init(&head, &mhi_cntrl->ch_db_list); 87762306a36Sopenharmony_ci spin_unlock(&mhi_cntrl->list_lock); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci queue_work(mhi_cntrl->wq, &mhi_cntrl->ch_ring_work); 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci/* 88462306a36Sopenharmony_ci * Channel interrupt statuses are contained in 4 registers each of 32bit length. 88562306a36Sopenharmony_ci * For checking all interrupts, we need to loop through each registers and then 88662306a36Sopenharmony_ci * check for bits set. 88762306a36Sopenharmony_ci */ 88862306a36Sopenharmony_cistatic void mhi_ep_check_channel_interrupt(struct mhi_ep_cntrl *mhi_cntrl) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci u32 ch_int, ch_idx, i; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci /* Bail out if there is no channel doorbell interrupt */ 89362306a36Sopenharmony_ci if (!mhi_ep_mmio_read_chdb_status_interrupts(mhi_cntrl)) 89462306a36Sopenharmony_ci return; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci for (i = 0; i < MHI_MASK_ROWS_CH_DB; i++) { 89762306a36Sopenharmony_ci ch_idx = i * MHI_MASK_CH_LEN; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci /* Only process channel interrupt if the mask is enabled */ 90062306a36Sopenharmony_ci ch_int = mhi_cntrl->chdb[i].status & mhi_cntrl->chdb[i].mask; 90162306a36Sopenharmony_ci if (ch_int) { 90262306a36Sopenharmony_ci mhi_ep_queue_channel_db(mhi_cntrl, ch_int, ch_idx); 90362306a36Sopenharmony_ci mhi_ep_mmio_write(mhi_cntrl, MHI_CHDB_INT_CLEAR_n(i), 90462306a36Sopenharmony_ci mhi_cntrl->chdb[i].status); 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci} 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_cistatic void mhi_ep_process_ctrl_interrupt(struct mhi_ep_cntrl *mhi_cntrl, 91062306a36Sopenharmony_ci enum mhi_state state) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci struct mhi_ep_state_transition *item; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci item = kzalloc(sizeof(*item), GFP_ATOMIC); 91562306a36Sopenharmony_ci if (!item) 91662306a36Sopenharmony_ci return; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci item->state = state; 91962306a36Sopenharmony_ci spin_lock(&mhi_cntrl->list_lock); 92062306a36Sopenharmony_ci list_add_tail(&item->node, &mhi_cntrl->st_transition_list); 92162306a36Sopenharmony_ci spin_unlock(&mhi_cntrl->list_lock); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci queue_work(mhi_cntrl->wq, &mhi_cntrl->state_work); 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci/* 92762306a36Sopenharmony_ci * Interrupt handler that services interrupts raised by the host writing to 92862306a36Sopenharmony_ci * MHICTRL and Command ring doorbell (CRDB) registers for state change and 92962306a36Sopenharmony_ci * channel interrupts. 93062306a36Sopenharmony_ci */ 93162306a36Sopenharmony_cistatic irqreturn_t mhi_ep_irq(int irq, void *data) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci struct mhi_ep_cntrl *mhi_cntrl = data; 93462306a36Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 93562306a36Sopenharmony_ci enum mhi_state state; 93662306a36Sopenharmony_ci u32 int_value; 93762306a36Sopenharmony_ci bool mhi_reset; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* Acknowledge the ctrl interrupt */ 94062306a36Sopenharmony_ci int_value = mhi_ep_mmio_read(mhi_cntrl, MHI_CTRL_INT_STATUS); 94162306a36Sopenharmony_ci mhi_ep_mmio_write(mhi_cntrl, MHI_CTRL_INT_CLEAR, int_value); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci /* Check for ctrl interrupt */ 94462306a36Sopenharmony_ci if (FIELD_GET(MHI_CTRL_INT_STATUS_MSK, int_value)) { 94562306a36Sopenharmony_ci dev_dbg(dev, "Processing ctrl interrupt\n"); 94662306a36Sopenharmony_ci mhi_ep_mmio_get_mhi_state(mhi_cntrl, &state, &mhi_reset); 94762306a36Sopenharmony_ci if (mhi_reset) { 94862306a36Sopenharmony_ci dev_info(dev, "Host triggered MHI reset!\n"); 94962306a36Sopenharmony_ci disable_irq_nosync(mhi_cntrl->irq); 95062306a36Sopenharmony_ci schedule_work(&mhi_cntrl->reset_work); 95162306a36Sopenharmony_ci return IRQ_HANDLED; 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci mhi_ep_process_ctrl_interrupt(mhi_cntrl, state); 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* Check for command doorbell interrupt */ 95862306a36Sopenharmony_ci if (FIELD_GET(MHI_CTRL_INT_STATUS_CRDB_MSK, int_value)) { 95962306a36Sopenharmony_ci dev_dbg(dev, "Processing command doorbell interrupt\n"); 96062306a36Sopenharmony_ci queue_work(mhi_cntrl->wq, &mhi_cntrl->cmd_ring_work); 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* Check for channel interrupts */ 96462306a36Sopenharmony_ci mhi_ep_check_channel_interrupt(mhi_cntrl); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci return IRQ_HANDLED; 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci struct mhi_ep_ring *ch_ring, *ev_ring; 97262306a36Sopenharmony_ci struct mhi_result result = {}; 97362306a36Sopenharmony_ci struct mhi_ep_chan *mhi_chan; 97462306a36Sopenharmony_ci int i; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* Stop all the channels */ 97762306a36Sopenharmony_ci for (i = 0; i < mhi_cntrl->max_chan; i++) { 97862306a36Sopenharmony_ci mhi_chan = &mhi_cntrl->mhi_chan[i]; 97962306a36Sopenharmony_ci if (!mhi_chan->ring.started) 98062306a36Sopenharmony_ci continue; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci mutex_lock(&mhi_chan->lock); 98362306a36Sopenharmony_ci /* Send channel disconnect status to client drivers */ 98462306a36Sopenharmony_ci if (mhi_chan->xfer_cb) { 98562306a36Sopenharmony_ci result.transaction_status = -ENOTCONN; 98662306a36Sopenharmony_ci result.bytes_xferd = 0; 98762306a36Sopenharmony_ci mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci mhi_chan->state = MHI_CH_STATE_DISABLED; 99162306a36Sopenharmony_ci mutex_unlock(&mhi_chan->lock); 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci flush_workqueue(mhi_cntrl->wq); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci /* Destroy devices associated with all channels */ 99762306a36Sopenharmony_ci device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_ep_destroy_device); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci /* Stop and reset the transfer rings */ 100062306a36Sopenharmony_ci for (i = 0; i < mhi_cntrl->max_chan; i++) { 100162306a36Sopenharmony_ci mhi_chan = &mhi_cntrl->mhi_chan[i]; 100262306a36Sopenharmony_ci if (!mhi_chan->ring.started) 100362306a36Sopenharmony_ci continue; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci ch_ring = &mhi_cntrl->mhi_chan[i].ring; 100662306a36Sopenharmony_ci mutex_lock(&mhi_chan->lock); 100762306a36Sopenharmony_ci mhi_ep_ring_reset(mhi_cntrl, ch_ring); 100862306a36Sopenharmony_ci mutex_unlock(&mhi_chan->lock); 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci /* Stop and reset the event rings */ 101262306a36Sopenharmony_ci for (i = 0; i < mhi_cntrl->event_rings; i++) { 101362306a36Sopenharmony_ci ev_ring = &mhi_cntrl->mhi_event[i].ring; 101462306a36Sopenharmony_ci if (!ev_ring->started) 101562306a36Sopenharmony_ci continue; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci mutex_lock(&mhi_cntrl->event_lock); 101862306a36Sopenharmony_ci mhi_ep_ring_reset(mhi_cntrl, ev_ring); 101962306a36Sopenharmony_ci mutex_unlock(&mhi_cntrl->event_lock); 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci /* Stop and reset the command ring */ 102362306a36Sopenharmony_ci mhi_ep_ring_reset(mhi_cntrl, &mhi_cntrl->mhi_cmd->ring); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci mhi_ep_free_host_cfg(mhi_cntrl); 102662306a36Sopenharmony_ci mhi_ep_mmio_mask_interrupts(mhi_cntrl); 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci mhi_cntrl->enabled = false; 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_cistatic void mhi_ep_reset_worker(struct work_struct *work) 103262306a36Sopenharmony_ci{ 103362306a36Sopenharmony_ci struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, reset_work); 103462306a36Sopenharmony_ci enum mhi_state cur_state; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci mhi_ep_power_down(mhi_cntrl); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci mutex_lock(&mhi_cntrl->state_lock); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci /* Reset MMIO to signal host that the MHI_RESET is completed in endpoint */ 104162306a36Sopenharmony_ci mhi_ep_mmio_reset(mhi_cntrl); 104262306a36Sopenharmony_ci cur_state = mhi_cntrl->mhi_state; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci /* 104562306a36Sopenharmony_ci * Only proceed further if the reset is due to SYS_ERR. The host will 104662306a36Sopenharmony_ci * issue reset during shutdown also and we don't need to do re-init in 104762306a36Sopenharmony_ci * that case. 104862306a36Sopenharmony_ci */ 104962306a36Sopenharmony_ci if (cur_state == MHI_STATE_SYS_ERR) 105062306a36Sopenharmony_ci mhi_ep_power_up(mhi_cntrl); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci mutex_unlock(&mhi_cntrl->state_lock); 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci/* 105662306a36Sopenharmony_ci * We don't need to do anything special other than setting the MHI SYS_ERR 105762306a36Sopenharmony_ci * state. The host will reset all contexts and issue MHI RESET so that we 105862306a36Sopenharmony_ci * could also recover from error state. 105962306a36Sopenharmony_ci */ 106062306a36Sopenharmony_civoid mhi_ep_handle_syserr(struct mhi_ep_cntrl *mhi_cntrl) 106162306a36Sopenharmony_ci{ 106262306a36Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 106362306a36Sopenharmony_ci int ret; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_SYS_ERR); 106662306a36Sopenharmony_ci if (ret) 106762306a36Sopenharmony_ci return; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* Signal host that the device went to SYS_ERR state */ 107062306a36Sopenharmony_ci ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_SYS_ERR); 107162306a36Sopenharmony_ci if (ret) 107262306a36Sopenharmony_ci dev_err(dev, "Failed sending SYS_ERR state change event: %d\n", ret); 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ciint mhi_ep_power_up(struct mhi_ep_cntrl *mhi_cntrl) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 107862306a36Sopenharmony_ci int ret, i; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci /* 108162306a36Sopenharmony_ci * Mask all interrupts until the state machine is ready. Interrupts will 108262306a36Sopenharmony_ci * be enabled later with mhi_ep_enable(). 108362306a36Sopenharmony_ci */ 108462306a36Sopenharmony_ci mhi_ep_mmio_mask_interrupts(mhi_cntrl); 108562306a36Sopenharmony_ci mhi_ep_mmio_init(mhi_cntrl); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci mhi_cntrl->mhi_event = kzalloc(mhi_cntrl->event_rings * (sizeof(*mhi_cntrl->mhi_event)), 108862306a36Sopenharmony_ci GFP_KERNEL); 108962306a36Sopenharmony_ci if (!mhi_cntrl->mhi_event) 109062306a36Sopenharmony_ci return -ENOMEM; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci /* Initialize command, channel and event rings */ 109362306a36Sopenharmony_ci mhi_ep_ring_init(&mhi_cntrl->mhi_cmd->ring, RING_TYPE_CMD, 0); 109462306a36Sopenharmony_ci for (i = 0; i < mhi_cntrl->max_chan; i++) 109562306a36Sopenharmony_ci mhi_ep_ring_init(&mhi_cntrl->mhi_chan[i].ring, RING_TYPE_CH, i); 109662306a36Sopenharmony_ci for (i = 0; i < mhi_cntrl->event_rings; i++) 109762306a36Sopenharmony_ci mhi_ep_ring_init(&mhi_cntrl->mhi_event[i].ring, RING_TYPE_ER, i); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci mhi_cntrl->mhi_state = MHI_STATE_RESET; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* Set AMSS EE before signaling ready state */ 110262306a36Sopenharmony_ci mhi_ep_mmio_set_env(mhi_cntrl, MHI_EE_AMSS); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci /* All set, notify the host that we are ready */ 110562306a36Sopenharmony_ci ret = mhi_ep_set_ready_state(mhi_cntrl); 110662306a36Sopenharmony_ci if (ret) 110762306a36Sopenharmony_ci goto err_free_event; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci dev_dbg(dev, "READY state notification sent to the host\n"); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci ret = mhi_ep_enable(mhi_cntrl); 111262306a36Sopenharmony_ci if (ret) { 111362306a36Sopenharmony_ci dev_err(dev, "Failed to enable MHI endpoint\n"); 111462306a36Sopenharmony_ci goto err_free_event; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci enable_irq(mhi_cntrl->irq); 111862306a36Sopenharmony_ci mhi_cntrl->enabled = true; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci return 0; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_cierr_free_event: 112362306a36Sopenharmony_ci kfree(mhi_cntrl->mhi_event); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci return ret; 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_ep_power_up); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_civoid mhi_ep_power_down(struct mhi_ep_cntrl *mhi_cntrl) 113062306a36Sopenharmony_ci{ 113162306a36Sopenharmony_ci if (mhi_cntrl->enabled) { 113262306a36Sopenharmony_ci mhi_ep_abort_transfer(mhi_cntrl); 113362306a36Sopenharmony_ci kfree(mhi_cntrl->mhi_event); 113462306a36Sopenharmony_ci disable_irq(mhi_cntrl->irq); 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci} 113762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_ep_power_down); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_civoid mhi_ep_suspend_channels(struct mhi_ep_cntrl *mhi_cntrl) 114062306a36Sopenharmony_ci{ 114162306a36Sopenharmony_ci struct mhi_ep_chan *mhi_chan; 114262306a36Sopenharmony_ci u32 tmp; 114362306a36Sopenharmony_ci int i; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci for (i = 0; i < mhi_cntrl->max_chan; i++) { 114662306a36Sopenharmony_ci mhi_chan = &mhi_cntrl->mhi_chan[i]; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci if (!mhi_chan->mhi_dev) 114962306a36Sopenharmony_ci continue; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci mutex_lock(&mhi_chan->lock); 115262306a36Sopenharmony_ci /* Skip if the channel is not currently running */ 115362306a36Sopenharmony_ci tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[i].chcfg); 115462306a36Sopenharmony_ci if (FIELD_GET(CHAN_CTX_CHSTATE_MASK, tmp) != MHI_CH_STATE_RUNNING) { 115562306a36Sopenharmony_ci mutex_unlock(&mhi_chan->lock); 115662306a36Sopenharmony_ci continue; 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci dev_dbg(&mhi_chan->mhi_dev->dev, "Suspending channel\n"); 116062306a36Sopenharmony_ci /* Set channel state to SUSPENDED */ 116162306a36Sopenharmony_ci mhi_chan->state = MHI_CH_STATE_SUSPENDED; 116262306a36Sopenharmony_ci tmp &= ~CHAN_CTX_CHSTATE_MASK; 116362306a36Sopenharmony_ci tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_SUSPENDED); 116462306a36Sopenharmony_ci mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp); 116562306a36Sopenharmony_ci mutex_unlock(&mhi_chan->lock); 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci} 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_civoid mhi_ep_resume_channels(struct mhi_ep_cntrl *mhi_cntrl) 117062306a36Sopenharmony_ci{ 117162306a36Sopenharmony_ci struct mhi_ep_chan *mhi_chan; 117262306a36Sopenharmony_ci u32 tmp; 117362306a36Sopenharmony_ci int i; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci for (i = 0; i < mhi_cntrl->max_chan; i++) { 117662306a36Sopenharmony_ci mhi_chan = &mhi_cntrl->mhi_chan[i]; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci if (!mhi_chan->mhi_dev) 117962306a36Sopenharmony_ci continue; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci mutex_lock(&mhi_chan->lock); 118262306a36Sopenharmony_ci /* Skip if the channel is not currently suspended */ 118362306a36Sopenharmony_ci tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[i].chcfg); 118462306a36Sopenharmony_ci if (FIELD_GET(CHAN_CTX_CHSTATE_MASK, tmp) != MHI_CH_STATE_SUSPENDED) { 118562306a36Sopenharmony_ci mutex_unlock(&mhi_chan->lock); 118662306a36Sopenharmony_ci continue; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci dev_dbg(&mhi_chan->mhi_dev->dev, "Resuming channel\n"); 119062306a36Sopenharmony_ci /* Set channel state to RUNNING */ 119162306a36Sopenharmony_ci mhi_chan->state = MHI_CH_STATE_RUNNING; 119262306a36Sopenharmony_ci tmp &= ~CHAN_CTX_CHSTATE_MASK; 119362306a36Sopenharmony_ci tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_RUNNING); 119462306a36Sopenharmony_ci mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp); 119562306a36Sopenharmony_ci mutex_unlock(&mhi_chan->lock); 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_cistatic void mhi_ep_release_device(struct device *dev) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER) 120462306a36Sopenharmony_ci mhi_dev->mhi_cntrl->mhi_dev = NULL; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci /* 120762306a36Sopenharmony_ci * We need to set the mhi_chan->mhi_dev to NULL here since the MHI 120862306a36Sopenharmony_ci * devices for the channels will only get created in mhi_ep_create_device() 120962306a36Sopenharmony_ci * if the mhi_dev associated with it is NULL. 121062306a36Sopenharmony_ci */ 121162306a36Sopenharmony_ci if (mhi_dev->ul_chan) 121262306a36Sopenharmony_ci mhi_dev->ul_chan->mhi_dev = NULL; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci if (mhi_dev->dl_chan) 121562306a36Sopenharmony_ci mhi_dev->dl_chan->mhi_dev = NULL; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci kfree(mhi_dev); 121862306a36Sopenharmony_ci} 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_cistatic struct mhi_ep_device *mhi_ep_alloc_device(struct mhi_ep_cntrl *mhi_cntrl, 122162306a36Sopenharmony_ci enum mhi_device_type dev_type) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci struct mhi_ep_device *mhi_dev; 122462306a36Sopenharmony_ci struct device *dev; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci mhi_dev = kzalloc(sizeof(*mhi_dev), GFP_KERNEL); 122762306a36Sopenharmony_ci if (!mhi_dev) 122862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci dev = &mhi_dev->dev; 123162306a36Sopenharmony_ci device_initialize(dev); 123262306a36Sopenharmony_ci dev->bus = &mhi_ep_bus_type; 123362306a36Sopenharmony_ci dev->release = mhi_ep_release_device; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci /* Controller device is always allocated first */ 123662306a36Sopenharmony_ci if (dev_type == MHI_DEVICE_CONTROLLER) 123762306a36Sopenharmony_ci /* for MHI controller device, parent is the bus device (e.g. PCI EPF) */ 123862306a36Sopenharmony_ci dev->parent = mhi_cntrl->cntrl_dev; 123962306a36Sopenharmony_ci else 124062306a36Sopenharmony_ci /* for MHI client devices, parent is the MHI controller device */ 124162306a36Sopenharmony_ci dev->parent = &mhi_cntrl->mhi_dev->dev; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci mhi_dev->mhi_cntrl = mhi_cntrl; 124462306a36Sopenharmony_ci mhi_dev->dev_type = dev_type; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci return mhi_dev; 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci/* 125062306a36Sopenharmony_ci * MHI channels are always defined in pairs with UL as the even numbered 125162306a36Sopenharmony_ci * channel and DL as odd numbered one. This function gets UL channel (primary) 125262306a36Sopenharmony_ci * as the ch_id and always looks after the next entry in channel list for 125362306a36Sopenharmony_ci * the corresponding DL channel (secondary). 125462306a36Sopenharmony_ci */ 125562306a36Sopenharmony_cistatic int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci struct mhi_ep_chan *mhi_chan = &mhi_cntrl->mhi_chan[ch_id]; 125862306a36Sopenharmony_ci struct device *dev = mhi_cntrl->cntrl_dev; 125962306a36Sopenharmony_ci struct mhi_ep_device *mhi_dev; 126062306a36Sopenharmony_ci int ret; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci /* Check if the channel name is same for both UL and DL */ 126362306a36Sopenharmony_ci if (strcmp(mhi_chan->name, mhi_chan[1].name)) { 126462306a36Sopenharmony_ci dev_err(dev, "UL and DL channel names are not same: (%s) != (%s)\n", 126562306a36Sopenharmony_ci mhi_chan->name, mhi_chan[1].name); 126662306a36Sopenharmony_ci return -EINVAL; 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci mhi_dev = mhi_ep_alloc_device(mhi_cntrl, MHI_DEVICE_XFER); 127062306a36Sopenharmony_ci if (IS_ERR(mhi_dev)) 127162306a36Sopenharmony_ci return PTR_ERR(mhi_dev); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci /* Configure primary channel */ 127462306a36Sopenharmony_ci mhi_dev->ul_chan = mhi_chan; 127562306a36Sopenharmony_ci get_device(&mhi_dev->dev); 127662306a36Sopenharmony_ci mhi_chan->mhi_dev = mhi_dev; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci /* Configure secondary channel as well */ 127962306a36Sopenharmony_ci mhi_chan++; 128062306a36Sopenharmony_ci mhi_dev->dl_chan = mhi_chan; 128162306a36Sopenharmony_ci get_device(&mhi_dev->dev); 128262306a36Sopenharmony_ci mhi_chan->mhi_dev = mhi_dev; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci /* Channel name is same for both UL and DL */ 128562306a36Sopenharmony_ci mhi_dev->name = mhi_chan->name; 128662306a36Sopenharmony_ci ret = dev_set_name(&mhi_dev->dev, "%s_%s", 128762306a36Sopenharmony_ci dev_name(&mhi_cntrl->mhi_dev->dev), 128862306a36Sopenharmony_ci mhi_dev->name); 128962306a36Sopenharmony_ci if (ret) { 129062306a36Sopenharmony_ci put_device(&mhi_dev->dev); 129162306a36Sopenharmony_ci return ret; 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci ret = device_add(&mhi_dev->dev); 129562306a36Sopenharmony_ci if (ret) 129662306a36Sopenharmony_ci put_device(&mhi_dev->dev); 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci return ret; 129962306a36Sopenharmony_ci} 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_cistatic int mhi_ep_destroy_device(struct device *dev, void *data) 130262306a36Sopenharmony_ci{ 130362306a36Sopenharmony_ci struct mhi_ep_device *mhi_dev; 130462306a36Sopenharmony_ci struct mhi_ep_cntrl *mhi_cntrl; 130562306a36Sopenharmony_ci struct mhi_ep_chan *ul_chan, *dl_chan; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci if (dev->bus != &mhi_ep_bus_type) 130862306a36Sopenharmony_ci return 0; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci mhi_dev = to_mhi_ep_device(dev); 131162306a36Sopenharmony_ci mhi_cntrl = mhi_dev->mhi_cntrl; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci /* Only destroy devices created for channels */ 131462306a36Sopenharmony_ci if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER) 131562306a36Sopenharmony_ci return 0; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci ul_chan = mhi_dev->ul_chan; 131862306a36Sopenharmony_ci dl_chan = mhi_dev->dl_chan; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci if (ul_chan) 132162306a36Sopenharmony_ci put_device(&ul_chan->mhi_dev->dev); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci if (dl_chan) 132462306a36Sopenharmony_ci put_device(&dl_chan->mhi_dev->dev); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci dev_dbg(&mhi_cntrl->mhi_dev->dev, "Destroying device for chan:%s\n", 132762306a36Sopenharmony_ci mhi_dev->name); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci /* Notify the client and remove the device from MHI bus */ 133062306a36Sopenharmony_ci device_del(dev); 133162306a36Sopenharmony_ci put_device(dev); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci return 0; 133462306a36Sopenharmony_ci} 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_cistatic int mhi_ep_chan_init(struct mhi_ep_cntrl *mhi_cntrl, 133762306a36Sopenharmony_ci const struct mhi_ep_cntrl_config *config) 133862306a36Sopenharmony_ci{ 133962306a36Sopenharmony_ci const struct mhi_ep_channel_config *ch_cfg; 134062306a36Sopenharmony_ci struct device *dev = mhi_cntrl->cntrl_dev; 134162306a36Sopenharmony_ci u32 chan, i; 134262306a36Sopenharmony_ci int ret = -EINVAL; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci mhi_cntrl->max_chan = config->max_channels; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci /* 134762306a36Sopenharmony_ci * Allocate max_channels supported by the MHI endpoint and populate 134862306a36Sopenharmony_ci * only the defined channels 134962306a36Sopenharmony_ci */ 135062306a36Sopenharmony_ci mhi_cntrl->mhi_chan = kcalloc(mhi_cntrl->max_chan, sizeof(*mhi_cntrl->mhi_chan), 135162306a36Sopenharmony_ci GFP_KERNEL); 135262306a36Sopenharmony_ci if (!mhi_cntrl->mhi_chan) 135362306a36Sopenharmony_ci return -ENOMEM; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci for (i = 0; i < config->num_channels; i++) { 135662306a36Sopenharmony_ci struct mhi_ep_chan *mhi_chan; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci ch_cfg = &config->ch_cfg[i]; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci chan = ch_cfg->num; 136162306a36Sopenharmony_ci if (chan >= mhi_cntrl->max_chan) { 136262306a36Sopenharmony_ci dev_err(dev, "Channel (%u) exceeds maximum available channels (%u)\n", 136362306a36Sopenharmony_ci chan, mhi_cntrl->max_chan); 136462306a36Sopenharmony_ci goto error_chan_cfg; 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci /* Bi-directional and direction less channels are not supported */ 136862306a36Sopenharmony_ci if (ch_cfg->dir == DMA_BIDIRECTIONAL || ch_cfg->dir == DMA_NONE) { 136962306a36Sopenharmony_ci dev_err(dev, "Invalid direction (%u) for channel (%u)\n", 137062306a36Sopenharmony_ci ch_cfg->dir, chan); 137162306a36Sopenharmony_ci goto error_chan_cfg; 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci mhi_chan = &mhi_cntrl->mhi_chan[chan]; 137562306a36Sopenharmony_ci mhi_chan->name = ch_cfg->name; 137662306a36Sopenharmony_ci mhi_chan->chan = chan; 137762306a36Sopenharmony_ci mhi_chan->dir = ch_cfg->dir; 137862306a36Sopenharmony_ci mutex_init(&mhi_chan->lock); 137962306a36Sopenharmony_ci } 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci return 0; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_cierror_chan_cfg: 138462306a36Sopenharmony_ci kfree(mhi_cntrl->mhi_chan); 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci return ret; 138762306a36Sopenharmony_ci} 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci/* 139062306a36Sopenharmony_ci * Allocate channel and command rings here. Event rings will be allocated 139162306a36Sopenharmony_ci * in mhi_ep_power_up() as the config comes from the host. 139262306a36Sopenharmony_ci */ 139362306a36Sopenharmony_ciint mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl, 139462306a36Sopenharmony_ci const struct mhi_ep_cntrl_config *config) 139562306a36Sopenharmony_ci{ 139662306a36Sopenharmony_ci struct mhi_ep_device *mhi_dev; 139762306a36Sopenharmony_ci int ret; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci if (!mhi_cntrl || !mhi_cntrl->cntrl_dev || !mhi_cntrl->mmio || !mhi_cntrl->irq) 140062306a36Sopenharmony_ci return -EINVAL; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci ret = mhi_ep_chan_init(mhi_cntrl, config); 140362306a36Sopenharmony_ci if (ret) 140462306a36Sopenharmony_ci return ret; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci mhi_cntrl->mhi_cmd = kcalloc(NR_OF_CMD_RINGS, sizeof(*mhi_cntrl->mhi_cmd), GFP_KERNEL); 140762306a36Sopenharmony_ci if (!mhi_cntrl->mhi_cmd) { 140862306a36Sopenharmony_ci ret = -ENOMEM; 140962306a36Sopenharmony_ci goto err_free_ch; 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci mhi_cntrl->ev_ring_el_cache = kmem_cache_create("mhi_ep_event_ring_el", 141362306a36Sopenharmony_ci sizeof(struct mhi_ring_element), 0, 141462306a36Sopenharmony_ci SLAB_CACHE_DMA, NULL); 141562306a36Sopenharmony_ci if (!mhi_cntrl->ev_ring_el_cache) { 141662306a36Sopenharmony_ci ret = -ENOMEM; 141762306a36Sopenharmony_ci goto err_free_cmd; 141862306a36Sopenharmony_ci } 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci mhi_cntrl->tre_buf_cache = kmem_cache_create("mhi_ep_tre_buf", MHI_EP_DEFAULT_MTU, 0, 142162306a36Sopenharmony_ci SLAB_CACHE_DMA, NULL); 142262306a36Sopenharmony_ci if (!mhi_cntrl->tre_buf_cache) { 142362306a36Sopenharmony_ci ret = -ENOMEM; 142462306a36Sopenharmony_ci goto err_destroy_ev_ring_el_cache; 142562306a36Sopenharmony_ci } 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci mhi_cntrl->ring_item_cache = kmem_cache_create("mhi_ep_ring_item", 142862306a36Sopenharmony_ci sizeof(struct mhi_ep_ring_item), 0, 142962306a36Sopenharmony_ci 0, NULL); 143062306a36Sopenharmony_ci if (!mhi_cntrl->ring_item_cache) { 143162306a36Sopenharmony_ci ret = -ENOMEM; 143262306a36Sopenharmony_ci goto err_destroy_tre_buf_cache; 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci INIT_WORK(&mhi_cntrl->state_work, mhi_ep_state_worker); 143562306a36Sopenharmony_ci INIT_WORK(&mhi_cntrl->reset_work, mhi_ep_reset_worker); 143662306a36Sopenharmony_ci INIT_WORK(&mhi_cntrl->cmd_ring_work, mhi_ep_cmd_ring_worker); 143762306a36Sopenharmony_ci INIT_WORK(&mhi_cntrl->ch_ring_work, mhi_ep_ch_ring_worker); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci mhi_cntrl->wq = alloc_workqueue("mhi_ep_wq", 0, 0); 144062306a36Sopenharmony_ci if (!mhi_cntrl->wq) { 144162306a36Sopenharmony_ci ret = -ENOMEM; 144262306a36Sopenharmony_ci goto err_destroy_ring_item_cache; 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci INIT_LIST_HEAD(&mhi_cntrl->st_transition_list); 144662306a36Sopenharmony_ci INIT_LIST_HEAD(&mhi_cntrl->ch_db_list); 144762306a36Sopenharmony_ci spin_lock_init(&mhi_cntrl->list_lock); 144862306a36Sopenharmony_ci mutex_init(&mhi_cntrl->state_lock); 144962306a36Sopenharmony_ci mutex_init(&mhi_cntrl->event_lock); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci /* Set MHI version and AMSS EE before enumeration */ 145262306a36Sopenharmony_ci mhi_ep_mmio_write(mhi_cntrl, EP_MHIVER, config->mhi_version); 145362306a36Sopenharmony_ci mhi_ep_mmio_set_env(mhi_cntrl, MHI_EE_AMSS); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci /* Set controller index */ 145662306a36Sopenharmony_ci ret = ida_alloc(&mhi_ep_cntrl_ida, GFP_KERNEL); 145762306a36Sopenharmony_ci if (ret < 0) 145862306a36Sopenharmony_ci goto err_destroy_wq; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci mhi_cntrl->index = ret; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci irq_set_status_flags(mhi_cntrl->irq, IRQ_NOAUTOEN); 146362306a36Sopenharmony_ci ret = request_irq(mhi_cntrl->irq, mhi_ep_irq, IRQF_TRIGGER_HIGH, 146462306a36Sopenharmony_ci "doorbell_irq", mhi_cntrl); 146562306a36Sopenharmony_ci if (ret) { 146662306a36Sopenharmony_ci dev_err(mhi_cntrl->cntrl_dev, "Failed to request Doorbell IRQ\n"); 146762306a36Sopenharmony_ci goto err_ida_free; 146862306a36Sopenharmony_ci } 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci /* Allocate the controller device */ 147162306a36Sopenharmony_ci mhi_dev = mhi_ep_alloc_device(mhi_cntrl, MHI_DEVICE_CONTROLLER); 147262306a36Sopenharmony_ci if (IS_ERR(mhi_dev)) { 147362306a36Sopenharmony_ci dev_err(mhi_cntrl->cntrl_dev, "Failed to allocate controller device\n"); 147462306a36Sopenharmony_ci ret = PTR_ERR(mhi_dev); 147562306a36Sopenharmony_ci goto err_free_irq; 147662306a36Sopenharmony_ci } 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci ret = dev_set_name(&mhi_dev->dev, "mhi_ep%u", mhi_cntrl->index); 147962306a36Sopenharmony_ci if (ret) 148062306a36Sopenharmony_ci goto err_put_dev; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci mhi_dev->name = dev_name(&mhi_dev->dev); 148362306a36Sopenharmony_ci mhi_cntrl->mhi_dev = mhi_dev; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci ret = device_add(&mhi_dev->dev); 148662306a36Sopenharmony_ci if (ret) 148762306a36Sopenharmony_ci goto err_put_dev; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci dev_dbg(&mhi_dev->dev, "MHI EP Controller registered\n"); 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci return 0; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_cierr_put_dev: 149462306a36Sopenharmony_ci put_device(&mhi_dev->dev); 149562306a36Sopenharmony_cierr_free_irq: 149662306a36Sopenharmony_ci free_irq(mhi_cntrl->irq, mhi_cntrl); 149762306a36Sopenharmony_cierr_ida_free: 149862306a36Sopenharmony_ci ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index); 149962306a36Sopenharmony_cierr_destroy_wq: 150062306a36Sopenharmony_ci destroy_workqueue(mhi_cntrl->wq); 150162306a36Sopenharmony_cierr_destroy_ring_item_cache: 150262306a36Sopenharmony_ci kmem_cache_destroy(mhi_cntrl->ring_item_cache); 150362306a36Sopenharmony_cierr_destroy_ev_ring_el_cache: 150462306a36Sopenharmony_ci kmem_cache_destroy(mhi_cntrl->ev_ring_el_cache); 150562306a36Sopenharmony_cierr_destroy_tre_buf_cache: 150662306a36Sopenharmony_ci kmem_cache_destroy(mhi_cntrl->tre_buf_cache); 150762306a36Sopenharmony_cierr_free_cmd: 150862306a36Sopenharmony_ci kfree(mhi_cntrl->mhi_cmd); 150962306a36Sopenharmony_cierr_free_ch: 151062306a36Sopenharmony_ci kfree(mhi_cntrl->mhi_chan); 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci return ret; 151362306a36Sopenharmony_ci} 151462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_ep_register_controller); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci/* 151762306a36Sopenharmony_ci * It is expected that the controller drivers will power down the MHI EP stack 151862306a36Sopenharmony_ci * using "mhi_ep_power_down()" before calling this function to unregister themselves. 151962306a36Sopenharmony_ci */ 152062306a36Sopenharmony_civoid mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl) 152162306a36Sopenharmony_ci{ 152262306a36Sopenharmony_ci struct mhi_ep_device *mhi_dev = mhi_cntrl->mhi_dev; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci destroy_workqueue(mhi_cntrl->wq); 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci free_irq(mhi_cntrl->irq, mhi_cntrl); 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci kmem_cache_destroy(mhi_cntrl->tre_buf_cache); 152962306a36Sopenharmony_ci kmem_cache_destroy(mhi_cntrl->ev_ring_el_cache); 153062306a36Sopenharmony_ci kmem_cache_destroy(mhi_cntrl->ring_item_cache); 153162306a36Sopenharmony_ci kfree(mhi_cntrl->mhi_cmd); 153262306a36Sopenharmony_ci kfree(mhi_cntrl->mhi_chan); 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci device_del(&mhi_dev->dev); 153562306a36Sopenharmony_ci put_device(&mhi_dev->dev); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index); 153862306a36Sopenharmony_ci} 153962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_ep_unregister_controller); 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_cistatic int mhi_ep_driver_probe(struct device *dev) 154262306a36Sopenharmony_ci{ 154362306a36Sopenharmony_ci struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev); 154462306a36Sopenharmony_ci struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver); 154562306a36Sopenharmony_ci struct mhi_ep_chan *ul_chan = mhi_dev->ul_chan; 154662306a36Sopenharmony_ci struct mhi_ep_chan *dl_chan = mhi_dev->dl_chan; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci ul_chan->xfer_cb = mhi_drv->ul_xfer_cb; 154962306a36Sopenharmony_ci dl_chan->xfer_cb = mhi_drv->dl_xfer_cb; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci return mhi_drv->probe(mhi_dev, mhi_dev->id); 155262306a36Sopenharmony_ci} 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_cistatic int mhi_ep_driver_remove(struct device *dev) 155562306a36Sopenharmony_ci{ 155662306a36Sopenharmony_ci struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev); 155762306a36Sopenharmony_ci struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver); 155862306a36Sopenharmony_ci struct mhi_result result = {}; 155962306a36Sopenharmony_ci struct mhi_ep_chan *mhi_chan; 156062306a36Sopenharmony_ci int dir; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci /* Skip if it is a controller device */ 156362306a36Sopenharmony_ci if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER) 156462306a36Sopenharmony_ci return 0; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci /* Disconnect the channels associated with the driver */ 156762306a36Sopenharmony_ci for (dir = 0; dir < 2; dir++) { 156862306a36Sopenharmony_ci mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci if (!mhi_chan) 157162306a36Sopenharmony_ci continue; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci mutex_lock(&mhi_chan->lock); 157462306a36Sopenharmony_ci /* Send channel disconnect status to the client driver */ 157562306a36Sopenharmony_ci if (mhi_chan->xfer_cb) { 157662306a36Sopenharmony_ci result.transaction_status = -ENOTCONN; 157762306a36Sopenharmony_ci result.bytes_xferd = 0; 157862306a36Sopenharmony_ci mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci mhi_chan->state = MHI_CH_STATE_DISABLED; 158262306a36Sopenharmony_ci mhi_chan->xfer_cb = NULL; 158362306a36Sopenharmony_ci mutex_unlock(&mhi_chan->lock); 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci /* Remove the client driver now */ 158762306a36Sopenharmony_ci mhi_drv->remove(mhi_dev); 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci return 0; 159062306a36Sopenharmony_ci} 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ciint __mhi_ep_driver_register(struct mhi_ep_driver *mhi_drv, struct module *owner) 159362306a36Sopenharmony_ci{ 159462306a36Sopenharmony_ci struct device_driver *driver = &mhi_drv->driver; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci if (!mhi_drv->probe || !mhi_drv->remove) 159762306a36Sopenharmony_ci return -EINVAL; 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci /* Client drivers should have callbacks defined for both channels */ 160062306a36Sopenharmony_ci if (!mhi_drv->ul_xfer_cb || !mhi_drv->dl_xfer_cb) 160162306a36Sopenharmony_ci return -EINVAL; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci driver->bus = &mhi_ep_bus_type; 160462306a36Sopenharmony_ci driver->owner = owner; 160562306a36Sopenharmony_ci driver->probe = mhi_ep_driver_probe; 160662306a36Sopenharmony_ci driver->remove = mhi_ep_driver_remove; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci return driver_register(driver); 160962306a36Sopenharmony_ci} 161062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__mhi_ep_driver_register); 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_civoid mhi_ep_driver_unregister(struct mhi_ep_driver *mhi_drv) 161362306a36Sopenharmony_ci{ 161462306a36Sopenharmony_ci driver_unregister(&mhi_drv->driver); 161562306a36Sopenharmony_ci} 161662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_ep_driver_unregister); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_cistatic int mhi_ep_uevent(const struct device *dev, struct kobj_uevent_env *env) 161962306a36Sopenharmony_ci{ 162062306a36Sopenharmony_ci const struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev); 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci return add_uevent_var(env, "MODALIAS=" MHI_EP_DEVICE_MODALIAS_FMT, 162362306a36Sopenharmony_ci mhi_dev->name); 162462306a36Sopenharmony_ci} 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_cistatic int mhi_ep_match(struct device *dev, struct device_driver *drv) 162762306a36Sopenharmony_ci{ 162862306a36Sopenharmony_ci struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev); 162962306a36Sopenharmony_ci struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(drv); 163062306a36Sopenharmony_ci const struct mhi_device_id *id; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci /* 163362306a36Sopenharmony_ci * If the device is a controller type then there is no client driver 163462306a36Sopenharmony_ci * associated with it 163562306a36Sopenharmony_ci */ 163662306a36Sopenharmony_ci if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER) 163762306a36Sopenharmony_ci return 0; 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci for (id = mhi_drv->id_table; id->chan[0]; id++) 164062306a36Sopenharmony_ci if (!strcmp(mhi_dev->name, id->chan)) { 164162306a36Sopenharmony_ci mhi_dev->id = id; 164262306a36Sopenharmony_ci return 1; 164362306a36Sopenharmony_ci } 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci return 0; 164662306a36Sopenharmony_ci}; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_cistruct bus_type mhi_ep_bus_type = { 164962306a36Sopenharmony_ci .name = "mhi_ep", 165062306a36Sopenharmony_ci .dev_name = "mhi_ep", 165162306a36Sopenharmony_ci .match = mhi_ep_match, 165262306a36Sopenharmony_ci .uevent = mhi_ep_uevent, 165362306a36Sopenharmony_ci}; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_cistatic int __init mhi_ep_init(void) 165662306a36Sopenharmony_ci{ 165762306a36Sopenharmony_ci return bus_register(&mhi_ep_bus_type); 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_cistatic void __exit mhi_ep_exit(void) 166162306a36Sopenharmony_ci{ 166262306a36Sopenharmony_ci bus_unregister(&mhi_ep_bus_type); 166362306a36Sopenharmony_ci} 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_cipostcore_initcall(mhi_ep_init); 166662306a36Sopenharmony_cimodule_exit(mhi_ep_exit); 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 166962306a36Sopenharmony_ciMODULE_DESCRIPTION("MHI Bus Endpoint stack"); 167062306a36Sopenharmony_ciMODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); 1671