xref: /OK3568_Linux_fs/kernel/drivers/mailbox/arm_mhu_db.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2013-2015 Fujitsu Semiconductor Ltd.
4*4882a593Smuzhiyun  * Copyright (C) 2015 Linaro Ltd.
5*4882a593Smuzhiyun  * Based on ARM MHU driver by Jassi Brar <jaswinder.singh@linaro.org>
6*4882a593Smuzhiyun  * Copyright (C) 2020 ARM Ltd.
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <linux/amba/bus.h>
10*4882a593Smuzhiyun #include <linux/device.h>
11*4882a593Smuzhiyun #include <linux/err.h>
12*4882a593Smuzhiyun #include <linux/interrupt.h>
13*4882a593Smuzhiyun #include <linux/io.h>
14*4882a593Smuzhiyun #include <linux/kernel.h>
15*4882a593Smuzhiyun #include <linux/mailbox_controller.h>
16*4882a593Smuzhiyun #include <linux/module.h>
17*4882a593Smuzhiyun #include <linux/of.h>
18*4882a593Smuzhiyun #include <linux/of_device.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #define INTR_STAT_OFS	0x0
21*4882a593Smuzhiyun #define INTR_SET_OFS	0x8
22*4882a593Smuzhiyun #define INTR_CLR_OFS	0x10
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #define MHU_LP_OFFSET	0x0
25*4882a593Smuzhiyun #define MHU_HP_OFFSET	0x20
26*4882a593Smuzhiyun #define MHU_SEC_OFFSET	0x200
27*4882a593Smuzhiyun #define TX_REG_OFFSET	0x100
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #define MHU_CHANS	3	/* Secure, Non-Secure High and Low Priority */
30*4882a593Smuzhiyun #define MHU_CHAN_MAX	20	/* Max channels to save on unused RAM */
31*4882a593Smuzhiyun #define MHU_NUM_DOORBELLS	32
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun struct mhu_db_link {
34*4882a593Smuzhiyun 	unsigned int irq;
35*4882a593Smuzhiyun 	void __iomem *tx_reg;
36*4882a593Smuzhiyun 	void __iomem *rx_reg;
37*4882a593Smuzhiyun };
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun struct arm_mhu {
40*4882a593Smuzhiyun 	void __iomem *base;
41*4882a593Smuzhiyun 	struct mhu_db_link mlink[MHU_CHANS];
42*4882a593Smuzhiyun 	struct mbox_controller mbox;
43*4882a593Smuzhiyun 	struct device *dev;
44*4882a593Smuzhiyun };
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun /**
47*4882a593Smuzhiyun  * ARM MHU Mailbox allocated channel information
48*4882a593Smuzhiyun  *
49*4882a593Smuzhiyun  * @mhu: Pointer to parent mailbox device
50*4882a593Smuzhiyun  * @pchan: Physical channel within which this doorbell resides in
51*4882a593Smuzhiyun  * @doorbell: doorbell number pertaining to this channel
52*4882a593Smuzhiyun  */
53*4882a593Smuzhiyun struct mhu_db_channel {
54*4882a593Smuzhiyun 	struct arm_mhu *mhu;
55*4882a593Smuzhiyun 	unsigned int pchan;
56*4882a593Smuzhiyun 	unsigned int doorbell;
57*4882a593Smuzhiyun };
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun static inline struct mbox_chan *
mhu_db_mbox_to_channel(struct mbox_controller * mbox,unsigned int pchan,unsigned int doorbell)60*4882a593Smuzhiyun mhu_db_mbox_to_channel(struct mbox_controller *mbox, unsigned int pchan,
61*4882a593Smuzhiyun 		       unsigned int doorbell)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun 	int i;
64*4882a593Smuzhiyun 	struct mhu_db_channel *chan_info;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	for (i = 0; i < mbox->num_chans; i++) {
67*4882a593Smuzhiyun 		chan_info = mbox->chans[i].con_priv;
68*4882a593Smuzhiyun 		if (chan_info && chan_info->pchan == pchan &&
69*4882a593Smuzhiyun 		    chan_info->doorbell == doorbell)
70*4882a593Smuzhiyun 			return &mbox->chans[i];
71*4882a593Smuzhiyun 	}
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	return NULL;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun 
mhu_db_mbox_clear_irq(struct mbox_chan * chan)76*4882a593Smuzhiyun static void mhu_db_mbox_clear_irq(struct mbox_chan *chan)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun 	struct mhu_db_channel *chan_info = chan->con_priv;
79*4882a593Smuzhiyun 	void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].rx_reg;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	writel_relaxed(BIT(chan_info->doorbell), base + INTR_CLR_OFS);
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun 
mhu_db_mbox_irq_to_pchan_num(struct arm_mhu * mhu,int irq)84*4882a593Smuzhiyun static unsigned int mhu_db_mbox_irq_to_pchan_num(struct arm_mhu *mhu, int irq)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun 	unsigned int pchan;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	for (pchan = 0; pchan < MHU_CHANS; pchan++)
89*4882a593Smuzhiyun 		if (mhu->mlink[pchan].irq == irq)
90*4882a593Smuzhiyun 			break;
91*4882a593Smuzhiyun 	return pchan;
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun static struct mbox_chan *
mhu_db_mbox_irq_to_channel(struct arm_mhu * mhu,unsigned int pchan)95*4882a593Smuzhiyun mhu_db_mbox_irq_to_channel(struct arm_mhu *mhu, unsigned int pchan)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun 	unsigned long bits;
98*4882a593Smuzhiyun 	unsigned int doorbell;
99*4882a593Smuzhiyun 	struct mbox_chan *chan = NULL;
100*4882a593Smuzhiyun 	struct mbox_controller *mbox = &mhu->mbox;
101*4882a593Smuzhiyun 	void __iomem *base = mhu->mlink[pchan].rx_reg;
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	bits = readl_relaxed(base + INTR_STAT_OFS);
104*4882a593Smuzhiyun 	if (!bits)
105*4882a593Smuzhiyun 		/* No IRQs fired in specified physical channel */
106*4882a593Smuzhiyun 		return NULL;
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	/* An IRQ has fired, find the associated channel */
109*4882a593Smuzhiyun 	for (doorbell = 0; bits; doorbell++) {
110*4882a593Smuzhiyun 		if (!test_and_clear_bit(doorbell, &bits))
111*4882a593Smuzhiyun 			continue;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 		chan = mhu_db_mbox_to_channel(mbox, pchan, doorbell);
114*4882a593Smuzhiyun 		if (chan)
115*4882a593Smuzhiyun 			break;
116*4882a593Smuzhiyun 		dev_err(mbox->dev,
117*4882a593Smuzhiyun 			"Channel not registered: pchan: %d doorbell: %d\n",
118*4882a593Smuzhiyun 			pchan, doorbell);
119*4882a593Smuzhiyun 	}
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	return chan;
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun 
mhu_db_mbox_rx_handler(int irq,void * data)124*4882a593Smuzhiyun static irqreturn_t mhu_db_mbox_rx_handler(int irq, void *data)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun 	struct mbox_chan *chan;
127*4882a593Smuzhiyun 	struct arm_mhu *mhu = data;
128*4882a593Smuzhiyun 	unsigned int pchan = mhu_db_mbox_irq_to_pchan_num(mhu, irq);
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	while (NULL != (chan = mhu_db_mbox_irq_to_channel(mhu, pchan))) {
131*4882a593Smuzhiyun 		mbox_chan_received_data(chan, NULL);
132*4882a593Smuzhiyun 		mhu_db_mbox_clear_irq(chan);
133*4882a593Smuzhiyun 	}
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	return IRQ_HANDLED;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun 
mhu_db_last_tx_done(struct mbox_chan * chan)138*4882a593Smuzhiyun static bool mhu_db_last_tx_done(struct mbox_chan *chan)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun 	struct mhu_db_channel *chan_info = chan->con_priv;
141*4882a593Smuzhiyun 	void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].tx_reg;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	if (readl_relaxed(base + INTR_STAT_OFS) & BIT(chan_info->doorbell))
144*4882a593Smuzhiyun 		return false;
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	return true;
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun 
mhu_db_send_data(struct mbox_chan * chan,void * data)149*4882a593Smuzhiyun static int mhu_db_send_data(struct mbox_chan *chan, void *data)
150*4882a593Smuzhiyun {
151*4882a593Smuzhiyun 	struct mhu_db_channel *chan_info = chan->con_priv;
152*4882a593Smuzhiyun 	void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].tx_reg;
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	/* Send event to co-processor */
155*4882a593Smuzhiyun 	writel_relaxed(BIT(chan_info->doorbell), base + INTR_SET_OFS);
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 	return 0;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun 
mhu_db_startup(struct mbox_chan * chan)160*4882a593Smuzhiyun static int mhu_db_startup(struct mbox_chan *chan)
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun 	mhu_db_mbox_clear_irq(chan);
163*4882a593Smuzhiyun 	return 0;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun 
mhu_db_shutdown(struct mbox_chan * chan)166*4882a593Smuzhiyun static void mhu_db_shutdown(struct mbox_chan *chan)
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun 	struct mhu_db_channel *chan_info = chan->con_priv;
169*4882a593Smuzhiyun 	struct mbox_controller *mbox = &chan_info->mhu->mbox;
170*4882a593Smuzhiyun 	int i;
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	for (i = 0; i < mbox->num_chans; i++)
173*4882a593Smuzhiyun 		if (chan == &mbox->chans[i])
174*4882a593Smuzhiyun 			break;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	if (mbox->num_chans == i) {
177*4882a593Smuzhiyun 		dev_warn(mbox->dev, "Request to free non-existent channel\n");
178*4882a593Smuzhiyun 		return;
179*4882a593Smuzhiyun 	}
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	/* Reset channel */
182*4882a593Smuzhiyun 	mhu_db_mbox_clear_irq(chan);
183*4882a593Smuzhiyun 	devm_kfree(mbox->dev, chan->con_priv);
184*4882a593Smuzhiyun 	chan->con_priv = NULL;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun 
mhu_db_mbox_xlate(struct mbox_controller * mbox,const struct of_phandle_args * spec)187*4882a593Smuzhiyun static struct mbox_chan *mhu_db_mbox_xlate(struct mbox_controller *mbox,
188*4882a593Smuzhiyun 					   const struct of_phandle_args *spec)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun 	struct arm_mhu *mhu = dev_get_drvdata(mbox->dev);
191*4882a593Smuzhiyun 	struct mhu_db_channel *chan_info;
192*4882a593Smuzhiyun 	struct mbox_chan *chan;
193*4882a593Smuzhiyun 	unsigned int pchan = spec->args[0];
194*4882a593Smuzhiyun 	unsigned int doorbell = spec->args[1];
195*4882a593Smuzhiyun 	int i;
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	/* Bounds checking */
198*4882a593Smuzhiyun 	if (pchan >= MHU_CHANS || doorbell >= MHU_NUM_DOORBELLS) {
199*4882a593Smuzhiyun 		dev_err(mbox->dev,
200*4882a593Smuzhiyun 			"Invalid channel requested pchan: %d doorbell: %d\n",
201*4882a593Smuzhiyun 			pchan, doorbell);
202*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
203*4882a593Smuzhiyun 	}
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	/* Is requested channel free? */
206*4882a593Smuzhiyun 	chan = mhu_db_mbox_to_channel(mbox, pchan, doorbell);
207*4882a593Smuzhiyun 	if (chan) {
208*4882a593Smuzhiyun 		dev_err(mbox->dev, "Channel in use: pchan: %d doorbell: %d\n",
209*4882a593Smuzhiyun 			pchan, doorbell);
210*4882a593Smuzhiyun 		return ERR_PTR(-EBUSY);
211*4882a593Smuzhiyun 	}
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	/* Find the first free slot */
214*4882a593Smuzhiyun 	for (i = 0; i < mbox->num_chans; i++)
215*4882a593Smuzhiyun 		if (!mbox->chans[i].con_priv)
216*4882a593Smuzhiyun 			break;
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	if (mbox->num_chans == i) {
219*4882a593Smuzhiyun 		dev_err(mbox->dev, "No free channels left\n");
220*4882a593Smuzhiyun 		return ERR_PTR(-EBUSY);
221*4882a593Smuzhiyun 	}
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	chan = &mbox->chans[i];
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	chan_info = devm_kzalloc(mbox->dev, sizeof(*chan_info), GFP_KERNEL);
226*4882a593Smuzhiyun 	if (!chan_info)
227*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	chan_info->mhu = mhu;
230*4882a593Smuzhiyun 	chan_info->pchan = pchan;
231*4882a593Smuzhiyun 	chan_info->doorbell = doorbell;
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	chan->con_priv = chan_info;
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	dev_dbg(mbox->dev, "mbox: created channel phys: %d doorbell: %d\n",
236*4882a593Smuzhiyun 		pchan, doorbell);
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	return chan;
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun static const struct mbox_chan_ops mhu_db_ops = {
242*4882a593Smuzhiyun 	.send_data = mhu_db_send_data,
243*4882a593Smuzhiyun 	.startup = mhu_db_startup,
244*4882a593Smuzhiyun 	.shutdown = mhu_db_shutdown,
245*4882a593Smuzhiyun 	.last_tx_done = mhu_db_last_tx_done,
246*4882a593Smuzhiyun };
247*4882a593Smuzhiyun 
mhu_db_probe(struct amba_device * adev,const struct amba_id * id)248*4882a593Smuzhiyun static int mhu_db_probe(struct amba_device *adev, const struct amba_id *id)
249*4882a593Smuzhiyun {
250*4882a593Smuzhiyun 	u32 cell_count;
251*4882a593Smuzhiyun 	int i, err, max_chans;
252*4882a593Smuzhiyun 	struct arm_mhu *mhu;
253*4882a593Smuzhiyun 	struct mbox_chan *chans;
254*4882a593Smuzhiyun 	struct device *dev = &adev->dev;
255*4882a593Smuzhiyun 	struct device_node *np = dev->of_node;
256*4882a593Smuzhiyun 	int mhu_reg[MHU_CHANS] = {
257*4882a593Smuzhiyun 		MHU_LP_OFFSET, MHU_HP_OFFSET, MHU_SEC_OFFSET,
258*4882a593Smuzhiyun 	};
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	if (!of_device_is_compatible(np, "arm,mhu-doorbell"))
261*4882a593Smuzhiyun 		return -ENODEV;
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	err = of_property_read_u32(np, "#mbox-cells", &cell_count);
264*4882a593Smuzhiyun 	if (err) {
265*4882a593Smuzhiyun 		dev_err(dev, "failed to read #mbox-cells in '%pOF'\n", np);
266*4882a593Smuzhiyun 		return err;
267*4882a593Smuzhiyun 	}
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	if (cell_count == 2) {
270*4882a593Smuzhiyun 		max_chans = MHU_CHAN_MAX;
271*4882a593Smuzhiyun 	} else {
272*4882a593Smuzhiyun 		dev_err(dev, "incorrect value of #mbox-cells in '%pOF'\n", np);
273*4882a593Smuzhiyun 		return -EINVAL;
274*4882a593Smuzhiyun 	}
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL);
277*4882a593Smuzhiyun 	if (!mhu)
278*4882a593Smuzhiyun 		return -ENOMEM;
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	mhu->base = devm_ioremap_resource(dev, &adev->res);
281*4882a593Smuzhiyun 	if (IS_ERR(mhu->base)) {
282*4882a593Smuzhiyun 		dev_err(dev, "ioremap failed\n");
283*4882a593Smuzhiyun 		return PTR_ERR(mhu->base);
284*4882a593Smuzhiyun 	}
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	chans = devm_kcalloc(dev, max_chans, sizeof(*chans), GFP_KERNEL);
287*4882a593Smuzhiyun 	if (!chans)
288*4882a593Smuzhiyun 		return -ENOMEM;
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	mhu->dev = dev;
291*4882a593Smuzhiyun 	mhu->mbox.dev = dev;
292*4882a593Smuzhiyun 	mhu->mbox.chans = chans;
293*4882a593Smuzhiyun 	mhu->mbox.num_chans = max_chans;
294*4882a593Smuzhiyun 	mhu->mbox.txdone_irq = false;
295*4882a593Smuzhiyun 	mhu->mbox.txdone_poll = true;
296*4882a593Smuzhiyun 	mhu->mbox.txpoll_period = 1;
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	mhu->mbox.of_xlate = mhu_db_mbox_xlate;
299*4882a593Smuzhiyun 	amba_set_drvdata(adev, mhu);
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	mhu->mbox.ops = &mhu_db_ops;
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 	err = devm_mbox_controller_register(dev, &mhu->mbox);
304*4882a593Smuzhiyun 	if (err) {
305*4882a593Smuzhiyun 		dev_err(dev, "Failed to register mailboxes %d\n", err);
306*4882a593Smuzhiyun 		return err;
307*4882a593Smuzhiyun 	}
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	for (i = 0; i < MHU_CHANS; i++) {
310*4882a593Smuzhiyun 		int irq = mhu->mlink[i].irq = adev->irq[i];
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 		if (irq <= 0) {
313*4882a593Smuzhiyun 			dev_dbg(dev, "No IRQ found for Channel %d\n", i);
314*4882a593Smuzhiyun 			continue;
315*4882a593Smuzhiyun 		}
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 		mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];
318*4882a593Smuzhiyun 		mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + TX_REG_OFFSET;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 		err = devm_request_threaded_irq(dev, irq, NULL,
321*4882a593Smuzhiyun 						mhu_db_mbox_rx_handler,
322*4882a593Smuzhiyun 						IRQF_ONESHOT, "mhu_db_link", mhu);
323*4882a593Smuzhiyun 		if (err) {
324*4882a593Smuzhiyun 			dev_err(dev, "Can't claim IRQ %d\n", irq);
325*4882a593Smuzhiyun 			mbox_controller_unregister(&mhu->mbox);
326*4882a593Smuzhiyun 			return err;
327*4882a593Smuzhiyun 		}
328*4882a593Smuzhiyun 	}
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	dev_info(dev, "ARM MHU Doorbell mailbox registered\n");
331*4882a593Smuzhiyun 	return 0;
332*4882a593Smuzhiyun }
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun static struct amba_id mhu_ids[] = {
335*4882a593Smuzhiyun 	{
336*4882a593Smuzhiyun 		.id	= 0x1bb098,
337*4882a593Smuzhiyun 		.mask	= 0xffffff,
338*4882a593Smuzhiyun 	},
339*4882a593Smuzhiyun 	{ 0, 0 },
340*4882a593Smuzhiyun };
341*4882a593Smuzhiyun MODULE_DEVICE_TABLE(amba, mhu_ids);
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun static struct amba_driver arm_mhu_db_driver = {
344*4882a593Smuzhiyun 	.drv = {
345*4882a593Smuzhiyun 		.name	= "mhu-doorbell",
346*4882a593Smuzhiyun 	},
347*4882a593Smuzhiyun 	.id_table	= mhu_ids,
348*4882a593Smuzhiyun 	.probe		= mhu_db_probe,
349*4882a593Smuzhiyun };
350*4882a593Smuzhiyun module_amba_driver(arm_mhu_db_driver);
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
353*4882a593Smuzhiyun MODULE_DESCRIPTION("ARM MHU Doorbell Driver");
354*4882a593Smuzhiyun MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
355