return 0;
 }
 
+static int mhi_ep_process_ch_ring(struct mhi_ep_ring *ring, struct mhi_ring_element *el)
+{
+       struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
+       struct mhi_result result = {};
+       u32 len = MHI_EP_DEFAULT_MTU;
+       struct mhi_ep_chan *mhi_chan;
+       int ret;
+
+       mhi_chan = &mhi_cntrl->mhi_chan[ring->ch_id];
+
+       /*
+        * Bail out if transfer callback is not registered for the channel.
+        * This is most likely due to the client driver not loaded at this point.
+        */
+       if (!mhi_chan->xfer_cb) {
+               dev_err(&mhi_chan->mhi_dev->dev, "Client driver not available\n");
+               return -ENODEV;
+       }
+
+       if (ring->ch_id % 2) {
+               /* DL channel */
+               result.dir = mhi_chan->dir;
+               mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
+       } else {
+               /* UL channel */
+               result.buf_addr = kzalloc(len, GFP_KERNEL);
+               if (!result.buf_addr)
+                       return -ENOMEM;
+
+               do {
+                       ret = mhi_ep_read_channel(mhi_cntrl, ring, &result, len);
+                       if (ret < 0) {
+                               dev_err(&mhi_chan->mhi_dev->dev, "Failed to read channel\n");
+                               kfree(result.buf_addr);
+                               return ret;
+                       }
+
+                       result.dir = mhi_chan->dir;
+                       mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
+                       result.bytes_xferd = 0;
+                       memset(result.buf_addr, 0, len);
+
+                       /* Read until the ring becomes empty */
+               } while (!mhi_ep_queue_is_empty(mhi_chan->mhi_dev, DMA_TO_DEVICE));
+
+               kfree(result.buf_addr);
+       }
+
+       return 0;
+}
+
 static int mhi_ep_cache_host_cfg(struct mhi_ep_cntrl *mhi_cntrl)
 {
        size_t cmd_ctx_host_size, ch_ctx_host_size, ev_ctx_host_size;
        }
 }
 
+static void mhi_ep_ch_ring_worker(struct work_struct *work)
+{
+       struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, ch_ring_work);
+       struct device *dev = &mhi_cntrl->mhi_dev->dev;
+       struct mhi_ep_ring_item *itr, *tmp;
+       struct mhi_ring_element *el;
+       struct mhi_ep_ring *ring;
+       struct mhi_ep_chan *chan;
+       unsigned long flags;
+       LIST_HEAD(head);
+       int ret;
+
+       spin_lock_irqsave(&mhi_cntrl->list_lock, flags);
+       list_splice_tail_init(&mhi_cntrl->ch_db_list, &head);
+       spin_unlock_irqrestore(&mhi_cntrl->list_lock, flags);
+
+       /* Process each queued channel ring. In case of an error, just process next element. */
+       list_for_each_entry_safe(itr, tmp, &head, node) {
+               list_del(&itr->node);
+               ring = itr->ring;
+
+               /* 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");
+                       kfree(itr);
+                       continue;
+               }
+
+               /* Sanity check to make sure there are elements in the ring */
+               if (ring->rd_offset == ring->wr_offset) {
+                       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) {
+                       dev_err(dev, "Error processing ring for channel (%u): %d\n",
+                               ring->ch_id, ret);
+                       mutex_unlock(&chan->lock);
+                       kfree(itr);
+                       continue;
+               }
+
+               mutex_unlock(&chan->lock);
+               kfree(itr);
+       }
+}
+
 static void mhi_ep_state_worker(struct work_struct *work)
 {
        struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, state_work);
                spin_lock(&mhi_cntrl->list_lock);
                list_splice_tail_init(&head, &mhi_cntrl->ch_db_list);
                spin_unlock(&mhi_cntrl->list_lock);
+
+               queue_work(mhi_cntrl->wq, &mhi_cntrl->ch_ring_work);
        }
 }
 
        INIT_WORK(&mhi_cntrl->state_work, mhi_ep_state_worker);
        INIT_WORK(&mhi_cntrl->reset_work, mhi_ep_reset_worker);
        INIT_WORK(&mhi_cntrl->cmd_ring_work, mhi_ep_cmd_ring_worker);
+       INIT_WORK(&mhi_cntrl->ch_ring_work, mhi_ep_ch_ring_worker);
 
        mhi_cntrl->wq = alloc_workqueue("mhi_ep_wq", 0, 0);
        if (!mhi_cntrl->wq) {
 
  * @state_work: State transition worker
  * @reset_work: Worker for MHI Endpoint reset
  * @cmd_ring_work: Worker for processing command rings
+ * @ch_ring_work: Worker for processing channel rings
  * @raise_irq: CB function for raising IRQ to the host
  * @alloc_map: CB function for allocating memory in endpoint for storing host context and mapping it
  * @unmap_free: CB function to unmap and free the allocated memory in endpoint for storing host context
        struct work_struct state_work;
        struct work_struct reset_work;
        struct work_struct cmd_ring_work;
+       struct work_struct ch_ring_work;
 
        void (*raise_irq)(struct mhi_ep_cntrl *mhi_cntrl, u32 vector);
        int (*alloc_map)(struct mhi_ep_cntrl *mhi_cntrl, u64 pci_addr, phys_addr_t *phys_ptr,