xref: /openbmc/linux/drivers/bus/mhi/host/init.c (revision ac191bcb)
1a0f5a630SManivannan Sadhasivam // SPDX-License-Identifier: GPL-2.0
2a0f5a630SManivannan Sadhasivam /*
3a0f5a630SManivannan Sadhasivam  * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
4a0f5a630SManivannan Sadhasivam  *
5a0f5a630SManivannan Sadhasivam  */
6a0f5a630SManivannan Sadhasivam 
7d28cab4dSManivannan Sadhasivam #include <linux/bitfield.h>
8a0f5a630SManivannan Sadhasivam #include <linux/debugfs.h>
9a0f5a630SManivannan Sadhasivam #include <linux/device.h>
10a0f5a630SManivannan Sadhasivam #include <linux/dma-direction.h>
11a0f5a630SManivannan Sadhasivam #include <linux/dma-mapping.h>
12a0f5a630SManivannan Sadhasivam #include <linux/idr.h>
13a0f5a630SManivannan Sadhasivam #include <linux/interrupt.h>
14a0f5a630SManivannan Sadhasivam #include <linux/list.h>
15a0f5a630SManivannan Sadhasivam #include <linux/mhi.h>
16a0f5a630SManivannan Sadhasivam #include <linux/mod_devicetable.h>
17a0f5a630SManivannan Sadhasivam #include <linux/module.h>
18a0f5a630SManivannan Sadhasivam #include <linux/slab.h>
19a0f5a630SManivannan Sadhasivam #include <linux/vmalloc.h>
20a0f5a630SManivannan Sadhasivam #include <linux/wait.h>
21a0f5a630SManivannan Sadhasivam #include "internal.h"
22a0f5a630SManivannan Sadhasivam 
23a0f5a630SManivannan Sadhasivam static DEFINE_IDA(mhi_controller_ida);
24a0f5a630SManivannan Sadhasivam 
25a0f5a630SManivannan Sadhasivam const char * const mhi_ee_str[MHI_EE_MAX] = {
26a0f5a630SManivannan Sadhasivam 	[MHI_EE_PBL] = "PRIMARY BOOTLOADER",
27a0f5a630SManivannan Sadhasivam 	[MHI_EE_SBL] = "SECONDARY BOOTLOADER",
28a0f5a630SManivannan Sadhasivam 	[MHI_EE_AMSS] = "MISSION MODE",
29a0f5a630SManivannan Sadhasivam 	[MHI_EE_RDDM] = "RAMDUMP DOWNLOAD MODE",
30a0f5a630SManivannan Sadhasivam 	[MHI_EE_WFW] = "WLAN FIRMWARE",
31a0f5a630SManivannan Sadhasivam 	[MHI_EE_PTHRU] = "PASS THROUGH",
32a0f5a630SManivannan Sadhasivam 	[MHI_EE_EDL] = "EMERGENCY DOWNLOAD",
33a0f5a630SManivannan Sadhasivam 	[MHI_EE_FP] = "FLASH PROGRAMMER",
34a0f5a630SManivannan Sadhasivam 	[MHI_EE_DISABLE_TRANSITION] = "DISABLE",
35a0f5a630SManivannan Sadhasivam 	[MHI_EE_NOT_SUPPORTED] = "NOT SUPPORTED",
36a0f5a630SManivannan Sadhasivam };
37a0f5a630SManivannan Sadhasivam 
38a0f5a630SManivannan Sadhasivam const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX] = {
39a0f5a630SManivannan Sadhasivam 	[DEV_ST_TRANSITION_PBL] = "PBL",
40a0f5a630SManivannan Sadhasivam 	[DEV_ST_TRANSITION_READY] = "READY",
41a0f5a630SManivannan Sadhasivam 	[DEV_ST_TRANSITION_SBL] = "SBL",
42a0f5a630SManivannan Sadhasivam 	[DEV_ST_TRANSITION_MISSION_MODE] = "MISSION MODE",
43a0f5a630SManivannan Sadhasivam 	[DEV_ST_TRANSITION_FP] = "FLASH PROGRAMMER",
44a0f5a630SManivannan Sadhasivam 	[DEV_ST_TRANSITION_SYS_ERR] = "SYS ERROR",
45a0f5a630SManivannan Sadhasivam 	[DEV_ST_TRANSITION_DISABLE] = "DISABLE",
46a0f5a630SManivannan Sadhasivam };
47a0f5a630SManivannan Sadhasivam 
48a0f5a630SManivannan Sadhasivam const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = {
49a0f5a630SManivannan Sadhasivam 	[MHI_CH_STATE_TYPE_RESET] = "RESET",
50a0f5a630SManivannan Sadhasivam 	[MHI_CH_STATE_TYPE_STOP] = "STOP",
51a0f5a630SManivannan Sadhasivam 	[MHI_CH_STATE_TYPE_START] = "START",
52a0f5a630SManivannan Sadhasivam };
53a0f5a630SManivannan Sadhasivam 
54a0f5a630SManivannan Sadhasivam static const char * const mhi_pm_state_str[] = {
55a0f5a630SManivannan Sadhasivam 	[MHI_PM_STATE_DISABLE] = "DISABLE",
56a0f5a630SManivannan Sadhasivam 	[MHI_PM_STATE_POR] = "POWER ON RESET",
57a0f5a630SManivannan Sadhasivam 	[MHI_PM_STATE_M0] = "M0",
58a0f5a630SManivannan Sadhasivam 	[MHI_PM_STATE_M2] = "M2",
59a0f5a630SManivannan Sadhasivam 	[MHI_PM_STATE_M3_ENTER] = "M?->M3",
60a0f5a630SManivannan Sadhasivam 	[MHI_PM_STATE_M3] = "M3",
61a0f5a630SManivannan Sadhasivam 	[MHI_PM_STATE_M3_EXIT] = "M3->M0",
62a0f5a630SManivannan Sadhasivam 	[MHI_PM_STATE_FW_DL_ERR] = "Firmware Download Error",
63a0f5a630SManivannan Sadhasivam 	[MHI_PM_STATE_SYS_ERR_DETECT] = "SYS ERROR Detect",
64a0f5a630SManivannan Sadhasivam 	[MHI_PM_STATE_SYS_ERR_PROCESS] = "SYS ERROR Process",
65ac191bcbSJeffrey Hugo 	[MHI_PM_STATE_SYS_ERR_FAIL] = "SYS ERROR Failure",
66a0f5a630SManivannan Sadhasivam 	[MHI_PM_STATE_SHUTDOWN_PROCESS] = "SHUTDOWN Process",
67a0f5a630SManivannan Sadhasivam 	[MHI_PM_STATE_LD_ERR_FATAL_DETECT] = "Linkdown or Error Fatal Detect",
68a0f5a630SManivannan Sadhasivam };
69a0f5a630SManivannan Sadhasivam 
to_mhi_pm_state_str(u32 state)70a0f5a630SManivannan Sadhasivam const char *to_mhi_pm_state_str(u32 state)
71a0f5a630SManivannan Sadhasivam {
72a0f5a630SManivannan Sadhasivam 	int index;
73a0f5a630SManivannan Sadhasivam 
74a0f5a630SManivannan Sadhasivam 	if (state)
75a0f5a630SManivannan Sadhasivam 		index = __fls(state);
76a0f5a630SManivannan Sadhasivam 
77a0f5a630SManivannan Sadhasivam 	if (!state || index >= ARRAY_SIZE(mhi_pm_state_str))
78a0f5a630SManivannan Sadhasivam 		return "Invalid State";
79a0f5a630SManivannan Sadhasivam 
80a0f5a630SManivannan Sadhasivam 	return mhi_pm_state_str[index];
81a0f5a630SManivannan Sadhasivam }
82a0f5a630SManivannan Sadhasivam 
serial_number_show(struct device * dev,struct device_attribute * attr,char * buf)83a0f5a630SManivannan Sadhasivam static ssize_t serial_number_show(struct device *dev,
84a0f5a630SManivannan Sadhasivam 				  struct device_attribute *attr,
85a0f5a630SManivannan Sadhasivam 				  char *buf)
86a0f5a630SManivannan Sadhasivam {
87a0f5a630SManivannan Sadhasivam 	struct mhi_device *mhi_dev = to_mhi_device(dev);
88a0f5a630SManivannan Sadhasivam 	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
89a0f5a630SManivannan Sadhasivam 
90249369e9SWan Jiabing 	return sysfs_emit(buf, "Serial Number: %u\n",
91a0f5a630SManivannan Sadhasivam 			mhi_cntrl->serial_number);
92a0f5a630SManivannan Sadhasivam }
93a0f5a630SManivannan Sadhasivam static DEVICE_ATTR_RO(serial_number);
94a0f5a630SManivannan Sadhasivam 
oem_pk_hash_show(struct device * dev,struct device_attribute * attr,char * buf)95a0f5a630SManivannan Sadhasivam static ssize_t oem_pk_hash_show(struct device *dev,
96a0f5a630SManivannan Sadhasivam 				struct device_attribute *attr,
97a0f5a630SManivannan Sadhasivam 				char *buf)
98a0f5a630SManivannan Sadhasivam {
99a0f5a630SManivannan Sadhasivam 	struct mhi_device *mhi_dev = to_mhi_device(dev);
100a0f5a630SManivannan Sadhasivam 	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
101a0f5a630SManivannan Sadhasivam 	int i, cnt = 0;
102a0f5a630SManivannan Sadhasivam 
103a0f5a630SManivannan Sadhasivam 	for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++)
104249369e9SWan Jiabing 		cnt += sysfs_emit_at(buf, cnt, "OEMPKHASH[%d]: 0x%x\n",
105249369e9SWan Jiabing 				i, mhi_cntrl->oem_pk_hash[i]);
106a0f5a630SManivannan Sadhasivam 
107a0f5a630SManivannan Sadhasivam 	return cnt;
108a0f5a630SManivannan Sadhasivam }
109a0f5a630SManivannan Sadhasivam static DEVICE_ATTR_RO(oem_pk_hash);
110a0f5a630SManivannan Sadhasivam 
soc_reset_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)11195c33ae4SJeffrey Hugo static ssize_t soc_reset_store(struct device *dev,
11295c33ae4SJeffrey Hugo 			       struct device_attribute *attr,
11395c33ae4SJeffrey Hugo 			       const char *buf,
11495c33ae4SJeffrey Hugo 			       size_t count)
11595c33ae4SJeffrey Hugo {
11695c33ae4SJeffrey Hugo 	struct mhi_device *mhi_dev = to_mhi_device(dev);
11795c33ae4SJeffrey Hugo 	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
11895c33ae4SJeffrey Hugo 
11995c33ae4SJeffrey Hugo 	mhi_soc_reset(mhi_cntrl);
12095c33ae4SJeffrey Hugo 	return count;
12195c33ae4SJeffrey Hugo }
12295c33ae4SJeffrey Hugo static DEVICE_ATTR_WO(soc_reset);
12395c33ae4SJeffrey Hugo 
124a0f5a630SManivannan Sadhasivam static struct attribute *mhi_dev_attrs[] = {
125a0f5a630SManivannan Sadhasivam 	&dev_attr_serial_number.attr,
126a0f5a630SManivannan Sadhasivam 	&dev_attr_oem_pk_hash.attr,
12795c33ae4SJeffrey Hugo 	&dev_attr_soc_reset.attr,
128a0f5a630SManivannan Sadhasivam 	NULL,
129a0f5a630SManivannan Sadhasivam };
130a0f5a630SManivannan Sadhasivam ATTRIBUTE_GROUPS(mhi_dev);
131a0f5a630SManivannan Sadhasivam 
132a0f5a630SManivannan Sadhasivam /* MHI protocol requires the transfer ring to be aligned with ring length */
mhi_alloc_aligned_ring(struct mhi_controller * mhi_cntrl,struct mhi_ring * ring,u64 len)133a0f5a630SManivannan Sadhasivam static int mhi_alloc_aligned_ring(struct mhi_controller *mhi_cntrl,
134a0f5a630SManivannan Sadhasivam 				  struct mhi_ring *ring,
135a0f5a630SManivannan Sadhasivam 				  u64 len)
136a0f5a630SManivannan Sadhasivam {
137a0f5a630SManivannan Sadhasivam 	ring->alloc_size = len + (len - 1);
138a0f5a630SManivannan Sadhasivam 	ring->pre_aligned = dma_alloc_coherent(mhi_cntrl->cntrl_dev, ring->alloc_size,
139a0f5a630SManivannan Sadhasivam 					       &ring->dma_handle, GFP_KERNEL);
140a0f5a630SManivannan Sadhasivam 	if (!ring->pre_aligned)
141a0f5a630SManivannan Sadhasivam 		return -ENOMEM;
142a0f5a630SManivannan Sadhasivam 
143a0f5a630SManivannan Sadhasivam 	ring->iommu_base = (ring->dma_handle + (len - 1)) & ~(len - 1);
144a0f5a630SManivannan Sadhasivam 	ring->base = ring->pre_aligned + (ring->iommu_base - ring->dma_handle);
145a0f5a630SManivannan Sadhasivam 
146a0f5a630SManivannan Sadhasivam 	return 0;
147a0f5a630SManivannan Sadhasivam }
148a0f5a630SManivannan Sadhasivam 
mhi_deinit_free_irq(struct mhi_controller * mhi_cntrl)149a0f5a630SManivannan Sadhasivam void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl)
150a0f5a630SManivannan Sadhasivam {
151a0f5a630SManivannan Sadhasivam 	int i;
152a0f5a630SManivannan Sadhasivam 	struct mhi_event *mhi_event = mhi_cntrl->mhi_event;
153a0f5a630SManivannan Sadhasivam 
154a0f5a630SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) {
155a0f5a630SManivannan Sadhasivam 		if (mhi_event->offload_ev)
156a0f5a630SManivannan Sadhasivam 			continue;
157a0f5a630SManivannan Sadhasivam 
158a0f5a630SManivannan Sadhasivam 		free_irq(mhi_cntrl->irq[mhi_event->irq], mhi_event);
159a0f5a630SManivannan Sadhasivam 	}
160a0f5a630SManivannan Sadhasivam 
161a0f5a630SManivannan Sadhasivam 	free_irq(mhi_cntrl->irq[0], mhi_cntrl);
162a0f5a630SManivannan Sadhasivam }
163a0f5a630SManivannan Sadhasivam 
mhi_init_irq_setup(struct mhi_controller * mhi_cntrl)164a0f5a630SManivannan Sadhasivam int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl)
165a0f5a630SManivannan Sadhasivam {
166a0f5a630SManivannan Sadhasivam 	struct mhi_event *mhi_event = mhi_cntrl->mhi_event;
167a0f5a630SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
168a0f5a630SManivannan Sadhasivam 	unsigned long irq_flags = IRQF_SHARED | IRQF_NO_SUSPEND;
169a0f5a630SManivannan Sadhasivam 	int i, ret;
170a0f5a630SManivannan Sadhasivam 
171a0f5a630SManivannan Sadhasivam 	/* if controller driver has set irq_flags, use it */
172a0f5a630SManivannan Sadhasivam 	if (mhi_cntrl->irq_flags)
173a0f5a630SManivannan Sadhasivam 		irq_flags = mhi_cntrl->irq_flags;
174a0f5a630SManivannan Sadhasivam 
175a0f5a630SManivannan Sadhasivam 	/* Setup BHI_INTVEC IRQ */
176a0f5a630SManivannan Sadhasivam 	ret = request_threaded_irq(mhi_cntrl->irq[0], mhi_intvec_handler,
177a0f5a630SManivannan Sadhasivam 				   mhi_intvec_threaded_handler,
178a0f5a630SManivannan Sadhasivam 				   irq_flags,
179a0f5a630SManivannan Sadhasivam 				   "bhi", mhi_cntrl);
180a0f5a630SManivannan Sadhasivam 	if (ret)
181a0f5a630SManivannan Sadhasivam 		return ret;
1821227d2a2SQiang Yu 	/*
1831227d2a2SQiang Yu 	 * IRQs should be enabled during mhi_async_power_up(), so disable them explicitly here.
1841227d2a2SQiang Yu 	 * Due to the use of IRQF_SHARED flag as default while requesting IRQs, we assume that
1851227d2a2SQiang Yu 	 * IRQ_NOAUTOEN is not applicable.
1861227d2a2SQiang Yu 	 */
1871227d2a2SQiang Yu 	disable_irq(mhi_cntrl->irq[0]);
188a0f5a630SManivannan Sadhasivam 
189a0f5a630SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) {
190a0f5a630SManivannan Sadhasivam 		if (mhi_event->offload_ev)
191a0f5a630SManivannan Sadhasivam 			continue;
192a0f5a630SManivannan Sadhasivam 
193a0f5a630SManivannan Sadhasivam 		if (mhi_event->irq >= mhi_cntrl->nr_irqs) {
194a0f5a630SManivannan Sadhasivam 			dev_err(dev, "irq %d not available for event ring\n",
195a0f5a630SManivannan Sadhasivam 				mhi_event->irq);
196a0f5a630SManivannan Sadhasivam 			ret = -EINVAL;
197a0f5a630SManivannan Sadhasivam 			goto error_request;
198a0f5a630SManivannan Sadhasivam 		}
199a0f5a630SManivannan Sadhasivam 
200a0f5a630SManivannan Sadhasivam 		ret = request_irq(mhi_cntrl->irq[mhi_event->irq],
201a0f5a630SManivannan Sadhasivam 				  mhi_irq_handler,
202a0f5a630SManivannan Sadhasivam 				  irq_flags,
203a0f5a630SManivannan Sadhasivam 				  "mhi", mhi_event);
204a0f5a630SManivannan Sadhasivam 		if (ret) {
205a0f5a630SManivannan Sadhasivam 			dev_err(dev, "Error requesting irq:%d for ev:%d\n",
206a0f5a630SManivannan Sadhasivam 				mhi_cntrl->irq[mhi_event->irq], i);
207a0f5a630SManivannan Sadhasivam 			goto error_request;
208a0f5a630SManivannan Sadhasivam 		}
2091227d2a2SQiang Yu 
2101227d2a2SQiang Yu 		disable_irq(mhi_cntrl->irq[mhi_event->irq]);
211a0f5a630SManivannan Sadhasivam 	}
212a0f5a630SManivannan Sadhasivam 
213a0f5a630SManivannan Sadhasivam 	return 0;
214a0f5a630SManivannan Sadhasivam 
215a0f5a630SManivannan Sadhasivam error_request:
216a0f5a630SManivannan Sadhasivam 	for (--i, --mhi_event; i >= 0; i--, mhi_event--) {
217a0f5a630SManivannan Sadhasivam 		if (mhi_event->offload_ev)
218a0f5a630SManivannan Sadhasivam 			continue;
219a0f5a630SManivannan Sadhasivam 
220a0f5a630SManivannan Sadhasivam 		free_irq(mhi_cntrl->irq[mhi_event->irq], mhi_event);
221a0f5a630SManivannan Sadhasivam 	}
222a0f5a630SManivannan Sadhasivam 	free_irq(mhi_cntrl->irq[0], mhi_cntrl);
223a0f5a630SManivannan Sadhasivam 
224a0f5a630SManivannan Sadhasivam 	return ret;
225a0f5a630SManivannan Sadhasivam }
226a0f5a630SManivannan Sadhasivam 
mhi_deinit_dev_ctxt(struct mhi_controller * mhi_cntrl)227a0f5a630SManivannan Sadhasivam void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl)
228a0f5a630SManivannan Sadhasivam {
229a0f5a630SManivannan Sadhasivam 	int i;
230a0f5a630SManivannan Sadhasivam 	struct mhi_ctxt *mhi_ctxt = mhi_cntrl->mhi_ctxt;
231a0f5a630SManivannan Sadhasivam 	struct mhi_cmd *mhi_cmd;
232a0f5a630SManivannan Sadhasivam 	struct mhi_event *mhi_event;
233a0f5a630SManivannan Sadhasivam 	struct mhi_ring *ring;
234a0f5a630SManivannan Sadhasivam 
235a0f5a630SManivannan Sadhasivam 	mhi_cmd = mhi_cntrl->mhi_cmd;
236a0f5a630SManivannan Sadhasivam 	for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++) {
237a0f5a630SManivannan Sadhasivam 		ring = &mhi_cmd->ring;
238a0f5a630SManivannan Sadhasivam 		dma_free_coherent(mhi_cntrl->cntrl_dev, ring->alloc_size,
239a0f5a630SManivannan Sadhasivam 				  ring->pre_aligned, ring->dma_handle);
240a0f5a630SManivannan Sadhasivam 		ring->base = NULL;
241a0f5a630SManivannan Sadhasivam 		ring->iommu_base = 0;
242a0f5a630SManivannan Sadhasivam 	}
243a0f5a630SManivannan Sadhasivam 
244a0f5a630SManivannan Sadhasivam 	dma_free_coherent(mhi_cntrl->cntrl_dev,
245a0f5a630SManivannan Sadhasivam 			  sizeof(*mhi_ctxt->cmd_ctxt) * NR_OF_CMD_RINGS,
246a0f5a630SManivannan Sadhasivam 			  mhi_ctxt->cmd_ctxt, mhi_ctxt->cmd_ctxt_addr);
247a0f5a630SManivannan Sadhasivam 
248a0f5a630SManivannan Sadhasivam 	mhi_event = mhi_cntrl->mhi_event;
249a0f5a630SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) {
250a0f5a630SManivannan Sadhasivam 		if (mhi_event->offload_ev)
251a0f5a630SManivannan Sadhasivam 			continue;
252a0f5a630SManivannan Sadhasivam 
253a0f5a630SManivannan Sadhasivam 		ring = &mhi_event->ring;
254a0f5a630SManivannan Sadhasivam 		dma_free_coherent(mhi_cntrl->cntrl_dev, ring->alloc_size,
255a0f5a630SManivannan Sadhasivam 				  ring->pre_aligned, ring->dma_handle);
256a0f5a630SManivannan Sadhasivam 		ring->base = NULL;
257a0f5a630SManivannan Sadhasivam 		ring->iommu_base = 0;
258a0f5a630SManivannan Sadhasivam 	}
259a0f5a630SManivannan Sadhasivam 
260a0f5a630SManivannan Sadhasivam 	dma_free_coherent(mhi_cntrl->cntrl_dev, sizeof(*mhi_ctxt->er_ctxt) *
261a0f5a630SManivannan Sadhasivam 			  mhi_cntrl->total_ev_rings, mhi_ctxt->er_ctxt,
262a0f5a630SManivannan Sadhasivam 			  mhi_ctxt->er_ctxt_addr);
263a0f5a630SManivannan Sadhasivam 
264a0f5a630SManivannan Sadhasivam 	dma_free_coherent(mhi_cntrl->cntrl_dev, sizeof(*mhi_ctxt->chan_ctxt) *
265a0f5a630SManivannan Sadhasivam 			  mhi_cntrl->max_chan, mhi_ctxt->chan_ctxt,
266a0f5a630SManivannan Sadhasivam 			  mhi_ctxt->chan_ctxt_addr);
267a0f5a630SManivannan Sadhasivam 
268a0f5a630SManivannan Sadhasivam 	kfree(mhi_ctxt);
269a0f5a630SManivannan Sadhasivam 	mhi_cntrl->mhi_ctxt = NULL;
270a0f5a630SManivannan Sadhasivam }
271a0f5a630SManivannan Sadhasivam 
mhi_init_dev_ctxt(struct mhi_controller * mhi_cntrl)272a0f5a630SManivannan Sadhasivam int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl)
273a0f5a630SManivannan Sadhasivam {
274a0f5a630SManivannan Sadhasivam 	struct mhi_ctxt *mhi_ctxt;
275a0f5a630SManivannan Sadhasivam 	struct mhi_chan_ctxt *chan_ctxt;
276a0f5a630SManivannan Sadhasivam 	struct mhi_event_ctxt *er_ctxt;
277a0f5a630SManivannan Sadhasivam 	struct mhi_cmd_ctxt *cmd_ctxt;
278a0f5a630SManivannan Sadhasivam 	struct mhi_chan *mhi_chan;
279a0f5a630SManivannan Sadhasivam 	struct mhi_event *mhi_event;
280a0f5a630SManivannan Sadhasivam 	struct mhi_cmd *mhi_cmd;
281a0f5a630SManivannan Sadhasivam 	u32 tmp;
282a0f5a630SManivannan Sadhasivam 	int ret = -ENOMEM, i;
283a0f5a630SManivannan Sadhasivam 
284a0f5a630SManivannan Sadhasivam 	atomic_set(&mhi_cntrl->dev_wake, 0);
285a0f5a630SManivannan Sadhasivam 	atomic_set(&mhi_cntrl->pending_pkts, 0);
286a0f5a630SManivannan Sadhasivam 
287a0f5a630SManivannan Sadhasivam 	mhi_ctxt = kzalloc(sizeof(*mhi_ctxt), GFP_KERNEL);
288a0f5a630SManivannan Sadhasivam 	if (!mhi_ctxt)
289a0f5a630SManivannan Sadhasivam 		return -ENOMEM;
290a0f5a630SManivannan Sadhasivam 
291a0f5a630SManivannan Sadhasivam 	/* Setup channel ctxt */
292a0f5a630SManivannan Sadhasivam 	mhi_ctxt->chan_ctxt = dma_alloc_coherent(mhi_cntrl->cntrl_dev,
293a0f5a630SManivannan Sadhasivam 						 sizeof(*mhi_ctxt->chan_ctxt) *
294a0f5a630SManivannan Sadhasivam 						 mhi_cntrl->max_chan,
295a0f5a630SManivannan Sadhasivam 						 &mhi_ctxt->chan_ctxt_addr,
296a0f5a630SManivannan Sadhasivam 						 GFP_KERNEL);
297a0f5a630SManivannan Sadhasivam 	if (!mhi_ctxt->chan_ctxt)
298a0f5a630SManivannan Sadhasivam 		goto error_alloc_chan_ctxt;
299a0f5a630SManivannan Sadhasivam 
300a0f5a630SManivannan Sadhasivam 	mhi_chan = mhi_cntrl->mhi_chan;
301a0f5a630SManivannan Sadhasivam 	chan_ctxt = mhi_ctxt->chan_ctxt;
302a0f5a630SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->max_chan; i++, chan_ctxt++, mhi_chan++) {
303a0f5a630SManivannan Sadhasivam 		/* Skip if it is an offload channel */
304a0f5a630SManivannan Sadhasivam 		if (mhi_chan->offload_ch)
305a0f5a630SManivannan Sadhasivam 			continue;
306a0f5a630SManivannan Sadhasivam 
307a0f5a630SManivannan Sadhasivam 		tmp = le32_to_cpu(chan_ctxt->chcfg);
308a0f5a630SManivannan Sadhasivam 		tmp &= ~CHAN_CTX_CHSTATE_MASK;
309d28cab4dSManivannan Sadhasivam 		tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_DISABLED);
310a0f5a630SManivannan Sadhasivam 		tmp &= ~CHAN_CTX_BRSTMODE_MASK;
311d28cab4dSManivannan Sadhasivam 		tmp |= FIELD_PREP(CHAN_CTX_BRSTMODE_MASK, mhi_chan->db_cfg.brstmode);
312a0f5a630SManivannan Sadhasivam 		tmp &= ~CHAN_CTX_POLLCFG_MASK;
313d28cab4dSManivannan Sadhasivam 		tmp |= FIELD_PREP(CHAN_CTX_POLLCFG_MASK, mhi_chan->db_cfg.pollcfg);
314a0f5a630SManivannan Sadhasivam 		chan_ctxt->chcfg = cpu_to_le32(tmp);
315a0f5a630SManivannan Sadhasivam 
316a0f5a630SManivannan Sadhasivam 		chan_ctxt->chtype = cpu_to_le32(mhi_chan->type);
317a0f5a630SManivannan Sadhasivam 		chan_ctxt->erindex = cpu_to_le32(mhi_chan->er_index);
318a0f5a630SManivannan Sadhasivam 
319a0f5a630SManivannan Sadhasivam 		mhi_chan->ch_state = MHI_CH_STATE_DISABLED;
320a0f5a630SManivannan Sadhasivam 		mhi_chan->tre_ring.db_addr = (void __iomem *)&chan_ctxt->wp;
321a0f5a630SManivannan Sadhasivam 	}
322a0f5a630SManivannan Sadhasivam 
323a0f5a630SManivannan Sadhasivam 	/* Setup event context */
324a0f5a630SManivannan Sadhasivam 	mhi_ctxt->er_ctxt = dma_alloc_coherent(mhi_cntrl->cntrl_dev,
325a0f5a630SManivannan Sadhasivam 					       sizeof(*mhi_ctxt->er_ctxt) *
326a0f5a630SManivannan Sadhasivam 					       mhi_cntrl->total_ev_rings,
327a0f5a630SManivannan Sadhasivam 					       &mhi_ctxt->er_ctxt_addr,
328a0f5a630SManivannan Sadhasivam 					       GFP_KERNEL);
329a0f5a630SManivannan Sadhasivam 	if (!mhi_ctxt->er_ctxt)
330a0f5a630SManivannan Sadhasivam 		goto error_alloc_er_ctxt;
331a0f5a630SManivannan Sadhasivam 
332a0f5a630SManivannan Sadhasivam 	er_ctxt = mhi_ctxt->er_ctxt;
333a0f5a630SManivannan Sadhasivam 	mhi_event = mhi_cntrl->mhi_event;
334a0f5a630SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->total_ev_rings; i++, er_ctxt++,
335a0f5a630SManivannan Sadhasivam 		     mhi_event++) {
336a0f5a630SManivannan Sadhasivam 		struct mhi_ring *ring = &mhi_event->ring;
337a0f5a630SManivannan Sadhasivam 
338a0f5a630SManivannan Sadhasivam 		/* Skip if it is an offload event */
339a0f5a630SManivannan Sadhasivam 		if (mhi_event->offload_ev)
340a0f5a630SManivannan Sadhasivam 			continue;
341a0f5a630SManivannan Sadhasivam 
342a0f5a630SManivannan Sadhasivam 		tmp = le32_to_cpu(er_ctxt->intmod);
343a0f5a630SManivannan Sadhasivam 		tmp &= ~EV_CTX_INTMODC_MASK;
344a0f5a630SManivannan Sadhasivam 		tmp &= ~EV_CTX_INTMODT_MASK;
345d28cab4dSManivannan Sadhasivam 		tmp |= FIELD_PREP(EV_CTX_INTMODT_MASK, mhi_event->intmod);
346a0f5a630SManivannan Sadhasivam 		er_ctxt->intmod = cpu_to_le32(tmp);
347a0f5a630SManivannan Sadhasivam 
348a0f5a630SManivannan Sadhasivam 		er_ctxt->ertype = cpu_to_le32(MHI_ER_TYPE_VALID);
349a0f5a630SManivannan Sadhasivam 		er_ctxt->msivec = cpu_to_le32(mhi_event->irq);
350a0f5a630SManivannan Sadhasivam 		mhi_event->db_cfg.db_mode = true;
351a0f5a630SManivannan Sadhasivam 
35284f5f31fSManivannan Sadhasivam 		ring->el_size = sizeof(struct mhi_ring_element);
353a0f5a630SManivannan Sadhasivam 		ring->len = ring->el_size * ring->elements;
354a0f5a630SManivannan Sadhasivam 		ret = mhi_alloc_aligned_ring(mhi_cntrl, ring, ring->len);
355a0f5a630SManivannan Sadhasivam 		if (ret)
356a0f5a630SManivannan Sadhasivam 			goto error_alloc_er;
357a0f5a630SManivannan Sadhasivam 
358a0f5a630SManivannan Sadhasivam 		/*
359a0f5a630SManivannan Sadhasivam 		 * If the read pointer equals to the write pointer, then the
360a0f5a630SManivannan Sadhasivam 		 * ring is empty
361a0f5a630SManivannan Sadhasivam 		 */
362a0f5a630SManivannan Sadhasivam 		ring->rp = ring->wp = ring->base;
363a0f5a630SManivannan Sadhasivam 		er_ctxt->rbase = cpu_to_le64(ring->iommu_base);
364a0f5a630SManivannan Sadhasivam 		er_ctxt->rp = er_ctxt->wp = er_ctxt->rbase;
365a0f5a630SManivannan Sadhasivam 		er_ctxt->rlen = cpu_to_le64(ring->len);
366a0f5a630SManivannan Sadhasivam 		ring->ctxt_wp = &er_ctxt->wp;
367a0f5a630SManivannan Sadhasivam 	}
368a0f5a630SManivannan Sadhasivam 
369a0f5a630SManivannan Sadhasivam 	/* Setup cmd context */
370a0f5a630SManivannan Sadhasivam 	ret = -ENOMEM;
371a0f5a630SManivannan Sadhasivam 	mhi_ctxt->cmd_ctxt = dma_alloc_coherent(mhi_cntrl->cntrl_dev,
372a0f5a630SManivannan Sadhasivam 						sizeof(*mhi_ctxt->cmd_ctxt) *
373a0f5a630SManivannan Sadhasivam 						NR_OF_CMD_RINGS,
374a0f5a630SManivannan Sadhasivam 						&mhi_ctxt->cmd_ctxt_addr,
375a0f5a630SManivannan Sadhasivam 						GFP_KERNEL);
376a0f5a630SManivannan Sadhasivam 	if (!mhi_ctxt->cmd_ctxt)
377a0f5a630SManivannan Sadhasivam 		goto error_alloc_er;
378a0f5a630SManivannan Sadhasivam 
379a0f5a630SManivannan Sadhasivam 	mhi_cmd = mhi_cntrl->mhi_cmd;
380a0f5a630SManivannan Sadhasivam 	cmd_ctxt = mhi_ctxt->cmd_ctxt;
381a0f5a630SManivannan Sadhasivam 	for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++, cmd_ctxt++) {
382a0f5a630SManivannan Sadhasivam 		struct mhi_ring *ring = &mhi_cmd->ring;
383a0f5a630SManivannan Sadhasivam 
38484f5f31fSManivannan Sadhasivam 		ring->el_size = sizeof(struct mhi_ring_element);
385a0f5a630SManivannan Sadhasivam 		ring->elements = CMD_EL_PER_RING;
386a0f5a630SManivannan Sadhasivam 		ring->len = ring->el_size * ring->elements;
387a0f5a630SManivannan Sadhasivam 		ret = mhi_alloc_aligned_ring(mhi_cntrl, ring, ring->len);
388a0f5a630SManivannan Sadhasivam 		if (ret)
389a0f5a630SManivannan Sadhasivam 			goto error_alloc_cmd;
390a0f5a630SManivannan Sadhasivam 
391a0f5a630SManivannan Sadhasivam 		ring->rp = ring->wp = ring->base;
392a0f5a630SManivannan Sadhasivam 		cmd_ctxt->rbase = cpu_to_le64(ring->iommu_base);
393a0f5a630SManivannan Sadhasivam 		cmd_ctxt->rp = cmd_ctxt->wp = cmd_ctxt->rbase;
394a0f5a630SManivannan Sadhasivam 		cmd_ctxt->rlen = cpu_to_le64(ring->len);
395a0f5a630SManivannan Sadhasivam 		ring->ctxt_wp = &cmd_ctxt->wp;
396a0f5a630SManivannan Sadhasivam 	}
397a0f5a630SManivannan Sadhasivam 
398a0f5a630SManivannan Sadhasivam 	mhi_cntrl->mhi_ctxt = mhi_ctxt;
399a0f5a630SManivannan Sadhasivam 
400a0f5a630SManivannan Sadhasivam 	return 0;
401a0f5a630SManivannan Sadhasivam 
402a0f5a630SManivannan Sadhasivam error_alloc_cmd:
403a0f5a630SManivannan Sadhasivam 	for (--i, --mhi_cmd; i >= 0; i--, mhi_cmd--) {
404a0f5a630SManivannan Sadhasivam 		struct mhi_ring *ring = &mhi_cmd->ring;
405a0f5a630SManivannan Sadhasivam 
406a0f5a630SManivannan Sadhasivam 		dma_free_coherent(mhi_cntrl->cntrl_dev, ring->alloc_size,
407a0f5a630SManivannan Sadhasivam 				  ring->pre_aligned, ring->dma_handle);
408a0f5a630SManivannan Sadhasivam 	}
409a0f5a630SManivannan Sadhasivam 	dma_free_coherent(mhi_cntrl->cntrl_dev,
410a0f5a630SManivannan Sadhasivam 			  sizeof(*mhi_ctxt->cmd_ctxt) * NR_OF_CMD_RINGS,
411a0f5a630SManivannan Sadhasivam 			  mhi_ctxt->cmd_ctxt, mhi_ctxt->cmd_ctxt_addr);
412a0f5a630SManivannan Sadhasivam 	i = mhi_cntrl->total_ev_rings;
413a0f5a630SManivannan Sadhasivam 	mhi_event = mhi_cntrl->mhi_event + i;
414a0f5a630SManivannan Sadhasivam 
415a0f5a630SManivannan Sadhasivam error_alloc_er:
416a0f5a630SManivannan Sadhasivam 	for (--i, --mhi_event; i >= 0; i--, mhi_event--) {
417a0f5a630SManivannan Sadhasivam 		struct mhi_ring *ring = &mhi_event->ring;
418a0f5a630SManivannan Sadhasivam 
419a0f5a630SManivannan Sadhasivam 		if (mhi_event->offload_ev)
420a0f5a630SManivannan Sadhasivam 			continue;
421a0f5a630SManivannan Sadhasivam 
422a0f5a630SManivannan Sadhasivam 		dma_free_coherent(mhi_cntrl->cntrl_dev, ring->alloc_size,
423a0f5a630SManivannan Sadhasivam 				  ring->pre_aligned, ring->dma_handle);
424a0f5a630SManivannan Sadhasivam 	}
425a0f5a630SManivannan Sadhasivam 	dma_free_coherent(mhi_cntrl->cntrl_dev, sizeof(*mhi_ctxt->er_ctxt) *
426a0f5a630SManivannan Sadhasivam 			  mhi_cntrl->total_ev_rings, mhi_ctxt->er_ctxt,
427a0f5a630SManivannan Sadhasivam 			  mhi_ctxt->er_ctxt_addr);
428a0f5a630SManivannan Sadhasivam 
429a0f5a630SManivannan Sadhasivam error_alloc_er_ctxt:
430a0f5a630SManivannan Sadhasivam 	dma_free_coherent(mhi_cntrl->cntrl_dev, sizeof(*mhi_ctxt->chan_ctxt) *
431a0f5a630SManivannan Sadhasivam 			  mhi_cntrl->max_chan, mhi_ctxt->chan_ctxt,
432a0f5a630SManivannan Sadhasivam 			  mhi_ctxt->chan_ctxt_addr);
433a0f5a630SManivannan Sadhasivam 
434a0f5a630SManivannan Sadhasivam error_alloc_chan_ctxt:
435a0f5a630SManivannan Sadhasivam 	kfree(mhi_ctxt);
436a0f5a630SManivannan Sadhasivam 
437a0f5a630SManivannan Sadhasivam 	return ret;
438a0f5a630SManivannan Sadhasivam }
439a0f5a630SManivannan Sadhasivam 
mhi_init_mmio(struct mhi_controller * mhi_cntrl)440a0f5a630SManivannan Sadhasivam int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
441a0f5a630SManivannan Sadhasivam {
442a0f5a630SManivannan Sadhasivam 	u32 val;
443a0f5a630SManivannan Sadhasivam 	int i, ret;
444a0f5a630SManivannan Sadhasivam 	struct mhi_chan *mhi_chan;
445a0f5a630SManivannan Sadhasivam 	struct mhi_event *mhi_event;
446a0f5a630SManivannan Sadhasivam 	void __iomem *base = mhi_cntrl->regs;
447a0f5a630SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
448a0f5a630SManivannan Sadhasivam 	struct {
449a0f5a630SManivannan Sadhasivam 		u32 offset;
450a0f5a630SManivannan Sadhasivam 		u32 val;
451a0f5a630SManivannan Sadhasivam 	} reg_info[] = {
452a0f5a630SManivannan Sadhasivam 		{
453d126bfeaSBhaumik Bhatt 			CCABAP_HIGHER,
454a0f5a630SManivannan Sadhasivam 			upper_32_bits(mhi_cntrl->mhi_ctxt->chan_ctxt_addr),
455a0f5a630SManivannan Sadhasivam 		},
456a0f5a630SManivannan Sadhasivam 		{
457d126bfeaSBhaumik Bhatt 			CCABAP_LOWER,
458a0f5a630SManivannan Sadhasivam 			lower_32_bits(mhi_cntrl->mhi_ctxt->chan_ctxt_addr),
459a0f5a630SManivannan Sadhasivam 		},
460a0f5a630SManivannan Sadhasivam 		{
461d126bfeaSBhaumik Bhatt 			ECABAP_HIGHER,
462a0f5a630SManivannan Sadhasivam 			upper_32_bits(mhi_cntrl->mhi_ctxt->er_ctxt_addr),
463a0f5a630SManivannan Sadhasivam 		},
464a0f5a630SManivannan Sadhasivam 		{
465d126bfeaSBhaumik Bhatt 			ECABAP_LOWER,
466a0f5a630SManivannan Sadhasivam 			lower_32_bits(mhi_cntrl->mhi_ctxt->er_ctxt_addr),
467a0f5a630SManivannan Sadhasivam 		},
468a0f5a630SManivannan Sadhasivam 		{
469d126bfeaSBhaumik Bhatt 			CRCBAP_HIGHER,
470a0f5a630SManivannan Sadhasivam 			upper_32_bits(mhi_cntrl->mhi_ctxt->cmd_ctxt_addr),
471a0f5a630SManivannan Sadhasivam 		},
472a0f5a630SManivannan Sadhasivam 		{
473d126bfeaSBhaumik Bhatt 			CRCBAP_LOWER,
474a0f5a630SManivannan Sadhasivam 			lower_32_bits(mhi_cntrl->mhi_ctxt->cmd_ctxt_addr),
475a0f5a630SManivannan Sadhasivam 		},
476a0f5a630SManivannan Sadhasivam 		{
477d126bfeaSBhaumik Bhatt 			MHICTRLBASE_HIGHER,
478a0f5a630SManivannan Sadhasivam 			upper_32_bits(mhi_cntrl->iova_start),
479a0f5a630SManivannan Sadhasivam 		},
480a0f5a630SManivannan Sadhasivam 		{
481d126bfeaSBhaumik Bhatt 			MHICTRLBASE_LOWER,
482a0f5a630SManivannan Sadhasivam 			lower_32_bits(mhi_cntrl->iova_start),
483a0f5a630SManivannan Sadhasivam 		},
484a0f5a630SManivannan Sadhasivam 		{
485d126bfeaSBhaumik Bhatt 			MHIDATABASE_HIGHER,
486a0f5a630SManivannan Sadhasivam 			upper_32_bits(mhi_cntrl->iova_start),
487a0f5a630SManivannan Sadhasivam 		},
488a0f5a630SManivannan Sadhasivam 		{
489d126bfeaSBhaumik Bhatt 			MHIDATABASE_LOWER,
490a0f5a630SManivannan Sadhasivam 			lower_32_bits(mhi_cntrl->iova_start),
491a0f5a630SManivannan Sadhasivam 		},
492a0f5a630SManivannan Sadhasivam 		{
493d126bfeaSBhaumik Bhatt 			MHICTRLLIMIT_HIGHER,
494a0f5a630SManivannan Sadhasivam 			upper_32_bits(mhi_cntrl->iova_stop),
495a0f5a630SManivannan Sadhasivam 		},
496a0f5a630SManivannan Sadhasivam 		{
497d126bfeaSBhaumik Bhatt 			MHICTRLLIMIT_LOWER,
498a0f5a630SManivannan Sadhasivam 			lower_32_bits(mhi_cntrl->iova_stop),
499a0f5a630SManivannan Sadhasivam 		},
500a0f5a630SManivannan Sadhasivam 		{
501d126bfeaSBhaumik Bhatt 			MHIDATALIMIT_HIGHER,
502a0f5a630SManivannan Sadhasivam 			upper_32_bits(mhi_cntrl->iova_stop),
503a0f5a630SManivannan Sadhasivam 		},
504a0f5a630SManivannan Sadhasivam 		{
505d126bfeaSBhaumik Bhatt 			MHIDATALIMIT_LOWER,
506a0f5a630SManivannan Sadhasivam 			lower_32_bits(mhi_cntrl->iova_stop),
507a0f5a630SManivannan Sadhasivam 		},
508d126bfeaSBhaumik Bhatt 		{0, 0}
509a0f5a630SManivannan Sadhasivam 	};
510a0f5a630SManivannan Sadhasivam 
511a0f5a630SManivannan Sadhasivam 	dev_dbg(dev, "Initializing MHI registers\n");
512a0f5a630SManivannan Sadhasivam 
513a0f5a630SManivannan Sadhasivam 	/* Read channel db offset */
514d28cab4dSManivannan Sadhasivam 	ret = mhi_read_reg(mhi_cntrl, base, CHDBOFF, &val);
515a0f5a630SManivannan Sadhasivam 	if (ret) {
516a0f5a630SManivannan Sadhasivam 		dev_err(dev, "Unable to read CHDBOFF register\n");
517a0f5a630SManivannan Sadhasivam 		return -EIO;
518a0f5a630SManivannan Sadhasivam 	}
519a0f5a630SManivannan Sadhasivam 
5206a0c637bSJeffrey Hugo 	if (val >= mhi_cntrl->reg_len - (8 * MHI_DEV_WAKE_DB)) {
5216a0c637bSJeffrey Hugo 		dev_err(dev, "CHDB offset: 0x%x is out of range: 0x%zx\n",
5226a0c637bSJeffrey Hugo 			val, mhi_cntrl->reg_len - (8 * MHI_DEV_WAKE_DB));
5236a0c637bSJeffrey Hugo 		return -ERANGE;
5246a0c637bSJeffrey Hugo 	}
5256a0c637bSJeffrey Hugo 
526a0f5a630SManivannan Sadhasivam 	/* Setup wake db */
527a0f5a630SManivannan Sadhasivam 	mhi_cntrl->wake_db = base + val + (8 * MHI_DEV_WAKE_DB);
528a0f5a630SManivannan Sadhasivam 	mhi_cntrl->wake_set = false;
529a0f5a630SManivannan Sadhasivam 
530a0f5a630SManivannan Sadhasivam 	/* Setup channel db address for each channel in tre_ring */
531a0f5a630SManivannan Sadhasivam 	mhi_chan = mhi_cntrl->mhi_chan;
532a0f5a630SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->max_chan; i++, val += 8, mhi_chan++)
533a0f5a630SManivannan Sadhasivam 		mhi_chan->tre_ring.db_addr = base + val;
534a0f5a630SManivannan Sadhasivam 
535a0f5a630SManivannan Sadhasivam 	/* Read event ring db offset */
536d28cab4dSManivannan Sadhasivam 	ret = mhi_read_reg(mhi_cntrl, base, ERDBOFF, &val);
537a0f5a630SManivannan Sadhasivam 	if (ret) {
538a0f5a630SManivannan Sadhasivam 		dev_err(dev, "Unable to read ERDBOFF register\n");
539a0f5a630SManivannan Sadhasivam 		return -EIO;
540a0f5a630SManivannan Sadhasivam 	}
541a0f5a630SManivannan Sadhasivam 
5426a0c637bSJeffrey Hugo 	if (val >= mhi_cntrl->reg_len - (8 * mhi_cntrl->total_ev_rings)) {
5436a0c637bSJeffrey Hugo 		dev_err(dev, "ERDB offset: 0x%x is out of range: 0x%zx\n",
5446a0c637bSJeffrey Hugo 			val, mhi_cntrl->reg_len - (8 * mhi_cntrl->total_ev_rings));
5456a0c637bSJeffrey Hugo 		return -ERANGE;
5466a0c637bSJeffrey Hugo 	}
5476a0c637bSJeffrey Hugo 
548a0f5a630SManivannan Sadhasivam 	/* Setup event db address for each ev_ring */
549a0f5a630SManivannan Sadhasivam 	mhi_event = mhi_cntrl->mhi_event;
550a0f5a630SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->total_ev_rings; i++, val += 8, mhi_event++) {
551a0f5a630SManivannan Sadhasivam 		if (mhi_event->offload_ev)
552a0f5a630SManivannan Sadhasivam 			continue;
553a0f5a630SManivannan Sadhasivam 
554a0f5a630SManivannan Sadhasivam 		mhi_event->ring.db_addr = base + val;
555a0f5a630SManivannan Sadhasivam 	}
556a0f5a630SManivannan Sadhasivam 
557a0f5a630SManivannan Sadhasivam 	/* Setup DB register for primary CMD rings */
558a0f5a630SManivannan Sadhasivam 	mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING].ring.db_addr = base + CRDB_LOWER;
559a0f5a630SManivannan Sadhasivam 
560a0f5a630SManivannan Sadhasivam 	/* Write to MMIO registers */
561a0f5a630SManivannan Sadhasivam 	for (i = 0; reg_info[i].offset; i++)
562d126bfeaSBhaumik Bhatt 		mhi_write_reg(mhi_cntrl, base, reg_info[i].offset,
563d126bfeaSBhaumik Bhatt 			      reg_info[i].val);
564d126bfeaSBhaumik Bhatt 
565d126bfeaSBhaumik Bhatt 	ret = mhi_write_reg_field(mhi_cntrl, base, MHICFG, MHICFG_NER_MASK,
566d126bfeaSBhaumik Bhatt 				  mhi_cntrl->total_ev_rings);
5670bca889fSBhaumik Bhatt 	if (ret) {
568d126bfeaSBhaumik Bhatt 		dev_err(dev, "Unable to write MHICFG register\n");
5690bca889fSBhaumik Bhatt 		return ret;
5700bca889fSBhaumik Bhatt 	}
571d126bfeaSBhaumik Bhatt 
572d126bfeaSBhaumik Bhatt 	ret = mhi_write_reg_field(mhi_cntrl, base, MHICFG, MHICFG_NHWER_MASK,
573d126bfeaSBhaumik Bhatt 				  mhi_cntrl->hw_ev_rings);
574d126bfeaSBhaumik Bhatt 	if (ret) {
575d126bfeaSBhaumik Bhatt 		dev_err(dev, "Unable to write MHICFG register\n");
576d126bfeaSBhaumik Bhatt 		return ret;
5770bca889fSBhaumik Bhatt 	}
578a0f5a630SManivannan Sadhasivam 
579a0f5a630SManivannan Sadhasivam 	return 0;
580a0f5a630SManivannan Sadhasivam }
581a0f5a630SManivannan Sadhasivam 
mhi_deinit_chan_ctxt(struct mhi_controller * mhi_cntrl,struct mhi_chan * mhi_chan)582a0f5a630SManivannan Sadhasivam void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl,
583a0f5a630SManivannan Sadhasivam 			  struct mhi_chan *mhi_chan)
584a0f5a630SManivannan Sadhasivam {
585a0f5a630SManivannan Sadhasivam 	struct mhi_ring *buf_ring;
586a0f5a630SManivannan Sadhasivam 	struct mhi_ring *tre_ring;
587a0f5a630SManivannan Sadhasivam 	struct mhi_chan_ctxt *chan_ctxt;
588a0f5a630SManivannan Sadhasivam 	u32 tmp;
589a0f5a630SManivannan Sadhasivam 
590a0f5a630SManivannan Sadhasivam 	buf_ring = &mhi_chan->buf_ring;
591a0f5a630SManivannan Sadhasivam 	tre_ring = &mhi_chan->tre_ring;
592a0f5a630SManivannan Sadhasivam 	chan_ctxt = &mhi_cntrl->mhi_ctxt->chan_ctxt[mhi_chan->chan];
593a0f5a630SManivannan Sadhasivam 
594a0f5a630SManivannan Sadhasivam 	if (!chan_ctxt->rbase) /* Already uninitialized */
595a0f5a630SManivannan Sadhasivam 		return;
596a0f5a630SManivannan Sadhasivam 
597a0f5a630SManivannan Sadhasivam 	dma_free_coherent(mhi_cntrl->cntrl_dev, tre_ring->alloc_size,
598a0f5a630SManivannan Sadhasivam 			  tre_ring->pre_aligned, tre_ring->dma_handle);
599a0f5a630SManivannan Sadhasivam 	vfree(buf_ring->base);
600a0f5a630SManivannan Sadhasivam 
601a0f5a630SManivannan Sadhasivam 	buf_ring->base = tre_ring->base = NULL;
602a0f5a630SManivannan Sadhasivam 	tre_ring->ctxt_wp = NULL;
603a0f5a630SManivannan Sadhasivam 	chan_ctxt->rbase = 0;
604a0f5a630SManivannan Sadhasivam 	chan_ctxt->rlen = 0;
605a0f5a630SManivannan Sadhasivam 	chan_ctxt->rp = 0;
606a0f5a630SManivannan Sadhasivam 	chan_ctxt->wp = 0;
607a0f5a630SManivannan Sadhasivam 
608a0f5a630SManivannan Sadhasivam 	tmp = le32_to_cpu(chan_ctxt->chcfg);
609a0f5a630SManivannan Sadhasivam 	tmp &= ~CHAN_CTX_CHSTATE_MASK;
610d28cab4dSManivannan Sadhasivam 	tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_DISABLED);
611a0f5a630SManivannan Sadhasivam 	chan_ctxt->chcfg = cpu_to_le32(tmp);
612a0f5a630SManivannan Sadhasivam 
613a0f5a630SManivannan Sadhasivam 	/* Update to all cores */
614a0f5a630SManivannan Sadhasivam 	smp_wmb();
615a0f5a630SManivannan Sadhasivam }
616a0f5a630SManivannan Sadhasivam 
mhi_init_chan_ctxt(struct mhi_controller * mhi_cntrl,struct mhi_chan * mhi_chan)617a0f5a630SManivannan Sadhasivam int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
618a0f5a630SManivannan Sadhasivam 		       struct mhi_chan *mhi_chan)
619a0f5a630SManivannan Sadhasivam {
620a0f5a630SManivannan Sadhasivam 	struct mhi_ring *buf_ring;
621a0f5a630SManivannan Sadhasivam 	struct mhi_ring *tre_ring;
622a0f5a630SManivannan Sadhasivam 	struct mhi_chan_ctxt *chan_ctxt;
623a0f5a630SManivannan Sadhasivam 	u32 tmp;
624a0f5a630SManivannan Sadhasivam 	int ret;
625a0f5a630SManivannan Sadhasivam 
626a0f5a630SManivannan Sadhasivam 	buf_ring = &mhi_chan->buf_ring;
627a0f5a630SManivannan Sadhasivam 	tre_ring = &mhi_chan->tre_ring;
62884f5f31fSManivannan Sadhasivam 	tre_ring->el_size = sizeof(struct mhi_ring_element);
629a0f5a630SManivannan Sadhasivam 	tre_ring->len = tre_ring->el_size * tre_ring->elements;
630a0f5a630SManivannan Sadhasivam 	chan_ctxt = &mhi_cntrl->mhi_ctxt->chan_ctxt[mhi_chan->chan];
631a0f5a630SManivannan Sadhasivam 	ret = mhi_alloc_aligned_ring(mhi_cntrl, tre_ring, tre_ring->len);
632a0f5a630SManivannan Sadhasivam 	if (ret)
633a0f5a630SManivannan Sadhasivam 		return -ENOMEM;
634a0f5a630SManivannan Sadhasivam 
635a0f5a630SManivannan Sadhasivam 	buf_ring->el_size = sizeof(struct mhi_buf_info);
636a0f5a630SManivannan Sadhasivam 	buf_ring->len = buf_ring->el_size * buf_ring->elements;
637a0f5a630SManivannan Sadhasivam 	buf_ring->base = vzalloc(buf_ring->len);
638a0f5a630SManivannan Sadhasivam 
639a0f5a630SManivannan Sadhasivam 	if (!buf_ring->base) {
640a0f5a630SManivannan Sadhasivam 		dma_free_coherent(mhi_cntrl->cntrl_dev, tre_ring->alloc_size,
641a0f5a630SManivannan Sadhasivam 				  tre_ring->pre_aligned, tre_ring->dma_handle);
642a0f5a630SManivannan Sadhasivam 		return -ENOMEM;
643a0f5a630SManivannan Sadhasivam 	}
644a0f5a630SManivannan Sadhasivam 
645a0f5a630SManivannan Sadhasivam 	tmp = le32_to_cpu(chan_ctxt->chcfg);
646a0f5a630SManivannan Sadhasivam 	tmp &= ~CHAN_CTX_CHSTATE_MASK;
647d28cab4dSManivannan Sadhasivam 	tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_ENABLED);
648a0f5a630SManivannan Sadhasivam 	chan_ctxt->chcfg = cpu_to_le32(tmp);
649a0f5a630SManivannan Sadhasivam 
650a0f5a630SManivannan Sadhasivam 	chan_ctxt->rbase = cpu_to_le64(tre_ring->iommu_base);
651a0f5a630SManivannan Sadhasivam 	chan_ctxt->rp = chan_ctxt->wp = chan_ctxt->rbase;
652a0f5a630SManivannan Sadhasivam 	chan_ctxt->rlen = cpu_to_le64(tre_ring->len);
653a0f5a630SManivannan Sadhasivam 	tre_ring->ctxt_wp = &chan_ctxt->wp;
654a0f5a630SManivannan Sadhasivam 
655a0f5a630SManivannan Sadhasivam 	tre_ring->rp = tre_ring->wp = tre_ring->base;
656a0f5a630SManivannan Sadhasivam 	buf_ring->rp = buf_ring->wp = buf_ring->base;
657a0f5a630SManivannan Sadhasivam 	mhi_chan->db_cfg.db_mode = 1;
658a0f5a630SManivannan Sadhasivam 
659a0f5a630SManivannan Sadhasivam 	/* Update to all cores */
660a0f5a630SManivannan Sadhasivam 	smp_wmb();
661a0f5a630SManivannan Sadhasivam 
662a0f5a630SManivannan Sadhasivam 	return 0;
663a0f5a630SManivannan Sadhasivam }
664a0f5a630SManivannan Sadhasivam 
parse_ev_cfg(struct mhi_controller * mhi_cntrl,const struct mhi_controller_config * config)665a0f5a630SManivannan Sadhasivam static int parse_ev_cfg(struct mhi_controller *mhi_cntrl,
666a0f5a630SManivannan Sadhasivam 			const struct mhi_controller_config *config)
667a0f5a630SManivannan Sadhasivam {
668a0f5a630SManivannan Sadhasivam 	struct mhi_event *mhi_event;
669a0f5a630SManivannan Sadhasivam 	const struct mhi_event_config *event_cfg;
670a0f5a630SManivannan Sadhasivam 	struct device *dev = mhi_cntrl->cntrl_dev;
671a0f5a630SManivannan Sadhasivam 	int i, num;
672a0f5a630SManivannan Sadhasivam 
673a0f5a630SManivannan Sadhasivam 	num = config->num_events;
674a0f5a630SManivannan Sadhasivam 	mhi_cntrl->total_ev_rings = num;
675a0f5a630SManivannan Sadhasivam 	mhi_cntrl->mhi_event = kcalloc(num, sizeof(*mhi_cntrl->mhi_event),
676a0f5a630SManivannan Sadhasivam 				       GFP_KERNEL);
677a0f5a630SManivannan Sadhasivam 	if (!mhi_cntrl->mhi_event)
678a0f5a630SManivannan Sadhasivam 		return -ENOMEM;
679a0f5a630SManivannan Sadhasivam 
680a0f5a630SManivannan Sadhasivam 	/* Populate event ring */
681a0f5a630SManivannan Sadhasivam 	mhi_event = mhi_cntrl->mhi_event;
682a0f5a630SManivannan Sadhasivam 	for (i = 0; i < num; i++) {
683a0f5a630SManivannan Sadhasivam 		event_cfg = &config->event_cfg[i];
684a0f5a630SManivannan Sadhasivam 
685a0f5a630SManivannan Sadhasivam 		mhi_event->er_index = i;
686a0f5a630SManivannan Sadhasivam 		mhi_event->ring.elements = event_cfg->num_elements;
687a0f5a630SManivannan Sadhasivam 		mhi_event->intmod = event_cfg->irq_moderation_ms;
688a0f5a630SManivannan Sadhasivam 		mhi_event->irq = event_cfg->irq;
689a0f5a630SManivannan Sadhasivam 
690a0f5a630SManivannan Sadhasivam 		if (event_cfg->channel != U32_MAX) {
691a0f5a630SManivannan Sadhasivam 			/* This event ring has a dedicated channel */
692a0f5a630SManivannan Sadhasivam 			mhi_event->chan = event_cfg->channel;
693a0f5a630SManivannan Sadhasivam 			if (mhi_event->chan >= mhi_cntrl->max_chan) {
694a0f5a630SManivannan Sadhasivam 				dev_err(dev,
695a0f5a630SManivannan Sadhasivam 					"Event Ring channel not available\n");
696a0f5a630SManivannan Sadhasivam 				goto error_ev_cfg;
697a0f5a630SManivannan Sadhasivam 			}
698a0f5a630SManivannan Sadhasivam 
699a0f5a630SManivannan Sadhasivam 			mhi_event->mhi_chan =
700a0f5a630SManivannan Sadhasivam 				&mhi_cntrl->mhi_chan[mhi_event->chan];
701a0f5a630SManivannan Sadhasivam 		}
702a0f5a630SManivannan Sadhasivam 
703a0f5a630SManivannan Sadhasivam 		/* Priority is fixed to 1 for now */
704a0f5a630SManivannan Sadhasivam 		mhi_event->priority = 1;
705a0f5a630SManivannan Sadhasivam 
706a0f5a630SManivannan Sadhasivam 		mhi_event->db_cfg.brstmode = event_cfg->mode;
707a0f5a630SManivannan Sadhasivam 		if (MHI_INVALID_BRSTMODE(mhi_event->db_cfg.brstmode))
708a0f5a630SManivannan Sadhasivam 			goto error_ev_cfg;
709a0f5a630SManivannan Sadhasivam 
710a0f5a630SManivannan Sadhasivam 		if (mhi_event->db_cfg.brstmode == MHI_DB_BRST_ENABLE)
711a0f5a630SManivannan Sadhasivam 			mhi_event->db_cfg.process_db = mhi_db_brstmode;
712a0f5a630SManivannan Sadhasivam 		else
713a0f5a630SManivannan Sadhasivam 			mhi_event->db_cfg.process_db = mhi_db_brstmode_disable;
714a0f5a630SManivannan Sadhasivam 
715a0f5a630SManivannan Sadhasivam 		mhi_event->data_type = event_cfg->data_type;
716a0f5a630SManivannan Sadhasivam 
717a0f5a630SManivannan Sadhasivam 		switch (mhi_event->data_type) {
718a0f5a630SManivannan Sadhasivam 		case MHI_ER_DATA:
719a0f5a630SManivannan Sadhasivam 			mhi_event->process_event = mhi_process_data_event_ring;
720a0f5a630SManivannan Sadhasivam 			break;
721a0f5a630SManivannan Sadhasivam 		case MHI_ER_CTRL:
722a0f5a630SManivannan Sadhasivam 			mhi_event->process_event = mhi_process_ctrl_ev_ring;
723a0f5a630SManivannan Sadhasivam 			break;
724a0f5a630SManivannan Sadhasivam 		default:
725a0f5a630SManivannan Sadhasivam 			dev_err(dev, "Event Ring type not supported\n");
726a0f5a630SManivannan Sadhasivam 			goto error_ev_cfg;
727a0f5a630SManivannan Sadhasivam 		}
728a0f5a630SManivannan Sadhasivam 
729a0f5a630SManivannan Sadhasivam 		mhi_event->hw_ring = event_cfg->hardware_event;
730a0f5a630SManivannan Sadhasivam 		if (mhi_event->hw_ring)
731a0f5a630SManivannan Sadhasivam 			mhi_cntrl->hw_ev_rings++;
732a0f5a630SManivannan Sadhasivam 		else
733a0f5a630SManivannan Sadhasivam 			mhi_cntrl->sw_ev_rings++;
734a0f5a630SManivannan Sadhasivam 
735a0f5a630SManivannan Sadhasivam 		mhi_event->cl_manage = event_cfg->client_managed;
736a0f5a630SManivannan Sadhasivam 		mhi_event->offload_ev = event_cfg->offload_channel;
737a0f5a630SManivannan Sadhasivam 		mhi_event++;
738a0f5a630SManivannan Sadhasivam 	}
739a0f5a630SManivannan Sadhasivam 
740a0f5a630SManivannan Sadhasivam 	return 0;
741a0f5a630SManivannan Sadhasivam 
742a0f5a630SManivannan Sadhasivam error_ev_cfg:
743a0f5a630SManivannan Sadhasivam 
744a0f5a630SManivannan Sadhasivam 	kfree(mhi_cntrl->mhi_event);
745a0f5a630SManivannan Sadhasivam 	return -EINVAL;
746a0f5a630SManivannan Sadhasivam }
747a0f5a630SManivannan Sadhasivam 
parse_ch_cfg(struct mhi_controller * mhi_cntrl,const struct mhi_controller_config * config)748a0f5a630SManivannan Sadhasivam static int parse_ch_cfg(struct mhi_controller *mhi_cntrl,
749a0f5a630SManivannan Sadhasivam 			const struct mhi_controller_config *config)
750a0f5a630SManivannan Sadhasivam {
751a0f5a630SManivannan Sadhasivam 	const struct mhi_channel_config *ch_cfg;
752a0f5a630SManivannan Sadhasivam 	struct device *dev = mhi_cntrl->cntrl_dev;
753a0f5a630SManivannan Sadhasivam 	int i;
754a0f5a630SManivannan Sadhasivam 	u32 chan;
755a0f5a630SManivannan Sadhasivam 
756a0f5a630SManivannan Sadhasivam 	mhi_cntrl->max_chan = config->max_channels;
757a0f5a630SManivannan Sadhasivam 
758a0f5a630SManivannan Sadhasivam 	/*
759a0f5a630SManivannan Sadhasivam 	 * The allocation of MHI channels can exceed 32KB in some scenarios,
760a0f5a630SManivannan Sadhasivam 	 * so to avoid any memory possible allocation failures, vzalloc is
761a0f5a630SManivannan Sadhasivam 	 * used here
762a0f5a630SManivannan Sadhasivam 	 */
763d0184830SJulia Lawall 	mhi_cntrl->mhi_chan = vcalloc(mhi_cntrl->max_chan,
764a0f5a630SManivannan Sadhasivam 				      sizeof(*mhi_cntrl->mhi_chan));
765a0f5a630SManivannan Sadhasivam 	if (!mhi_cntrl->mhi_chan)
766a0f5a630SManivannan Sadhasivam 		return -ENOMEM;
767a0f5a630SManivannan Sadhasivam 
768a0f5a630SManivannan Sadhasivam 	INIT_LIST_HEAD(&mhi_cntrl->lpm_chans);
769a0f5a630SManivannan Sadhasivam 
770a0f5a630SManivannan Sadhasivam 	/* Populate channel configurations */
771a0f5a630SManivannan Sadhasivam 	for (i = 0; i < config->num_channels; i++) {
772a0f5a630SManivannan Sadhasivam 		struct mhi_chan *mhi_chan;
773a0f5a630SManivannan Sadhasivam 
774a0f5a630SManivannan Sadhasivam 		ch_cfg = &config->ch_cfg[i];
775a0f5a630SManivannan Sadhasivam 
776a0f5a630SManivannan Sadhasivam 		chan = ch_cfg->num;
777a0f5a630SManivannan Sadhasivam 		if (chan >= mhi_cntrl->max_chan) {
778a0f5a630SManivannan Sadhasivam 			dev_err(dev, "Channel %d not available\n", chan);
779a0f5a630SManivannan Sadhasivam 			goto error_chan_cfg;
780a0f5a630SManivannan Sadhasivam 		}
781a0f5a630SManivannan Sadhasivam 
782a0f5a630SManivannan Sadhasivam 		mhi_chan = &mhi_cntrl->mhi_chan[chan];
783a0f5a630SManivannan Sadhasivam 		mhi_chan->name = ch_cfg->name;
784a0f5a630SManivannan Sadhasivam 		mhi_chan->chan = chan;
785a0f5a630SManivannan Sadhasivam 
786a0f5a630SManivannan Sadhasivam 		mhi_chan->tre_ring.elements = ch_cfg->num_elements;
787a0f5a630SManivannan Sadhasivam 		if (!mhi_chan->tre_ring.elements)
788a0f5a630SManivannan Sadhasivam 			goto error_chan_cfg;
789a0f5a630SManivannan Sadhasivam 
790a0f5a630SManivannan Sadhasivam 		/*
791a0f5a630SManivannan Sadhasivam 		 * For some channels, local ring length should be bigger than
792a0f5a630SManivannan Sadhasivam 		 * the transfer ring length due to internal logical channels
793a0f5a630SManivannan Sadhasivam 		 * in device. So host can queue much more buffers than transfer
794a0f5a630SManivannan Sadhasivam 		 * ring length. Example, RSC channels should have a larger local
795a0f5a630SManivannan Sadhasivam 		 * channel length than transfer ring length.
796a0f5a630SManivannan Sadhasivam 		 */
797a0f5a630SManivannan Sadhasivam 		mhi_chan->buf_ring.elements = ch_cfg->local_elements;
798a0f5a630SManivannan Sadhasivam 		if (!mhi_chan->buf_ring.elements)
799a0f5a630SManivannan Sadhasivam 			mhi_chan->buf_ring.elements = mhi_chan->tre_ring.elements;
800a0f5a630SManivannan Sadhasivam 		mhi_chan->er_index = ch_cfg->event_ring;
801a0f5a630SManivannan Sadhasivam 		mhi_chan->dir = ch_cfg->dir;
802a0f5a630SManivannan Sadhasivam 
803a0f5a630SManivannan Sadhasivam 		/*
804a0f5a630SManivannan Sadhasivam 		 * For most channels, chtype is identical to channel directions.
805a0f5a630SManivannan Sadhasivam 		 * So, if it is not defined then assign channel direction to
806a0f5a630SManivannan Sadhasivam 		 * chtype
807a0f5a630SManivannan Sadhasivam 		 */
808a0f5a630SManivannan Sadhasivam 		mhi_chan->type = ch_cfg->type;
809a0f5a630SManivannan Sadhasivam 		if (!mhi_chan->type)
810a0f5a630SManivannan Sadhasivam 			mhi_chan->type = (enum mhi_ch_type)mhi_chan->dir;
811a0f5a630SManivannan Sadhasivam 
812a0f5a630SManivannan Sadhasivam 		mhi_chan->ee_mask = ch_cfg->ee_mask;
813a0f5a630SManivannan Sadhasivam 		mhi_chan->db_cfg.pollcfg = ch_cfg->pollcfg;
814a0f5a630SManivannan Sadhasivam 		mhi_chan->lpm_notify = ch_cfg->lpm_notify;
815a0f5a630SManivannan Sadhasivam 		mhi_chan->offload_ch = ch_cfg->offload_channel;
816a0f5a630SManivannan Sadhasivam 		mhi_chan->db_cfg.reset_req = ch_cfg->doorbell_mode_switch;
817a0f5a630SManivannan Sadhasivam 		mhi_chan->pre_alloc = ch_cfg->auto_queue;
818a0f5a630SManivannan Sadhasivam 		mhi_chan->wake_capable = ch_cfg->wake_capable;
819a0f5a630SManivannan Sadhasivam 
820a0f5a630SManivannan Sadhasivam 		/*
821a0f5a630SManivannan Sadhasivam 		 * If MHI host allocates buffers, then the channel direction
822a0f5a630SManivannan Sadhasivam 		 * should be DMA_FROM_DEVICE
823a0f5a630SManivannan Sadhasivam 		 */
824a0f5a630SManivannan Sadhasivam 		if (mhi_chan->pre_alloc && mhi_chan->dir != DMA_FROM_DEVICE) {
825a0f5a630SManivannan Sadhasivam 			dev_err(dev, "Invalid channel configuration\n");
826a0f5a630SManivannan Sadhasivam 			goto error_chan_cfg;
827a0f5a630SManivannan Sadhasivam 		}
828a0f5a630SManivannan Sadhasivam 
829a0f5a630SManivannan Sadhasivam 		/*
830a0f5a630SManivannan Sadhasivam 		 * Bi-directional and direction less channel must be an
831a0f5a630SManivannan Sadhasivam 		 * offload channel
832a0f5a630SManivannan Sadhasivam 		 */
833a0f5a630SManivannan Sadhasivam 		if ((mhi_chan->dir == DMA_BIDIRECTIONAL ||
834a0f5a630SManivannan Sadhasivam 		     mhi_chan->dir == DMA_NONE) && !mhi_chan->offload_ch) {
835a0f5a630SManivannan Sadhasivam 			dev_err(dev, "Invalid channel configuration\n");
836a0f5a630SManivannan Sadhasivam 			goto error_chan_cfg;
837a0f5a630SManivannan Sadhasivam 		}
838a0f5a630SManivannan Sadhasivam 
839a0f5a630SManivannan Sadhasivam 		if (!mhi_chan->offload_ch) {
840a0f5a630SManivannan Sadhasivam 			mhi_chan->db_cfg.brstmode = ch_cfg->doorbell;
841a0f5a630SManivannan Sadhasivam 			if (MHI_INVALID_BRSTMODE(mhi_chan->db_cfg.brstmode)) {
842a0f5a630SManivannan Sadhasivam 				dev_err(dev, "Invalid Door bell mode\n");
843a0f5a630SManivannan Sadhasivam 				goto error_chan_cfg;
844a0f5a630SManivannan Sadhasivam 			}
845a0f5a630SManivannan Sadhasivam 		}
846a0f5a630SManivannan Sadhasivam 
847a0f5a630SManivannan Sadhasivam 		if (mhi_chan->db_cfg.brstmode == MHI_DB_BRST_ENABLE)
848a0f5a630SManivannan Sadhasivam 			mhi_chan->db_cfg.process_db = mhi_db_brstmode;
849a0f5a630SManivannan Sadhasivam 		else
850a0f5a630SManivannan Sadhasivam 			mhi_chan->db_cfg.process_db = mhi_db_brstmode_disable;
851a0f5a630SManivannan Sadhasivam 
852a0f5a630SManivannan Sadhasivam 		mhi_chan->configured = true;
853a0f5a630SManivannan Sadhasivam 
854a0f5a630SManivannan Sadhasivam 		if (mhi_chan->lpm_notify)
855a0f5a630SManivannan Sadhasivam 			list_add_tail(&mhi_chan->node, &mhi_cntrl->lpm_chans);
856a0f5a630SManivannan Sadhasivam 	}
857a0f5a630SManivannan Sadhasivam 
858a0f5a630SManivannan Sadhasivam 	return 0;
859a0f5a630SManivannan Sadhasivam 
860a0f5a630SManivannan Sadhasivam error_chan_cfg:
861a0f5a630SManivannan Sadhasivam 	vfree(mhi_cntrl->mhi_chan);
862a0f5a630SManivannan Sadhasivam 
863a0f5a630SManivannan Sadhasivam 	return -EINVAL;
864a0f5a630SManivannan Sadhasivam }
865a0f5a630SManivannan Sadhasivam 
parse_config(struct mhi_controller * mhi_cntrl,const struct mhi_controller_config * config)866a0f5a630SManivannan Sadhasivam static int parse_config(struct mhi_controller *mhi_cntrl,
867a0f5a630SManivannan Sadhasivam 			const struct mhi_controller_config *config)
868a0f5a630SManivannan Sadhasivam {
869a0f5a630SManivannan Sadhasivam 	int ret;
870a0f5a630SManivannan Sadhasivam 
871a0f5a630SManivannan Sadhasivam 	/* Parse MHI channel configuration */
872a0f5a630SManivannan Sadhasivam 	ret = parse_ch_cfg(mhi_cntrl, config);
873a0f5a630SManivannan Sadhasivam 	if (ret)
874a0f5a630SManivannan Sadhasivam 		return ret;
875a0f5a630SManivannan Sadhasivam 
876a0f5a630SManivannan Sadhasivam 	/* Parse MHI event configuration */
877a0f5a630SManivannan Sadhasivam 	ret = parse_ev_cfg(mhi_cntrl, config);
878a0f5a630SManivannan Sadhasivam 	if (ret)
879a0f5a630SManivannan Sadhasivam 		goto error_ev_cfg;
880a0f5a630SManivannan Sadhasivam 
881a0f5a630SManivannan Sadhasivam 	mhi_cntrl->timeout_ms = config->timeout_ms;
882a0f5a630SManivannan Sadhasivam 	if (!mhi_cntrl->timeout_ms)
883a0f5a630SManivannan Sadhasivam 		mhi_cntrl->timeout_ms = MHI_TIMEOUT_MS;
884a0f5a630SManivannan Sadhasivam 
885a0f5a630SManivannan Sadhasivam 	mhi_cntrl->bounce_buf = config->use_bounce_buf;
886a0f5a630SManivannan Sadhasivam 	mhi_cntrl->buffer_len = config->buf_len;
887a0f5a630SManivannan Sadhasivam 	if (!mhi_cntrl->buffer_len)
888a0f5a630SManivannan Sadhasivam 		mhi_cntrl->buffer_len = MHI_MAX_MTU;
889a0f5a630SManivannan Sadhasivam 
890a0f5a630SManivannan Sadhasivam 	/* By default, host is allowed to ring DB in both M0 and M2 states */
891a0f5a630SManivannan Sadhasivam 	mhi_cntrl->db_access = MHI_PM_M0 | MHI_PM_M2;
892a0f5a630SManivannan Sadhasivam 	if (config->m2_no_db)
893a0f5a630SManivannan Sadhasivam 		mhi_cntrl->db_access &= ~MHI_PM_M2;
894a0f5a630SManivannan Sadhasivam 
895a0f5a630SManivannan Sadhasivam 	return 0;
896a0f5a630SManivannan Sadhasivam 
897a0f5a630SManivannan Sadhasivam error_ev_cfg:
898a0f5a630SManivannan Sadhasivam 	vfree(mhi_cntrl->mhi_chan);
899a0f5a630SManivannan Sadhasivam 
900a0f5a630SManivannan Sadhasivam 	return ret;
901a0f5a630SManivannan Sadhasivam }
902a0f5a630SManivannan Sadhasivam 
mhi_register_controller(struct mhi_controller * mhi_cntrl,const struct mhi_controller_config * config)903a0f5a630SManivannan Sadhasivam int mhi_register_controller(struct mhi_controller *mhi_cntrl,
904a0f5a630SManivannan Sadhasivam 			    const struct mhi_controller_config *config)
905a0f5a630SManivannan Sadhasivam {
906a0f5a630SManivannan Sadhasivam 	struct mhi_event *mhi_event;
907a0f5a630SManivannan Sadhasivam 	struct mhi_chan *mhi_chan;
908a0f5a630SManivannan Sadhasivam 	struct mhi_cmd *mhi_cmd;
909a0f5a630SManivannan Sadhasivam 	struct mhi_device *mhi_dev;
910a0f5a630SManivannan Sadhasivam 	u32 soc_info;
911a0f5a630SManivannan Sadhasivam 	int ret, i;
912a0f5a630SManivannan Sadhasivam 
913a0f5a630SManivannan Sadhasivam 	if (!mhi_cntrl || !mhi_cntrl->cntrl_dev || !mhi_cntrl->regs ||
914a0f5a630SManivannan Sadhasivam 	    !mhi_cntrl->runtime_get || !mhi_cntrl->runtime_put ||
915a0f5a630SManivannan Sadhasivam 	    !mhi_cntrl->status_cb || !mhi_cntrl->read_reg ||
916a0f5a630SManivannan Sadhasivam 	    !mhi_cntrl->write_reg || !mhi_cntrl->nr_irqs ||
917a0f5a630SManivannan Sadhasivam 	    !mhi_cntrl->irq || !mhi_cntrl->reg_len)
918a0f5a630SManivannan Sadhasivam 		return -EINVAL;
919a0f5a630SManivannan Sadhasivam 
920a0f5a630SManivannan Sadhasivam 	ret = parse_config(mhi_cntrl, config);
921a0f5a630SManivannan Sadhasivam 	if (ret)
922a0f5a630SManivannan Sadhasivam 		return -EINVAL;
923a0f5a630SManivannan Sadhasivam 
924a0f5a630SManivannan Sadhasivam 	mhi_cntrl->mhi_cmd = kcalloc(NR_OF_CMD_RINGS,
925a0f5a630SManivannan Sadhasivam 				     sizeof(*mhi_cntrl->mhi_cmd), GFP_KERNEL);
926a0f5a630SManivannan Sadhasivam 	if (!mhi_cntrl->mhi_cmd) {
927a0f5a630SManivannan Sadhasivam 		ret = -ENOMEM;
928a0f5a630SManivannan Sadhasivam 		goto err_free_event;
929a0f5a630SManivannan Sadhasivam 	}
930a0f5a630SManivannan Sadhasivam 
931a0f5a630SManivannan Sadhasivam 	INIT_LIST_HEAD(&mhi_cntrl->transition_list);
932a0f5a630SManivannan Sadhasivam 	mutex_init(&mhi_cntrl->pm_mutex);
933a0f5a630SManivannan Sadhasivam 	rwlock_init(&mhi_cntrl->pm_lock);
934a0f5a630SManivannan Sadhasivam 	spin_lock_init(&mhi_cntrl->transition_lock);
935a0f5a630SManivannan Sadhasivam 	spin_lock_init(&mhi_cntrl->wlock);
936a0f5a630SManivannan Sadhasivam 	INIT_WORK(&mhi_cntrl->st_worker, mhi_pm_st_worker);
937a0f5a630SManivannan Sadhasivam 	init_waitqueue_head(&mhi_cntrl->state_event);
938a0f5a630SManivannan Sadhasivam 
939a0f5a630SManivannan Sadhasivam 	mhi_cntrl->hiprio_wq = alloc_ordered_workqueue("mhi_hiprio_wq", WQ_HIGHPRI);
940a0f5a630SManivannan Sadhasivam 	if (!mhi_cntrl->hiprio_wq) {
941a0f5a630SManivannan Sadhasivam 		dev_err(mhi_cntrl->cntrl_dev, "Failed to allocate workqueue\n");
942a0f5a630SManivannan Sadhasivam 		ret = -ENOMEM;
943a0f5a630SManivannan Sadhasivam 		goto err_free_cmd;
944a0f5a630SManivannan Sadhasivam 	}
945a0f5a630SManivannan Sadhasivam 
946a0f5a630SManivannan Sadhasivam 	mhi_cmd = mhi_cntrl->mhi_cmd;
947a0f5a630SManivannan Sadhasivam 	for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++)
948a0f5a630SManivannan Sadhasivam 		spin_lock_init(&mhi_cmd->lock);
949a0f5a630SManivannan Sadhasivam 
950a0f5a630SManivannan Sadhasivam 	mhi_event = mhi_cntrl->mhi_event;
951a0f5a630SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) {
952a0f5a630SManivannan Sadhasivam 		/* Skip for offload events */
953a0f5a630SManivannan Sadhasivam 		if (mhi_event->offload_ev)
954a0f5a630SManivannan Sadhasivam 			continue;
955a0f5a630SManivannan Sadhasivam 
956a0f5a630SManivannan Sadhasivam 		mhi_event->mhi_cntrl = mhi_cntrl;
957a0f5a630SManivannan Sadhasivam 		spin_lock_init(&mhi_event->lock);
958a0f5a630SManivannan Sadhasivam 		if (mhi_event->data_type == MHI_ER_CTRL)
959a0f5a630SManivannan Sadhasivam 			tasklet_init(&mhi_event->task, mhi_ctrl_ev_task,
960a0f5a630SManivannan Sadhasivam 				     (ulong)mhi_event);
961a0f5a630SManivannan Sadhasivam 		else
962a0f5a630SManivannan Sadhasivam 			tasklet_init(&mhi_event->task, mhi_ev_task,
963a0f5a630SManivannan Sadhasivam 				     (ulong)mhi_event);
964a0f5a630SManivannan Sadhasivam 	}
965a0f5a630SManivannan Sadhasivam 
966a0f5a630SManivannan Sadhasivam 	mhi_chan = mhi_cntrl->mhi_chan;
967a0f5a630SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->max_chan; i++, mhi_chan++) {
968a0f5a630SManivannan Sadhasivam 		mutex_init(&mhi_chan->mutex);
969a0f5a630SManivannan Sadhasivam 		init_completion(&mhi_chan->completion);
970a0f5a630SManivannan Sadhasivam 		rwlock_init(&mhi_chan->lock);
971a0f5a630SManivannan Sadhasivam 
972a0f5a630SManivannan Sadhasivam 		/* used in setting bei field of TRE */
973a0f5a630SManivannan Sadhasivam 		mhi_event = &mhi_cntrl->mhi_event[mhi_chan->er_index];
974a0f5a630SManivannan Sadhasivam 		mhi_chan->intmod = mhi_event->intmod;
975a0f5a630SManivannan Sadhasivam 	}
976a0f5a630SManivannan Sadhasivam 
977a0f5a630SManivannan Sadhasivam 	if (mhi_cntrl->bounce_buf) {
978a0f5a630SManivannan Sadhasivam 		mhi_cntrl->map_single = mhi_map_single_use_bb;
979a0f5a630SManivannan Sadhasivam 		mhi_cntrl->unmap_single = mhi_unmap_single_use_bb;
980a0f5a630SManivannan Sadhasivam 	} else {
981a0f5a630SManivannan Sadhasivam 		mhi_cntrl->map_single = mhi_map_single_no_bb;
982a0f5a630SManivannan Sadhasivam 		mhi_cntrl->unmap_single = mhi_unmap_single_no_bb;
983a0f5a630SManivannan Sadhasivam 	}
984a0f5a630SManivannan Sadhasivam 
985a0f5a630SManivannan Sadhasivam 	/* Read the MHI device info */
986a0f5a630SManivannan Sadhasivam 	ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs,
987a0f5a630SManivannan Sadhasivam 			   SOC_HW_VERSION_OFFS, &soc_info);
988a0f5a630SManivannan Sadhasivam 	if (ret)
989a0f5a630SManivannan Sadhasivam 		goto err_destroy_wq;
990a0f5a630SManivannan Sadhasivam 
991d28cab4dSManivannan Sadhasivam 	mhi_cntrl->family_number = FIELD_GET(SOC_HW_VERSION_FAM_NUM_BMSK, soc_info);
992d28cab4dSManivannan Sadhasivam 	mhi_cntrl->device_number = FIELD_GET(SOC_HW_VERSION_DEV_NUM_BMSK, soc_info);
993d28cab4dSManivannan Sadhasivam 	mhi_cntrl->major_version = FIELD_GET(SOC_HW_VERSION_MAJOR_VER_BMSK, soc_info);
994d28cab4dSManivannan Sadhasivam 	mhi_cntrl->minor_version = FIELD_GET(SOC_HW_VERSION_MINOR_VER_BMSK, soc_info);
995a0f5a630SManivannan Sadhasivam 
996a0f5a630SManivannan Sadhasivam 	mhi_cntrl->index = ida_alloc(&mhi_controller_ida, GFP_KERNEL);
997a0f5a630SManivannan Sadhasivam 	if (mhi_cntrl->index < 0) {
998a0f5a630SManivannan Sadhasivam 		ret = mhi_cntrl->index;
999a0f5a630SManivannan Sadhasivam 		goto err_destroy_wq;
1000a0f5a630SManivannan Sadhasivam 	}
1001a0f5a630SManivannan Sadhasivam 
10021227d2a2SQiang Yu 	ret = mhi_init_irq_setup(mhi_cntrl);
10031227d2a2SQiang Yu 	if (ret)
10041227d2a2SQiang Yu 		goto err_ida_free;
10051227d2a2SQiang Yu 
1006a0f5a630SManivannan Sadhasivam 	/* Register controller with MHI bus */
1007a0f5a630SManivannan Sadhasivam 	mhi_dev = mhi_alloc_device(mhi_cntrl);
1008a0f5a630SManivannan Sadhasivam 	if (IS_ERR(mhi_dev)) {
1009a0f5a630SManivannan Sadhasivam 		dev_err(mhi_cntrl->cntrl_dev, "Failed to allocate MHI device\n");
1010a0f5a630SManivannan Sadhasivam 		ret = PTR_ERR(mhi_dev);
10111227d2a2SQiang Yu 		goto error_setup_irq;
1012a0f5a630SManivannan Sadhasivam 	}
1013a0f5a630SManivannan Sadhasivam 
1014a0f5a630SManivannan Sadhasivam 	mhi_dev->dev_type = MHI_DEVICE_CONTROLLER;
1015a0f5a630SManivannan Sadhasivam 	mhi_dev->mhi_cntrl = mhi_cntrl;
1016a0f5a630SManivannan Sadhasivam 	dev_set_name(&mhi_dev->dev, "mhi%d", mhi_cntrl->index);
1017a0f5a630SManivannan Sadhasivam 	mhi_dev->name = dev_name(&mhi_dev->dev);
1018a0f5a630SManivannan Sadhasivam 
1019a0f5a630SManivannan Sadhasivam 	/* Init wakeup source */
1020a0f5a630SManivannan Sadhasivam 	device_init_wakeup(&mhi_dev->dev, true);
1021a0f5a630SManivannan Sadhasivam 
1022a0f5a630SManivannan Sadhasivam 	ret = device_add(&mhi_dev->dev);
1023a0f5a630SManivannan Sadhasivam 	if (ret)
1024a0f5a630SManivannan Sadhasivam 		goto err_release_dev;
1025a0f5a630SManivannan Sadhasivam 
1026a0f5a630SManivannan Sadhasivam 	mhi_cntrl->mhi_dev = mhi_dev;
1027a0f5a630SManivannan Sadhasivam 
1028a0f5a630SManivannan Sadhasivam 	mhi_create_debugfs(mhi_cntrl);
1029a0f5a630SManivannan Sadhasivam 
1030a0f5a630SManivannan Sadhasivam 	return 0;
1031a0f5a630SManivannan Sadhasivam 
1032a0f5a630SManivannan Sadhasivam err_release_dev:
1033a0f5a630SManivannan Sadhasivam 	put_device(&mhi_dev->dev);
10341227d2a2SQiang Yu error_setup_irq:
10351227d2a2SQiang Yu 	mhi_deinit_free_irq(mhi_cntrl);
1036a0f5a630SManivannan Sadhasivam err_ida_free:
1037a0f5a630SManivannan Sadhasivam 	ida_free(&mhi_controller_ida, mhi_cntrl->index);
1038a0f5a630SManivannan Sadhasivam err_destroy_wq:
1039a0f5a630SManivannan Sadhasivam 	destroy_workqueue(mhi_cntrl->hiprio_wq);
1040a0f5a630SManivannan Sadhasivam err_free_cmd:
1041a0f5a630SManivannan Sadhasivam 	kfree(mhi_cntrl->mhi_cmd);
1042a0f5a630SManivannan Sadhasivam err_free_event:
1043a0f5a630SManivannan Sadhasivam 	kfree(mhi_cntrl->mhi_event);
1044a0f5a630SManivannan Sadhasivam 	vfree(mhi_cntrl->mhi_chan);
1045a0f5a630SManivannan Sadhasivam 
1046a0f5a630SManivannan Sadhasivam 	return ret;
1047a0f5a630SManivannan Sadhasivam }
1048a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_register_controller);
1049a0f5a630SManivannan Sadhasivam 
mhi_unregister_controller(struct mhi_controller * mhi_cntrl)1050a0f5a630SManivannan Sadhasivam void mhi_unregister_controller(struct mhi_controller *mhi_cntrl)
1051a0f5a630SManivannan Sadhasivam {
1052a0f5a630SManivannan Sadhasivam 	struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;
1053a0f5a630SManivannan Sadhasivam 	struct mhi_chan *mhi_chan = mhi_cntrl->mhi_chan;
1054a0f5a630SManivannan Sadhasivam 	unsigned int i;
1055a0f5a630SManivannan Sadhasivam 
10561227d2a2SQiang Yu 	mhi_deinit_free_irq(mhi_cntrl);
1057a0f5a630SManivannan Sadhasivam 	mhi_destroy_debugfs(mhi_cntrl);
1058a0f5a630SManivannan Sadhasivam 
1059a0f5a630SManivannan Sadhasivam 	destroy_workqueue(mhi_cntrl->hiprio_wq);
1060a0f5a630SManivannan Sadhasivam 	kfree(mhi_cntrl->mhi_cmd);
1061a0f5a630SManivannan Sadhasivam 	kfree(mhi_cntrl->mhi_event);
1062a0f5a630SManivannan Sadhasivam 
1063a0f5a630SManivannan Sadhasivam 	/* Drop the references to MHI devices created for channels */
1064a0f5a630SManivannan Sadhasivam 	for (i = 0; i < mhi_cntrl->max_chan; i++, mhi_chan++) {
1065a0f5a630SManivannan Sadhasivam 		if (!mhi_chan->mhi_dev)
1066a0f5a630SManivannan Sadhasivam 			continue;
1067a0f5a630SManivannan Sadhasivam 
1068a0f5a630SManivannan Sadhasivam 		put_device(&mhi_chan->mhi_dev->dev);
1069a0f5a630SManivannan Sadhasivam 	}
1070a0f5a630SManivannan Sadhasivam 	vfree(mhi_cntrl->mhi_chan);
1071a0f5a630SManivannan Sadhasivam 
1072a0f5a630SManivannan Sadhasivam 	device_del(&mhi_dev->dev);
1073a0f5a630SManivannan Sadhasivam 	put_device(&mhi_dev->dev);
1074a0f5a630SManivannan Sadhasivam 
1075a0f5a630SManivannan Sadhasivam 	ida_free(&mhi_controller_ida, mhi_cntrl->index);
1076a0f5a630SManivannan Sadhasivam }
1077a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_unregister_controller);
1078a0f5a630SManivannan Sadhasivam 
mhi_alloc_controller(void)1079a0f5a630SManivannan Sadhasivam struct mhi_controller *mhi_alloc_controller(void)
1080a0f5a630SManivannan Sadhasivam {
1081a0f5a630SManivannan Sadhasivam 	struct mhi_controller *mhi_cntrl;
1082a0f5a630SManivannan Sadhasivam 
1083a0f5a630SManivannan Sadhasivam 	mhi_cntrl = kzalloc(sizeof(*mhi_cntrl), GFP_KERNEL);
1084a0f5a630SManivannan Sadhasivam 
1085a0f5a630SManivannan Sadhasivam 	return mhi_cntrl;
1086a0f5a630SManivannan Sadhasivam }
1087a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_alloc_controller);
1088a0f5a630SManivannan Sadhasivam 
mhi_free_controller(struct mhi_controller * mhi_cntrl)1089a0f5a630SManivannan Sadhasivam void mhi_free_controller(struct mhi_controller *mhi_cntrl)
1090a0f5a630SManivannan Sadhasivam {
1091a0f5a630SManivannan Sadhasivam 	kfree(mhi_cntrl);
1092a0f5a630SManivannan Sadhasivam }
1093a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_free_controller);
1094a0f5a630SManivannan Sadhasivam 
mhi_prepare_for_power_up(struct mhi_controller * mhi_cntrl)1095a0f5a630SManivannan Sadhasivam int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl)
1096a0f5a630SManivannan Sadhasivam {
1097a0f5a630SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
1098a0f5a630SManivannan Sadhasivam 	u32 bhi_off, bhie_off;
1099a0f5a630SManivannan Sadhasivam 	int ret;
1100a0f5a630SManivannan Sadhasivam 
1101a0f5a630SManivannan Sadhasivam 	mutex_lock(&mhi_cntrl->pm_mutex);
1102a0f5a630SManivannan Sadhasivam 
1103a0f5a630SManivannan Sadhasivam 	ret = mhi_init_dev_ctxt(mhi_cntrl);
1104a0f5a630SManivannan Sadhasivam 	if (ret)
1105a0f5a630SManivannan Sadhasivam 		goto error_dev_ctxt;
1106a0f5a630SManivannan Sadhasivam 
1107a0f5a630SManivannan Sadhasivam 	ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, BHIOFF, &bhi_off);
1108a0f5a630SManivannan Sadhasivam 	if (ret) {
1109a0f5a630SManivannan Sadhasivam 		dev_err(dev, "Error getting BHI offset\n");
1110a0f5a630SManivannan Sadhasivam 		goto error_reg_offset;
1111a0f5a630SManivannan Sadhasivam 	}
1112a0f5a630SManivannan Sadhasivam 
1113a0f5a630SManivannan Sadhasivam 	if (bhi_off >= mhi_cntrl->reg_len) {
1114a0f5a630SManivannan Sadhasivam 		dev_err(dev, "BHI offset: 0x%x is out of range: 0x%zx\n",
1115a0f5a630SManivannan Sadhasivam 			bhi_off, mhi_cntrl->reg_len);
1116446271e5SJeffrey Hugo 		ret = -ERANGE;
1117a0f5a630SManivannan Sadhasivam 		goto error_reg_offset;
1118a0f5a630SManivannan Sadhasivam 	}
1119a0f5a630SManivannan Sadhasivam 	mhi_cntrl->bhi = mhi_cntrl->regs + bhi_off;
1120a0f5a630SManivannan Sadhasivam 
1121a0f5a630SManivannan Sadhasivam 	if (mhi_cntrl->fbc_download || mhi_cntrl->rddm_size) {
1122a0f5a630SManivannan Sadhasivam 		ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, BHIEOFF,
1123a0f5a630SManivannan Sadhasivam 				   &bhie_off);
1124a0f5a630SManivannan Sadhasivam 		if (ret) {
1125a0f5a630SManivannan Sadhasivam 			dev_err(dev, "Error getting BHIE offset\n");
1126a0f5a630SManivannan Sadhasivam 			goto error_reg_offset;
1127a0f5a630SManivannan Sadhasivam 		}
1128a0f5a630SManivannan Sadhasivam 
1129a0f5a630SManivannan Sadhasivam 		if (bhie_off >= mhi_cntrl->reg_len) {
1130a0f5a630SManivannan Sadhasivam 			dev_err(dev,
1131a0f5a630SManivannan Sadhasivam 				"BHIe offset: 0x%x is out of range: 0x%zx\n",
1132a0f5a630SManivannan Sadhasivam 				bhie_off, mhi_cntrl->reg_len);
1133446271e5SJeffrey Hugo 			ret = -ERANGE;
1134a0f5a630SManivannan Sadhasivam 			goto error_reg_offset;
1135a0f5a630SManivannan Sadhasivam 		}
1136a0f5a630SManivannan Sadhasivam 		mhi_cntrl->bhie = mhi_cntrl->regs + bhie_off;
1137a0f5a630SManivannan Sadhasivam 	}
1138a0f5a630SManivannan Sadhasivam 
1139a0f5a630SManivannan Sadhasivam 	if (mhi_cntrl->rddm_size) {
1140a0f5a630SManivannan Sadhasivam 		/*
1141a0f5a630SManivannan Sadhasivam 		 * This controller supports RDDM, so we need to manually clear
1142a0f5a630SManivannan Sadhasivam 		 * BHIE RX registers since POR values are undefined.
1143a0f5a630SManivannan Sadhasivam 		 */
1144a0f5a630SManivannan Sadhasivam 		memset_io(mhi_cntrl->bhie + BHIE_RXVECADDR_LOW_OFFS,
1145a0f5a630SManivannan Sadhasivam 			  0, BHIE_RXVECSTATUS_OFFS - BHIE_RXVECADDR_LOW_OFFS +
1146a0f5a630SManivannan Sadhasivam 			  4);
1147a0f5a630SManivannan Sadhasivam 		/*
1148a0f5a630SManivannan Sadhasivam 		 * Allocate RDDM table for debugging purpose if specified
1149a0f5a630SManivannan Sadhasivam 		 */
1150a0f5a630SManivannan Sadhasivam 		mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->rddm_image,
1151a0f5a630SManivannan Sadhasivam 				     mhi_cntrl->rddm_size);
11520bca889fSBhaumik Bhatt 		if (mhi_cntrl->rddm_image) {
11530bca889fSBhaumik Bhatt 			ret = mhi_rddm_prepare(mhi_cntrl,
11540bca889fSBhaumik Bhatt 					       mhi_cntrl->rddm_image);
11550bca889fSBhaumik Bhatt 			if (ret) {
11560bca889fSBhaumik Bhatt 				mhi_free_bhie_table(mhi_cntrl,
11570bca889fSBhaumik Bhatt 						    mhi_cntrl->rddm_image);
11580bca889fSBhaumik Bhatt 				goto error_reg_offset;
11590bca889fSBhaumik Bhatt 			}
11600bca889fSBhaumik Bhatt 		}
1161a0f5a630SManivannan Sadhasivam 	}
1162a0f5a630SManivannan Sadhasivam 
1163a0f5a630SManivannan Sadhasivam 	mutex_unlock(&mhi_cntrl->pm_mutex);
1164a0f5a630SManivannan Sadhasivam 
1165a0f5a630SManivannan Sadhasivam 	return 0;
1166a0f5a630SManivannan Sadhasivam 
1167a0f5a630SManivannan Sadhasivam error_reg_offset:
1168a0f5a630SManivannan Sadhasivam 	mhi_deinit_dev_ctxt(mhi_cntrl);
1169a0f5a630SManivannan Sadhasivam 
1170a0f5a630SManivannan Sadhasivam error_dev_ctxt:
1171a0f5a630SManivannan Sadhasivam 	mutex_unlock(&mhi_cntrl->pm_mutex);
1172a0f5a630SManivannan Sadhasivam 
1173a0f5a630SManivannan Sadhasivam 	return ret;
1174a0f5a630SManivannan Sadhasivam }
1175a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_prepare_for_power_up);
1176a0f5a630SManivannan Sadhasivam 
mhi_unprepare_after_power_down(struct mhi_controller * mhi_cntrl)1177a0f5a630SManivannan Sadhasivam void mhi_unprepare_after_power_down(struct mhi_controller *mhi_cntrl)
1178a0f5a630SManivannan Sadhasivam {
1179a0f5a630SManivannan Sadhasivam 	if (mhi_cntrl->fbc_image) {
1180a0f5a630SManivannan Sadhasivam 		mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image);
1181a0f5a630SManivannan Sadhasivam 		mhi_cntrl->fbc_image = NULL;
1182a0f5a630SManivannan Sadhasivam 	}
1183a0f5a630SManivannan Sadhasivam 
1184a0f5a630SManivannan Sadhasivam 	if (mhi_cntrl->rddm_image) {
1185a0f5a630SManivannan Sadhasivam 		mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->rddm_image);
1186a0f5a630SManivannan Sadhasivam 		mhi_cntrl->rddm_image = NULL;
1187a0f5a630SManivannan Sadhasivam 	}
1188a0f5a630SManivannan Sadhasivam 
1189a0f5a630SManivannan Sadhasivam 	mhi_cntrl->bhi = NULL;
1190a0f5a630SManivannan Sadhasivam 	mhi_cntrl->bhie = NULL;
1191a0f5a630SManivannan Sadhasivam 
1192a0f5a630SManivannan Sadhasivam 	mhi_deinit_dev_ctxt(mhi_cntrl);
1193a0f5a630SManivannan Sadhasivam }
1194a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_unprepare_after_power_down);
1195a0f5a630SManivannan Sadhasivam 
mhi_release_device(struct device * dev)1196a0f5a630SManivannan Sadhasivam static void mhi_release_device(struct device *dev)
1197a0f5a630SManivannan Sadhasivam {
1198a0f5a630SManivannan Sadhasivam 	struct mhi_device *mhi_dev = to_mhi_device(dev);
1199a0f5a630SManivannan Sadhasivam 
1200a0f5a630SManivannan Sadhasivam 	/*
1201a0f5a630SManivannan Sadhasivam 	 * We need to set the mhi_chan->mhi_dev to NULL here since the MHI
1202a0f5a630SManivannan Sadhasivam 	 * devices for the channels will only get created if the mhi_dev
1203a0f5a630SManivannan Sadhasivam 	 * associated with it is NULL. This scenario will happen during the
1204a0f5a630SManivannan Sadhasivam 	 * controller suspend and resume.
1205a0f5a630SManivannan Sadhasivam 	 */
1206a0f5a630SManivannan Sadhasivam 	if (mhi_dev->ul_chan)
1207a0f5a630SManivannan Sadhasivam 		mhi_dev->ul_chan->mhi_dev = NULL;
1208a0f5a630SManivannan Sadhasivam 
1209a0f5a630SManivannan Sadhasivam 	if (mhi_dev->dl_chan)
1210a0f5a630SManivannan Sadhasivam 		mhi_dev->dl_chan->mhi_dev = NULL;
1211a0f5a630SManivannan Sadhasivam 
1212a0f5a630SManivannan Sadhasivam 	kfree(mhi_dev);
1213a0f5a630SManivannan Sadhasivam }
1214a0f5a630SManivannan Sadhasivam 
mhi_alloc_device(struct mhi_controller * mhi_cntrl)1215a0f5a630SManivannan Sadhasivam struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl)
1216a0f5a630SManivannan Sadhasivam {
1217a0f5a630SManivannan Sadhasivam 	struct mhi_device *mhi_dev;
1218a0f5a630SManivannan Sadhasivam 	struct device *dev;
1219a0f5a630SManivannan Sadhasivam 
1220a0f5a630SManivannan Sadhasivam 	mhi_dev = kzalloc(sizeof(*mhi_dev), GFP_KERNEL);
1221a0f5a630SManivannan Sadhasivam 	if (!mhi_dev)
1222a0f5a630SManivannan Sadhasivam 		return ERR_PTR(-ENOMEM);
1223a0f5a630SManivannan Sadhasivam 
1224a0f5a630SManivannan Sadhasivam 	dev = &mhi_dev->dev;
1225a0f5a630SManivannan Sadhasivam 	device_initialize(dev);
1226a0f5a630SManivannan Sadhasivam 	dev->bus = &mhi_bus_type;
1227a0f5a630SManivannan Sadhasivam 	dev->release = mhi_release_device;
1228a0f5a630SManivannan Sadhasivam 
1229a0f5a630SManivannan Sadhasivam 	if (mhi_cntrl->mhi_dev) {
1230a0f5a630SManivannan Sadhasivam 		/* for MHI client devices, parent is the MHI controller device */
1231a0f5a630SManivannan Sadhasivam 		dev->parent = &mhi_cntrl->mhi_dev->dev;
1232a0f5a630SManivannan Sadhasivam 	} else {
1233a0f5a630SManivannan Sadhasivam 		/* for MHI controller device, parent is the bus device (e.g. pci device) */
1234a0f5a630SManivannan Sadhasivam 		dev->parent = mhi_cntrl->cntrl_dev;
1235a0f5a630SManivannan Sadhasivam 	}
1236a0f5a630SManivannan Sadhasivam 
1237a0f5a630SManivannan Sadhasivam 	mhi_dev->mhi_cntrl = mhi_cntrl;
1238a0f5a630SManivannan Sadhasivam 	mhi_dev->dev_wake = 0;
1239a0f5a630SManivannan Sadhasivam 
1240a0f5a630SManivannan Sadhasivam 	return mhi_dev;
1241a0f5a630SManivannan Sadhasivam }
1242a0f5a630SManivannan Sadhasivam 
mhi_driver_probe(struct device * dev)1243a0f5a630SManivannan Sadhasivam static int mhi_driver_probe(struct device *dev)
1244a0f5a630SManivannan Sadhasivam {
1245a0f5a630SManivannan Sadhasivam 	struct mhi_device *mhi_dev = to_mhi_device(dev);
1246a0f5a630SManivannan Sadhasivam 	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
1247a0f5a630SManivannan Sadhasivam 	struct device_driver *drv = dev->driver;
1248a0f5a630SManivannan Sadhasivam 	struct mhi_driver *mhi_drv = to_mhi_driver(drv);
1249a0f5a630SManivannan Sadhasivam 	struct mhi_event *mhi_event;
1250a0f5a630SManivannan Sadhasivam 	struct mhi_chan *ul_chan = mhi_dev->ul_chan;
1251a0f5a630SManivannan Sadhasivam 	struct mhi_chan *dl_chan = mhi_dev->dl_chan;
1252a0f5a630SManivannan Sadhasivam 	int ret;
1253a0f5a630SManivannan Sadhasivam 
1254a0f5a630SManivannan Sadhasivam 	/* Bring device out of LPM */
1255a0f5a630SManivannan Sadhasivam 	ret = mhi_device_get_sync(mhi_dev);
1256a0f5a630SManivannan Sadhasivam 	if (ret)
1257a0f5a630SManivannan Sadhasivam 		return ret;
1258a0f5a630SManivannan Sadhasivam 
1259a0f5a630SManivannan Sadhasivam 	ret = -EINVAL;
1260a0f5a630SManivannan Sadhasivam 
1261a0f5a630SManivannan Sadhasivam 	if (ul_chan) {
1262a0f5a630SManivannan Sadhasivam 		/*
1263a0f5a630SManivannan Sadhasivam 		 * If channel supports LPM notifications then status_cb should
1264a0f5a630SManivannan Sadhasivam 		 * be provided
1265a0f5a630SManivannan Sadhasivam 		 */
1266a0f5a630SManivannan Sadhasivam 		if (ul_chan->lpm_notify && !mhi_drv->status_cb)
1267a0f5a630SManivannan Sadhasivam 			goto exit_probe;
1268a0f5a630SManivannan Sadhasivam 
1269a0f5a630SManivannan Sadhasivam 		/* For non-offload channels then xfer_cb should be provided */
1270a0f5a630SManivannan Sadhasivam 		if (!ul_chan->offload_ch && !mhi_drv->ul_xfer_cb)
1271a0f5a630SManivannan Sadhasivam 			goto exit_probe;
1272a0f5a630SManivannan Sadhasivam 
1273a0f5a630SManivannan Sadhasivam 		ul_chan->xfer_cb = mhi_drv->ul_xfer_cb;
1274a0f5a630SManivannan Sadhasivam 	}
1275a0f5a630SManivannan Sadhasivam 
1276a0f5a630SManivannan Sadhasivam 	ret = -EINVAL;
1277a0f5a630SManivannan Sadhasivam 	if (dl_chan) {
1278a0f5a630SManivannan Sadhasivam 		/*
1279a0f5a630SManivannan Sadhasivam 		 * If channel supports LPM notifications then status_cb should
1280a0f5a630SManivannan Sadhasivam 		 * be provided
1281a0f5a630SManivannan Sadhasivam 		 */
1282a0f5a630SManivannan Sadhasivam 		if (dl_chan->lpm_notify && !mhi_drv->status_cb)
1283a0f5a630SManivannan Sadhasivam 			goto exit_probe;
1284a0f5a630SManivannan Sadhasivam 
1285a0f5a630SManivannan Sadhasivam 		/* For non-offload channels then xfer_cb should be provided */
1286a0f5a630SManivannan Sadhasivam 		if (!dl_chan->offload_ch && !mhi_drv->dl_xfer_cb)
1287a0f5a630SManivannan Sadhasivam 			goto exit_probe;
1288a0f5a630SManivannan Sadhasivam 
1289a0f5a630SManivannan Sadhasivam 		mhi_event = &mhi_cntrl->mhi_event[dl_chan->er_index];
1290a0f5a630SManivannan Sadhasivam 
1291a0f5a630SManivannan Sadhasivam 		/*
1292a0f5a630SManivannan Sadhasivam 		 * If the channel event ring is managed by client, then
1293a0f5a630SManivannan Sadhasivam 		 * status_cb must be provided so that the framework can
1294a0f5a630SManivannan Sadhasivam 		 * notify pending data
1295a0f5a630SManivannan Sadhasivam 		 */
1296a0f5a630SManivannan Sadhasivam 		if (mhi_event->cl_manage && !mhi_drv->status_cb)
1297a0f5a630SManivannan Sadhasivam 			goto exit_probe;
1298a0f5a630SManivannan Sadhasivam 
1299a0f5a630SManivannan Sadhasivam 		dl_chan->xfer_cb = mhi_drv->dl_xfer_cb;
1300a0f5a630SManivannan Sadhasivam 	}
1301a0f5a630SManivannan Sadhasivam 
1302a0f5a630SManivannan Sadhasivam 	/* Call the user provided probe function */
1303a0f5a630SManivannan Sadhasivam 	ret = mhi_drv->probe(mhi_dev, mhi_dev->id);
1304a0f5a630SManivannan Sadhasivam 	if (ret)
1305a0f5a630SManivannan Sadhasivam 		goto exit_probe;
1306a0f5a630SManivannan Sadhasivam 
1307a0f5a630SManivannan Sadhasivam 	mhi_device_put(mhi_dev);
1308a0f5a630SManivannan Sadhasivam 
1309a0f5a630SManivannan Sadhasivam 	return ret;
1310a0f5a630SManivannan Sadhasivam 
1311a0f5a630SManivannan Sadhasivam exit_probe:
1312a0f5a630SManivannan Sadhasivam 	mhi_unprepare_from_transfer(mhi_dev);
1313a0f5a630SManivannan Sadhasivam 
1314a0f5a630SManivannan Sadhasivam 	mhi_device_put(mhi_dev);
1315a0f5a630SManivannan Sadhasivam 
1316a0f5a630SManivannan Sadhasivam 	return ret;
1317a0f5a630SManivannan Sadhasivam }
1318a0f5a630SManivannan Sadhasivam 
mhi_driver_remove(struct device * dev)1319a0f5a630SManivannan Sadhasivam static int mhi_driver_remove(struct device *dev)
1320a0f5a630SManivannan Sadhasivam {
1321a0f5a630SManivannan Sadhasivam 	struct mhi_device *mhi_dev = to_mhi_device(dev);
1322a0f5a630SManivannan Sadhasivam 	struct mhi_driver *mhi_drv = to_mhi_driver(dev->driver);
1323a0f5a630SManivannan Sadhasivam 	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
1324a0f5a630SManivannan Sadhasivam 	struct mhi_chan *mhi_chan;
1325a0f5a630SManivannan Sadhasivam 	enum mhi_ch_state ch_state[] = {
1326a0f5a630SManivannan Sadhasivam 		MHI_CH_STATE_DISABLED,
1327a0f5a630SManivannan Sadhasivam 		MHI_CH_STATE_DISABLED
1328a0f5a630SManivannan Sadhasivam 	};
1329a0f5a630SManivannan Sadhasivam 	int dir;
1330a0f5a630SManivannan Sadhasivam 
1331a0f5a630SManivannan Sadhasivam 	/* Skip if it is a controller device */
1332a0f5a630SManivannan Sadhasivam 	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
1333a0f5a630SManivannan Sadhasivam 		return 0;
1334a0f5a630SManivannan Sadhasivam 
1335a0f5a630SManivannan Sadhasivam 	/* Reset both channels */
1336a0f5a630SManivannan Sadhasivam 	for (dir = 0; dir < 2; dir++) {
1337a0f5a630SManivannan Sadhasivam 		mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
1338a0f5a630SManivannan Sadhasivam 
1339a0f5a630SManivannan Sadhasivam 		if (!mhi_chan)
1340a0f5a630SManivannan Sadhasivam 			continue;
1341a0f5a630SManivannan Sadhasivam 
1342a0f5a630SManivannan Sadhasivam 		/* Wake all threads waiting for completion */
1343a0f5a630SManivannan Sadhasivam 		write_lock_irq(&mhi_chan->lock);
1344a0f5a630SManivannan Sadhasivam 		mhi_chan->ccs = MHI_EV_CC_INVALID;
1345a0f5a630SManivannan Sadhasivam 		complete_all(&mhi_chan->completion);
1346a0f5a630SManivannan Sadhasivam 		write_unlock_irq(&mhi_chan->lock);
1347a0f5a630SManivannan Sadhasivam 
1348a0f5a630SManivannan Sadhasivam 		/* Set the channel state to disabled */
1349a0f5a630SManivannan Sadhasivam 		mutex_lock(&mhi_chan->mutex);
1350a0f5a630SManivannan Sadhasivam 		write_lock_irq(&mhi_chan->lock);
1351a0f5a630SManivannan Sadhasivam 		ch_state[dir] = mhi_chan->ch_state;
1352a0f5a630SManivannan Sadhasivam 		mhi_chan->ch_state = MHI_CH_STATE_SUSPENDED;
1353a0f5a630SManivannan Sadhasivam 		write_unlock_irq(&mhi_chan->lock);
1354a0f5a630SManivannan Sadhasivam 
1355a0f5a630SManivannan Sadhasivam 		/* Reset the non-offload channel */
1356a0f5a630SManivannan Sadhasivam 		if (!mhi_chan->offload_ch)
1357a0f5a630SManivannan Sadhasivam 			mhi_reset_chan(mhi_cntrl, mhi_chan);
1358a0f5a630SManivannan Sadhasivam 
1359a0f5a630SManivannan Sadhasivam 		mutex_unlock(&mhi_chan->mutex);
1360a0f5a630SManivannan Sadhasivam 	}
1361a0f5a630SManivannan Sadhasivam 
1362a0f5a630SManivannan Sadhasivam 	mhi_drv->remove(mhi_dev);
1363a0f5a630SManivannan Sadhasivam 
1364a0f5a630SManivannan Sadhasivam 	/* De-init channel if it was enabled */
1365a0f5a630SManivannan Sadhasivam 	for (dir = 0; dir < 2; dir++) {
1366a0f5a630SManivannan Sadhasivam 		mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
1367a0f5a630SManivannan Sadhasivam 
1368a0f5a630SManivannan Sadhasivam 		if (!mhi_chan)
1369a0f5a630SManivannan Sadhasivam 			continue;
1370a0f5a630SManivannan Sadhasivam 
1371a0f5a630SManivannan Sadhasivam 		mutex_lock(&mhi_chan->mutex);
1372a0f5a630SManivannan Sadhasivam 
1373a0f5a630SManivannan Sadhasivam 		if ((ch_state[dir] == MHI_CH_STATE_ENABLED ||
1374a0f5a630SManivannan Sadhasivam 		     ch_state[dir] == MHI_CH_STATE_STOP) &&
1375a0f5a630SManivannan Sadhasivam 		    !mhi_chan->offload_ch)
1376a0f5a630SManivannan Sadhasivam 			mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan);
1377a0f5a630SManivannan Sadhasivam 
1378a0f5a630SManivannan Sadhasivam 		mhi_chan->ch_state = MHI_CH_STATE_DISABLED;
1379a0f5a630SManivannan Sadhasivam 
1380a0f5a630SManivannan Sadhasivam 		mutex_unlock(&mhi_chan->mutex);
1381a0f5a630SManivannan Sadhasivam 	}
1382a0f5a630SManivannan Sadhasivam 
1383a0f5a630SManivannan Sadhasivam 	while (mhi_dev->dev_wake)
1384a0f5a630SManivannan Sadhasivam 		mhi_device_put(mhi_dev);
1385a0f5a630SManivannan Sadhasivam 
1386a0f5a630SManivannan Sadhasivam 	return 0;
1387a0f5a630SManivannan Sadhasivam }
1388a0f5a630SManivannan Sadhasivam 
__mhi_driver_register(struct mhi_driver * mhi_drv,struct module * owner)1389a0f5a630SManivannan Sadhasivam int __mhi_driver_register(struct mhi_driver *mhi_drv, struct module *owner)
1390a0f5a630SManivannan Sadhasivam {
1391a0f5a630SManivannan Sadhasivam 	struct device_driver *driver = &mhi_drv->driver;
1392a0f5a630SManivannan Sadhasivam 
1393a0f5a630SManivannan Sadhasivam 	if (!mhi_drv->probe || !mhi_drv->remove)
1394a0f5a630SManivannan Sadhasivam 		return -EINVAL;
1395a0f5a630SManivannan Sadhasivam 
1396a0f5a630SManivannan Sadhasivam 	driver->bus = &mhi_bus_type;
1397a0f5a630SManivannan Sadhasivam 	driver->owner = owner;
1398a0f5a630SManivannan Sadhasivam 	driver->probe = mhi_driver_probe;
1399a0f5a630SManivannan Sadhasivam 	driver->remove = mhi_driver_remove;
1400a0f5a630SManivannan Sadhasivam 
1401a0f5a630SManivannan Sadhasivam 	return driver_register(driver);
1402a0f5a630SManivannan Sadhasivam }
1403a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(__mhi_driver_register);
1404a0f5a630SManivannan Sadhasivam 
mhi_driver_unregister(struct mhi_driver * mhi_drv)1405a0f5a630SManivannan Sadhasivam void mhi_driver_unregister(struct mhi_driver *mhi_drv)
1406a0f5a630SManivannan Sadhasivam {
1407a0f5a630SManivannan Sadhasivam 	driver_unregister(&mhi_drv->driver);
1408a0f5a630SManivannan Sadhasivam }
1409a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_driver_unregister);
1410a0f5a630SManivannan Sadhasivam 
mhi_uevent(const struct device * dev,struct kobj_uevent_env * env)14112a81ada3SGreg Kroah-Hartman static int mhi_uevent(const struct device *dev, struct kobj_uevent_env *env)
1412a0f5a630SManivannan Sadhasivam {
14132a81ada3SGreg Kroah-Hartman 	const struct mhi_device *mhi_dev = to_mhi_device(dev);
1414a0f5a630SManivannan Sadhasivam 
1415a0f5a630SManivannan Sadhasivam 	return add_uevent_var(env, "MODALIAS=" MHI_DEVICE_MODALIAS_FMT,
1416a0f5a630SManivannan Sadhasivam 					mhi_dev->name);
1417a0f5a630SManivannan Sadhasivam }
1418a0f5a630SManivannan Sadhasivam 
mhi_match(struct device * dev,struct device_driver * drv)1419a0f5a630SManivannan Sadhasivam static int mhi_match(struct device *dev, struct device_driver *drv)
1420a0f5a630SManivannan Sadhasivam {
1421a0f5a630SManivannan Sadhasivam 	struct mhi_device *mhi_dev = to_mhi_device(dev);
1422a0f5a630SManivannan Sadhasivam 	struct mhi_driver *mhi_drv = to_mhi_driver(drv);
1423a0f5a630SManivannan Sadhasivam 	const struct mhi_device_id *id;
1424a0f5a630SManivannan Sadhasivam 
1425a0f5a630SManivannan Sadhasivam 	/*
1426a0f5a630SManivannan Sadhasivam 	 * If the device is a controller type then there is no client driver
1427a0f5a630SManivannan Sadhasivam 	 * associated with it
1428a0f5a630SManivannan Sadhasivam 	 */
1429a0f5a630SManivannan Sadhasivam 	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
1430a0f5a630SManivannan Sadhasivam 		return 0;
1431a0f5a630SManivannan Sadhasivam 
1432a0f5a630SManivannan Sadhasivam 	for (id = mhi_drv->id_table; id->chan[0]; id++)
1433a0f5a630SManivannan Sadhasivam 		if (!strcmp(mhi_dev->name, id->chan)) {
1434a0f5a630SManivannan Sadhasivam 			mhi_dev->id = id;
1435a0f5a630SManivannan Sadhasivam 			return 1;
1436a0f5a630SManivannan Sadhasivam 		}
1437a0f5a630SManivannan Sadhasivam 
1438a0f5a630SManivannan Sadhasivam 	return 0;
1439a0f5a630SManivannan Sadhasivam };
1440a0f5a630SManivannan Sadhasivam 
1441a0f5a630SManivannan Sadhasivam struct bus_type mhi_bus_type = {
1442a0f5a630SManivannan Sadhasivam 	.name = "mhi",
1443a0f5a630SManivannan Sadhasivam 	.dev_name = "mhi",
1444a0f5a630SManivannan Sadhasivam 	.match = mhi_match,
1445a0f5a630SManivannan Sadhasivam 	.uevent = mhi_uevent,
1446a0f5a630SManivannan Sadhasivam 	.dev_groups = mhi_dev_groups,
1447a0f5a630SManivannan Sadhasivam };
1448a0f5a630SManivannan Sadhasivam 
mhi_init(void)1449a0f5a630SManivannan Sadhasivam static int __init mhi_init(void)
1450a0f5a630SManivannan Sadhasivam {
1451a0f5a630SManivannan Sadhasivam 	mhi_debugfs_init();
1452a0f5a630SManivannan Sadhasivam 	return bus_register(&mhi_bus_type);
1453a0f5a630SManivannan Sadhasivam }
1454a0f5a630SManivannan Sadhasivam 
mhi_exit(void)1455a0f5a630SManivannan Sadhasivam static void __exit mhi_exit(void)
1456a0f5a630SManivannan Sadhasivam {
1457a0f5a630SManivannan Sadhasivam 	mhi_debugfs_exit();
1458a0f5a630SManivannan Sadhasivam 	bus_unregister(&mhi_bus_type);
1459a0f5a630SManivannan Sadhasivam }
1460a0f5a630SManivannan Sadhasivam 
1461a0f5a630SManivannan Sadhasivam postcore_initcall(mhi_init);
1462a0f5a630SManivannan Sadhasivam module_exit(mhi_exit);
1463a0f5a630SManivannan Sadhasivam 
1464a0f5a630SManivannan Sadhasivam MODULE_LICENSE("GPL v2");
1465a33ca174SSlark Xiao MODULE_DESCRIPTION("Modem Host Interface");
1466