xref: /OK3568_Linux_fs/kernel/drivers/mailbox/omap-mailbox.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * OMAP mailbox driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved.
6*4882a593Smuzhiyun  * Copyright (C) 2013-2019 Texas Instruments Incorporated - https://www.ti.com
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
9*4882a593Smuzhiyun  *          Suman Anna <s-anna@ti.com>
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/interrupt.h>
13*4882a593Smuzhiyun #include <linux/spinlock.h>
14*4882a593Smuzhiyun #include <linux/mutex.h>
15*4882a593Smuzhiyun #include <linux/slab.h>
16*4882a593Smuzhiyun #include <linux/kfifo.h>
17*4882a593Smuzhiyun #include <linux/err.h>
18*4882a593Smuzhiyun #include <linux/module.h>
19*4882a593Smuzhiyun #include <linux/of_device.h>
20*4882a593Smuzhiyun #include <linux/platform_device.h>
21*4882a593Smuzhiyun #include <linux/pm_runtime.h>
22*4882a593Smuzhiyun #include <linux/omap-mailbox.h>
23*4882a593Smuzhiyun #include <linux/mailbox_controller.h>
24*4882a593Smuzhiyun #include <linux/mailbox_client.h>
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #include "mailbox.h"
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun #define MAILBOX_REVISION		0x000
29*4882a593Smuzhiyun #define MAILBOX_MESSAGE(m)		(0x040 + 4 * (m))
30*4882a593Smuzhiyun #define MAILBOX_FIFOSTATUS(m)		(0x080 + 4 * (m))
31*4882a593Smuzhiyun #define MAILBOX_MSGSTATUS(m)		(0x0c0 + 4 * (m))
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun #define OMAP2_MAILBOX_IRQSTATUS(u)	(0x100 + 8 * (u))
34*4882a593Smuzhiyun #define OMAP2_MAILBOX_IRQENABLE(u)	(0x104 + 8 * (u))
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun #define OMAP4_MAILBOX_IRQSTATUS(u)	(0x104 + 0x10 * (u))
37*4882a593Smuzhiyun #define OMAP4_MAILBOX_IRQENABLE(u)	(0x108 + 0x10 * (u))
38*4882a593Smuzhiyun #define OMAP4_MAILBOX_IRQENABLE_CLR(u)	(0x10c + 0x10 * (u))
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun #define MAILBOX_IRQSTATUS(type, u)	(type ? OMAP4_MAILBOX_IRQSTATUS(u) : \
41*4882a593Smuzhiyun 						OMAP2_MAILBOX_IRQSTATUS(u))
42*4882a593Smuzhiyun #define MAILBOX_IRQENABLE(type, u)	(type ? OMAP4_MAILBOX_IRQENABLE(u) : \
43*4882a593Smuzhiyun 						OMAP2_MAILBOX_IRQENABLE(u))
44*4882a593Smuzhiyun #define MAILBOX_IRQDISABLE(type, u)	(type ? OMAP4_MAILBOX_IRQENABLE_CLR(u) \
45*4882a593Smuzhiyun 						: OMAP2_MAILBOX_IRQENABLE(u))
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun #define MAILBOX_IRQ_NEWMSG(m)		(1 << (2 * (m)))
48*4882a593Smuzhiyun #define MAILBOX_IRQ_NOTFULL(m)		(1 << (2 * (m) + 1))
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun /* Interrupt register configuration types */
51*4882a593Smuzhiyun #define MBOX_INTR_CFG_TYPE1		0
52*4882a593Smuzhiyun #define MBOX_INTR_CFG_TYPE2		1
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun struct omap_mbox_fifo {
55*4882a593Smuzhiyun 	unsigned long msg;
56*4882a593Smuzhiyun 	unsigned long fifo_stat;
57*4882a593Smuzhiyun 	unsigned long msg_stat;
58*4882a593Smuzhiyun 	unsigned long irqenable;
59*4882a593Smuzhiyun 	unsigned long irqstatus;
60*4882a593Smuzhiyun 	unsigned long irqdisable;
61*4882a593Smuzhiyun 	u32 intr_bit;
62*4882a593Smuzhiyun };
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun struct omap_mbox_queue {
65*4882a593Smuzhiyun 	spinlock_t		lock;
66*4882a593Smuzhiyun 	struct kfifo		fifo;
67*4882a593Smuzhiyun 	struct work_struct	work;
68*4882a593Smuzhiyun 	struct omap_mbox	*mbox;
69*4882a593Smuzhiyun 	bool full;
70*4882a593Smuzhiyun };
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun struct omap_mbox_match_data {
73*4882a593Smuzhiyun 	u32 intr_type;
74*4882a593Smuzhiyun };
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun struct omap_mbox_device {
77*4882a593Smuzhiyun 	struct device *dev;
78*4882a593Smuzhiyun 	struct mutex cfg_lock;
79*4882a593Smuzhiyun 	void __iomem *mbox_base;
80*4882a593Smuzhiyun 	u32 *irq_ctx;
81*4882a593Smuzhiyun 	u32 num_users;
82*4882a593Smuzhiyun 	u32 num_fifos;
83*4882a593Smuzhiyun 	u32 intr_type;
84*4882a593Smuzhiyun 	struct omap_mbox **mboxes;
85*4882a593Smuzhiyun 	struct mbox_controller controller;
86*4882a593Smuzhiyun 	struct list_head elem;
87*4882a593Smuzhiyun };
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun struct omap_mbox_fifo_info {
90*4882a593Smuzhiyun 	int tx_id;
91*4882a593Smuzhiyun 	int tx_usr;
92*4882a593Smuzhiyun 	int tx_irq;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	int rx_id;
95*4882a593Smuzhiyun 	int rx_usr;
96*4882a593Smuzhiyun 	int rx_irq;
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	const char *name;
99*4882a593Smuzhiyun 	bool send_no_irq;
100*4882a593Smuzhiyun };
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun struct omap_mbox {
103*4882a593Smuzhiyun 	const char		*name;
104*4882a593Smuzhiyun 	int			irq;
105*4882a593Smuzhiyun 	struct omap_mbox_queue	*rxq;
106*4882a593Smuzhiyun 	struct device		*dev;
107*4882a593Smuzhiyun 	struct omap_mbox_device *parent;
108*4882a593Smuzhiyun 	struct omap_mbox_fifo	tx_fifo;
109*4882a593Smuzhiyun 	struct omap_mbox_fifo	rx_fifo;
110*4882a593Smuzhiyun 	u32			intr_type;
111*4882a593Smuzhiyun 	struct mbox_chan	*chan;
112*4882a593Smuzhiyun 	bool			send_no_irq;
113*4882a593Smuzhiyun };
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun /* global variables for the mailbox devices */
116*4882a593Smuzhiyun static DEFINE_MUTEX(omap_mbox_devices_lock);
117*4882a593Smuzhiyun static LIST_HEAD(omap_mbox_devices);
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun static unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE;
120*4882a593Smuzhiyun module_param(mbox_kfifo_size, uint, S_IRUGO);
121*4882a593Smuzhiyun MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)");
122*4882a593Smuzhiyun 
mbox_chan_to_omap_mbox(struct mbox_chan * chan)123*4882a593Smuzhiyun static struct omap_mbox *mbox_chan_to_omap_mbox(struct mbox_chan *chan)
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun 	if (!chan || !chan->con_priv)
126*4882a593Smuzhiyun 		return NULL;
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	return (struct omap_mbox *)chan->con_priv;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun static inline
mbox_read_reg(struct omap_mbox_device * mdev,size_t ofs)132*4882a593Smuzhiyun unsigned int mbox_read_reg(struct omap_mbox_device *mdev, size_t ofs)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun 	return __raw_readl(mdev->mbox_base + ofs);
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun static inline
mbox_write_reg(struct omap_mbox_device * mdev,u32 val,size_t ofs)138*4882a593Smuzhiyun void mbox_write_reg(struct omap_mbox_device *mdev, u32 val, size_t ofs)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun 	__raw_writel(val, mdev->mbox_base + ofs);
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun /* Mailbox FIFO handle functions */
mbox_fifo_read(struct omap_mbox * mbox)144*4882a593Smuzhiyun static u32 mbox_fifo_read(struct omap_mbox *mbox)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	struct omap_mbox_fifo *fifo = &mbox->rx_fifo;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	return mbox_read_reg(mbox->parent, fifo->msg);
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun 
mbox_fifo_write(struct omap_mbox * mbox,u32 msg)151*4882a593Smuzhiyun static void mbox_fifo_write(struct omap_mbox *mbox, u32 msg)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun 	struct omap_mbox_fifo *fifo = &mbox->tx_fifo;
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	mbox_write_reg(mbox->parent, msg, fifo->msg);
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun 
mbox_fifo_empty(struct omap_mbox * mbox)158*4882a593Smuzhiyun static int mbox_fifo_empty(struct omap_mbox *mbox)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun 	struct omap_mbox_fifo *fifo = &mbox->rx_fifo;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	return (mbox_read_reg(mbox->parent, fifo->msg_stat) == 0);
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun 
mbox_fifo_full(struct omap_mbox * mbox)165*4882a593Smuzhiyun static int mbox_fifo_full(struct omap_mbox *mbox)
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun 	struct omap_mbox_fifo *fifo = &mbox->tx_fifo;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	return mbox_read_reg(mbox->parent, fifo->fifo_stat);
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun /* Mailbox IRQ handle functions */
ack_mbox_irq(struct omap_mbox * mbox,omap_mbox_irq_t irq)173*4882a593Smuzhiyun static void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun 	struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
176*4882a593Smuzhiyun 				&mbox->tx_fifo : &mbox->rx_fifo;
177*4882a593Smuzhiyun 	u32 bit = fifo->intr_bit;
178*4882a593Smuzhiyun 	u32 irqstatus = fifo->irqstatus;
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	mbox_write_reg(mbox->parent, bit, irqstatus);
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	/* Flush posted write for irq status to avoid spurious interrupts */
183*4882a593Smuzhiyun 	mbox_read_reg(mbox->parent, irqstatus);
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun 
is_mbox_irq(struct omap_mbox * mbox,omap_mbox_irq_t irq)186*4882a593Smuzhiyun static int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
187*4882a593Smuzhiyun {
188*4882a593Smuzhiyun 	struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
189*4882a593Smuzhiyun 				&mbox->tx_fifo : &mbox->rx_fifo;
190*4882a593Smuzhiyun 	u32 bit = fifo->intr_bit;
191*4882a593Smuzhiyun 	u32 irqenable = fifo->irqenable;
192*4882a593Smuzhiyun 	u32 irqstatus = fifo->irqstatus;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	u32 enable = mbox_read_reg(mbox->parent, irqenable);
195*4882a593Smuzhiyun 	u32 status = mbox_read_reg(mbox->parent, irqstatus);
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	return (int)(enable & status & bit);
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun 
_omap_mbox_enable_irq(struct omap_mbox * mbox,omap_mbox_irq_t irq)200*4882a593Smuzhiyun static void _omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
201*4882a593Smuzhiyun {
202*4882a593Smuzhiyun 	u32 l;
203*4882a593Smuzhiyun 	struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
204*4882a593Smuzhiyun 				&mbox->tx_fifo : &mbox->rx_fifo;
205*4882a593Smuzhiyun 	u32 bit = fifo->intr_bit;
206*4882a593Smuzhiyun 	u32 irqenable = fifo->irqenable;
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	l = mbox_read_reg(mbox->parent, irqenable);
209*4882a593Smuzhiyun 	l |= bit;
210*4882a593Smuzhiyun 	mbox_write_reg(mbox->parent, l, irqenable);
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun 
_omap_mbox_disable_irq(struct omap_mbox * mbox,omap_mbox_irq_t irq)213*4882a593Smuzhiyun static void _omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
214*4882a593Smuzhiyun {
215*4882a593Smuzhiyun 	struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
216*4882a593Smuzhiyun 				&mbox->tx_fifo : &mbox->rx_fifo;
217*4882a593Smuzhiyun 	u32 bit = fifo->intr_bit;
218*4882a593Smuzhiyun 	u32 irqdisable = fifo->irqdisable;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	/*
221*4882a593Smuzhiyun 	 * Read and update the interrupt configuration register for pre-OMAP4.
222*4882a593Smuzhiyun 	 * OMAP4 and later SoCs have a dedicated interrupt disabling register.
223*4882a593Smuzhiyun 	 */
224*4882a593Smuzhiyun 	if (!mbox->intr_type)
225*4882a593Smuzhiyun 		bit = mbox_read_reg(mbox->parent, irqdisable) & ~bit;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	mbox_write_reg(mbox->parent, bit, irqdisable);
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun 
omap_mbox_enable_irq(struct mbox_chan * chan,omap_mbox_irq_t irq)230*4882a593Smuzhiyun void omap_mbox_enable_irq(struct mbox_chan *chan, omap_mbox_irq_t irq)
231*4882a593Smuzhiyun {
232*4882a593Smuzhiyun 	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	if (WARN_ON(!mbox))
235*4882a593Smuzhiyun 		return;
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	_omap_mbox_enable_irq(mbox, irq);
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun EXPORT_SYMBOL(omap_mbox_enable_irq);
240*4882a593Smuzhiyun 
omap_mbox_disable_irq(struct mbox_chan * chan,omap_mbox_irq_t irq)241*4882a593Smuzhiyun void omap_mbox_disable_irq(struct mbox_chan *chan, omap_mbox_irq_t irq)
242*4882a593Smuzhiyun {
243*4882a593Smuzhiyun 	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	if (WARN_ON(!mbox))
246*4882a593Smuzhiyun 		return;
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	_omap_mbox_disable_irq(mbox, irq);
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun EXPORT_SYMBOL(omap_mbox_disable_irq);
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun /*
253*4882a593Smuzhiyun  * Message receiver(workqueue)
254*4882a593Smuzhiyun  */
mbox_rx_work(struct work_struct * work)255*4882a593Smuzhiyun static void mbox_rx_work(struct work_struct *work)
256*4882a593Smuzhiyun {
257*4882a593Smuzhiyun 	struct omap_mbox_queue *mq =
258*4882a593Smuzhiyun 			container_of(work, struct omap_mbox_queue, work);
259*4882a593Smuzhiyun 	mbox_msg_t data;
260*4882a593Smuzhiyun 	u32 msg;
261*4882a593Smuzhiyun 	int len;
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	while (kfifo_len(&mq->fifo) >= sizeof(msg)) {
264*4882a593Smuzhiyun 		len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
265*4882a593Smuzhiyun 		WARN_ON(len != sizeof(msg));
266*4882a593Smuzhiyun 		data = msg;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 		mbox_chan_received_data(mq->mbox->chan, (void *)data);
269*4882a593Smuzhiyun 		spin_lock_irq(&mq->lock);
270*4882a593Smuzhiyun 		if (mq->full) {
271*4882a593Smuzhiyun 			mq->full = false;
272*4882a593Smuzhiyun 			_omap_mbox_enable_irq(mq->mbox, IRQ_RX);
273*4882a593Smuzhiyun 		}
274*4882a593Smuzhiyun 		spin_unlock_irq(&mq->lock);
275*4882a593Smuzhiyun 	}
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun /*
279*4882a593Smuzhiyun  * Mailbox interrupt handler
280*4882a593Smuzhiyun  */
__mbox_tx_interrupt(struct omap_mbox * mbox)281*4882a593Smuzhiyun static void __mbox_tx_interrupt(struct omap_mbox *mbox)
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun 	_omap_mbox_disable_irq(mbox, IRQ_TX);
284*4882a593Smuzhiyun 	ack_mbox_irq(mbox, IRQ_TX);
285*4882a593Smuzhiyun 	mbox_chan_txdone(mbox->chan, 0);
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun 
__mbox_rx_interrupt(struct omap_mbox * mbox)288*4882a593Smuzhiyun static void __mbox_rx_interrupt(struct omap_mbox *mbox)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun 	struct omap_mbox_queue *mq = mbox->rxq;
291*4882a593Smuzhiyun 	u32 msg;
292*4882a593Smuzhiyun 	int len;
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	while (!mbox_fifo_empty(mbox)) {
295*4882a593Smuzhiyun 		if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) {
296*4882a593Smuzhiyun 			_omap_mbox_disable_irq(mbox, IRQ_RX);
297*4882a593Smuzhiyun 			mq->full = true;
298*4882a593Smuzhiyun 			goto nomem;
299*4882a593Smuzhiyun 		}
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 		msg = mbox_fifo_read(mbox);
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 		len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
304*4882a593Smuzhiyun 		WARN_ON(len != sizeof(msg));
305*4882a593Smuzhiyun 	}
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	/* no more messages in the fifo. clear IRQ source. */
308*4882a593Smuzhiyun 	ack_mbox_irq(mbox, IRQ_RX);
309*4882a593Smuzhiyun nomem:
310*4882a593Smuzhiyun 	schedule_work(&mbox->rxq->work);
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun 
mbox_interrupt(int irq,void * p)313*4882a593Smuzhiyun static irqreturn_t mbox_interrupt(int irq, void *p)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun 	struct omap_mbox *mbox = p;
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	if (is_mbox_irq(mbox, IRQ_TX))
318*4882a593Smuzhiyun 		__mbox_tx_interrupt(mbox);
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	if (is_mbox_irq(mbox, IRQ_RX))
321*4882a593Smuzhiyun 		__mbox_rx_interrupt(mbox);
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	return IRQ_HANDLED;
324*4882a593Smuzhiyun }
325*4882a593Smuzhiyun 
mbox_queue_alloc(struct omap_mbox * mbox,void (* work)(struct work_struct *))326*4882a593Smuzhiyun static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox,
327*4882a593Smuzhiyun 					void (*work)(struct work_struct *))
328*4882a593Smuzhiyun {
329*4882a593Smuzhiyun 	struct omap_mbox_queue *mq;
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	if (!work)
332*4882a593Smuzhiyun 		return NULL;
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	mq = kzalloc(sizeof(*mq), GFP_KERNEL);
335*4882a593Smuzhiyun 	if (!mq)
336*4882a593Smuzhiyun 		return NULL;
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	spin_lock_init(&mq->lock);
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL))
341*4882a593Smuzhiyun 		goto error;
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	INIT_WORK(&mq->work, work);
344*4882a593Smuzhiyun 	return mq;
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun error:
347*4882a593Smuzhiyun 	kfree(mq);
348*4882a593Smuzhiyun 	return NULL;
349*4882a593Smuzhiyun }
350*4882a593Smuzhiyun 
mbox_queue_free(struct omap_mbox_queue * q)351*4882a593Smuzhiyun static void mbox_queue_free(struct omap_mbox_queue *q)
352*4882a593Smuzhiyun {
353*4882a593Smuzhiyun 	kfifo_free(&q->fifo);
354*4882a593Smuzhiyun 	kfree(q);
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun 
omap_mbox_startup(struct omap_mbox * mbox)357*4882a593Smuzhiyun static int omap_mbox_startup(struct omap_mbox *mbox)
358*4882a593Smuzhiyun {
359*4882a593Smuzhiyun 	int ret = 0;
360*4882a593Smuzhiyun 	struct omap_mbox_queue *mq;
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	mq = mbox_queue_alloc(mbox, mbox_rx_work);
363*4882a593Smuzhiyun 	if (!mq)
364*4882a593Smuzhiyun 		return -ENOMEM;
365*4882a593Smuzhiyun 	mbox->rxq = mq;
366*4882a593Smuzhiyun 	mq->mbox = mbox;
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED,
369*4882a593Smuzhiyun 			  mbox->name, mbox);
370*4882a593Smuzhiyun 	if (unlikely(ret)) {
371*4882a593Smuzhiyun 		pr_err("failed to register mailbox interrupt:%d\n", ret);
372*4882a593Smuzhiyun 		goto fail_request_irq;
373*4882a593Smuzhiyun 	}
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	if (mbox->send_no_irq)
376*4882a593Smuzhiyun 		mbox->chan->txdone_method = TXDONE_BY_ACK;
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	_omap_mbox_enable_irq(mbox, IRQ_RX);
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	return 0;
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun fail_request_irq:
383*4882a593Smuzhiyun 	mbox_queue_free(mbox->rxq);
384*4882a593Smuzhiyun 	return ret;
385*4882a593Smuzhiyun }
386*4882a593Smuzhiyun 
omap_mbox_fini(struct omap_mbox * mbox)387*4882a593Smuzhiyun static void omap_mbox_fini(struct omap_mbox *mbox)
388*4882a593Smuzhiyun {
389*4882a593Smuzhiyun 	_omap_mbox_disable_irq(mbox, IRQ_RX);
390*4882a593Smuzhiyun 	free_irq(mbox->irq, mbox);
391*4882a593Smuzhiyun 	flush_work(&mbox->rxq->work);
392*4882a593Smuzhiyun 	mbox_queue_free(mbox->rxq);
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun 
omap_mbox_device_find(struct omap_mbox_device * mdev,const char * mbox_name)395*4882a593Smuzhiyun static struct omap_mbox *omap_mbox_device_find(struct omap_mbox_device *mdev,
396*4882a593Smuzhiyun 					       const char *mbox_name)
397*4882a593Smuzhiyun {
398*4882a593Smuzhiyun 	struct omap_mbox *_mbox, *mbox = NULL;
399*4882a593Smuzhiyun 	struct omap_mbox **mboxes = mdev->mboxes;
400*4882a593Smuzhiyun 	int i;
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	if (!mboxes)
403*4882a593Smuzhiyun 		return NULL;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	for (i = 0; (_mbox = mboxes[i]); i++) {
406*4882a593Smuzhiyun 		if (!strcmp(_mbox->name, mbox_name)) {
407*4882a593Smuzhiyun 			mbox = _mbox;
408*4882a593Smuzhiyun 			break;
409*4882a593Smuzhiyun 		}
410*4882a593Smuzhiyun 	}
411*4882a593Smuzhiyun 	return mbox;
412*4882a593Smuzhiyun }
413*4882a593Smuzhiyun 
omap_mbox_request_channel(struct mbox_client * cl,const char * chan_name)414*4882a593Smuzhiyun struct mbox_chan *omap_mbox_request_channel(struct mbox_client *cl,
415*4882a593Smuzhiyun 					    const char *chan_name)
416*4882a593Smuzhiyun {
417*4882a593Smuzhiyun 	struct device *dev = cl->dev;
418*4882a593Smuzhiyun 	struct omap_mbox *mbox = NULL;
419*4882a593Smuzhiyun 	struct omap_mbox_device *mdev;
420*4882a593Smuzhiyun 	struct mbox_chan *chan;
421*4882a593Smuzhiyun 	unsigned long flags;
422*4882a593Smuzhiyun 	int ret;
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	if (!dev)
425*4882a593Smuzhiyun 		return ERR_PTR(-ENODEV);
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	if (dev->of_node) {
428*4882a593Smuzhiyun 		pr_err("%s: please use mbox_request_channel(), this API is supported only for OMAP non-DT usage\n",
429*4882a593Smuzhiyun 		       __func__);
430*4882a593Smuzhiyun 		return ERR_PTR(-ENODEV);
431*4882a593Smuzhiyun 	}
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	mutex_lock(&omap_mbox_devices_lock);
434*4882a593Smuzhiyun 	list_for_each_entry(mdev, &omap_mbox_devices, elem) {
435*4882a593Smuzhiyun 		mbox = omap_mbox_device_find(mdev, chan_name);
436*4882a593Smuzhiyun 		if (mbox)
437*4882a593Smuzhiyun 			break;
438*4882a593Smuzhiyun 	}
439*4882a593Smuzhiyun 	mutex_unlock(&omap_mbox_devices_lock);
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	if (!mbox || !mbox->chan)
442*4882a593Smuzhiyun 		return ERR_PTR(-ENOENT);
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 	chan = mbox->chan;
445*4882a593Smuzhiyun 	spin_lock_irqsave(&chan->lock, flags);
446*4882a593Smuzhiyun 	chan->msg_free = 0;
447*4882a593Smuzhiyun 	chan->msg_count = 0;
448*4882a593Smuzhiyun 	chan->active_req = NULL;
449*4882a593Smuzhiyun 	chan->cl = cl;
450*4882a593Smuzhiyun 	init_completion(&chan->tx_complete);
451*4882a593Smuzhiyun 	spin_unlock_irqrestore(&chan->lock, flags);
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	ret = chan->mbox->ops->startup(chan);
454*4882a593Smuzhiyun 	if (ret) {
455*4882a593Smuzhiyun 		pr_err("Unable to startup the chan (%d)\n", ret);
456*4882a593Smuzhiyun 		mbox_free_channel(chan);
457*4882a593Smuzhiyun 		chan = ERR_PTR(ret);
458*4882a593Smuzhiyun 	}
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	return chan;
461*4882a593Smuzhiyun }
462*4882a593Smuzhiyun EXPORT_SYMBOL(omap_mbox_request_channel);
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun static struct class omap_mbox_class = { .name = "mbox", };
465*4882a593Smuzhiyun 
omap_mbox_register(struct omap_mbox_device * mdev)466*4882a593Smuzhiyun static int omap_mbox_register(struct omap_mbox_device *mdev)
467*4882a593Smuzhiyun {
468*4882a593Smuzhiyun 	int ret;
469*4882a593Smuzhiyun 	int i;
470*4882a593Smuzhiyun 	struct omap_mbox **mboxes;
471*4882a593Smuzhiyun 
472*4882a593Smuzhiyun 	if (!mdev || !mdev->mboxes)
473*4882a593Smuzhiyun 		return -EINVAL;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	mboxes = mdev->mboxes;
476*4882a593Smuzhiyun 	for (i = 0; mboxes[i]; i++) {
477*4882a593Smuzhiyun 		struct omap_mbox *mbox = mboxes[i];
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun 		mbox->dev = device_create(&omap_mbox_class, mdev->dev,
480*4882a593Smuzhiyun 					0, mbox, "%s", mbox->name);
481*4882a593Smuzhiyun 		if (IS_ERR(mbox->dev)) {
482*4882a593Smuzhiyun 			ret = PTR_ERR(mbox->dev);
483*4882a593Smuzhiyun 			goto err_out;
484*4882a593Smuzhiyun 		}
485*4882a593Smuzhiyun 	}
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun 	mutex_lock(&omap_mbox_devices_lock);
488*4882a593Smuzhiyun 	list_add(&mdev->elem, &omap_mbox_devices);
489*4882a593Smuzhiyun 	mutex_unlock(&omap_mbox_devices_lock);
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	ret = devm_mbox_controller_register(mdev->dev, &mdev->controller);
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun err_out:
494*4882a593Smuzhiyun 	if (ret) {
495*4882a593Smuzhiyun 		while (i--)
496*4882a593Smuzhiyun 			device_unregister(mboxes[i]->dev);
497*4882a593Smuzhiyun 	}
498*4882a593Smuzhiyun 	return ret;
499*4882a593Smuzhiyun }
500*4882a593Smuzhiyun 
omap_mbox_unregister(struct omap_mbox_device * mdev)501*4882a593Smuzhiyun static int omap_mbox_unregister(struct omap_mbox_device *mdev)
502*4882a593Smuzhiyun {
503*4882a593Smuzhiyun 	int i;
504*4882a593Smuzhiyun 	struct omap_mbox **mboxes;
505*4882a593Smuzhiyun 
506*4882a593Smuzhiyun 	if (!mdev || !mdev->mboxes)
507*4882a593Smuzhiyun 		return -EINVAL;
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	mutex_lock(&omap_mbox_devices_lock);
510*4882a593Smuzhiyun 	list_del(&mdev->elem);
511*4882a593Smuzhiyun 	mutex_unlock(&omap_mbox_devices_lock);
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun 	mboxes = mdev->mboxes;
514*4882a593Smuzhiyun 	for (i = 0; mboxes[i]; i++)
515*4882a593Smuzhiyun 		device_unregister(mboxes[i]->dev);
516*4882a593Smuzhiyun 	return 0;
517*4882a593Smuzhiyun }
518*4882a593Smuzhiyun 
omap_mbox_chan_startup(struct mbox_chan * chan)519*4882a593Smuzhiyun static int omap_mbox_chan_startup(struct mbox_chan *chan)
520*4882a593Smuzhiyun {
521*4882a593Smuzhiyun 	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
522*4882a593Smuzhiyun 	struct omap_mbox_device *mdev = mbox->parent;
523*4882a593Smuzhiyun 	int ret = 0;
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	mutex_lock(&mdev->cfg_lock);
526*4882a593Smuzhiyun 	pm_runtime_get_sync(mdev->dev);
527*4882a593Smuzhiyun 	ret = omap_mbox_startup(mbox);
528*4882a593Smuzhiyun 	if (ret)
529*4882a593Smuzhiyun 		pm_runtime_put_sync(mdev->dev);
530*4882a593Smuzhiyun 	mutex_unlock(&mdev->cfg_lock);
531*4882a593Smuzhiyun 	return ret;
532*4882a593Smuzhiyun }
533*4882a593Smuzhiyun 
omap_mbox_chan_shutdown(struct mbox_chan * chan)534*4882a593Smuzhiyun static void omap_mbox_chan_shutdown(struct mbox_chan *chan)
535*4882a593Smuzhiyun {
536*4882a593Smuzhiyun 	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
537*4882a593Smuzhiyun 	struct omap_mbox_device *mdev = mbox->parent;
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	mutex_lock(&mdev->cfg_lock);
540*4882a593Smuzhiyun 	omap_mbox_fini(mbox);
541*4882a593Smuzhiyun 	pm_runtime_put_sync(mdev->dev);
542*4882a593Smuzhiyun 	mutex_unlock(&mdev->cfg_lock);
543*4882a593Smuzhiyun }
544*4882a593Smuzhiyun 
omap_mbox_chan_send_noirq(struct omap_mbox * mbox,u32 msg)545*4882a593Smuzhiyun static int omap_mbox_chan_send_noirq(struct omap_mbox *mbox, u32 msg)
546*4882a593Smuzhiyun {
547*4882a593Smuzhiyun 	int ret = -EBUSY;
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun 	if (!mbox_fifo_full(mbox)) {
550*4882a593Smuzhiyun 		_omap_mbox_enable_irq(mbox, IRQ_RX);
551*4882a593Smuzhiyun 		mbox_fifo_write(mbox, msg);
552*4882a593Smuzhiyun 		ret = 0;
553*4882a593Smuzhiyun 		_omap_mbox_disable_irq(mbox, IRQ_RX);
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 		/* we must read and ack the interrupt directly from here */
556*4882a593Smuzhiyun 		mbox_fifo_read(mbox);
557*4882a593Smuzhiyun 		ack_mbox_irq(mbox, IRQ_RX);
558*4882a593Smuzhiyun 	}
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun 	return ret;
561*4882a593Smuzhiyun }
562*4882a593Smuzhiyun 
omap_mbox_chan_send(struct omap_mbox * mbox,u32 msg)563*4882a593Smuzhiyun static int omap_mbox_chan_send(struct omap_mbox *mbox, u32 msg)
564*4882a593Smuzhiyun {
565*4882a593Smuzhiyun 	int ret = -EBUSY;
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	if (!mbox_fifo_full(mbox)) {
568*4882a593Smuzhiyun 		mbox_fifo_write(mbox, msg);
569*4882a593Smuzhiyun 		ret = 0;
570*4882a593Smuzhiyun 	}
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun 	/* always enable the interrupt */
573*4882a593Smuzhiyun 	_omap_mbox_enable_irq(mbox, IRQ_TX);
574*4882a593Smuzhiyun 	return ret;
575*4882a593Smuzhiyun }
576*4882a593Smuzhiyun 
omap_mbox_chan_send_data(struct mbox_chan * chan,void * data)577*4882a593Smuzhiyun static int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data)
578*4882a593Smuzhiyun {
579*4882a593Smuzhiyun 	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
580*4882a593Smuzhiyun 	int ret;
581*4882a593Smuzhiyun 	u32 msg = omap_mbox_message(data);
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	if (!mbox)
584*4882a593Smuzhiyun 		return -EINVAL;
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 	if (mbox->send_no_irq)
587*4882a593Smuzhiyun 		ret = omap_mbox_chan_send_noirq(mbox, msg);
588*4882a593Smuzhiyun 	else
589*4882a593Smuzhiyun 		ret = omap_mbox_chan_send(mbox, msg);
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 	return ret;
592*4882a593Smuzhiyun }
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun static const struct mbox_chan_ops omap_mbox_chan_ops = {
595*4882a593Smuzhiyun 	.startup        = omap_mbox_chan_startup,
596*4882a593Smuzhiyun 	.send_data      = omap_mbox_chan_send_data,
597*4882a593Smuzhiyun 	.shutdown       = omap_mbox_chan_shutdown,
598*4882a593Smuzhiyun };
599*4882a593Smuzhiyun 
600*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
omap_mbox_suspend(struct device * dev)601*4882a593Smuzhiyun static int omap_mbox_suspend(struct device *dev)
602*4882a593Smuzhiyun {
603*4882a593Smuzhiyun 	struct omap_mbox_device *mdev = dev_get_drvdata(dev);
604*4882a593Smuzhiyun 	u32 usr, fifo, reg;
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	if (pm_runtime_status_suspended(dev))
607*4882a593Smuzhiyun 		return 0;
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun 	for (fifo = 0; fifo < mdev->num_fifos; fifo++) {
610*4882a593Smuzhiyun 		if (mbox_read_reg(mdev, MAILBOX_MSGSTATUS(fifo))) {
611*4882a593Smuzhiyun 			dev_err(mdev->dev, "fifo %d has unexpected unread messages\n",
612*4882a593Smuzhiyun 				fifo);
613*4882a593Smuzhiyun 			return -EBUSY;
614*4882a593Smuzhiyun 		}
615*4882a593Smuzhiyun 	}
616*4882a593Smuzhiyun 
617*4882a593Smuzhiyun 	for (usr = 0; usr < mdev->num_users; usr++) {
618*4882a593Smuzhiyun 		reg = MAILBOX_IRQENABLE(mdev->intr_type, usr);
619*4882a593Smuzhiyun 		mdev->irq_ctx[usr] = mbox_read_reg(mdev, reg);
620*4882a593Smuzhiyun 	}
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun 	return 0;
623*4882a593Smuzhiyun }
624*4882a593Smuzhiyun 
omap_mbox_resume(struct device * dev)625*4882a593Smuzhiyun static int omap_mbox_resume(struct device *dev)
626*4882a593Smuzhiyun {
627*4882a593Smuzhiyun 	struct omap_mbox_device *mdev = dev_get_drvdata(dev);
628*4882a593Smuzhiyun 	u32 usr, reg;
629*4882a593Smuzhiyun 
630*4882a593Smuzhiyun 	if (pm_runtime_status_suspended(dev))
631*4882a593Smuzhiyun 		return 0;
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 	for (usr = 0; usr < mdev->num_users; usr++) {
634*4882a593Smuzhiyun 		reg = MAILBOX_IRQENABLE(mdev->intr_type, usr);
635*4882a593Smuzhiyun 		mbox_write_reg(mdev, mdev->irq_ctx[usr], reg);
636*4882a593Smuzhiyun 	}
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun 	return 0;
639*4882a593Smuzhiyun }
640*4882a593Smuzhiyun #endif
641*4882a593Smuzhiyun 
642*4882a593Smuzhiyun static const struct dev_pm_ops omap_mbox_pm_ops = {
643*4882a593Smuzhiyun 	SET_SYSTEM_SLEEP_PM_OPS(omap_mbox_suspend, omap_mbox_resume)
644*4882a593Smuzhiyun };
645*4882a593Smuzhiyun 
646*4882a593Smuzhiyun static const struct omap_mbox_match_data omap2_data = { MBOX_INTR_CFG_TYPE1 };
647*4882a593Smuzhiyun static const struct omap_mbox_match_data omap4_data = { MBOX_INTR_CFG_TYPE2 };
648*4882a593Smuzhiyun 
649*4882a593Smuzhiyun static const struct of_device_id omap_mailbox_of_match[] = {
650*4882a593Smuzhiyun 	{
651*4882a593Smuzhiyun 		.compatible	= "ti,omap2-mailbox",
652*4882a593Smuzhiyun 		.data		= &omap2_data,
653*4882a593Smuzhiyun 	},
654*4882a593Smuzhiyun 	{
655*4882a593Smuzhiyun 		.compatible	= "ti,omap3-mailbox",
656*4882a593Smuzhiyun 		.data		= &omap2_data,
657*4882a593Smuzhiyun 	},
658*4882a593Smuzhiyun 	{
659*4882a593Smuzhiyun 		.compatible	= "ti,omap4-mailbox",
660*4882a593Smuzhiyun 		.data		= &omap4_data,
661*4882a593Smuzhiyun 	},
662*4882a593Smuzhiyun 	{
663*4882a593Smuzhiyun 		.compatible	= "ti,am654-mailbox",
664*4882a593Smuzhiyun 		.data		= &omap4_data,
665*4882a593Smuzhiyun 	},
666*4882a593Smuzhiyun 	{
667*4882a593Smuzhiyun 		/* end */
668*4882a593Smuzhiyun 	},
669*4882a593Smuzhiyun };
670*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, omap_mailbox_of_match);
671*4882a593Smuzhiyun 
omap_mbox_of_xlate(struct mbox_controller * controller,const struct of_phandle_args * sp)672*4882a593Smuzhiyun static struct mbox_chan *omap_mbox_of_xlate(struct mbox_controller *controller,
673*4882a593Smuzhiyun 					    const struct of_phandle_args *sp)
674*4882a593Smuzhiyun {
675*4882a593Smuzhiyun 	phandle phandle = sp->args[0];
676*4882a593Smuzhiyun 	struct device_node *node;
677*4882a593Smuzhiyun 	struct omap_mbox_device *mdev;
678*4882a593Smuzhiyun 	struct omap_mbox *mbox;
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun 	mdev = container_of(controller, struct omap_mbox_device, controller);
681*4882a593Smuzhiyun 	if (WARN_ON(!mdev))
682*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 	node = of_find_node_by_phandle(phandle);
685*4882a593Smuzhiyun 	if (!node) {
686*4882a593Smuzhiyun 		pr_err("%s: could not find node phandle 0x%x\n",
687*4882a593Smuzhiyun 		       __func__, phandle);
688*4882a593Smuzhiyun 		return ERR_PTR(-ENODEV);
689*4882a593Smuzhiyun 	}
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun 	mbox = omap_mbox_device_find(mdev, node->name);
692*4882a593Smuzhiyun 	of_node_put(node);
693*4882a593Smuzhiyun 	return mbox ? mbox->chan : ERR_PTR(-ENOENT);
694*4882a593Smuzhiyun }
695*4882a593Smuzhiyun 
omap_mbox_probe(struct platform_device * pdev)696*4882a593Smuzhiyun static int omap_mbox_probe(struct platform_device *pdev)
697*4882a593Smuzhiyun {
698*4882a593Smuzhiyun 	struct resource *mem;
699*4882a593Smuzhiyun 	int ret;
700*4882a593Smuzhiyun 	struct mbox_chan *chnls;
701*4882a593Smuzhiyun 	struct omap_mbox **list, *mbox, *mboxblk;
702*4882a593Smuzhiyun 	struct omap_mbox_fifo_info *finfo, *finfoblk;
703*4882a593Smuzhiyun 	struct omap_mbox_device *mdev;
704*4882a593Smuzhiyun 	struct omap_mbox_fifo *fifo;
705*4882a593Smuzhiyun 	struct device_node *node = pdev->dev.of_node;
706*4882a593Smuzhiyun 	struct device_node *child;
707*4882a593Smuzhiyun 	const struct omap_mbox_match_data *match_data;
708*4882a593Smuzhiyun 	u32 intr_type, info_count;
709*4882a593Smuzhiyun 	u32 num_users, num_fifos;
710*4882a593Smuzhiyun 	u32 tmp[3];
711*4882a593Smuzhiyun 	u32 l;
712*4882a593Smuzhiyun 	int i;
713*4882a593Smuzhiyun 
714*4882a593Smuzhiyun 	if (!node) {
715*4882a593Smuzhiyun 		pr_err("%s: only DT-based devices are supported\n", __func__);
716*4882a593Smuzhiyun 		return -ENODEV;
717*4882a593Smuzhiyun 	}
718*4882a593Smuzhiyun 
719*4882a593Smuzhiyun 	match_data = of_device_get_match_data(&pdev->dev);
720*4882a593Smuzhiyun 	if (!match_data)
721*4882a593Smuzhiyun 		return -ENODEV;
722*4882a593Smuzhiyun 	intr_type = match_data->intr_type;
723*4882a593Smuzhiyun 
724*4882a593Smuzhiyun 	if (of_property_read_u32(node, "ti,mbox-num-users", &num_users))
725*4882a593Smuzhiyun 		return -ENODEV;
726*4882a593Smuzhiyun 
727*4882a593Smuzhiyun 	if (of_property_read_u32(node, "ti,mbox-num-fifos", &num_fifos))
728*4882a593Smuzhiyun 		return -ENODEV;
729*4882a593Smuzhiyun 
730*4882a593Smuzhiyun 	info_count = of_get_available_child_count(node);
731*4882a593Smuzhiyun 	if (!info_count) {
732*4882a593Smuzhiyun 		dev_err(&pdev->dev, "no available mbox devices found\n");
733*4882a593Smuzhiyun 		return -ENODEV;
734*4882a593Smuzhiyun 	}
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun 	finfoblk = devm_kcalloc(&pdev->dev, info_count, sizeof(*finfoblk),
737*4882a593Smuzhiyun 				GFP_KERNEL);
738*4882a593Smuzhiyun 	if (!finfoblk)
739*4882a593Smuzhiyun 		return -ENOMEM;
740*4882a593Smuzhiyun 
741*4882a593Smuzhiyun 	finfo = finfoblk;
742*4882a593Smuzhiyun 	child = NULL;
743*4882a593Smuzhiyun 	for (i = 0; i < info_count; i++, finfo++) {
744*4882a593Smuzhiyun 		child = of_get_next_available_child(node, child);
745*4882a593Smuzhiyun 		ret = of_property_read_u32_array(child, "ti,mbox-tx", tmp,
746*4882a593Smuzhiyun 						 ARRAY_SIZE(tmp));
747*4882a593Smuzhiyun 		if (ret)
748*4882a593Smuzhiyun 			return ret;
749*4882a593Smuzhiyun 		finfo->tx_id = tmp[0];
750*4882a593Smuzhiyun 		finfo->tx_irq = tmp[1];
751*4882a593Smuzhiyun 		finfo->tx_usr = tmp[2];
752*4882a593Smuzhiyun 
753*4882a593Smuzhiyun 		ret = of_property_read_u32_array(child, "ti,mbox-rx", tmp,
754*4882a593Smuzhiyun 						 ARRAY_SIZE(tmp));
755*4882a593Smuzhiyun 		if (ret)
756*4882a593Smuzhiyun 			return ret;
757*4882a593Smuzhiyun 		finfo->rx_id = tmp[0];
758*4882a593Smuzhiyun 		finfo->rx_irq = tmp[1];
759*4882a593Smuzhiyun 		finfo->rx_usr = tmp[2];
760*4882a593Smuzhiyun 
761*4882a593Smuzhiyun 		finfo->name = child->name;
762*4882a593Smuzhiyun 
763*4882a593Smuzhiyun 		if (of_find_property(child, "ti,mbox-send-noirq", NULL))
764*4882a593Smuzhiyun 			finfo->send_no_irq = true;
765*4882a593Smuzhiyun 
766*4882a593Smuzhiyun 		if (finfo->tx_id >= num_fifos || finfo->rx_id >= num_fifos ||
767*4882a593Smuzhiyun 		    finfo->tx_usr >= num_users || finfo->rx_usr >= num_users)
768*4882a593Smuzhiyun 			return -EINVAL;
769*4882a593Smuzhiyun 	}
770*4882a593Smuzhiyun 
771*4882a593Smuzhiyun 	mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL);
772*4882a593Smuzhiyun 	if (!mdev)
773*4882a593Smuzhiyun 		return -ENOMEM;
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
776*4882a593Smuzhiyun 	mdev->mbox_base = devm_ioremap_resource(&pdev->dev, mem);
777*4882a593Smuzhiyun 	if (IS_ERR(mdev->mbox_base))
778*4882a593Smuzhiyun 		return PTR_ERR(mdev->mbox_base);
779*4882a593Smuzhiyun 
780*4882a593Smuzhiyun 	mdev->irq_ctx = devm_kcalloc(&pdev->dev, num_users, sizeof(u32),
781*4882a593Smuzhiyun 				     GFP_KERNEL);
782*4882a593Smuzhiyun 	if (!mdev->irq_ctx)
783*4882a593Smuzhiyun 		return -ENOMEM;
784*4882a593Smuzhiyun 
785*4882a593Smuzhiyun 	/* allocate one extra for marking end of list */
786*4882a593Smuzhiyun 	list = devm_kcalloc(&pdev->dev, info_count + 1, sizeof(*list),
787*4882a593Smuzhiyun 			    GFP_KERNEL);
788*4882a593Smuzhiyun 	if (!list)
789*4882a593Smuzhiyun 		return -ENOMEM;
790*4882a593Smuzhiyun 
791*4882a593Smuzhiyun 	chnls = devm_kcalloc(&pdev->dev, info_count + 1, sizeof(*chnls),
792*4882a593Smuzhiyun 			     GFP_KERNEL);
793*4882a593Smuzhiyun 	if (!chnls)
794*4882a593Smuzhiyun 		return -ENOMEM;
795*4882a593Smuzhiyun 
796*4882a593Smuzhiyun 	mboxblk = devm_kcalloc(&pdev->dev, info_count, sizeof(*mbox),
797*4882a593Smuzhiyun 			       GFP_KERNEL);
798*4882a593Smuzhiyun 	if (!mboxblk)
799*4882a593Smuzhiyun 		return -ENOMEM;
800*4882a593Smuzhiyun 
801*4882a593Smuzhiyun 	mbox = mboxblk;
802*4882a593Smuzhiyun 	finfo = finfoblk;
803*4882a593Smuzhiyun 	for (i = 0; i < info_count; i++, finfo++) {
804*4882a593Smuzhiyun 		fifo = &mbox->tx_fifo;
805*4882a593Smuzhiyun 		fifo->msg = MAILBOX_MESSAGE(finfo->tx_id);
806*4882a593Smuzhiyun 		fifo->fifo_stat = MAILBOX_FIFOSTATUS(finfo->tx_id);
807*4882a593Smuzhiyun 		fifo->intr_bit = MAILBOX_IRQ_NOTFULL(finfo->tx_id);
808*4882a593Smuzhiyun 		fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->tx_usr);
809*4882a593Smuzhiyun 		fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->tx_usr);
810*4882a593Smuzhiyun 		fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->tx_usr);
811*4882a593Smuzhiyun 
812*4882a593Smuzhiyun 		fifo = &mbox->rx_fifo;
813*4882a593Smuzhiyun 		fifo->msg = MAILBOX_MESSAGE(finfo->rx_id);
814*4882a593Smuzhiyun 		fifo->msg_stat =  MAILBOX_MSGSTATUS(finfo->rx_id);
815*4882a593Smuzhiyun 		fifo->intr_bit = MAILBOX_IRQ_NEWMSG(finfo->rx_id);
816*4882a593Smuzhiyun 		fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->rx_usr);
817*4882a593Smuzhiyun 		fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->rx_usr);
818*4882a593Smuzhiyun 		fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->rx_usr);
819*4882a593Smuzhiyun 
820*4882a593Smuzhiyun 		mbox->send_no_irq = finfo->send_no_irq;
821*4882a593Smuzhiyun 		mbox->intr_type = intr_type;
822*4882a593Smuzhiyun 
823*4882a593Smuzhiyun 		mbox->parent = mdev;
824*4882a593Smuzhiyun 		mbox->name = finfo->name;
825*4882a593Smuzhiyun 		mbox->irq = platform_get_irq(pdev, finfo->tx_irq);
826*4882a593Smuzhiyun 		if (mbox->irq < 0)
827*4882a593Smuzhiyun 			return mbox->irq;
828*4882a593Smuzhiyun 		mbox->chan = &chnls[i];
829*4882a593Smuzhiyun 		chnls[i].con_priv = mbox;
830*4882a593Smuzhiyun 		list[i] = mbox++;
831*4882a593Smuzhiyun 	}
832*4882a593Smuzhiyun 
833*4882a593Smuzhiyun 	mutex_init(&mdev->cfg_lock);
834*4882a593Smuzhiyun 	mdev->dev = &pdev->dev;
835*4882a593Smuzhiyun 	mdev->num_users = num_users;
836*4882a593Smuzhiyun 	mdev->num_fifos = num_fifos;
837*4882a593Smuzhiyun 	mdev->intr_type = intr_type;
838*4882a593Smuzhiyun 	mdev->mboxes = list;
839*4882a593Smuzhiyun 
840*4882a593Smuzhiyun 	/*
841*4882a593Smuzhiyun 	 * OMAP/K3 Mailbox IP does not have a Tx-Done IRQ, but rather a Tx-Ready
842*4882a593Smuzhiyun 	 * IRQ and is needed to run the Tx state machine
843*4882a593Smuzhiyun 	 */
844*4882a593Smuzhiyun 	mdev->controller.txdone_irq = true;
845*4882a593Smuzhiyun 	mdev->controller.dev = mdev->dev;
846*4882a593Smuzhiyun 	mdev->controller.ops = &omap_mbox_chan_ops;
847*4882a593Smuzhiyun 	mdev->controller.chans = chnls;
848*4882a593Smuzhiyun 	mdev->controller.num_chans = info_count;
849*4882a593Smuzhiyun 	mdev->controller.of_xlate = omap_mbox_of_xlate;
850*4882a593Smuzhiyun 	ret = omap_mbox_register(mdev);
851*4882a593Smuzhiyun 	if (ret)
852*4882a593Smuzhiyun 		return ret;
853*4882a593Smuzhiyun 
854*4882a593Smuzhiyun 	platform_set_drvdata(pdev, mdev);
855*4882a593Smuzhiyun 	pm_runtime_enable(mdev->dev);
856*4882a593Smuzhiyun 
857*4882a593Smuzhiyun 	ret = pm_runtime_get_sync(mdev->dev);
858*4882a593Smuzhiyun 	if (ret < 0) {
859*4882a593Smuzhiyun 		pm_runtime_put_noidle(mdev->dev);
860*4882a593Smuzhiyun 		goto unregister;
861*4882a593Smuzhiyun 	}
862*4882a593Smuzhiyun 
863*4882a593Smuzhiyun 	/*
864*4882a593Smuzhiyun 	 * just print the raw revision register, the format is not
865*4882a593Smuzhiyun 	 * uniform across all SoCs
866*4882a593Smuzhiyun 	 */
867*4882a593Smuzhiyun 	l = mbox_read_reg(mdev, MAILBOX_REVISION);
868*4882a593Smuzhiyun 	dev_info(mdev->dev, "omap mailbox rev 0x%x\n", l);
869*4882a593Smuzhiyun 
870*4882a593Smuzhiyun 	ret = pm_runtime_put_sync(mdev->dev);
871*4882a593Smuzhiyun 	if (ret < 0 && ret != -ENOSYS)
872*4882a593Smuzhiyun 		goto unregister;
873*4882a593Smuzhiyun 
874*4882a593Smuzhiyun 	devm_kfree(&pdev->dev, finfoblk);
875*4882a593Smuzhiyun 	return 0;
876*4882a593Smuzhiyun 
877*4882a593Smuzhiyun unregister:
878*4882a593Smuzhiyun 	pm_runtime_disable(mdev->dev);
879*4882a593Smuzhiyun 	omap_mbox_unregister(mdev);
880*4882a593Smuzhiyun 	return ret;
881*4882a593Smuzhiyun }
882*4882a593Smuzhiyun 
omap_mbox_remove(struct platform_device * pdev)883*4882a593Smuzhiyun static int omap_mbox_remove(struct platform_device *pdev)
884*4882a593Smuzhiyun {
885*4882a593Smuzhiyun 	struct omap_mbox_device *mdev = platform_get_drvdata(pdev);
886*4882a593Smuzhiyun 
887*4882a593Smuzhiyun 	pm_runtime_disable(mdev->dev);
888*4882a593Smuzhiyun 	omap_mbox_unregister(mdev);
889*4882a593Smuzhiyun 
890*4882a593Smuzhiyun 	return 0;
891*4882a593Smuzhiyun }
892*4882a593Smuzhiyun 
893*4882a593Smuzhiyun static struct platform_driver omap_mbox_driver = {
894*4882a593Smuzhiyun 	.probe	= omap_mbox_probe,
895*4882a593Smuzhiyun 	.remove	= omap_mbox_remove,
896*4882a593Smuzhiyun 	.driver	= {
897*4882a593Smuzhiyun 		.name = "omap-mailbox",
898*4882a593Smuzhiyun 		.pm = &omap_mbox_pm_ops,
899*4882a593Smuzhiyun 		.of_match_table = of_match_ptr(omap_mailbox_of_match),
900*4882a593Smuzhiyun 	},
901*4882a593Smuzhiyun };
902*4882a593Smuzhiyun 
omap_mbox_init(void)903*4882a593Smuzhiyun static int __init omap_mbox_init(void)
904*4882a593Smuzhiyun {
905*4882a593Smuzhiyun 	int err;
906*4882a593Smuzhiyun 
907*4882a593Smuzhiyun 	err = class_register(&omap_mbox_class);
908*4882a593Smuzhiyun 	if (err)
909*4882a593Smuzhiyun 		return err;
910*4882a593Smuzhiyun 
911*4882a593Smuzhiyun 	/* kfifo size sanity check: alignment and minimal size */
912*4882a593Smuzhiyun 	mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(u32));
913*4882a593Smuzhiyun 	mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size, sizeof(u32));
914*4882a593Smuzhiyun 
915*4882a593Smuzhiyun 	err = platform_driver_register(&omap_mbox_driver);
916*4882a593Smuzhiyun 	if (err)
917*4882a593Smuzhiyun 		class_unregister(&omap_mbox_class);
918*4882a593Smuzhiyun 
919*4882a593Smuzhiyun 	return err;
920*4882a593Smuzhiyun }
921*4882a593Smuzhiyun subsys_initcall(omap_mbox_init);
922*4882a593Smuzhiyun 
omap_mbox_exit(void)923*4882a593Smuzhiyun static void __exit omap_mbox_exit(void)
924*4882a593Smuzhiyun {
925*4882a593Smuzhiyun 	platform_driver_unregister(&omap_mbox_driver);
926*4882a593Smuzhiyun 	class_unregister(&omap_mbox_class);
927*4882a593Smuzhiyun }
928*4882a593Smuzhiyun module_exit(omap_mbox_exit);
929*4882a593Smuzhiyun 
930*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
931*4882a593Smuzhiyun MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging");
932*4882a593Smuzhiyun MODULE_AUTHOR("Toshihiro Kobayashi");
933*4882a593Smuzhiyun MODULE_AUTHOR("Hiroshi DOYU");
934