Skip to content

Commit

Permalink
bus: mhi: core: Add support for data transfer
Browse files Browse the repository at this point in the history
Add support for transferring data between external modem and host
processor using MHI protocol.

This is based on the patch submitted by Sujeev Dias:
https://lkml.org/lkml/2018/7/9/988

Signed-off-by: Sujeev Dias <sdias@codeaurora.org>
Signed-off-by: Siddartha Mohanadoss <smohanad@codeaurora.org>
[mani: splitted the data transfer patch and cleaned up for upstream]
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Reviewed-by: Jeffrey Hugo <jhugo@codeaurora.org>
Tested-by: Jeffrey Hugo <jhugo@codeaurora.org>
Link: https://lore.kernel.org/r/20200220095854.4804-12-manivannan.sadhasivam@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Manivannan Sadhasivam authored and Greg Kroah-Hartman committed Mar 19, 2020
1 parent 1d3173a commit 189ff97
Show file tree
Hide file tree
Showing 5 changed files with 979 additions and 8 deletions.
130 changes: 124 additions & 6 deletions drivers/bus/mhi/core/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,73 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
return 0;
}

void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan)
{
struct mhi_ring *buf_ring;
struct mhi_ring *tre_ring;
struct mhi_chan_ctxt *chan_ctxt;

buf_ring = &mhi_chan->buf_ring;
tre_ring = &mhi_chan->tre_ring;
chan_ctxt = &mhi_cntrl->mhi_ctxt->chan_ctxt[mhi_chan->chan];

mhi_free_coherent(mhi_cntrl, tre_ring->alloc_size,
tre_ring->pre_aligned, tre_ring->dma_handle);
vfree(buf_ring->base);

buf_ring->base = tre_ring->base = NULL;
chan_ctxt->rbase = 0;
}

int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan)
{
struct mhi_ring *buf_ring;
struct mhi_ring *tre_ring;
struct mhi_chan_ctxt *chan_ctxt;
u32 tmp;
int ret;

buf_ring = &mhi_chan->buf_ring;
tre_ring = &mhi_chan->tre_ring;
tre_ring->el_size = sizeof(struct mhi_tre);
tre_ring->len = tre_ring->el_size * tre_ring->elements;
chan_ctxt = &mhi_cntrl->mhi_ctxt->chan_ctxt[mhi_chan->chan];
ret = mhi_alloc_aligned_ring(mhi_cntrl, tre_ring, tre_ring->len);
if (ret)
return -ENOMEM;

buf_ring->el_size = sizeof(struct mhi_buf_info);
buf_ring->len = buf_ring->el_size * buf_ring->elements;
buf_ring->base = vzalloc(buf_ring->len);

if (!buf_ring->base) {
mhi_free_coherent(mhi_cntrl, tre_ring->alloc_size,
tre_ring->pre_aligned, tre_ring->dma_handle);
return -ENOMEM;
}

tmp = chan_ctxt->chcfg;
tmp &= ~CHAN_CTX_CHSTATE_MASK;
tmp |= (MHI_CH_STATE_ENABLED << CHAN_CTX_CHSTATE_SHIFT);
chan_ctxt->chcfg = tmp;

chan_ctxt->rbase = tre_ring->iommu_base;
chan_ctxt->rp = chan_ctxt->wp = chan_ctxt->rbase;
chan_ctxt->rlen = tre_ring->len;
tre_ring->ctxt_wp = &chan_ctxt->wp;

tre_ring->rp = tre_ring->wp = tre_ring->base;
buf_ring->rp = buf_ring->wp = buf_ring->base;
mhi_chan->db_cfg.db_mode = 1;

/* Update to all cores */
smp_wmb();

return 0;
}

static int parse_ev_cfg(struct mhi_controller *mhi_cntrl,
struct mhi_controller_config *config)
{
Expand Down Expand Up @@ -799,6 +866,14 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
rwlock_init(&mhi_chan->lock);
}

if (mhi_cntrl->bounce_buf) {
mhi_cntrl->map_single = mhi_map_single_use_bb;
mhi_cntrl->unmap_single = mhi_unmap_single_use_bb;
} else {
mhi_cntrl->map_single = mhi_map_single_no_bb;
mhi_cntrl->unmap_single = mhi_unmap_single_no_bb;
}

/* Register controller with MHI bus */
mhi_dev = mhi_alloc_device(mhi_cntrl);
if (IS_ERR(mhi_dev)) {
Expand Down Expand Up @@ -969,20 +1044,33 @@ static int mhi_driver_probe(struct device *dev)
struct mhi_event *mhi_event;
struct mhi_chan *ul_chan = mhi_dev->ul_chan;
struct mhi_chan *dl_chan = mhi_dev->dl_chan;
int ret;

/* Bring device out of LPM */
ret = mhi_device_get_sync(mhi_dev);
if (ret)
return ret;

ret = -EINVAL;

if (ul_chan) {
/*
* If channel supports LPM notifications then status_cb should
* be provided
*/
if (ul_chan->lpm_notify && !mhi_drv->status_cb)
return -EINVAL;
goto exit_probe;

/* For non-offload channels then xfer_cb should be provided */
if (!ul_chan->offload_ch && !mhi_drv->ul_xfer_cb)
return -EINVAL;
goto exit_probe;

ul_chan->xfer_cb = mhi_drv->ul_xfer_cb;
if (ul_chan->auto_start) {
ret = mhi_prepare_channel(mhi_cntrl, ul_chan);
if (ret)
goto exit_probe;
}
}

if (dl_chan) {
Expand All @@ -991,11 +1079,11 @@ static int mhi_driver_probe(struct device *dev)
* be provided
*/
if (dl_chan->lpm_notify && !mhi_drv->status_cb)
return -EINVAL;
goto exit_probe;

/* For non-offload channels then xfer_cb should be provided */
if (!dl_chan->offload_ch && !mhi_drv->dl_xfer_cb)
return -EINVAL;
goto exit_probe;

mhi_event = &mhi_cntrl->mhi_event[dl_chan->er_index];

Expand All @@ -1005,19 +1093,36 @@ static int mhi_driver_probe(struct device *dev)
* notify pending data
*/
if (mhi_event->cl_manage && !mhi_drv->status_cb)
return -EINVAL;
goto exit_probe;

dl_chan->xfer_cb = mhi_drv->dl_xfer_cb;
}

/* Call the user provided probe function */
return mhi_drv->probe(mhi_dev, mhi_dev->id);
ret = mhi_drv->probe(mhi_dev, mhi_dev->id);
if (ret)
goto exit_probe;

if (dl_chan && dl_chan->auto_start)
mhi_prepare_channel(mhi_cntrl, dl_chan);

mhi_device_put(mhi_dev);

return ret;

exit_probe:
mhi_unprepare_from_transfer(mhi_dev);

mhi_device_put(mhi_dev);

return ret;
}

static int mhi_driver_remove(struct device *dev)
{
struct mhi_device *mhi_dev = to_mhi_device(dev);
struct mhi_driver *mhi_drv = to_mhi_driver(dev->driver);
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
struct mhi_chan *mhi_chan;
enum mhi_ch_state ch_state[] = {
MHI_CH_STATE_DISABLED,
Expand Down Expand Up @@ -1049,6 +1154,10 @@ static int mhi_driver_remove(struct device *dev)
mhi_chan->ch_state = MHI_CH_STATE_SUSPENDED;
write_unlock_irq(&mhi_chan->lock);

/* Reset the non-offload channel */
if (!mhi_chan->offload_ch)
mhi_reset_chan(mhi_cntrl, mhi_chan);

mutex_unlock(&mhi_chan->mutex);
}

Expand All @@ -1063,11 +1172,20 @@ static int mhi_driver_remove(struct device *dev)

mutex_lock(&mhi_chan->mutex);

if (ch_state[dir] == MHI_CH_STATE_ENABLED &&
!mhi_chan->offload_ch)
mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan);

mhi_chan->ch_state = MHI_CH_STATE_DISABLED;

mutex_unlock(&mhi_chan->mutex);
}

read_lock_bh(&mhi_cntrl->pm_lock);
while (mhi_dev->dev_wake)
mhi_device_put(mhi_dev);
read_unlock_bh(&mhi_cntrl->pm_lock);

return 0;
}

Expand Down
22 changes: 22 additions & 0 deletions drivers/bus/mhi/core/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,8 @@ int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl);
void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl);
int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl);
int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl);
int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
enum mhi_cmd_type cmd);

/* Register access methods */
void mhi_db_brstmode(struct mhi_controller *mhi_cntrl, struct db_cfg *db_cfg,
Expand Down Expand Up @@ -618,6 +620,14 @@ int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl);
void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl);
void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
struct image_info *img_info);
int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
void mhi_reset_chan(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);

/* Memory allocation methods */
static inline void *mhi_alloc_coherent(struct mhi_controller *mhi_cntrl,
Expand Down Expand Up @@ -652,4 +662,16 @@ irqreturn_t mhi_irq_handler(int irq_number, void *dev);
irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *dev);
irqreturn_t mhi_intvec_handler(int irq_number, void *dev);

int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
void *buf, void *cb, size_t buf_len, enum mhi_flags flags);

int mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf_info);
int mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf_info);
void mhi_unmap_single_no_bb(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf_info);
void mhi_unmap_single_use_bb(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf_info);

#endif /* _MHI_INT_H */
Loading

0 comments on commit 189ff97

Please sign in to comment.