bus: mhi: ep: Add support for powering down the MHI endpoint stack
authorManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Tue, 5 Apr 2022 13:57:46 +0000 (19:27 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 26 Apr 2022 11:17:41 +0000 (13:17 +0200)
Add support for MHI endpoint power_down that includes stopping all
available channels, destroying the channels, resetting the event and
transfer rings and freeing the host cache.

The stack will be powered down whenever the physical bus link goes down.

Reviewed-by: Alex Elder <elder@linaro.org>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Link: https://lore.kernel.org/r/20220405135754.6622-11-manivannan.sadhasivam@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/bus/mhi/ep/main.c
include/linux/mhi_ep.h

index 20d57973348641f1e33a26c64cc5a53057c1ebb4..968025e4d3accafd15f433e1889ff46a96de258f 100644 (file)
@@ -22,6 +22,8 @@
 
 static DEFINE_IDA(mhi_ep_cntrl_ida);
 
+static int mhi_ep_destroy_device(struct device *dev, void *data);
+
 static int mhi_ep_send_event(struct mhi_ep_cntrl *mhi_cntrl, u32 ring_idx,
                             struct mhi_ring_element *el, bool bei)
 {
@@ -400,6 +402,68 @@ static irqreturn_t mhi_ep_irq(int irq, void *data)
        return IRQ_HANDLED;
 }
 
+static void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl)
+{
+       struct mhi_ep_ring *ch_ring, *ev_ring;
+       struct mhi_result result = {};
+       struct mhi_ep_chan *mhi_chan;
+       int i;
+
+       /* Stop all the channels */
+       for (i = 0; i < mhi_cntrl->max_chan; i++) {
+               mhi_chan = &mhi_cntrl->mhi_chan[i];
+               if (!mhi_chan->ring.started)
+                       continue;
+
+               mutex_lock(&mhi_chan->lock);
+               /* Send channel disconnect status to client drivers */
+               if (mhi_chan->xfer_cb) {
+                       result.transaction_status = -ENOTCONN;
+                       result.bytes_xferd = 0;
+                       mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
+               }
+
+               mhi_chan->state = MHI_CH_STATE_DISABLED;
+               mutex_unlock(&mhi_chan->lock);
+       }
+
+       flush_workqueue(mhi_cntrl->wq);
+
+       /* Destroy devices associated with all channels */
+       device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_ep_destroy_device);
+
+       /* Stop and reset the transfer rings */
+       for (i = 0; i < mhi_cntrl->max_chan; i++) {
+               mhi_chan = &mhi_cntrl->mhi_chan[i];
+               if (!mhi_chan->ring.started)
+                       continue;
+
+               ch_ring = &mhi_cntrl->mhi_chan[i].ring;
+               mutex_lock(&mhi_chan->lock);
+               mhi_ep_ring_reset(mhi_cntrl, ch_ring);
+               mutex_unlock(&mhi_chan->lock);
+       }
+
+       /* Stop and reset the event rings */
+       for (i = 0; i < mhi_cntrl->event_rings; i++) {
+               ev_ring = &mhi_cntrl->mhi_event[i].ring;
+               if (!ev_ring->started)
+                       continue;
+
+               mutex_lock(&mhi_cntrl->event_lock);
+               mhi_ep_ring_reset(mhi_cntrl, ev_ring);
+               mutex_unlock(&mhi_cntrl->event_lock);
+       }
+
+       /* Stop and reset the command ring */
+       mhi_ep_ring_reset(mhi_cntrl, &mhi_cntrl->mhi_cmd->ring);
+
+       mhi_ep_free_host_cfg(mhi_cntrl);
+       mhi_ep_mmio_mask_interrupts(mhi_cntrl);
+
+       mhi_cntrl->enabled = false;
+}
+
 int mhi_ep_power_up(struct mhi_ep_cntrl *mhi_cntrl)
 {
        struct device *dev = &mhi_cntrl->mhi_dev->dev;
@@ -454,6 +518,16 @@ err_free_event:
 }
 EXPORT_SYMBOL_GPL(mhi_ep_power_up);
 
+void mhi_ep_power_down(struct mhi_ep_cntrl *mhi_cntrl)
+{
+       if (mhi_cntrl->enabled)
+               mhi_ep_abort_transfer(mhi_cntrl);
+
+       kfree(mhi_cntrl->mhi_event);
+       disable_irq(mhi_cntrl->irq);
+}
+EXPORT_SYMBOL_GPL(mhi_ep_power_down);
+
 static void mhi_ep_release_device(struct device *dev)
 {
        struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
@@ -733,6 +807,10 @@ err_free_ch:
 }
 EXPORT_SYMBOL_GPL(mhi_ep_register_controller);
 
+/*
+ * It is expected that the controller drivers will power down the MHI EP stack
+ * using "mhi_ep_power_down()" before calling this function to unregister themselves.
+ */
 void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl)
 {
        struct mhi_ep_device *mhi_dev = mhi_cntrl->mhi_dev;
index 3b065f82fbebacac3b9585c3283b9d40a1d3f890..9da683e8302c37e32c2746f088b7dee2182be782 100644 (file)
@@ -244,4 +244,10 @@ void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl);
  */
 int mhi_ep_power_up(struct mhi_ep_cntrl *mhi_cntrl);
 
+/**
+ * mhi_ep_power_down - Power down the MHI endpoint stack
+ * @mhi_cntrl: MHI controller
+ */
+void mhi_ep_power_down(struct mhi_ep_cntrl *mhi_cntrl);
+
 #endif