xref: /kernel/linux/linux-6.6/drivers/bus/mhi/ep/main.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * MHI Endpoint bus stack
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2022 Linaro Ltd.
662306a36Sopenharmony_ci * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/bitfield.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/dma-direction.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/irq.h>
1562306a36Sopenharmony_ci#include <linux/mhi_ep.h>
1662306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include "internal.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define M0_WAIT_DELAY_MS	100
2162306a36Sopenharmony_ci#define M0_WAIT_COUNT		100
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic DEFINE_IDA(mhi_ep_cntrl_ida);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id);
2662306a36Sopenharmony_cistatic int mhi_ep_destroy_device(struct device *dev, void *data);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic int mhi_ep_send_event(struct mhi_ep_cntrl *mhi_cntrl, u32 ring_idx,
2962306a36Sopenharmony_ci			     struct mhi_ring_element *el, bool bei)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
3262306a36Sopenharmony_ci	union mhi_ep_ring_ctx *ctx;
3362306a36Sopenharmony_ci	struct mhi_ep_ring *ring;
3462306a36Sopenharmony_ci	int ret;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	mutex_lock(&mhi_cntrl->event_lock);
3762306a36Sopenharmony_ci	ring = &mhi_cntrl->mhi_event[ring_idx].ring;
3862306a36Sopenharmony_ci	ctx = (union mhi_ep_ring_ctx *)&mhi_cntrl->ev_ctx_cache[ring_idx];
3962306a36Sopenharmony_ci	if (!ring->started) {
4062306a36Sopenharmony_ci		ret = mhi_ep_ring_start(mhi_cntrl, ring, ctx);
4162306a36Sopenharmony_ci		if (ret) {
4262306a36Sopenharmony_ci			dev_err(dev, "Error starting event ring (%u)\n", ring_idx);
4362306a36Sopenharmony_ci			goto err_unlock;
4462306a36Sopenharmony_ci		}
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	/* Add element to the event ring */
4862306a36Sopenharmony_ci	ret = mhi_ep_ring_add_element(ring, el);
4962306a36Sopenharmony_ci	if (ret) {
5062306a36Sopenharmony_ci		dev_err(dev, "Error adding element to event ring (%u)\n", ring_idx);
5162306a36Sopenharmony_ci		goto err_unlock;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	mutex_unlock(&mhi_cntrl->event_lock);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/*
5762306a36Sopenharmony_ci	 * Raise IRQ to host only if the BEI flag is not set in TRE. Host might
5862306a36Sopenharmony_ci	 * set this flag for interrupt moderation as per MHI protocol.
5962306a36Sopenharmony_ci	 */
6062306a36Sopenharmony_ci	if (!bei)
6162306a36Sopenharmony_ci		mhi_cntrl->raise_irq(mhi_cntrl, ring->irq_vector);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return 0;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cierr_unlock:
6662306a36Sopenharmony_ci	mutex_unlock(&mhi_cntrl->event_lock);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return ret;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic int mhi_ep_send_completion_event(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring,
7262306a36Sopenharmony_ci					struct mhi_ring_element *tre, u32 len, enum mhi_ev_ccs code)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct mhi_ring_element *event;
7562306a36Sopenharmony_ci	int ret;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL | GFP_DMA);
7862306a36Sopenharmony_ci	if (!event)
7962306a36Sopenharmony_ci		return -ENOMEM;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	event->ptr = cpu_to_le64(ring->rbase + ring->rd_offset * sizeof(*tre));
8262306a36Sopenharmony_ci	event->dword[0] = MHI_TRE_EV_DWORD0(code, len);
8362306a36Sopenharmony_ci	event->dword[1] = MHI_TRE_EV_DWORD1(ring->ch_id, MHI_PKT_TYPE_TX_EVENT);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	ret = mhi_ep_send_event(mhi_cntrl, ring->er_index, event, MHI_TRE_DATA_GET_BEI(tre));
8662306a36Sopenharmony_ci	kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return ret;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ciint mhi_ep_send_state_change_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state state)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct mhi_ring_element *event;
9462306a36Sopenharmony_ci	int ret;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL | GFP_DMA);
9762306a36Sopenharmony_ci	if (!event)
9862306a36Sopenharmony_ci		return -ENOMEM;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	event->dword[0] = MHI_SC_EV_DWORD0(state);
10162306a36Sopenharmony_ci	event->dword[1] = MHI_SC_EV_DWORD1(MHI_PKT_TYPE_STATE_CHANGE_EVENT);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0);
10462306a36Sopenharmony_ci	kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return ret;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ciint mhi_ep_send_ee_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_ee_type exec_env)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	struct mhi_ring_element *event;
11262306a36Sopenharmony_ci	int ret;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL | GFP_DMA);
11562306a36Sopenharmony_ci	if (!event)
11662306a36Sopenharmony_ci		return -ENOMEM;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	event->dword[0] = MHI_EE_EV_DWORD0(exec_env);
11962306a36Sopenharmony_ci	event->dword[1] = MHI_SC_EV_DWORD1(MHI_PKT_TYPE_EE_EVENT);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0);
12262306a36Sopenharmony_ci	kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return ret;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic int mhi_ep_send_cmd_comp_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_ev_ccs code)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct mhi_ep_ring *ring = &mhi_cntrl->mhi_cmd->ring;
13062306a36Sopenharmony_ci	struct mhi_ring_element *event;
13162306a36Sopenharmony_ci	int ret;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL | GFP_DMA);
13462306a36Sopenharmony_ci	if (!event)
13562306a36Sopenharmony_ci		return -ENOMEM;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	event->ptr = cpu_to_le64(ring->rbase + ring->rd_offset * sizeof(struct mhi_ring_element));
13862306a36Sopenharmony_ci	event->dword[0] = MHI_CC_EV_DWORD0(code);
13962306a36Sopenharmony_ci	event->dword[1] = MHI_CC_EV_DWORD1(MHI_PKT_TYPE_CMD_COMPLETION_EVENT);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0);
14262306a36Sopenharmony_ci	kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return ret;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_element *el)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
15062306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
15162306a36Sopenharmony_ci	struct mhi_result result = {};
15262306a36Sopenharmony_ci	struct mhi_ep_chan *mhi_chan;
15362306a36Sopenharmony_ci	struct mhi_ep_ring *ch_ring;
15462306a36Sopenharmony_ci	u32 tmp, ch_id;
15562306a36Sopenharmony_ci	int ret;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	ch_id = MHI_TRE_GET_CMD_CHID(el);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/* Check if the channel is supported by the controller */
16062306a36Sopenharmony_ci	if ((ch_id >= mhi_cntrl->max_chan) || !mhi_cntrl->mhi_chan[ch_id].name) {
16162306a36Sopenharmony_ci		dev_dbg(dev, "Channel (%u) not supported!\n", ch_id);
16262306a36Sopenharmony_ci		return -ENODEV;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	mhi_chan = &mhi_cntrl->mhi_chan[ch_id];
16662306a36Sopenharmony_ci	ch_ring = &mhi_cntrl->mhi_chan[ch_id].ring;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	switch (MHI_TRE_GET_CMD_TYPE(el)) {
16962306a36Sopenharmony_ci	case MHI_PKT_TYPE_START_CHAN_CMD:
17062306a36Sopenharmony_ci		dev_dbg(dev, "Received START command for channel (%u)\n", ch_id);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci		mutex_lock(&mhi_chan->lock);
17362306a36Sopenharmony_ci		/* Initialize and configure the corresponding channel ring */
17462306a36Sopenharmony_ci		if (!ch_ring->started) {
17562306a36Sopenharmony_ci			ret = mhi_ep_ring_start(mhi_cntrl, ch_ring,
17662306a36Sopenharmony_ci				(union mhi_ep_ring_ctx *)&mhi_cntrl->ch_ctx_cache[ch_id]);
17762306a36Sopenharmony_ci			if (ret) {
17862306a36Sopenharmony_ci				dev_err(dev, "Failed to start ring for channel (%u)\n", ch_id);
17962306a36Sopenharmony_ci				ret = mhi_ep_send_cmd_comp_event(mhi_cntrl,
18062306a36Sopenharmony_ci							MHI_EV_CC_UNDEFINED_ERR);
18162306a36Sopenharmony_ci				if (ret)
18262306a36Sopenharmony_ci					dev_err(dev, "Error sending completion event: %d\n", ret);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci				goto err_unlock;
18562306a36Sopenharmony_ci			}
18662306a36Sopenharmony_ci		}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci		/* Set channel state to RUNNING */
18962306a36Sopenharmony_ci		mhi_chan->state = MHI_CH_STATE_RUNNING;
19062306a36Sopenharmony_ci		tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg);
19162306a36Sopenharmony_ci		tmp &= ~CHAN_CTX_CHSTATE_MASK;
19262306a36Sopenharmony_ci		tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_RUNNING);
19362306a36Sopenharmony_ci		mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS);
19662306a36Sopenharmony_ci		if (ret) {
19762306a36Sopenharmony_ci			dev_err(dev, "Error sending command completion event (%u)\n",
19862306a36Sopenharmony_ci				MHI_EV_CC_SUCCESS);
19962306a36Sopenharmony_ci			goto err_unlock;
20062306a36Sopenharmony_ci		}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		mutex_unlock(&mhi_chan->lock);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci		/*
20562306a36Sopenharmony_ci		 * Create MHI device only during UL channel start. Since the MHI
20662306a36Sopenharmony_ci		 * channels operate in a pair, we'll associate both UL and DL
20762306a36Sopenharmony_ci		 * channels to the same device.
20862306a36Sopenharmony_ci		 *
20962306a36Sopenharmony_ci		 * We also need to check for mhi_dev != NULL because, the host
21062306a36Sopenharmony_ci		 * will issue START_CHAN command during resume and we don't
21162306a36Sopenharmony_ci		 * destroy the device during suspend.
21262306a36Sopenharmony_ci		 */
21362306a36Sopenharmony_ci		if (!(ch_id % 2) && !mhi_chan->mhi_dev) {
21462306a36Sopenharmony_ci			ret = mhi_ep_create_device(mhi_cntrl, ch_id);
21562306a36Sopenharmony_ci			if (ret) {
21662306a36Sopenharmony_ci				dev_err(dev, "Error creating device for channel (%u)\n", ch_id);
21762306a36Sopenharmony_ci				mhi_ep_handle_syserr(mhi_cntrl);
21862306a36Sopenharmony_ci				return ret;
21962306a36Sopenharmony_ci			}
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		/* Finally, enable DB for the channel */
22362306a36Sopenharmony_ci		mhi_ep_mmio_enable_chdb(mhi_cntrl, ch_id);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		break;
22662306a36Sopenharmony_ci	case MHI_PKT_TYPE_STOP_CHAN_CMD:
22762306a36Sopenharmony_ci		dev_dbg(dev, "Received STOP command for channel (%u)\n", ch_id);
22862306a36Sopenharmony_ci		if (!ch_ring->started) {
22962306a36Sopenharmony_ci			dev_err(dev, "Channel (%u) not opened\n", ch_id);
23062306a36Sopenharmony_ci			return -ENODEV;
23162306a36Sopenharmony_ci		}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		mutex_lock(&mhi_chan->lock);
23462306a36Sopenharmony_ci		/* Disable DB for the channel */
23562306a36Sopenharmony_ci		mhi_ep_mmio_disable_chdb(mhi_cntrl, ch_id);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		/* Send channel disconnect status to client drivers */
23862306a36Sopenharmony_ci		if (mhi_chan->xfer_cb) {
23962306a36Sopenharmony_ci			result.transaction_status = -ENOTCONN;
24062306a36Sopenharmony_ci			result.bytes_xferd = 0;
24162306a36Sopenharmony_ci			mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
24262306a36Sopenharmony_ci		}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci		/* Set channel state to STOP */
24562306a36Sopenharmony_ci		mhi_chan->state = MHI_CH_STATE_STOP;
24662306a36Sopenharmony_ci		tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg);
24762306a36Sopenharmony_ci		tmp &= ~CHAN_CTX_CHSTATE_MASK;
24862306a36Sopenharmony_ci		tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_STOP);
24962306a36Sopenharmony_ci		mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci		ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS);
25262306a36Sopenharmony_ci		if (ret) {
25362306a36Sopenharmony_ci			dev_err(dev, "Error sending command completion event (%u)\n",
25462306a36Sopenharmony_ci				MHI_EV_CC_SUCCESS);
25562306a36Sopenharmony_ci			goto err_unlock;
25662306a36Sopenharmony_ci		}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci		mutex_unlock(&mhi_chan->lock);
25962306a36Sopenharmony_ci		break;
26062306a36Sopenharmony_ci	case MHI_PKT_TYPE_RESET_CHAN_CMD:
26162306a36Sopenharmony_ci		dev_dbg(dev, "Received RESET command for channel (%u)\n", ch_id);
26262306a36Sopenharmony_ci		if (!ch_ring->started) {
26362306a36Sopenharmony_ci			dev_err(dev, "Channel (%u) not opened\n", ch_id);
26462306a36Sopenharmony_ci			return -ENODEV;
26562306a36Sopenharmony_ci		}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		mutex_lock(&mhi_chan->lock);
26862306a36Sopenharmony_ci		/* Stop and reset the transfer ring */
26962306a36Sopenharmony_ci		mhi_ep_ring_reset(mhi_cntrl, ch_ring);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		/* Send channel disconnect status to client driver */
27262306a36Sopenharmony_ci		if (mhi_chan->xfer_cb) {
27362306a36Sopenharmony_ci			result.transaction_status = -ENOTCONN;
27462306a36Sopenharmony_ci			result.bytes_xferd = 0;
27562306a36Sopenharmony_ci			mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
27662306a36Sopenharmony_ci		}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		/* Set channel state to DISABLED */
27962306a36Sopenharmony_ci		mhi_chan->state = MHI_CH_STATE_DISABLED;
28062306a36Sopenharmony_ci		tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg);
28162306a36Sopenharmony_ci		tmp &= ~CHAN_CTX_CHSTATE_MASK;
28262306a36Sopenharmony_ci		tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_DISABLED);
28362306a36Sopenharmony_ci		mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS);
28662306a36Sopenharmony_ci		if (ret) {
28762306a36Sopenharmony_ci			dev_err(dev, "Error sending command completion event (%u)\n",
28862306a36Sopenharmony_ci				MHI_EV_CC_SUCCESS);
28962306a36Sopenharmony_ci			goto err_unlock;
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		mutex_unlock(&mhi_chan->lock);
29362306a36Sopenharmony_ci		break;
29462306a36Sopenharmony_ci	default:
29562306a36Sopenharmony_ci		dev_err(dev, "Invalid command received: %lu for channel (%u)\n",
29662306a36Sopenharmony_ci			MHI_TRE_GET_CMD_TYPE(el), ch_id);
29762306a36Sopenharmony_ci		return -EINVAL;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return 0;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cierr_unlock:
30362306a36Sopenharmony_ci	mutex_unlock(&mhi_chan->lock);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	return ret;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cibool mhi_ep_queue_is_empty(struct mhi_ep_device *mhi_dev, enum dma_data_direction dir)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct mhi_ep_chan *mhi_chan = (dir == DMA_FROM_DEVICE) ? mhi_dev->dl_chan :
31162306a36Sopenharmony_ci								mhi_dev->ul_chan;
31262306a36Sopenharmony_ci	struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
31362306a36Sopenharmony_ci	struct mhi_ep_ring *ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return !!(ring->rd_offset == ring->wr_offset);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_ep_queue_is_empty);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl,
32062306a36Sopenharmony_ci				struct mhi_ep_ring *ring,
32162306a36Sopenharmony_ci				struct mhi_result *result,
32262306a36Sopenharmony_ci				u32 len)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct mhi_ep_chan *mhi_chan = &mhi_cntrl->mhi_chan[ring->ch_id];
32562306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
32662306a36Sopenharmony_ci	size_t tr_len, read_offset, write_offset;
32762306a36Sopenharmony_ci	struct mhi_ep_buf_info buf_info = {};
32862306a36Sopenharmony_ci	struct mhi_ring_element *el;
32962306a36Sopenharmony_ci	bool tr_done = false;
33062306a36Sopenharmony_ci	u32 buf_left;
33162306a36Sopenharmony_ci	int ret;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	buf_left = len;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	do {
33662306a36Sopenharmony_ci		/* Don't process the transfer ring if the channel is not in RUNNING state */
33762306a36Sopenharmony_ci		if (mhi_chan->state != MHI_CH_STATE_RUNNING) {
33862306a36Sopenharmony_ci			dev_err(dev, "Channel not available\n");
33962306a36Sopenharmony_ci			return -ENODEV;
34062306a36Sopenharmony_ci		}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci		el = &ring->ring_cache[ring->rd_offset];
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		/* Check if there is data pending to be read from previous read operation */
34562306a36Sopenharmony_ci		if (mhi_chan->tre_bytes_left) {
34662306a36Sopenharmony_ci			dev_dbg(dev, "TRE bytes remaining: %u\n", mhi_chan->tre_bytes_left);
34762306a36Sopenharmony_ci			tr_len = min(buf_left, mhi_chan->tre_bytes_left);
34862306a36Sopenharmony_ci		} else {
34962306a36Sopenharmony_ci			mhi_chan->tre_loc = MHI_TRE_DATA_GET_PTR(el);
35062306a36Sopenharmony_ci			mhi_chan->tre_size = MHI_TRE_DATA_GET_LEN(el);
35162306a36Sopenharmony_ci			mhi_chan->tre_bytes_left = mhi_chan->tre_size;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci			tr_len = min(buf_left, mhi_chan->tre_size);
35462306a36Sopenharmony_ci		}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		read_offset = mhi_chan->tre_size - mhi_chan->tre_bytes_left;
35762306a36Sopenharmony_ci		write_offset = len - buf_left;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		buf_info.host_addr = mhi_chan->tre_loc + read_offset;
36062306a36Sopenharmony_ci		buf_info.dev_addr = result->buf_addr + write_offset;
36162306a36Sopenharmony_ci		buf_info.size = tr_len;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci		dev_dbg(dev, "Reading %zd bytes from channel (%u)\n", tr_len, ring->ch_id);
36462306a36Sopenharmony_ci		ret = mhi_cntrl->read_from_host(mhi_cntrl, &buf_info);
36562306a36Sopenharmony_ci		if (ret < 0) {
36662306a36Sopenharmony_ci			dev_err(&mhi_chan->mhi_dev->dev, "Error reading from channel\n");
36762306a36Sopenharmony_ci			return ret;
36862306a36Sopenharmony_ci		}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		buf_left -= tr_len;
37162306a36Sopenharmony_ci		mhi_chan->tre_bytes_left -= tr_len;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		/*
37462306a36Sopenharmony_ci		 * Once the TRE (Transfer Ring Element) of a TD (Transfer Descriptor) has been
37562306a36Sopenharmony_ci		 * read completely:
37662306a36Sopenharmony_ci		 *
37762306a36Sopenharmony_ci		 * 1. Send completion event to the host based on the flags set in TRE.
37862306a36Sopenharmony_ci		 * 2. Increment the local read offset of the transfer ring.
37962306a36Sopenharmony_ci		 */
38062306a36Sopenharmony_ci		if (!mhi_chan->tre_bytes_left) {
38162306a36Sopenharmony_ci			/*
38262306a36Sopenharmony_ci			 * The host will split the data packet into multiple TREs if it can't fit
38362306a36Sopenharmony_ci			 * the packet in a single TRE. In that case, CHAIN flag will be set by the
38462306a36Sopenharmony_ci			 * host for all TREs except the last one.
38562306a36Sopenharmony_ci			 */
38662306a36Sopenharmony_ci			if (MHI_TRE_DATA_GET_CHAIN(el)) {
38762306a36Sopenharmony_ci				/*
38862306a36Sopenharmony_ci				 * IEOB (Interrupt on End of Block) flag will be set by the host if
38962306a36Sopenharmony_ci				 * it expects the completion event for all TREs of a TD.
39062306a36Sopenharmony_ci				 */
39162306a36Sopenharmony_ci				if (MHI_TRE_DATA_GET_IEOB(el)) {
39262306a36Sopenharmony_ci					ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el,
39362306a36Sopenharmony_ci								     MHI_TRE_DATA_GET_LEN(el),
39462306a36Sopenharmony_ci								     MHI_EV_CC_EOB);
39562306a36Sopenharmony_ci					if (ret < 0) {
39662306a36Sopenharmony_ci						dev_err(&mhi_chan->mhi_dev->dev,
39762306a36Sopenharmony_ci							"Error sending transfer compl. event\n");
39862306a36Sopenharmony_ci						return ret;
39962306a36Sopenharmony_ci					}
40062306a36Sopenharmony_ci				}
40162306a36Sopenharmony_ci			} else {
40262306a36Sopenharmony_ci				/*
40362306a36Sopenharmony_ci				 * IEOT (Interrupt on End of Transfer) flag will be set by the host
40462306a36Sopenharmony_ci				 * for the last TRE of the TD and expects the completion event for
40562306a36Sopenharmony_ci				 * the same.
40662306a36Sopenharmony_ci				 */
40762306a36Sopenharmony_ci				if (MHI_TRE_DATA_GET_IEOT(el)) {
40862306a36Sopenharmony_ci					ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el,
40962306a36Sopenharmony_ci								     MHI_TRE_DATA_GET_LEN(el),
41062306a36Sopenharmony_ci								     MHI_EV_CC_EOT);
41162306a36Sopenharmony_ci					if (ret < 0) {
41262306a36Sopenharmony_ci						dev_err(&mhi_chan->mhi_dev->dev,
41362306a36Sopenharmony_ci							"Error sending transfer compl. event\n");
41462306a36Sopenharmony_ci						return ret;
41562306a36Sopenharmony_ci					}
41662306a36Sopenharmony_ci				}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci				tr_done = true;
41962306a36Sopenharmony_ci			}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci			mhi_ep_ring_inc_index(ring);
42262306a36Sopenharmony_ci		}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		result->bytes_xferd += tr_len;
42562306a36Sopenharmony_ci	} while (buf_left && !tr_done);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	return 0;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic int mhi_ep_process_ch_ring(struct mhi_ep_ring *ring, struct mhi_ring_element *el)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
43362306a36Sopenharmony_ci	struct mhi_result result = {};
43462306a36Sopenharmony_ci	u32 len = MHI_EP_DEFAULT_MTU;
43562306a36Sopenharmony_ci	struct mhi_ep_chan *mhi_chan;
43662306a36Sopenharmony_ci	int ret;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	mhi_chan = &mhi_cntrl->mhi_chan[ring->ch_id];
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/*
44162306a36Sopenharmony_ci	 * Bail out if transfer callback is not registered for the channel.
44262306a36Sopenharmony_ci	 * This is most likely due to the client driver not loaded at this point.
44362306a36Sopenharmony_ci	 */
44462306a36Sopenharmony_ci	if (!mhi_chan->xfer_cb) {
44562306a36Sopenharmony_ci		dev_err(&mhi_chan->mhi_dev->dev, "Client driver not available\n");
44662306a36Sopenharmony_ci		return -ENODEV;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (ring->ch_id % 2) {
45062306a36Sopenharmony_ci		/* DL channel */
45162306a36Sopenharmony_ci		result.dir = mhi_chan->dir;
45262306a36Sopenharmony_ci		mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
45362306a36Sopenharmony_ci	} else {
45462306a36Sopenharmony_ci		/* UL channel */
45562306a36Sopenharmony_ci		result.buf_addr = kmem_cache_zalloc(mhi_cntrl->tre_buf_cache, GFP_KERNEL | GFP_DMA);
45662306a36Sopenharmony_ci		if (!result.buf_addr)
45762306a36Sopenharmony_ci			return -ENOMEM;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci		do {
46062306a36Sopenharmony_ci			ret = mhi_ep_read_channel(mhi_cntrl, ring, &result, len);
46162306a36Sopenharmony_ci			if (ret < 0) {
46262306a36Sopenharmony_ci				dev_err(&mhi_chan->mhi_dev->dev, "Failed to read channel\n");
46362306a36Sopenharmony_ci				kmem_cache_free(mhi_cntrl->tre_buf_cache, result.buf_addr);
46462306a36Sopenharmony_ci				return ret;
46562306a36Sopenharmony_ci			}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci			result.dir = mhi_chan->dir;
46862306a36Sopenharmony_ci			mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
46962306a36Sopenharmony_ci			result.bytes_xferd = 0;
47062306a36Sopenharmony_ci			memset(result.buf_addr, 0, len);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci			/* Read until the ring becomes empty */
47362306a36Sopenharmony_ci		} while (!mhi_ep_queue_is_empty(mhi_chan->mhi_dev, DMA_TO_DEVICE));
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		kmem_cache_free(mhi_cntrl->tre_buf_cache, result.buf_addr);
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	return 0;
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci/* TODO: Handle partially formed TDs */
48262306a36Sopenharmony_ciint mhi_ep_queue_skb(struct mhi_ep_device *mhi_dev, struct sk_buff *skb)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
48562306a36Sopenharmony_ci	struct mhi_ep_chan *mhi_chan = mhi_dev->dl_chan;
48662306a36Sopenharmony_ci	struct device *dev = &mhi_chan->mhi_dev->dev;
48762306a36Sopenharmony_ci	struct mhi_ep_buf_info buf_info = {};
48862306a36Sopenharmony_ci	struct mhi_ring_element *el;
48962306a36Sopenharmony_ci	u32 buf_left, read_offset;
49062306a36Sopenharmony_ci	struct mhi_ep_ring *ring;
49162306a36Sopenharmony_ci	enum mhi_ev_ccs code;
49262306a36Sopenharmony_ci	size_t tr_len;
49362306a36Sopenharmony_ci	u32 tre_len;
49462306a36Sopenharmony_ci	int ret;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	buf_left = skb->len;
49762306a36Sopenharmony_ci	ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	mutex_lock(&mhi_chan->lock);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	do {
50262306a36Sopenharmony_ci		/* Don't process the transfer ring if the channel is not in RUNNING state */
50362306a36Sopenharmony_ci		if (mhi_chan->state != MHI_CH_STATE_RUNNING) {
50462306a36Sopenharmony_ci			dev_err(dev, "Channel not available\n");
50562306a36Sopenharmony_ci			ret = -ENODEV;
50662306a36Sopenharmony_ci			goto err_exit;
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		if (mhi_ep_queue_is_empty(mhi_dev, DMA_FROM_DEVICE)) {
51062306a36Sopenharmony_ci			dev_err(dev, "TRE not available!\n");
51162306a36Sopenharmony_ci			ret = -ENOSPC;
51262306a36Sopenharmony_ci			goto err_exit;
51362306a36Sopenharmony_ci		}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		el = &ring->ring_cache[ring->rd_offset];
51662306a36Sopenharmony_ci		tre_len = MHI_TRE_DATA_GET_LEN(el);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci		tr_len = min(buf_left, tre_len);
51962306a36Sopenharmony_ci		read_offset = skb->len - buf_left;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		buf_info.dev_addr = skb->data + read_offset;
52262306a36Sopenharmony_ci		buf_info.host_addr = MHI_TRE_DATA_GET_PTR(el);
52362306a36Sopenharmony_ci		buf_info.size = tr_len;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci		dev_dbg(dev, "Writing %zd bytes to channel (%u)\n", tr_len, ring->ch_id);
52662306a36Sopenharmony_ci		ret = mhi_cntrl->write_to_host(mhi_cntrl, &buf_info);
52762306a36Sopenharmony_ci		if (ret < 0) {
52862306a36Sopenharmony_ci			dev_err(dev, "Error writing to the channel\n");
52962306a36Sopenharmony_ci			goto err_exit;
53062306a36Sopenharmony_ci		}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		buf_left -= tr_len;
53362306a36Sopenharmony_ci		/*
53462306a36Sopenharmony_ci		 * For all TREs queued by the host for DL channel, only the EOT flag will be set.
53562306a36Sopenharmony_ci		 * If the packet doesn't fit into a single TRE, send the OVERFLOW event to
53662306a36Sopenharmony_ci		 * the host so that the host can adjust the packet boundary to next TREs. Else send
53762306a36Sopenharmony_ci		 * the EOT event to the host indicating the packet boundary.
53862306a36Sopenharmony_ci		 */
53962306a36Sopenharmony_ci		if (buf_left)
54062306a36Sopenharmony_ci			code = MHI_EV_CC_OVERFLOW;
54162306a36Sopenharmony_ci		else
54262306a36Sopenharmony_ci			code = MHI_EV_CC_EOT;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci		ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, tr_len, code);
54562306a36Sopenharmony_ci		if (ret) {
54662306a36Sopenharmony_ci			dev_err(dev, "Error sending transfer completion event\n");
54762306a36Sopenharmony_ci			goto err_exit;
54862306a36Sopenharmony_ci		}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		mhi_ep_ring_inc_index(ring);
55162306a36Sopenharmony_ci	} while (buf_left);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	mutex_unlock(&mhi_chan->lock);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	return 0;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cierr_exit:
55862306a36Sopenharmony_ci	mutex_unlock(&mhi_chan->lock);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return ret;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_ep_queue_skb);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic int mhi_ep_cache_host_cfg(struct mhi_ep_cntrl *mhi_cntrl)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	size_t cmd_ctx_host_size, ch_ctx_host_size, ev_ctx_host_size;
56762306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
56862306a36Sopenharmony_ci	int ret;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	/* Update the number of event rings (NER) programmed by the host */
57162306a36Sopenharmony_ci	mhi_ep_mmio_update_ner(mhi_cntrl);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	dev_dbg(dev, "Number of Event rings: %u, HW Event rings: %u\n",
57462306a36Sopenharmony_ci		 mhi_cntrl->event_rings, mhi_cntrl->hw_event_rings);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	ch_ctx_host_size = sizeof(struct mhi_chan_ctxt) * mhi_cntrl->max_chan;
57762306a36Sopenharmony_ci	ev_ctx_host_size = sizeof(struct mhi_event_ctxt) * mhi_cntrl->event_rings;
57862306a36Sopenharmony_ci	cmd_ctx_host_size = sizeof(struct mhi_cmd_ctxt) * NR_OF_CMD_RINGS;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	/* Get the channel context base pointer from host */
58162306a36Sopenharmony_ci	mhi_ep_mmio_get_chc_base(mhi_cntrl);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* Allocate and map memory for caching host channel context */
58462306a36Sopenharmony_ci	ret = mhi_cntrl->alloc_map(mhi_cntrl, mhi_cntrl->ch_ctx_host_pa,
58562306a36Sopenharmony_ci				   &mhi_cntrl->ch_ctx_cache_phys,
58662306a36Sopenharmony_ci				   (void __iomem **) &mhi_cntrl->ch_ctx_cache,
58762306a36Sopenharmony_ci				   ch_ctx_host_size);
58862306a36Sopenharmony_ci	if (ret) {
58962306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate and map ch_ctx_cache\n");
59062306a36Sopenharmony_ci		return ret;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	/* Get the event context base pointer from host */
59462306a36Sopenharmony_ci	mhi_ep_mmio_get_erc_base(mhi_cntrl);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	/* Allocate and map memory for caching host event context */
59762306a36Sopenharmony_ci	ret = mhi_cntrl->alloc_map(mhi_cntrl, mhi_cntrl->ev_ctx_host_pa,
59862306a36Sopenharmony_ci				   &mhi_cntrl->ev_ctx_cache_phys,
59962306a36Sopenharmony_ci				   (void __iomem **) &mhi_cntrl->ev_ctx_cache,
60062306a36Sopenharmony_ci				   ev_ctx_host_size);
60162306a36Sopenharmony_ci	if (ret) {
60262306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate and map ev_ctx_cache\n");
60362306a36Sopenharmony_ci		goto err_ch_ctx;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	/* Get the command context base pointer from host */
60762306a36Sopenharmony_ci	mhi_ep_mmio_get_crc_base(mhi_cntrl);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	/* Allocate and map memory for caching host command context */
61062306a36Sopenharmony_ci	ret = mhi_cntrl->alloc_map(mhi_cntrl, mhi_cntrl->cmd_ctx_host_pa,
61162306a36Sopenharmony_ci				   &mhi_cntrl->cmd_ctx_cache_phys,
61262306a36Sopenharmony_ci				   (void __iomem **) &mhi_cntrl->cmd_ctx_cache,
61362306a36Sopenharmony_ci				   cmd_ctx_host_size);
61462306a36Sopenharmony_ci	if (ret) {
61562306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate and map cmd_ctx_cache\n");
61662306a36Sopenharmony_ci		goto err_ev_ctx;
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	/* Initialize command ring */
62062306a36Sopenharmony_ci	ret = mhi_ep_ring_start(mhi_cntrl, &mhi_cntrl->mhi_cmd->ring,
62162306a36Sopenharmony_ci				(union mhi_ep_ring_ctx *)mhi_cntrl->cmd_ctx_cache);
62262306a36Sopenharmony_ci	if (ret) {
62362306a36Sopenharmony_ci		dev_err(dev, "Failed to start the command ring\n");
62462306a36Sopenharmony_ci		goto err_cmd_ctx;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	return ret;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cierr_cmd_ctx:
63062306a36Sopenharmony_ci	mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->cmd_ctx_host_pa, mhi_cntrl->cmd_ctx_cache_phys,
63162306a36Sopenharmony_ci			      (void __iomem *) mhi_cntrl->cmd_ctx_cache, cmd_ctx_host_size);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_cierr_ev_ctx:
63462306a36Sopenharmony_ci	mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->ev_ctx_host_pa, mhi_cntrl->ev_ctx_cache_phys,
63562306a36Sopenharmony_ci			      (void __iomem *) mhi_cntrl->ev_ctx_cache, ev_ctx_host_size);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cierr_ch_ctx:
63862306a36Sopenharmony_ci	mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->ch_ctx_host_pa, mhi_cntrl->ch_ctx_cache_phys,
63962306a36Sopenharmony_ci			      (void __iomem *) mhi_cntrl->ch_ctx_cache, ch_ctx_host_size);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return ret;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic void mhi_ep_free_host_cfg(struct mhi_ep_cntrl *mhi_cntrl)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	size_t cmd_ctx_host_size, ch_ctx_host_size, ev_ctx_host_size;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	ch_ctx_host_size = sizeof(struct mhi_chan_ctxt) * mhi_cntrl->max_chan;
64962306a36Sopenharmony_ci	ev_ctx_host_size = sizeof(struct mhi_event_ctxt) * mhi_cntrl->event_rings;
65062306a36Sopenharmony_ci	cmd_ctx_host_size = sizeof(struct mhi_cmd_ctxt) * NR_OF_CMD_RINGS;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->cmd_ctx_host_pa, mhi_cntrl->cmd_ctx_cache_phys,
65362306a36Sopenharmony_ci			      (void __iomem *) mhi_cntrl->cmd_ctx_cache, cmd_ctx_host_size);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->ev_ctx_host_pa, mhi_cntrl->ev_ctx_cache_phys,
65662306a36Sopenharmony_ci			      (void __iomem *) mhi_cntrl->ev_ctx_cache, ev_ctx_host_size);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->ch_ctx_host_pa, mhi_cntrl->ch_ctx_cache_phys,
65962306a36Sopenharmony_ci			      (void __iomem *) mhi_cntrl->ch_ctx_cache, ch_ctx_host_size);
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic void mhi_ep_enable_int(struct mhi_ep_cntrl *mhi_cntrl)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	/*
66562306a36Sopenharmony_ci	 * Doorbell interrupts are enabled when the corresponding channel gets started.
66662306a36Sopenharmony_ci	 * Enabling all interrupts here triggers spurious irqs as some of the interrupts
66762306a36Sopenharmony_ci	 * associated with hw channels always get triggered.
66862306a36Sopenharmony_ci	 */
66962306a36Sopenharmony_ci	mhi_ep_mmio_enable_ctrl_interrupt(mhi_cntrl);
67062306a36Sopenharmony_ci	mhi_ep_mmio_enable_cmdb_interrupt(mhi_cntrl);
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic int mhi_ep_enable(struct mhi_ep_cntrl *mhi_cntrl)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
67662306a36Sopenharmony_ci	enum mhi_state state;
67762306a36Sopenharmony_ci	bool mhi_reset;
67862306a36Sopenharmony_ci	u32 count = 0;
67962306a36Sopenharmony_ci	int ret;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	/* Wait for Host to set the M0 state */
68262306a36Sopenharmony_ci	do {
68362306a36Sopenharmony_ci		msleep(M0_WAIT_DELAY_MS);
68462306a36Sopenharmony_ci		mhi_ep_mmio_get_mhi_state(mhi_cntrl, &state, &mhi_reset);
68562306a36Sopenharmony_ci		if (mhi_reset) {
68662306a36Sopenharmony_ci			/* Clear the MHI reset if host is in reset state */
68762306a36Sopenharmony_ci			mhi_ep_mmio_clear_reset(mhi_cntrl);
68862306a36Sopenharmony_ci			dev_info(dev, "Detected Host reset while waiting for M0\n");
68962306a36Sopenharmony_ci		}
69062306a36Sopenharmony_ci		count++;
69162306a36Sopenharmony_ci	} while (state != MHI_STATE_M0 && count < M0_WAIT_COUNT);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (state != MHI_STATE_M0) {
69462306a36Sopenharmony_ci		dev_err(dev, "Host failed to enter M0\n");
69562306a36Sopenharmony_ci		return -ETIMEDOUT;
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	ret = mhi_ep_cache_host_cfg(mhi_cntrl);
69962306a36Sopenharmony_ci	if (ret) {
70062306a36Sopenharmony_ci		dev_err(dev, "Failed to cache host config\n");
70162306a36Sopenharmony_ci		return ret;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	mhi_ep_mmio_set_env(mhi_cntrl, MHI_EE_AMSS);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	/* Enable all interrupts now */
70762306a36Sopenharmony_ci	mhi_ep_enable_int(mhi_cntrl);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	return 0;
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic void mhi_ep_cmd_ring_worker(struct work_struct *work)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, cmd_ring_work);
71562306a36Sopenharmony_ci	struct mhi_ep_ring *ring = &mhi_cntrl->mhi_cmd->ring;
71662306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
71762306a36Sopenharmony_ci	struct mhi_ring_element *el;
71862306a36Sopenharmony_ci	int ret;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	/* Update the write offset for the ring */
72162306a36Sopenharmony_ci	ret = mhi_ep_update_wr_offset(ring);
72262306a36Sopenharmony_ci	if (ret) {
72362306a36Sopenharmony_ci		dev_err(dev, "Error updating write offset for ring\n");
72462306a36Sopenharmony_ci		return;
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	/* Sanity check to make sure there are elements in the ring */
72862306a36Sopenharmony_ci	if (ring->rd_offset == ring->wr_offset)
72962306a36Sopenharmony_ci		return;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	/*
73262306a36Sopenharmony_ci	 * Process command ring element till write offset. In case of an error, just try to
73362306a36Sopenharmony_ci	 * process next element.
73462306a36Sopenharmony_ci	 */
73562306a36Sopenharmony_ci	while (ring->rd_offset != ring->wr_offset) {
73662306a36Sopenharmony_ci		el = &ring->ring_cache[ring->rd_offset];
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci		ret = mhi_ep_process_cmd_ring(ring, el);
73962306a36Sopenharmony_ci		if (ret && ret != -ENODEV)
74062306a36Sopenharmony_ci			dev_err(dev, "Error processing cmd ring element: %zu\n", ring->rd_offset);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		mhi_ep_ring_inc_index(ring);
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ci}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_cistatic void mhi_ep_ch_ring_worker(struct work_struct *work)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, ch_ring_work);
74962306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
75062306a36Sopenharmony_ci	struct mhi_ep_ring_item *itr, *tmp;
75162306a36Sopenharmony_ci	struct mhi_ring_element *el;
75262306a36Sopenharmony_ci	struct mhi_ep_ring *ring;
75362306a36Sopenharmony_ci	struct mhi_ep_chan *chan;
75462306a36Sopenharmony_ci	unsigned long flags;
75562306a36Sopenharmony_ci	LIST_HEAD(head);
75662306a36Sopenharmony_ci	int ret;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	spin_lock_irqsave(&mhi_cntrl->list_lock, flags);
75962306a36Sopenharmony_ci	list_splice_tail_init(&mhi_cntrl->ch_db_list, &head);
76062306a36Sopenharmony_ci	spin_unlock_irqrestore(&mhi_cntrl->list_lock, flags);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	/* Process each queued channel ring. In case of an error, just process next element. */
76362306a36Sopenharmony_ci	list_for_each_entry_safe(itr, tmp, &head, node) {
76462306a36Sopenharmony_ci		list_del(&itr->node);
76562306a36Sopenharmony_ci		ring = itr->ring;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci		chan = &mhi_cntrl->mhi_chan[ring->ch_id];
76862306a36Sopenharmony_ci		mutex_lock(&chan->lock);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci		/*
77162306a36Sopenharmony_ci		 * The ring could've stopped while we waited to grab the (chan->lock), so do
77262306a36Sopenharmony_ci		 * a sanity check before going further.
77362306a36Sopenharmony_ci		 */
77462306a36Sopenharmony_ci		if (!ring->started) {
77562306a36Sopenharmony_ci			mutex_unlock(&chan->lock);
77662306a36Sopenharmony_ci			kfree(itr);
77762306a36Sopenharmony_ci			continue;
77862306a36Sopenharmony_ci		}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci		/* Update the write offset for the ring */
78162306a36Sopenharmony_ci		ret = mhi_ep_update_wr_offset(ring);
78262306a36Sopenharmony_ci		if (ret) {
78362306a36Sopenharmony_ci			dev_err(dev, "Error updating write offset for ring\n");
78462306a36Sopenharmony_ci			mutex_unlock(&chan->lock);
78562306a36Sopenharmony_ci			kmem_cache_free(mhi_cntrl->ring_item_cache, itr);
78662306a36Sopenharmony_ci			continue;
78762306a36Sopenharmony_ci		}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci		/* Sanity check to make sure there are elements in the ring */
79062306a36Sopenharmony_ci		if (ring->rd_offset == ring->wr_offset) {
79162306a36Sopenharmony_ci			mutex_unlock(&chan->lock);
79262306a36Sopenharmony_ci			kmem_cache_free(mhi_cntrl->ring_item_cache, itr);
79362306a36Sopenharmony_ci			continue;
79462306a36Sopenharmony_ci		}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci		el = &ring->ring_cache[ring->rd_offset];
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci		dev_dbg(dev, "Processing the ring for channel (%u)\n", ring->ch_id);
79962306a36Sopenharmony_ci		ret = mhi_ep_process_ch_ring(ring, el);
80062306a36Sopenharmony_ci		if (ret) {
80162306a36Sopenharmony_ci			dev_err(dev, "Error processing ring for channel (%u): %d\n",
80262306a36Sopenharmony_ci				ring->ch_id, ret);
80362306a36Sopenharmony_ci			mutex_unlock(&chan->lock);
80462306a36Sopenharmony_ci			kmem_cache_free(mhi_cntrl->ring_item_cache, itr);
80562306a36Sopenharmony_ci			continue;
80662306a36Sopenharmony_ci		}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci		mutex_unlock(&chan->lock);
80962306a36Sopenharmony_ci		kmem_cache_free(mhi_cntrl->ring_item_cache, itr);
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_cistatic void mhi_ep_state_worker(struct work_struct *work)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, state_work);
81662306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
81762306a36Sopenharmony_ci	struct mhi_ep_state_transition *itr, *tmp;
81862306a36Sopenharmony_ci	unsigned long flags;
81962306a36Sopenharmony_ci	LIST_HEAD(head);
82062306a36Sopenharmony_ci	int ret;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	spin_lock_irqsave(&mhi_cntrl->list_lock, flags);
82362306a36Sopenharmony_ci	list_splice_tail_init(&mhi_cntrl->st_transition_list, &head);
82462306a36Sopenharmony_ci	spin_unlock_irqrestore(&mhi_cntrl->list_lock, flags);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	list_for_each_entry_safe(itr, tmp, &head, node) {
82762306a36Sopenharmony_ci		list_del(&itr->node);
82862306a36Sopenharmony_ci		dev_dbg(dev, "Handling MHI state transition to %s\n",
82962306a36Sopenharmony_ci			 mhi_state_str(itr->state));
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci		switch (itr->state) {
83262306a36Sopenharmony_ci		case MHI_STATE_M0:
83362306a36Sopenharmony_ci			ret = mhi_ep_set_m0_state(mhi_cntrl);
83462306a36Sopenharmony_ci			if (ret)
83562306a36Sopenharmony_ci				dev_err(dev, "Failed to transition to M0 state\n");
83662306a36Sopenharmony_ci			break;
83762306a36Sopenharmony_ci		case MHI_STATE_M3:
83862306a36Sopenharmony_ci			ret = mhi_ep_set_m3_state(mhi_cntrl);
83962306a36Sopenharmony_ci			if (ret)
84062306a36Sopenharmony_ci				dev_err(dev, "Failed to transition to M3 state\n");
84162306a36Sopenharmony_ci			break;
84262306a36Sopenharmony_ci		default:
84362306a36Sopenharmony_ci			dev_err(dev, "Invalid MHI state transition: %d\n", itr->state);
84462306a36Sopenharmony_ci			break;
84562306a36Sopenharmony_ci		}
84662306a36Sopenharmony_ci		kfree(itr);
84762306a36Sopenharmony_ci	}
84862306a36Sopenharmony_ci}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_cistatic void mhi_ep_queue_channel_db(struct mhi_ep_cntrl *mhi_cntrl, unsigned long ch_int,
85162306a36Sopenharmony_ci				    u32 ch_idx)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	struct mhi_ep_ring_item *item;
85462306a36Sopenharmony_ci	struct mhi_ep_ring *ring;
85562306a36Sopenharmony_ci	bool work = !!ch_int;
85662306a36Sopenharmony_ci	LIST_HEAD(head);
85762306a36Sopenharmony_ci	u32 i;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	/* First add the ring items to a local list */
86062306a36Sopenharmony_ci	for_each_set_bit(i, &ch_int, 32) {
86162306a36Sopenharmony_ci		/* Channel index varies for each register: 0, 32, 64, 96 */
86262306a36Sopenharmony_ci		u32 ch_id = ch_idx + i;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci		ring = &mhi_cntrl->mhi_chan[ch_id].ring;
86562306a36Sopenharmony_ci		item = kmem_cache_zalloc(mhi_cntrl->ring_item_cache, GFP_ATOMIC);
86662306a36Sopenharmony_ci		if (!item)
86762306a36Sopenharmony_ci			return;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci		item->ring = ring;
87062306a36Sopenharmony_ci		list_add_tail(&item->node, &head);
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	/* Now, splice the local list into ch_db_list and queue the work item */
87462306a36Sopenharmony_ci	if (work) {
87562306a36Sopenharmony_ci		spin_lock(&mhi_cntrl->list_lock);
87662306a36Sopenharmony_ci		list_splice_tail_init(&head, &mhi_cntrl->ch_db_list);
87762306a36Sopenharmony_ci		spin_unlock(&mhi_cntrl->list_lock);
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci		queue_work(mhi_cntrl->wq, &mhi_cntrl->ch_ring_work);
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci/*
88462306a36Sopenharmony_ci * Channel interrupt statuses are contained in 4 registers each of 32bit length.
88562306a36Sopenharmony_ci * For checking all interrupts, we need to loop through each registers and then
88662306a36Sopenharmony_ci * check for bits set.
88762306a36Sopenharmony_ci */
88862306a36Sopenharmony_cistatic void mhi_ep_check_channel_interrupt(struct mhi_ep_cntrl *mhi_cntrl)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	u32 ch_int, ch_idx, i;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	/* Bail out if there is no channel doorbell interrupt */
89362306a36Sopenharmony_ci	if (!mhi_ep_mmio_read_chdb_status_interrupts(mhi_cntrl))
89462306a36Sopenharmony_ci		return;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	for (i = 0; i < MHI_MASK_ROWS_CH_DB; i++) {
89762306a36Sopenharmony_ci		ch_idx = i * MHI_MASK_CH_LEN;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci		/* Only process channel interrupt if the mask is enabled */
90062306a36Sopenharmony_ci		ch_int = mhi_cntrl->chdb[i].status & mhi_cntrl->chdb[i].mask;
90162306a36Sopenharmony_ci		if (ch_int) {
90262306a36Sopenharmony_ci			mhi_ep_queue_channel_db(mhi_cntrl, ch_int, ch_idx);
90362306a36Sopenharmony_ci			mhi_ep_mmio_write(mhi_cntrl, MHI_CHDB_INT_CLEAR_n(i),
90462306a36Sopenharmony_ci							mhi_cntrl->chdb[i].status);
90562306a36Sopenharmony_ci		}
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cistatic void mhi_ep_process_ctrl_interrupt(struct mhi_ep_cntrl *mhi_cntrl,
91062306a36Sopenharmony_ci					 enum mhi_state state)
91162306a36Sopenharmony_ci{
91262306a36Sopenharmony_ci	struct mhi_ep_state_transition *item;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	item = kzalloc(sizeof(*item), GFP_ATOMIC);
91562306a36Sopenharmony_ci	if (!item)
91662306a36Sopenharmony_ci		return;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	item->state = state;
91962306a36Sopenharmony_ci	spin_lock(&mhi_cntrl->list_lock);
92062306a36Sopenharmony_ci	list_add_tail(&item->node, &mhi_cntrl->st_transition_list);
92162306a36Sopenharmony_ci	spin_unlock(&mhi_cntrl->list_lock);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	queue_work(mhi_cntrl->wq, &mhi_cntrl->state_work);
92462306a36Sopenharmony_ci}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci/*
92762306a36Sopenharmony_ci * Interrupt handler that services interrupts raised by the host writing to
92862306a36Sopenharmony_ci * MHICTRL and Command ring doorbell (CRDB) registers for state change and
92962306a36Sopenharmony_ci * channel interrupts.
93062306a36Sopenharmony_ci */
93162306a36Sopenharmony_cistatic irqreturn_t mhi_ep_irq(int irq, void *data)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	struct mhi_ep_cntrl *mhi_cntrl = data;
93462306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
93562306a36Sopenharmony_ci	enum mhi_state state;
93662306a36Sopenharmony_ci	u32 int_value;
93762306a36Sopenharmony_ci	bool mhi_reset;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	/* Acknowledge the ctrl interrupt */
94062306a36Sopenharmony_ci	int_value = mhi_ep_mmio_read(mhi_cntrl, MHI_CTRL_INT_STATUS);
94162306a36Sopenharmony_ci	mhi_ep_mmio_write(mhi_cntrl, MHI_CTRL_INT_CLEAR, int_value);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	/* Check for ctrl interrupt */
94462306a36Sopenharmony_ci	if (FIELD_GET(MHI_CTRL_INT_STATUS_MSK, int_value)) {
94562306a36Sopenharmony_ci		dev_dbg(dev, "Processing ctrl interrupt\n");
94662306a36Sopenharmony_ci		mhi_ep_mmio_get_mhi_state(mhi_cntrl, &state, &mhi_reset);
94762306a36Sopenharmony_ci		if (mhi_reset) {
94862306a36Sopenharmony_ci			dev_info(dev, "Host triggered MHI reset!\n");
94962306a36Sopenharmony_ci			disable_irq_nosync(mhi_cntrl->irq);
95062306a36Sopenharmony_ci			schedule_work(&mhi_cntrl->reset_work);
95162306a36Sopenharmony_ci			return IRQ_HANDLED;
95262306a36Sopenharmony_ci		}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci		mhi_ep_process_ctrl_interrupt(mhi_cntrl, state);
95562306a36Sopenharmony_ci	}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	/* Check for command doorbell interrupt */
95862306a36Sopenharmony_ci	if (FIELD_GET(MHI_CTRL_INT_STATUS_CRDB_MSK, int_value)) {
95962306a36Sopenharmony_ci		dev_dbg(dev, "Processing command doorbell interrupt\n");
96062306a36Sopenharmony_ci		queue_work(mhi_cntrl->wq, &mhi_cntrl->cmd_ring_work);
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	/* Check for channel interrupts */
96462306a36Sopenharmony_ci	mhi_ep_check_channel_interrupt(mhi_cntrl);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	return IRQ_HANDLED;
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	struct mhi_ep_ring *ch_ring, *ev_ring;
97262306a36Sopenharmony_ci	struct mhi_result result = {};
97362306a36Sopenharmony_ci	struct mhi_ep_chan *mhi_chan;
97462306a36Sopenharmony_ci	int i;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	/* Stop all the channels */
97762306a36Sopenharmony_ci	for (i = 0; i < mhi_cntrl->max_chan; i++) {
97862306a36Sopenharmony_ci		mhi_chan = &mhi_cntrl->mhi_chan[i];
97962306a36Sopenharmony_ci		if (!mhi_chan->ring.started)
98062306a36Sopenharmony_ci			continue;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		mutex_lock(&mhi_chan->lock);
98362306a36Sopenharmony_ci		/* Send channel disconnect status to client drivers */
98462306a36Sopenharmony_ci		if (mhi_chan->xfer_cb) {
98562306a36Sopenharmony_ci			result.transaction_status = -ENOTCONN;
98662306a36Sopenharmony_ci			result.bytes_xferd = 0;
98762306a36Sopenharmony_ci			mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
98862306a36Sopenharmony_ci		}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci		mhi_chan->state = MHI_CH_STATE_DISABLED;
99162306a36Sopenharmony_ci		mutex_unlock(&mhi_chan->lock);
99262306a36Sopenharmony_ci	}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	flush_workqueue(mhi_cntrl->wq);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	/* Destroy devices associated with all channels */
99762306a36Sopenharmony_ci	device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_ep_destroy_device);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	/* Stop and reset the transfer rings */
100062306a36Sopenharmony_ci	for (i = 0; i < mhi_cntrl->max_chan; i++) {
100162306a36Sopenharmony_ci		mhi_chan = &mhi_cntrl->mhi_chan[i];
100262306a36Sopenharmony_ci		if (!mhi_chan->ring.started)
100362306a36Sopenharmony_ci			continue;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci		ch_ring = &mhi_cntrl->mhi_chan[i].ring;
100662306a36Sopenharmony_ci		mutex_lock(&mhi_chan->lock);
100762306a36Sopenharmony_ci		mhi_ep_ring_reset(mhi_cntrl, ch_ring);
100862306a36Sopenharmony_ci		mutex_unlock(&mhi_chan->lock);
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	/* Stop and reset the event rings */
101262306a36Sopenharmony_ci	for (i = 0; i < mhi_cntrl->event_rings; i++) {
101362306a36Sopenharmony_ci		ev_ring = &mhi_cntrl->mhi_event[i].ring;
101462306a36Sopenharmony_ci		if (!ev_ring->started)
101562306a36Sopenharmony_ci			continue;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci		mutex_lock(&mhi_cntrl->event_lock);
101862306a36Sopenharmony_ci		mhi_ep_ring_reset(mhi_cntrl, ev_ring);
101962306a36Sopenharmony_ci		mutex_unlock(&mhi_cntrl->event_lock);
102062306a36Sopenharmony_ci	}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	/* Stop and reset the command ring */
102362306a36Sopenharmony_ci	mhi_ep_ring_reset(mhi_cntrl, &mhi_cntrl->mhi_cmd->ring);
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	mhi_ep_free_host_cfg(mhi_cntrl);
102662306a36Sopenharmony_ci	mhi_ep_mmio_mask_interrupts(mhi_cntrl);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	mhi_cntrl->enabled = false;
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cistatic void mhi_ep_reset_worker(struct work_struct *work)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, reset_work);
103462306a36Sopenharmony_ci	enum mhi_state cur_state;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	mhi_ep_power_down(mhi_cntrl);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	mutex_lock(&mhi_cntrl->state_lock);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	/* Reset MMIO to signal host that the MHI_RESET is completed in endpoint */
104162306a36Sopenharmony_ci	mhi_ep_mmio_reset(mhi_cntrl);
104262306a36Sopenharmony_ci	cur_state = mhi_cntrl->mhi_state;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	/*
104562306a36Sopenharmony_ci	 * Only proceed further if the reset is due to SYS_ERR. The host will
104662306a36Sopenharmony_ci	 * issue reset during shutdown also and we don't need to do re-init in
104762306a36Sopenharmony_ci	 * that case.
104862306a36Sopenharmony_ci	 */
104962306a36Sopenharmony_ci	if (cur_state == MHI_STATE_SYS_ERR)
105062306a36Sopenharmony_ci		mhi_ep_power_up(mhi_cntrl);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	mutex_unlock(&mhi_cntrl->state_lock);
105362306a36Sopenharmony_ci}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci/*
105662306a36Sopenharmony_ci * We don't need to do anything special other than setting the MHI SYS_ERR
105762306a36Sopenharmony_ci * state. The host will reset all contexts and issue MHI RESET so that we
105862306a36Sopenharmony_ci * could also recover from error state.
105962306a36Sopenharmony_ci */
106062306a36Sopenharmony_civoid mhi_ep_handle_syserr(struct mhi_ep_cntrl *mhi_cntrl)
106162306a36Sopenharmony_ci{
106262306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
106362306a36Sopenharmony_ci	int ret;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_SYS_ERR);
106662306a36Sopenharmony_ci	if (ret)
106762306a36Sopenharmony_ci		return;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	/* Signal host that the device went to SYS_ERR state */
107062306a36Sopenharmony_ci	ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_SYS_ERR);
107162306a36Sopenharmony_ci	if (ret)
107262306a36Sopenharmony_ci		dev_err(dev, "Failed sending SYS_ERR state change event: %d\n", ret);
107362306a36Sopenharmony_ci}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ciint mhi_ep_power_up(struct mhi_ep_cntrl *mhi_cntrl)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	struct device *dev = &mhi_cntrl->mhi_dev->dev;
107862306a36Sopenharmony_ci	int ret, i;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	/*
108162306a36Sopenharmony_ci	 * Mask all interrupts until the state machine is ready. Interrupts will
108262306a36Sopenharmony_ci	 * be enabled later with mhi_ep_enable().
108362306a36Sopenharmony_ci	 */
108462306a36Sopenharmony_ci	mhi_ep_mmio_mask_interrupts(mhi_cntrl);
108562306a36Sopenharmony_ci	mhi_ep_mmio_init(mhi_cntrl);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	mhi_cntrl->mhi_event = kzalloc(mhi_cntrl->event_rings * (sizeof(*mhi_cntrl->mhi_event)),
108862306a36Sopenharmony_ci					GFP_KERNEL);
108962306a36Sopenharmony_ci	if (!mhi_cntrl->mhi_event)
109062306a36Sopenharmony_ci		return -ENOMEM;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	/* Initialize command, channel and event rings */
109362306a36Sopenharmony_ci	mhi_ep_ring_init(&mhi_cntrl->mhi_cmd->ring, RING_TYPE_CMD, 0);
109462306a36Sopenharmony_ci	for (i = 0; i < mhi_cntrl->max_chan; i++)
109562306a36Sopenharmony_ci		mhi_ep_ring_init(&mhi_cntrl->mhi_chan[i].ring, RING_TYPE_CH, i);
109662306a36Sopenharmony_ci	for (i = 0; i < mhi_cntrl->event_rings; i++)
109762306a36Sopenharmony_ci		mhi_ep_ring_init(&mhi_cntrl->mhi_event[i].ring, RING_TYPE_ER, i);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	mhi_cntrl->mhi_state = MHI_STATE_RESET;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	/* Set AMSS EE before signaling ready state */
110262306a36Sopenharmony_ci	mhi_ep_mmio_set_env(mhi_cntrl, MHI_EE_AMSS);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	/* All set, notify the host that we are ready */
110562306a36Sopenharmony_ci	ret = mhi_ep_set_ready_state(mhi_cntrl);
110662306a36Sopenharmony_ci	if (ret)
110762306a36Sopenharmony_ci		goto err_free_event;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	dev_dbg(dev, "READY state notification sent to the host\n");
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	ret = mhi_ep_enable(mhi_cntrl);
111262306a36Sopenharmony_ci	if (ret) {
111362306a36Sopenharmony_ci		dev_err(dev, "Failed to enable MHI endpoint\n");
111462306a36Sopenharmony_ci		goto err_free_event;
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	enable_irq(mhi_cntrl->irq);
111862306a36Sopenharmony_ci	mhi_cntrl->enabled = true;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	return 0;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_cierr_free_event:
112362306a36Sopenharmony_ci	kfree(mhi_cntrl->mhi_event);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	return ret;
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_ep_power_up);
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_civoid mhi_ep_power_down(struct mhi_ep_cntrl *mhi_cntrl)
113062306a36Sopenharmony_ci{
113162306a36Sopenharmony_ci	if (mhi_cntrl->enabled) {
113262306a36Sopenharmony_ci		mhi_ep_abort_transfer(mhi_cntrl);
113362306a36Sopenharmony_ci		kfree(mhi_cntrl->mhi_event);
113462306a36Sopenharmony_ci		disable_irq(mhi_cntrl->irq);
113562306a36Sopenharmony_ci	}
113662306a36Sopenharmony_ci}
113762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_ep_power_down);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_civoid mhi_ep_suspend_channels(struct mhi_ep_cntrl *mhi_cntrl)
114062306a36Sopenharmony_ci{
114162306a36Sopenharmony_ci	struct mhi_ep_chan *mhi_chan;
114262306a36Sopenharmony_ci	u32 tmp;
114362306a36Sopenharmony_ci	int i;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	for (i = 0; i < mhi_cntrl->max_chan; i++) {
114662306a36Sopenharmony_ci		mhi_chan = &mhi_cntrl->mhi_chan[i];
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci		if (!mhi_chan->mhi_dev)
114962306a36Sopenharmony_ci			continue;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci		mutex_lock(&mhi_chan->lock);
115262306a36Sopenharmony_ci		/* Skip if the channel is not currently running */
115362306a36Sopenharmony_ci		tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[i].chcfg);
115462306a36Sopenharmony_ci		if (FIELD_GET(CHAN_CTX_CHSTATE_MASK, tmp) != MHI_CH_STATE_RUNNING) {
115562306a36Sopenharmony_ci			mutex_unlock(&mhi_chan->lock);
115662306a36Sopenharmony_ci			continue;
115762306a36Sopenharmony_ci		}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci		dev_dbg(&mhi_chan->mhi_dev->dev, "Suspending channel\n");
116062306a36Sopenharmony_ci		/* Set channel state to SUSPENDED */
116162306a36Sopenharmony_ci		mhi_chan->state = MHI_CH_STATE_SUSPENDED;
116262306a36Sopenharmony_ci		tmp &= ~CHAN_CTX_CHSTATE_MASK;
116362306a36Sopenharmony_ci		tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_SUSPENDED);
116462306a36Sopenharmony_ci		mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp);
116562306a36Sopenharmony_ci		mutex_unlock(&mhi_chan->lock);
116662306a36Sopenharmony_ci	}
116762306a36Sopenharmony_ci}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_civoid mhi_ep_resume_channels(struct mhi_ep_cntrl *mhi_cntrl)
117062306a36Sopenharmony_ci{
117162306a36Sopenharmony_ci	struct mhi_ep_chan *mhi_chan;
117262306a36Sopenharmony_ci	u32 tmp;
117362306a36Sopenharmony_ci	int i;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	for (i = 0; i < mhi_cntrl->max_chan; i++) {
117662306a36Sopenharmony_ci		mhi_chan = &mhi_cntrl->mhi_chan[i];
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci		if (!mhi_chan->mhi_dev)
117962306a36Sopenharmony_ci			continue;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci		mutex_lock(&mhi_chan->lock);
118262306a36Sopenharmony_ci		/* Skip if the channel is not currently suspended */
118362306a36Sopenharmony_ci		tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[i].chcfg);
118462306a36Sopenharmony_ci		if (FIELD_GET(CHAN_CTX_CHSTATE_MASK, tmp) != MHI_CH_STATE_SUSPENDED) {
118562306a36Sopenharmony_ci			mutex_unlock(&mhi_chan->lock);
118662306a36Sopenharmony_ci			continue;
118762306a36Sopenharmony_ci		}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci		dev_dbg(&mhi_chan->mhi_dev->dev, "Resuming channel\n");
119062306a36Sopenharmony_ci		/* Set channel state to RUNNING */
119162306a36Sopenharmony_ci		mhi_chan->state = MHI_CH_STATE_RUNNING;
119262306a36Sopenharmony_ci		tmp &= ~CHAN_CTX_CHSTATE_MASK;
119362306a36Sopenharmony_ci		tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_RUNNING);
119462306a36Sopenharmony_ci		mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp);
119562306a36Sopenharmony_ci		mutex_unlock(&mhi_chan->lock);
119662306a36Sopenharmony_ci	}
119762306a36Sopenharmony_ci}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic void mhi_ep_release_device(struct device *dev)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
120462306a36Sopenharmony_ci		mhi_dev->mhi_cntrl->mhi_dev = NULL;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	/*
120762306a36Sopenharmony_ci	 * We need to set the mhi_chan->mhi_dev to NULL here since the MHI
120862306a36Sopenharmony_ci	 * devices for the channels will only get created in mhi_ep_create_device()
120962306a36Sopenharmony_ci	 * if the mhi_dev associated with it is NULL.
121062306a36Sopenharmony_ci	 */
121162306a36Sopenharmony_ci	if (mhi_dev->ul_chan)
121262306a36Sopenharmony_ci		mhi_dev->ul_chan->mhi_dev = NULL;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	if (mhi_dev->dl_chan)
121562306a36Sopenharmony_ci		mhi_dev->dl_chan->mhi_dev = NULL;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	kfree(mhi_dev);
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_cistatic struct mhi_ep_device *mhi_ep_alloc_device(struct mhi_ep_cntrl *mhi_cntrl,
122162306a36Sopenharmony_ci						 enum mhi_device_type dev_type)
122262306a36Sopenharmony_ci{
122362306a36Sopenharmony_ci	struct mhi_ep_device *mhi_dev;
122462306a36Sopenharmony_ci	struct device *dev;
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	mhi_dev = kzalloc(sizeof(*mhi_dev), GFP_KERNEL);
122762306a36Sopenharmony_ci	if (!mhi_dev)
122862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	dev = &mhi_dev->dev;
123162306a36Sopenharmony_ci	device_initialize(dev);
123262306a36Sopenharmony_ci	dev->bus = &mhi_ep_bus_type;
123362306a36Sopenharmony_ci	dev->release = mhi_ep_release_device;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	/* Controller device is always allocated first */
123662306a36Sopenharmony_ci	if (dev_type == MHI_DEVICE_CONTROLLER)
123762306a36Sopenharmony_ci		/* for MHI controller device, parent is the bus device (e.g. PCI EPF) */
123862306a36Sopenharmony_ci		dev->parent = mhi_cntrl->cntrl_dev;
123962306a36Sopenharmony_ci	else
124062306a36Sopenharmony_ci		/* for MHI client devices, parent is the MHI controller device */
124162306a36Sopenharmony_ci		dev->parent = &mhi_cntrl->mhi_dev->dev;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	mhi_dev->mhi_cntrl = mhi_cntrl;
124462306a36Sopenharmony_ci	mhi_dev->dev_type = dev_type;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	return mhi_dev;
124762306a36Sopenharmony_ci}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci/*
125062306a36Sopenharmony_ci * MHI channels are always defined in pairs with UL as the even numbered
125162306a36Sopenharmony_ci * channel and DL as odd numbered one. This function gets UL channel (primary)
125262306a36Sopenharmony_ci * as the ch_id and always looks after the next entry in channel list for
125362306a36Sopenharmony_ci * the corresponding DL channel (secondary).
125462306a36Sopenharmony_ci */
125562306a36Sopenharmony_cistatic int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id)
125662306a36Sopenharmony_ci{
125762306a36Sopenharmony_ci	struct mhi_ep_chan *mhi_chan = &mhi_cntrl->mhi_chan[ch_id];
125862306a36Sopenharmony_ci	struct device *dev = mhi_cntrl->cntrl_dev;
125962306a36Sopenharmony_ci	struct mhi_ep_device *mhi_dev;
126062306a36Sopenharmony_ci	int ret;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	/* Check if the channel name is same for both UL and DL */
126362306a36Sopenharmony_ci	if (strcmp(mhi_chan->name, mhi_chan[1].name)) {
126462306a36Sopenharmony_ci		dev_err(dev, "UL and DL channel names are not same: (%s) != (%s)\n",
126562306a36Sopenharmony_ci			mhi_chan->name, mhi_chan[1].name);
126662306a36Sopenharmony_ci		return -EINVAL;
126762306a36Sopenharmony_ci	}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	mhi_dev = mhi_ep_alloc_device(mhi_cntrl, MHI_DEVICE_XFER);
127062306a36Sopenharmony_ci	if (IS_ERR(mhi_dev))
127162306a36Sopenharmony_ci		return PTR_ERR(mhi_dev);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	/* Configure primary channel */
127462306a36Sopenharmony_ci	mhi_dev->ul_chan = mhi_chan;
127562306a36Sopenharmony_ci	get_device(&mhi_dev->dev);
127662306a36Sopenharmony_ci	mhi_chan->mhi_dev = mhi_dev;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	/* Configure secondary channel as well */
127962306a36Sopenharmony_ci	mhi_chan++;
128062306a36Sopenharmony_ci	mhi_dev->dl_chan = mhi_chan;
128162306a36Sopenharmony_ci	get_device(&mhi_dev->dev);
128262306a36Sopenharmony_ci	mhi_chan->mhi_dev = mhi_dev;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	/* Channel name is same for both UL and DL */
128562306a36Sopenharmony_ci	mhi_dev->name = mhi_chan->name;
128662306a36Sopenharmony_ci	ret = dev_set_name(&mhi_dev->dev, "%s_%s",
128762306a36Sopenharmony_ci		     dev_name(&mhi_cntrl->mhi_dev->dev),
128862306a36Sopenharmony_ci		     mhi_dev->name);
128962306a36Sopenharmony_ci	if (ret) {
129062306a36Sopenharmony_ci		put_device(&mhi_dev->dev);
129162306a36Sopenharmony_ci		return ret;
129262306a36Sopenharmony_ci	}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	ret = device_add(&mhi_dev->dev);
129562306a36Sopenharmony_ci	if (ret)
129662306a36Sopenharmony_ci		put_device(&mhi_dev->dev);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	return ret;
129962306a36Sopenharmony_ci}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_cistatic int mhi_ep_destroy_device(struct device *dev, void *data)
130262306a36Sopenharmony_ci{
130362306a36Sopenharmony_ci	struct mhi_ep_device *mhi_dev;
130462306a36Sopenharmony_ci	struct mhi_ep_cntrl *mhi_cntrl;
130562306a36Sopenharmony_ci	struct mhi_ep_chan *ul_chan, *dl_chan;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	if (dev->bus != &mhi_ep_bus_type)
130862306a36Sopenharmony_ci		return 0;
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	mhi_dev = to_mhi_ep_device(dev);
131162306a36Sopenharmony_ci	mhi_cntrl = mhi_dev->mhi_cntrl;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	/* Only destroy devices created for channels */
131462306a36Sopenharmony_ci	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
131562306a36Sopenharmony_ci		return 0;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	ul_chan = mhi_dev->ul_chan;
131862306a36Sopenharmony_ci	dl_chan = mhi_dev->dl_chan;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	if (ul_chan)
132162306a36Sopenharmony_ci		put_device(&ul_chan->mhi_dev->dev);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	if (dl_chan)
132462306a36Sopenharmony_ci		put_device(&dl_chan->mhi_dev->dev);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	dev_dbg(&mhi_cntrl->mhi_dev->dev, "Destroying device for chan:%s\n",
132762306a36Sopenharmony_ci		 mhi_dev->name);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	/* Notify the client and remove the device from MHI bus */
133062306a36Sopenharmony_ci	device_del(dev);
133162306a36Sopenharmony_ci	put_device(dev);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	return 0;
133462306a36Sopenharmony_ci}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_cistatic int mhi_ep_chan_init(struct mhi_ep_cntrl *mhi_cntrl,
133762306a36Sopenharmony_ci			    const struct mhi_ep_cntrl_config *config)
133862306a36Sopenharmony_ci{
133962306a36Sopenharmony_ci	const struct mhi_ep_channel_config *ch_cfg;
134062306a36Sopenharmony_ci	struct device *dev = mhi_cntrl->cntrl_dev;
134162306a36Sopenharmony_ci	u32 chan, i;
134262306a36Sopenharmony_ci	int ret = -EINVAL;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	mhi_cntrl->max_chan = config->max_channels;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	/*
134762306a36Sopenharmony_ci	 * Allocate max_channels supported by the MHI endpoint and populate
134862306a36Sopenharmony_ci	 * only the defined channels
134962306a36Sopenharmony_ci	 */
135062306a36Sopenharmony_ci	mhi_cntrl->mhi_chan = kcalloc(mhi_cntrl->max_chan, sizeof(*mhi_cntrl->mhi_chan),
135162306a36Sopenharmony_ci				      GFP_KERNEL);
135262306a36Sopenharmony_ci	if (!mhi_cntrl->mhi_chan)
135362306a36Sopenharmony_ci		return -ENOMEM;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	for (i = 0; i < config->num_channels; i++) {
135662306a36Sopenharmony_ci		struct mhi_ep_chan *mhi_chan;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci		ch_cfg = &config->ch_cfg[i];
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci		chan = ch_cfg->num;
136162306a36Sopenharmony_ci		if (chan >= mhi_cntrl->max_chan) {
136262306a36Sopenharmony_ci			dev_err(dev, "Channel (%u) exceeds maximum available channels (%u)\n",
136362306a36Sopenharmony_ci				chan, mhi_cntrl->max_chan);
136462306a36Sopenharmony_ci			goto error_chan_cfg;
136562306a36Sopenharmony_ci		}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci		/* Bi-directional and direction less channels are not supported */
136862306a36Sopenharmony_ci		if (ch_cfg->dir == DMA_BIDIRECTIONAL || ch_cfg->dir == DMA_NONE) {
136962306a36Sopenharmony_ci			dev_err(dev, "Invalid direction (%u) for channel (%u)\n",
137062306a36Sopenharmony_ci				ch_cfg->dir, chan);
137162306a36Sopenharmony_ci			goto error_chan_cfg;
137262306a36Sopenharmony_ci		}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci		mhi_chan = &mhi_cntrl->mhi_chan[chan];
137562306a36Sopenharmony_ci		mhi_chan->name = ch_cfg->name;
137662306a36Sopenharmony_ci		mhi_chan->chan = chan;
137762306a36Sopenharmony_ci		mhi_chan->dir = ch_cfg->dir;
137862306a36Sopenharmony_ci		mutex_init(&mhi_chan->lock);
137962306a36Sopenharmony_ci	}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	return 0;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_cierror_chan_cfg:
138462306a36Sopenharmony_ci	kfree(mhi_cntrl->mhi_chan);
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	return ret;
138762306a36Sopenharmony_ci}
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci/*
139062306a36Sopenharmony_ci * Allocate channel and command rings here. Event rings will be allocated
139162306a36Sopenharmony_ci * in mhi_ep_power_up() as the config comes from the host.
139262306a36Sopenharmony_ci */
139362306a36Sopenharmony_ciint mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
139462306a36Sopenharmony_ci				const struct mhi_ep_cntrl_config *config)
139562306a36Sopenharmony_ci{
139662306a36Sopenharmony_ci	struct mhi_ep_device *mhi_dev;
139762306a36Sopenharmony_ci	int ret;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	if (!mhi_cntrl || !mhi_cntrl->cntrl_dev || !mhi_cntrl->mmio || !mhi_cntrl->irq)
140062306a36Sopenharmony_ci		return -EINVAL;
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	ret = mhi_ep_chan_init(mhi_cntrl, config);
140362306a36Sopenharmony_ci	if (ret)
140462306a36Sopenharmony_ci		return ret;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	mhi_cntrl->mhi_cmd = kcalloc(NR_OF_CMD_RINGS, sizeof(*mhi_cntrl->mhi_cmd), GFP_KERNEL);
140762306a36Sopenharmony_ci	if (!mhi_cntrl->mhi_cmd) {
140862306a36Sopenharmony_ci		ret = -ENOMEM;
140962306a36Sopenharmony_ci		goto err_free_ch;
141062306a36Sopenharmony_ci	}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	mhi_cntrl->ev_ring_el_cache = kmem_cache_create("mhi_ep_event_ring_el",
141362306a36Sopenharmony_ci							sizeof(struct mhi_ring_element), 0,
141462306a36Sopenharmony_ci							SLAB_CACHE_DMA, NULL);
141562306a36Sopenharmony_ci	if (!mhi_cntrl->ev_ring_el_cache) {
141662306a36Sopenharmony_ci		ret = -ENOMEM;
141762306a36Sopenharmony_ci		goto err_free_cmd;
141862306a36Sopenharmony_ci	}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	mhi_cntrl->tre_buf_cache = kmem_cache_create("mhi_ep_tre_buf", MHI_EP_DEFAULT_MTU, 0,
142162306a36Sopenharmony_ci						      SLAB_CACHE_DMA, NULL);
142262306a36Sopenharmony_ci	if (!mhi_cntrl->tre_buf_cache) {
142362306a36Sopenharmony_ci		ret = -ENOMEM;
142462306a36Sopenharmony_ci		goto err_destroy_ev_ring_el_cache;
142562306a36Sopenharmony_ci	}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	mhi_cntrl->ring_item_cache = kmem_cache_create("mhi_ep_ring_item",
142862306a36Sopenharmony_ci							sizeof(struct mhi_ep_ring_item), 0,
142962306a36Sopenharmony_ci							0, NULL);
143062306a36Sopenharmony_ci	if (!mhi_cntrl->ring_item_cache) {
143162306a36Sopenharmony_ci		ret = -ENOMEM;
143262306a36Sopenharmony_ci		goto err_destroy_tre_buf_cache;
143362306a36Sopenharmony_ci	}
143462306a36Sopenharmony_ci	INIT_WORK(&mhi_cntrl->state_work, mhi_ep_state_worker);
143562306a36Sopenharmony_ci	INIT_WORK(&mhi_cntrl->reset_work, mhi_ep_reset_worker);
143662306a36Sopenharmony_ci	INIT_WORK(&mhi_cntrl->cmd_ring_work, mhi_ep_cmd_ring_worker);
143762306a36Sopenharmony_ci	INIT_WORK(&mhi_cntrl->ch_ring_work, mhi_ep_ch_ring_worker);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	mhi_cntrl->wq = alloc_workqueue("mhi_ep_wq", 0, 0);
144062306a36Sopenharmony_ci	if (!mhi_cntrl->wq) {
144162306a36Sopenharmony_ci		ret = -ENOMEM;
144262306a36Sopenharmony_ci		goto err_destroy_ring_item_cache;
144362306a36Sopenharmony_ci	}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	INIT_LIST_HEAD(&mhi_cntrl->st_transition_list);
144662306a36Sopenharmony_ci	INIT_LIST_HEAD(&mhi_cntrl->ch_db_list);
144762306a36Sopenharmony_ci	spin_lock_init(&mhi_cntrl->list_lock);
144862306a36Sopenharmony_ci	mutex_init(&mhi_cntrl->state_lock);
144962306a36Sopenharmony_ci	mutex_init(&mhi_cntrl->event_lock);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	/* Set MHI version and AMSS EE before enumeration */
145262306a36Sopenharmony_ci	mhi_ep_mmio_write(mhi_cntrl, EP_MHIVER, config->mhi_version);
145362306a36Sopenharmony_ci	mhi_ep_mmio_set_env(mhi_cntrl, MHI_EE_AMSS);
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	/* Set controller index */
145662306a36Sopenharmony_ci	ret = ida_alloc(&mhi_ep_cntrl_ida, GFP_KERNEL);
145762306a36Sopenharmony_ci	if (ret < 0)
145862306a36Sopenharmony_ci		goto err_destroy_wq;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	mhi_cntrl->index = ret;
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	irq_set_status_flags(mhi_cntrl->irq, IRQ_NOAUTOEN);
146362306a36Sopenharmony_ci	ret = request_irq(mhi_cntrl->irq, mhi_ep_irq, IRQF_TRIGGER_HIGH,
146462306a36Sopenharmony_ci			  "doorbell_irq", mhi_cntrl);
146562306a36Sopenharmony_ci	if (ret) {
146662306a36Sopenharmony_ci		dev_err(mhi_cntrl->cntrl_dev, "Failed to request Doorbell IRQ\n");
146762306a36Sopenharmony_ci		goto err_ida_free;
146862306a36Sopenharmony_ci	}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	/* Allocate the controller device */
147162306a36Sopenharmony_ci	mhi_dev = mhi_ep_alloc_device(mhi_cntrl, MHI_DEVICE_CONTROLLER);
147262306a36Sopenharmony_ci	if (IS_ERR(mhi_dev)) {
147362306a36Sopenharmony_ci		dev_err(mhi_cntrl->cntrl_dev, "Failed to allocate controller device\n");
147462306a36Sopenharmony_ci		ret = PTR_ERR(mhi_dev);
147562306a36Sopenharmony_ci		goto err_free_irq;
147662306a36Sopenharmony_ci	}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	ret = dev_set_name(&mhi_dev->dev, "mhi_ep%u", mhi_cntrl->index);
147962306a36Sopenharmony_ci	if (ret)
148062306a36Sopenharmony_ci		goto err_put_dev;
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	mhi_dev->name = dev_name(&mhi_dev->dev);
148362306a36Sopenharmony_ci	mhi_cntrl->mhi_dev = mhi_dev;
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	ret = device_add(&mhi_dev->dev);
148662306a36Sopenharmony_ci	if (ret)
148762306a36Sopenharmony_ci		goto err_put_dev;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	dev_dbg(&mhi_dev->dev, "MHI EP Controller registered\n");
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	return 0;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_cierr_put_dev:
149462306a36Sopenharmony_ci	put_device(&mhi_dev->dev);
149562306a36Sopenharmony_cierr_free_irq:
149662306a36Sopenharmony_ci	free_irq(mhi_cntrl->irq, mhi_cntrl);
149762306a36Sopenharmony_cierr_ida_free:
149862306a36Sopenharmony_ci	ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index);
149962306a36Sopenharmony_cierr_destroy_wq:
150062306a36Sopenharmony_ci	destroy_workqueue(mhi_cntrl->wq);
150162306a36Sopenharmony_cierr_destroy_ring_item_cache:
150262306a36Sopenharmony_ci	kmem_cache_destroy(mhi_cntrl->ring_item_cache);
150362306a36Sopenharmony_cierr_destroy_ev_ring_el_cache:
150462306a36Sopenharmony_ci	kmem_cache_destroy(mhi_cntrl->ev_ring_el_cache);
150562306a36Sopenharmony_cierr_destroy_tre_buf_cache:
150662306a36Sopenharmony_ci	kmem_cache_destroy(mhi_cntrl->tre_buf_cache);
150762306a36Sopenharmony_cierr_free_cmd:
150862306a36Sopenharmony_ci	kfree(mhi_cntrl->mhi_cmd);
150962306a36Sopenharmony_cierr_free_ch:
151062306a36Sopenharmony_ci	kfree(mhi_cntrl->mhi_chan);
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	return ret;
151362306a36Sopenharmony_ci}
151462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_ep_register_controller);
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci/*
151762306a36Sopenharmony_ci * It is expected that the controller drivers will power down the MHI EP stack
151862306a36Sopenharmony_ci * using "mhi_ep_power_down()" before calling this function to unregister themselves.
151962306a36Sopenharmony_ci */
152062306a36Sopenharmony_civoid mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl)
152162306a36Sopenharmony_ci{
152262306a36Sopenharmony_ci	struct mhi_ep_device *mhi_dev = mhi_cntrl->mhi_dev;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	destroy_workqueue(mhi_cntrl->wq);
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	free_irq(mhi_cntrl->irq, mhi_cntrl);
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	kmem_cache_destroy(mhi_cntrl->tre_buf_cache);
152962306a36Sopenharmony_ci	kmem_cache_destroy(mhi_cntrl->ev_ring_el_cache);
153062306a36Sopenharmony_ci	kmem_cache_destroy(mhi_cntrl->ring_item_cache);
153162306a36Sopenharmony_ci	kfree(mhi_cntrl->mhi_cmd);
153262306a36Sopenharmony_ci	kfree(mhi_cntrl->mhi_chan);
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	device_del(&mhi_dev->dev);
153562306a36Sopenharmony_ci	put_device(&mhi_dev->dev);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index);
153862306a36Sopenharmony_ci}
153962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_ep_unregister_controller);
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_cistatic int mhi_ep_driver_probe(struct device *dev)
154262306a36Sopenharmony_ci{
154362306a36Sopenharmony_ci	struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
154462306a36Sopenharmony_ci	struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
154562306a36Sopenharmony_ci	struct mhi_ep_chan *ul_chan = mhi_dev->ul_chan;
154662306a36Sopenharmony_ci	struct mhi_ep_chan *dl_chan = mhi_dev->dl_chan;
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	ul_chan->xfer_cb = mhi_drv->ul_xfer_cb;
154962306a36Sopenharmony_ci	dl_chan->xfer_cb = mhi_drv->dl_xfer_cb;
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	return mhi_drv->probe(mhi_dev, mhi_dev->id);
155262306a36Sopenharmony_ci}
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_cistatic int mhi_ep_driver_remove(struct device *dev)
155562306a36Sopenharmony_ci{
155662306a36Sopenharmony_ci	struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
155762306a36Sopenharmony_ci	struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
155862306a36Sopenharmony_ci	struct mhi_result result = {};
155962306a36Sopenharmony_ci	struct mhi_ep_chan *mhi_chan;
156062306a36Sopenharmony_ci	int dir;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	/* Skip if it is a controller device */
156362306a36Sopenharmony_ci	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
156462306a36Sopenharmony_ci		return 0;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	/* Disconnect the channels associated with the driver */
156762306a36Sopenharmony_ci	for (dir = 0; dir < 2; dir++) {
156862306a36Sopenharmony_ci		mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci		if (!mhi_chan)
157162306a36Sopenharmony_ci			continue;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci		mutex_lock(&mhi_chan->lock);
157462306a36Sopenharmony_ci		/* Send channel disconnect status to the client driver */
157562306a36Sopenharmony_ci		if (mhi_chan->xfer_cb) {
157662306a36Sopenharmony_ci			result.transaction_status = -ENOTCONN;
157762306a36Sopenharmony_ci			result.bytes_xferd = 0;
157862306a36Sopenharmony_ci			mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
157962306a36Sopenharmony_ci		}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci		mhi_chan->state = MHI_CH_STATE_DISABLED;
158262306a36Sopenharmony_ci		mhi_chan->xfer_cb = NULL;
158362306a36Sopenharmony_ci		mutex_unlock(&mhi_chan->lock);
158462306a36Sopenharmony_ci	}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	/* Remove the client driver now */
158762306a36Sopenharmony_ci	mhi_drv->remove(mhi_dev);
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	return 0;
159062306a36Sopenharmony_ci}
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ciint __mhi_ep_driver_register(struct mhi_ep_driver *mhi_drv, struct module *owner)
159362306a36Sopenharmony_ci{
159462306a36Sopenharmony_ci	struct device_driver *driver = &mhi_drv->driver;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	if (!mhi_drv->probe || !mhi_drv->remove)
159762306a36Sopenharmony_ci		return -EINVAL;
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	/* Client drivers should have callbacks defined for both channels */
160062306a36Sopenharmony_ci	if (!mhi_drv->ul_xfer_cb || !mhi_drv->dl_xfer_cb)
160162306a36Sopenharmony_ci		return -EINVAL;
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	driver->bus = &mhi_ep_bus_type;
160462306a36Sopenharmony_ci	driver->owner = owner;
160562306a36Sopenharmony_ci	driver->probe = mhi_ep_driver_probe;
160662306a36Sopenharmony_ci	driver->remove = mhi_ep_driver_remove;
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	return driver_register(driver);
160962306a36Sopenharmony_ci}
161062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__mhi_ep_driver_register);
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_civoid mhi_ep_driver_unregister(struct mhi_ep_driver *mhi_drv)
161362306a36Sopenharmony_ci{
161462306a36Sopenharmony_ci	driver_unregister(&mhi_drv->driver);
161562306a36Sopenharmony_ci}
161662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_ep_driver_unregister);
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_cistatic int mhi_ep_uevent(const struct device *dev, struct kobj_uevent_env *env)
161962306a36Sopenharmony_ci{
162062306a36Sopenharmony_ci	const struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	return add_uevent_var(env, "MODALIAS=" MHI_EP_DEVICE_MODALIAS_FMT,
162362306a36Sopenharmony_ci					mhi_dev->name);
162462306a36Sopenharmony_ci}
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_cistatic int mhi_ep_match(struct device *dev, struct device_driver *drv)
162762306a36Sopenharmony_ci{
162862306a36Sopenharmony_ci	struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
162962306a36Sopenharmony_ci	struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(drv);
163062306a36Sopenharmony_ci	const struct mhi_device_id *id;
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	/*
163362306a36Sopenharmony_ci	 * If the device is a controller type then there is no client driver
163462306a36Sopenharmony_ci	 * associated with it
163562306a36Sopenharmony_ci	 */
163662306a36Sopenharmony_ci	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
163762306a36Sopenharmony_ci		return 0;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	for (id = mhi_drv->id_table; id->chan[0]; id++)
164062306a36Sopenharmony_ci		if (!strcmp(mhi_dev->name, id->chan)) {
164162306a36Sopenharmony_ci			mhi_dev->id = id;
164262306a36Sopenharmony_ci			return 1;
164362306a36Sopenharmony_ci		}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	return 0;
164662306a36Sopenharmony_ci};
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_cistruct bus_type mhi_ep_bus_type = {
164962306a36Sopenharmony_ci	.name = "mhi_ep",
165062306a36Sopenharmony_ci	.dev_name = "mhi_ep",
165162306a36Sopenharmony_ci	.match = mhi_ep_match,
165262306a36Sopenharmony_ci	.uevent = mhi_ep_uevent,
165362306a36Sopenharmony_ci};
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_cistatic int __init mhi_ep_init(void)
165662306a36Sopenharmony_ci{
165762306a36Sopenharmony_ci	return bus_register(&mhi_ep_bus_type);
165862306a36Sopenharmony_ci}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_cistatic void __exit mhi_ep_exit(void)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	bus_unregister(&mhi_ep_bus_type);
166362306a36Sopenharmony_ci}
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_cipostcore_initcall(mhi_ep_init);
166662306a36Sopenharmony_cimodule_exit(mhi_ep_exit);
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
166962306a36Sopenharmony_ciMODULE_DESCRIPTION("MHI Bus Endpoint stack");
167062306a36Sopenharmony_ciMODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
1671