18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/device.h>
88c2ecf20Sopenharmony_ci#include <linux/dma-direction.h>
98c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
108c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
118c2ecf20Sopenharmony_ci#include <linux/list.h>
128c2ecf20Sopenharmony_ci#include <linux/mhi.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include "internal.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ciint __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl,
198c2ecf20Sopenharmony_ci			      void __iomem *base, u32 offset, u32 *out)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	return mhi_cntrl->read_reg(mhi_cntrl, base + offset, out);
228c2ecf20Sopenharmony_ci}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciint __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
258c2ecf20Sopenharmony_ci				    void __iomem *base, u32 offset,
268c2ecf20Sopenharmony_ci				    u32 mask, u32 shift, u32 *out)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	u32 tmp;
298c2ecf20Sopenharmony_ci	int ret;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	ret = mhi_read_reg(mhi_cntrl, base, offset, &tmp);
328c2ecf20Sopenharmony_ci	if (ret)
338c2ecf20Sopenharmony_ci		return ret;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	*out = (tmp & mask) >> shift;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	return 0;
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_civoid mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
418c2ecf20Sopenharmony_ci		   u32 offset, u32 val)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	mhi_cntrl->write_reg(mhi_cntrl, base + offset, val);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_civoid mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base,
478c2ecf20Sopenharmony_ci			 u32 offset, u32 mask, u32 shift, u32 val)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	int ret;
508c2ecf20Sopenharmony_ci	u32 tmp;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	ret = mhi_read_reg(mhi_cntrl, base, offset, &tmp);
538c2ecf20Sopenharmony_ci	if (ret)
548c2ecf20Sopenharmony_ci		return;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	tmp &= ~mask;
578c2ecf20Sopenharmony_ci	tmp |= (val << shift);
588c2ecf20Sopenharmony_ci	mhi_write_reg(mhi_cntrl, base, offset, tmp);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_civoid mhi_write_db(struct mhi_controller *mhi_cntrl, void __iomem *db_addr,
628c2ecf20Sopenharmony_ci		  dma_addr_t db_val)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	mhi_write_reg(mhi_cntrl, db_addr, 4, upper_32_bits(db_val));
658c2ecf20Sopenharmony_ci	mhi_write_reg(mhi_cntrl, db_addr, 0, lower_32_bits(db_val));
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_civoid mhi_db_brstmode(struct mhi_controller *mhi_cntrl,
698c2ecf20Sopenharmony_ci		     struct db_cfg *db_cfg,
708c2ecf20Sopenharmony_ci		     void __iomem *db_addr,
718c2ecf20Sopenharmony_ci		     dma_addr_t db_val)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	if (db_cfg->db_mode) {
748c2ecf20Sopenharmony_ci		db_cfg->db_val = db_val;
758c2ecf20Sopenharmony_ci		mhi_write_db(mhi_cntrl, db_addr, db_val);
768c2ecf20Sopenharmony_ci		db_cfg->db_mode = 0;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_civoid mhi_db_brstmode_disable(struct mhi_controller *mhi_cntrl,
818c2ecf20Sopenharmony_ci			     struct db_cfg *db_cfg,
828c2ecf20Sopenharmony_ci			     void __iomem *db_addr,
838c2ecf20Sopenharmony_ci			     dma_addr_t db_val)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	db_cfg->db_val = db_val;
868c2ecf20Sopenharmony_ci	mhi_write_db(mhi_cntrl, db_addr, db_val);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_civoid mhi_ring_er_db(struct mhi_event *mhi_event)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct mhi_ring *ring = &mhi_event->ring;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	mhi_event->db_cfg.process_db(mhi_event->mhi_cntrl, &mhi_event->db_cfg,
948c2ecf20Sopenharmony_ci				     ring->db_addr, *ring->ctxt_wp);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_civoid mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	dma_addr_t db;
1008c2ecf20Sopenharmony_ci	struct mhi_ring *ring = &mhi_cmd->ring;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	db = ring->iommu_base + (ring->wp - ring->base);
1038c2ecf20Sopenharmony_ci	*ring->ctxt_wp = db;
1048c2ecf20Sopenharmony_ci	mhi_write_db(mhi_cntrl, ring->db_addr, db);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_civoid mhi_ring_chan_db(struct mhi_controller *mhi_cntrl,
1088c2ecf20Sopenharmony_ci		      struct mhi_chan *mhi_chan)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct mhi_ring *ring = &mhi_chan->tre_ring;
1118c2ecf20Sopenharmony_ci	dma_addr_t db;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	db = ring->iommu_base + (ring->wp - ring->base);
1148c2ecf20Sopenharmony_ci	*ring->ctxt_wp = db;
1158c2ecf20Sopenharmony_ci	mhi_chan->db_cfg.process_db(mhi_cntrl, &mhi_chan->db_cfg,
1168c2ecf20Sopenharmony_ci				    ring->db_addr, db);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cienum mhi_ee_type mhi_get_exec_env(struct mhi_controller *mhi_cntrl)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	u32 exec;
1228c2ecf20Sopenharmony_ci	int ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_EXECENV, &exec);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return (ret) ? MHI_EE_MAX : exec;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cienum mhi_state mhi_get_mhi_state(struct mhi_controller *mhi_cntrl)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	u32 state;
1308c2ecf20Sopenharmony_ci	int ret = mhi_read_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS,
1318c2ecf20Sopenharmony_ci				     MHISTATUS_MHISTATE_MASK,
1328c2ecf20Sopenharmony_ci				     MHISTATUS_MHISTATE_SHIFT, &state);
1338c2ecf20Sopenharmony_ci	return ret ? MHI_STATE_MAX : state;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ciint mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl,
1378c2ecf20Sopenharmony_ci			 struct mhi_buf_info *buf_info)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	buf_info->p_addr = dma_map_single(mhi_cntrl->cntrl_dev,
1408c2ecf20Sopenharmony_ci					  buf_info->v_addr, buf_info->len,
1418c2ecf20Sopenharmony_ci					  buf_info->dir);
1428c2ecf20Sopenharmony_ci	if (dma_mapping_error(mhi_cntrl->cntrl_dev, buf_info->p_addr))
1438c2ecf20Sopenharmony_ci		return -ENOMEM;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return 0;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ciint mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl,
1498c2ecf20Sopenharmony_ci			  struct mhi_buf_info *buf_info)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	void *buf = mhi_alloc_coherent(mhi_cntrl, buf_info->len,
1528c2ecf20Sopenharmony_ci				       &buf_info->p_addr, GFP_ATOMIC);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (!buf)
1558c2ecf20Sopenharmony_ci		return -ENOMEM;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (buf_info->dir == DMA_TO_DEVICE)
1588c2ecf20Sopenharmony_ci		memcpy(buf, buf_info->v_addr, buf_info->len);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	buf_info->bb_addr = buf;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return 0;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_civoid mhi_unmap_single_no_bb(struct mhi_controller *mhi_cntrl,
1668c2ecf20Sopenharmony_ci			    struct mhi_buf_info *buf_info)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	dma_unmap_single(mhi_cntrl->cntrl_dev, buf_info->p_addr, buf_info->len,
1698c2ecf20Sopenharmony_ci			 buf_info->dir);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_civoid mhi_unmap_single_use_bb(struct mhi_controller *mhi_cntrl,
1738c2ecf20Sopenharmony_ci			     struct mhi_buf_info *buf_info)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	if (buf_info->dir == DMA_FROM_DEVICE)
1768c2ecf20Sopenharmony_ci		memcpy(buf_info->v_addr, buf_info->bb_addr, buf_info->len);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	mhi_free_coherent(mhi_cntrl, buf_info->len, buf_info->bb_addr,
1798c2ecf20Sopenharmony_ci			  buf_info->p_addr);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic int get_nr_avail_ring_elements(struct mhi_controller *mhi_cntrl,
1838c2ecf20Sopenharmony_ci				      struct mhi_ring *ring)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	int nr_el;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (ring->wp < ring->rp) {
1888c2ecf20Sopenharmony_ci		nr_el = ((ring->rp - ring->wp) / ring->el_size) - 1;
1898c2ecf20Sopenharmony_ci	} else {
1908c2ecf20Sopenharmony_ci		nr_el = (ring->rp - ring->base) / ring->el_size;
1918c2ecf20Sopenharmony_ci		nr_el += ((ring->base + ring->len - ring->wp) /
1928c2ecf20Sopenharmony_ci			  ring->el_size) - 1;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	return nr_el;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic void *mhi_to_virtual(struct mhi_ring *ring, dma_addr_t addr)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	return (addr - ring->iommu_base) + ring->base;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic void mhi_add_ring_element(struct mhi_controller *mhi_cntrl,
2048c2ecf20Sopenharmony_ci				 struct mhi_ring *ring)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	ring->wp += ring->el_size;
2078c2ecf20Sopenharmony_ci	if (ring->wp >= (ring->base + ring->len))
2088c2ecf20Sopenharmony_ci		ring->wp = ring->base;
2098c2ecf20Sopenharmony_ci	/* smp update */
2108c2ecf20Sopenharmony_ci	smp_wmb();
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic void mhi_del_ring_element(struct mhi_controller *mhi_cntrl,
2148c2ecf20Sopenharmony_ci				 struct mhi_ring *ring)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	ring->rp += ring->el_size;
2178c2ecf20Sopenharmony_ci	if (ring->rp >= (ring->base + ring->len))
2188c2ecf20Sopenharmony_ci		ring->rp = ring->base;
2198c2ecf20Sopenharmony_ci	/* smp update */
2208c2ecf20Sopenharmony_ci	smp_wmb();
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic bool is_valid_ring_ptr(struct mhi_ring *ring, dma_addr_t addr)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	return addr >= ring->iommu_base && addr < ring->iommu_base + ring->len;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ciint mhi_destroy_device(struct device *dev, void *data)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct mhi_chan *ul_chan, *dl_chan;
2318c2ecf20Sopenharmony_ci	struct mhi_device *mhi_dev;
2328c2ecf20Sopenharmony_ci	struct mhi_controller *mhi_cntrl;
2338c2ecf20Sopenharmony_ci	enum mhi_ee_type ee = MHI_EE_MAX;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (dev->bus != &mhi_bus_type)
2368c2ecf20Sopenharmony_ci		return 0;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	mhi_dev = to_mhi_device(dev);
2398c2ecf20Sopenharmony_ci	mhi_cntrl = mhi_dev->mhi_cntrl;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	/* Only destroy virtual devices thats attached to bus */
2428c2ecf20Sopenharmony_ci	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
2438c2ecf20Sopenharmony_ci		return 0;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	ul_chan = mhi_dev->ul_chan;
2468c2ecf20Sopenharmony_ci	dl_chan = mhi_dev->dl_chan;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/*
2498c2ecf20Sopenharmony_ci	 * If execution environment is specified, remove only those devices that
2508c2ecf20Sopenharmony_ci	 * started in them based on ee_mask for the channels as we move on to a
2518c2ecf20Sopenharmony_ci	 * different execution environment
2528c2ecf20Sopenharmony_ci	 */
2538c2ecf20Sopenharmony_ci	if (data)
2548c2ecf20Sopenharmony_ci		ee = *(enum mhi_ee_type *)data;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	/*
2578c2ecf20Sopenharmony_ci	 * For the suspend and resume case, this function will get called
2588c2ecf20Sopenharmony_ci	 * without mhi_unregister_controller(). Hence, we need to drop the
2598c2ecf20Sopenharmony_ci	 * references to mhi_dev created for ul and dl channels. We can
2608c2ecf20Sopenharmony_ci	 * be sure that there will be no instances of mhi_dev left after
2618c2ecf20Sopenharmony_ci	 * this.
2628c2ecf20Sopenharmony_ci	 */
2638c2ecf20Sopenharmony_ci	if (ul_chan) {
2648c2ecf20Sopenharmony_ci		if (ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
2658c2ecf20Sopenharmony_ci			return 0;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci		put_device(&ul_chan->mhi_dev->dev);
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (dl_chan) {
2718c2ecf20Sopenharmony_ci		if (ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
2728c2ecf20Sopenharmony_ci			return 0;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		put_device(&dl_chan->mhi_dev->dev);
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	dev_dbg(&mhi_cntrl->mhi_dev->dev, "destroy device for chan:%s\n",
2788c2ecf20Sopenharmony_ci		 mhi_dev->name);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/* Notify the client and remove the device from MHI bus */
2818c2ecf20Sopenharmony_ci	device_del(dev);
2828c2ecf20Sopenharmony_ci	put_device(dev);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	return 0;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_civoid mhi_notify(struct mhi_device *mhi_dev, enum mhi_callback cb_reason)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct mhi_driver *mhi_drv;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (!mhi_dev->dev.driver)
2928c2ecf20Sopenharmony_ci		return;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	mhi_drv = to_mhi_driver(mhi_dev->dev.driver);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (mhi_drv->status_cb)
2978c2ecf20Sopenharmony_ci		mhi_drv->status_cb(mhi_dev, cb_reason);
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_notify);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci/* Bind MHI channels to MHI devices */
3028c2ecf20Sopenharmony_civoid mhi_create_devices(struct mhi_controller *mhi_cntrl)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct mhi_chan *mhi_chan;
3058c2ecf20Sopenharmony_ci	struct mhi_device *mhi_dev;
3068c2ecf20Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
3078c2ecf20Sopenharmony_ci	int i, ret;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	mhi_chan = mhi_cntrl->mhi_chan;
3108c2ecf20Sopenharmony_ci	for (i = 0; i < mhi_cntrl->max_chan; i++, mhi_chan++) {
3118c2ecf20Sopenharmony_ci		if (!mhi_chan->configured || mhi_chan->mhi_dev ||
3128c2ecf20Sopenharmony_ci		    !(mhi_chan->ee_mask & BIT(mhi_cntrl->ee)))
3138c2ecf20Sopenharmony_ci			continue;
3148c2ecf20Sopenharmony_ci		mhi_dev = mhi_alloc_device(mhi_cntrl);
3158c2ecf20Sopenharmony_ci		if (IS_ERR(mhi_dev))
3168c2ecf20Sopenharmony_ci			return;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		mhi_dev->dev_type = MHI_DEVICE_XFER;
3198c2ecf20Sopenharmony_ci		switch (mhi_chan->dir) {
3208c2ecf20Sopenharmony_ci		case DMA_TO_DEVICE:
3218c2ecf20Sopenharmony_ci			mhi_dev->ul_chan = mhi_chan;
3228c2ecf20Sopenharmony_ci			mhi_dev->ul_chan_id = mhi_chan->chan;
3238c2ecf20Sopenharmony_ci			break;
3248c2ecf20Sopenharmony_ci		case DMA_FROM_DEVICE:
3258c2ecf20Sopenharmony_ci			/* We use dl_chan as offload channels */
3268c2ecf20Sopenharmony_ci			mhi_dev->dl_chan = mhi_chan;
3278c2ecf20Sopenharmony_ci			mhi_dev->dl_chan_id = mhi_chan->chan;
3288c2ecf20Sopenharmony_ci			break;
3298c2ecf20Sopenharmony_ci		default:
3308c2ecf20Sopenharmony_ci			dev_err(dev, "Direction not supported\n");
3318c2ecf20Sopenharmony_ci			put_device(&mhi_dev->dev);
3328c2ecf20Sopenharmony_ci			return;
3338c2ecf20Sopenharmony_ci		}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci		get_device(&mhi_dev->dev);
3368c2ecf20Sopenharmony_ci		mhi_chan->mhi_dev = mhi_dev;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci		/* Check next channel if it matches */
3398c2ecf20Sopenharmony_ci		if ((i + 1) < mhi_cntrl->max_chan && mhi_chan[1].configured) {
3408c2ecf20Sopenharmony_ci			if (!strcmp(mhi_chan[1].name, mhi_chan->name)) {
3418c2ecf20Sopenharmony_ci				i++;
3428c2ecf20Sopenharmony_ci				mhi_chan++;
3438c2ecf20Sopenharmony_ci				if (mhi_chan->dir == DMA_TO_DEVICE) {
3448c2ecf20Sopenharmony_ci					mhi_dev->ul_chan = mhi_chan;
3458c2ecf20Sopenharmony_ci					mhi_dev->ul_chan_id = mhi_chan->chan;
3468c2ecf20Sopenharmony_ci				} else {
3478c2ecf20Sopenharmony_ci					mhi_dev->dl_chan = mhi_chan;
3488c2ecf20Sopenharmony_ci					mhi_dev->dl_chan_id = mhi_chan->chan;
3498c2ecf20Sopenharmony_ci				}
3508c2ecf20Sopenharmony_ci				get_device(&mhi_dev->dev);
3518c2ecf20Sopenharmony_ci				mhi_chan->mhi_dev = mhi_dev;
3528c2ecf20Sopenharmony_ci			}
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		/* Channel name is same for both UL and DL */
3568c2ecf20Sopenharmony_ci		mhi_dev->name = mhi_chan->name;
3578c2ecf20Sopenharmony_ci		dev_set_name(&mhi_dev->dev, "%s_%s",
3588c2ecf20Sopenharmony_ci			     dev_name(mhi_cntrl->cntrl_dev),
3598c2ecf20Sopenharmony_ci			     mhi_dev->name);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		/* Init wakeup source if available */
3628c2ecf20Sopenharmony_ci		if (mhi_dev->dl_chan && mhi_dev->dl_chan->wake_capable)
3638c2ecf20Sopenharmony_ci			device_init_wakeup(&mhi_dev->dev, true);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		ret = device_add(&mhi_dev->dev);
3668c2ecf20Sopenharmony_ci		if (ret)
3678c2ecf20Sopenharmony_ci			put_device(&mhi_dev->dev);
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ciirqreturn_t mhi_irq_handler(int irq_number, void *dev)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct mhi_event *mhi_event = dev;
3748c2ecf20Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl;
3758c2ecf20Sopenharmony_ci	struct mhi_event_ctxt *er_ctxt =
3768c2ecf20Sopenharmony_ci		&mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index];
3778c2ecf20Sopenharmony_ci	struct mhi_ring *ev_ring = &mhi_event->ring;
3788c2ecf20Sopenharmony_ci	dma_addr_t ptr = er_ctxt->rp;
3798c2ecf20Sopenharmony_ci	void *dev_rp;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (!is_valid_ring_ptr(ev_ring, ptr)) {
3828c2ecf20Sopenharmony_ci		dev_err(&mhi_cntrl->mhi_dev->dev,
3838c2ecf20Sopenharmony_ci			"Event ring rp points outside of the event ring\n");
3848c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	dev_rp = mhi_to_virtual(ev_ring, ptr);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* Only proceed if event ring has pending events */
3908c2ecf20Sopenharmony_ci	if (ev_ring->rp == dev_rp)
3918c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	/* For client managed event ring, notify pending data */
3948c2ecf20Sopenharmony_ci	if (mhi_event->cl_manage) {
3958c2ecf20Sopenharmony_ci		struct mhi_chan *mhi_chan = mhi_event->mhi_chan;
3968c2ecf20Sopenharmony_ci		struct mhi_device *mhi_dev = mhi_chan->mhi_dev;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci		if (mhi_dev)
3998c2ecf20Sopenharmony_ci			mhi_notify(mhi_dev, MHI_CB_PENDING_DATA);
4008c2ecf20Sopenharmony_ci	} else {
4018c2ecf20Sopenharmony_ci		tasklet_schedule(&mhi_event->task);
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ciirqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	struct mhi_controller *mhi_cntrl = priv;
4108c2ecf20Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
4118c2ecf20Sopenharmony_ci	enum mhi_state state = MHI_STATE_MAX;
4128c2ecf20Sopenharmony_ci	enum mhi_pm_state pm_state = 0;
4138c2ecf20Sopenharmony_ci	enum mhi_ee_type ee = 0;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	write_lock_irq(&mhi_cntrl->pm_lock);
4168c2ecf20Sopenharmony_ci	if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
4178c2ecf20Sopenharmony_ci		write_unlock_irq(&mhi_cntrl->pm_lock);
4188c2ecf20Sopenharmony_ci		goto exit_intvec;
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	state = mhi_get_mhi_state(mhi_cntrl);
4228c2ecf20Sopenharmony_ci	ee = mhi_cntrl->ee;
4238c2ecf20Sopenharmony_ci	mhi_cntrl->ee = mhi_get_exec_env(mhi_cntrl);
4248c2ecf20Sopenharmony_ci	dev_dbg(dev, "local ee:%s device ee:%s dev_state:%s\n",
4258c2ecf20Sopenharmony_ci		TO_MHI_EXEC_STR(mhi_cntrl->ee), TO_MHI_EXEC_STR(ee),
4268c2ecf20Sopenharmony_ci		TO_MHI_STATE_STR(state));
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (state == MHI_STATE_SYS_ERR) {
4298c2ecf20Sopenharmony_ci		dev_dbg(dev, "System error detected\n");
4308c2ecf20Sopenharmony_ci		pm_state = mhi_tryset_pm_state(mhi_cntrl,
4318c2ecf20Sopenharmony_ci					       MHI_PM_SYS_ERR_DETECT);
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci	write_unlock_irq(&mhi_cntrl->pm_lock);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	 /* If device supports RDDM don't bother processing SYS error */
4368c2ecf20Sopenharmony_ci	if (mhi_cntrl->rddm_image) {
4378c2ecf20Sopenharmony_ci		if (mhi_cntrl->ee == MHI_EE_RDDM && mhi_cntrl->ee != ee) {
4388c2ecf20Sopenharmony_ci			mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_RDDM);
4398c2ecf20Sopenharmony_ci			wake_up_all(&mhi_cntrl->state_event);
4408c2ecf20Sopenharmony_ci		}
4418c2ecf20Sopenharmony_ci		goto exit_intvec;
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (pm_state == MHI_PM_SYS_ERR_DETECT) {
4458c2ecf20Sopenharmony_ci		wake_up_all(&mhi_cntrl->state_event);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci		/* For fatal errors, we let controller decide next step */
4488c2ecf20Sopenharmony_ci		if (MHI_IN_PBL(ee))
4498c2ecf20Sopenharmony_ci			mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_FATAL_ERROR);
4508c2ecf20Sopenharmony_ci		else
4518c2ecf20Sopenharmony_ci			mhi_pm_sys_err_handler(mhi_cntrl);
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ciexit_intvec:
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ciirqreturn_t mhi_intvec_handler(int irq_number, void *dev)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	struct mhi_controller *mhi_cntrl = dev;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	/* Wake up events waiting for state change */
4648c2ecf20Sopenharmony_ci	wake_up_all(&mhi_cntrl->state_event);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	return IRQ_WAKE_THREAD;
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic void mhi_recycle_ev_ring_element(struct mhi_controller *mhi_cntrl,
4708c2ecf20Sopenharmony_ci					struct mhi_ring *ring)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	dma_addr_t ctxt_wp;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* Update the WP */
4758c2ecf20Sopenharmony_ci	ring->wp += ring->el_size;
4768c2ecf20Sopenharmony_ci	ctxt_wp = *ring->ctxt_wp + ring->el_size;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	if (ring->wp >= (ring->base + ring->len)) {
4798c2ecf20Sopenharmony_ci		ring->wp = ring->base;
4808c2ecf20Sopenharmony_ci		ctxt_wp = ring->iommu_base;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	*ring->ctxt_wp = ctxt_wp;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	/* Update the RP */
4868c2ecf20Sopenharmony_ci	ring->rp += ring->el_size;
4878c2ecf20Sopenharmony_ci	if (ring->rp >= (ring->base + ring->len))
4888c2ecf20Sopenharmony_ci		ring->rp = ring->base;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	/* Update to all cores */
4918c2ecf20Sopenharmony_ci	smp_wmb();
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cistatic int parse_xfer_event(struct mhi_controller *mhi_cntrl,
4958c2ecf20Sopenharmony_ci			    struct mhi_tre *event,
4968c2ecf20Sopenharmony_ci			    struct mhi_chan *mhi_chan)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	struct mhi_ring *buf_ring, *tre_ring;
4998c2ecf20Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
5008c2ecf20Sopenharmony_ci	struct mhi_result result;
5018c2ecf20Sopenharmony_ci	unsigned long flags = 0;
5028c2ecf20Sopenharmony_ci	u32 ev_code;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	ev_code = MHI_TRE_GET_EV_CODE(event);
5058c2ecf20Sopenharmony_ci	buf_ring = &mhi_chan->buf_ring;
5068c2ecf20Sopenharmony_ci	tre_ring = &mhi_chan->tre_ring;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	result.transaction_status = (ev_code == MHI_EV_CC_OVERFLOW) ?
5098c2ecf20Sopenharmony_ci		-EOVERFLOW : 0;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	/*
5128c2ecf20Sopenharmony_ci	 * If it's a DB Event then we need to grab the lock
5138c2ecf20Sopenharmony_ci	 * with preemption disabled and as a write because we
5148c2ecf20Sopenharmony_ci	 * have to update db register and there are chances that
5158c2ecf20Sopenharmony_ci	 * another thread could be doing the same.
5168c2ecf20Sopenharmony_ci	 */
5178c2ecf20Sopenharmony_ci	if (ev_code >= MHI_EV_CC_OOB)
5188c2ecf20Sopenharmony_ci		write_lock_irqsave(&mhi_chan->lock, flags);
5198c2ecf20Sopenharmony_ci	else
5208c2ecf20Sopenharmony_ci		read_lock_bh(&mhi_chan->lock);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED)
5238c2ecf20Sopenharmony_ci		goto end_process_tx_event;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	switch (ev_code) {
5268c2ecf20Sopenharmony_ci	case MHI_EV_CC_OVERFLOW:
5278c2ecf20Sopenharmony_ci	case MHI_EV_CC_EOB:
5288c2ecf20Sopenharmony_ci	case MHI_EV_CC_EOT:
5298c2ecf20Sopenharmony_ci	{
5308c2ecf20Sopenharmony_ci		dma_addr_t ptr = MHI_TRE_GET_EV_PTR(event);
5318c2ecf20Sopenharmony_ci		struct mhi_tre *local_rp, *ev_tre;
5328c2ecf20Sopenharmony_ci		void *dev_rp;
5338c2ecf20Sopenharmony_ci		struct mhi_buf_info *buf_info;
5348c2ecf20Sopenharmony_ci		u16 xfer_len;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci		if (!is_valid_ring_ptr(tre_ring, ptr)) {
5378c2ecf20Sopenharmony_ci			dev_err(&mhi_cntrl->mhi_dev->dev,
5388c2ecf20Sopenharmony_ci				"Event element points outside of the tre ring\n");
5398c2ecf20Sopenharmony_ci			break;
5408c2ecf20Sopenharmony_ci		}
5418c2ecf20Sopenharmony_ci		/* Get the TRB this event points to */
5428c2ecf20Sopenharmony_ci		ev_tre = mhi_to_virtual(tre_ring, ptr);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci		dev_rp = ev_tre + 1;
5458c2ecf20Sopenharmony_ci		if (dev_rp >= (tre_ring->base + tre_ring->len))
5468c2ecf20Sopenharmony_ci			dev_rp = tre_ring->base;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci		result.dir = mhi_chan->dir;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		local_rp = tre_ring->rp;
5518c2ecf20Sopenharmony_ci		while (local_rp != dev_rp) {
5528c2ecf20Sopenharmony_ci			buf_info = buf_ring->rp;
5538c2ecf20Sopenharmony_ci			/* If it's the last TRE, get length from the event */
5548c2ecf20Sopenharmony_ci			if (local_rp == ev_tre)
5558c2ecf20Sopenharmony_ci				xfer_len = MHI_TRE_GET_EV_LEN(event);
5568c2ecf20Sopenharmony_ci			else
5578c2ecf20Sopenharmony_ci				xfer_len = buf_info->len;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci			/* Unmap if it's not pre-mapped by client */
5608c2ecf20Sopenharmony_ci			if (likely(!buf_info->pre_mapped))
5618c2ecf20Sopenharmony_ci				mhi_cntrl->unmap_single(mhi_cntrl, buf_info);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci			result.buf_addr = buf_info->cb_buf;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci			/* truncate to buf len if xfer_len is larger */
5668c2ecf20Sopenharmony_ci			result.bytes_xferd =
5678c2ecf20Sopenharmony_ci				min_t(u16, xfer_len, buf_info->len);
5688c2ecf20Sopenharmony_ci			mhi_del_ring_element(mhi_cntrl, buf_ring);
5698c2ecf20Sopenharmony_ci			mhi_del_ring_element(mhi_cntrl, tre_ring);
5708c2ecf20Sopenharmony_ci			local_rp = tre_ring->rp;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci			read_unlock_bh(&mhi_chan->lock);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci			/* notify client */
5758c2ecf20Sopenharmony_ci			mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci			if (mhi_chan->dir == DMA_TO_DEVICE)
5788c2ecf20Sopenharmony_ci				atomic_dec(&mhi_cntrl->pending_pkts);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci			/*
5818c2ecf20Sopenharmony_ci			 * Recycle the buffer if buffer is pre-allocated,
5828c2ecf20Sopenharmony_ci			 * if there is an error, not much we can do apart
5838c2ecf20Sopenharmony_ci			 * from dropping the packet
5848c2ecf20Sopenharmony_ci			 */
5858c2ecf20Sopenharmony_ci			if (mhi_chan->pre_alloc) {
5868c2ecf20Sopenharmony_ci				if (mhi_queue_buf(mhi_chan->mhi_dev,
5878c2ecf20Sopenharmony_ci						  mhi_chan->dir,
5888c2ecf20Sopenharmony_ci						  buf_info->cb_buf,
5898c2ecf20Sopenharmony_ci						  buf_info->len, MHI_EOT)) {
5908c2ecf20Sopenharmony_ci					dev_err(dev,
5918c2ecf20Sopenharmony_ci						"Error recycling buffer for chan:%d\n",
5928c2ecf20Sopenharmony_ci						mhi_chan->chan);
5938c2ecf20Sopenharmony_ci					kfree(buf_info->cb_buf);
5948c2ecf20Sopenharmony_ci				}
5958c2ecf20Sopenharmony_ci			}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci			read_lock_bh(&mhi_chan->lock);
5988c2ecf20Sopenharmony_ci		}
5998c2ecf20Sopenharmony_ci		break;
6008c2ecf20Sopenharmony_ci	} /* CC_EOT */
6018c2ecf20Sopenharmony_ci	case MHI_EV_CC_OOB:
6028c2ecf20Sopenharmony_ci	case MHI_EV_CC_DB_MODE:
6038c2ecf20Sopenharmony_ci	{
6048c2ecf20Sopenharmony_ci		unsigned long flags;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci		mhi_chan->db_cfg.db_mode = 1;
6078c2ecf20Sopenharmony_ci		read_lock_irqsave(&mhi_cntrl->pm_lock, flags);
6088c2ecf20Sopenharmony_ci		if (tre_ring->wp != tre_ring->rp &&
6098c2ecf20Sopenharmony_ci		    MHI_DB_ACCESS_VALID(mhi_cntrl)) {
6108c2ecf20Sopenharmony_ci			mhi_ring_chan_db(mhi_cntrl, mhi_chan);
6118c2ecf20Sopenharmony_ci		}
6128c2ecf20Sopenharmony_ci		read_unlock_irqrestore(&mhi_cntrl->pm_lock, flags);
6138c2ecf20Sopenharmony_ci		break;
6148c2ecf20Sopenharmony_ci	}
6158c2ecf20Sopenharmony_ci	case MHI_EV_CC_BAD_TRE:
6168c2ecf20Sopenharmony_ci	default:
6178c2ecf20Sopenharmony_ci		dev_err(dev, "Unknown event 0x%x\n", ev_code);
6188c2ecf20Sopenharmony_ci		break;
6198c2ecf20Sopenharmony_ci	} /* switch(MHI_EV_READ_CODE(EV_TRB_CODE,event)) */
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ciend_process_tx_event:
6228c2ecf20Sopenharmony_ci	if (ev_code >= MHI_EV_CC_OOB)
6238c2ecf20Sopenharmony_ci		write_unlock_irqrestore(&mhi_chan->lock, flags);
6248c2ecf20Sopenharmony_ci	else
6258c2ecf20Sopenharmony_ci		read_unlock_bh(&mhi_chan->lock);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	return 0;
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic int parse_rsc_event(struct mhi_controller *mhi_cntrl,
6318c2ecf20Sopenharmony_ci			   struct mhi_tre *event,
6328c2ecf20Sopenharmony_ci			   struct mhi_chan *mhi_chan)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	struct mhi_ring *buf_ring, *tre_ring;
6358c2ecf20Sopenharmony_ci	struct mhi_buf_info *buf_info;
6368c2ecf20Sopenharmony_ci	struct mhi_result result;
6378c2ecf20Sopenharmony_ci	int ev_code;
6388c2ecf20Sopenharmony_ci	u32 cookie; /* offset to local descriptor */
6398c2ecf20Sopenharmony_ci	u16 xfer_len;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	buf_ring = &mhi_chan->buf_ring;
6428c2ecf20Sopenharmony_ci	tre_ring = &mhi_chan->tre_ring;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	ev_code = MHI_TRE_GET_EV_CODE(event);
6458c2ecf20Sopenharmony_ci	cookie = MHI_TRE_GET_EV_COOKIE(event);
6468c2ecf20Sopenharmony_ci	xfer_len = MHI_TRE_GET_EV_LEN(event);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	/* Received out of bound cookie */
6498c2ecf20Sopenharmony_ci	WARN_ON(cookie >= buf_ring->len);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	buf_info = buf_ring->base + cookie;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	result.transaction_status = (ev_code == MHI_EV_CC_OVERFLOW) ?
6548c2ecf20Sopenharmony_ci		-EOVERFLOW : 0;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	/* truncate to buf len if xfer_len is larger */
6578c2ecf20Sopenharmony_ci	result.bytes_xferd = min_t(u16, xfer_len, buf_info->len);
6588c2ecf20Sopenharmony_ci	result.buf_addr = buf_info->cb_buf;
6598c2ecf20Sopenharmony_ci	result.dir = mhi_chan->dir;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	read_lock_bh(&mhi_chan->lock);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED)
6648c2ecf20Sopenharmony_ci		goto end_process_rsc_event;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	WARN_ON(!buf_info->used);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	/* notify the client */
6698c2ecf20Sopenharmony_ci	mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	/*
6728c2ecf20Sopenharmony_ci	 * Note: We're arbitrarily incrementing RP even though, completion
6738c2ecf20Sopenharmony_ci	 * packet we processed might not be the same one, reason we can do this
6748c2ecf20Sopenharmony_ci	 * is because device guaranteed to cache descriptors in order it
6758c2ecf20Sopenharmony_ci	 * receive, so even though completion event is different we can re-use
6768c2ecf20Sopenharmony_ci	 * all descriptors in between.
6778c2ecf20Sopenharmony_ci	 * Example:
6788c2ecf20Sopenharmony_ci	 * Transfer Ring has descriptors: A, B, C, D
6798c2ecf20Sopenharmony_ci	 * Last descriptor host queue is D (WP) and first descriptor
6808c2ecf20Sopenharmony_ci	 * host queue is A (RP).
6818c2ecf20Sopenharmony_ci	 * The completion event we just serviced is descriptor C.
6828c2ecf20Sopenharmony_ci	 * Then we can safely queue descriptors to replace A, B, and C
6838c2ecf20Sopenharmony_ci	 * even though host did not receive any completions.
6848c2ecf20Sopenharmony_ci	 */
6858c2ecf20Sopenharmony_ci	mhi_del_ring_element(mhi_cntrl, tre_ring);
6868c2ecf20Sopenharmony_ci	buf_info->used = false;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ciend_process_rsc_event:
6898c2ecf20Sopenharmony_ci	read_unlock_bh(&mhi_chan->lock);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	return 0;
6928c2ecf20Sopenharmony_ci}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cistatic void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
6958c2ecf20Sopenharmony_ci				       struct mhi_tre *tre)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	dma_addr_t ptr = MHI_TRE_GET_EV_PTR(tre);
6988c2ecf20Sopenharmony_ci	struct mhi_cmd *cmd_ring = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING];
6998c2ecf20Sopenharmony_ci	struct mhi_ring *mhi_ring = &cmd_ring->ring;
7008c2ecf20Sopenharmony_ci	struct mhi_tre *cmd_pkt;
7018c2ecf20Sopenharmony_ci	struct mhi_chan *mhi_chan;
7028c2ecf20Sopenharmony_ci	u32 chan;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (!is_valid_ring_ptr(mhi_ring, ptr)) {
7058c2ecf20Sopenharmony_ci		dev_err(&mhi_cntrl->mhi_dev->dev,
7068c2ecf20Sopenharmony_ci			"Event element points outside of the cmd ring\n");
7078c2ecf20Sopenharmony_ci		return;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	cmd_pkt = mhi_to_virtual(mhi_ring, ptr);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	chan = MHI_TRE_GET_CMD_CHID(cmd_pkt);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	if (chan < mhi_cntrl->max_chan &&
7158c2ecf20Sopenharmony_ci	    mhi_cntrl->mhi_chan[chan].configured) {
7168c2ecf20Sopenharmony_ci		mhi_chan = &mhi_cntrl->mhi_chan[chan];
7178c2ecf20Sopenharmony_ci		write_lock_bh(&mhi_chan->lock);
7188c2ecf20Sopenharmony_ci		mhi_chan->ccs = MHI_TRE_GET_EV_CODE(tre);
7198c2ecf20Sopenharmony_ci		complete(&mhi_chan->completion);
7208c2ecf20Sopenharmony_ci		write_unlock_bh(&mhi_chan->lock);
7218c2ecf20Sopenharmony_ci	} else {
7228c2ecf20Sopenharmony_ci		dev_err(&mhi_cntrl->mhi_dev->dev,
7238c2ecf20Sopenharmony_ci			"Completion packet for invalid channel ID: %d\n", chan);
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	mhi_del_ring_element(mhi_cntrl, mhi_ring);
7278c2ecf20Sopenharmony_ci}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ciint mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
7308c2ecf20Sopenharmony_ci			     struct mhi_event *mhi_event,
7318c2ecf20Sopenharmony_ci			     u32 event_quota)
7328c2ecf20Sopenharmony_ci{
7338c2ecf20Sopenharmony_ci	struct mhi_tre *dev_rp, *local_rp;
7348c2ecf20Sopenharmony_ci	struct mhi_ring *ev_ring = &mhi_event->ring;
7358c2ecf20Sopenharmony_ci	struct mhi_event_ctxt *er_ctxt =
7368c2ecf20Sopenharmony_ci		&mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index];
7378c2ecf20Sopenharmony_ci	struct mhi_chan *mhi_chan;
7388c2ecf20Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
7398c2ecf20Sopenharmony_ci	u32 chan;
7408c2ecf20Sopenharmony_ci	int count = 0;
7418c2ecf20Sopenharmony_ci	dma_addr_t ptr = er_ctxt->rp;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	/*
7448c2ecf20Sopenharmony_ci	 * This is a quick check to avoid unnecessary event processing
7458c2ecf20Sopenharmony_ci	 * in case MHI is already in error state, but it's still possible
7468c2ecf20Sopenharmony_ci	 * to transition to error state while processing events
7478c2ecf20Sopenharmony_ci	 */
7488c2ecf20Sopenharmony_ci	if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state)))
7498c2ecf20Sopenharmony_ci		return -EIO;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	if (!is_valid_ring_ptr(ev_ring, ptr)) {
7528c2ecf20Sopenharmony_ci		dev_err(&mhi_cntrl->mhi_dev->dev,
7538c2ecf20Sopenharmony_ci			"Event ring rp points outside of the event ring\n");
7548c2ecf20Sopenharmony_ci		return -EIO;
7558c2ecf20Sopenharmony_ci	}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	dev_rp = mhi_to_virtual(ev_ring, ptr);
7588c2ecf20Sopenharmony_ci	local_rp = ev_ring->rp;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	while (dev_rp != local_rp) {
7618c2ecf20Sopenharmony_ci		enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci		switch (type) {
7648c2ecf20Sopenharmony_ci		case MHI_PKT_TYPE_BW_REQ_EVENT:
7658c2ecf20Sopenharmony_ci		{
7668c2ecf20Sopenharmony_ci			struct mhi_link_info *link_info;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci			link_info = &mhi_cntrl->mhi_link_info;
7698c2ecf20Sopenharmony_ci			write_lock_irq(&mhi_cntrl->pm_lock);
7708c2ecf20Sopenharmony_ci			link_info->target_link_speed =
7718c2ecf20Sopenharmony_ci				MHI_TRE_GET_EV_LINKSPEED(local_rp);
7728c2ecf20Sopenharmony_ci			link_info->target_link_width =
7738c2ecf20Sopenharmony_ci				MHI_TRE_GET_EV_LINKWIDTH(local_rp);
7748c2ecf20Sopenharmony_ci			write_unlock_irq(&mhi_cntrl->pm_lock);
7758c2ecf20Sopenharmony_ci			dev_dbg(dev, "Received BW_REQ event\n");
7768c2ecf20Sopenharmony_ci			mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_BW_REQ);
7778c2ecf20Sopenharmony_ci			break;
7788c2ecf20Sopenharmony_ci		}
7798c2ecf20Sopenharmony_ci		case MHI_PKT_TYPE_STATE_CHANGE_EVENT:
7808c2ecf20Sopenharmony_ci		{
7818c2ecf20Sopenharmony_ci			enum mhi_state new_state;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci			new_state = MHI_TRE_GET_EV_STATE(local_rp);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci			dev_dbg(dev, "State change event to state: %s\n",
7868c2ecf20Sopenharmony_ci				TO_MHI_STATE_STR(new_state));
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci			switch (new_state) {
7898c2ecf20Sopenharmony_ci			case MHI_STATE_M0:
7908c2ecf20Sopenharmony_ci				mhi_pm_m0_transition(mhi_cntrl);
7918c2ecf20Sopenharmony_ci				break;
7928c2ecf20Sopenharmony_ci			case MHI_STATE_M1:
7938c2ecf20Sopenharmony_ci				mhi_pm_m1_transition(mhi_cntrl);
7948c2ecf20Sopenharmony_ci				break;
7958c2ecf20Sopenharmony_ci			case MHI_STATE_M3:
7968c2ecf20Sopenharmony_ci				mhi_pm_m3_transition(mhi_cntrl);
7978c2ecf20Sopenharmony_ci				break;
7988c2ecf20Sopenharmony_ci			case MHI_STATE_SYS_ERR:
7998c2ecf20Sopenharmony_ci			{
8008c2ecf20Sopenharmony_ci				enum mhi_pm_state new_state;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci				/* skip SYS_ERROR handling if RDDM supported */
8038c2ecf20Sopenharmony_ci				if (mhi_cntrl->ee == MHI_EE_RDDM ||
8048c2ecf20Sopenharmony_ci				    mhi_cntrl->rddm_image)
8058c2ecf20Sopenharmony_ci					break;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci				dev_dbg(dev, "System error detected\n");
8088c2ecf20Sopenharmony_ci				write_lock_irq(&mhi_cntrl->pm_lock);
8098c2ecf20Sopenharmony_ci				new_state = mhi_tryset_pm_state(mhi_cntrl,
8108c2ecf20Sopenharmony_ci							MHI_PM_SYS_ERR_DETECT);
8118c2ecf20Sopenharmony_ci				write_unlock_irq(&mhi_cntrl->pm_lock);
8128c2ecf20Sopenharmony_ci				if (new_state == MHI_PM_SYS_ERR_DETECT)
8138c2ecf20Sopenharmony_ci					mhi_pm_sys_err_handler(mhi_cntrl);
8148c2ecf20Sopenharmony_ci				break;
8158c2ecf20Sopenharmony_ci			}
8168c2ecf20Sopenharmony_ci			default:
8178c2ecf20Sopenharmony_ci				dev_err(dev, "Invalid state: %s\n",
8188c2ecf20Sopenharmony_ci					TO_MHI_STATE_STR(new_state));
8198c2ecf20Sopenharmony_ci			}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci			break;
8228c2ecf20Sopenharmony_ci		}
8238c2ecf20Sopenharmony_ci		case MHI_PKT_TYPE_CMD_COMPLETION_EVENT:
8248c2ecf20Sopenharmony_ci			mhi_process_cmd_completion(mhi_cntrl, local_rp);
8258c2ecf20Sopenharmony_ci			break;
8268c2ecf20Sopenharmony_ci		case MHI_PKT_TYPE_EE_EVENT:
8278c2ecf20Sopenharmony_ci		{
8288c2ecf20Sopenharmony_ci			enum dev_st_transition st = DEV_ST_TRANSITION_MAX;
8298c2ecf20Sopenharmony_ci			enum mhi_ee_type event = MHI_TRE_GET_EV_EXECENV(local_rp);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci			dev_dbg(dev, "Received EE event: %s\n",
8328c2ecf20Sopenharmony_ci				TO_MHI_EXEC_STR(event));
8338c2ecf20Sopenharmony_ci			switch (event) {
8348c2ecf20Sopenharmony_ci			case MHI_EE_SBL:
8358c2ecf20Sopenharmony_ci				st = DEV_ST_TRANSITION_SBL;
8368c2ecf20Sopenharmony_ci				break;
8378c2ecf20Sopenharmony_ci			case MHI_EE_WFW:
8388c2ecf20Sopenharmony_ci			case MHI_EE_AMSS:
8398c2ecf20Sopenharmony_ci				st = DEV_ST_TRANSITION_MISSION_MODE;
8408c2ecf20Sopenharmony_ci				break;
8418c2ecf20Sopenharmony_ci			case MHI_EE_RDDM:
8428c2ecf20Sopenharmony_ci				mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_RDDM);
8438c2ecf20Sopenharmony_ci				write_lock_irq(&mhi_cntrl->pm_lock);
8448c2ecf20Sopenharmony_ci				mhi_cntrl->ee = event;
8458c2ecf20Sopenharmony_ci				write_unlock_irq(&mhi_cntrl->pm_lock);
8468c2ecf20Sopenharmony_ci				wake_up_all(&mhi_cntrl->state_event);
8478c2ecf20Sopenharmony_ci				break;
8488c2ecf20Sopenharmony_ci			default:
8498c2ecf20Sopenharmony_ci				dev_err(dev,
8508c2ecf20Sopenharmony_ci					"Unhandled EE event: 0x%x\n", type);
8518c2ecf20Sopenharmony_ci			}
8528c2ecf20Sopenharmony_ci			if (st != DEV_ST_TRANSITION_MAX)
8538c2ecf20Sopenharmony_ci				mhi_queue_state_transition(mhi_cntrl, st);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci			break;
8568c2ecf20Sopenharmony_ci		}
8578c2ecf20Sopenharmony_ci		case MHI_PKT_TYPE_TX_EVENT:
8588c2ecf20Sopenharmony_ci			chan = MHI_TRE_GET_EV_CHID(local_rp);
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci			WARN_ON(chan >= mhi_cntrl->max_chan);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci			/*
8638c2ecf20Sopenharmony_ci			 * Only process the event ring elements whose channel
8648c2ecf20Sopenharmony_ci			 * ID is within the maximum supported range.
8658c2ecf20Sopenharmony_ci			 */
8668c2ecf20Sopenharmony_ci			if (chan < mhi_cntrl->max_chan) {
8678c2ecf20Sopenharmony_ci				mhi_chan = &mhi_cntrl->mhi_chan[chan];
8688c2ecf20Sopenharmony_ci				if (!mhi_chan->configured)
8698c2ecf20Sopenharmony_ci					break;
8708c2ecf20Sopenharmony_ci				parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
8718c2ecf20Sopenharmony_ci				event_quota--;
8728c2ecf20Sopenharmony_ci			}
8738c2ecf20Sopenharmony_ci			break;
8748c2ecf20Sopenharmony_ci		default:
8758c2ecf20Sopenharmony_ci			dev_err(dev, "Unhandled event type: %d\n", type);
8768c2ecf20Sopenharmony_ci			break;
8778c2ecf20Sopenharmony_ci		}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci		mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring);
8808c2ecf20Sopenharmony_ci		local_rp = ev_ring->rp;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci		ptr = er_ctxt->rp;
8838c2ecf20Sopenharmony_ci		if (!is_valid_ring_ptr(ev_ring, ptr)) {
8848c2ecf20Sopenharmony_ci			dev_err(&mhi_cntrl->mhi_dev->dev,
8858c2ecf20Sopenharmony_ci				"Event ring rp points outside of the event ring\n");
8868c2ecf20Sopenharmony_ci			return -EIO;
8878c2ecf20Sopenharmony_ci		}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci		dev_rp = mhi_to_virtual(ev_ring, ptr);
8908c2ecf20Sopenharmony_ci		count++;
8918c2ecf20Sopenharmony_ci	}
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	read_lock_bh(&mhi_cntrl->pm_lock);
8948c2ecf20Sopenharmony_ci	if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)))
8958c2ecf20Sopenharmony_ci		mhi_ring_er_db(mhi_event);
8968c2ecf20Sopenharmony_ci	read_unlock_bh(&mhi_cntrl->pm_lock);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	return count;
8998c2ecf20Sopenharmony_ci}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ciint mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
9028c2ecf20Sopenharmony_ci				struct mhi_event *mhi_event,
9038c2ecf20Sopenharmony_ci				u32 event_quota)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	struct mhi_tre *dev_rp, *local_rp;
9068c2ecf20Sopenharmony_ci	struct mhi_ring *ev_ring = &mhi_event->ring;
9078c2ecf20Sopenharmony_ci	struct mhi_event_ctxt *er_ctxt =
9088c2ecf20Sopenharmony_ci		&mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index];
9098c2ecf20Sopenharmony_ci	int count = 0;
9108c2ecf20Sopenharmony_ci	u32 chan;
9118c2ecf20Sopenharmony_ci	struct mhi_chan *mhi_chan;
9128c2ecf20Sopenharmony_ci	dma_addr_t ptr = er_ctxt->rp;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state)))
9158c2ecf20Sopenharmony_ci		return -EIO;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	if (!is_valid_ring_ptr(ev_ring, ptr)) {
9188c2ecf20Sopenharmony_ci		dev_err(&mhi_cntrl->mhi_dev->dev,
9198c2ecf20Sopenharmony_ci			"Event ring rp points outside of the event ring\n");
9208c2ecf20Sopenharmony_ci		return -EIO;
9218c2ecf20Sopenharmony_ci	}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	dev_rp = mhi_to_virtual(ev_ring, ptr);
9248c2ecf20Sopenharmony_ci	local_rp = ev_ring->rp;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	while (dev_rp != local_rp && event_quota > 0) {
9278c2ecf20Sopenharmony_ci		enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci		chan = MHI_TRE_GET_EV_CHID(local_rp);
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci		WARN_ON(chan >= mhi_cntrl->max_chan);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci		/*
9348c2ecf20Sopenharmony_ci		 * Only process the event ring elements whose channel
9358c2ecf20Sopenharmony_ci		 * ID is within the maximum supported range.
9368c2ecf20Sopenharmony_ci		 */
9378c2ecf20Sopenharmony_ci		if (chan < mhi_cntrl->max_chan &&
9388c2ecf20Sopenharmony_ci		    mhi_cntrl->mhi_chan[chan].configured) {
9398c2ecf20Sopenharmony_ci			mhi_chan = &mhi_cntrl->mhi_chan[chan];
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci			if (likely(type == MHI_PKT_TYPE_TX_EVENT)) {
9428c2ecf20Sopenharmony_ci				parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
9438c2ecf20Sopenharmony_ci				event_quota--;
9448c2ecf20Sopenharmony_ci			} else if (type == MHI_PKT_TYPE_RSC_TX_EVENT) {
9458c2ecf20Sopenharmony_ci				parse_rsc_event(mhi_cntrl, local_rp, mhi_chan);
9468c2ecf20Sopenharmony_ci				event_quota--;
9478c2ecf20Sopenharmony_ci			}
9488c2ecf20Sopenharmony_ci		}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci		mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring);
9518c2ecf20Sopenharmony_ci		local_rp = ev_ring->rp;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci		ptr = er_ctxt->rp;
9548c2ecf20Sopenharmony_ci		if (!is_valid_ring_ptr(ev_ring, ptr)) {
9558c2ecf20Sopenharmony_ci			dev_err(&mhi_cntrl->mhi_dev->dev,
9568c2ecf20Sopenharmony_ci				"Event ring rp points outside of the event ring\n");
9578c2ecf20Sopenharmony_ci			return -EIO;
9588c2ecf20Sopenharmony_ci		}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci		dev_rp = mhi_to_virtual(ev_ring, ptr);
9618c2ecf20Sopenharmony_ci		count++;
9628c2ecf20Sopenharmony_ci	}
9638c2ecf20Sopenharmony_ci	read_lock_bh(&mhi_cntrl->pm_lock);
9648c2ecf20Sopenharmony_ci	if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)))
9658c2ecf20Sopenharmony_ci		mhi_ring_er_db(mhi_event);
9668c2ecf20Sopenharmony_ci	read_unlock_bh(&mhi_cntrl->pm_lock);
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	return count;
9698c2ecf20Sopenharmony_ci}
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_civoid mhi_ev_task(unsigned long data)
9728c2ecf20Sopenharmony_ci{
9738c2ecf20Sopenharmony_ci	struct mhi_event *mhi_event = (struct mhi_event *)data;
9748c2ecf20Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	/* process all pending events */
9778c2ecf20Sopenharmony_ci	spin_lock_bh(&mhi_event->lock);
9788c2ecf20Sopenharmony_ci	mhi_event->process_event(mhi_cntrl, mhi_event, U32_MAX);
9798c2ecf20Sopenharmony_ci	spin_unlock_bh(&mhi_event->lock);
9808c2ecf20Sopenharmony_ci}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_civoid mhi_ctrl_ev_task(unsigned long data)
9838c2ecf20Sopenharmony_ci{
9848c2ecf20Sopenharmony_ci	struct mhi_event *mhi_event = (struct mhi_event *)data;
9858c2ecf20Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl;
9868c2ecf20Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
9878c2ecf20Sopenharmony_ci	enum mhi_state state;
9888c2ecf20Sopenharmony_ci	enum mhi_pm_state pm_state = 0;
9898c2ecf20Sopenharmony_ci	int ret;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	/*
9928c2ecf20Sopenharmony_ci	 * We can check PM state w/o a lock here because there is no way
9938c2ecf20Sopenharmony_ci	 * PM state can change from reg access valid to no access while this
9948c2ecf20Sopenharmony_ci	 * thread being executed.
9958c2ecf20Sopenharmony_ci	 */
9968c2ecf20Sopenharmony_ci	if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
9978c2ecf20Sopenharmony_ci		/*
9988c2ecf20Sopenharmony_ci		 * We may have a pending event but not allowed to
9998c2ecf20Sopenharmony_ci		 * process it since we are probably in a suspended state,
10008c2ecf20Sopenharmony_ci		 * so trigger a resume.
10018c2ecf20Sopenharmony_ci		 */
10028c2ecf20Sopenharmony_ci		mhi_trigger_resume(mhi_cntrl);
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci		return;
10058c2ecf20Sopenharmony_ci	}
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	/* Process ctrl events events */
10088c2ecf20Sopenharmony_ci	ret = mhi_event->process_event(mhi_cntrl, mhi_event, U32_MAX);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	/*
10118c2ecf20Sopenharmony_ci	 * We received an IRQ but no events to process, maybe device went to
10128c2ecf20Sopenharmony_ci	 * SYS_ERR state? Check the state to confirm.
10138c2ecf20Sopenharmony_ci	 */
10148c2ecf20Sopenharmony_ci	if (!ret) {
10158c2ecf20Sopenharmony_ci		write_lock_irq(&mhi_cntrl->pm_lock);
10168c2ecf20Sopenharmony_ci		state = mhi_get_mhi_state(mhi_cntrl);
10178c2ecf20Sopenharmony_ci		if (state == MHI_STATE_SYS_ERR) {
10188c2ecf20Sopenharmony_ci			dev_dbg(dev, "System error detected\n");
10198c2ecf20Sopenharmony_ci			pm_state = mhi_tryset_pm_state(mhi_cntrl,
10208c2ecf20Sopenharmony_ci						       MHI_PM_SYS_ERR_DETECT);
10218c2ecf20Sopenharmony_ci		}
10228c2ecf20Sopenharmony_ci		write_unlock_irq(&mhi_cntrl->pm_lock);
10238c2ecf20Sopenharmony_ci		if (pm_state == MHI_PM_SYS_ERR_DETECT)
10248c2ecf20Sopenharmony_ci			mhi_pm_sys_err_handler(mhi_cntrl);
10258c2ecf20Sopenharmony_ci	}
10268c2ecf20Sopenharmony_ci}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_cistatic bool mhi_is_ring_full(struct mhi_controller *mhi_cntrl,
10298c2ecf20Sopenharmony_ci			     struct mhi_ring *ring)
10308c2ecf20Sopenharmony_ci{
10318c2ecf20Sopenharmony_ci	void *tmp = ring->wp + ring->el_size;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	if (tmp >= (ring->base + ring->len))
10348c2ecf20Sopenharmony_ci		tmp = ring->base;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	return (tmp == ring->rp);
10378c2ecf20Sopenharmony_ci}
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ciint mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
10408c2ecf20Sopenharmony_ci		  struct sk_buff *skb, size_t len, enum mhi_flags mflags)
10418c2ecf20Sopenharmony_ci{
10428c2ecf20Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
10438c2ecf20Sopenharmony_ci	struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan :
10448c2ecf20Sopenharmony_ci							     mhi_dev->dl_chan;
10458c2ecf20Sopenharmony_ci	struct mhi_ring *tre_ring = &mhi_chan->tre_ring;
10468c2ecf20Sopenharmony_ci	struct mhi_buf_info buf_info = { };
10478c2ecf20Sopenharmony_ci	int ret;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	/* If MHI host pre-allocates buffers then client drivers cannot queue */
10508c2ecf20Sopenharmony_ci	if (mhi_chan->pre_alloc)
10518c2ecf20Sopenharmony_ci		return -EINVAL;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	if (mhi_is_ring_full(mhi_cntrl, tre_ring))
10548c2ecf20Sopenharmony_ci		return -ENOMEM;
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	read_lock_bh(&mhi_cntrl->pm_lock);
10578c2ecf20Sopenharmony_ci	if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) {
10588c2ecf20Sopenharmony_ci		read_unlock_bh(&mhi_cntrl->pm_lock);
10598c2ecf20Sopenharmony_ci		return -EIO;
10608c2ecf20Sopenharmony_ci	}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	/* we're in M3 or transitioning to M3 */
10638c2ecf20Sopenharmony_ci	if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
10648c2ecf20Sopenharmony_ci		mhi_trigger_resume(mhi_cntrl);
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	/* Toggle wake to exit out of M2 */
10678c2ecf20Sopenharmony_ci	mhi_cntrl->wake_toggle(mhi_cntrl);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	buf_info.v_addr = skb->data;
10708c2ecf20Sopenharmony_ci	buf_info.cb_buf = skb;
10718c2ecf20Sopenharmony_ci	buf_info.len = len;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &buf_info, mflags);
10748c2ecf20Sopenharmony_ci	if (unlikely(ret)) {
10758c2ecf20Sopenharmony_ci		read_unlock_bh(&mhi_cntrl->pm_lock);
10768c2ecf20Sopenharmony_ci		return ret;
10778c2ecf20Sopenharmony_ci	}
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	if (mhi_chan->dir == DMA_TO_DEVICE)
10808c2ecf20Sopenharmony_ci		atomic_inc(&mhi_cntrl->pending_pkts);
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) {
10838c2ecf20Sopenharmony_ci		read_lock_bh(&mhi_chan->lock);
10848c2ecf20Sopenharmony_ci		mhi_ring_chan_db(mhi_cntrl, mhi_chan);
10858c2ecf20Sopenharmony_ci		read_unlock_bh(&mhi_chan->lock);
10868c2ecf20Sopenharmony_ci	}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	read_unlock_bh(&mhi_cntrl->pm_lock);
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	return 0;
10918c2ecf20Sopenharmony_ci}
10928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_queue_skb);
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ciint mhi_queue_dma(struct mhi_device *mhi_dev, enum dma_data_direction dir,
10958c2ecf20Sopenharmony_ci		  struct mhi_buf *mhi_buf, size_t len, enum mhi_flags mflags)
10968c2ecf20Sopenharmony_ci{
10978c2ecf20Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
10988c2ecf20Sopenharmony_ci	struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan :
10998c2ecf20Sopenharmony_ci							     mhi_dev->dl_chan;
11008c2ecf20Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
11018c2ecf20Sopenharmony_ci	struct mhi_ring *tre_ring = &mhi_chan->tre_ring;
11028c2ecf20Sopenharmony_ci	struct mhi_buf_info buf_info = { };
11038c2ecf20Sopenharmony_ci	int ret;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	/* If MHI host pre-allocates buffers then client drivers cannot queue */
11068c2ecf20Sopenharmony_ci	if (mhi_chan->pre_alloc)
11078c2ecf20Sopenharmony_ci		return -EINVAL;
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	if (mhi_is_ring_full(mhi_cntrl, tre_ring))
11108c2ecf20Sopenharmony_ci		return -ENOMEM;
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	read_lock_bh(&mhi_cntrl->pm_lock);
11138c2ecf20Sopenharmony_ci	if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) {
11148c2ecf20Sopenharmony_ci		dev_err(dev, "MHI is not in activate state, PM state: %s\n",
11158c2ecf20Sopenharmony_ci			to_mhi_pm_state_str(mhi_cntrl->pm_state));
11168c2ecf20Sopenharmony_ci		read_unlock_bh(&mhi_cntrl->pm_lock);
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci		return -EIO;
11198c2ecf20Sopenharmony_ci	}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	/* we're in M3 or transitioning to M3 */
11228c2ecf20Sopenharmony_ci	if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
11238c2ecf20Sopenharmony_ci		mhi_trigger_resume(mhi_cntrl);
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	/* Toggle wake to exit out of M2 */
11268c2ecf20Sopenharmony_ci	mhi_cntrl->wake_toggle(mhi_cntrl);
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	buf_info.p_addr = mhi_buf->dma_addr;
11298c2ecf20Sopenharmony_ci	buf_info.cb_buf = mhi_buf;
11308c2ecf20Sopenharmony_ci	buf_info.pre_mapped = true;
11318c2ecf20Sopenharmony_ci	buf_info.len = len;
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &buf_info, mflags);
11348c2ecf20Sopenharmony_ci	if (unlikely(ret)) {
11358c2ecf20Sopenharmony_ci		read_unlock_bh(&mhi_cntrl->pm_lock);
11368c2ecf20Sopenharmony_ci		return ret;
11378c2ecf20Sopenharmony_ci	}
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	if (mhi_chan->dir == DMA_TO_DEVICE)
11408c2ecf20Sopenharmony_ci		atomic_inc(&mhi_cntrl->pending_pkts);
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) {
11438c2ecf20Sopenharmony_ci		read_lock_bh(&mhi_chan->lock);
11448c2ecf20Sopenharmony_ci		mhi_ring_chan_db(mhi_cntrl, mhi_chan);
11458c2ecf20Sopenharmony_ci		read_unlock_bh(&mhi_chan->lock);
11468c2ecf20Sopenharmony_ci	}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	read_unlock_bh(&mhi_cntrl->pm_lock);
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	return 0;
11518c2ecf20Sopenharmony_ci}
11528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_queue_dma);
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ciint mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
11558c2ecf20Sopenharmony_ci			struct mhi_buf_info *info, enum mhi_flags flags)
11568c2ecf20Sopenharmony_ci{
11578c2ecf20Sopenharmony_ci	struct mhi_ring *buf_ring, *tre_ring;
11588c2ecf20Sopenharmony_ci	struct mhi_tre *mhi_tre;
11598c2ecf20Sopenharmony_ci	struct mhi_buf_info *buf_info;
11608c2ecf20Sopenharmony_ci	int eot, eob, chain, bei;
11618c2ecf20Sopenharmony_ci	int ret;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	buf_ring = &mhi_chan->buf_ring;
11648c2ecf20Sopenharmony_ci	tre_ring = &mhi_chan->tre_ring;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	buf_info = buf_ring->wp;
11678c2ecf20Sopenharmony_ci	WARN_ON(buf_info->used);
11688c2ecf20Sopenharmony_ci	buf_info->pre_mapped = info->pre_mapped;
11698c2ecf20Sopenharmony_ci	if (info->pre_mapped)
11708c2ecf20Sopenharmony_ci		buf_info->p_addr = info->p_addr;
11718c2ecf20Sopenharmony_ci	else
11728c2ecf20Sopenharmony_ci		buf_info->v_addr = info->v_addr;
11738c2ecf20Sopenharmony_ci	buf_info->cb_buf = info->cb_buf;
11748c2ecf20Sopenharmony_ci	buf_info->wp = tre_ring->wp;
11758c2ecf20Sopenharmony_ci	buf_info->dir = mhi_chan->dir;
11768c2ecf20Sopenharmony_ci	buf_info->len = info->len;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	if (!info->pre_mapped) {
11798c2ecf20Sopenharmony_ci		ret = mhi_cntrl->map_single(mhi_cntrl, buf_info);
11808c2ecf20Sopenharmony_ci		if (ret)
11818c2ecf20Sopenharmony_ci			return ret;
11828c2ecf20Sopenharmony_ci	}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	eob = !!(flags & MHI_EOB);
11858c2ecf20Sopenharmony_ci	eot = !!(flags & MHI_EOT);
11868c2ecf20Sopenharmony_ci	chain = !!(flags & MHI_CHAIN);
11878c2ecf20Sopenharmony_ci	bei = !!(mhi_chan->intmod);
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	mhi_tre = tre_ring->wp;
11908c2ecf20Sopenharmony_ci	mhi_tre->ptr = MHI_TRE_DATA_PTR(buf_info->p_addr);
11918c2ecf20Sopenharmony_ci	mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(info->len);
11928c2ecf20Sopenharmony_ci	mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(bei, eot, eob, chain);
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	/* increment WP */
11958c2ecf20Sopenharmony_ci	mhi_add_ring_element(mhi_cntrl, tre_ring);
11968c2ecf20Sopenharmony_ci	mhi_add_ring_element(mhi_cntrl, buf_ring);
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	return 0;
11998c2ecf20Sopenharmony_ci}
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ciint mhi_queue_buf(struct mhi_device *mhi_dev, enum dma_data_direction dir,
12028c2ecf20Sopenharmony_ci		  void *buf, size_t len, enum mhi_flags mflags)
12038c2ecf20Sopenharmony_ci{
12048c2ecf20Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
12058c2ecf20Sopenharmony_ci	struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan :
12068c2ecf20Sopenharmony_ci							     mhi_dev->dl_chan;
12078c2ecf20Sopenharmony_ci	struct mhi_ring *tre_ring;
12088c2ecf20Sopenharmony_ci	struct mhi_buf_info buf_info = { };
12098c2ecf20Sopenharmony_ci	unsigned long flags;
12108c2ecf20Sopenharmony_ci	int ret;
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	/*
12138c2ecf20Sopenharmony_ci	 * this check here only as a guard, it's always
12148c2ecf20Sopenharmony_ci	 * possible mhi can enter error while executing rest of function,
12158c2ecf20Sopenharmony_ci	 * which is not fatal so we do not need to hold pm_lock
12168c2ecf20Sopenharmony_ci	 */
12178c2ecf20Sopenharmony_ci	if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)))
12188c2ecf20Sopenharmony_ci		return -EIO;
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	tre_ring = &mhi_chan->tre_ring;
12218c2ecf20Sopenharmony_ci	if (mhi_is_ring_full(mhi_cntrl, tre_ring))
12228c2ecf20Sopenharmony_ci		return -ENOMEM;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	buf_info.v_addr = buf;
12258c2ecf20Sopenharmony_ci	buf_info.cb_buf = buf;
12268c2ecf20Sopenharmony_ci	buf_info.len = len;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &buf_info, mflags);
12298c2ecf20Sopenharmony_ci	if (unlikely(ret))
12308c2ecf20Sopenharmony_ci		return ret;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	read_lock_irqsave(&mhi_cntrl->pm_lock, flags);
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	/* we're in M3 or transitioning to M3 */
12358c2ecf20Sopenharmony_ci	if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
12368c2ecf20Sopenharmony_ci		mhi_trigger_resume(mhi_cntrl);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	/* Toggle wake to exit out of M2 */
12398c2ecf20Sopenharmony_ci	mhi_cntrl->wake_toggle(mhi_cntrl);
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	if (mhi_chan->dir == DMA_TO_DEVICE)
12428c2ecf20Sopenharmony_ci		atomic_inc(&mhi_cntrl->pending_pkts);
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) {
12458c2ecf20Sopenharmony_ci		unsigned long flags;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci		read_lock_irqsave(&mhi_chan->lock, flags);
12488c2ecf20Sopenharmony_ci		mhi_ring_chan_db(mhi_cntrl, mhi_chan);
12498c2ecf20Sopenharmony_ci		read_unlock_irqrestore(&mhi_chan->lock, flags);
12508c2ecf20Sopenharmony_ci	}
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	read_unlock_irqrestore(&mhi_cntrl->pm_lock, flags);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	return 0;
12558c2ecf20Sopenharmony_ci}
12568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_queue_buf);
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ciint mhi_send_cmd(struct mhi_controller *mhi_cntrl,
12598c2ecf20Sopenharmony_ci		 struct mhi_chan *mhi_chan,
12608c2ecf20Sopenharmony_ci		 enum mhi_cmd_type cmd)
12618c2ecf20Sopenharmony_ci{
12628c2ecf20Sopenharmony_ci	struct mhi_tre *cmd_tre = NULL;
12638c2ecf20Sopenharmony_ci	struct mhi_cmd *mhi_cmd = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING];
12648c2ecf20Sopenharmony_ci	struct mhi_ring *ring = &mhi_cmd->ring;
12658c2ecf20Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
12668c2ecf20Sopenharmony_ci	int chan = 0;
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	if (mhi_chan)
12698c2ecf20Sopenharmony_ci		chan = mhi_chan->chan;
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	spin_lock_bh(&mhi_cmd->lock);
12728c2ecf20Sopenharmony_ci	if (!get_nr_avail_ring_elements(mhi_cntrl, ring)) {
12738c2ecf20Sopenharmony_ci		spin_unlock_bh(&mhi_cmd->lock);
12748c2ecf20Sopenharmony_ci		return -ENOMEM;
12758c2ecf20Sopenharmony_ci	}
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	/* prepare the cmd tre */
12788c2ecf20Sopenharmony_ci	cmd_tre = ring->wp;
12798c2ecf20Sopenharmony_ci	switch (cmd) {
12808c2ecf20Sopenharmony_ci	case MHI_CMD_RESET_CHAN:
12818c2ecf20Sopenharmony_ci		cmd_tre->ptr = MHI_TRE_CMD_RESET_PTR;
12828c2ecf20Sopenharmony_ci		cmd_tre->dword[0] = MHI_TRE_CMD_RESET_DWORD0;
12838c2ecf20Sopenharmony_ci		cmd_tre->dword[1] = MHI_TRE_CMD_RESET_DWORD1(chan);
12848c2ecf20Sopenharmony_ci		break;
12858c2ecf20Sopenharmony_ci	case MHI_CMD_START_CHAN:
12868c2ecf20Sopenharmony_ci		cmd_tre->ptr = MHI_TRE_CMD_START_PTR;
12878c2ecf20Sopenharmony_ci		cmd_tre->dword[0] = MHI_TRE_CMD_START_DWORD0;
12888c2ecf20Sopenharmony_ci		cmd_tre->dword[1] = MHI_TRE_CMD_START_DWORD1(chan);
12898c2ecf20Sopenharmony_ci		break;
12908c2ecf20Sopenharmony_ci	default:
12918c2ecf20Sopenharmony_ci		dev_err(dev, "Command not supported\n");
12928c2ecf20Sopenharmony_ci		break;
12938c2ecf20Sopenharmony_ci	}
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	/* queue to hardware */
12968c2ecf20Sopenharmony_ci	mhi_add_ring_element(mhi_cntrl, ring);
12978c2ecf20Sopenharmony_ci	read_lock_bh(&mhi_cntrl->pm_lock);
12988c2ecf20Sopenharmony_ci	if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)))
12998c2ecf20Sopenharmony_ci		mhi_ring_cmd_db(mhi_cntrl, mhi_cmd);
13008c2ecf20Sopenharmony_ci	read_unlock_bh(&mhi_cntrl->pm_lock);
13018c2ecf20Sopenharmony_ci	spin_unlock_bh(&mhi_cmd->lock);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	return 0;
13048c2ecf20Sopenharmony_ci}
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_cistatic void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl,
13078c2ecf20Sopenharmony_ci				    struct mhi_chan *mhi_chan)
13088c2ecf20Sopenharmony_ci{
13098c2ecf20Sopenharmony_ci	int ret;
13108c2ecf20Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	dev_dbg(dev, "Entered: unprepare channel:%d\n", mhi_chan->chan);
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	/* no more processing events for this channel */
13158c2ecf20Sopenharmony_ci	mutex_lock(&mhi_chan->mutex);
13168c2ecf20Sopenharmony_ci	write_lock_irq(&mhi_chan->lock);
13178c2ecf20Sopenharmony_ci	if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) {
13188c2ecf20Sopenharmony_ci		write_unlock_irq(&mhi_chan->lock);
13198c2ecf20Sopenharmony_ci		mutex_unlock(&mhi_chan->mutex);
13208c2ecf20Sopenharmony_ci		return;
13218c2ecf20Sopenharmony_ci	}
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	mhi_chan->ch_state = MHI_CH_STATE_DISABLED;
13248c2ecf20Sopenharmony_ci	write_unlock_irq(&mhi_chan->lock);
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	reinit_completion(&mhi_chan->completion);
13278c2ecf20Sopenharmony_ci	read_lock_bh(&mhi_cntrl->pm_lock);
13288c2ecf20Sopenharmony_ci	if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
13298c2ecf20Sopenharmony_ci		read_unlock_bh(&mhi_cntrl->pm_lock);
13308c2ecf20Sopenharmony_ci		goto error_invalid_state;
13318c2ecf20Sopenharmony_ci	}
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	mhi_cntrl->wake_toggle(mhi_cntrl);
13348c2ecf20Sopenharmony_ci	read_unlock_bh(&mhi_cntrl->pm_lock);
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	mhi_cntrl->runtime_get(mhi_cntrl);
13378c2ecf20Sopenharmony_ci	mhi_cntrl->runtime_put(mhi_cntrl);
13388c2ecf20Sopenharmony_ci	ret = mhi_send_cmd(mhi_cntrl, mhi_chan, MHI_CMD_RESET_CHAN);
13398c2ecf20Sopenharmony_ci	if (ret)
13408c2ecf20Sopenharmony_ci		goto error_invalid_state;
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	/* even if it fails we will still reset */
13438c2ecf20Sopenharmony_ci	ret = wait_for_completion_timeout(&mhi_chan->completion,
13448c2ecf20Sopenharmony_ci				msecs_to_jiffies(mhi_cntrl->timeout_ms));
13458c2ecf20Sopenharmony_ci	if (!ret || mhi_chan->ccs != MHI_EV_CC_SUCCESS)
13468c2ecf20Sopenharmony_ci		dev_err(dev,
13478c2ecf20Sopenharmony_ci			"Failed to receive cmd completion, still resetting\n");
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_cierror_invalid_state:
13508c2ecf20Sopenharmony_ci	if (!mhi_chan->offload_ch) {
13518c2ecf20Sopenharmony_ci		mhi_reset_chan(mhi_cntrl, mhi_chan);
13528c2ecf20Sopenharmony_ci		mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan);
13538c2ecf20Sopenharmony_ci	}
13548c2ecf20Sopenharmony_ci	dev_dbg(dev, "chan:%d successfully resetted\n", mhi_chan->chan);
13558c2ecf20Sopenharmony_ci	mutex_unlock(&mhi_chan->mutex);
13568c2ecf20Sopenharmony_ci}
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ciint mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
13598c2ecf20Sopenharmony_ci			struct mhi_chan *mhi_chan)
13608c2ecf20Sopenharmony_ci{
13618c2ecf20Sopenharmony_ci	int ret = 0;
13628c2ecf20Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	dev_dbg(dev, "Preparing channel: %d\n", mhi_chan->chan);
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	if (!(BIT(mhi_cntrl->ee) & mhi_chan->ee_mask)) {
13678c2ecf20Sopenharmony_ci		dev_err(dev,
13688c2ecf20Sopenharmony_ci			"Current EE: %s Required EE Mask: 0x%x for chan: %s\n",
13698c2ecf20Sopenharmony_ci			TO_MHI_EXEC_STR(mhi_cntrl->ee), mhi_chan->ee_mask,
13708c2ecf20Sopenharmony_ci			mhi_chan->name);
13718c2ecf20Sopenharmony_ci		return -ENOTCONN;
13728c2ecf20Sopenharmony_ci	}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	mutex_lock(&mhi_chan->mutex);
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	/* If channel is not in disable state, do not allow it to start */
13778c2ecf20Sopenharmony_ci	if (mhi_chan->ch_state != MHI_CH_STATE_DISABLED) {
13788c2ecf20Sopenharmony_ci		ret = -EIO;
13798c2ecf20Sopenharmony_ci		dev_dbg(dev, "channel: %d is not in disabled state\n",
13808c2ecf20Sopenharmony_ci			mhi_chan->chan);
13818c2ecf20Sopenharmony_ci		goto error_init_chan;
13828c2ecf20Sopenharmony_ci	}
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	/* Check of client manages channel context for offload channels */
13858c2ecf20Sopenharmony_ci	if (!mhi_chan->offload_ch) {
13868c2ecf20Sopenharmony_ci		ret = mhi_init_chan_ctxt(mhi_cntrl, mhi_chan);
13878c2ecf20Sopenharmony_ci		if (ret)
13888c2ecf20Sopenharmony_ci			goto error_init_chan;
13898c2ecf20Sopenharmony_ci	}
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	reinit_completion(&mhi_chan->completion);
13928c2ecf20Sopenharmony_ci	read_lock_bh(&mhi_cntrl->pm_lock);
13938c2ecf20Sopenharmony_ci	if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
13948c2ecf20Sopenharmony_ci		read_unlock_bh(&mhi_cntrl->pm_lock);
13958c2ecf20Sopenharmony_ci		ret = -EIO;
13968c2ecf20Sopenharmony_ci		goto error_pm_state;
13978c2ecf20Sopenharmony_ci	}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	mhi_cntrl->wake_toggle(mhi_cntrl);
14008c2ecf20Sopenharmony_ci	read_unlock_bh(&mhi_cntrl->pm_lock);
14018c2ecf20Sopenharmony_ci	mhi_cntrl->runtime_get(mhi_cntrl);
14028c2ecf20Sopenharmony_ci	mhi_cntrl->runtime_put(mhi_cntrl);
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	ret = mhi_send_cmd(mhi_cntrl, mhi_chan, MHI_CMD_START_CHAN);
14058c2ecf20Sopenharmony_ci	if (ret)
14068c2ecf20Sopenharmony_ci		goto error_pm_state;
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	ret = wait_for_completion_timeout(&mhi_chan->completion,
14098c2ecf20Sopenharmony_ci				msecs_to_jiffies(mhi_cntrl->timeout_ms));
14108c2ecf20Sopenharmony_ci	if (!ret || mhi_chan->ccs != MHI_EV_CC_SUCCESS) {
14118c2ecf20Sopenharmony_ci		ret = -EIO;
14128c2ecf20Sopenharmony_ci		goto error_pm_state;
14138c2ecf20Sopenharmony_ci	}
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	write_lock_irq(&mhi_chan->lock);
14168c2ecf20Sopenharmony_ci	mhi_chan->ch_state = MHI_CH_STATE_ENABLED;
14178c2ecf20Sopenharmony_ci	write_unlock_irq(&mhi_chan->lock);
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	/* Pre-allocate buffer for xfer ring */
14208c2ecf20Sopenharmony_ci	if (mhi_chan->pre_alloc) {
14218c2ecf20Sopenharmony_ci		int nr_el = get_nr_avail_ring_elements(mhi_cntrl,
14228c2ecf20Sopenharmony_ci						       &mhi_chan->tre_ring);
14238c2ecf20Sopenharmony_ci		size_t len = mhi_cntrl->buffer_len;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci		while (nr_el--) {
14268c2ecf20Sopenharmony_ci			void *buf;
14278c2ecf20Sopenharmony_ci			struct mhi_buf_info info = { };
14288c2ecf20Sopenharmony_ci			buf = kmalloc(len, GFP_KERNEL);
14298c2ecf20Sopenharmony_ci			if (!buf) {
14308c2ecf20Sopenharmony_ci				ret = -ENOMEM;
14318c2ecf20Sopenharmony_ci				goto error_pre_alloc;
14328c2ecf20Sopenharmony_ci			}
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci			/* Prepare transfer descriptors */
14358c2ecf20Sopenharmony_ci			info.v_addr = buf;
14368c2ecf20Sopenharmony_ci			info.cb_buf = buf;
14378c2ecf20Sopenharmony_ci			info.len = len;
14388c2ecf20Sopenharmony_ci			ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &info, MHI_EOT);
14398c2ecf20Sopenharmony_ci			if (ret) {
14408c2ecf20Sopenharmony_ci				kfree(buf);
14418c2ecf20Sopenharmony_ci				goto error_pre_alloc;
14428c2ecf20Sopenharmony_ci			}
14438c2ecf20Sopenharmony_ci		}
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci		read_lock_bh(&mhi_cntrl->pm_lock);
14468c2ecf20Sopenharmony_ci		if (MHI_DB_ACCESS_VALID(mhi_cntrl)) {
14478c2ecf20Sopenharmony_ci			read_lock_irq(&mhi_chan->lock);
14488c2ecf20Sopenharmony_ci			mhi_ring_chan_db(mhi_cntrl, mhi_chan);
14498c2ecf20Sopenharmony_ci			read_unlock_irq(&mhi_chan->lock);
14508c2ecf20Sopenharmony_ci		}
14518c2ecf20Sopenharmony_ci		read_unlock_bh(&mhi_cntrl->pm_lock);
14528c2ecf20Sopenharmony_ci	}
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	mutex_unlock(&mhi_chan->mutex);
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	dev_dbg(dev, "Chan: %d successfully moved to start state\n",
14578c2ecf20Sopenharmony_ci		mhi_chan->chan);
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	return 0;
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_cierror_pm_state:
14628c2ecf20Sopenharmony_ci	if (!mhi_chan->offload_ch)
14638c2ecf20Sopenharmony_ci		mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan);
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_cierror_init_chan:
14668c2ecf20Sopenharmony_ci	mutex_unlock(&mhi_chan->mutex);
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	return ret;
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_cierror_pre_alloc:
14718c2ecf20Sopenharmony_ci	mutex_unlock(&mhi_chan->mutex);
14728c2ecf20Sopenharmony_ci	__mhi_unprepare_channel(mhi_cntrl, mhi_chan);
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	return ret;
14758c2ecf20Sopenharmony_ci}
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_cistatic void mhi_mark_stale_events(struct mhi_controller *mhi_cntrl,
14788c2ecf20Sopenharmony_ci				  struct mhi_event *mhi_event,
14798c2ecf20Sopenharmony_ci				  struct mhi_event_ctxt *er_ctxt,
14808c2ecf20Sopenharmony_ci				  int chan)
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci{
14838c2ecf20Sopenharmony_ci	struct mhi_tre *dev_rp, *local_rp;
14848c2ecf20Sopenharmony_ci	struct mhi_ring *ev_ring;
14858c2ecf20Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
14868c2ecf20Sopenharmony_ci	unsigned long flags;
14878c2ecf20Sopenharmony_ci	dma_addr_t ptr;
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	dev_dbg(dev, "Marking all events for chan: %d as stale\n", chan);
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	ev_ring = &mhi_event->ring;
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	/* mark all stale events related to channel as STALE event */
14948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mhi_event->lock, flags);
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	ptr = er_ctxt->rp;
14978c2ecf20Sopenharmony_ci	if (!is_valid_ring_ptr(ev_ring, ptr)) {
14988c2ecf20Sopenharmony_ci		dev_err(&mhi_cntrl->mhi_dev->dev,
14998c2ecf20Sopenharmony_ci			"Event ring rp points outside of the event ring\n");
15008c2ecf20Sopenharmony_ci		dev_rp = ev_ring->rp;
15018c2ecf20Sopenharmony_ci	} else {
15028c2ecf20Sopenharmony_ci		dev_rp = mhi_to_virtual(ev_ring, ptr);
15038c2ecf20Sopenharmony_ci	}
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	local_rp = ev_ring->rp;
15068c2ecf20Sopenharmony_ci	while (dev_rp != local_rp) {
15078c2ecf20Sopenharmony_ci		if (MHI_TRE_GET_EV_TYPE(local_rp) == MHI_PKT_TYPE_TX_EVENT &&
15088c2ecf20Sopenharmony_ci		    chan == MHI_TRE_GET_EV_CHID(local_rp))
15098c2ecf20Sopenharmony_ci			local_rp->dword[1] = MHI_TRE_EV_DWORD1(chan,
15108c2ecf20Sopenharmony_ci					MHI_PKT_TYPE_STALE_EVENT);
15118c2ecf20Sopenharmony_ci		local_rp++;
15128c2ecf20Sopenharmony_ci		if (local_rp == (ev_ring->base + ev_ring->len))
15138c2ecf20Sopenharmony_ci			local_rp = ev_ring->base;
15148c2ecf20Sopenharmony_ci	}
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	dev_dbg(dev, "Finished marking events as stale events\n");
15178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mhi_event->lock, flags);
15188c2ecf20Sopenharmony_ci}
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_cistatic void mhi_reset_data_chan(struct mhi_controller *mhi_cntrl,
15218c2ecf20Sopenharmony_ci				struct mhi_chan *mhi_chan)
15228c2ecf20Sopenharmony_ci{
15238c2ecf20Sopenharmony_ci	struct mhi_ring *buf_ring, *tre_ring;
15248c2ecf20Sopenharmony_ci	struct mhi_result result;
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	/* Reset any pending buffers */
15278c2ecf20Sopenharmony_ci	buf_ring = &mhi_chan->buf_ring;
15288c2ecf20Sopenharmony_ci	tre_ring = &mhi_chan->tre_ring;
15298c2ecf20Sopenharmony_ci	result.transaction_status = -ENOTCONN;
15308c2ecf20Sopenharmony_ci	result.bytes_xferd = 0;
15318c2ecf20Sopenharmony_ci	while (tre_ring->rp != tre_ring->wp) {
15328c2ecf20Sopenharmony_ci		struct mhi_buf_info *buf_info = buf_ring->rp;
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci		if (mhi_chan->dir == DMA_TO_DEVICE)
15358c2ecf20Sopenharmony_ci			atomic_dec(&mhi_cntrl->pending_pkts);
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci		if (!buf_info->pre_mapped)
15388c2ecf20Sopenharmony_ci			mhi_cntrl->unmap_single(mhi_cntrl, buf_info);
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci		mhi_del_ring_element(mhi_cntrl, buf_ring);
15418c2ecf20Sopenharmony_ci		mhi_del_ring_element(mhi_cntrl, tre_ring);
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci		if (mhi_chan->pre_alloc) {
15448c2ecf20Sopenharmony_ci			kfree(buf_info->cb_buf);
15458c2ecf20Sopenharmony_ci		} else {
15468c2ecf20Sopenharmony_ci			result.buf_addr = buf_info->cb_buf;
15478c2ecf20Sopenharmony_ci			mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
15488c2ecf20Sopenharmony_ci		}
15498c2ecf20Sopenharmony_ci	}
15508c2ecf20Sopenharmony_ci}
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_civoid mhi_reset_chan(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan)
15538c2ecf20Sopenharmony_ci{
15548c2ecf20Sopenharmony_ci	struct mhi_event *mhi_event;
15558c2ecf20Sopenharmony_ci	struct mhi_event_ctxt *er_ctxt;
15568c2ecf20Sopenharmony_ci	int chan = mhi_chan->chan;
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci	/* Nothing to reset, client doesn't queue buffers */
15598c2ecf20Sopenharmony_ci	if (mhi_chan->offload_ch)
15608c2ecf20Sopenharmony_ci		return;
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	read_lock_bh(&mhi_cntrl->pm_lock);
15638c2ecf20Sopenharmony_ci	mhi_event = &mhi_cntrl->mhi_event[mhi_chan->er_index];
15648c2ecf20Sopenharmony_ci	er_ctxt = &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_chan->er_index];
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	mhi_mark_stale_events(mhi_cntrl, mhi_event, er_ctxt, chan);
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci	mhi_reset_data_chan(mhi_cntrl, mhi_chan);
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	read_unlock_bh(&mhi_cntrl->pm_lock);
15718c2ecf20Sopenharmony_ci}
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci/* Move channel to start state */
15748c2ecf20Sopenharmony_ciint mhi_prepare_for_transfer(struct mhi_device *mhi_dev)
15758c2ecf20Sopenharmony_ci{
15768c2ecf20Sopenharmony_ci	int ret, dir;
15778c2ecf20Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
15788c2ecf20Sopenharmony_ci	struct mhi_chan *mhi_chan;
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	for (dir = 0; dir < 2; dir++) {
15818c2ecf20Sopenharmony_ci		mhi_chan = dir ? mhi_dev->dl_chan : mhi_dev->ul_chan;
15828c2ecf20Sopenharmony_ci		if (!mhi_chan)
15838c2ecf20Sopenharmony_ci			continue;
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci		ret = mhi_prepare_channel(mhi_cntrl, mhi_chan);
15868c2ecf20Sopenharmony_ci		if (ret)
15878c2ecf20Sopenharmony_ci			goto error_open_chan;
15888c2ecf20Sopenharmony_ci	}
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci	return 0;
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_cierror_open_chan:
15938c2ecf20Sopenharmony_ci	for (--dir; dir >= 0; dir--) {
15948c2ecf20Sopenharmony_ci		mhi_chan = dir ? mhi_dev->dl_chan : mhi_dev->ul_chan;
15958c2ecf20Sopenharmony_ci		if (!mhi_chan)
15968c2ecf20Sopenharmony_ci			continue;
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci		__mhi_unprepare_channel(mhi_cntrl, mhi_chan);
15998c2ecf20Sopenharmony_ci	}
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci	return ret;
16028c2ecf20Sopenharmony_ci}
16038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_prepare_for_transfer);
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_civoid mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
16068c2ecf20Sopenharmony_ci{
16078c2ecf20Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
16088c2ecf20Sopenharmony_ci	struct mhi_chan *mhi_chan;
16098c2ecf20Sopenharmony_ci	int dir;
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	for (dir = 0; dir < 2; dir++) {
16128c2ecf20Sopenharmony_ci		mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
16138c2ecf20Sopenharmony_ci		if (!mhi_chan)
16148c2ecf20Sopenharmony_ci			continue;
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci		__mhi_unprepare_channel(mhi_cntrl, mhi_chan);
16178c2ecf20Sopenharmony_ci	}
16188c2ecf20Sopenharmony_ci}
16198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ciint mhi_poll(struct mhi_device *mhi_dev, u32 budget)
16228c2ecf20Sopenharmony_ci{
16238c2ecf20Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
16248c2ecf20Sopenharmony_ci	struct mhi_chan *mhi_chan = mhi_dev->dl_chan;
16258c2ecf20Sopenharmony_ci	struct mhi_event *mhi_event = &mhi_cntrl->mhi_event[mhi_chan->er_index];
16268c2ecf20Sopenharmony_ci	int ret;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	spin_lock_bh(&mhi_event->lock);
16298c2ecf20Sopenharmony_ci	ret = mhi_event->process_event(mhi_cntrl, mhi_event, budget);
16308c2ecf20Sopenharmony_ci	spin_unlock_bh(&mhi_event->lock);
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	return ret;
16338c2ecf20Sopenharmony_ci}
16348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_poll);
1635