Merge tag 'driver-core-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-block.git] / drivers / bus / mhi / ep / main.c
index 1dc8a3557a46494f7d5e45509fc1be03abe52209..a6a48e5154784f133f66122d3faaf2bb0aea9551 100644 (file)
@@ -123,6 +123,13 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele
        int ret;
 
        ch_id = MHI_TRE_GET_CMD_CHID(el);
+
+       /* Check if the channel is supported by the controller */
+       if ((ch_id >= mhi_cntrl->max_chan) || !mhi_cntrl->mhi_chan[ch_id].name) {
+               dev_err(dev, "Channel (%u) not supported!\n", ch_id);
+               return -ENODEV;
+       }
+
        mhi_chan = &mhi_cntrl->mhi_chan[ch_id];
        ch_ring = &mhi_cntrl->mhi_chan[ch_id].ring;
 
@@ -196,9 +203,11 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele
                mhi_ep_mmio_disable_chdb(mhi_cntrl, ch_id);
 
                /* Send channel disconnect status to client drivers */
-               result.transaction_status = -ENOTCONN;
-               result.bytes_xferd = 0;
-               mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
+               if (mhi_chan->xfer_cb) {
+                       result.transaction_status = -ENOTCONN;
+                       result.bytes_xferd = 0;
+                       mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
+               }
 
                /* Set channel state to STOP */
                mhi_chan->state = MHI_CH_STATE_STOP;
@@ -217,7 +226,7 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele
                mutex_unlock(&mhi_chan->lock);
                break;
        case MHI_PKT_TYPE_RESET_CHAN_CMD:
-               dev_dbg(dev, "Received STOP command for channel (%u)\n", ch_id);
+               dev_dbg(dev, "Received RESET command for channel (%u)\n", ch_id);
                if (!ch_ring->started) {
                        dev_err(dev, "Channel (%u) not opened\n", ch_id);
                        return -ENODEV;
@@ -228,9 +237,11 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele
                mhi_ep_ring_reset(mhi_cntrl, ch_ring);
 
                /* Send channel disconnect status to client driver */
-               result.transaction_status = -ENOTCONN;
-               result.bytes_xferd = 0;
-               mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
+               if (mhi_chan->xfer_cb) {
+                       result.transaction_status = -ENOTCONN;
+                       result.bytes_xferd = 0;
+                       mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
+               }
 
                /* Set channel state to DISABLED */
                mhi_chan->state = MHI_CH_STATE_DISABLED;
@@ -719,24 +730,37 @@ static void mhi_ep_ch_ring_worker(struct work_struct *work)
                list_del(&itr->node);
                ring = itr->ring;
 
+               chan = &mhi_cntrl->mhi_chan[ring->ch_id];
+               mutex_lock(&chan->lock);
+
+               /*
+                * The ring could've stopped while we waited to grab the (chan->lock), so do
+                * a sanity check before going further.
+                */
+               if (!ring->started) {
+                       mutex_unlock(&chan->lock);
+                       kfree(itr);
+                       continue;
+               }
+
                /* Update the write offset for the ring */
                ret = mhi_ep_update_wr_offset(ring);
                if (ret) {
                        dev_err(dev, "Error updating write offset for ring\n");
+                       mutex_unlock(&chan->lock);
                        kfree(itr);
                        continue;
                }
 
                /* Sanity check to make sure there are elements in the ring */
                if (ring->rd_offset == ring->wr_offset) {
+                       mutex_unlock(&chan->lock);
                        kfree(itr);
                        continue;
                }
 
                el = &ring->ring_cache[ring->rd_offset];
-               chan = &mhi_cntrl->mhi_chan[ring->ch_id];
 
-               mutex_lock(&chan->lock);
                dev_dbg(dev, "Processing the ring for channel (%u)\n", ring->ch_id);
                ret = mhi_ep_process_ch_ring(ring, el);
                if (ret) {
@@ -973,44 +997,25 @@ static void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl)
 static void mhi_ep_reset_worker(struct work_struct *work)
 {
        struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, reset_work);
-       struct device *dev = &mhi_cntrl->mhi_dev->dev;
        enum mhi_state cur_state;
-       int ret;
 
-       mhi_ep_abort_transfer(mhi_cntrl);
+       mhi_ep_power_down(mhi_cntrl);
+
+       mutex_lock(&mhi_cntrl->state_lock);
 
-       spin_lock_bh(&mhi_cntrl->state_lock);
        /* Reset MMIO to signal host that the MHI_RESET is completed in endpoint */
        mhi_ep_mmio_reset(mhi_cntrl);
        cur_state = mhi_cntrl->mhi_state;
-       spin_unlock_bh(&mhi_cntrl->state_lock);
 
        /*
         * Only proceed further if the reset is due to SYS_ERR. The host will
         * issue reset during shutdown also and we don't need to do re-init in
         * that case.
         */
-       if (cur_state == MHI_STATE_SYS_ERR) {
-               mhi_ep_mmio_init(mhi_cntrl);
-
-               /* Set AMSS EE before signaling ready state */
-               mhi_ep_mmio_set_env(mhi_cntrl, MHI_EE_AMSS);
-
-               /* All set, notify the host that we are ready */
-               ret = mhi_ep_set_ready_state(mhi_cntrl);
-               if (ret)
-                       return;
-
-               dev_dbg(dev, "READY state notification sent to the host\n");
-
-               ret = mhi_ep_enable(mhi_cntrl);
-               if (ret) {
-                       dev_err(dev, "Failed to enable MHI endpoint: %d\n", ret);
-                       return;
-               }
+       if (cur_state == MHI_STATE_SYS_ERR)
+               mhi_ep_power_up(mhi_cntrl);
 
-               enable_irq(mhi_cntrl->irq);
-       }
+       mutex_unlock(&mhi_cntrl->state_lock);
 }
 
 /*
@@ -1089,11 +1094,11 @@ EXPORT_SYMBOL_GPL(mhi_ep_power_up);
 
 void mhi_ep_power_down(struct mhi_ep_cntrl *mhi_cntrl)
 {
-       if (mhi_cntrl->enabled)
+       if (mhi_cntrl->enabled) {
                mhi_ep_abort_transfer(mhi_cntrl);
-
-       kfree(mhi_cntrl->mhi_event);
-       disable_irq(mhi_cntrl->irq);
+               kfree(mhi_cntrl->mhi_event);
+               disable_irq(mhi_cntrl->irq);
+       }
 }
 EXPORT_SYMBOL_GPL(mhi_ep_power_down);
 
@@ -1119,6 +1124,7 @@ void mhi_ep_suspend_channels(struct mhi_ep_cntrl *mhi_cntrl)
 
                dev_dbg(&mhi_chan->mhi_dev->dev, "Suspending channel\n");
                /* Set channel state to SUSPENDED */
+               mhi_chan->state = MHI_CH_STATE_SUSPENDED;
                tmp &= ~CHAN_CTX_CHSTATE_MASK;
                tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_SUSPENDED);
                mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp);
@@ -1148,6 +1154,7 @@ void mhi_ep_resume_channels(struct mhi_ep_cntrl *mhi_cntrl)
 
                dev_dbg(&mhi_chan->mhi_dev->dev, "Resuming channel\n");
                /* Set channel state to RUNNING */
+               mhi_chan->state = MHI_CH_STATE_RUNNING;
                tmp &= ~CHAN_CTX_CHSTATE_MASK;
                tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_RUNNING);
                mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp);
@@ -1381,8 +1388,8 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
 
        INIT_LIST_HEAD(&mhi_cntrl->st_transition_list);
        INIT_LIST_HEAD(&mhi_cntrl->ch_db_list);
-       spin_lock_init(&mhi_cntrl->state_lock);
        spin_lock_init(&mhi_cntrl->list_lock);
+       mutex_init(&mhi_cntrl->state_lock);
        mutex_init(&mhi_cntrl->event_lock);
 
        /* Set MHI version and AMSS EE before enumeration */
@@ -1543,9 +1550,9 @@ void mhi_ep_driver_unregister(struct mhi_ep_driver *mhi_drv)
 }
 EXPORT_SYMBOL_GPL(mhi_ep_driver_unregister);
 
-static int mhi_ep_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int mhi_ep_uevent(const struct device *dev, struct kobj_uevent_env *env)
 {
-       struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
+       const struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
 
        return add_uevent_var(env, "MODALIAS=" MHI_EP_DEVICE_MODALIAS_FMT,
                                        mhi_dev->name);