summaryrefslogtreecommitdiff
path: root/drivers/bus/mhi/core/init.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/bus/mhi/core/init.c')
-rw-r--r--drivers/bus/mhi/core/init.c130
1 files changed, 124 insertions, 6 deletions
diff --git a/drivers/bus/mhi/core/init.c b/drivers/bus/mhi/core/init.c
index 3f77397eefea..7bfffa9a5ad1 100644
--- a/drivers/bus/mhi/core/init.c
+++ b/drivers/bus/mhi/core/init.c
@@ -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)
{
@@ -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)) {
@@ -969,6 +1044,14 @@ 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) {
/*
@@ -976,13 +1059,18 @@ static int mhi_driver_probe(struct device *dev)
* 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) {
@@ -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];
@@ -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,
@@ -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);
}
@@ -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;
}