xref: /kernel/linux/linux-6.6/drivers/bus/mhi/host/main.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/delay.h>
862306a36Sopenharmony_ci#include <linux/device.h>
962306a36Sopenharmony_ci#include <linux/dma-direction.h>
1062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1162306a36Sopenharmony_ci#include <linux/interrupt.h>
1262306a36Sopenharmony_ci#include <linux/list.h>
1362306a36Sopenharmony_ci#include <linux/mhi.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/skbuff.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include "internal.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciint __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl,
2062306a36Sopenharmony_ci			      void __iomem *base, u32 offset, u32 *out)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	return mhi_cntrl->read_reg(mhi_cntrl, base + offset, out);
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciint __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
2662306a36Sopenharmony_ci				    void __iomem *base, u32 offset,
2762306a36Sopenharmony_ci				    u32 mask, u32 *out)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	u32 tmp;
3062306a36Sopenharmony_ci	int ret;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	ret = mhi_read_reg(mhi_cntrl, base, offset, &tmp);
3362306a36Sopenharmony_ci	if (ret)
3462306a36Sopenharmony_ci		return ret;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	*out = (tmp & mask) >> __ffs(mask);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	return 0;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ciint __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
4262306a36Sopenharmony_ci				    void __iomem *base, u32 offset,
4362306a36Sopenharmony_ci				    u32 mask, u32 val, u32 delayus)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	int ret;
4662306a36Sopenharmony_ci	u32 out, retry = (mhi_cntrl->timeout_ms * 1000) / delayus;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	while (retry--) {
4962306a36Sopenharmony_ci		ret = mhi_read_reg_field(mhi_cntrl, base, offset, mask, &out);
5062306a36Sopenharmony_ci		if (ret)
5162306a36Sopenharmony_ci			return ret;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci		if (out == val)
5462306a36Sopenharmony_ci			return 0;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci		fsleep(delayus);
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return -ETIMEDOUT;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_civoid mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
6362306a36Sopenharmony_ci		   u32 offset, u32 val)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	mhi_cntrl->write_reg(mhi_cntrl, base + offset, val);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciint __must_check mhi_write_reg_field(struct mhi_controller *mhi_cntrl,
6962306a36Sopenharmony_ci				     void __iomem *base, u32 offset, u32 mask,
7062306a36Sopenharmony_ci				     u32 val)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	int ret;
7362306a36Sopenharmony_ci	u32 tmp;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	ret = mhi_read_reg(mhi_cntrl, base, offset, &tmp);
7662306a36Sopenharmony_ci	if (ret)
7762306a36Sopenharmony_ci		return ret;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	tmp &= ~mask;
8062306a36Sopenharmony_ci	tmp |= (val << __ffs(mask));
8162306a36Sopenharmony_ci	mhi_write_reg(mhi_cntrl, base, offset, tmp);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_civoid mhi_write_db(struct mhi_controller *mhi_cntrl, void __iomem *db_addr,
8762306a36Sopenharmony_ci		  dma_addr_t db_val)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	mhi_write_reg(mhi_cntrl, db_addr, 4, upper_32_bits(db_val));
9062306a36Sopenharmony_ci	mhi_write_reg(mhi_cntrl, db_addr, 0, lower_32_bits(db_val));
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_civoid mhi_db_brstmode(struct mhi_controller *mhi_cntrl,
9462306a36Sopenharmony_ci		     struct db_cfg *db_cfg,
9562306a36Sopenharmony_ci		     void __iomem *db_addr,
9662306a36Sopenharmony_ci		     dma_addr_t db_val)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	if (db_cfg->db_mode) {
9962306a36Sopenharmony_ci		db_cfg->db_val = db_val;
10062306a36Sopenharmony_ci		mhi_write_db(mhi_cntrl, db_addr, db_val);
10162306a36Sopenharmony_ci		db_cfg->db_mode = 0;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_civoid mhi_db_brstmode_disable(struct mhi_controller *mhi_cntrl,
10662306a36Sopenharmony_ci			     struct db_cfg *db_cfg,
10762306a36Sopenharmony_ci			     void __iomem *db_addr,
10862306a36Sopenharmony_ci			     dma_addr_t db_val)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	db_cfg->db_val = db_val;
11162306a36Sopenharmony_ci	mhi_write_db(mhi_cntrl, db_addr, db_val);
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_civoid mhi_ring_er_db(struct mhi_event *mhi_event)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct mhi_ring *ring = &mhi_event->ring;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	mhi_event->db_cfg.process_db(mhi_event->mhi_cntrl, &mhi_event->db_cfg,
11962306a36Sopenharmony_ci				     ring->db_addr, le64_to_cpu(*ring->ctxt_wp));
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_civoid mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	dma_addr_t db;
12562306a36Sopenharmony_ci	struct mhi_ring *ring = &mhi_cmd->ring;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	db = ring->iommu_base + (ring->wp - ring->base);
12862306a36Sopenharmony_ci	*ring->ctxt_wp = cpu_to_le64(db);
12962306a36Sopenharmony_ci	mhi_write_db(mhi_cntrl, ring->db_addr, db);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_civoid mhi_ring_chan_db(struct mhi_controller *mhi_cntrl,
13362306a36Sopenharmony_ci		      struct mhi_chan *mhi_chan)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct mhi_ring *ring = &mhi_chan->tre_ring;
13662306a36Sopenharmony_ci	dma_addr_t db;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	db = ring->iommu_base + (ring->wp - ring->base);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/*
14162306a36Sopenharmony_ci	 * Writes to the new ring element must be visible to the hardware
14262306a36Sopenharmony_ci	 * before letting h/w know there is new element to fetch.
14362306a36Sopenharmony_ci	 */
14462306a36Sopenharmony_ci	dma_wmb();
14562306a36Sopenharmony_ci	*ring->ctxt_wp = cpu_to_le64(db);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	mhi_chan->db_cfg.process_db(mhi_cntrl, &mhi_chan->db_cfg,
14862306a36Sopenharmony_ci				    ring->db_addr, db);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cienum mhi_ee_type mhi_get_exec_env(struct mhi_controller *mhi_cntrl)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	u32 exec;
15462306a36Sopenharmony_ci	int ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_EXECENV, &exec);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return (ret) ? MHI_EE_MAX : exec;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_get_exec_env);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cienum mhi_state mhi_get_mhi_state(struct mhi_controller *mhi_cntrl)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	u32 state;
16362306a36Sopenharmony_ci	int ret = mhi_read_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS,
16462306a36Sopenharmony_ci				     MHISTATUS_MHISTATE_MASK, &state);
16562306a36Sopenharmony_ci	return ret ? MHI_STATE_MAX : state;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_get_mhi_state);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_civoid mhi_soc_reset(struct mhi_controller *mhi_cntrl)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	if (mhi_cntrl->reset) {
17262306a36Sopenharmony_ci		mhi_cntrl->reset(mhi_cntrl);
17362306a36Sopenharmony_ci		return;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* Generic MHI SoC reset */
17762306a36Sopenharmony_ci	mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET,
17862306a36Sopenharmony_ci		      MHI_SOC_RESET_REQ);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_soc_reset);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ciint mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl,
18362306a36Sopenharmony_ci			 struct mhi_buf_info *buf_info)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	buf_info->p_addr = dma_map_single(mhi_cntrl->cntrl_dev,
18662306a36Sopenharmony_ci					  buf_info->v_addr, buf_info->len,
18762306a36Sopenharmony_ci					  buf_info->dir);
18862306a36Sopenharmony_ci	if (dma_mapping_error(mhi_cntrl->cntrl_dev, buf_info->p_addr))
18962306a36Sopenharmony_ci		return -ENOMEM;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return 0;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ciint mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl,
19562306a36Sopenharmony_ci			  struct mhi_buf_info *buf_info)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	void *buf = dma_alloc_coherent(mhi_cntrl->cntrl_dev, buf_info->len,
19862306a36Sopenharmony_ci				       &buf_info->p_addr, GFP_ATOMIC);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (!buf)
20162306a36Sopenharmony_ci		return -ENOMEM;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (buf_info->dir == DMA_TO_DEVICE)
20462306a36Sopenharmony_ci		memcpy(buf, buf_info->v_addr, buf_info->len);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	buf_info->bb_addr = buf;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return 0;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_civoid mhi_unmap_single_no_bb(struct mhi_controller *mhi_cntrl,
21262306a36Sopenharmony_ci			    struct mhi_buf_info *buf_info)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	dma_unmap_single(mhi_cntrl->cntrl_dev, buf_info->p_addr, buf_info->len,
21562306a36Sopenharmony_ci			 buf_info->dir);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_civoid mhi_unmap_single_use_bb(struct mhi_controller *mhi_cntrl,
21962306a36Sopenharmony_ci			     struct mhi_buf_info *buf_info)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	if (buf_info->dir == DMA_FROM_DEVICE)
22262306a36Sopenharmony_ci		memcpy(buf_info->v_addr, buf_info->bb_addr, buf_info->len);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	dma_free_coherent(mhi_cntrl->cntrl_dev, buf_info->len,
22562306a36Sopenharmony_ci			  buf_info->bb_addr, buf_info->p_addr);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int get_nr_avail_ring_elements(struct mhi_controller *mhi_cntrl,
22962306a36Sopenharmony_ci				      struct mhi_ring *ring)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	int nr_el;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (ring->wp < ring->rp) {
23462306a36Sopenharmony_ci		nr_el = ((ring->rp - ring->wp) / ring->el_size) - 1;
23562306a36Sopenharmony_ci	} else {
23662306a36Sopenharmony_ci		nr_el = (ring->rp - ring->base) / ring->el_size;
23762306a36Sopenharmony_ci		nr_el += ((ring->base + ring->len - ring->wp) /
23862306a36Sopenharmony_ci			  ring->el_size) - 1;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	return nr_el;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic void *mhi_to_virtual(struct mhi_ring *ring, dma_addr_t addr)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	return (addr - ring->iommu_base) + ring->base;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic void mhi_add_ring_element(struct mhi_controller *mhi_cntrl,
25062306a36Sopenharmony_ci				 struct mhi_ring *ring)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	ring->wp += ring->el_size;
25362306a36Sopenharmony_ci	if (ring->wp >= (ring->base + ring->len))
25462306a36Sopenharmony_ci		ring->wp = ring->base;
25562306a36Sopenharmony_ci	/* smp update */
25662306a36Sopenharmony_ci	smp_wmb();
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void mhi_del_ring_element(struct mhi_controller *mhi_cntrl,
26062306a36Sopenharmony_ci				 struct mhi_ring *ring)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	ring->rp += ring->el_size;
26362306a36Sopenharmony_ci	if (ring->rp >= (ring->base + ring->len))
26462306a36Sopenharmony_ci		ring->rp = ring->base;
26562306a36Sopenharmony_ci	/* smp update */
26662306a36Sopenharmony_ci	smp_wmb();
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic bool is_valid_ring_ptr(struct mhi_ring *ring, dma_addr_t addr)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	return addr >= ring->iommu_base && addr < ring->iommu_base + ring->len &&
27262306a36Sopenharmony_ci			!(addr & (sizeof(struct mhi_ring_element) - 1));
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ciint mhi_destroy_device(struct device *dev, void *data)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct mhi_chan *ul_chan, *dl_chan;
27862306a36Sopenharmony_ci	struct mhi_device *mhi_dev;
27962306a36Sopenharmony_ci	struct mhi_controller *mhi_cntrl;
28062306a36Sopenharmony_ci	enum mhi_ee_type ee = MHI_EE_MAX;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (dev->bus != &mhi_bus_type)
28362306a36Sopenharmony_ci		return 0;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	mhi_dev = to_mhi_device(dev);
28662306a36Sopenharmony_ci	mhi_cntrl = mhi_dev->mhi_cntrl;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* Only destroy virtual devices thats attached to bus */
28962306a36Sopenharmony_ci	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
29062306a36Sopenharmony_ci		return 0;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	ul_chan = mhi_dev->ul_chan;
29362306a36Sopenharmony_ci	dl_chan = mhi_dev->dl_chan;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/*
29662306a36Sopenharmony_ci	 * If execution environment is specified, remove only those devices that
29762306a36Sopenharmony_ci	 * started in them based on ee_mask for the channels as we move on to a
29862306a36Sopenharmony_ci	 * different execution environment
29962306a36Sopenharmony_ci	 */
30062306a36Sopenharmony_ci	if (data)
30162306a36Sopenharmony_ci		ee = *(enum mhi_ee_type *)data;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	/*
30462306a36Sopenharmony_ci	 * For the suspend and resume case, this function will get called
30562306a36Sopenharmony_ci	 * without mhi_unregister_controller(). Hence, we need to drop the
30662306a36Sopenharmony_ci	 * references to mhi_dev created for ul and dl channels. We can
30762306a36Sopenharmony_ci	 * be sure that there will be no instances of mhi_dev left after
30862306a36Sopenharmony_ci	 * this.
30962306a36Sopenharmony_ci	 */
31062306a36Sopenharmony_ci	if (ul_chan) {
31162306a36Sopenharmony_ci		if (ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
31262306a36Sopenharmony_ci			return 0;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		put_device(&ul_chan->mhi_dev->dev);
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (dl_chan) {
31862306a36Sopenharmony_ci		if (ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
31962306a36Sopenharmony_ci			return 0;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci		put_device(&dl_chan->mhi_dev->dev);
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	dev_dbg(&mhi_cntrl->mhi_dev->dev, "destroy device for chan:%s\n",
32562306a36Sopenharmony_ci		 mhi_dev->name);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* Notify the client and remove the device from MHI bus */
32862306a36Sopenharmony_ci	device_del(dev);
32962306a36Sopenharmony_ci	put_device(dev);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	return 0;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ciint mhi_get_free_desc_count(struct mhi_device *mhi_dev,
33562306a36Sopenharmony_ci				enum dma_data_direction dir)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
33862306a36Sopenharmony_ci	struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ?
33962306a36Sopenharmony_ci		mhi_dev->ul_chan : mhi_dev->dl_chan;
34062306a36Sopenharmony_ci	struct mhi_ring *tre_ring = &mhi_chan->tre_ring;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return get_nr_avail_ring_elements(mhi_cntrl, tre_ring);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_get_free_desc_count);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_civoid mhi_notify(struct mhi_device *mhi_dev, enum mhi_callback cb_reason)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct mhi_driver *mhi_drv;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (!mhi_dev->dev.driver)
35162306a36Sopenharmony_ci		return;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	mhi_drv = to_mhi_driver(mhi_dev->dev.driver);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (mhi_drv->status_cb)
35662306a36Sopenharmony_ci		mhi_drv->status_cb(mhi_dev, cb_reason);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_notify);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci/* Bind MHI channels to MHI devices */
36162306a36Sopenharmony_civoid mhi_create_devices(struct mhi_controller *mhi_cntrl)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct mhi_chan *mhi_chan;
36462306a36Sopenharmony_ci	struct mhi_device *mhi_dev;
36562306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
36662306a36Sopenharmony_ci	int i, ret;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	mhi_chan = mhi_cntrl->mhi_chan;
36962306a36Sopenharmony_ci	for (i = 0; i < mhi_cntrl->max_chan; i++, mhi_chan++) {
37062306a36Sopenharmony_ci		if (!mhi_chan->configured || mhi_chan->mhi_dev ||
37162306a36Sopenharmony_ci		    !(mhi_chan->ee_mask & BIT(mhi_cntrl->ee)))
37262306a36Sopenharmony_ci			continue;
37362306a36Sopenharmony_ci		mhi_dev = mhi_alloc_device(mhi_cntrl);
37462306a36Sopenharmony_ci		if (IS_ERR(mhi_dev))
37562306a36Sopenharmony_ci			return;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		mhi_dev->dev_type = MHI_DEVICE_XFER;
37862306a36Sopenharmony_ci		switch (mhi_chan->dir) {
37962306a36Sopenharmony_ci		case DMA_TO_DEVICE:
38062306a36Sopenharmony_ci			mhi_dev->ul_chan = mhi_chan;
38162306a36Sopenharmony_ci			mhi_dev->ul_chan_id = mhi_chan->chan;
38262306a36Sopenharmony_ci			break;
38362306a36Sopenharmony_ci		case DMA_FROM_DEVICE:
38462306a36Sopenharmony_ci			/* We use dl_chan as offload channels */
38562306a36Sopenharmony_ci			mhi_dev->dl_chan = mhi_chan;
38662306a36Sopenharmony_ci			mhi_dev->dl_chan_id = mhi_chan->chan;
38762306a36Sopenharmony_ci			break;
38862306a36Sopenharmony_ci		default:
38962306a36Sopenharmony_ci			dev_err(dev, "Direction not supported\n");
39062306a36Sopenharmony_ci			put_device(&mhi_dev->dev);
39162306a36Sopenharmony_ci			return;
39262306a36Sopenharmony_ci		}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		get_device(&mhi_dev->dev);
39562306a36Sopenharmony_ci		mhi_chan->mhi_dev = mhi_dev;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci		/* Check next channel if it matches */
39862306a36Sopenharmony_ci		if ((i + 1) < mhi_cntrl->max_chan && mhi_chan[1].configured) {
39962306a36Sopenharmony_ci			if (!strcmp(mhi_chan[1].name, mhi_chan->name)) {
40062306a36Sopenharmony_ci				i++;
40162306a36Sopenharmony_ci				mhi_chan++;
40262306a36Sopenharmony_ci				if (mhi_chan->dir == DMA_TO_DEVICE) {
40362306a36Sopenharmony_ci					mhi_dev->ul_chan = mhi_chan;
40462306a36Sopenharmony_ci					mhi_dev->ul_chan_id = mhi_chan->chan;
40562306a36Sopenharmony_ci				} else {
40662306a36Sopenharmony_ci					mhi_dev->dl_chan = mhi_chan;
40762306a36Sopenharmony_ci					mhi_dev->dl_chan_id = mhi_chan->chan;
40862306a36Sopenharmony_ci				}
40962306a36Sopenharmony_ci				get_device(&mhi_dev->dev);
41062306a36Sopenharmony_ci				mhi_chan->mhi_dev = mhi_dev;
41162306a36Sopenharmony_ci			}
41262306a36Sopenharmony_ci		}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		/* Channel name is same for both UL and DL */
41562306a36Sopenharmony_ci		mhi_dev->name = mhi_chan->name;
41662306a36Sopenharmony_ci		dev_set_name(&mhi_dev->dev, "%s_%s",
41762306a36Sopenharmony_ci			     dev_name(&mhi_cntrl->mhi_dev->dev),
41862306a36Sopenharmony_ci			     mhi_dev->name);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci		/* Init wakeup source if available */
42162306a36Sopenharmony_ci		if (mhi_dev->dl_chan && mhi_dev->dl_chan->wake_capable)
42262306a36Sopenharmony_ci			device_init_wakeup(&mhi_dev->dev, true);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		ret = device_add(&mhi_dev->dev);
42562306a36Sopenharmony_ci		if (ret)
42662306a36Sopenharmony_ci			put_device(&mhi_dev->dev);
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ciirqreturn_t mhi_irq_handler(int irq_number, void *dev)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	struct mhi_event *mhi_event = dev;
43362306a36Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl;
43462306a36Sopenharmony_ci	struct mhi_event_ctxt *er_ctxt;
43562306a36Sopenharmony_ci	struct mhi_ring *ev_ring = &mhi_event->ring;
43662306a36Sopenharmony_ci	dma_addr_t ptr;
43762306a36Sopenharmony_ci	void *dev_rp;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	/*
44062306a36Sopenharmony_ci	 * If CONFIG_DEBUG_SHIRQ is set, the IRQ handler will get invoked during __free_irq()
44162306a36Sopenharmony_ci	 * and by that time mhi_ctxt() would've freed. So check for the existence of mhi_ctxt
44262306a36Sopenharmony_ci	 * before handling the IRQs.
44362306a36Sopenharmony_ci	 */
44462306a36Sopenharmony_ci	if (!mhi_cntrl->mhi_ctxt) {
44562306a36Sopenharmony_ci		dev_dbg(&mhi_cntrl->mhi_dev->dev,
44662306a36Sopenharmony_ci			"mhi_ctxt has been freed\n");
44762306a36Sopenharmony_ci		return IRQ_HANDLED;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	er_ctxt = &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index];
45162306a36Sopenharmony_ci	ptr = le64_to_cpu(er_ctxt->rp);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (!is_valid_ring_ptr(ev_ring, ptr)) {
45462306a36Sopenharmony_ci		dev_err(&mhi_cntrl->mhi_dev->dev,
45562306a36Sopenharmony_ci			"Event ring rp points outside of the event ring\n");
45662306a36Sopenharmony_ci		return IRQ_HANDLED;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	dev_rp = mhi_to_virtual(ev_ring, ptr);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/* Only proceed if event ring has pending events */
46262306a36Sopenharmony_ci	if (ev_ring->rp == dev_rp)
46362306a36Sopenharmony_ci		return IRQ_HANDLED;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	/* For client managed event ring, notify pending data */
46662306a36Sopenharmony_ci	if (mhi_event->cl_manage) {
46762306a36Sopenharmony_ci		struct mhi_chan *mhi_chan = mhi_event->mhi_chan;
46862306a36Sopenharmony_ci		struct mhi_device *mhi_dev = mhi_chan->mhi_dev;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		if (mhi_dev)
47162306a36Sopenharmony_ci			mhi_notify(mhi_dev, MHI_CB_PENDING_DATA);
47262306a36Sopenharmony_ci	} else {
47362306a36Sopenharmony_ci		tasklet_schedule(&mhi_event->task);
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	return IRQ_HANDLED;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ciirqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	struct mhi_controller *mhi_cntrl = priv;
48262306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
48362306a36Sopenharmony_ci	enum mhi_state state;
48462306a36Sopenharmony_ci	enum mhi_pm_state pm_state = 0;
48562306a36Sopenharmony_ci	enum mhi_ee_type ee;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	write_lock_irq(&mhi_cntrl->pm_lock);
48862306a36Sopenharmony_ci	if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
48962306a36Sopenharmony_ci		write_unlock_irq(&mhi_cntrl->pm_lock);
49062306a36Sopenharmony_ci		goto exit_intvec;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	state = mhi_get_mhi_state(mhi_cntrl);
49462306a36Sopenharmony_ci	ee = mhi_get_exec_env(mhi_cntrl);
49562306a36Sopenharmony_ci	dev_dbg(dev, "local ee: %s state: %s device ee: %s state: %s\n",
49662306a36Sopenharmony_ci		TO_MHI_EXEC_STR(mhi_cntrl->ee),
49762306a36Sopenharmony_ci		mhi_state_str(mhi_cntrl->dev_state),
49862306a36Sopenharmony_ci		TO_MHI_EXEC_STR(ee), mhi_state_str(state));
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	if (state == MHI_STATE_SYS_ERR) {
50162306a36Sopenharmony_ci		dev_dbg(dev, "System error detected\n");
50262306a36Sopenharmony_ci		pm_state = mhi_tryset_pm_state(mhi_cntrl,
50362306a36Sopenharmony_ci					       MHI_PM_SYS_ERR_DETECT);
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci	write_unlock_irq(&mhi_cntrl->pm_lock);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (pm_state != MHI_PM_SYS_ERR_DETECT)
50862306a36Sopenharmony_ci		goto exit_intvec;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	switch (ee) {
51162306a36Sopenharmony_ci	case MHI_EE_RDDM:
51262306a36Sopenharmony_ci		/* proceed if power down is not already in progress */
51362306a36Sopenharmony_ci		if (mhi_cntrl->rddm_image && mhi_is_active(mhi_cntrl)) {
51462306a36Sopenharmony_ci			mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_RDDM);
51562306a36Sopenharmony_ci			mhi_cntrl->ee = ee;
51662306a36Sopenharmony_ci			wake_up_all(&mhi_cntrl->state_event);
51762306a36Sopenharmony_ci		}
51862306a36Sopenharmony_ci		break;
51962306a36Sopenharmony_ci	case MHI_EE_PBL:
52062306a36Sopenharmony_ci	case MHI_EE_EDL:
52162306a36Sopenharmony_ci	case MHI_EE_PTHRU:
52262306a36Sopenharmony_ci		mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_FATAL_ERROR);
52362306a36Sopenharmony_ci		mhi_cntrl->ee = ee;
52462306a36Sopenharmony_ci		wake_up_all(&mhi_cntrl->state_event);
52562306a36Sopenharmony_ci		mhi_pm_sys_err_handler(mhi_cntrl);
52662306a36Sopenharmony_ci		break;
52762306a36Sopenharmony_ci	default:
52862306a36Sopenharmony_ci		wake_up_all(&mhi_cntrl->state_event);
52962306a36Sopenharmony_ci		mhi_pm_sys_err_handler(mhi_cntrl);
53062306a36Sopenharmony_ci		break;
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ciexit_intvec:
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	return IRQ_HANDLED;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ciirqreturn_t mhi_intvec_handler(int irq_number, void *dev)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct mhi_controller *mhi_cntrl = dev;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	/* Wake up events waiting for state change */
54362306a36Sopenharmony_ci	wake_up_all(&mhi_cntrl->state_event);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic void mhi_recycle_ev_ring_element(struct mhi_controller *mhi_cntrl,
54962306a36Sopenharmony_ci					struct mhi_ring *ring)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	/* Update the WP */
55262306a36Sopenharmony_ci	ring->wp += ring->el_size;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	if (ring->wp >= (ring->base + ring->len))
55562306a36Sopenharmony_ci		ring->wp = ring->base;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	*ring->ctxt_wp = cpu_to_le64(ring->iommu_base + (ring->wp - ring->base));
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/* Update the RP */
56062306a36Sopenharmony_ci	ring->rp += ring->el_size;
56162306a36Sopenharmony_ci	if (ring->rp >= (ring->base + ring->len))
56262306a36Sopenharmony_ci		ring->rp = ring->base;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* Update to all cores */
56562306a36Sopenharmony_ci	smp_wmb();
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic int parse_xfer_event(struct mhi_controller *mhi_cntrl,
56962306a36Sopenharmony_ci			    struct mhi_ring_element *event,
57062306a36Sopenharmony_ci			    struct mhi_chan *mhi_chan)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	struct mhi_ring *buf_ring, *tre_ring;
57362306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
57462306a36Sopenharmony_ci	struct mhi_result result;
57562306a36Sopenharmony_ci	unsigned long flags = 0;
57662306a36Sopenharmony_ci	u32 ev_code;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	ev_code = MHI_TRE_GET_EV_CODE(event);
57962306a36Sopenharmony_ci	buf_ring = &mhi_chan->buf_ring;
58062306a36Sopenharmony_ci	tre_ring = &mhi_chan->tre_ring;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	result.transaction_status = (ev_code == MHI_EV_CC_OVERFLOW) ?
58362306a36Sopenharmony_ci		-EOVERFLOW : 0;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/*
58662306a36Sopenharmony_ci	 * If it's a DB Event then we need to grab the lock
58762306a36Sopenharmony_ci	 * with preemption disabled and as a write because we
58862306a36Sopenharmony_ci	 * have to update db register and there are chances that
58962306a36Sopenharmony_ci	 * another thread could be doing the same.
59062306a36Sopenharmony_ci	 */
59162306a36Sopenharmony_ci	if (ev_code >= MHI_EV_CC_OOB)
59262306a36Sopenharmony_ci		write_lock_irqsave(&mhi_chan->lock, flags);
59362306a36Sopenharmony_ci	else
59462306a36Sopenharmony_ci		read_lock_bh(&mhi_chan->lock);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED)
59762306a36Sopenharmony_ci		goto end_process_tx_event;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	switch (ev_code) {
60062306a36Sopenharmony_ci	case MHI_EV_CC_OVERFLOW:
60162306a36Sopenharmony_ci	case MHI_EV_CC_EOB:
60262306a36Sopenharmony_ci	case MHI_EV_CC_EOT:
60362306a36Sopenharmony_ci	{
60462306a36Sopenharmony_ci		dma_addr_t ptr = MHI_TRE_GET_EV_PTR(event);
60562306a36Sopenharmony_ci		struct mhi_ring_element *local_rp, *ev_tre;
60662306a36Sopenharmony_ci		void *dev_rp;
60762306a36Sopenharmony_ci		struct mhi_buf_info *buf_info;
60862306a36Sopenharmony_ci		u16 xfer_len;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		if (!is_valid_ring_ptr(tre_ring, ptr)) {
61162306a36Sopenharmony_ci			dev_err(&mhi_cntrl->mhi_dev->dev,
61262306a36Sopenharmony_ci				"Event element points outside of the tre ring\n");
61362306a36Sopenharmony_ci			break;
61462306a36Sopenharmony_ci		}
61562306a36Sopenharmony_ci		/* Get the TRB this event points to */
61662306a36Sopenharmony_ci		ev_tre = mhi_to_virtual(tre_ring, ptr);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		dev_rp = ev_tre + 1;
61962306a36Sopenharmony_ci		if (dev_rp >= (tre_ring->base + tre_ring->len))
62062306a36Sopenharmony_ci			dev_rp = tre_ring->base;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci		result.dir = mhi_chan->dir;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		local_rp = tre_ring->rp;
62562306a36Sopenharmony_ci		while (local_rp != dev_rp) {
62662306a36Sopenharmony_ci			buf_info = buf_ring->rp;
62762306a36Sopenharmony_ci			/* If it's the last TRE, get length from the event */
62862306a36Sopenharmony_ci			if (local_rp == ev_tre)
62962306a36Sopenharmony_ci				xfer_len = MHI_TRE_GET_EV_LEN(event);
63062306a36Sopenharmony_ci			else
63162306a36Sopenharmony_ci				xfer_len = buf_info->len;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci			/* Unmap if it's not pre-mapped by client */
63462306a36Sopenharmony_ci			if (likely(!buf_info->pre_mapped))
63562306a36Sopenharmony_ci				mhi_cntrl->unmap_single(mhi_cntrl, buf_info);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci			result.buf_addr = buf_info->cb_buf;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci			/* truncate to buf len if xfer_len is larger */
64062306a36Sopenharmony_ci			result.bytes_xferd =
64162306a36Sopenharmony_ci				min_t(u16, xfer_len, buf_info->len);
64262306a36Sopenharmony_ci			mhi_del_ring_element(mhi_cntrl, buf_ring);
64362306a36Sopenharmony_ci			mhi_del_ring_element(mhi_cntrl, tre_ring);
64462306a36Sopenharmony_ci			local_rp = tre_ring->rp;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci			read_unlock_bh(&mhi_chan->lock);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci			/* notify client */
64962306a36Sopenharmony_ci			mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci			if (mhi_chan->dir == DMA_TO_DEVICE) {
65262306a36Sopenharmony_ci				atomic_dec(&mhi_cntrl->pending_pkts);
65362306a36Sopenharmony_ci				/* Release the reference got from mhi_queue() */
65462306a36Sopenharmony_ci				mhi_cntrl->runtime_put(mhi_cntrl);
65562306a36Sopenharmony_ci			}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci			/*
65862306a36Sopenharmony_ci			 * Recycle the buffer if buffer is pre-allocated,
65962306a36Sopenharmony_ci			 * if there is an error, not much we can do apart
66062306a36Sopenharmony_ci			 * from dropping the packet
66162306a36Sopenharmony_ci			 */
66262306a36Sopenharmony_ci			if (mhi_chan->pre_alloc) {
66362306a36Sopenharmony_ci				if (mhi_queue_buf(mhi_chan->mhi_dev,
66462306a36Sopenharmony_ci						  mhi_chan->dir,
66562306a36Sopenharmony_ci						  buf_info->cb_buf,
66662306a36Sopenharmony_ci						  buf_info->len, MHI_EOT)) {
66762306a36Sopenharmony_ci					dev_err(dev,
66862306a36Sopenharmony_ci						"Error recycling buffer for chan:%d\n",
66962306a36Sopenharmony_ci						mhi_chan->chan);
67062306a36Sopenharmony_ci					kfree(buf_info->cb_buf);
67162306a36Sopenharmony_ci				}
67262306a36Sopenharmony_ci			}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci			read_lock_bh(&mhi_chan->lock);
67562306a36Sopenharmony_ci		}
67662306a36Sopenharmony_ci		break;
67762306a36Sopenharmony_ci	} /* CC_EOT */
67862306a36Sopenharmony_ci	case MHI_EV_CC_OOB:
67962306a36Sopenharmony_ci	case MHI_EV_CC_DB_MODE:
68062306a36Sopenharmony_ci	{
68162306a36Sopenharmony_ci		unsigned long pm_lock_flags;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		mhi_chan->db_cfg.db_mode = 1;
68462306a36Sopenharmony_ci		read_lock_irqsave(&mhi_cntrl->pm_lock, pm_lock_flags);
68562306a36Sopenharmony_ci		if (tre_ring->wp != tre_ring->rp &&
68662306a36Sopenharmony_ci		    MHI_DB_ACCESS_VALID(mhi_cntrl)) {
68762306a36Sopenharmony_ci			mhi_ring_chan_db(mhi_cntrl, mhi_chan);
68862306a36Sopenharmony_ci		}
68962306a36Sopenharmony_ci		read_unlock_irqrestore(&mhi_cntrl->pm_lock, pm_lock_flags);
69062306a36Sopenharmony_ci		break;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci	case MHI_EV_CC_BAD_TRE:
69362306a36Sopenharmony_ci	default:
69462306a36Sopenharmony_ci		dev_err(dev, "Unknown event 0x%x\n", ev_code);
69562306a36Sopenharmony_ci		break;
69662306a36Sopenharmony_ci	} /* switch(MHI_EV_READ_CODE(EV_TRB_CODE,event)) */
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ciend_process_tx_event:
69962306a36Sopenharmony_ci	if (ev_code >= MHI_EV_CC_OOB)
70062306a36Sopenharmony_ci		write_unlock_irqrestore(&mhi_chan->lock, flags);
70162306a36Sopenharmony_ci	else
70262306a36Sopenharmony_ci		read_unlock_bh(&mhi_chan->lock);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	return 0;
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic int parse_rsc_event(struct mhi_controller *mhi_cntrl,
70862306a36Sopenharmony_ci			   struct mhi_ring_element *event,
70962306a36Sopenharmony_ci			   struct mhi_chan *mhi_chan)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	struct mhi_ring *buf_ring, *tre_ring;
71262306a36Sopenharmony_ci	struct mhi_buf_info *buf_info;
71362306a36Sopenharmony_ci	struct mhi_result result;
71462306a36Sopenharmony_ci	int ev_code;
71562306a36Sopenharmony_ci	u32 cookie; /* offset to local descriptor */
71662306a36Sopenharmony_ci	u16 xfer_len;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	buf_ring = &mhi_chan->buf_ring;
71962306a36Sopenharmony_ci	tre_ring = &mhi_chan->tre_ring;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	ev_code = MHI_TRE_GET_EV_CODE(event);
72262306a36Sopenharmony_ci	cookie = MHI_TRE_GET_EV_COOKIE(event);
72362306a36Sopenharmony_ci	xfer_len = MHI_TRE_GET_EV_LEN(event);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	/* Received out of bound cookie */
72662306a36Sopenharmony_ci	WARN_ON(cookie >= buf_ring->len);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	buf_info = buf_ring->base + cookie;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	result.transaction_status = (ev_code == MHI_EV_CC_OVERFLOW) ?
73162306a36Sopenharmony_ci		-EOVERFLOW : 0;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	/* truncate to buf len if xfer_len is larger */
73462306a36Sopenharmony_ci	result.bytes_xferd = min_t(u16, xfer_len, buf_info->len);
73562306a36Sopenharmony_ci	result.buf_addr = buf_info->cb_buf;
73662306a36Sopenharmony_ci	result.dir = mhi_chan->dir;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	read_lock_bh(&mhi_chan->lock);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED)
74162306a36Sopenharmony_ci		goto end_process_rsc_event;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	WARN_ON(!buf_info->used);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	/* notify the client */
74662306a36Sopenharmony_ci	mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	/*
74962306a36Sopenharmony_ci	 * Note: We're arbitrarily incrementing RP even though, completion
75062306a36Sopenharmony_ci	 * packet we processed might not be the same one, reason we can do this
75162306a36Sopenharmony_ci	 * is because device guaranteed to cache descriptors in order it
75262306a36Sopenharmony_ci	 * receive, so even though completion event is different we can re-use
75362306a36Sopenharmony_ci	 * all descriptors in between.
75462306a36Sopenharmony_ci	 * Example:
75562306a36Sopenharmony_ci	 * Transfer Ring has descriptors: A, B, C, D
75662306a36Sopenharmony_ci	 * Last descriptor host queue is D (WP) and first descriptor
75762306a36Sopenharmony_ci	 * host queue is A (RP).
75862306a36Sopenharmony_ci	 * The completion event we just serviced is descriptor C.
75962306a36Sopenharmony_ci	 * Then we can safely queue descriptors to replace A, B, and C
76062306a36Sopenharmony_ci	 * even though host did not receive any completions.
76162306a36Sopenharmony_ci	 */
76262306a36Sopenharmony_ci	mhi_del_ring_element(mhi_cntrl, tre_ring);
76362306a36Sopenharmony_ci	buf_info->used = false;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ciend_process_rsc_event:
76662306a36Sopenharmony_ci	read_unlock_bh(&mhi_chan->lock);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	return 0;
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cistatic void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
77262306a36Sopenharmony_ci				       struct mhi_ring_element *tre)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	dma_addr_t ptr = MHI_TRE_GET_EV_PTR(tre);
77562306a36Sopenharmony_ci	struct mhi_cmd *cmd_ring = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING];
77662306a36Sopenharmony_ci	struct mhi_ring *mhi_ring = &cmd_ring->ring;
77762306a36Sopenharmony_ci	struct mhi_ring_element *cmd_pkt;
77862306a36Sopenharmony_ci	struct mhi_chan *mhi_chan;
77962306a36Sopenharmony_ci	u32 chan;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	if (!is_valid_ring_ptr(mhi_ring, ptr)) {
78262306a36Sopenharmony_ci		dev_err(&mhi_cntrl->mhi_dev->dev,
78362306a36Sopenharmony_ci			"Event element points outside of the cmd ring\n");
78462306a36Sopenharmony_ci		return;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	cmd_pkt = mhi_to_virtual(mhi_ring, ptr);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	chan = MHI_TRE_GET_CMD_CHID(cmd_pkt);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (chan < mhi_cntrl->max_chan &&
79262306a36Sopenharmony_ci	    mhi_cntrl->mhi_chan[chan].configured) {
79362306a36Sopenharmony_ci		mhi_chan = &mhi_cntrl->mhi_chan[chan];
79462306a36Sopenharmony_ci		write_lock_bh(&mhi_chan->lock);
79562306a36Sopenharmony_ci		mhi_chan->ccs = MHI_TRE_GET_EV_CODE(tre);
79662306a36Sopenharmony_ci		complete(&mhi_chan->completion);
79762306a36Sopenharmony_ci		write_unlock_bh(&mhi_chan->lock);
79862306a36Sopenharmony_ci	} else {
79962306a36Sopenharmony_ci		dev_err(&mhi_cntrl->mhi_dev->dev,
80062306a36Sopenharmony_ci			"Completion packet for invalid channel ID: %d\n", chan);
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	mhi_del_ring_element(mhi_cntrl, mhi_ring);
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ciint mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
80762306a36Sopenharmony_ci			     struct mhi_event *mhi_event,
80862306a36Sopenharmony_ci			     u32 event_quota)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	struct mhi_ring_element *dev_rp, *local_rp;
81162306a36Sopenharmony_ci	struct mhi_ring *ev_ring = &mhi_event->ring;
81262306a36Sopenharmony_ci	struct mhi_event_ctxt *er_ctxt =
81362306a36Sopenharmony_ci		&mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index];
81462306a36Sopenharmony_ci	struct mhi_chan *mhi_chan;
81562306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
81662306a36Sopenharmony_ci	u32 chan;
81762306a36Sopenharmony_ci	int count = 0;
81862306a36Sopenharmony_ci	dma_addr_t ptr = le64_to_cpu(er_ctxt->rp);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	/*
82162306a36Sopenharmony_ci	 * This is a quick check to avoid unnecessary event processing
82262306a36Sopenharmony_ci	 * in case MHI is already in error state, but it's still possible
82362306a36Sopenharmony_ci	 * to transition to error state while processing events
82462306a36Sopenharmony_ci	 */
82562306a36Sopenharmony_ci	if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state)))
82662306a36Sopenharmony_ci		return -EIO;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	if (!is_valid_ring_ptr(ev_ring, ptr)) {
82962306a36Sopenharmony_ci		dev_err(&mhi_cntrl->mhi_dev->dev,
83062306a36Sopenharmony_ci			"Event ring rp points outside of the event ring\n");
83162306a36Sopenharmony_ci		return -EIO;
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	dev_rp = mhi_to_virtual(ev_ring, ptr);
83562306a36Sopenharmony_ci	local_rp = ev_ring->rp;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	while (dev_rp != local_rp) {
83862306a36Sopenharmony_ci		enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci		switch (type) {
84162306a36Sopenharmony_ci		case MHI_PKT_TYPE_BW_REQ_EVENT:
84262306a36Sopenharmony_ci		{
84362306a36Sopenharmony_ci			struct mhi_link_info *link_info;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci			link_info = &mhi_cntrl->mhi_link_info;
84662306a36Sopenharmony_ci			write_lock_irq(&mhi_cntrl->pm_lock);
84762306a36Sopenharmony_ci			link_info->target_link_speed =
84862306a36Sopenharmony_ci				MHI_TRE_GET_EV_LINKSPEED(local_rp);
84962306a36Sopenharmony_ci			link_info->target_link_width =
85062306a36Sopenharmony_ci				MHI_TRE_GET_EV_LINKWIDTH(local_rp);
85162306a36Sopenharmony_ci			write_unlock_irq(&mhi_cntrl->pm_lock);
85262306a36Sopenharmony_ci			dev_dbg(dev, "Received BW_REQ event\n");
85362306a36Sopenharmony_ci			mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_BW_REQ);
85462306a36Sopenharmony_ci			break;
85562306a36Sopenharmony_ci		}
85662306a36Sopenharmony_ci		case MHI_PKT_TYPE_STATE_CHANGE_EVENT:
85762306a36Sopenharmony_ci		{
85862306a36Sopenharmony_ci			enum mhi_state new_state;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci			new_state = MHI_TRE_GET_EV_STATE(local_rp);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci			dev_dbg(dev, "State change event to state: %s\n",
86362306a36Sopenharmony_ci				mhi_state_str(new_state));
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci			switch (new_state) {
86662306a36Sopenharmony_ci			case MHI_STATE_M0:
86762306a36Sopenharmony_ci				mhi_pm_m0_transition(mhi_cntrl);
86862306a36Sopenharmony_ci				break;
86962306a36Sopenharmony_ci			case MHI_STATE_M1:
87062306a36Sopenharmony_ci				mhi_pm_m1_transition(mhi_cntrl);
87162306a36Sopenharmony_ci				break;
87262306a36Sopenharmony_ci			case MHI_STATE_M3:
87362306a36Sopenharmony_ci				mhi_pm_m3_transition(mhi_cntrl);
87462306a36Sopenharmony_ci				break;
87562306a36Sopenharmony_ci			case MHI_STATE_SYS_ERR:
87662306a36Sopenharmony_ci			{
87762306a36Sopenharmony_ci				enum mhi_pm_state pm_state;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci				dev_dbg(dev, "System error detected\n");
88062306a36Sopenharmony_ci				write_lock_irq(&mhi_cntrl->pm_lock);
88162306a36Sopenharmony_ci				pm_state = mhi_tryset_pm_state(mhi_cntrl,
88262306a36Sopenharmony_ci							MHI_PM_SYS_ERR_DETECT);
88362306a36Sopenharmony_ci				write_unlock_irq(&mhi_cntrl->pm_lock);
88462306a36Sopenharmony_ci				if (pm_state == MHI_PM_SYS_ERR_DETECT)
88562306a36Sopenharmony_ci					mhi_pm_sys_err_handler(mhi_cntrl);
88662306a36Sopenharmony_ci				break;
88762306a36Sopenharmony_ci			}
88862306a36Sopenharmony_ci			default:
88962306a36Sopenharmony_ci				dev_err(dev, "Invalid state: %s\n",
89062306a36Sopenharmony_ci					mhi_state_str(new_state));
89162306a36Sopenharmony_ci			}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci			break;
89462306a36Sopenharmony_ci		}
89562306a36Sopenharmony_ci		case MHI_PKT_TYPE_CMD_COMPLETION_EVENT:
89662306a36Sopenharmony_ci			mhi_process_cmd_completion(mhi_cntrl, local_rp);
89762306a36Sopenharmony_ci			break;
89862306a36Sopenharmony_ci		case MHI_PKT_TYPE_EE_EVENT:
89962306a36Sopenharmony_ci		{
90062306a36Sopenharmony_ci			enum dev_st_transition st = DEV_ST_TRANSITION_MAX;
90162306a36Sopenharmony_ci			enum mhi_ee_type event = MHI_TRE_GET_EV_EXECENV(local_rp);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci			dev_dbg(dev, "Received EE event: %s\n",
90462306a36Sopenharmony_ci				TO_MHI_EXEC_STR(event));
90562306a36Sopenharmony_ci			switch (event) {
90662306a36Sopenharmony_ci			case MHI_EE_SBL:
90762306a36Sopenharmony_ci				st = DEV_ST_TRANSITION_SBL;
90862306a36Sopenharmony_ci				break;
90962306a36Sopenharmony_ci			case MHI_EE_WFW:
91062306a36Sopenharmony_ci			case MHI_EE_AMSS:
91162306a36Sopenharmony_ci				st = DEV_ST_TRANSITION_MISSION_MODE;
91262306a36Sopenharmony_ci				break;
91362306a36Sopenharmony_ci			case MHI_EE_FP:
91462306a36Sopenharmony_ci				st = DEV_ST_TRANSITION_FP;
91562306a36Sopenharmony_ci				break;
91662306a36Sopenharmony_ci			case MHI_EE_RDDM:
91762306a36Sopenharmony_ci				mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_RDDM);
91862306a36Sopenharmony_ci				write_lock_irq(&mhi_cntrl->pm_lock);
91962306a36Sopenharmony_ci				mhi_cntrl->ee = event;
92062306a36Sopenharmony_ci				write_unlock_irq(&mhi_cntrl->pm_lock);
92162306a36Sopenharmony_ci				wake_up_all(&mhi_cntrl->state_event);
92262306a36Sopenharmony_ci				break;
92362306a36Sopenharmony_ci			default:
92462306a36Sopenharmony_ci				dev_err(dev,
92562306a36Sopenharmony_ci					"Unhandled EE event: 0x%x\n", type);
92662306a36Sopenharmony_ci			}
92762306a36Sopenharmony_ci			if (st != DEV_ST_TRANSITION_MAX)
92862306a36Sopenharmony_ci				mhi_queue_state_transition(mhi_cntrl, st);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci			break;
93162306a36Sopenharmony_ci		}
93262306a36Sopenharmony_ci		case MHI_PKT_TYPE_TX_EVENT:
93362306a36Sopenharmony_ci			chan = MHI_TRE_GET_EV_CHID(local_rp);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci			WARN_ON(chan >= mhi_cntrl->max_chan);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci			/*
93862306a36Sopenharmony_ci			 * Only process the event ring elements whose channel
93962306a36Sopenharmony_ci			 * ID is within the maximum supported range.
94062306a36Sopenharmony_ci			 */
94162306a36Sopenharmony_ci			if (chan < mhi_cntrl->max_chan) {
94262306a36Sopenharmony_ci				mhi_chan = &mhi_cntrl->mhi_chan[chan];
94362306a36Sopenharmony_ci				if (!mhi_chan->configured)
94462306a36Sopenharmony_ci					break;
94562306a36Sopenharmony_ci				parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
94662306a36Sopenharmony_ci			}
94762306a36Sopenharmony_ci			break;
94862306a36Sopenharmony_ci		default:
94962306a36Sopenharmony_ci			dev_err(dev, "Unhandled event type: %d\n", type);
95062306a36Sopenharmony_ci			break;
95162306a36Sopenharmony_ci		}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci		mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring);
95462306a36Sopenharmony_ci		local_rp = ev_ring->rp;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci		ptr = le64_to_cpu(er_ctxt->rp);
95762306a36Sopenharmony_ci		if (!is_valid_ring_ptr(ev_ring, ptr)) {
95862306a36Sopenharmony_ci			dev_err(&mhi_cntrl->mhi_dev->dev,
95962306a36Sopenharmony_ci				"Event ring rp points outside of the event ring\n");
96062306a36Sopenharmony_ci			return -EIO;
96162306a36Sopenharmony_ci		}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci		dev_rp = mhi_to_virtual(ev_ring, ptr);
96462306a36Sopenharmony_ci		count++;
96562306a36Sopenharmony_ci	}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	read_lock_bh(&mhi_cntrl->pm_lock);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	/* Ring EV DB only if there is any pending element to process */
97062306a36Sopenharmony_ci	if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)) && count)
97162306a36Sopenharmony_ci		mhi_ring_er_db(mhi_event);
97262306a36Sopenharmony_ci	read_unlock_bh(&mhi_cntrl->pm_lock);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	return count;
97562306a36Sopenharmony_ci}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ciint mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
97862306a36Sopenharmony_ci				struct mhi_event *mhi_event,
97962306a36Sopenharmony_ci				u32 event_quota)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	struct mhi_ring_element *dev_rp, *local_rp;
98262306a36Sopenharmony_ci	struct mhi_ring *ev_ring = &mhi_event->ring;
98362306a36Sopenharmony_ci	struct mhi_event_ctxt *er_ctxt =
98462306a36Sopenharmony_ci		&mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index];
98562306a36Sopenharmony_ci	int count = 0;
98662306a36Sopenharmony_ci	u32 chan;
98762306a36Sopenharmony_ci	struct mhi_chan *mhi_chan;
98862306a36Sopenharmony_ci	dma_addr_t ptr = le64_to_cpu(er_ctxt->rp);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state)))
99162306a36Sopenharmony_ci		return -EIO;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	if (!is_valid_ring_ptr(ev_ring, ptr)) {
99462306a36Sopenharmony_ci		dev_err(&mhi_cntrl->mhi_dev->dev,
99562306a36Sopenharmony_ci			"Event ring rp points outside of the event ring\n");
99662306a36Sopenharmony_ci		return -EIO;
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	dev_rp = mhi_to_virtual(ev_ring, ptr);
100062306a36Sopenharmony_ci	local_rp = ev_ring->rp;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	while (dev_rp != local_rp && event_quota > 0) {
100362306a36Sopenharmony_ci		enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci		chan = MHI_TRE_GET_EV_CHID(local_rp);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci		WARN_ON(chan >= mhi_cntrl->max_chan);
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci		/*
101062306a36Sopenharmony_ci		 * Only process the event ring elements whose channel
101162306a36Sopenharmony_ci		 * ID is within the maximum supported range.
101262306a36Sopenharmony_ci		 */
101362306a36Sopenharmony_ci		if (chan < mhi_cntrl->max_chan &&
101462306a36Sopenharmony_ci		    mhi_cntrl->mhi_chan[chan].configured) {
101562306a36Sopenharmony_ci			mhi_chan = &mhi_cntrl->mhi_chan[chan];
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci			if (likely(type == MHI_PKT_TYPE_TX_EVENT)) {
101862306a36Sopenharmony_ci				parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
101962306a36Sopenharmony_ci				event_quota--;
102062306a36Sopenharmony_ci			} else if (type == MHI_PKT_TYPE_RSC_TX_EVENT) {
102162306a36Sopenharmony_ci				parse_rsc_event(mhi_cntrl, local_rp, mhi_chan);
102262306a36Sopenharmony_ci				event_quota--;
102362306a36Sopenharmony_ci			}
102462306a36Sopenharmony_ci		}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci		mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring);
102762306a36Sopenharmony_ci		local_rp = ev_ring->rp;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci		ptr = le64_to_cpu(er_ctxt->rp);
103062306a36Sopenharmony_ci		if (!is_valid_ring_ptr(ev_ring, ptr)) {
103162306a36Sopenharmony_ci			dev_err(&mhi_cntrl->mhi_dev->dev,
103262306a36Sopenharmony_ci				"Event ring rp points outside of the event ring\n");
103362306a36Sopenharmony_ci			return -EIO;
103462306a36Sopenharmony_ci		}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci		dev_rp = mhi_to_virtual(ev_ring, ptr);
103762306a36Sopenharmony_ci		count++;
103862306a36Sopenharmony_ci	}
103962306a36Sopenharmony_ci	read_lock_bh(&mhi_cntrl->pm_lock);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	/* Ring EV DB only if there is any pending element to process */
104262306a36Sopenharmony_ci	if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)) && count)
104362306a36Sopenharmony_ci		mhi_ring_er_db(mhi_event);
104462306a36Sopenharmony_ci	read_unlock_bh(&mhi_cntrl->pm_lock);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	return count;
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_civoid mhi_ev_task(unsigned long data)
105062306a36Sopenharmony_ci{
105162306a36Sopenharmony_ci	struct mhi_event *mhi_event = (struct mhi_event *)data;
105262306a36Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	/* process all pending events */
105562306a36Sopenharmony_ci	spin_lock_bh(&mhi_event->lock);
105662306a36Sopenharmony_ci	mhi_event->process_event(mhi_cntrl, mhi_event, U32_MAX);
105762306a36Sopenharmony_ci	spin_unlock_bh(&mhi_event->lock);
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_civoid mhi_ctrl_ev_task(unsigned long data)
106162306a36Sopenharmony_ci{
106262306a36Sopenharmony_ci	struct mhi_event *mhi_event = (struct mhi_event *)data;
106362306a36Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl;
106462306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
106562306a36Sopenharmony_ci	enum mhi_state state;
106662306a36Sopenharmony_ci	enum mhi_pm_state pm_state = 0;
106762306a36Sopenharmony_ci	int ret;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	/*
107062306a36Sopenharmony_ci	 * We can check PM state w/o a lock here because there is no way
107162306a36Sopenharmony_ci	 * PM state can change from reg access valid to no access while this
107262306a36Sopenharmony_ci	 * thread being executed.
107362306a36Sopenharmony_ci	 */
107462306a36Sopenharmony_ci	if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
107562306a36Sopenharmony_ci		/*
107662306a36Sopenharmony_ci		 * We may have a pending event but not allowed to
107762306a36Sopenharmony_ci		 * process it since we are probably in a suspended state,
107862306a36Sopenharmony_ci		 * so trigger a resume.
107962306a36Sopenharmony_ci		 */
108062306a36Sopenharmony_ci		mhi_trigger_resume(mhi_cntrl);
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci		return;
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	/* Process ctrl events */
108662306a36Sopenharmony_ci	ret = mhi_event->process_event(mhi_cntrl, mhi_event, U32_MAX);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	/*
108962306a36Sopenharmony_ci	 * We received an IRQ but no events to process, maybe device went to
109062306a36Sopenharmony_ci	 * SYS_ERR state? Check the state to confirm.
109162306a36Sopenharmony_ci	 */
109262306a36Sopenharmony_ci	if (!ret) {
109362306a36Sopenharmony_ci		write_lock_irq(&mhi_cntrl->pm_lock);
109462306a36Sopenharmony_ci		state = mhi_get_mhi_state(mhi_cntrl);
109562306a36Sopenharmony_ci		if (state == MHI_STATE_SYS_ERR) {
109662306a36Sopenharmony_ci			dev_dbg(dev, "System error detected\n");
109762306a36Sopenharmony_ci			pm_state = mhi_tryset_pm_state(mhi_cntrl,
109862306a36Sopenharmony_ci						       MHI_PM_SYS_ERR_DETECT);
109962306a36Sopenharmony_ci		}
110062306a36Sopenharmony_ci		write_unlock_irq(&mhi_cntrl->pm_lock);
110162306a36Sopenharmony_ci		if (pm_state == MHI_PM_SYS_ERR_DETECT)
110262306a36Sopenharmony_ci			mhi_pm_sys_err_handler(mhi_cntrl);
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_cistatic bool mhi_is_ring_full(struct mhi_controller *mhi_cntrl,
110762306a36Sopenharmony_ci			     struct mhi_ring *ring)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	void *tmp = ring->wp + ring->el_size;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	if (tmp >= (ring->base + ring->len))
111262306a36Sopenharmony_ci		tmp = ring->base;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	return (tmp == ring->rp);
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_cistatic int mhi_queue(struct mhi_device *mhi_dev, struct mhi_buf_info *buf_info,
111862306a36Sopenharmony_ci		     enum dma_data_direction dir, enum mhi_flags mflags)
111962306a36Sopenharmony_ci{
112062306a36Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
112162306a36Sopenharmony_ci	struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan :
112262306a36Sopenharmony_ci							     mhi_dev->dl_chan;
112362306a36Sopenharmony_ci	struct mhi_ring *tre_ring = &mhi_chan->tre_ring;
112462306a36Sopenharmony_ci	unsigned long flags;
112562306a36Sopenharmony_ci	int ret;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)))
112862306a36Sopenharmony_ci		return -EIO;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	ret = mhi_is_ring_full(mhi_cntrl, tre_ring);
113162306a36Sopenharmony_ci	if (unlikely(ret))
113262306a36Sopenharmony_ci		return -EAGAIN;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	ret = mhi_gen_tre(mhi_cntrl, mhi_chan, buf_info, mflags);
113562306a36Sopenharmony_ci	if (unlikely(ret))
113662306a36Sopenharmony_ci		return ret;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	read_lock_irqsave(&mhi_cntrl->pm_lock, flags);
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	/* Packet is queued, take a usage ref to exit M3 if necessary
114162306a36Sopenharmony_ci	 * for host->device buffer, balanced put is done on buffer completion
114262306a36Sopenharmony_ci	 * for device->host buffer, balanced put is after ringing the DB
114362306a36Sopenharmony_ci	 */
114462306a36Sopenharmony_ci	mhi_cntrl->runtime_get(mhi_cntrl);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	/* Assert dev_wake (to exit/prevent M1/M2)*/
114762306a36Sopenharmony_ci	mhi_cntrl->wake_toggle(mhi_cntrl);
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	if (mhi_chan->dir == DMA_TO_DEVICE)
115062306a36Sopenharmony_ci		atomic_inc(&mhi_cntrl->pending_pkts);
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)))
115362306a36Sopenharmony_ci		mhi_ring_chan_db(mhi_cntrl, mhi_chan);
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	if (dir == DMA_FROM_DEVICE)
115662306a36Sopenharmony_ci		mhi_cntrl->runtime_put(mhi_cntrl);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	read_unlock_irqrestore(&mhi_cntrl->pm_lock, flags);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	return ret;
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ciint mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
116462306a36Sopenharmony_ci		  struct sk_buff *skb, size_t len, enum mhi_flags mflags)
116562306a36Sopenharmony_ci{
116662306a36Sopenharmony_ci	struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan :
116762306a36Sopenharmony_ci							     mhi_dev->dl_chan;
116862306a36Sopenharmony_ci	struct mhi_buf_info buf_info = { };
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	buf_info.v_addr = skb->data;
117162306a36Sopenharmony_ci	buf_info.cb_buf = skb;
117262306a36Sopenharmony_ci	buf_info.len = len;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	if (unlikely(mhi_chan->pre_alloc))
117562306a36Sopenharmony_ci		return -EINVAL;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	return mhi_queue(mhi_dev, &buf_info, dir, mflags);
117862306a36Sopenharmony_ci}
117962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_queue_skb);
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ciint mhi_queue_dma(struct mhi_device *mhi_dev, enum dma_data_direction dir,
118262306a36Sopenharmony_ci		  struct mhi_buf *mhi_buf, size_t len, enum mhi_flags mflags)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan :
118562306a36Sopenharmony_ci							     mhi_dev->dl_chan;
118662306a36Sopenharmony_ci	struct mhi_buf_info buf_info = { };
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	buf_info.p_addr = mhi_buf->dma_addr;
118962306a36Sopenharmony_ci	buf_info.cb_buf = mhi_buf;
119062306a36Sopenharmony_ci	buf_info.pre_mapped = true;
119162306a36Sopenharmony_ci	buf_info.len = len;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	if (unlikely(mhi_chan->pre_alloc))
119462306a36Sopenharmony_ci		return -EINVAL;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	return mhi_queue(mhi_dev, &buf_info, dir, mflags);
119762306a36Sopenharmony_ci}
119862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_queue_dma);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ciint mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
120162306a36Sopenharmony_ci			struct mhi_buf_info *info, enum mhi_flags flags)
120262306a36Sopenharmony_ci{
120362306a36Sopenharmony_ci	struct mhi_ring *buf_ring, *tre_ring;
120462306a36Sopenharmony_ci	struct mhi_ring_element *mhi_tre;
120562306a36Sopenharmony_ci	struct mhi_buf_info *buf_info;
120662306a36Sopenharmony_ci	int eot, eob, chain, bei;
120762306a36Sopenharmony_ci	int ret;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	/* Protect accesses for reading and incrementing WP */
121062306a36Sopenharmony_ci	write_lock_bh(&mhi_chan->lock);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	buf_ring = &mhi_chan->buf_ring;
121362306a36Sopenharmony_ci	tre_ring = &mhi_chan->tre_ring;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	buf_info = buf_ring->wp;
121662306a36Sopenharmony_ci	WARN_ON(buf_info->used);
121762306a36Sopenharmony_ci	buf_info->pre_mapped = info->pre_mapped;
121862306a36Sopenharmony_ci	if (info->pre_mapped)
121962306a36Sopenharmony_ci		buf_info->p_addr = info->p_addr;
122062306a36Sopenharmony_ci	else
122162306a36Sopenharmony_ci		buf_info->v_addr = info->v_addr;
122262306a36Sopenharmony_ci	buf_info->cb_buf = info->cb_buf;
122362306a36Sopenharmony_ci	buf_info->wp = tre_ring->wp;
122462306a36Sopenharmony_ci	buf_info->dir = mhi_chan->dir;
122562306a36Sopenharmony_ci	buf_info->len = info->len;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	if (!info->pre_mapped) {
122862306a36Sopenharmony_ci		ret = mhi_cntrl->map_single(mhi_cntrl, buf_info);
122962306a36Sopenharmony_ci		if (ret) {
123062306a36Sopenharmony_ci			write_unlock_bh(&mhi_chan->lock);
123162306a36Sopenharmony_ci			return ret;
123262306a36Sopenharmony_ci		}
123362306a36Sopenharmony_ci	}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	eob = !!(flags & MHI_EOB);
123662306a36Sopenharmony_ci	eot = !!(flags & MHI_EOT);
123762306a36Sopenharmony_ci	chain = !!(flags & MHI_CHAIN);
123862306a36Sopenharmony_ci	bei = !!(mhi_chan->intmod);
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	mhi_tre = tre_ring->wp;
124162306a36Sopenharmony_ci	mhi_tre->ptr = MHI_TRE_DATA_PTR(buf_info->p_addr);
124262306a36Sopenharmony_ci	mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(info->len);
124362306a36Sopenharmony_ci	mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(bei, eot, eob, chain);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	/* increment WP */
124662306a36Sopenharmony_ci	mhi_add_ring_element(mhi_cntrl, tre_ring);
124762306a36Sopenharmony_ci	mhi_add_ring_element(mhi_cntrl, buf_ring);
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	write_unlock_bh(&mhi_chan->lock);
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	return 0;
125262306a36Sopenharmony_ci}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ciint mhi_queue_buf(struct mhi_device *mhi_dev, enum dma_data_direction dir,
125562306a36Sopenharmony_ci		  void *buf, size_t len, enum mhi_flags mflags)
125662306a36Sopenharmony_ci{
125762306a36Sopenharmony_ci	struct mhi_buf_info buf_info = { };
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	buf_info.v_addr = buf;
126062306a36Sopenharmony_ci	buf_info.cb_buf = buf;
126162306a36Sopenharmony_ci	buf_info.len = len;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	return mhi_queue(mhi_dev, &buf_info, dir, mflags);
126462306a36Sopenharmony_ci}
126562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_queue_buf);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_cibool mhi_queue_is_full(struct mhi_device *mhi_dev, enum dma_data_direction dir)
126862306a36Sopenharmony_ci{
126962306a36Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
127062306a36Sopenharmony_ci	struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ?
127162306a36Sopenharmony_ci					mhi_dev->ul_chan : mhi_dev->dl_chan;
127262306a36Sopenharmony_ci	struct mhi_ring *tre_ring = &mhi_chan->tre_ring;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	return mhi_is_ring_full(mhi_cntrl, tre_ring);
127562306a36Sopenharmony_ci}
127662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_queue_is_full);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ciint mhi_send_cmd(struct mhi_controller *mhi_cntrl,
127962306a36Sopenharmony_ci		 struct mhi_chan *mhi_chan,
128062306a36Sopenharmony_ci		 enum mhi_cmd_type cmd)
128162306a36Sopenharmony_ci{
128262306a36Sopenharmony_ci	struct mhi_ring_element *cmd_tre = NULL;
128362306a36Sopenharmony_ci	struct mhi_cmd *mhi_cmd = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING];
128462306a36Sopenharmony_ci	struct mhi_ring *ring = &mhi_cmd->ring;
128562306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
128662306a36Sopenharmony_ci	int chan = 0;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	if (mhi_chan)
128962306a36Sopenharmony_ci		chan = mhi_chan->chan;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	spin_lock_bh(&mhi_cmd->lock);
129262306a36Sopenharmony_ci	if (!get_nr_avail_ring_elements(mhi_cntrl, ring)) {
129362306a36Sopenharmony_ci		spin_unlock_bh(&mhi_cmd->lock);
129462306a36Sopenharmony_ci		return -ENOMEM;
129562306a36Sopenharmony_ci	}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	/* prepare the cmd tre */
129862306a36Sopenharmony_ci	cmd_tre = ring->wp;
129962306a36Sopenharmony_ci	switch (cmd) {
130062306a36Sopenharmony_ci	case MHI_CMD_RESET_CHAN:
130162306a36Sopenharmony_ci		cmd_tre->ptr = MHI_TRE_CMD_RESET_PTR;
130262306a36Sopenharmony_ci		cmd_tre->dword[0] = MHI_TRE_CMD_RESET_DWORD0;
130362306a36Sopenharmony_ci		cmd_tre->dword[1] = MHI_TRE_CMD_RESET_DWORD1(chan);
130462306a36Sopenharmony_ci		break;
130562306a36Sopenharmony_ci	case MHI_CMD_STOP_CHAN:
130662306a36Sopenharmony_ci		cmd_tre->ptr = MHI_TRE_CMD_STOP_PTR;
130762306a36Sopenharmony_ci		cmd_tre->dword[0] = MHI_TRE_CMD_STOP_DWORD0;
130862306a36Sopenharmony_ci		cmd_tre->dword[1] = MHI_TRE_CMD_STOP_DWORD1(chan);
130962306a36Sopenharmony_ci		break;
131062306a36Sopenharmony_ci	case MHI_CMD_START_CHAN:
131162306a36Sopenharmony_ci		cmd_tre->ptr = MHI_TRE_CMD_START_PTR;
131262306a36Sopenharmony_ci		cmd_tre->dword[0] = MHI_TRE_CMD_START_DWORD0;
131362306a36Sopenharmony_ci		cmd_tre->dword[1] = MHI_TRE_CMD_START_DWORD1(chan);
131462306a36Sopenharmony_ci		break;
131562306a36Sopenharmony_ci	default:
131662306a36Sopenharmony_ci		dev_err(dev, "Command not supported\n");
131762306a36Sopenharmony_ci		break;
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	/* queue to hardware */
132162306a36Sopenharmony_ci	mhi_add_ring_element(mhi_cntrl, ring);
132262306a36Sopenharmony_ci	read_lock_bh(&mhi_cntrl->pm_lock);
132362306a36Sopenharmony_ci	if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)))
132462306a36Sopenharmony_ci		mhi_ring_cmd_db(mhi_cntrl, mhi_cmd);
132562306a36Sopenharmony_ci	read_unlock_bh(&mhi_cntrl->pm_lock);
132662306a36Sopenharmony_ci	spin_unlock_bh(&mhi_cmd->lock);
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	return 0;
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_cistatic int mhi_update_channel_state(struct mhi_controller *mhi_cntrl,
133262306a36Sopenharmony_ci				    struct mhi_chan *mhi_chan,
133362306a36Sopenharmony_ci				    enum mhi_ch_state_type to_state)
133462306a36Sopenharmony_ci{
133562306a36Sopenharmony_ci	struct device *dev = &mhi_chan->mhi_dev->dev;
133662306a36Sopenharmony_ci	enum mhi_cmd_type cmd = MHI_CMD_NOP;
133762306a36Sopenharmony_ci	int ret;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	dev_dbg(dev, "%d: Updating channel state to: %s\n", mhi_chan->chan,
134062306a36Sopenharmony_ci		TO_CH_STATE_TYPE_STR(to_state));
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	switch (to_state) {
134362306a36Sopenharmony_ci	case MHI_CH_STATE_TYPE_RESET:
134462306a36Sopenharmony_ci		write_lock_irq(&mhi_chan->lock);
134562306a36Sopenharmony_ci		if (mhi_chan->ch_state != MHI_CH_STATE_STOP &&
134662306a36Sopenharmony_ci		    mhi_chan->ch_state != MHI_CH_STATE_ENABLED &&
134762306a36Sopenharmony_ci		    mhi_chan->ch_state != MHI_CH_STATE_SUSPENDED) {
134862306a36Sopenharmony_ci			write_unlock_irq(&mhi_chan->lock);
134962306a36Sopenharmony_ci			return -EINVAL;
135062306a36Sopenharmony_ci		}
135162306a36Sopenharmony_ci		mhi_chan->ch_state = MHI_CH_STATE_DISABLED;
135262306a36Sopenharmony_ci		write_unlock_irq(&mhi_chan->lock);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci		cmd = MHI_CMD_RESET_CHAN;
135562306a36Sopenharmony_ci		break;
135662306a36Sopenharmony_ci	case MHI_CH_STATE_TYPE_STOP:
135762306a36Sopenharmony_ci		if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED)
135862306a36Sopenharmony_ci			return -EINVAL;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci		cmd = MHI_CMD_STOP_CHAN;
136162306a36Sopenharmony_ci		break;
136262306a36Sopenharmony_ci	case MHI_CH_STATE_TYPE_START:
136362306a36Sopenharmony_ci		if (mhi_chan->ch_state != MHI_CH_STATE_STOP &&
136462306a36Sopenharmony_ci		    mhi_chan->ch_state != MHI_CH_STATE_DISABLED)
136562306a36Sopenharmony_ci			return -EINVAL;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci		cmd = MHI_CMD_START_CHAN;
136862306a36Sopenharmony_ci		break;
136962306a36Sopenharmony_ci	default:
137062306a36Sopenharmony_ci		dev_err(dev, "%d: Channel state update to %s not allowed\n",
137162306a36Sopenharmony_ci			mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state));
137262306a36Sopenharmony_ci		return -EINVAL;
137362306a36Sopenharmony_ci	}
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	/* bring host and device out of suspended states */
137662306a36Sopenharmony_ci	ret = mhi_device_get_sync(mhi_cntrl->mhi_dev);
137762306a36Sopenharmony_ci	if (ret)
137862306a36Sopenharmony_ci		return ret;
137962306a36Sopenharmony_ci	mhi_cntrl->runtime_get(mhi_cntrl);
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	reinit_completion(&mhi_chan->completion);
138262306a36Sopenharmony_ci	ret = mhi_send_cmd(mhi_cntrl, mhi_chan, cmd);
138362306a36Sopenharmony_ci	if (ret) {
138462306a36Sopenharmony_ci		dev_err(dev, "%d: Failed to send %s channel command\n",
138562306a36Sopenharmony_ci			mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state));
138662306a36Sopenharmony_ci		goto exit_channel_update;
138762306a36Sopenharmony_ci	}
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&mhi_chan->completion,
139062306a36Sopenharmony_ci				       msecs_to_jiffies(mhi_cntrl->timeout_ms));
139162306a36Sopenharmony_ci	if (!ret || mhi_chan->ccs != MHI_EV_CC_SUCCESS) {
139262306a36Sopenharmony_ci		dev_err(dev,
139362306a36Sopenharmony_ci			"%d: Failed to receive %s channel command completion\n",
139462306a36Sopenharmony_ci			mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state));
139562306a36Sopenharmony_ci		ret = -EIO;
139662306a36Sopenharmony_ci		goto exit_channel_update;
139762306a36Sopenharmony_ci	}
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	ret = 0;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	if (to_state != MHI_CH_STATE_TYPE_RESET) {
140262306a36Sopenharmony_ci		write_lock_irq(&mhi_chan->lock);
140362306a36Sopenharmony_ci		mhi_chan->ch_state = (to_state == MHI_CH_STATE_TYPE_START) ?
140462306a36Sopenharmony_ci				      MHI_CH_STATE_ENABLED : MHI_CH_STATE_STOP;
140562306a36Sopenharmony_ci		write_unlock_irq(&mhi_chan->lock);
140662306a36Sopenharmony_ci	}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	dev_dbg(dev, "%d: Channel state change to %s successful\n",
140962306a36Sopenharmony_ci		mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state));
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ciexit_channel_update:
141262306a36Sopenharmony_ci	mhi_cntrl->runtime_put(mhi_cntrl);
141362306a36Sopenharmony_ci	mhi_device_put(mhi_cntrl->mhi_dev);
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	return ret;
141662306a36Sopenharmony_ci}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_cistatic void mhi_unprepare_channel(struct mhi_controller *mhi_cntrl,
141962306a36Sopenharmony_ci				  struct mhi_chan *mhi_chan)
142062306a36Sopenharmony_ci{
142162306a36Sopenharmony_ci	int ret;
142262306a36Sopenharmony_ci	struct device *dev = &mhi_chan->mhi_dev->dev;
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	mutex_lock(&mhi_chan->mutex);
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	if (!(BIT(mhi_cntrl->ee) & mhi_chan->ee_mask)) {
142762306a36Sopenharmony_ci		dev_dbg(dev, "Current EE: %s Required EE Mask: 0x%x\n",
142862306a36Sopenharmony_ci			TO_MHI_EXEC_STR(mhi_cntrl->ee), mhi_chan->ee_mask);
142962306a36Sopenharmony_ci		goto exit_unprepare_channel;
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	/* no more processing events for this channel */
143362306a36Sopenharmony_ci	ret = mhi_update_channel_state(mhi_cntrl, mhi_chan,
143462306a36Sopenharmony_ci				       MHI_CH_STATE_TYPE_RESET);
143562306a36Sopenharmony_ci	if (ret)
143662306a36Sopenharmony_ci		dev_err(dev, "%d: Failed to reset channel, still resetting\n",
143762306a36Sopenharmony_ci			mhi_chan->chan);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ciexit_unprepare_channel:
144062306a36Sopenharmony_ci	write_lock_irq(&mhi_chan->lock);
144162306a36Sopenharmony_ci	mhi_chan->ch_state = MHI_CH_STATE_DISABLED;
144262306a36Sopenharmony_ci	write_unlock_irq(&mhi_chan->lock);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	if (!mhi_chan->offload_ch) {
144562306a36Sopenharmony_ci		mhi_reset_chan(mhi_cntrl, mhi_chan);
144662306a36Sopenharmony_ci		mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan);
144762306a36Sopenharmony_ci	}
144862306a36Sopenharmony_ci	dev_dbg(dev, "%d: successfully reset\n", mhi_chan->chan);
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	mutex_unlock(&mhi_chan->mutex);
145162306a36Sopenharmony_ci}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ciint mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
145462306a36Sopenharmony_ci			struct mhi_chan *mhi_chan, unsigned int flags)
145562306a36Sopenharmony_ci{
145662306a36Sopenharmony_ci	int ret = 0;
145762306a36Sopenharmony_ci	struct device *dev = &mhi_chan->mhi_dev->dev;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	if (!(BIT(mhi_cntrl->ee) & mhi_chan->ee_mask)) {
146062306a36Sopenharmony_ci		dev_err(dev, "Current EE: %s Required EE Mask: 0x%x\n",
146162306a36Sopenharmony_ci			TO_MHI_EXEC_STR(mhi_cntrl->ee), mhi_chan->ee_mask);
146262306a36Sopenharmony_ci		return -ENOTCONN;
146362306a36Sopenharmony_ci	}
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	mutex_lock(&mhi_chan->mutex);
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	/* Check of client manages channel context for offload channels */
146862306a36Sopenharmony_ci	if (!mhi_chan->offload_ch) {
146962306a36Sopenharmony_ci		ret = mhi_init_chan_ctxt(mhi_cntrl, mhi_chan);
147062306a36Sopenharmony_ci		if (ret)
147162306a36Sopenharmony_ci			goto error_init_chan;
147262306a36Sopenharmony_ci	}
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	ret = mhi_update_channel_state(mhi_cntrl, mhi_chan,
147562306a36Sopenharmony_ci				       MHI_CH_STATE_TYPE_START);
147662306a36Sopenharmony_ci	if (ret)
147762306a36Sopenharmony_ci		goto error_pm_state;
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	if (mhi_chan->dir == DMA_FROM_DEVICE)
148062306a36Sopenharmony_ci		mhi_chan->pre_alloc = !!(flags & MHI_CH_INBOUND_ALLOC_BUFS);
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	/* Pre-allocate buffer for xfer ring */
148362306a36Sopenharmony_ci	if (mhi_chan->pre_alloc) {
148462306a36Sopenharmony_ci		int nr_el = get_nr_avail_ring_elements(mhi_cntrl,
148562306a36Sopenharmony_ci						       &mhi_chan->tre_ring);
148662306a36Sopenharmony_ci		size_t len = mhi_cntrl->buffer_len;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci		while (nr_el--) {
148962306a36Sopenharmony_ci			void *buf;
149062306a36Sopenharmony_ci			struct mhi_buf_info info = { };
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci			buf = kmalloc(len, GFP_KERNEL);
149362306a36Sopenharmony_ci			if (!buf) {
149462306a36Sopenharmony_ci				ret = -ENOMEM;
149562306a36Sopenharmony_ci				goto error_pre_alloc;
149662306a36Sopenharmony_ci			}
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci			/* Prepare transfer descriptors */
149962306a36Sopenharmony_ci			info.v_addr = buf;
150062306a36Sopenharmony_ci			info.cb_buf = buf;
150162306a36Sopenharmony_ci			info.len = len;
150262306a36Sopenharmony_ci			ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &info, MHI_EOT);
150362306a36Sopenharmony_ci			if (ret) {
150462306a36Sopenharmony_ci				kfree(buf);
150562306a36Sopenharmony_ci				goto error_pre_alloc;
150662306a36Sopenharmony_ci			}
150762306a36Sopenharmony_ci		}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci		read_lock_bh(&mhi_cntrl->pm_lock);
151062306a36Sopenharmony_ci		if (MHI_DB_ACCESS_VALID(mhi_cntrl)) {
151162306a36Sopenharmony_ci			read_lock_irq(&mhi_chan->lock);
151262306a36Sopenharmony_ci			mhi_ring_chan_db(mhi_cntrl, mhi_chan);
151362306a36Sopenharmony_ci			read_unlock_irq(&mhi_chan->lock);
151462306a36Sopenharmony_ci		}
151562306a36Sopenharmony_ci		read_unlock_bh(&mhi_cntrl->pm_lock);
151662306a36Sopenharmony_ci	}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	mutex_unlock(&mhi_chan->mutex);
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	return 0;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_cierror_pm_state:
152362306a36Sopenharmony_ci	if (!mhi_chan->offload_ch)
152462306a36Sopenharmony_ci		mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan);
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_cierror_init_chan:
152762306a36Sopenharmony_ci	mutex_unlock(&mhi_chan->mutex);
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	return ret;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_cierror_pre_alloc:
153262306a36Sopenharmony_ci	mutex_unlock(&mhi_chan->mutex);
153362306a36Sopenharmony_ci	mhi_unprepare_channel(mhi_cntrl, mhi_chan);
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	return ret;
153662306a36Sopenharmony_ci}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_cistatic void mhi_mark_stale_events(struct mhi_controller *mhi_cntrl,
153962306a36Sopenharmony_ci				  struct mhi_event *mhi_event,
154062306a36Sopenharmony_ci				  struct mhi_event_ctxt *er_ctxt,
154162306a36Sopenharmony_ci				  int chan)
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci{
154462306a36Sopenharmony_ci	struct mhi_ring_element *dev_rp, *local_rp;
154562306a36Sopenharmony_ci	struct mhi_ring *ev_ring;
154662306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
154762306a36Sopenharmony_ci	unsigned long flags;
154862306a36Sopenharmony_ci	dma_addr_t ptr;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	dev_dbg(dev, "Marking all events for chan: %d as stale\n", chan);
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	ev_ring = &mhi_event->ring;
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	/* mark all stale events related to channel as STALE event */
155562306a36Sopenharmony_ci	spin_lock_irqsave(&mhi_event->lock, flags);
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	ptr = le64_to_cpu(er_ctxt->rp);
155862306a36Sopenharmony_ci	if (!is_valid_ring_ptr(ev_ring, ptr)) {
155962306a36Sopenharmony_ci		dev_err(&mhi_cntrl->mhi_dev->dev,
156062306a36Sopenharmony_ci			"Event ring rp points outside of the event ring\n");
156162306a36Sopenharmony_ci		dev_rp = ev_ring->rp;
156262306a36Sopenharmony_ci	} else {
156362306a36Sopenharmony_ci		dev_rp = mhi_to_virtual(ev_ring, ptr);
156462306a36Sopenharmony_ci	}
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	local_rp = ev_ring->rp;
156762306a36Sopenharmony_ci	while (dev_rp != local_rp) {
156862306a36Sopenharmony_ci		if (MHI_TRE_GET_EV_TYPE(local_rp) == MHI_PKT_TYPE_TX_EVENT &&
156962306a36Sopenharmony_ci		    chan == MHI_TRE_GET_EV_CHID(local_rp))
157062306a36Sopenharmony_ci			local_rp->dword[1] = MHI_TRE_EV_DWORD1(chan,
157162306a36Sopenharmony_ci					MHI_PKT_TYPE_STALE_EVENT);
157262306a36Sopenharmony_ci		local_rp++;
157362306a36Sopenharmony_ci		if (local_rp == (ev_ring->base + ev_ring->len))
157462306a36Sopenharmony_ci			local_rp = ev_ring->base;
157562306a36Sopenharmony_ci	}
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	dev_dbg(dev, "Finished marking events as stale events\n");
157862306a36Sopenharmony_ci	spin_unlock_irqrestore(&mhi_event->lock, flags);
157962306a36Sopenharmony_ci}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_cistatic void mhi_reset_data_chan(struct mhi_controller *mhi_cntrl,
158262306a36Sopenharmony_ci				struct mhi_chan *mhi_chan)
158362306a36Sopenharmony_ci{
158462306a36Sopenharmony_ci	struct mhi_ring *buf_ring, *tre_ring;
158562306a36Sopenharmony_ci	struct mhi_result result;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	/* Reset any pending buffers */
158862306a36Sopenharmony_ci	buf_ring = &mhi_chan->buf_ring;
158962306a36Sopenharmony_ci	tre_ring = &mhi_chan->tre_ring;
159062306a36Sopenharmony_ci	result.transaction_status = -ENOTCONN;
159162306a36Sopenharmony_ci	result.bytes_xferd = 0;
159262306a36Sopenharmony_ci	while (tre_ring->rp != tre_ring->wp) {
159362306a36Sopenharmony_ci		struct mhi_buf_info *buf_info = buf_ring->rp;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci		if (mhi_chan->dir == DMA_TO_DEVICE) {
159662306a36Sopenharmony_ci			atomic_dec(&mhi_cntrl->pending_pkts);
159762306a36Sopenharmony_ci			/* Release the reference got from mhi_queue() */
159862306a36Sopenharmony_ci			mhi_cntrl->runtime_put(mhi_cntrl);
159962306a36Sopenharmony_ci		}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci		if (!buf_info->pre_mapped)
160262306a36Sopenharmony_ci			mhi_cntrl->unmap_single(mhi_cntrl, buf_info);
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci		mhi_del_ring_element(mhi_cntrl, buf_ring);
160562306a36Sopenharmony_ci		mhi_del_ring_element(mhi_cntrl, tre_ring);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci		if (mhi_chan->pre_alloc) {
160862306a36Sopenharmony_ci			kfree(buf_info->cb_buf);
160962306a36Sopenharmony_ci		} else {
161062306a36Sopenharmony_ci			result.buf_addr = buf_info->cb_buf;
161162306a36Sopenharmony_ci			mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
161262306a36Sopenharmony_ci		}
161362306a36Sopenharmony_ci	}
161462306a36Sopenharmony_ci}
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_civoid mhi_reset_chan(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan)
161762306a36Sopenharmony_ci{
161862306a36Sopenharmony_ci	struct mhi_event *mhi_event;
161962306a36Sopenharmony_ci	struct mhi_event_ctxt *er_ctxt;
162062306a36Sopenharmony_ci	int chan = mhi_chan->chan;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	/* Nothing to reset, client doesn't queue buffers */
162362306a36Sopenharmony_ci	if (mhi_chan->offload_ch)
162462306a36Sopenharmony_ci		return;
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	read_lock_bh(&mhi_cntrl->pm_lock);
162762306a36Sopenharmony_ci	mhi_event = &mhi_cntrl->mhi_event[mhi_chan->er_index];
162862306a36Sopenharmony_ci	er_ctxt = &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_chan->er_index];
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	mhi_mark_stale_events(mhi_cntrl, mhi_event, er_ctxt, chan);
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	mhi_reset_data_chan(mhi_cntrl, mhi_chan);
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	read_unlock_bh(&mhi_cntrl->pm_lock);
163562306a36Sopenharmony_ci}
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_cistatic int __mhi_prepare_for_transfer(struct mhi_device *mhi_dev, unsigned int flags)
163862306a36Sopenharmony_ci{
163962306a36Sopenharmony_ci	int ret, dir;
164062306a36Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
164162306a36Sopenharmony_ci	struct mhi_chan *mhi_chan;
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	for (dir = 0; dir < 2; dir++) {
164462306a36Sopenharmony_ci		mhi_chan = dir ? mhi_dev->dl_chan : mhi_dev->ul_chan;
164562306a36Sopenharmony_ci		if (!mhi_chan)
164662306a36Sopenharmony_ci			continue;
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci		ret = mhi_prepare_channel(mhi_cntrl, mhi_chan, flags);
164962306a36Sopenharmony_ci		if (ret)
165062306a36Sopenharmony_ci			goto error_open_chan;
165162306a36Sopenharmony_ci	}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	return 0;
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_cierror_open_chan:
165662306a36Sopenharmony_ci	for (--dir; dir >= 0; dir--) {
165762306a36Sopenharmony_ci		mhi_chan = dir ? mhi_dev->dl_chan : mhi_dev->ul_chan;
165862306a36Sopenharmony_ci		if (!mhi_chan)
165962306a36Sopenharmony_ci			continue;
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci		mhi_unprepare_channel(mhi_cntrl, mhi_chan);
166262306a36Sopenharmony_ci	}
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	return ret;
166562306a36Sopenharmony_ci}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ciint mhi_prepare_for_transfer(struct mhi_device *mhi_dev)
166862306a36Sopenharmony_ci{
166962306a36Sopenharmony_ci	return __mhi_prepare_for_transfer(mhi_dev, 0);
167062306a36Sopenharmony_ci}
167162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_prepare_for_transfer);
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ciint mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev)
167462306a36Sopenharmony_ci{
167562306a36Sopenharmony_ci	return __mhi_prepare_for_transfer(mhi_dev, MHI_CH_INBOUND_ALLOC_BUFS);
167662306a36Sopenharmony_ci}
167762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_prepare_for_transfer_autoqueue);
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_civoid mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
168062306a36Sopenharmony_ci{
168162306a36Sopenharmony_ci	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
168262306a36Sopenharmony_ci	struct mhi_chan *mhi_chan;
168362306a36Sopenharmony_ci	int dir;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	for (dir = 0; dir < 2; dir++) {
168662306a36Sopenharmony_ci		mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
168762306a36Sopenharmony_ci		if (!mhi_chan)
168862306a36Sopenharmony_ci			continue;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci		mhi_unprepare_channel(mhi_cntrl, mhi_chan);
169162306a36Sopenharmony_ci	}
169262306a36Sopenharmony_ci}
169362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
1694