xref: /openbmc/linux/drivers/bus/mhi/ep/main.c (revision 7b7fd0ac7dc1ffcaf24d9bca0f051b0168e43cd4)
1d434743eSManivannan Sadhasivam // SPDX-License-Identifier: GPL-2.0
2d434743eSManivannan Sadhasivam /*
3d434743eSManivannan Sadhasivam  * MHI Endpoint bus stack
4d434743eSManivannan Sadhasivam  *
5d434743eSManivannan Sadhasivam  * Copyright (C) 2022 Linaro Ltd.
6d434743eSManivannan Sadhasivam  * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
7d434743eSManivannan Sadhasivam  */
8d434743eSManivannan Sadhasivam 
9d434743eSManivannan Sadhasivam #include <linux/bitfield.h>
10d434743eSManivannan Sadhasivam #include <linux/delay.h>
11d434743eSManivannan Sadhasivam #include <linux/dma-direction.h>
12d434743eSManivannan Sadhasivam #include <linux/interrupt.h>
13d434743eSManivannan Sadhasivam #include <linux/io.h>
144799e71bSManivannan Sadhasivam #include <linux/irq.h>
15d434743eSManivannan Sadhasivam #include <linux/mhi_ep.h>
16d434743eSManivannan Sadhasivam #include <linux/mod_devicetable.h>
17d434743eSManivannan Sadhasivam #include <linux/module.h>
18d434743eSManivannan Sadhasivam #include "internal.h"
19d434743eSManivannan Sadhasivam 
20fb3a26b7SManivannan Sadhasivam #define M0_WAIT_DELAY_MS	100
21fb3a26b7SManivannan Sadhasivam #define M0_WAIT_COUNT		100
22fb3a26b7SManivannan Sadhasivam 
23d434743eSManivannan Sadhasivam static DEFINE_IDA(mhi_ep_cntrl_ida);
24d434743eSManivannan Sadhasivam 
25e8275690SManivannan Sadhasivam static int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id);
265d507ee0SManivannan Sadhasivam static int mhi_ep_destroy_device(struct device *dev, void *data);
275d507ee0SManivannan Sadhasivam 
mhi_ep_send_event(struct mhi_ep_cntrl * mhi_cntrl,u32 ring_idx,struct mhi_ring_element * el,bool bei)28961aeb68SManivannan Sadhasivam static int mhi_ep_send_event(struct mhi_ep_cntrl *mhi_cntrl, u32 ring_idx,
29961aeb68SManivannan Sadhasivam 			     struct mhi_ring_element *el, bool bei)
30961aeb68SManivannan Sadhasivam {
31961aeb68SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
32961aeb68SManivannan Sadhasivam 	union mhi_ep_ring_ctx *ctx;
33961aeb68SManivannan Sadhasivam 	struct mhi_ep_ring *ring;
34961aeb68SManivannan Sadhasivam 	int ret;
35961aeb68SManivannan Sadhasivam 
36961aeb68SManivannan Sadhasivam 	mutex_lock(&mhi_cntrl->event_lock);
37961aeb68SManivannan Sadhasivam 	ring = &mhi_cntrl->mhi_event[ring_idx].ring;
38961aeb68SManivannan Sadhasivam 	ctx = (union mhi_ep_ring_ctx *)&mhi_cntrl->ev_ctx_cache[ring_idx];
39961aeb68SManivannan Sadhasivam 	if (!ring->started) {
40961aeb68SManivannan Sadhasivam 		ret = mhi_ep_ring_start(mhi_cntrl, ring, ctx);
41961aeb68SManivannan Sadhasivam 		if (ret) {
42961aeb68SManivannan Sadhasivam 			dev_err(dev, "Error starting event ring (%u)\n", ring_idx);
43961aeb68SManivannan Sadhasivam 			goto err_unlock;
44961aeb68SManivannan Sadhasivam 		}
45961aeb68SManivannan Sadhasivam 	}
46961aeb68SManivannan Sadhasivam 
47961aeb68SManivannan Sadhasivam 	/* Add element to the event ring */
48961aeb68SManivannan Sadhasivam 	ret = mhi_ep_ring_add_element(ring, el);
49961aeb68SManivannan Sadhasivam 	if (ret) {
50961aeb68SManivannan Sadhasivam 		dev_err(dev, "Error adding element to event ring (%u)\n", ring_idx);
51961aeb68SManivannan Sadhasivam 		goto err_unlock;
52961aeb68SManivannan Sadhasivam 	}
53961aeb68SManivannan Sadhasivam 
54961aeb68SManivannan Sadhasivam 	mutex_unlock(&mhi_cntrl->event_lock);
55961aeb68SManivannan Sadhasivam 
56961aeb68SManivannan Sadhasivam 	/*
57961aeb68SManivannan Sadhasivam 	 * Raise IRQ to host only if the BEI flag is not set in TRE. Host might
58961aeb68SManivannan Sadhasivam 	 * set this flag for interrupt moderation as per MHI protocol.
59961aeb68SManivannan Sadhasivam 	 */
60961aeb68SManivannan Sadhasivam 	if (!bei)
61961aeb68SManivannan Sadhasivam 		mhi_cntrl->raise_irq(mhi_cntrl, ring->irq_vector);
62961aeb68SManivannan Sadhasivam 
63961aeb68SManivannan Sadhasivam 	return 0;
64961aeb68SManivannan Sadhasivam 
65961aeb68SManivannan Sadhasivam err_unlock:
66961aeb68SManivannan Sadhasivam 	mutex_unlock(&mhi_cntrl->event_lock);
67961aeb68SManivannan Sadhasivam 
68961aeb68SManivannan Sadhasivam 	return ret;
69961aeb68SManivannan Sadhasivam }
70961aeb68SManivannan Sadhasivam 
mhi_ep_send_completion_event(struct mhi_ep_cntrl * mhi_cntrl,struct mhi_ep_ring * ring,struct mhi_ring_element * tre,u32 len,enum mhi_ev_ccs code)71961aeb68SManivannan Sadhasivam static int mhi_ep_send_completion_event(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring,
72961aeb68SManivannan Sadhasivam 					struct mhi_ring_element *tre, u32 len, enum mhi_ev_ccs code)
73961aeb68SManivannan Sadhasivam {
7423bab2b8SManivannan Sadhasivam 	struct mhi_ring_element *event;
7523bab2b8SManivannan Sadhasivam 	int ret;
76961aeb68SManivannan Sadhasivam 
77*43987659SManivannan Sadhasivam 	event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL);
7823bab2b8SManivannan Sadhasivam 	if (!event)
7923bab2b8SManivannan Sadhasivam 		return -ENOMEM;
80961aeb68SManivannan Sadhasivam 
8123bab2b8SManivannan Sadhasivam 	event->ptr = cpu_to_le64(ring->rbase + ring->rd_offset * sizeof(*tre));
8223bab2b8SManivannan Sadhasivam 	event->dword[0] = MHI_TRE_EV_DWORD0(code, len);
8323bab2b8SManivannan Sadhasivam 	event->dword[1] = MHI_TRE_EV_DWORD1(ring->ch_id, MHI_PKT_TYPE_TX_EVENT);
8423bab2b8SManivannan Sadhasivam 
8523bab2b8SManivannan Sadhasivam 	ret = mhi_ep_send_event(mhi_cntrl, ring->er_index, event, MHI_TRE_DATA_GET_BEI(tre));
86bd4f6f1fSManivannan Sadhasivam 	kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event);
8723bab2b8SManivannan Sadhasivam 
8823bab2b8SManivannan Sadhasivam 	return ret;
89961aeb68SManivannan Sadhasivam }
90961aeb68SManivannan Sadhasivam 
mhi_ep_send_state_change_event(struct mhi_ep_cntrl * mhi_cntrl,enum mhi_state state)91961aeb68SManivannan Sadhasivam int mhi_ep_send_state_change_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state state)
92961aeb68SManivannan Sadhasivam {
9323bab2b8SManivannan Sadhasivam 	struct mhi_ring_element *event;
9423bab2b8SManivannan Sadhasivam 	int ret;
95961aeb68SManivannan Sadhasivam 
96*43987659SManivannan Sadhasivam 	event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL);
9723bab2b8SManivannan Sadhasivam 	if (!event)
9823bab2b8SManivannan Sadhasivam 		return -ENOMEM;
99961aeb68SManivannan Sadhasivam 
10023bab2b8SManivannan Sadhasivam 	event->dword[0] = MHI_SC_EV_DWORD0(state);
10123bab2b8SManivannan Sadhasivam 	event->dword[1] = MHI_SC_EV_DWORD1(MHI_PKT_TYPE_STATE_CHANGE_EVENT);
10223bab2b8SManivannan Sadhasivam 
10323bab2b8SManivannan Sadhasivam 	ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0);
104bd4f6f1fSManivannan Sadhasivam 	kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event);
10523bab2b8SManivannan Sadhasivam 
10623bab2b8SManivannan Sadhasivam 	return ret;
107961aeb68SManivannan Sadhasivam }
108961aeb68SManivannan Sadhasivam 
mhi_ep_send_ee_event(struct mhi_ep_cntrl * mhi_cntrl,enum mhi_ee_type exec_env)109961aeb68SManivannan Sadhasivam int mhi_ep_send_ee_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_ee_type exec_env)
110961aeb68SManivannan Sadhasivam {
11123bab2b8SManivannan Sadhasivam 	struct mhi_ring_element *event;
11223bab2b8SManivannan Sadhasivam 	int ret;
113961aeb68SManivannan Sadhasivam 
114*43987659SManivannan Sadhasivam 	event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL);
11523bab2b8SManivannan Sadhasivam 	if (!event)
11623bab2b8SManivannan Sadhasivam 		return -ENOMEM;
117961aeb68SManivannan Sadhasivam 
11823bab2b8SManivannan Sadhasivam 	event->dword[0] = MHI_EE_EV_DWORD0(exec_env);
11923bab2b8SManivannan Sadhasivam 	event->dword[1] = MHI_SC_EV_DWORD1(MHI_PKT_TYPE_EE_EVENT);
12023bab2b8SManivannan Sadhasivam 
12123bab2b8SManivannan Sadhasivam 	ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0);
122bd4f6f1fSManivannan Sadhasivam 	kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event);
12323bab2b8SManivannan Sadhasivam 
12423bab2b8SManivannan Sadhasivam 	return ret;
125961aeb68SManivannan Sadhasivam }
126961aeb68SManivannan Sadhasivam 
mhi_ep_send_cmd_comp_event(struct mhi_ep_cntrl * mhi_cntrl,enum mhi_ev_ccs code)127961aeb68SManivannan Sadhasivam static int mhi_ep_send_cmd_comp_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_ev_ccs code)
128961aeb68SManivannan Sadhasivam {
129961aeb68SManivannan Sadhasivam 	struct mhi_ep_ring *ring = &mhi_cntrl->mhi_cmd->ring;
13023bab2b8SManivannan Sadhasivam 	struct mhi_ring_element *event;
13123bab2b8SManivannan Sadhasivam 	int ret;
132961aeb68SManivannan Sadhasivam 
133*43987659SManivannan Sadhasivam 	event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL);
13423bab2b8SManivannan Sadhasivam 	if (!event)
13523bab2b8SManivannan Sadhasivam 		return -ENOMEM;
136961aeb68SManivannan Sadhasivam 
13723bab2b8SManivannan Sadhasivam 	event->ptr = cpu_to_le64(ring->rbase + ring->rd_offset * sizeof(struct mhi_ring_element));
13823bab2b8SManivannan Sadhasivam 	event->dword[0] = MHI_CC_EV_DWORD0(code);
13923bab2b8SManivannan Sadhasivam 	event->dword[1] = MHI_CC_EV_DWORD1(MHI_PKT_TYPE_CMD_COMPLETION_EVENT);
14023bab2b8SManivannan Sadhasivam 
14123bab2b8SManivannan Sadhasivam 	ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0);
142bd4f6f1fSManivannan Sadhasivam 	kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event);
14323bab2b8SManivannan Sadhasivam 
14423bab2b8SManivannan Sadhasivam 	return ret;
145961aeb68SManivannan Sadhasivam }
146961aeb68SManivannan Sadhasivam 
mhi_ep_process_cmd_ring(struct mhi_ep_ring * ring,struct mhi_ring_element * el)147e8275690SManivannan Sadhasivam static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_element *el)
148e8275690SManivannan Sadhasivam {
149e8275690SManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
150e8275690SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
151e8275690SManivannan Sadhasivam 	struct mhi_result result = {};
152e8275690SManivannan Sadhasivam 	struct mhi_ep_chan *mhi_chan;
153e8275690SManivannan Sadhasivam 	struct mhi_ep_ring *ch_ring;
154e8275690SManivannan Sadhasivam 	u32 tmp, ch_id;
155e8275690SManivannan Sadhasivam 	int ret;
156e8275690SManivannan Sadhasivam 
157e8275690SManivannan Sadhasivam 	ch_id = MHI_TRE_GET_CMD_CHID(el);
1586de4941cSManivannan Sadhasivam 
1596de4941cSManivannan Sadhasivam 	/* Check if the channel is supported by the controller */
1603c54a3ffSDan Carpenter 	if ((ch_id >= mhi_cntrl->max_chan) || !mhi_cntrl->mhi_chan[ch_id].name) {
16194e19f4fSManivannan Sadhasivam 		dev_dbg(dev, "Channel (%u) not supported!\n", ch_id);
1626de4941cSManivannan Sadhasivam 		return -ENODEV;
1636de4941cSManivannan Sadhasivam 	}
1646de4941cSManivannan Sadhasivam 
165e8275690SManivannan Sadhasivam 	mhi_chan = &mhi_cntrl->mhi_chan[ch_id];
166e8275690SManivannan Sadhasivam 	ch_ring = &mhi_cntrl->mhi_chan[ch_id].ring;
167e8275690SManivannan Sadhasivam 
168e8275690SManivannan Sadhasivam 	switch (MHI_TRE_GET_CMD_TYPE(el)) {
169e8275690SManivannan Sadhasivam 	case MHI_PKT_TYPE_START_CHAN_CMD:
170e8275690SManivannan Sadhasivam 		dev_dbg(dev, "Received START command for channel (%u)\n", ch_id);
171e8275690SManivannan Sadhasivam 
172e8275690SManivannan Sadhasivam 		mutex_lock(&mhi_chan->lock);
173e8275690SManivannan Sadhasivam 		/* Initialize and configure the corresponding channel ring */
174e8275690SManivannan Sadhasivam 		if (!ch_ring->started) {
175e8275690SManivannan Sadhasivam 			ret = mhi_ep_ring_start(mhi_cntrl, ch_ring,
176e8275690SManivannan Sadhasivam 				(union mhi_ep_ring_ctx *)&mhi_cntrl->ch_ctx_cache[ch_id]);
177e8275690SManivannan Sadhasivam 			if (ret) {
178e8275690SManivannan Sadhasivam 				dev_err(dev, "Failed to start ring for channel (%u)\n", ch_id);
179e8275690SManivannan Sadhasivam 				ret = mhi_ep_send_cmd_comp_event(mhi_cntrl,
180e8275690SManivannan Sadhasivam 							MHI_EV_CC_UNDEFINED_ERR);
181e8275690SManivannan Sadhasivam 				if (ret)
182e8275690SManivannan Sadhasivam 					dev_err(dev, "Error sending completion event: %d\n", ret);
183e8275690SManivannan Sadhasivam 
184e8275690SManivannan Sadhasivam 				goto err_unlock;
185e8275690SManivannan Sadhasivam 			}
186b6af3a95SManivannan Sadhasivam 
187b6af3a95SManivannan Sadhasivam 			mhi_chan->rd_offset = ch_ring->rd_offset;
188e8275690SManivannan Sadhasivam 		}
189e8275690SManivannan Sadhasivam 
190e8275690SManivannan Sadhasivam 		/* Set channel state to RUNNING */
191e8275690SManivannan Sadhasivam 		mhi_chan->state = MHI_CH_STATE_RUNNING;
192e8275690SManivannan Sadhasivam 		tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg);
193e8275690SManivannan Sadhasivam 		tmp &= ~CHAN_CTX_CHSTATE_MASK;
194e8275690SManivannan Sadhasivam 		tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_RUNNING);
195e8275690SManivannan Sadhasivam 		mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp);
196e8275690SManivannan Sadhasivam 
197e8275690SManivannan Sadhasivam 		ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS);
198e8275690SManivannan Sadhasivam 		if (ret) {
199e8275690SManivannan Sadhasivam 			dev_err(dev, "Error sending command completion event (%u)\n",
200e8275690SManivannan Sadhasivam 				MHI_EV_CC_SUCCESS);
201e8275690SManivannan Sadhasivam 			goto err_unlock;
202e8275690SManivannan Sadhasivam 		}
203e8275690SManivannan Sadhasivam 
204e8275690SManivannan Sadhasivam 		mutex_unlock(&mhi_chan->lock);
205e8275690SManivannan Sadhasivam 
206e8275690SManivannan Sadhasivam 		/*
207e8275690SManivannan Sadhasivam 		 * Create MHI device only during UL channel start. Since the MHI
208e8275690SManivannan Sadhasivam 		 * channels operate in a pair, we'll associate both UL and DL
209e8275690SManivannan Sadhasivam 		 * channels to the same device.
210e8275690SManivannan Sadhasivam 		 *
211e8275690SManivannan Sadhasivam 		 * We also need to check for mhi_dev != NULL because, the host
212e8275690SManivannan Sadhasivam 		 * will issue START_CHAN command during resume and we don't
213e8275690SManivannan Sadhasivam 		 * destroy the device during suspend.
214e8275690SManivannan Sadhasivam 		 */
215e8275690SManivannan Sadhasivam 		if (!(ch_id % 2) && !mhi_chan->mhi_dev) {
216e8275690SManivannan Sadhasivam 			ret = mhi_ep_create_device(mhi_cntrl, ch_id);
217e8275690SManivannan Sadhasivam 			if (ret) {
218e8275690SManivannan Sadhasivam 				dev_err(dev, "Error creating device for channel (%u)\n", ch_id);
219e8275690SManivannan Sadhasivam 				mhi_ep_handle_syserr(mhi_cntrl);
220e8275690SManivannan Sadhasivam 				return ret;
221e8275690SManivannan Sadhasivam 			}
222e8275690SManivannan Sadhasivam 		}
223e8275690SManivannan Sadhasivam 
224e8275690SManivannan Sadhasivam 		/* Finally, enable DB for the channel */
225e8275690SManivannan Sadhasivam 		mhi_ep_mmio_enable_chdb(mhi_cntrl, ch_id);
226e8275690SManivannan Sadhasivam 
227e8275690SManivannan Sadhasivam 		break;
228e8275690SManivannan Sadhasivam 	case MHI_PKT_TYPE_STOP_CHAN_CMD:
229e8275690SManivannan Sadhasivam 		dev_dbg(dev, "Received STOP command for channel (%u)\n", ch_id);
230e8275690SManivannan Sadhasivam 		if (!ch_ring->started) {
231e8275690SManivannan Sadhasivam 			dev_err(dev, "Channel (%u) not opened\n", ch_id);
232e8275690SManivannan Sadhasivam 			return -ENODEV;
233e8275690SManivannan Sadhasivam 		}
234e8275690SManivannan Sadhasivam 
235e8275690SManivannan Sadhasivam 		mutex_lock(&mhi_chan->lock);
236e8275690SManivannan Sadhasivam 		/* Disable DB for the channel */
237e8275690SManivannan Sadhasivam 		mhi_ep_mmio_disable_chdb(mhi_cntrl, ch_id);
238e8275690SManivannan Sadhasivam 
239e8275690SManivannan Sadhasivam 		/* Send channel disconnect status to client drivers */
240e6cebcc2SManivannan Sadhasivam 		if (mhi_chan->xfer_cb) {
241e8275690SManivannan Sadhasivam 			result.transaction_status = -ENOTCONN;
242e8275690SManivannan Sadhasivam 			result.bytes_xferd = 0;
243e8275690SManivannan Sadhasivam 			mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
244e6cebcc2SManivannan Sadhasivam 		}
245e8275690SManivannan Sadhasivam 
246e8275690SManivannan Sadhasivam 		/* Set channel state to STOP */
247e8275690SManivannan Sadhasivam 		mhi_chan->state = MHI_CH_STATE_STOP;
248e8275690SManivannan Sadhasivam 		tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg);
249e8275690SManivannan Sadhasivam 		tmp &= ~CHAN_CTX_CHSTATE_MASK;
250e8275690SManivannan Sadhasivam 		tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_STOP);
251e8275690SManivannan Sadhasivam 		mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp);
252e8275690SManivannan Sadhasivam 
253e8275690SManivannan Sadhasivam 		ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS);
254e8275690SManivannan Sadhasivam 		if (ret) {
255e8275690SManivannan Sadhasivam 			dev_err(dev, "Error sending command completion event (%u)\n",
256e8275690SManivannan Sadhasivam 				MHI_EV_CC_SUCCESS);
257e8275690SManivannan Sadhasivam 			goto err_unlock;
258e8275690SManivannan Sadhasivam 		}
259e8275690SManivannan Sadhasivam 
260e8275690SManivannan Sadhasivam 		mutex_unlock(&mhi_chan->lock);
261e8275690SManivannan Sadhasivam 		break;
262e8275690SManivannan Sadhasivam 	case MHI_PKT_TYPE_RESET_CHAN_CMD:
2638e697fcfSManivannan Sadhasivam 		dev_dbg(dev, "Received RESET command for channel (%u)\n", ch_id);
264e8275690SManivannan Sadhasivam 		if (!ch_ring->started) {
265e8275690SManivannan Sadhasivam 			dev_err(dev, "Channel (%u) not opened\n", ch_id);
266e8275690SManivannan Sadhasivam 			return -ENODEV;
267e8275690SManivannan Sadhasivam 		}
268e8275690SManivannan Sadhasivam 
269e8275690SManivannan Sadhasivam 		mutex_lock(&mhi_chan->lock);
270e8275690SManivannan Sadhasivam 		/* Stop and reset the transfer ring */
271e8275690SManivannan Sadhasivam 		mhi_ep_ring_reset(mhi_cntrl, ch_ring);
272e8275690SManivannan Sadhasivam 
273e8275690SManivannan Sadhasivam 		/* Send channel disconnect status to client driver */
274e6cebcc2SManivannan Sadhasivam 		if (mhi_chan->xfer_cb) {
275e8275690SManivannan Sadhasivam 			result.transaction_status = -ENOTCONN;
276e8275690SManivannan Sadhasivam 			result.bytes_xferd = 0;
277e8275690SManivannan Sadhasivam 			mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
278e6cebcc2SManivannan Sadhasivam 		}
279e8275690SManivannan Sadhasivam 
280e8275690SManivannan Sadhasivam 		/* Set channel state to DISABLED */
281e8275690SManivannan Sadhasivam 		mhi_chan->state = MHI_CH_STATE_DISABLED;
282e8275690SManivannan Sadhasivam 		tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg);
283e8275690SManivannan Sadhasivam 		tmp &= ~CHAN_CTX_CHSTATE_MASK;
284e8275690SManivannan Sadhasivam 		tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_DISABLED);
285e8275690SManivannan Sadhasivam 		mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp);
286e8275690SManivannan Sadhasivam 
287e8275690SManivannan Sadhasivam 		ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS);
288e8275690SManivannan Sadhasivam 		if (ret) {
289e8275690SManivannan Sadhasivam 			dev_err(dev, "Error sending command completion event (%u)\n",
290e8275690SManivannan Sadhasivam 				MHI_EV_CC_SUCCESS);
291e8275690SManivannan Sadhasivam 			goto err_unlock;
292e8275690SManivannan Sadhasivam 		}
293e8275690SManivannan Sadhasivam 
294e8275690SManivannan Sadhasivam 		mutex_unlock(&mhi_chan->lock);
295e8275690SManivannan Sadhasivam 		break;
296e8275690SManivannan Sadhasivam 	default:
297e8275690SManivannan Sadhasivam 		dev_err(dev, "Invalid command received: %lu for channel (%u)\n",
298e8275690SManivannan Sadhasivam 			MHI_TRE_GET_CMD_TYPE(el), ch_id);
299e8275690SManivannan Sadhasivam 		return -EINVAL;
300e8275690SManivannan Sadhasivam 	}
301e8275690SManivannan Sadhasivam 
302e8275690SManivannan Sadhasivam 	return 0;
303e8275690SManivannan Sadhasivam 
304e8275690SManivannan Sadhasivam err_unlock:
305e8275690SManivannan Sadhasivam 	mutex_unlock(&mhi_chan->lock);
306e8275690SManivannan Sadhasivam 
307e8275690SManivannan Sadhasivam 	return ret;
308e8275690SManivannan Sadhasivam }
309e8275690SManivannan Sadhasivam 
mhi_ep_queue_is_empty(struct mhi_ep_device * mhi_dev,enum dma_data_direction dir)31053012588SManivannan Sadhasivam bool mhi_ep_queue_is_empty(struct mhi_ep_device *mhi_dev, enum dma_data_direction dir)
31153012588SManivannan Sadhasivam {
31253012588SManivannan Sadhasivam 	struct mhi_ep_chan *mhi_chan = (dir == DMA_FROM_DEVICE) ? mhi_dev->dl_chan :
31353012588SManivannan Sadhasivam 								mhi_dev->ul_chan;
31453012588SManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
31553012588SManivannan Sadhasivam 	struct mhi_ep_ring *ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring;
31653012588SManivannan Sadhasivam 
317b6af3a95SManivannan Sadhasivam 	return !!(mhi_chan->rd_offset == ring->wr_offset);
31853012588SManivannan Sadhasivam }
31953012588SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_ep_queue_is_empty);
32053012588SManivannan Sadhasivam 
mhi_ep_read_completion(struct mhi_ep_buf_info * buf_info)32139601f49SManivannan Sadhasivam static void mhi_ep_read_completion(struct mhi_ep_buf_info *buf_info)
32239601f49SManivannan Sadhasivam {
32339601f49SManivannan Sadhasivam 	struct mhi_ep_device *mhi_dev = buf_info->mhi_dev;
32439601f49SManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
32539601f49SManivannan Sadhasivam 	struct mhi_ep_chan *mhi_chan = mhi_dev->ul_chan;
32639601f49SManivannan Sadhasivam 	struct mhi_ep_ring *ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring;
32739601f49SManivannan Sadhasivam 	struct mhi_ring_element *el = &ring->ring_cache[ring->rd_offset];
32839601f49SManivannan Sadhasivam 	struct mhi_result result = {};
32939601f49SManivannan Sadhasivam 	int ret;
33039601f49SManivannan Sadhasivam 
33139601f49SManivannan Sadhasivam 	if (mhi_chan->xfer_cb) {
33239601f49SManivannan Sadhasivam 		result.buf_addr = buf_info->cb_buf;
33339601f49SManivannan Sadhasivam 		result.dir = mhi_chan->dir;
33439601f49SManivannan Sadhasivam 		result.bytes_xferd = buf_info->size;
33539601f49SManivannan Sadhasivam 
33639601f49SManivannan Sadhasivam 		mhi_chan->xfer_cb(mhi_dev, &result);
33739601f49SManivannan Sadhasivam 	}
33839601f49SManivannan Sadhasivam 
33939601f49SManivannan Sadhasivam 	/*
34039601f49SManivannan Sadhasivam 	 * The host will split the data packet into multiple TREs if it can't fit
34139601f49SManivannan Sadhasivam 	 * the packet in a single TRE. In that case, CHAIN flag will be set by the
34239601f49SManivannan Sadhasivam 	 * host for all TREs except the last one.
34339601f49SManivannan Sadhasivam 	 */
34439601f49SManivannan Sadhasivam 	if (buf_info->code != MHI_EV_CC_OVERFLOW) {
34539601f49SManivannan Sadhasivam 		if (MHI_TRE_DATA_GET_CHAIN(el)) {
34639601f49SManivannan Sadhasivam 			/*
34739601f49SManivannan Sadhasivam 			 * IEOB (Interrupt on End of Block) flag will be set by the host if
34839601f49SManivannan Sadhasivam 			 * it expects the completion event for all TREs of a TD.
34939601f49SManivannan Sadhasivam 			 */
35039601f49SManivannan Sadhasivam 			if (MHI_TRE_DATA_GET_IEOB(el)) {
35139601f49SManivannan Sadhasivam 				ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el,
35239601f49SManivannan Sadhasivam 							     MHI_TRE_DATA_GET_LEN(el),
35339601f49SManivannan Sadhasivam 							     MHI_EV_CC_EOB);
35439601f49SManivannan Sadhasivam 				if (ret < 0) {
35539601f49SManivannan Sadhasivam 					dev_err(&mhi_chan->mhi_dev->dev,
35639601f49SManivannan Sadhasivam 						"Error sending transfer compl. event\n");
35739601f49SManivannan Sadhasivam 					goto err_free_tre_buf;
35839601f49SManivannan Sadhasivam 				}
35939601f49SManivannan Sadhasivam 			}
36039601f49SManivannan Sadhasivam 		} else {
36139601f49SManivannan Sadhasivam 			/*
36239601f49SManivannan Sadhasivam 			 * IEOT (Interrupt on End of Transfer) flag will be set by the host
36339601f49SManivannan Sadhasivam 			 * for the last TRE of the TD and expects the completion event for
36439601f49SManivannan Sadhasivam 			 * the same.
36539601f49SManivannan Sadhasivam 			 */
36639601f49SManivannan Sadhasivam 			if (MHI_TRE_DATA_GET_IEOT(el)) {
36739601f49SManivannan Sadhasivam 				ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el,
36839601f49SManivannan Sadhasivam 							     MHI_TRE_DATA_GET_LEN(el),
36939601f49SManivannan Sadhasivam 							     MHI_EV_CC_EOT);
37039601f49SManivannan Sadhasivam 				if (ret < 0) {
37139601f49SManivannan Sadhasivam 					dev_err(&mhi_chan->mhi_dev->dev,
37239601f49SManivannan Sadhasivam 						"Error sending transfer compl. event\n");
37339601f49SManivannan Sadhasivam 					goto err_free_tre_buf;
37439601f49SManivannan Sadhasivam 				}
37539601f49SManivannan Sadhasivam 			}
37639601f49SManivannan Sadhasivam 		}
37739601f49SManivannan Sadhasivam 	}
37839601f49SManivannan Sadhasivam 
37939601f49SManivannan Sadhasivam 	mhi_ep_ring_inc_index(ring);
38039601f49SManivannan Sadhasivam 
38139601f49SManivannan Sadhasivam err_free_tre_buf:
38239601f49SManivannan Sadhasivam 	kmem_cache_free(mhi_cntrl->tre_buf_cache, buf_info->cb_buf);
38339601f49SManivannan Sadhasivam }
38439601f49SManivannan Sadhasivam 
mhi_ep_read_channel(struct mhi_ep_cntrl * mhi_cntrl,struct mhi_ep_ring * ring)38553012588SManivannan Sadhasivam static int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl,
38639601f49SManivannan Sadhasivam 			       struct mhi_ep_ring *ring)
38753012588SManivannan Sadhasivam {
38853012588SManivannan Sadhasivam 	struct mhi_ep_chan *mhi_chan = &mhi_cntrl->mhi_chan[ring->ch_id];
38953012588SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
39053012588SManivannan Sadhasivam 	size_t tr_len, read_offset, write_offset;
391ad671dfcSManivannan Sadhasivam 	struct mhi_ep_buf_info buf_info = {};
39239601f49SManivannan Sadhasivam 	u32 len = MHI_EP_DEFAULT_MTU;
39353012588SManivannan Sadhasivam 	struct mhi_ring_element *el;
39453012588SManivannan Sadhasivam 	bool tr_done = false;
39539601f49SManivannan Sadhasivam 	void *buf_addr;
39653012588SManivannan Sadhasivam 	u32 buf_left;
39753012588SManivannan Sadhasivam 	int ret;
39853012588SManivannan Sadhasivam 
39953012588SManivannan Sadhasivam 	buf_left = len;
40053012588SManivannan Sadhasivam 
40153012588SManivannan Sadhasivam 	do {
40253012588SManivannan Sadhasivam 		/* Don't process the transfer ring if the channel is not in RUNNING state */
40353012588SManivannan Sadhasivam 		if (mhi_chan->state != MHI_CH_STATE_RUNNING) {
40453012588SManivannan Sadhasivam 			dev_err(dev, "Channel not available\n");
40553012588SManivannan Sadhasivam 			return -ENODEV;
40653012588SManivannan Sadhasivam 		}
40753012588SManivannan Sadhasivam 
408b6af3a95SManivannan Sadhasivam 		el = &ring->ring_cache[mhi_chan->rd_offset];
40953012588SManivannan Sadhasivam 
41053012588SManivannan Sadhasivam 		/* Check if there is data pending to be read from previous read operation */
41153012588SManivannan Sadhasivam 		if (mhi_chan->tre_bytes_left) {
41253012588SManivannan Sadhasivam 			dev_dbg(dev, "TRE bytes remaining: %u\n", mhi_chan->tre_bytes_left);
41353012588SManivannan Sadhasivam 			tr_len = min(buf_left, mhi_chan->tre_bytes_left);
41453012588SManivannan Sadhasivam 		} else {
41553012588SManivannan Sadhasivam 			mhi_chan->tre_loc = MHI_TRE_DATA_GET_PTR(el);
41653012588SManivannan Sadhasivam 			mhi_chan->tre_size = MHI_TRE_DATA_GET_LEN(el);
41753012588SManivannan Sadhasivam 			mhi_chan->tre_bytes_left = mhi_chan->tre_size;
41853012588SManivannan Sadhasivam 
41953012588SManivannan Sadhasivam 			tr_len = min(buf_left, mhi_chan->tre_size);
42053012588SManivannan Sadhasivam 		}
42153012588SManivannan Sadhasivam 
42253012588SManivannan Sadhasivam 		read_offset = mhi_chan->tre_size - mhi_chan->tre_bytes_left;
42353012588SManivannan Sadhasivam 		write_offset = len - buf_left;
424ad671dfcSManivannan Sadhasivam 
425*43987659SManivannan Sadhasivam 		buf_addr = kmem_cache_zalloc(mhi_cntrl->tre_buf_cache, GFP_KERNEL);
42639601f49SManivannan Sadhasivam 		if (!buf_addr)
42739601f49SManivannan Sadhasivam 			return -ENOMEM;
42839601f49SManivannan Sadhasivam 
429ad671dfcSManivannan Sadhasivam 		buf_info.host_addr = mhi_chan->tre_loc + read_offset;
43039601f49SManivannan Sadhasivam 		buf_info.dev_addr = buf_addr + write_offset;
431ad671dfcSManivannan Sadhasivam 		buf_info.size = tr_len;
43239601f49SManivannan Sadhasivam 		buf_info.cb = mhi_ep_read_completion;
43339601f49SManivannan Sadhasivam 		buf_info.cb_buf = buf_addr;
43439601f49SManivannan Sadhasivam 		buf_info.mhi_dev = mhi_chan->mhi_dev;
43539601f49SManivannan Sadhasivam 
43639601f49SManivannan Sadhasivam 		if (mhi_chan->tre_bytes_left - tr_len)
43739601f49SManivannan Sadhasivam 			buf_info.code = MHI_EV_CC_OVERFLOW;
43853012588SManivannan Sadhasivam 
43953012588SManivannan Sadhasivam 		dev_dbg(dev, "Reading %zd bytes from channel (%u)\n", tr_len, ring->ch_id);
44039601f49SManivannan Sadhasivam 		ret = mhi_cntrl->read_async(mhi_cntrl, &buf_info);
44153012588SManivannan Sadhasivam 		if (ret < 0) {
44253012588SManivannan Sadhasivam 			dev_err(&mhi_chan->mhi_dev->dev, "Error reading from channel\n");
44339601f49SManivannan Sadhasivam 			goto err_free_buf_addr;
44453012588SManivannan Sadhasivam 		}
44553012588SManivannan Sadhasivam 
44653012588SManivannan Sadhasivam 		buf_left -= tr_len;
44753012588SManivannan Sadhasivam 		mhi_chan->tre_bytes_left -= tr_len;
44853012588SManivannan Sadhasivam 
44953012588SManivannan Sadhasivam 		if (!mhi_chan->tre_bytes_left) {
45039601f49SManivannan Sadhasivam 			if (MHI_TRE_DATA_GET_IEOT(el))
45153012588SManivannan Sadhasivam 				tr_done = true;
45253012588SManivannan Sadhasivam 
453b6af3a95SManivannan Sadhasivam 			mhi_chan->rd_offset = (mhi_chan->rd_offset + 1) % ring->ring_size;
45453012588SManivannan Sadhasivam 		}
45553012588SManivannan Sadhasivam 	} while (buf_left && !tr_done);
45653012588SManivannan Sadhasivam 
45753012588SManivannan Sadhasivam 	return 0;
45839601f49SManivannan Sadhasivam 
45939601f49SManivannan Sadhasivam err_free_buf_addr:
46039601f49SManivannan Sadhasivam 	kmem_cache_free(mhi_cntrl->tre_buf_cache, buf_addr);
46139601f49SManivannan Sadhasivam 
46239601f49SManivannan Sadhasivam 	return ret;
46353012588SManivannan Sadhasivam }
46453012588SManivannan Sadhasivam 
mhi_ep_process_ch_ring(struct mhi_ep_ring * ring)46539601f49SManivannan Sadhasivam static int mhi_ep_process_ch_ring(struct mhi_ep_ring *ring)
46603c0bb8eSManivannan Sadhasivam {
46703c0bb8eSManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
46803c0bb8eSManivannan Sadhasivam 	struct mhi_result result = {};
46903c0bb8eSManivannan Sadhasivam 	struct mhi_ep_chan *mhi_chan;
47003c0bb8eSManivannan Sadhasivam 	int ret;
47103c0bb8eSManivannan Sadhasivam 
47203c0bb8eSManivannan Sadhasivam 	mhi_chan = &mhi_cntrl->mhi_chan[ring->ch_id];
47303c0bb8eSManivannan Sadhasivam 
47403c0bb8eSManivannan Sadhasivam 	/*
47503c0bb8eSManivannan Sadhasivam 	 * Bail out if transfer callback is not registered for the channel.
47603c0bb8eSManivannan Sadhasivam 	 * This is most likely due to the client driver not loaded at this point.
47703c0bb8eSManivannan Sadhasivam 	 */
47803c0bb8eSManivannan Sadhasivam 	if (!mhi_chan->xfer_cb) {
47903c0bb8eSManivannan Sadhasivam 		dev_err(&mhi_chan->mhi_dev->dev, "Client driver not available\n");
48003c0bb8eSManivannan Sadhasivam 		return -ENODEV;
48103c0bb8eSManivannan Sadhasivam 	}
48203c0bb8eSManivannan Sadhasivam 
48303c0bb8eSManivannan Sadhasivam 	if (ring->ch_id % 2) {
48403c0bb8eSManivannan Sadhasivam 		/* DL channel */
48503c0bb8eSManivannan Sadhasivam 		result.dir = mhi_chan->dir;
48603c0bb8eSManivannan Sadhasivam 		mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
48703c0bb8eSManivannan Sadhasivam 	} else {
48803c0bb8eSManivannan Sadhasivam 		/* UL channel */
48903c0bb8eSManivannan Sadhasivam 		do {
49039601f49SManivannan Sadhasivam 			ret = mhi_ep_read_channel(mhi_cntrl, ring);
49103c0bb8eSManivannan Sadhasivam 			if (ret < 0) {
49203c0bb8eSManivannan Sadhasivam 				dev_err(&mhi_chan->mhi_dev->dev, "Failed to read channel\n");
49303c0bb8eSManivannan Sadhasivam 				return ret;
49403c0bb8eSManivannan Sadhasivam 			}
49503c0bb8eSManivannan Sadhasivam 
49603c0bb8eSManivannan Sadhasivam 			/* Read until the ring becomes empty */
49703c0bb8eSManivannan Sadhasivam 		} while (!mhi_ep_queue_is_empty(mhi_chan->mhi_dev, DMA_TO_DEVICE));
49803c0bb8eSManivannan Sadhasivam 	}
49903c0bb8eSManivannan Sadhasivam 
50003c0bb8eSManivannan Sadhasivam 	return 0;
50103c0bb8eSManivannan Sadhasivam }
50203c0bb8eSManivannan Sadhasivam 
mhi_ep_skb_completion(struct mhi_ep_buf_info * buf_info)503b6af3a95SManivannan Sadhasivam static void mhi_ep_skb_completion(struct mhi_ep_buf_info *buf_info)
504b6af3a95SManivannan Sadhasivam {
505b6af3a95SManivannan Sadhasivam 	struct mhi_ep_device *mhi_dev = buf_info->mhi_dev;
506b6af3a95SManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
507b6af3a95SManivannan Sadhasivam 	struct mhi_ep_chan *mhi_chan = mhi_dev->dl_chan;
508b6af3a95SManivannan Sadhasivam 	struct mhi_ep_ring *ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring;
509b6af3a95SManivannan Sadhasivam 	struct mhi_ring_element *el = &ring->ring_cache[ring->rd_offset];
510b6af3a95SManivannan Sadhasivam 	struct device *dev = &mhi_dev->dev;
511b6af3a95SManivannan Sadhasivam 	struct mhi_result result = {};
512b6af3a95SManivannan Sadhasivam 	int ret;
513b6af3a95SManivannan Sadhasivam 
514b6af3a95SManivannan Sadhasivam 	if (mhi_chan->xfer_cb) {
515b6af3a95SManivannan Sadhasivam 		result.buf_addr = buf_info->cb_buf;
516b6af3a95SManivannan Sadhasivam 		result.dir = mhi_chan->dir;
517b6af3a95SManivannan Sadhasivam 		result.bytes_xferd = buf_info->size;
518b6af3a95SManivannan Sadhasivam 
519b6af3a95SManivannan Sadhasivam 		mhi_chan->xfer_cb(mhi_dev, &result);
520b6af3a95SManivannan Sadhasivam 	}
521b6af3a95SManivannan Sadhasivam 
522b6af3a95SManivannan Sadhasivam 	ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, buf_info->size,
523b6af3a95SManivannan Sadhasivam 					   buf_info->code);
524b6af3a95SManivannan Sadhasivam 	if (ret) {
525b6af3a95SManivannan Sadhasivam 		dev_err(dev, "Error sending transfer completion event\n");
526b6af3a95SManivannan Sadhasivam 		return;
527b6af3a95SManivannan Sadhasivam 	}
528b6af3a95SManivannan Sadhasivam 
529b6af3a95SManivannan Sadhasivam 	mhi_ep_ring_inc_index(ring);
530b6af3a95SManivannan Sadhasivam }
531b6af3a95SManivannan Sadhasivam 
5322d945a39SManivannan Sadhasivam /* TODO: Handle partially formed TDs */
mhi_ep_queue_skb(struct mhi_ep_device * mhi_dev,struct sk_buff * skb)5332d945a39SManivannan Sadhasivam int mhi_ep_queue_skb(struct mhi_ep_device *mhi_dev, struct sk_buff *skb)
5342d945a39SManivannan Sadhasivam {
5352d945a39SManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
5362d945a39SManivannan Sadhasivam 	struct mhi_ep_chan *mhi_chan = mhi_dev->dl_chan;
5372d945a39SManivannan Sadhasivam 	struct device *dev = &mhi_chan->mhi_dev->dev;
538ad671dfcSManivannan Sadhasivam 	struct mhi_ep_buf_info buf_info = {};
5392d945a39SManivannan Sadhasivam 	struct mhi_ring_element *el;
5402d945a39SManivannan Sadhasivam 	u32 buf_left, read_offset;
5412d945a39SManivannan Sadhasivam 	struct mhi_ep_ring *ring;
5422d945a39SManivannan Sadhasivam 	size_t tr_len;
5432d945a39SManivannan Sadhasivam 	u32 tre_len;
5442d945a39SManivannan Sadhasivam 	int ret;
5452d945a39SManivannan Sadhasivam 
5462d945a39SManivannan Sadhasivam 	buf_left = skb->len;
5472d945a39SManivannan Sadhasivam 	ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring;
5482d945a39SManivannan Sadhasivam 
5492d945a39SManivannan Sadhasivam 	mutex_lock(&mhi_chan->lock);
5502d945a39SManivannan Sadhasivam 
5512d945a39SManivannan Sadhasivam 	do {
5522d945a39SManivannan Sadhasivam 		/* Don't process the transfer ring if the channel is not in RUNNING state */
5532d945a39SManivannan Sadhasivam 		if (mhi_chan->state != MHI_CH_STATE_RUNNING) {
5542d945a39SManivannan Sadhasivam 			dev_err(dev, "Channel not available\n");
5552d945a39SManivannan Sadhasivam 			ret = -ENODEV;
5562d945a39SManivannan Sadhasivam 			goto err_exit;
5572d945a39SManivannan Sadhasivam 		}
5582d945a39SManivannan Sadhasivam 
5592d945a39SManivannan Sadhasivam 		if (mhi_ep_queue_is_empty(mhi_dev, DMA_FROM_DEVICE)) {
5602d945a39SManivannan Sadhasivam 			dev_err(dev, "TRE not available!\n");
5612d945a39SManivannan Sadhasivam 			ret = -ENOSPC;
5622d945a39SManivannan Sadhasivam 			goto err_exit;
5632d945a39SManivannan Sadhasivam 		}
5642d945a39SManivannan Sadhasivam 
565b6af3a95SManivannan Sadhasivam 		el = &ring->ring_cache[mhi_chan->rd_offset];
5662d945a39SManivannan Sadhasivam 		tre_len = MHI_TRE_DATA_GET_LEN(el);
5672d945a39SManivannan Sadhasivam 
5682d945a39SManivannan Sadhasivam 		tr_len = min(buf_left, tre_len);
5692d945a39SManivannan Sadhasivam 		read_offset = skb->len - buf_left;
570ad671dfcSManivannan Sadhasivam 
571ad671dfcSManivannan Sadhasivam 		buf_info.dev_addr = skb->data + read_offset;
572ad671dfcSManivannan Sadhasivam 		buf_info.host_addr = MHI_TRE_DATA_GET_PTR(el);
573ad671dfcSManivannan Sadhasivam 		buf_info.size = tr_len;
574b6af3a95SManivannan Sadhasivam 		buf_info.cb = mhi_ep_skb_completion;
575b6af3a95SManivannan Sadhasivam 		buf_info.cb_buf = skb;
576b6af3a95SManivannan Sadhasivam 		buf_info.mhi_dev = mhi_dev;
5772d945a39SManivannan Sadhasivam 
5782d945a39SManivannan Sadhasivam 		/*
5792d945a39SManivannan Sadhasivam 		 * For all TREs queued by the host for DL channel, only the EOT flag will be set.
5802d945a39SManivannan Sadhasivam 		 * If the packet doesn't fit into a single TRE, send the OVERFLOW event to
5812d945a39SManivannan Sadhasivam 		 * the host so that the host can adjust the packet boundary to next TREs. Else send
5822d945a39SManivannan Sadhasivam 		 * the EOT event to the host indicating the packet boundary.
5832d945a39SManivannan Sadhasivam 		 */
584b6af3a95SManivannan Sadhasivam 		if (buf_left - tr_len)
585b6af3a95SManivannan Sadhasivam 			buf_info.code = MHI_EV_CC_OVERFLOW;
5862d945a39SManivannan Sadhasivam 		else
587b6af3a95SManivannan Sadhasivam 			buf_info.code = MHI_EV_CC_EOT;
5882d945a39SManivannan Sadhasivam 
589b6af3a95SManivannan Sadhasivam 		dev_dbg(dev, "Writing %zd bytes to channel (%u)\n", tr_len, ring->ch_id);
590b6af3a95SManivannan Sadhasivam 		ret = mhi_cntrl->write_async(mhi_cntrl, &buf_info);
591b6af3a95SManivannan Sadhasivam 		if (ret < 0) {
592b6af3a95SManivannan Sadhasivam 			dev_err(dev, "Error writing to the channel\n");
5932d945a39SManivannan Sadhasivam 			goto err_exit;
5942d945a39SManivannan Sadhasivam 		}
5952d945a39SManivannan Sadhasivam 
596b6af3a95SManivannan Sadhasivam 		buf_left -= tr_len;
597b6af3a95SManivannan Sadhasivam 
598b6af3a95SManivannan Sadhasivam 		/*
599b6af3a95SManivannan Sadhasivam 		 * Update the read offset cached in mhi_chan. Actual read offset
600b6af3a95SManivannan Sadhasivam 		 * will be updated by the completion handler.
601b6af3a95SManivannan Sadhasivam 		 */
602b6af3a95SManivannan Sadhasivam 		mhi_chan->rd_offset = (mhi_chan->rd_offset + 1) % ring->ring_size;
6032d945a39SManivannan Sadhasivam 	} while (buf_left);
6042d945a39SManivannan Sadhasivam 
6052d945a39SManivannan Sadhasivam 	mutex_unlock(&mhi_chan->lock);
6062d945a39SManivannan Sadhasivam 
6072d945a39SManivannan Sadhasivam 	return 0;
6082d945a39SManivannan Sadhasivam 
6092d945a39SManivannan Sadhasivam err_exit:
6102d945a39SManivannan Sadhasivam 	mutex_unlock(&mhi_chan->lock);
6112d945a39SManivannan Sadhasivam 
6122d945a39SManivannan Sadhasivam 	return ret;
6132d945a39SManivannan Sadhasivam }
6142d945a39SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_ep_queue_skb);
6152d945a39SManivannan Sadhasivam 
mhi_ep_cache_host_cfg(struct mhi_ep_cntrl * mhi_cntrl)616fb3a26b7SManivannan Sadhasivam static int mhi_ep_cache_host_cfg(struct mhi_ep_cntrl *mhi_cntrl)
617fb3a26b7SManivannan Sadhasivam {
618fb3a26b7SManivannan Sadhasivam 	size_t cmd_ctx_host_size, ch_ctx_host_size, ev_ctx_host_size;
619fb3a26b7SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
620fb3a26b7SManivannan Sadhasivam 	int ret;
621fb3a26b7SManivannan Sadhasivam 
622fb3a26b7SManivannan Sadhasivam 	/* Update the number of event rings (NER) programmed by the host */
623fb3a26b7SManivannan Sadhasivam 	mhi_ep_mmio_update_ner(mhi_cntrl);
624fb3a26b7SManivannan Sadhasivam 
625fb3a26b7SManivannan Sadhasivam 	dev_dbg(dev, "Number of Event rings: %u, HW Event rings: %u\n",
626fb3a26b7SManivannan Sadhasivam 		 mhi_cntrl->event_rings, mhi_cntrl->hw_event_rings);
627fb3a26b7SManivannan Sadhasivam 
628fb3a26b7SManivannan Sadhasivam 	ch_ctx_host_size = sizeof(struct mhi_chan_ctxt) * mhi_cntrl->max_chan;
629fb3a26b7SManivannan Sadhasivam 	ev_ctx_host_size = sizeof(struct mhi_event_ctxt) * mhi_cntrl->event_rings;
630fb3a26b7SManivannan Sadhasivam 	cmd_ctx_host_size = sizeof(struct mhi_cmd_ctxt) * NR_OF_CMD_RINGS;
631fb3a26b7SManivannan Sadhasivam 
632fb3a26b7SManivannan Sadhasivam 	/* Get the channel context base pointer from host */
633fb3a26b7SManivannan Sadhasivam 	mhi_ep_mmio_get_chc_base(mhi_cntrl);
634fb3a26b7SManivannan Sadhasivam 
635fb3a26b7SManivannan Sadhasivam 	/* Allocate and map memory for caching host channel context */
636fb3a26b7SManivannan Sadhasivam 	ret = mhi_cntrl->alloc_map(mhi_cntrl, mhi_cntrl->ch_ctx_host_pa,
637fb3a26b7SManivannan Sadhasivam 				   &mhi_cntrl->ch_ctx_cache_phys,
638fb3a26b7SManivannan Sadhasivam 				   (void __iomem **) &mhi_cntrl->ch_ctx_cache,
639fb3a26b7SManivannan Sadhasivam 				   ch_ctx_host_size);
640fb3a26b7SManivannan Sadhasivam 	if (ret) {
641fb3a26b7SManivannan Sadhasivam 		dev_err(dev, "Failed to allocate and map ch_ctx_cache\n");
642fb3a26b7SManivannan Sadhasivam 		return ret;
643fb3a26b7SManivannan Sadhasivam 	}
644fb3a26b7SManivannan Sadhasivam 
645fb3a26b7SManivannan Sadhasivam 	/* Get the event context base pointer from host */
646fb3a26b7SManivannan Sadhasivam 	mhi_ep_mmio_get_erc_base(mhi_cntrl);
647fb3a26b7SManivannan Sadhasivam 
648fb3a26b7SManivannan Sadhasivam 	/* Allocate and map memory for caching host event context */
649fb3a26b7SManivannan Sadhasivam 	ret = mhi_cntrl->alloc_map(mhi_cntrl, mhi_cntrl->ev_ctx_host_pa,
650fb3a26b7SManivannan Sadhasivam 				   &mhi_cntrl->ev_ctx_cache_phys,
651fb3a26b7SManivannan Sadhasivam 				   (void __iomem **) &mhi_cntrl->ev_ctx_cache,
652fb3a26b7SManivannan Sadhasivam 				   ev_ctx_host_size);
653fb3a26b7SManivannan Sadhasivam 	if (ret) {
654fb3a26b7SManivannan Sadhasivam 		dev_err(dev, "Failed to allocate and map ev_ctx_cache\n");
655fb3a26b7SManivannan Sadhasivam 		goto err_ch_ctx;
656fb3a26b7SManivannan Sadhasivam 	}
657fb3a26b7SManivannan Sadhasivam 
658fb3a26b7SManivannan Sadhasivam 	/* Get the command context base pointer from host */
659fb3a26b7SManivannan Sadhasivam 	mhi_ep_mmio_get_crc_base(mhi_cntrl);
660fb3a26b7SManivannan Sadhasivam 
661fb3a26b7SManivannan Sadhasivam 	/* Allocate and map memory for caching host command context */
662fb3a26b7SManivannan Sadhasivam 	ret = mhi_cntrl->alloc_map(mhi_cntrl, mhi_cntrl->cmd_ctx_host_pa,
663fb3a26b7SManivannan Sadhasivam 				   &mhi_cntrl->cmd_ctx_cache_phys,
664fb3a26b7SManivannan Sadhasivam 				   (void __iomem **) &mhi_cntrl->cmd_ctx_cache,
665fb3a26b7SManivannan Sadhasivam 				   cmd_ctx_host_size);
666fb3a26b7SManivannan Sadhasivam 	if (ret) {
667fb3a26b7SManivannan Sadhasivam 		dev_err(dev, "Failed to allocate and map cmd_ctx_cache\n");
668fb3a26b7SManivannan Sadhasivam 		goto err_ev_ctx;
669fb3a26b7SManivannan Sadhasivam 	}
670fb3a26b7SManivannan Sadhasivam 
671fb3a26b7SManivannan Sadhasivam 	/* Initialize command ring */
672fb3a26b7SManivannan Sadhasivam 	ret = mhi_ep_ring_start(mhi_cntrl, &mhi_cntrl->mhi_cmd->ring,
673fb3a26b7SManivannan Sadhasivam 				(union mhi_ep_ring_ctx *)mhi_cntrl->cmd_ctx_cache);
674fb3a26b7SManivannan Sadhasivam 	if (ret) {
675fb3a26b7SManivannan Sadhasivam 		dev_err(dev, "Failed to start the command ring\n");
676fb3a26b7SManivannan Sadhasivam 		goto err_cmd_ctx;
677fb3a26b7SManivannan Sadhasivam 	}
678fb3a26b7SManivannan Sadhasivam 
679fb3a26b7SManivannan Sadhasivam 	return ret;
680fb3a26b7SManivannan Sadhasivam 
681fb3a26b7SManivannan Sadhasivam err_cmd_ctx:
682fb3a26b7SManivannan Sadhasivam 	mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->cmd_ctx_host_pa, mhi_cntrl->cmd_ctx_cache_phys,
683fb3a26b7SManivannan Sadhasivam 			      (void __iomem *) mhi_cntrl->cmd_ctx_cache, cmd_ctx_host_size);
684fb3a26b7SManivannan Sadhasivam 
685fb3a26b7SManivannan Sadhasivam err_ev_ctx:
686fb3a26b7SManivannan Sadhasivam 	mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->ev_ctx_host_pa, mhi_cntrl->ev_ctx_cache_phys,
687fb3a26b7SManivannan Sadhasivam 			      (void __iomem *) mhi_cntrl->ev_ctx_cache, ev_ctx_host_size);
688fb3a26b7SManivannan Sadhasivam 
689fb3a26b7SManivannan Sadhasivam err_ch_ctx:
690fb3a26b7SManivannan Sadhasivam 	mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->ch_ctx_host_pa, mhi_cntrl->ch_ctx_cache_phys,
691fb3a26b7SManivannan Sadhasivam 			      (void __iomem *) mhi_cntrl->ch_ctx_cache, ch_ctx_host_size);
692fb3a26b7SManivannan Sadhasivam 
693fb3a26b7SManivannan Sadhasivam 	return ret;
694fb3a26b7SManivannan Sadhasivam }
695fb3a26b7SManivannan Sadhasivam 
mhi_ep_free_host_cfg(struct mhi_ep_cntrl * mhi_cntrl)696fb3a26b7SManivannan Sadhasivam static void mhi_ep_free_host_cfg(struct mhi_ep_cntrl *mhi_cntrl)
697fb3a26b7SManivannan Sadhasivam {
698fb3a26b7SManivannan Sadhasivam 	size_t cmd_ctx_host_size, ch_ctx_host_size, ev_ctx_host_size;
699fb3a26b7SManivannan Sadhasivam 
700fb3a26b7SManivannan Sadhasivam 	ch_ctx_host_size = sizeof(struct mhi_chan_ctxt) * mhi_cntrl->max_chan;
701fb3a26b7SManivannan Sadhasivam 	ev_ctx_host_size = sizeof(struct mhi_event_ctxt) * mhi_cntrl->event_rings;
702fb3a26b7SManivannan Sadhasivam 	cmd_ctx_host_size = sizeof(struct mhi_cmd_ctxt) * NR_OF_CMD_RINGS;
703fb3a26b7SManivannan Sadhasivam 
704fb3a26b7SManivannan Sadhasivam 	mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->cmd_ctx_host_pa, mhi_cntrl->cmd_ctx_cache_phys,
705fb3a26b7SManivannan Sadhasivam 			      (void __iomem *) mhi_cntrl->cmd_ctx_cache, cmd_ctx_host_size);
706fb3a26b7SManivannan Sadhasivam 
707fb3a26b7SManivannan Sadhasivam 	mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->ev_ctx_host_pa, mhi_cntrl->ev_ctx_cache_phys,
708fb3a26b7SManivannan Sadhasivam 			      (void __iomem *) mhi_cntrl->ev_ctx_cache, ev_ctx_host_size);
709fb3a26b7SManivannan Sadhasivam 
710fb3a26b7SManivannan Sadhasivam 	mhi_cntrl->unmap_free(mhi_cntrl, mhi_cntrl->ch_ctx_host_pa, mhi_cntrl->ch_ctx_cache_phys,
711fb3a26b7SManivannan Sadhasivam 			      (void __iomem *) mhi_cntrl->ch_ctx_cache, ch_ctx_host_size);
712fb3a26b7SManivannan Sadhasivam }
713fb3a26b7SManivannan Sadhasivam 
mhi_ep_enable_int(struct mhi_ep_cntrl * mhi_cntrl)714fb3a26b7SManivannan Sadhasivam static void mhi_ep_enable_int(struct mhi_ep_cntrl *mhi_cntrl)
715fb3a26b7SManivannan Sadhasivam {
716fb3a26b7SManivannan Sadhasivam 	/*
717fb3a26b7SManivannan Sadhasivam 	 * Doorbell interrupts are enabled when the corresponding channel gets started.
718fb3a26b7SManivannan Sadhasivam 	 * Enabling all interrupts here triggers spurious irqs as some of the interrupts
719fb3a26b7SManivannan Sadhasivam 	 * associated with hw channels always get triggered.
720fb3a26b7SManivannan Sadhasivam 	 */
721fb3a26b7SManivannan Sadhasivam 	mhi_ep_mmio_enable_ctrl_interrupt(mhi_cntrl);
722fb3a26b7SManivannan Sadhasivam 	mhi_ep_mmio_enable_cmdb_interrupt(mhi_cntrl);
723fb3a26b7SManivannan Sadhasivam }
724fb3a26b7SManivannan Sadhasivam 
mhi_ep_enable(struct mhi_ep_cntrl * mhi_cntrl)725fb3a26b7SManivannan Sadhasivam static int mhi_ep_enable(struct mhi_ep_cntrl *mhi_cntrl)
726fb3a26b7SManivannan Sadhasivam {
727fb3a26b7SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
728fb3a26b7SManivannan Sadhasivam 	enum mhi_state state;
729fb3a26b7SManivannan Sadhasivam 	bool mhi_reset;
730fb3a26b7SManivannan Sadhasivam 	u32 count = 0;
731fb3a26b7SManivannan Sadhasivam 	int ret;
732fb3a26b7SManivannan Sadhasivam 
733fb3a26b7SManivannan Sadhasivam 	/* Wait for Host to set the M0 state */
734fb3a26b7SManivannan Sadhasivam 	do {
735fb3a26b7SManivannan Sadhasivam 		msleep(M0_WAIT_DELAY_MS);
736fb3a26b7SManivannan Sadhasivam 		mhi_ep_mmio_get_mhi_state(mhi_cntrl, &state, &mhi_reset);
737fb3a26b7SManivannan Sadhasivam 		if (mhi_reset) {
738fb3a26b7SManivannan Sadhasivam 			/* Clear the MHI reset if host is in reset state */
739fb3a26b7SManivannan Sadhasivam 			mhi_ep_mmio_clear_reset(mhi_cntrl);
740fb3a26b7SManivannan Sadhasivam 			dev_info(dev, "Detected Host reset while waiting for M0\n");
741fb3a26b7SManivannan Sadhasivam 		}
742fb3a26b7SManivannan Sadhasivam 		count++;
743fb3a26b7SManivannan Sadhasivam 	} while (state != MHI_STATE_M0 && count < M0_WAIT_COUNT);
744fb3a26b7SManivannan Sadhasivam 
745fb3a26b7SManivannan Sadhasivam 	if (state != MHI_STATE_M0) {
746fb3a26b7SManivannan Sadhasivam 		dev_err(dev, "Host failed to enter M0\n");
747fb3a26b7SManivannan Sadhasivam 		return -ETIMEDOUT;
748fb3a26b7SManivannan Sadhasivam 	}
749fb3a26b7SManivannan Sadhasivam 
750fb3a26b7SManivannan Sadhasivam 	ret = mhi_ep_cache_host_cfg(mhi_cntrl);
751fb3a26b7SManivannan Sadhasivam 	if (ret) {
752fb3a26b7SManivannan Sadhasivam 		dev_err(dev, "Failed to cache host config\n");
753fb3a26b7SManivannan Sadhasivam 		return ret;
754fb3a26b7SManivannan Sadhasivam 	}
755fb3a26b7SManivannan Sadhasivam 
756fb3a26b7SManivannan Sadhasivam 	mhi_ep_mmio_set_env(mhi_cntrl, MHI_EE_AMSS);
757fb3a26b7SManivannan Sadhasivam 
758fb3a26b7SManivannan Sadhasivam 	/* Enable all interrupts now */
759fb3a26b7SManivannan Sadhasivam 	mhi_ep_enable_int(mhi_cntrl);
760fb3a26b7SManivannan Sadhasivam 
761fb3a26b7SManivannan Sadhasivam 	return 0;
762fb3a26b7SManivannan Sadhasivam }
763fb3a26b7SManivannan Sadhasivam 
mhi_ep_cmd_ring_worker(struct work_struct * work)764e8275690SManivannan Sadhasivam static void mhi_ep_cmd_ring_worker(struct work_struct *work)
765e8275690SManivannan Sadhasivam {
766e8275690SManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, cmd_ring_work);
767e8275690SManivannan Sadhasivam 	struct mhi_ep_ring *ring = &mhi_cntrl->mhi_cmd->ring;
768e8275690SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
769e8275690SManivannan Sadhasivam 	struct mhi_ring_element *el;
770e8275690SManivannan Sadhasivam 	int ret;
771e8275690SManivannan Sadhasivam 
772e8275690SManivannan Sadhasivam 	/* Update the write offset for the ring */
773e8275690SManivannan Sadhasivam 	ret = mhi_ep_update_wr_offset(ring);
774e8275690SManivannan Sadhasivam 	if (ret) {
775e8275690SManivannan Sadhasivam 		dev_err(dev, "Error updating write offset for ring\n");
776e8275690SManivannan Sadhasivam 		return;
777e8275690SManivannan Sadhasivam 	}
778e8275690SManivannan Sadhasivam 
779e8275690SManivannan Sadhasivam 	/* Sanity check to make sure there are elements in the ring */
780e8275690SManivannan Sadhasivam 	if (ring->rd_offset == ring->wr_offset)
781e8275690SManivannan Sadhasivam 		return;
782e8275690SManivannan Sadhasivam 
783e8275690SManivannan Sadhasivam 	/*
784e8275690SManivannan Sadhasivam 	 * Process command ring element till write offset. In case of an error, just try to
785e8275690SManivannan Sadhasivam 	 * process next element.
786e8275690SManivannan Sadhasivam 	 */
787e8275690SManivannan Sadhasivam 	while (ring->rd_offset != ring->wr_offset) {
788e8275690SManivannan Sadhasivam 		el = &ring->ring_cache[ring->rd_offset];
789e8275690SManivannan Sadhasivam 
790e8275690SManivannan Sadhasivam 		ret = mhi_ep_process_cmd_ring(ring, el);
79194e19f4fSManivannan Sadhasivam 		if (ret && ret != -ENODEV)
792e8275690SManivannan Sadhasivam 			dev_err(dev, "Error processing cmd ring element: %zu\n", ring->rd_offset);
793e8275690SManivannan Sadhasivam 
794e8275690SManivannan Sadhasivam 		mhi_ep_ring_inc_index(ring);
795e8275690SManivannan Sadhasivam 	}
796e8275690SManivannan Sadhasivam }
797e8275690SManivannan Sadhasivam 
mhi_ep_ch_ring_worker(struct work_struct * work)79803c0bb8eSManivannan Sadhasivam static void mhi_ep_ch_ring_worker(struct work_struct *work)
79903c0bb8eSManivannan Sadhasivam {
80003c0bb8eSManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, ch_ring_work);
80103c0bb8eSManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
80203c0bb8eSManivannan Sadhasivam 	struct mhi_ep_ring_item *itr, *tmp;
80303c0bb8eSManivannan Sadhasivam 	struct mhi_ep_ring *ring;
80403c0bb8eSManivannan Sadhasivam 	struct mhi_ep_chan *chan;
80503c0bb8eSManivannan Sadhasivam 	unsigned long flags;
80603c0bb8eSManivannan Sadhasivam 	LIST_HEAD(head);
80703c0bb8eSManivannan Sadhasivam 	int ret;
80803c0bb8eSManivannan Sadhasivam 
80903c0bb8eSManivannan Sadhasivam 	spin_lock_irqsave(&mhi_cntrl->list_lock, flags);
81003c0bb8eSManivannan Sadhasivam 	list_splice_tail_init(&mhi_cntrl->ch_db_list, &head);
81103c0bb8eSManivannan Sadhasivam 	spin_unlock_irqrestore(&mhi_cntrl->list_lock, flags);
81203c0bb8eSManivannan Sadhasivam 
81303c0bb8eSManivannan Sadhasivam 	/* Process each queued channel ring. In case of an error, just process next element. */
81403c0bb8eSManivannan Sadhasivam 	list_for_each_entry_safe(itr, tmp, &head, node) {
81503c0bb8eSManivannan Sadhasivam 		list_del(&itr->node);
81603c0bb8eSManivannan Sadhasivam 		ring = itr->ring;
81703c0bb8eSManivannan Sadhasivam 
8188d6a1feaSManivannan Sadhasivam 		chan = &mhi_cntrl->mhi_chan[ring->ch_id];
8198d6a1feaSManivannan Sadhasivam 		mutex_lock(&chan->lock);
8208d6a1feaSManivannan Sadhasivam 
8218d6a1feaSManivannan Sadhasivam 		/*
8228d6a1feaSManivannan Sadhasivam 		 * The ring could've stopped while we waited to grab the (chan->lock), so do
8238d6a1feaSManivannan Sadhasivam 		 * a sanity check before going further.
8248d6a1feaSManivannan Sadhasivam 		 */
8258d6a1feaSManivannan Sadhasivam 		if (!ring->started) {
8268d6a1feaSManivannan Sadhasivam 			mutex_unlock(&chan->lock);
8278d6a1feaSManivannan Sadhasivam 			kfree(itr);
8288d6a1feaSManivannan Sadhasivam 			continue;
8298d6a1feaSManivannan Sadhasivam 		}
8308d6a1feaSManivannan Sadhasivam 
83103c0bb8eSManivannan Sadhasivam 		/* Update the write offset for the ring */
83203c0bb8eSManivannan Sadhasivam 		ret = mhi_ep_update_wr_offset(ring);
83303c0bb8eSManivannan Sadhasivam 		if (ret) {
83403c0bb8eSManivannan Sadhasivam 			dev_err(dev, "Error updating write offset for ring\n");
8358d6a1feaSManivannan Sadhasivam 			mutex_unlock(&chan->lock);
836bd4f6f1fSManivannan Sadhasivam 			kmem_cache_free(mhi_cntrl->ring_item_cache, itr);
83703c0bb8eSManivannan Sadhasivam 			continue;
83803c0bb8eSManivannan Sadhasivam 		}
83903c0bb8eSManivannan Sadhasivam 
84003c0bb8eSManivannan Sadhasivam 		/* Sanity check to make sure there are elements in the ring */
841b6af3a95SManivannan Sadhasivam 		if (chan->rd_offset == ring->wr_offset) {
8428d6a1feaSManivannan Sadhasivam 			mutex_unlock(&chan->lock);
843bd4f6f1fSManivannan Sadhasivam 			kmem_cache_free(mhi_cntrl->ring_item_cache, itr);
84403c0bb8eSManivannan Sadhasivam 			continue;
84503c0bb8eSManivannan Sadhasivam 		}
84603c0bb8eSManivannan Sadhasivam 
84703c0bb8eSManivannan Sadhasivam 		dev_dbg(dev, "Processing the ring for channel (%u)\n", ring->ch_id);
84839601f49SManivannan Sadhasivam 		ret = mhi_ep_process_ch_ring(ring);
84903c0bb8eSManivannan Sadhasivam 		if (ret) {
85003c0bb8eSManivannan Sadhasivam 			dev_err(dev, "Error processing ring for channel (%u): %d\n",
85103c0bb8eSManivannan Sadhasivam 				ring->ch_id, ret);
85203c0bb8eSManivannan Sadhasivam 			mutex_unlock(&chan->lock);
853bd4f6f1fSManivannan Sadhasivam 			kmem_cache_free(mhi_cntrl->ring_item_cache, itr);
85403c0bb8eSManivannan Sadhasivam 			continue;
85503c0bb8eSManivannan Sadhasivam 		}
85603c0bb8eSManivannan Sadhasivam 
85703c0bb8eSManivannan Sadhasivam 		mutex_unlock(&chan->lock);
858bd4f6f1fSManivannan Sadhasivam 		kmem_cache_free(mhi_cntrl->ring_item_cache, itr);
85903c0bb8eSManivannan Sadhasivam 	}
86003c0bb8eSManivannan Sadhasivam }
86103c0bb8eSManivannan Sadhasivam 
mhi_ep_state_worker(struct work_struct * work)862f9baa4f7SManivannan Sadhasivam static void mhi_ep_state_worker(struct work_struct *work)
863f9baa4f7SManivannan Sadhasivam {
864f9baa4f7SManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, state_work);
865f9baa4f7SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
866f9baa4f7SManivannan Sadhasivam 	struct mhi_ep_state_transition *itr, *tmp;
867f9baa4f7SManivannan Sadhasivam 	unsigned long flags;
868f9baa4f7SManivannan Sadhasivam 	LIST_HEAD(head);
869f9baa4f7SManivannan Sadhasivam 	int ret;
870f9baa4f7SManivannan Sadhasivam 
871f9baa4f7SManivannan Sadhasivam 	spin_lock_irqsave(&mhi_cntrl->list_lock, flags);
872f9baa4f7SManivannan Sadhasivam 	list_splice_tail_init(&mhi_cntrl->st_transition_list, &head);
873f9baa4f7SManivannan Sadhasivam 	spin_unlock_irqrestore(&mhi_cntrl->list_lock, flags);
874f9baa4f7SManivannan Sadhasivam 
875f9baa4f7SManivannan Sadhasivam 	list_for_each_entry_safe(itr, tmp, &head, node) {
876f9baa4f7SManivannan Sadhasivam 		list_del(&itr->node);
877f9baa4f7SManivannan Sadhasivam 		dev_dbg(dev, "Handling MHI state transition to %s\n",
878f9baa4f7SManivannan Sadhasivam 			 mhi_state_str(itr->state));
879f9baa4f7SManivannan Sadhasivam 
880f9baa4f7SManivannan Sadhasivam 		switch (itr->state) {
881f9baa4f7SManivannan Sadhasivam 		case MHI_STATE_M0:
882f9baa4f7SManivannan Sadhasivam 			ret = mhi_ep_set_m0_state(mhi_cntrl);
883f9baa4f7SManivannan Sadhasivam 			if (ret)
884f9baa4f7SManivannan Sadhasivam 				dev_err(dev, "Failed to transition to M0 state\n");
885f9baa4f7SManivannan Sadhasivam 			break;
886f9baa4f7SManivannan Sadhasivam 		case MHI_STATE_M3:
887f9baa4f7SManivannan Sadhasivam 			ret = mhi_ep_set_m3_state(mhi_cntrl);
888f9baa4f7SManivannan Sadhasivam 			if (ret)
889f9baa4f7SManivannan Sadhasivam 				dev_err(dev, "Failed to transition to M3 state\n");
890f9baa4f7SManivannan Sadhasivam 			break;
891f9baa4f7SManivannan Sadhasivam 		default:
892f9baa4f7SManivannan Sadhasivam 			dev_err(dev, "Invalid MHI state transition: %d\n", itr->state);
893f9baa4f7SManivannan Sadhasivam 			break;
894f9baa4f7SManivannan Sadhasivam 		}
895f9baa4f7SManivannan Sadhasivam 		kfree(itr);
896f9baa4f7SManivannan Sadhasivam 	}
897f9baa4f7SManivannan Sadhasivam }
898f9baa4f7SManivannan Sadhasivam 
mhi_ep_queue_channel_db(struct mhi_ep_cntrl * mhi_cntrl,unsigned long ch_int,u32 ch_idx)8994799e71bSManivannan Sadhasivam static void mhi_ep_queue_channel_db(struct mhi_ep_cntrl *mhi_cntrl, unsigned long ch_int,
9004799e71bSManivannan Sadhasivam 				    u32 ch_idx)
9014799e71bSManivannan Sadhasivam {
9024799e71bSManivannan Sadhasivam 	struct mhi_ep_ring_item *item;
9034799e71bSManivannan Sadhasivam 	struct mhi_ep_ring *ring;
9044799e71bSManivannan Sadhasivam 	bool work = !!ch_int;
9054799e71bSManivannan Sadhasivam 	LIST_HEAD(head);
9064799e71bSManivannan Sadhasivam 	u32 i;
9074799e71bSManivannan Sadhasivam 
9084799e71bSManivannan Sadhasivam 	/* First add the ring items to a local list */
9094799e71bSManivannan Sadhasivam 	for_each_set_bit(i, &ch_int, 32) {
9104799e71bSManivannan Sadhasivam 		/* Channel index varies for each register: 0, 32, 64, 96 */
9114799e71bSManivannan Sadhasivam 		u32 ch_id = ch_idx + i;
9124799e71bSManivannan Sadhasivam 
9134799e71bSManivannan Sadhasivam 		ring = &mhi_cntrl->mhi_chan[ch_id].ring;
914bd4f6f1fSManivannan Sadhasivam 		item = kmem_cache_zalloc(mhi_cntrl->ring_item_cache, GFP_ATOMIC);
9154799e71bSManivannan Sadhasivam 		if (!item)
9164799e71bSManivannan Sadhasivam 			return;
9174799e71bSManivannan Sadhasivam 
9184799e71bSManivannan Sadhasivam 		item->ring = ring;
9194799e71bSManivannan Sadhasivam 		list_add_tail(&item->node, &head);
9204799e71bSManivannan Sadhasivam 	}
9214799e71bSManivannan Sadhasivam 
9224799e71bSManivannan Sadhasivam 	/* Now, splice the local list into ch_db_list and queue the work item */
9234799e71bSManivannan Sadhasivam 	if (work) {
9244799e71bSManivannan Sadhasivam 		spin_lock(&mhi_cntrl->list_lock);
9254799e71bSManivannan Sadhasivam 		list_splice_tail_init(&head, &mhi_cntrl->ch_db_list);
9264799e71bSManivannan Sadhasivam 		spin_unlock(&mhi_cntrl->list_lock);
92703c0bb8eSManivannan Sadhasivam 
92803c0bb8eSManivannan Sadhasivam 		queue_work(mhi_cntrl->wq, &mhi_cntrl->ch_ring_work);
9294799e71bSManivannan Sadhasivam 	}
9304799e71bSManivannan Sadhasivam }
9314799e71bSManivannan Sadhasivam 
9324799e71bSManivannan Sadhasivam /*
9334799e71bSManivannan Sadhasivam  * Channel interrupt statuses are contained in 4 registers each of 32bit length.
9344799e71bSManivannan Sadhasivam  * For checking all interrupts, we need to loop through each registers and then
9354799e71bSManivannan Sadhasivam  * check for bits set.
9364799e71bSManivannan Sadhasivam  */
mhi_ep_check_channel_interrupt(struct mhi_ep_cntrl * mhi_cntrl)9374799e71bSManivannan Sadhasivam static void mhi_ep_check_channel_interrupt(struct mhi_ep_cntrl *mhi_cntrl)
9384799e71bSManivannan Sadhasivam {
9394799e71bSManivannan Sadhasivam 	u32 ch_int, ch_idx, i;
9404799e71bSManivannan Sadhasivam 
9414799e71bSManivannan Sadhasivam 	/* Bail out if there is no channel doorbell interrupt */
9424799e71bSManivannan Sadhasivam 	if (!mhi_ep_mmio_read_chdb_status_interrupts(mhi_cntrl))
9434799e71bSManivannan Sadhasivam 		return;
9444799e71bSManivannan Sadhasivam 
9454799e71bSManivannan Sadhasivam 	for (i = 0; i < MHI_MASK_ROWS_CH_DB; i++) {
9464799e71bSManivannan Sadhasivam 		ch_idx = i * MHI_MASK_CH_LEN;
9474799e71bSManivannan Sadhasivam 
9484799e71bSManivannan Sadhasivam 		/* Only process channel interrupt if the mask is enabled */
9494799e71bSManivannan Sadhasivam 		ch_int = mhi_cntrl->chdb[i].status & mhi_cntrl->chdb[i].mask;
9504799e71bSManivannan Sadhasivam 		if (ch_int) {
9514799e71bSManivannan Sadhasivam 			mhi_ep_queue_channel_db(mhi_cntrl, ch_int, ch_idx);
9524799e71bSManivannan Sadhasivam 			mhi_ep_mmio_write(mhi_cntrl, MHI_CHDB_INT_CLEAR_n(i),
9534799e71bSManivannan Sadhasivam 							mhi_cntrl->chdb[i].status);
9544799e71bSManivannan Sadhasivam 		}
9554799e71bSManivannan Sadhasivam 	}
9564799e71bSManivannan Sadhasivam }
9574799e71bSManivannan Sadhasivam 
mhi_ep_process_ctrl_interrupt(struct mhi_ep_cntrl * mhi_cntrl,enum mhi_state state)9584799e71bSManivannan Sadhasivam static void mhi_ep_process_ctrl_interrupt(struct mhi_ep_cntrl *mhi_cntrl,
9594799e71bSManivannan Sadhasivam 					 enum mhi_state state)
9604799e71bSManivannan Sadhasivam {
9614799e71bSManivannan Sadhasivam 	struct mhi_ep_state_transition *item;
9624799e71bSManivannan Sadhasivam 
9634799e71bSManivannan Sadhasivam 	item = kzalloc(sizeof(*item), GFP_ATOMIC);
9644799e71bSManivannan Sadhasivam 	if (!item)
9654799e71bSManivannan Sadhasivam 		return;
9664799e71bSManivannan Sadhasivam 
9674799e71bSManivannan Sadhasivam 	item->state = state;
9684799e71bSManivannan Sadhasivam 	spin_lock(&mhi_cntrl->list_lock);
9694799e71bSManivannan Sadhasivam 	list_add_tail(&item->node, &mhi_cntrl->st_transition_list);
9704799e71bSManivannan Sadhasivam 	spin_unlock(&mhi_cntrl->list_lock);
9714799e71bSManivannan Sadhasivam 
9724799e71bSManivannan Sadhasivam 	queue_work(mhi_cntrl->wq, &mhi_cntrl->state_work);
9734799e71bSManivannan Sadhasivam }
9744799e71bSManivannan Sadhasivam 
9754799e71bSManivannan Sadhasivam /*
9764799e71bSManivannan Sadhasivam  * Interrupt handler that services interrupts raised by the host writing to
9774799e71bSManivannan Sadhasivam  * MHICTRL and Command ring doorbell (CRDB) registers for state change and
9784799e71bSManivannan Sadhasivam  * channel interrupts.
9794799e71bSManivannan Sadhasivam  */
mhi_ep_irq(int irq,void * data)9804799e71bSManivannan Sadhasivam static irqreturn_t mhi_ep_irq(int irq, void *data)
9814799e71bSManivannan Sadhasivam {
9824799e71bSManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl = data;
9834799e71bSManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
9844799e71bSManivannan Sadhasivam 	enum mhi_state state;
9854799e71bSManivannan Sadhasivam 	u32 int_value;
9867a97b6b4SManivannan Sadhasivam 	bool mhi_reset;
9874799e71bSManivannan Sadhasivam 
9884799e71bSManivannan Sadhasivam 	/* Acknowledge the ctrl interrupt */
9894799e71bSManivannan Sadhasivam 	int_value = mhi_ep_mmio_read(mhi_cntrl, MHI_CTRL_INT_STATUS);
9904799e71bSManivannan Sadhasivam 	mhi_ep_mmio_write(mhi_cntrl, MHI_CTRL_INT_CLEAR, int_value);
9914799e71bSManivannan Sadhasivam 
9924799e71bSManivannan Sadhasivam 	/* Check for ctrl interrupt */
9934799e71bSManivannan Sadhasivam 	if (FIELD_GET(MHI_CTRL_INT_STATUS_MSK, int_value)) {
9944799e71bSManivannan Sadhasivam 		dev_dbg(dev, "Processing ctrl interrupt\n");
9957a97b6b4SManivannan Sadhasivam 		mhi_ep_mmio_get_mhi_state(mhi_cntrl, &state, &mhi_reset);
9967a97b6b4SManivannan Sadhasivam 		if (mhi_reset) {
9977a97b6b4SManivannan Sadhasivam 			dev_info(dev, "Host triggered MHI reset!\n");
9987a97b6b4SManivannan Sadhasivam 			disable_irq_nosync(mhi_cntrl->irq);
9997a97b6b4SManivannan Sadhasivam 			schedule_work(&mhi_cntrl->reset_work);
10007a97b6b4SManivannan Sadhasivam 			return IRQ_HANDLED;
10017a97b6b4SManivannan Sadhasivam 		}
10027a97b6b4SManivannan Sadhasivam 
10034799e71bSManivannan Sadhasivam 		mhi_ep_process_ctrl_interrupt(mhi_cntrl, state);
10044799e71bSManivannan Sadhasivam 	}
10054799e71bSManivannan Sadhasivam 
10064799e71bSManivannan Sadhasivam 	/* Check for command doorbell interrupt */
1007e8275690SManivannan Sadhasivam 	if (FIELD_GET(MHI_CTRL_INT_STATUS_CRDB_MSK, int_value)) {
10084799e71bSManivannan Sadhasivam 		dev_dbg(dev, "Processing command doorbell interrupt\n");
1009e8275690SManivannan Sadhasivam 		queue_work(mhi_cntrl->wq, &mhi_cntrl->cmd_ring_work);
1010e8275690SManivannan Sadhasivam 	}
10114799e71bSManivannan Sadhasivam 
10124799e71bSManivannan Sadhasivam 	/* Check for channel interrupts */
10134799e71bSManivannan Sadhasivam 	mhi_ep_check_channel_interrupt(mhi_cntrl);
10144799e71bSManivannan Sadhasivam 
10154799e71bSManivannan Sadhasivam 	return IRQ_HANDLED;
10164799e71bSManivannan Sadhasivam }
10174799e71bSManivannan Sadhasivam 
mhi_ep_abort_transfer(struct mhi_ep_cntrl * mhi_cntrl)10185d507ee0SManivannan Sadhasivam static void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl)
10195d507ee0SManivannan Sadhasivam {
10205d507ee0SManivannan Sadhasivam 	struct mhi_ep_ring *ch_ring, *ev_ring;
10215d507ee0SManivannan Sadhasivam 	struct mhi_result result = {};
10225d507ee0SManivannan Sadhasivam 	struct mhi_ep_chan *mhi_chan;
10235d507ee0SManivannan Sadhasivam 	int i;
10245d507ee0SManivannan Sadhasivam 
10255d507ee0SManivannan Sadhasivam 	/* Stop all the channels */
10265d507ee0SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->max_chan; i++) {
10275d507ee0SManivannan Sadhasivam 		mhi_chan = &mhi_cntrl->mhi_chan[i];
10285d507ee0SManivannan Sadhasivam 		if (!mhi_chan->ring.started)
10295d507ee0SManivannan Sadhasivam 			continue;
10305d507ee0SManivannan Sadhasivam 
10315d507ee0SManivannan Sadhasivam 		mutex_lock(&mhi_chan->lock);
10325d507ee0SManivannan Sadhasivam 		/* Send channel disconnect status to client drivers */
10335d507ee0SManivannan Sadhasivam 		if (mhi_chan->xfer_cb) {
10345d507ee0SManivannan Sadhasivam 			result.transaction_status = -ENOTCONN;
10355d507ee0SManivannan Sadhasivam 			result.bytes_xferd = 0;
10365d507ee0SManivannan Sadhasivam 			mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
10375d507ee0SManivannan Sadhasivam 		}
10385d507ee0SManivannan Sadhasivam 
10395d507ee0SManivannan Sadhasivam 		mhi_chan->state = MHI_CH_STATE_DISABLED;
10405d507ee0SManivannan Sadhasivam 		mutex_unlock(&mhi_chan->lock);
10415d507ee0SManivannan Sadhasivam 	}
10425d507ee0SManivannan Sadhasivam 
10435d507ee0SManivannan Sadhasivam 	flush_workqueue(mhi_cntrl->wq);
10445d507ee0SManivannan Sadhasivam 
10455d507ee0SManivannan Sadhasivam 	/* Destroy devices associated with all channels */
10465d507ee0SManivannan Sadhasivam 	device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_ep_destroy_device);
10475d507ee0SManivannan Sadhasivam 
10485d507ee0SManivannan Sadhasivam 	/* Stop and reset the transfer rings */
10495d507ee0SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->max_chan; i++) {
10505d507ee0SManivannan Sadhasivam 		mhi_chan = &mhi_cntrl->mhi_chan[i];
10515d507ee0SManivannan Sadhasivam 		if (!mhi_chan->ring.started)
10525d507ee0SManivannan Sadhasivam 			continue;
10535d507ee0SManivannan Sadhasivam 
10545d507ee0SManivannan Sadhasivam 		ch_ring = &mhi_cntrl->mhi_chan[i].ring;
10555d507ee0SManivannan Sadhasivam 		mutex_lock(&mhi_chan->lock);
10565d507ee0SManivannan Sadhasivam 		mhi_ep_ring_reset(mhi_cntrl, ch_ring);
10575d507ee0SManivannan Sadhasivam 		mutex_unlock(&mhi_chan->lock);
10585d507ee0SManivannan Sadhasivam 	}
10595d507ee0SManivannan Sadhasivam 
10605d507ee0SManivannan Sadhasivam 	/* Stop and reset the event rings */
10615d507ee0SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->event_rings; i++) {
10625d507ee0SManivannan Sadhasivam 		ev_ring = &mhi_cntrl->mhi_event[i].ring;
10635d507ee0SManivannan Sadhasivam 		if (!ev_ring->started)
10645d507ee0SManivannan Sadhasivam 			continue;
10655d507ee0SManivannan Sadhasivam 
10665d507ee0SManivannan Sadhasivam 		mutex_lock(&mhi_cntrl->event_lock);
10675d507ee0SManivannan Sadhasivam 		mhi_ep_ring_reset(mhi_cntrl, ev_ring);
10685d507ee0SManivannan Sadhasivam 		mutex_unlock(&mhi_cntrl->event_lock);
10695d507ee0SManivannan Sadhasivam 	}
10705d507ee0SManivannan Sadhasivam 
10715d507ee0SManivannan Sadhasivam 	/* Stop and reset the command ring */
10725d507ee0SManivannan Sadhasivam 	mhi_ep_ring_reset(mhi_cntrl, &mhi_cntrl->mhi_cmd->ring);
10735d507ee0SManivannan Sadhasivam 
10745d507ee0SManivannan Sadhasivam 	mhi_ep_free_host_cfg(mhi_cntrl);
10755d507ee0SManivannan Sadhasivam 	mhi_ep_mmio_mask_interrupts(mhi_cntrl);
10765d507ee0SManivannan Sadhasivam 
10775d507ee0SManivannan Sadhasivam 	mhi_cntrl->enabled = false;
10785d507ee0SManivannan Sadhasivam }
10795d507ee0SManivannan Sadhasivam 
mhi_ep_reset_worker(struct work_struct * work)10807a97b6b4SManivannan Sadhasivam static void mhi_ep_reset_worker(struct work_struct *work)
10817a97b6b4SManivannan Sadhasivam {
10827a97b6b4SManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, reset_work);
10837a97b6b4SManivannan Sadhasivam 	enum mhi_state cur_state;
10847a97b6b4SManivannan Sadhasivam 
108547a1dcaeSManivannan Sadhasivam 	mhi_ep_power_down(mhi_cntrl);
10867a97b6b4SManivannan Sadhasivam 
10871ddc7618SManivannan Sadhasivam 	mutex_lock(&mhi_cntrl->state_lock);
10881ddc7618SManivannan Sadhasivam 
10897a97b6b4SManivannan Sadhasivam 	/* Reset MMIO to signal host that the MHI_RESET is completed in endpoint */
10907a97b6b4SManivannan Sadhasivam 	mhi_ep_mmio_reset(mhi_cntrl);
10917a97b6b4SManivannan Sadhasivam 	cur_state = mhi_cntrl->mhi_state;
10927a97b6b4SManivannan Sadhasivam 
10937a97b6b4SManivannan Sadhasivam 	/*
10947a97b6b4SManivannan Sadhasivam 	 * Only proceed further if the reset is due to SYS_ERR. The host will
10957a97b6b4SManivannan Sadhasivam 	 * issue reset during shutdown also and we don't need to do re-init in
10967a97b6b4SManivannan Sadhasivam 	 * that case.
10977a97b6b4SManivannan Sadhasivam 	 */
109847a1dcaeSManivannan Sadhasivam 	if (cur_state == MHI_STATE_SYS_ERR)
109947a1dcaeSManivannan Sadhasivam 		mhi_ep_power_up(mhi_cntrl);
11001ddc7618SManivannan Sadhasivam 
11011ddc7618SManivannan Sadhasivam 	mutex_unlock(&mhi_cntrl->state_lock);
11027a97b6b4SManivannan Sadhasivam }
11037a97b6b4SManivannan Sadhasivam 
1104f7d0806bSManivannan Sadhasivam /*
1105f7d0806bSManivannan Sadhasivam  * We don't need to do anything special other than setting the MHI SYS_ERR
1106f7d0806bSManivannan Sadhasivam  * state. The host will reset all contexts and issue MHI RESET so that we
1107f7d0806bSManivannan Sadhasivam  * could also recover from error state.
1108f7d0806bSManivannan Sadhasivam  */
mhi_ep_handle_syserr(struct mhi_ep_cntrl * mhi_cntrl)1109f7d0806bSManivannan Sadhasivam void mhi_ep_handle_syserr(struct mhi_ep_cntrl *mhi_cntrl)
1110f7d0806bSManivannan Sadhasivam {
1111f7d0806bSManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
1112f7d0806bSManivannan Sadhasivam 	int ret;
1113f7d0806bSManivannan Sadhasivam 
1114f7d0806bSManivannan Sadhasivam 	ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_SYS_ERR);
1115f7d0806bSManivannan Sadhasivam 	if (ret)
1116f7d0806bSManivannan Sadhasivam 		return;
1117f7d0806bSManivannan Sadhasivam 
1118f7d0806bSManivannan Sadhasivam 	/* Signal host that the device went to SYS_ERR state */
1119f7d0806bSManivannan Sadhasivam 	ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_SYS_ERR);
1120f7d0806bSManivannan Sadhasivam 	if (ret)
1121f7d0806bSManivannan Sadhasivam 		dev_err(dev, "Failed sending SYS_ERR state change event: %d\n", ret);
1122f7d0806bSManivannan Sadhasivam }
1123f7d0806bSManivannan Sadhasivam 
mhi_ep_power_up(struct mhi_ep_cntrl * mhi_cntrl)1124fb3a26b7SManivannan Sadhasivam int mhi_ep_power_up(struct mhi_ep_cntrl *mhi_cntrl)
1125fb3a26b7SManivannan Sadhasivam {
1126fb3a26b7SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
1127fb3a26b7SManivannan Sadhasivam 	int ret, i;
1128fb3a26b7SManivannan Sadhasivam 
1129fb3a26b7SManivannan Sadhasivam 	/*
1130fb3a26b7SManivannan Sadhasivam 	 * Mask all interrupts until the state machine is ready. Interrupts will
1131fb3a26b7SManivannan Sadhasivam 	 * be enabled later with mhi_ep_enable().
1132fb3a26b7SManivannan Sadhasivam 	 */
1133fb3a26b7SManivannan Sadhasivam 	mhi_ep_mmio_mask_interrupts(mhi_cntrl);
1134fb3a26b7SManivannan Sadhasivam 	mhi_ep_mmio_init(mhi_cntrl);
1135fb3a26b7SManivannan Sadhasivam 
1136fb3a26b7SManivannan Sadhasivam 	mhi_cntrl->mhi_event = kzalloc(mhi_cntrl->event_rings * (sizeof(*mhi_cntrl->mhi_event)),
1137fb3a26b7SManivannan Sadhasivam 					GFP_KERNEL);
1138fb3a26b7SManivannan Sadhasivam 	if (!mhi_cntrl->mhi_event)
1139fb3a26b7SManivannan Sadhasivam 		return -ENOMEM;
1140fb3a26b7SManivannan Sadhasivam 
1141fb3a26b7SManivannan Sadhasivam 	/* Initialize command, channel and event rings */
1142fb3a26b7SManivannan Sadhasivam 	mhi_ep_ring_init(&mhi_cntrl->mhi_cmd->ring, RING_TYPE_CMD, 0);
1143fb3a26b7SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->max_chan; i++)
1144fb3a26b7SManivannan Sadhasivam 		mhi_ep_ring_init(&mhi_cntrl->mhi_chan[i].ring, RING_TYPE_CH, i);
1145fb3a26b7SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->event_rings; i++)
1146fb3a26b7SManivannan Sadhasivam 		mhi_ep_ring_init(&mhi_cntrl->mhi_event[i].ring, RING_TYPE_ER, i);
1147fb3a26b7SManivannan Sadhasivam 
1148fb3a26b7SManivannan Sadhasivam 	mhi_cntrl->mhi_state = MHI_STATE_RESET;
1149fb3a26b7SManivannan Sadhasivam 
1150fb3a26b7SManivannan Sadhasivam 	/* Set AMSS EE before signaling ready state */
1151fb3a26b7SManivannan Sadhasivam 	mhi_ep_mmio_set_env(mhi_cntrl, MHI_EE_AMSS);
1152fb3a26b7SManivannan Sadhasivam 
1153fb3a26b7SManivannan Sadhasivam 	/* All set, notify the host that we are ready */
1154fb3a26b7SManivannan Sadhasivam 	ret = mhi_ep_set_ready_state(mhi_cntrl);
1155fb3a26b7SManivannan Sadhasivam 	if (ret)
1156fb3a26b7SManivannan Sadhasivam 		goto err_free_event;
1157fb3a26b7SManivannan Sadhasivam 
1158fb3a26b7SManivannan Sadhasivam 	dev_dbg(dev, "READY state notification sent to the host\n");
1159fb3a26b7SManivannan Sadhasivam 
1160fb3a26b7SManivannan Sadhasivam 	ret = mhi_ep_enable(mhi_cntrl);
1161fb3a26b7SManivannan Sadhasivam 	if (ret) {
1162fb3a26b7SManivannan Sadhasivam 		dev_err(dev, "Failed to enable MHI endpoint\n");
1163fb3a26b7SManivannan Sadhasivam 		goto err_free_event;
1164fb3a26b7SManivannan Sadhasivam 	}
1165fb3a26b7SManivannan Sadhasivam 
1166fb3a26b7SManivannan Sadhasivam 	enable_irq(mhi_cntrl->irq);
1167fb3a26b7SManivannan Sadhasivam 	mhi_cntrl->enabled = true;
1168fb3a26b7SManivannan Sadhasivam 
1169fb3a26b7SManivannan Sadhasivam 	return 0;
1170fb3a26b7SManivannan Sadhasivam 
1171fb3a26b7SManivannan Sadhasivam err_free_event:
1172fb3a26b7SManivannan Sadhasivam 	kfree(mhi_cntrl->mhi_event);
1173fb3a26b7SManivannan Sadhasivam 
1174fb3a26b7SManivannan Sadhasivam 	return ret;
1175fb3a26b7SManivannan Sadhasivam }
1176fb3a26b7SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_ep_power_up);
1177fb3a26b7SManivannan Sadhasivam 
mhi_ep_power_down(struct mhi_ep_cntrl * mhi_cntrl)11785d507ee0SManivannan Sadhasivam void mhi_ep_power_down(struct mhi_ep_cntrl *mhi_cntrl)
11795d507ee0SManivannan Sadhasivam {
118047a1dcaeSManivannan Sadhasivam 	if (mhi_cntrl->enabled) {
11815d507ee0SManivannan Sadhasivam 		mhi_ep_abort_transfer(mhi_cntrl);
11825d507ee0SManivannan Sadhasivam 		kfree(mhi_cntrl->mhi_event);
11835d507ee0SManivannan Sadhasivam 		disable_irq(mhi_cntrl->irq);
11845d507ee0SManivannan Sadhasivam 	}
118547a1dcaeSManivannan Sadhasivam }
11865d507ee0SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_ep_power_down);
11875d507ee0SManivannan Sadhasivam 
mhi_ep_suspend_channels(struct mhi_ep_cntrl * mhi_cntrl)1188e4b7b5f0SManivannan Sadhasivam void mhi_ep_suspend_channels(struct mhi_ep_cntrl *mhi_cntrl)
1189e4b7b5f0SManivannan Sadhasivam {
1190e4b7b5f0SManivannan Sadhasivam 	struct mhi_ep_chan *mhi_chan;
1191e4b7b5f0SManivannan Sadhasivam 	u32 tmp;
1192e4b7b5f0SManivannan Sadhasivam 	int i;
1193e4b7b5f0SManivannan Sadhasivam 
1194e4b7b5f0SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->max_chan; i++) {
1195e4b7b5f0SManivannan Sadhasivam 		mhi_chan = &mhi_cntrl->mhi_chan[i];
1196e4b7b5f0SManivannan Sadhasivam 
1197e4b7b5f0SManivannan Sadhasivam 		if (!mhi_chan->mhi_dev)
1198e4b7b5f0SManivannan Sadhasivam 			continue;
1199e4b7b5f0SManivannan Sadhasivam 
1200e4b7b5f0SManivannan Sadhasivam 		mutex_lock(&mhi_chan->lock);
1201e4b7b5f0SManivannan Sadhasivam 		/* Skip if the channel is not currently running */
1202e4b7b5f0SManivannan Sadhasivam 		tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[i].chcfg);
1203e4b7b5f0SManivannan Sadhasivam 		if (FIELD_GET(CHAN_CTX_CHSTATE_MASK, tmp) != MHI_CH_STATE_RUNNING) {
1204e4b7b5f0SManivannan Sadhasivam 			mutex_unlock(&mhi_chan->lock);
1205e4b7b5f0SManivannan Sadhasivam 			continue;
1206e4b7b5f0SManivannan Sadhasivam 		}
1207e4b7b5f0SManivannan Sadhasivam 
1208e4b7b5f0SManivannan Sadhasivam 		dev_dbg(&mhi_chan->mhi_dev->dev, "Suspending channel\n");
1209e4b7b5f0SManivannan Sadhasivam 		/* Set channel state to SUSPENDED */
12108a1c24bbSManivannan Sadhasivam 		mhi_chan->state = MHI_CH_STATE_SUSPENDED;
1211e4b7b5f0SManivannan Sadhasivam 		tmp &= ~CHAN_CTX_CHSTATE_MASK;
1212e4b7b5f0SManivannan Sadhasivam 		tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_SUSPENDED);
1213e4b7b5f0SManivannan Sadhasivam 		mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp);
1214e4b7b5f0SManivannan Sadhasivam 		mutex_unlock(&mhi_chan->lock);
1215e4b7b5f0SManivannan Sadhasivam 	}
1216e4b7b5f0SManivannan Sadhasivam }
1217e4b7b5f0SManivannan Sadhasivam 
mhi_ep_resume_channels(struct mhi_ep_cntrl * mhi_cntrl)1218e4b7b5f0SManivannan Sadhasivam void mhi_ep_resume_channels(struct mhi_ep_cntrl *mhi_cntrl)
1219e4b7b5f0SManivannan Sadhasivam {
1220e4b7b5f0SManivannan Sadhasivam 	struct mhi_ep_chan *mhi_chan;
1221e4b7b5f0SManivannan Sadhasivam 	u32 tmp;
1222e4b7b5f0SManivannan Sadhasivam 	int i;
1223e4b7b5f0SManivannan Sadhasivam 
1224e4b7b5f0SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->max_chan; i++) {
1225e4b7b5f0SManivannan Sadhasivam 		mhi_chan = &mhi_cntrl->mhi_chan[i];
1226e4b7b5f0SManivannan Sadhasivam 
1227e4b7b5f0SManivannan Sadhasivam 		if (!mhi_chan->mhi_dev)
1228e4b7b5f0SManivannan Sadhasivam 			continue;
1229e4b7b5f0SManivannan Sadhasivam 
1230e4b7b5f0SManivannan Sadhasivam 		mutex_lock(&mhi_chan->lock);
1231e4b7b5f0SManivannan Sadhasivam 		/* Skip if the channel is not currently suspended */
1232e4b7b5f0SManivannan Sadhasivam 		tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[i].chcfg);
1233e4b7b5f0SManivannan Sadhasivam 		if (FIELD_GET(CHAN_CTX_CHSTATE_MASK, tmp) != MHI_CH_STATE_SUSPENDED) {
1234e4b7b5f0SManivannan Sadhasivam 			mutex_unlock(&mhi_chan->lock);
1235e4b7b5f0SManivannan Sadhasivam 			continue;
1236e4b7b5f0SManivannan Sadhasivam 		}
1237e4b7b5f0SManivannan Sadhasivam 
1238e4b7b5f0SManivannan Sadhasivam 		dev_dbg(&mhi_chan->mhi_dev->dev, "Resuming channel\n");
1239e4b7b5f0SManivannan Sadhasivam 		/* Set channel state to RUNNING */
12408a1c24bbSManivannan Sadhasivam 		mhi_chan->state = MHI_CH_STATE_RUNNING;
1241e4b7b5f0SManivannan Sadhasivam 		tmp &= ~CHAN_CTX_CHSTATE_MASK;
1242e4b7b5f0SManivannan Sadhasivam 		tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_RUNNING);
1243e4b7b5f0SManivannan Sadhasivam 		mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp);
1244e4b7b5f0SManivannan Sadhasivam 		mutex_unlock(&mhi_chan->lock);
1245e4b7b5f0SManivannan Sadhasivam 	}
1246e4b7b5f0SManivannan Sadhasivam }
1247e4b7b5f0SManivannan Sadhasivam 
mhi_ep_release_device(struct device * dev)1248d434743eSManivannan Sadhasivam static void mhi_ep_release_device(struct device *dev)
1249d434743eSManivannan Sadhasivam {
1250d434743eSManivannan Sadhasivam 	struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
1251d434743eSManivannan Sadhasivam 
1252d434743eSManivannan Sadhasivam 	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
1253d434743eSManivannan Sadhasivam 		mhi_dev->mhi_cntrl->mhi_dev = NULL;
1254d434743eSManivannan Sadhasivam 
1255d434743eSManivannan Sadhasivam 	/*
1256d434743eSManivannan Sadhasivam 	 * We need to set the mhi_chan->mhi_dev to NULL here since the MHI
1257d434743eSManivannan Sadhasivam 	 * devices for the channels will only get created in mhi_ep_create_device()
1258d434743eSManivannan Sadhasivam 	 * if the mhi_dev associated with it is NULL.
1259d434743eSManivannan Sadhasivam 	 */
1260d434743eSManivannan Sadhasivam 	if (mhi_dev->ul_chan)
1261d434743eSManivannan Sadhasivam 		mhi_dev->ul_chan->mhi_dev = NULL;
1262d434743eSManivannan Sadhasivam 
1263d434743eSManivannan Sadhasivam 	if (mhi_dev->dl_chan)
1264d434743eSManivannan Sadhasivam 		mhi_dev->dl_chan->mhi_dev = NULL;
1265d434743eSManivannan Sadhasivam 
1266d434743eSManivannan Sadhasivam 	kfree(mhi_dev);
1267d434743eSManivannan Sadhasivam }
1268d434743eSManivannan Sadhasivam 
mhi_ep_alloc_device(struct mhi_ep_cntrl * mhi_cntrl,enum mhi_device_type dev_type)1269d434743eSManivannan Sadhasivam static struct mhi_ep_device *mhi_ep_alloc_device(struct mhi_ep_cntrl *mhi_cntrl,
1270d434743eSManivannan Sadhasivam 						 enum mhi_device_type dev_type)
1271d434743eSManivannan Sadhasivam {
1272d434743eSManivannan Sadhasivam 	struct mhi_ep_device *mhi_dev;
1273d434743eSManivannan Sadhasivam 	struct device *dev;
1274d434743eSManivannan Sadhasivam 
1275d434743eSManivannan Sadhasivam 	mhi_dev = kzalloc(sizeof(*mhi_dev), GFP_KERNEL);
1276d434743eSManivannan Sadhasivam 	if (!mhi_dev)
1277d434743eSManivannan Sadhasivam 		return ERR_PTR(-ENOMEM);
1278d434743eSManivannan Sadhasivam 
1279d434743eSManivannan Sadhasivam 	dev = &mhi_dev->dev;
1280d434743eSManivannan Sadhasivam 	device_initialize(dev);
1281d434743eSManivannan Sadhasivam 	dev->bus = &mhi_ep_bus_type;
1282d434743eSManivannan Sadhasivam 	dev->release = mhi_ep_release_device;
1283d434743eSManivannan Sadhasivam 
1284d434743eSManivannan Sadhasivam 	/* Controller device is always allocated first */
1285d434743eSManivannan Sadhasivam 	if (dev_type == MHI_DEVICE_CONTROLLER)
1286d434743eSManivannan Sadhasivam 		/* for MHI controller device, parent is the bus device (e.g. PCI EPF) */
1287d434743eSManivannan Sadhasivam 		dev->parent = mhi_cntrl->cntrl_dev;
1288d434743eSManivannan Sadhasivam 	else
1289d434743eSManivannan Sadhasivam 		/* for MHI client devices, parent is the MHI controller device */
1290d434743eSManivannan Sadhasivam 		dev->parent = &mhi_cntrl->mhi_dev->dev;
1291d434743eSManivannan Sadhasivam 
1292d434743eSManivannan Sadhasivam 	mhi_dev->mhi_cntrl = mhi_cntrl;
1293d434743eSManivannan Sadhasivam 	mhi_dev->dev_type = dev_type;
1294d434743eSManivannan Sadhasivam 
1295d434743eSManivannan Sadhasivam 	return mhi_dev;
1296d434743eSManivannan Sadhasivam }
1297d434743eSManivannan Sadhasivam 
1298297c77a0SManivannan Sadhasivam /*
1299297c77a0SManivannan Sadhasivam  * MHI channels are always defined in pairs with UL as the even numbered
1300297c77a0SManivannan Sadhasivam  * channel and DL as odd numbered one. This function gets UL channel (primary)
1301297c77a0SManivannan Sadhasivam  * as the ch_id and always looks after the next entry in channel list for
1302297c77a0SManivannan Sadhasivam  * the corresponding DL channel (secondary).
1303297c77a0SManivannan Sadhasivam  */
mhi_ep_create_device(struct mhi_ep_cntrl * mhi_cntrl,u32 ch_id)1304297c77a0SManivannan Sadhasivam static int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id)
1305297c77a0SManivannan Sadhasivam {
1306297c77a0SManivannan Sadhasivam 	struct mhi_ep_chan *mhi_chan = &mhi_cntrl->mhi_chan[ch_id];
1307297c77a0SManivannan Sadhasivam 	struct device *dev = mhi_cntrl->cntrl_dev;
1308297c77a0SManivannan Sadhasivam 	struct mhi_ep_device *mhi_dev;
1309297c77a0SManivannan Sadhasivam 	int ret;
1310297c77a0SManivannan Sadhasivam 
1311297c77a0SManivannan Sadhasivam 	/* Check if the channel name is same for both UL and DL */
1312297c77a0SManivannan Sadhasivam 	if (strcmp(mhi_chan->name, mhi_chan[1].name)) {
1313297c77a0SManivannan Sadhasivam 		dev_err(dev, "UL and DL channel names are not same: (%s) != (%s)\n",
1314297c77a0SManivannan Sadhasivam 			mhi_chan->name, mhi_chan[1].name);
1315297c77a0SManivannan Sadhasivam 		return -EINVAL;
1316297c77a0SManivannan Sadhasivam 	}
1317297c77a0SManivannan Sadhasivam 
1318297c77a0SManivannan Sadhasivam 	mhi_dev = mhi_ep_alloc_device(mhi_cntrl, MHI_DEVICE_XFER);
1319297c77a0SManivannan Sadhasivam 	if (IS_ERR(mhi_dev))
1320297c77a0SManivannan Sadhasivam 		return PTR_ERR(mhi_dev);
1321297c77a0SManivannan Sadhasivam 
1322297c77a0SManivannan Sadhasivam 	/* Configure primary channel */
1323297c77a0SManivannan Sadhasivam 	mhi_dev->ul_chan = mhi_chan;
1324297c77a0SManivannan Sadhasivam 	get_device(&mhi_dev->dev);
1325297c77a0SManivannan Sadhasivam 	mhi_chan->mhi_dev = mhi_dev;
1326297c77a0SManivannan Sadhasivam 
1327297c77a0SManivannan Sadhasivam 	/* Configure secondary channel as well */
1328297c77a0SManivannan Sadhasivam 	mhi_chan++;
1329297c77a0SManivannan Sadhasivam 	mhi_dev->dl_chan = mhi_chan;
1330297c77a0SManivannan Sadhasivam 	get_device(&mhi_dev->dev);
1331297c77a0SManivannan Sadhasivam 	mhi_chan->mhi_dev = mhi_dev;
1332297c77a0SManivannan Sadhasivam 
1333297c77a0SManivannan Sadhasivam 	/* Channel name is same for both UL and DL */
1334297c77a0SManivannan Sadhasivam 	mhi_dev->name = mhi_chan->name;
13352ebb36eaSBo Liu 	ret = dev_set_name(&mhi_dev->dev, "%s_%s",
1336297c77a0SManivannan Sadhasivam 		     dev_name(&mhi_cntrl->mhi_dev->dev),
1337297c77a0SManivannan Sadhasivam 		     mhi_dev->name);
13382ebb36eaSBo Liu 	if (ret) {
13392ebb36eaSBo Liu 		put_device(&mhi_dev->dev);
13402ebb36eaSBo Liu 		return ret;
13412ebb36eaSBo Liu 	}
1342297c77a0SManivannan Sadhasivam 
1343297c77a0SManivannan Sadhasivam 	ret = device_add(&mhi_dev->dev);
1344297c77a0SManivannan Sadhasivam 	if (ret)
1345297c77a0SManivannan Sadhasivam 		put_device(&mhi_dev->dev);
1346297c77a0SManivannan Sadhasivam 
1347297c77a0SManivannan Sadhasivam 	return ret;
1348297c77a0SManivannan Sadhasivam }
1349297c77a0SManivannan Sadhasivam 
mhi_ep_destroy_device(struct device * dev,void * data)1350297c77a0SManivannan Sadhasivam static int mhi_ep_destroy_device(struct device *dev, void *data)
1351297c77a0SManivannan Sadhasivam {
1352297c77a0SManivannan Sadhasivam 	struct mhi_ep_device *mhi_dev;
1353297c77a0SManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl;
1354297c77a0SManivannan Sadhasivam 	struct mhi_ep_chan *ul_chan, *dl_chan;
1355297c77a0SManivannan Sadhasivam 
1356297c77a0SManivannan Sadhasivam 	if (dev->bus != &mhi_ep_bus_type)
1357297c77a0SManivannan Sadhasivam 		return 0;
1358297c77a0SManivannan Sadhasivam 
1359297c77a0SManivannan Sadhasivam 	mhi_dev = to_mhi_ep_device(dev);
1360297c77a0SManivannan Sadhasivam 	mhi_cntrl = mhi_dev->mhi_cntrl;
1361297c77a0SManivannan Sadhasivam 
1362297c77a0SManivannan Sadhasivam 	/* Only destroy devices created for channels */
1363297c77a0SManivannan Sadhasivam 	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
1364297c77a0SManivannan Sadhasivam 		return 0;
1365297c77a0SManivannan Sadhasivam 
1366297c77a0SManivannan Sadhasivam 	ul_chan = mhi_dev->ul_chan;
1367297c77a0SManivannan Sadhasivam 	dl_chan = mhi_dev->dl_chan;
1368297c77a0SManivannan Sadhasivam 
1369297c77a0SManivannan Sadhasivam 	if (ul_chan)
1370297c77a0SManivannan Sadhasivam 		put_device(&ul_chan->mhi_dev->dev);
1371297c77a0SManivannan Sadhasivam 
1372297c77a0SManivannan Sadhasivam 	if (dl_chan)
1373297c77a0SManivannan Sadhasivam 		put_device(&dl_chan->mhi_dev->dev);
1374297c77a0SManivannan Sadhasivam 
1375297c77a0SManivannan Sadhasivam 	dev_dbg(&mhi_cntrl->mhi_dev->dev, "Destroying device for chan:%s\n",
1376297c77a0SManivannan Sadhasivam 		 mhi_dev->name);
1377297c77a0SManivannan Sadhasivam 
1378297c77a0SManivannan Sadhasivam 	/* Notify the client and remove the device from MHI bus */
1379297c77a0SManivannan Sadhasivam 	device_del(dev);
1380297c77a0SManivannan Sadhasivam 	put_device(dev);
1381297c77a0SManivannan Sadhasivam 
1382297c77a0SManivannan Sadhasivam 	return 0;
1383297c77a0SManivannan Sadhasivam }
1384297c77a0SManivannan Sadhasivam 
mhi_ep_chan_init(struct mhi_ep_cntrl * mhi_cntrl,const struct mhi_ep_cntrl_config * config)1385d434743eSManivannan Sadhasivam static int mhi_ep_chan_init(struct mhi_ep_cntrl *mhi_cntrl,
1386d434743eSManivannan Sadhasivam 			    const struct mhi_ep_cntrl_config *config)
1387d434743eSManivannan Sadhasivam {
1388d434743eSManivannan Sadhasivam 	const struct mhi_ep_channel_config *ch_cfg;
1389d434743eSManivannan Sadhasivam 	struct device *dev = mhi_cntrl->cntrl_dev;
1390d434743eSManivannan Sadhasivam 	u32 chan, i;
1391d434743eSManivannan Sadhasivam 	int ret = -EINVAL;
1392d434743eSManivannan Sadhasivam 
1393d434743eSManivannan Sadhasivam 	mhi_cntrl->max_chan = config->max_channels;
1394d434743eSManivannan Sadhasivam 
1395d434743eSManivannan Sadhasivam 	/*
1396d434743eSManivannan Sadhasivam 	 * Allocate max_channels supported by the MHI endpoint and populate
1397d434743eSManivannan Sadhasivam 	 * only the defined channels
1398d434743eSManivannan Sadhasivam 	 */
1399d434743eSManivannan Sadhasivam 	mhi_cntrl->mhi_chan = kcalloc(mhi_cntrl->max_chan, sizeof(*mhi_cntrl->mhi_chan),
1400d434743eSManivannan Sadhasivam 				      GFP_KERNEL);
1401d434743eSManivannan Sadhasivam 	if (!mhi_cntrl->mhi_chan)
1402d434743eSManivannan Sadhasivam 		return -ENOMEM;
1403d434743eSManivannan Sadhasivam 
1404d434743eSManivannan Sadhasivam 	for (i = 0; i < config->num_channels; i++) {
1405d434743eSManivannan Sadhasivam 		struct mhi_ep_chan *mhi_chan;
1406d434743eSManivannan Sadhasivam 
1407d434743eSManivannan Sadhasivam 		ch_cfg = &config->ch_cfg[i];
1408d434743eSManivannan Sadhasivam 
1409d434743eSManivannan Sadhasivam 		chan = ch_cfg->num;
1410d434743eSManivannan Sadhasivam 		if (chan >= mhi_cntrl->max_chan) {
1411d434743eSManivannan Sadhasivam 			dev_err(dev, "Channel (%u) exceeds maximum available channels (%u)\n",
1412d434743eSManivannan Sadhasivam 				chan, mhi_cntrl->max_chan);
1413d434743eSManivannan Sadhasivam 			goto error_chan_cfg;
1414d434743eSManivannan Sadhasivam 		}
1415d434743eSManivannan Sadhasivam 
1416d434743eSManivannan Sadhasivam 		/* Bi-directional and direction less channels are not supported */
1417d434743eSManivannan Sadhasivam 		if (ch_cfg->dir == DMA_BIDIRECTIONAL || ch_cfg->dir == DMA_NONE) {
1418d434743eSManivannan Sadhasivam 			dev_err(dev, "Invalid direction (%u) for channel (%u)\n",
1419d434743eSManivannan Sadhasivam 				ch_cfg->dir, chan);
1420d434743eSManivannan Sadhasivam 			goto error_chan_cfg;
1421d434743eSManivannan Sadhasivam 		}
1422d434743eSManivannan Sadhasivam 
1423d434743eSManivannan Sadhasivam 		mhi_chan = &mhi_cntrl->mhi_chan[chan];
1424d434743eSManivannan Sadhasivam 		mhi_chan->name = ch_cfg->name;
1425d434743eSManivannan Sadhasivam 		mhi_chan->chan = chan;
1426d434743eSManivannan Sadhasivam 		mhi_chan->dir = ch_cfg->dir;
1427d434743eSManivannan Sadhasivam 		mutex_init(&mhi_chan->lock);
1428d434743eSManivannan Sadhasivam 	}
1429d434743eSManivannan Sadhasivam 
1430d434743eSManivannan Sadhasivam 	return 0;
1431d434743eSManivannan Sadhasivam 
1432d434743eSManivannan Sadhasivam error_chan_cfg:
1433d434743eSManivannan Sadhasivam 	kfree(mhi_cntrl->mhi_chan);
1434d434743eSManivannan Sadhasivam 
1435d434743eSManivannan Sadhasivam 	return ret;
1436d434743eSManivannan Sadhasivam }
1437d434743eSManivannan Sadhasivam 
1438d434743eSManivannan Sadhasivam /*
1439d434743eSManivannan Sadhasivam  * Allocate channel and command rings here. Event rings will be allocated
1440d434743eSManivannan Sadhasivam  * in mhi_ep_power_up() as the config comes from the host.
1441d434743eSManivannan Sadhasivam  */
mhi_ep_register_controller(struct mhi_ep_cntrl * mhi_cntrl,const struct mhi_ep_cntrl_config * config)1442d434743eSManivannan Sadhasivam int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
1443d434743eSManivannan Sadhasivam 				const struct mhi_ep_cntrl_config *config)
1444d434743eSManivannan Sadhasivam {
1445d434743eSManivannan Sadhasivam 	struct mhi_ep_device *mhi_dev;
1446d434743eSManivannan Sadhasivam 	int ret;
1447d434743eSManivannan Sadhasivam 
14484799e71bSManivannan Sadhasivam 	if (!mhi_cntrl || !mhi_cntrl->cntrl_dev || !mhi_cntrl->mmio || !mhi_cntrl->irq)
1449d434743eSManivannan Sadhasivam 		return -EINVAL;
1450d434743eSManivannan Sadhasivam 
1451d434743eSManivannan Sadhasivam 	ret = mhi_ep_chan_init(mhi_cntrl, config);
1452d434743eSManivannan Sadhasivam 	if (ret)
1453d434743eSManivannan Sadhasivam 		return ret;
1454d434743eSManivannan Sadhasivam 
1455d434743eSManivannan Sadhasivam 	mhi_cntrl->mhi_cmd = kcalloc(NR_OF_CMD_RINGS, sizeof(*mhi_cntrl->mhi_cmd), GFP_KERNEL);
1456d434743eSManivannan Sadhasivam 	if (!mhi_cntrl->mhi_cmd) {
1457d434743eSManivannan Sadhasivam 		ret = -ENOMEM;
1458d434743eSManivannan Sadhasivam 		goto err_free_ch;
1459d434743eSManivannan Sadhasivam 	}
1460d434743eSManivannan Sadhasivam 
1461bd4f6f1fSManivannan Sadhasivam 	mhi_cntrl->ev_ring_el_cache = kmem_cache_create("mhi_ep_event_ring_el",
1462bd4f6f1fSManivannan Sadhasivam 							sizeof(struct mhi_ring_element), 0,
1463*43987659SManivannan Sadhasivam 							0, NULL);
1464bd4f6f1fSManivannan Sadhasivam 	if (!mhi_cntrl->ev_ring_el_cache) {
1465bd4f6f1fSManivannan Sadhasivam 		ret = -ENOMEM;
1466bd4f6f1fSManivannan Sadhasivam 		goto err_free_cmd;
1467bd4f6f1fSManivannan Sadhasivam 	}
1468bd4f6f1fSManivannan Sadhasivam 
1469bd4f6f1fSManivannan Sadhasivam 	mhi_cntrl->tre_buf_cache = kmem_cache_create("mhi_ep_tre_buf", MHI_EP_DEFAULT_MTU, 0,
1470*43987659SManivannan Sadhasivam 						      0, NULL);
1471bd4f6f1fSManivannan Sadhasivam 	if (!mhi_cntrl->tre_buf_cache) {
1472bd4f6f1fSManivannan Sadhasivam 		ret = -ENOMEM;
1473bd4f6f1fSManivannan Sadhasivam 		goto err_destroy_ev_ring_el_cache;
1474bd4f6f1fSManivannan Sadhasivam 	}
1475bd4f6f1fSManivannan Sadhasivam 
1476bd4f6f1fSManivannan Sadhasivam 	mhi_cntrl->ring_item_cache = kmem_cache_create("mhi_ep_ring_item",
1477bd4f6f1fSManivannan Sadhasivam 							sizeof(struct mhi_ep_ring_item), 0,
1478bd4f6f1fSManivannan Sadhasivam 							0, NULL);
14790c925477SDan Carpenter 	if (!mhi_cntrl->ring_item_cache) {
1480bd4f6f1fSManivannan Sadhasivam 		ret = -ENOMEM;
1481bd4f6f1fSManivannan Sadhasivam 		goto err_destroy_tre_buf_cache;
1482bd4f6f1fSManivannan Sadhasivam 	}
1483b6af3a95SManivannan Sadhasivam 
1484f9baa4f7SManivannan Sadhasivam 	INIT_WORK(&mhi_cntrl->state_work, mhi_ep_state_worker);
14857a97b6b4SManivannan Sadhasivam 	INIT_WORK(&mhi_cntrl->reset_work, mhi_ep_reset_worker);
1486e8275690SManivannan Sadhasivam 	INIT_WORK(&mhi_cntrl->cmd_ring_work, mhi_ep_cmd_ring_worker);
148703c0bb8eSManivannan Sadhasivam 	INIT_WORK(&mhi_cntrl->ch_ring_work, mhi_ep_ch_ring_worker);
1488f9baa4f7SManivannan Sadhasivam 
1489f9baa4f7SManivannan Sadhasivam 	mhi_cntrl->wq = alloc_workqueue("mhi_ep_wq", 0, 0);
1490f9baa4f7SManivannan Sadhasivam 	if (!mhi_cntrl->wq) {
1491f9baa4f7SManivannan Sadhasivam 		ret = -ENOMEM;
1492bd4f6f1fSManivannan Sadhasivam 		goto err_destroy_ring_item_cache;
1493f9baa4f7SManivannan Sadhasivam 	}
1494f9baa4f7SManivannan Sadhasivam 
1495f9baa4f7SManivannan Sadhasivam 	INIT_LIST_HEAD(&mhi_cntrl->st_transition_list);
14964799e71bSManivannan Sadhasivam 	INIT_LIST_HEAD(&mhi_cntrl->ch_db_list);
1497f9baa4f7SManivannan Sadhasivam 	spin_lock_init(&mhi_cntrl->list_lock);
14981ddc7618SManivannan Sadhasivam 	mutex_init(&mhi_cntrl->state_lock);
1499961aeb68SManivannan Sadhasivam 	mutex_init(&mhi_cntrl->event_lock);
1500961aeb68SManivannan Sadhasivam 
1501e9e4da23SManivannan Sadhasivam 	/* Set MHI version and AMSS EE before enumeration */
1502e9e4da23SManivannan Sadhasivam 	mhi_ep_mmio_write(mhi_cntrl, EP_MHIVER, config->mhi_version);
1503e9e4da23SManivannan Sadhasivam 	mhi_ep_mmio_set_env(mhi_cntrl, MHI_EE_AMSS);
1504e9e4da23SManivannan Sadhasivam 
1505d434743eSManivannan Sadhasivam 	/* Set controller index */
1506d434743eSManivannan Sadhasivam 	ret = ida_alloc(&mhi_ep_cntrl_ida, GFP_KERNEL);
1507d434743eSManivannan Sadhasivam 	if (ret < 0)
1508f9baa4f7SManivannan Sadhasivam 		goto err_destroy_wq;
1509d434743eSManivannan Sadhasivam 
1510d434743eSManivannan Sadhasivam 	mhi_cntrl->index = ret;
1511d434743eSManivannan Sadhasivam 
15124799e71bSManivannan Sadhasivam 	irq_set_status_flags(mhi_cntrl->irq, IRQ_NOAUTOEN);
15134799e71bSManivannan Sadhasivam 	ret = request_irq(mhi_cntrl->irq, mhi_ep_irq, IRQF_TRIGGER_HIGH,
15144799e71bSManivannan Sadhasivam 			  "doorbell_irq", mhi_cntrl);
15154799e71bSManivannan Sadhasivam 	if (ret) {
15164799e71bSManivannan Sadhasivam 		dev_err(mhi_cntrl->cntrl_dev, "Failed to request Doorbell IRQ\n");
15174799e71bSManivannan Sadhasivam 		goto err_ida_free;
15184799e71bSManivannan Sadhasivam 	}
15194799e71bSManivannan Sadhasivam 
1520d434743eSManivannan Sadhasivam 	/* Allocate the controller device */
1521d434743eSManivannan Sadhasivam 	mhi_dev = mhi_ep_alloc_device(mhi_cntrl, MHI_DEVICE_CONTROLLER);
1522d434743eSManivannan Sadhasivam 	if (IS_ERR(mhi_dev)) {
1523d434743eSManivannan Sadhasivam 		dev_err(mhi_cntrl->cntrl_dev, "Failed to allocate controller device\n");
1524d434743eSManivannan Sadhasivam 		ret = PTR_ERR(mhi_dev);
15254799e71bSManivannan Sadhasivam 		goto err_free_irq;
1526d434743eSManivannan Sadhasivam 	}
1527d434743eSManivannan Sadhasivam 
15282ebb36eaSBo Liu 	ret = dev_set_name(&mhi_dev->dev, "mhi_ep%u", mhi_cntrl->index);
15292ebb36eaSBo Liu 	if (ret)
15302ebb36eaSBo Liu 		goto err_put_dev;
15312ebb36eaSBo Liu 
1532d434743eSManivannan Sadhasivam 	mhi_dev->name = dev_name(&mhi_dev->dev);
1533d434743eSManivannan Sadhasivam 	mhi_cntrl->mhi_dev = mhi_dev;
1534d434743eSManivannan Sadhasivam 
1535d434743eSManivannan Sadhasivam 	ret = device_add(&mhi_dev->dev);
1536d434743eSManivannan Sadhasivam 	if (ret)
1537d434743eSManivannan Sadhasivam 		goto err_put_dev;
1538d434743eSManivannan Sadhasivam 
1539d434743eSManivannan Sadhasivam 	dev_dbg(&mhi_dev->dev, "MHI EP Controller registered\n");
1540d434743eSManivannan Sadhasivam 
1541d434743eSManivannan Sadhasivam 	return 0;
1542d434743eSManivannan Sadhasivam 
1543d434743eSManivannan Sadhasivam err_put_dev:
1544d434743eSManivannan Sadhasivam 	put_device(&mhi_dev->dev);
15454799e71bSManivannan Sadhasivam err_free_irq:
15464799e71bSManivannan Sadhasivam 	free_irq(mhi_cntrl->irq, mhi_cntrl);
1547d434743eSManivannan Sadhasivam err_ida_free:
1548d434743eSManivannan Sadhasivam 	ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index);
1549f9baa4f7SManivannan Sadhasivam err_destroy_wq:
1550f9baa4f7SManivannan Sadhasivam 	destroy_workqueue(mhi_cntrl->wq);
1551bd4f6f1fSManivannan Sadhasivam err_destroy_ring_item_cache:
1552bd4f6f1fSManivannan Sadhasivam 	kmem_cache_destroy(mhi_cntrl->ring_item_cache);
1553bd4f6f1fSManivannan Sadhasivam err_destroy_ev_ring_el_cache:
1554bd4f6f1fSManivannan Sadhasivam 	kmem_cache_destroy(mhi_cntrl->ev_ring_el_cache);
1555bd4f6f1fSManivannan Sadhasivam err_destroy_tre_buf_cache:
1556bd4f6f1fSManivannan Sadhasivam 	kmem_cache_destroy(mhi_cntrl->tre_buf_cache);
1557d434743eSManivannan Sadhasivam err_free_cmd:
1558d434743eSManivannan Sadhasivam 	kfree(mhi_cntrl->mhi_cmd);
1559d434743eSManivannan Sadhasivam err_free_ch:
1560d434743eSManivannan Sadhasivam 	kfree(mhi_cntrl->mhi_chan);
1561d434743eSManivannan Sadhasivam 
1562d434743eSManivannan Sadhasivam 	return ret;
1563d434743eSManivannan Sadhasivam }
1564d434743eSManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_ep_register_controller);
1565d434743eSManivannan Sadhasivam 
15665d507ee0SManivannan Sadhasivam /*
15675d507ee0SManivannan Sadhasivam  * It is expected that the controller drivers will power down the MHI EP stack
15685d507ee0SManivannan Sadhasivam  * using "mhi_ep_power_down()" before calling this function to unregister themselves.
15695d507ee0SManivannan Sadhasivam  */
mhi_ep_unregister_controller(struct mhi_ep_cntrl * mhi_cntrl)1570d434743eSManivannan Sadhasivam void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl)
1571d434743eSManivannan Sadhasivam {
1572d434743eSManivannan Sadhasivam 	struct mhi_ep_device *mhi_dev = mhi_cntrl->mhi_dev;
1573d434743eSManivannan Sadhasivam 
1574f9baa4f7SManivannan Sadhasivam 	destroy_workqueue(mhi_cntrl->wq);
1575f9baa4f7SManivannan Sadhasivam 
15764799e71bSManivannan Sadhasivam 	free_irq(mhi_cntrl->irq, mhi_cntrl);
15774799e71bSManivannan Sadhasivam 
1578bd4f6f1fSManivannan Sadhasivam 	kmem_cache_destroy(mhi_cntrl->tre_buf_cache);
1579bd4f6f1fSManivannan Sadhasivam 	kmem_cache_destroy(mhi_cntrl->ev_ring_el_cache);
1580bd4f6f1fSManivannan Sadhasivam 	kmem_cache_destroy(mhi_cntrl->ring_item_cache);
1581d434743eSManivannan Sadhasivam 	kfree(mhi_cntrl->mhi_cmd);
1582d434743eSManivannan Sadhasivam 	kfree(mhi_cntrl->mhi_chan);
1583d434743eSManivannan Sadhasivam 
1584d434743eSManivannan Sadhasivam 	device_del(&mhi_dev->dev);
1585d434743eSManivannan Sadhasivam 	put_device(&mhi_dev->dev);
1586d434743eSManivannan Sadhasivam 
1587d434743eSManivannan Sadhasivam 	ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index);
1588d434743eSManivannan Sadhasivam }
1589d434743eSManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_ep_unregister_controller);
1590d434743eSManivannan Sadhasivam 
mhi_ep_driver_probe(struct device * dev)1591ee0360b2SManivannan Sadhasivam static int mhi_ep_driver_probe(struct device *dev)
1592ee0360b2SManivannan Sadhasivam {
1593ee0360b2SManivannan Sadhasivam 	struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
1594ee0360b2SManivannan Sadhasivam 	struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
1595ee0360b2SManivannan Sadhasivam 	struct mhi_ep_chan *ul_chan = mhi_dev->ul_chan;
1596ee0360b2SManivannan Sadhasivam 	struct mhi_ep_chan *dl_chan = mhi_dev->dl_chan;
1597ee0360b2SManivannan Sadhasivam 
1598ee0360b2SManivannan Sadhasivam 	ul_chan->xfer_cb = mhi_drv->ul_xfer_cb;
1599ee0360b2SManivannan Sadhasivam 	dl_chan->xfer_cb = mhi_drv->dl_xfer_cb;
1600ee0360b2SManivannan Sadhasivam 
1601ee0360b2SManivannan Sadhasivam 	return mhi_drv->probe(mhi_dev, mhi_dev->id);
1602ee0360b2SManivannan Sadhasivam }
1603ee0360b2SManivannan Sadhasivam 
mhi_ep_driver_remove(struct device * dev)1604ee0360b2SManivannan Sadhasivam static int mhi_ep_driver_remove(struct device *dev)
1605ee0360b2SManivannan Sadhasivam {
1606ee0360b2SManivannan Sadhasivam 	struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
1607ee0360b2SManivannan Sadhasivam 	struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
1608ee0360b2SManivannan Sadhasivam 	struct mhi_result result = {};
1609ee0360b2SManivannan Sadhasivam 	struct mhi_ep_chan *mhi_chan;
1610ee0360b2SManivannan Sadhasivam 	int dir;
1611ee0360b2SManivannan Sadhasivam 
1612ee0360b2SManivannan Sadhasivam 	/* Skip if it is a controller device */
1613ee0360b2SManivannan Sadhasivam 	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
1614ee0360b2SManivannan Sadhasivam 		return 0;
1615ee0360b2SManivannan Sadhasivam 
1616ee0360b2SManivannan Sadhasivam 	/* Disconnect the channels associated with the driver */
1617ee0360b2SManivannan Sadhasivam 	for (dir = 0; dir < 2; dir++) {
1618ee0360b2SManivannan Sadhasivam 		mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
1619ee0360b2SManivannan Sadhasivam 
1620ee0360b2SManivannan Sadhasivam 		if (!mhi_chan)
1621ee0360b2SManivannan Sadhasivam 			continue;
1622ee0360b2SManivannan Sadhasivam 
1623ee0360b2SManivannan Sadhasivam 		mutex_lock(&mhi_chan->lock);
1624ee0360b2SManivannan Sadhasivam 		/* Send channel disconnect status to the client driver */
1625ee0360b2SManivannan Sadhasivam 		if (mhi_chan->xfer_cb) {
1626ee0360b2SManivannan Sadhasivam 			result.transaction_status = -ENOTCONN;
1627ee0360b2SManivannan Sadhasivam 			result.bytes_xferd = 0;
1628ee0360b2SManivannan Sadhasivam 			mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
1629ee0360b2SManivannan Sadhasivam 		}
1630ee0360b2SManivannan Sadhasivam 
1631ee0360b2SManivannan Sadhasivam 		mhi_chan->state = MHI_CH_STATE_DISABLED;
1632ee0360b2SManivannan Sadhasivam 		mhi_chan->xfer_cb = NULL;
1633ee0360b2SManivannan Sadhasivam 		mutex_unlock(&mhi_chan->lock);
1634ee0360b2SManivannan Sadhasivam 	}
1635ee0360b2SManivannan Sadhasivam 
1636ee0360b2SManivannan Sadhasivam 	/* Remove the client driver now */
1637ee0360b2SManivannan Sadhasivam 	mhi_drv->remove(mhi_dev);
1638ee0360b2SManivannan Sadhasivam 
1639ee0360b2SManivannan Sadhasivam 	return 0;
1640ee0360b2SManivannan Sadhasivam }
1641ee0360b2SManivannan Sadhasivam 
__mhi_ep_driver_register(struct mhi_ep_driver * mhi_drv,struct module * owner)1642ee0360b2SManivannan Sadhasivam int __mhi_ep_driver_register(struct mhi_ep_driver *mhi_drv, struct module *owner)
1643ee0360b2SManivannan Sadhasivam {
1644ee0360b2SManivannan Sadhasivam 	struct device_driver *driver = &mhi_drv->driver;
1645ee0360b2SManivannan Sadhasivam 
1646ee0360b2SManivannan Sadhasivam 	if (!mhi_drv->probe || !mhi_drv->remove)
1647ee0360b2SManivannan Sadhasivam 		return -EINVAL;
1648ee0360b2SManivannan Sadhasivam 
1649ee0360b2SManivannan Sadhasivam 	/* Client drivers should have callbacks defined for both channels */
1650ee0360b2SManivannan Sadhasivam 	if (!mhi_drv->ul_xfer_cb || !mhi_drv->dl_xfer_cb)
1651ee0360b2SManivannan Sadhasivam 		return -EINVAL;
1652ee0360b2SManivannan Sadhasivam 
1653ee0360b2SManivannan Sadhasivam 	driver->bus = &mhi_ep_bus_type;
1654ee0360b2SManivannan Sadhasivam 	driver->owner = owner;
1655ee0360b2SManivannan Sadhasivam 	driver->probe = mhi_ep_driver_probe;
1656ee0360b2SManivannan Sadhasivam 	driver->remove = mhi_ep_driver_remove;
1657ee0360b2SManivannan Sadhasivam 
1658ee0360b2SManivannan Sadhasivam 	return driver_register(driver);
1659ee0360b2SManivannan Sadhasivam }
1660ee0360b2SManivannan Sadhasivam EXPORT_SYMBOL_GPL(__mhi_ep_driver_register);
1661ee0360b2SManivannan Sadhasivam 
mhi_ep_driver_unregister(struct mhi_ep_driver * mhi_drv)1662ee0360b2SManivannan Sadhasivam void mhi_ep_driver_unregister(struct mhi_ep_driver *mhi_drv)
1663ee0360b2SManivannan Sadhasivam {
1664ee0360b2SManivannan Sadhasivam 	driver_unregister(&mhi_drv->driver);
1665ee0360b2SManivannan Sadhasivam }
1666ee0360b2SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_ep_driver_unregister);
1667ee0360b2SManivannan Sadhasivam 
mhi_ep_uevent(const struct device * dev,struct kobj_uevent_env * env)16682a81ada3SGreg Kroah-Hartman static int mhi_ep_uevent(const struct device *dev, struct kobj_uevent_env *env)
1669c268c0a8SManivannan Sadhasivam {
16702a81ada3SGreg Kroah-Hartman 	const struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
1671c268c0a8SManivannan Sadhasivam 
1672c268c0a8SManivannan Sadhasivam 	return add_uevent_var(env, "MODALIAS=" MHI_EP_DEVICE_MODALIAS_FMT,
1673c268c0a8SManivannan Sadhasivam 					mhi_dev->name);
1674c268c0a8SManivannan Sadhasivam }
1675c268c0a8SManivannan Sadhasivam 
mhi_ep_match(struct device * dev,struct device_driver * drv)1676d434743eSManivannan Sadhasivam static int mhi_ep_match(struct device *dev, struct device_driver *drv)
1677d434743eSManivannan Sadhasivam {
1678d434743eSManivannan Sadhasivam 	struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
1679ee0360b2SManivannan Sadhasivam 	struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(drv);
1680ee0360b2SManivannan Sadhasivam 	const struct mhi_device_id *id;
1681d434743eSManivannan Sadhasivam 
1682d434743eSManivannan Sadhasivam 	/*
1683d434743eSManivannan Sadhasivam 	 * If the device is a controller type then there is no client driver
1684d434743eSManivannan Sadhasivam 	 * associated with it
1685d434743eSManivannan Sadhasivam 	 */
1686d434743eSManivannan Sadhasivam 	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
1687d434743eSManivannan Sadhasivam 		return 0;
1688d434743eSManivannan Sadhasivam 
1689ee0360b2SManivannan Sadhasivam 	for (id = mhi_drv->id_table; id->chan[0]; id++)
1690ee0360b2SManivannan Sadhasivam 		if (!strcmp(mhi_dev->name, id->chan)) {
1691ee0360b2SManivannan Sadhasivam 			mhi_dev->id = id;
1692ee0360b2SManivannan Sadhasivam 			return 1;
1693ee0360b2SManivannan Sadhasivam 		}
1694ee0360b2SManivannan Sadhasivam 
1695d434743eSManivannan Sadhasivam 	return 0;
1696d434743eSManivannan Sadhasivam };
1697d434743eSManivannan Sadhasivam 
1698d434743eSManivannan Sadhasivam struct bus_type mhi_ep_bus_type = {
1699d434743eSManivannan Sadhasivam 	.name = "mhi_ep",
1700d434743eSManivannan Sadhasivam 	.dev_name = "mhi_ep",
1701d434743eSManivannan Sadhasivam 	.match = mhi_ep_match,
1702c268c0a8SManivannan Sadhasivam 	.uevent = mhi_ep_uevent,
1703d434743eSManivannan Sadhasivam };
1704d434743eSManivannan Sadhasivam 
mhi_ep_init(void)1705d434743eSManivannan Sadhasivam static int __init mhi_ep_init(void)
1706d434743eSManivannan Sadhasivam {
1707d434743eSManivannan Sadhasivam 	return bus_register(&mhi_ep_bus_type);
1708d434743eSManivannan Sadhasivam }
1709d434743eSManivannan Sadhasivam 
mhi_ep_exit(void)1710d434743eSManivannan Sadhasivam static void __exit mhi_ep_exit(void)
1711d434743eSManivannan Sadhasivam {
1712d434743eSManivannan Sadhasivam 	bus_unregister(&mhi_ep_bus_type);
1713d434743eSManivannan Sadhasivam }
1714d434743eSManivannan Sadhasivam 
1715d434743eSManivannan Sadhasivam postcore_initcall(mhi_ep_init);
1716d434743eSManivannan Sadhasivam module_exit(mhi_ep_exit);
1717d434743eSManivannan Sadhasivam 
1718d434743eSManivannan Sadhasivam MODULE_LICENSE("GPL v2");
1719d434743eSManivannan Sadhasivam MODULE_DESCRIPTION("MHI Bus Endpoint stack");
1720d434743eSManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
1721