File Index
Files and directories
Sign in
FILE

bcm74110-mailbox.c

File preview with syntax highlighting.

linux-6.19.14/drivers/mailbox/bcm74110-mailbox.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Broadcom BCM74110 Mailbox Driver
  4 *
  5 * Copyright (c) 2025 Broadcom
  6 */
  7#include <linux/list.h>
  8#include <linux/types.h>
  9#include <linux/workqueue.h>
 10#include <linux/io-64-nonatomic-hi-lo.h>
 11#include <linux/interrupt.h>
 12#include <linux/module.h>
 13#include <linux/platform_device.h>
 14#include <linux/of.h>
 15#include <linux/delay.h>
 16#include <linux/mailbox_controller.h>
 17#include <linux/bitfield.h>
 18#include <linux/slab.h>
 19
 20#define BCM_MBOX_BASE(sel)		((sel) * 0x40)
 21#define BCM_MBOX_IRQ_BASE(sel)		(((sel) * 0x20) + 0x800)
 22
 23#define BCM_MBOX_CFGA			0x0
 24#define BCM_MBOX_CFGB			0x4
 25#define BCM_MBOX_CFGC			0x8
 26#define BCM_MBOX_CFGD			0xc
 27#define BCM_MBOX_CTRL			0x10
 28#define  BCM_MBOX_CTRL_EN		BIT(0)
 29#define  BCM_MBOX_CTRL_CLR		BIT(1)
 30#define BCM_MBOX_STATUS0		0x14
 31#define  BCM_MBOX_STATUS0_NOT_EMPTY	BIT(28)
 32#define  BCM_MBOX_STATUS0_FULL		BIT(29)
 33#define BCM_MBOX_STATUS1		0x18
 34#define BCM_MBOX_STATUS2		0x1c
 35#define BCM_MBOX_WDATA			0x20
 36#define BCM_MBOX_RDATA			0x28
 37
 38#define BCM_MBOX_IRQ_STATUS		0x0
 39#define BCM_MBOX_IRQ_SET		0x4
 40#define BCM_MBOX_IRQ_CLEAR		0x8
 41#define BCM_MBOX_IRQ_MASK_STATUS	0xc
 42#define BCM_MBOX_IRQ_MASK_SET		0x10
 43#define BCM_MBOX_IRQ_MASK_CLEAR		0x14
 44#define  BCM_MBOX_IRQ_TIMEOUT		BIT(0)
 45#define  BCM_MBOX_IRQ_NOT_EMPTY		BIT(1)
 46#define  BCM_MBOX_IRQ_FULL		BIT(2)
 47#define  BCM_MBOX_IRQ_LOW_WM		BIT(3)
 48#define  BCM_MBOX_IRQ_HIGH_WM		BIT(4)
 49
 50#define BCM_LINK_CODE0			0xbe0
 51#define BCM_LINK_CODE1			0xbe1
 52#define BCM_LINK_CODE2			0xbe2
 53
 54enum {
 55	BCM_MSG_FUNC_LINK_START = 0,
 56	BCM_MSG_FUNC_LINK_STOP,
 57	BCM_MSG_FUNC_SHMEM_TX,
 58	BCM_MSG_FUNC_SHMEM_RX,
 59	BCM_MSG_FUNC_SHMEM_STOP,
 60	BCM_MSG_FUNC_MAX,
 61};
 62
 63enum {
 64	BCM_MSG_SVC_INIT = 0,
 65	BCM_MSG_SVC_PMC,
 66	BCM_MSG_SVC_SCMI,
 67	BCM_MSG_SVC_DPFE,
 68	BCM_MSG_SVC_MAX,
 69};
 70
 71struct bcm74110_mbox_msg {
 72	struct list_head		list_entry;
 73#define BCM_MSG_VERSION_MASK		GENMASK(31, 29)
 74#define  BCM_MSG_VERSION		0x1
 75#define BCM_MSG_REQ_MASK		BIT(28)
 76#define BCM_MSG_RPLY_MASK		BIT(27)
 77#define BCM_MSG_SVC_MASK		GENMASK(26, 24)
 78#define BCM_MSG_FUNC_MASK		GENMASK(23, 16)
 79#define BCM_MSG_LENGTH_MASK		GENMASK(15, 4)
 80#define BCM_MSG_SLOT_MASK		GENMASK(3, 0)
 81
 82#define BCM_MSG_SET_FIELD(hdr, field, val)			\
 83	do {							\
 84		hdr &= ~BCM_MSG_##field##_MASK;			\
 85		hdr |= FIELD_PREP(BCM_MSG_##field##_MASK, val);	\
 86	} while (0)
 87
 88#define BCM_MSG_GET_FIELD(hdr, field)				\
 89		FIELD_GET(BCM_MSG_##field##_MASK, hdr)
 90	u32				msg;
 91};
 92
 93struct bcm74110_mbox_chan {
 94	struct bcm74110_mbox		*mbox;
 95	bool				en;
 96	int				slot;
 97	int				type;
 98};
 99
100struct bcm74110_mbox {
101	struct platform_device		*pdev;
102	void __iomem			*base;
103
104	int				tx_chan;
105	int				rx_chan;
106	int				rx_irq;
107	struct list_head		rx_svc_init_list;
108	spinlock_t			rx_svc_list_lock;
109
110	struct mbox_controller		controller;
111	struct bcm74110_mbox_chan	*mbox_chan;
112};
113
114#define BCM74110_OFFSET_IO_WRITEL_MACRO(name, offset_base)	\
115static void bcm74110_##name##_writel(struct bcm74110_mbox *mbox,\
116				     u32 val, u32 off)		\
117{								\
118	writel_relaxed(val, mbox->base + offset_base + off);	\
119}
120BCM74110_OFFSET_IO_WRITEL_MACRO(tx, BCM_MBOX_BASE(mbox->tx_chan));
121BCM74110_OFFSET_IO_WRITEL_MACRO(irq, BCM_MBOX_IRQ_BASE(mbox->rx_chan));
122
123#define BCM74110_OFFSET_IO_READL_MACRO(name, offset_base)	\
124static u32 bcm74110_##name##_readl(struct bcm74110_mbox *mbox,	\
125				   u32 off)			\
126{								\
127	return readl_relaxed(mbox->base + offset_base + off);	\
128}
129BCM74110_OFFSET_IO_READL_MACRO(tx, BCM_MBOX_BASE(mbox->tx_chan));
130BCM74110_OFFSET_IO_READL_MACRO(rx, BCM_MBOX_BASE(mbox->rx_chan));
131BCM74110_OFFSET_IO_READL_MACRO(irq, BCM_MBOX_IRQ_BASE(mbox->rx_chan));
132
133static inline struct bcm74110_mbox *bcm74110_mbox_from_cntrl(
134					struct mbox_controller *cntrl)
135{
136	return container_of(cntrl, struct bcm74110_mbox, controller);
137}
138
139static void bcm74110_rx_push_init_msg(struct bcm74110_mbox *mbox, u32 val)
140{
141	struct bcm74110_mbox_msg *msg;
142
143	msg = kzalloc(sizeof(*msg), GFP_ATOMIC);
144	if (!msg)
145		return;
146
147	INIT_LIST_HEAD(&msg->list_entry);
148	msg->msg = val;
149
150	spin_lock(&mbox->rx_svc_list_lock);
151	list_add_tail(&msg->list_entry, &mbox->rx_svc_init_list);
152	spin_unlock(&mbox->rx_svc_list_lock);
153}
154
155static void bcm74110_rx_process_msg(struct bcm74110_mbox *mbox)
156{
157	struct device *dev = &mbox->pdev->dev;
158	struct bcm74110_mbox_chan *chan_priv;
159	struct mbox_chan *chan;
160	u32 msg, status;
161	int type;
162
163	do {
164		msg = bcm74110_rx_readl(mbox, BCM_MBOX_RDATA);
165		status = bcm74110_rx_readl(mbox, BCM_MBOX_STATUS0);
166
167		dev_dbg(dev, "rx: [{req=%lu|rply=%lu|srv=%lu|fn=%lu|length=%lu|slot=%lu]\n",
168			BCM_MSG_GET_FIELD(msg, REQ), BCM_MSG_GET_FIELD(msg, RPLY),
169			BCM_MSG_GET_FIELD(msg, SVC), BCM_MSG_GET_FIELD(msg, FUNC),
170			BCM_MSG_GET_FIELD(msg, LENGTH), BCM_MSG_GET_FIELD(msg, SLOT));
171
172		type = BCM_MSG_GET_FIELD(msg, SVC);
173		switch (type) {
174		case BCM_MSG_SVC_INIT:
175			bcm74110_rx_push_init_msg(mbox, msg);
176			break;
177		case BCM_MSG_SVC_PMC:
178		case BCM_MSG_SVC_SCMI:
179		case BCM_MSG_SVC_DPFE:
180			chan = &mbox->controller.chans[type];
181			chan_priv = chan->con_priv;
182			if (chan_priv->en)
183				mbox_chan_received_data(chan, NULL);
184			else
185				dev_warn(dev, "Channel not enabled\n");
186			break;
187		default:
188			dev_warn(dev, "Unsupported msg received\n");
189		}
190	} while (status & BCM_MBOX_STATUS0_NOT_EMPTY);
191}
192
193static irqreturn_t bcm74110_mbox_isr(int irq, void *data)
194{
195	struct bcm74110_mbox *mbox = data;
196	u32 status;
197
198	status = bcm74110_irq_readl(mbox, BCM_MBOX_IRQ_STATUS);
199
200	bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_CLEAR);
201
202	if (status & BCM_MBOX_IRQ_NOT_EMPTY)
203		bcm74110_rx_process_msg(mbox);
204	else
205		dev_warn(&mbox->pdev->dev, "Spurious interrupt\n");
206
207	return IRQ_HANDLED;
208}
209
210static void bcm74110_mbox_mask_and_clear(struct bcm74110_mbox *mbox)
211{
212	bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_MASK_SET);
213	bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_CLEAR);
214}
215
216static int bcm74110_rx_pop_init_msg(struct bcm74110_mbox *mbox, u32 func_type,
217				    u32 *val)
218{
219	struct bcm74110_mbox_msg *msg, *msg_tmp;
220	unsigned long flags;
221	bool found = false;
222
223	spin_lock_irqsave(&mbox->rx_svc_list_lock, flags);
224	list_for_each_entry_safe(msg, msg_tmp, &mbox->rx_svc_init_list,
225				 list_entry) {
226		if (BCM_MSG_GET_FIELD(msg->msg, FUNC) == func_type) {
227			list_del(&msg->list_entry);
228			found = true;
229			break;
230		}
231	}
232	spin_unlock_irqrestore(&mbox->rx_svc_list_lock, flags);
233
234	if (!found)
235		return -EINVAL;
236
237	*val = msg->msg;
238	kfree(msg);
239
240	return 0;
241}
242
243static void bcm74110_rx_flush_msg(struct bcm74110_mbox *mbox)
244{
245	struct bcm74110_mbox_msg *msg, *msg_tmp;
246	LIST_HEAD(list_temp);
247	unsigned long flags;
248
249	spin_lock_irqsave(&mbox->rx_svc_list_lock, flags);
250	list_splice_init(&mbox->rx_svc_init_list, &list_temp);
251	spin_unlock_irqrestore(&mbox->rx_svc_list_lock, flags);
252
253	list_for_each_entry_safe(msg, msg_tmp, &list_temp, list_entry) {
254		list_del(&msg->list_entry);
255		kfree(msg);
256	}
257}
258
259#define BCM_DEQUEUE_TIMEOUT_MS 30
260static int bcm74110_rx_pop_init_msg_block(struct bcm74110_mbox *mbox, u32 func_type,
261					  u32 *val)
262{
263	int ret, timeout = 0;
264
265	do {
266		ret = bcm74110_rx_pop_init_msg(mbox, func_type, val);
267
268		if (!ret)
269			return 0;
270
271		/* TODO: Figure out what is a good sleep here. */
272		usleep_range(1000, 2000);
273		timeout++;
274	} while (timeout < BCM_DEQUEUE_TIMEOUT_MS);
275
276	dev_warn(&mbox->pdev->dev, "Timeout waiting for service init response\n");
277	return -ETIMEDOUT;
278}
279
280static int bcm74110_mbox_create_msg(int req, int rply, int svc, int func,
281				    int length, int slot)
282{
283	u32 msg = 0;
284
285	BCM_MSG_SET_FIELD(msg, REQ, req);
286	BCM_MSG_SET_FIELD(msg, RPLY, rply);
287	BCM_MSG_SET_FIELD(msg, SVC, svc);
288	BCM_MSG_SET_FIELD(msg, FUNC, func);
289	BCM_MSG_SET_FIELD(msg, LENGTH, length);
290	BCM_MSG_SET_FIELD(msg, SLOT, slot);
291
292	return msg;
293}
294
295static int bcm74110_mbox_tx_msg(struct bcm74110_mbox *mbox, u32 msg)
296{
297	int val;
298
299	/* We can potentially poll with timeout here instead */
300	val = bcm74110_tx_readl(mbox, BCM_MBOX_STATUS0);
301	if (val & BCM_MBOX_STATUS0_FULL) {
302		dev_err(&mbox->pdev->dev, "Mailbox full\n");
303		return -EINVAL;
304	}
305
306	dev_dbg(&mbox->pdev->dev, "tx: [{req=%lu|rply=%lu|srv=%lu|fn=%lu|length=%lu|slot=%lu]\n",
307		BCM_MSG_GET_FIELD(msg, REQ), BCM_MSG_GET_FIELD(msg, RPLY),
308		BCM_MSG_GET_FIELD(msg, SVC), BCM_MSG_GET_FIELD(msg, FUNC),
309		BCM_MSG_GET_FIELD(msg, LENGTH), BCM_MSG_GET_FIELD(msg, SLOT));
310
311	bcm74110_tx_writel(mbox, msg, BCM_MBOX_WDATA);
312
313	return 0;
314}
315
316#define BCM_MBOX_LINK_TRAINING_RETRIES	5
317static int bcm74110_mbox_link_training(struct bcm74110_mbox *mbox)
318{
319	int ret, retries = 0;
320	u32 msg = 0, orig_len = 0, len = BCM_LINK_CODE0;
321
322	do {
323		switch (len) {
324		case 0:
325			retries++;
326			dev_warn(&mbox->pdev->dev,
327				 "Link train failed, trying again... %d\n",
328				 retries);
329			if (retries > BCM_MBOX_LINK_TRAINING_RETRIES)
330				return -EINVAL;
331			len = BCM_LINK_CODE0;
332			fallthrough;
333		case BCM_LINK_CODE0:
334		case BCM_LINK_CODE1:
335		case BCM_LINK_CODE2:
336			msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT,
337						       BCM_MSG_FUNC_LINK_START,
338						       len, BCM_MSG_SVC_INIT);
339			break;
340		default:
341			break;
342		}
343
344		bcm74110_mbox_tx_msg(mbox, msg);
345
346		/* No response expected for LINK_CODE2 */
347		if (len == BCM_LINK_CODE2)
348			return 0;
349
350		orig_len = len;
351
352		ret = bcm74110_rx_pop_init_msg_block(mbox,
353						     BCM_MSG_GET_FIELD(msg, FUNC),
354						     &msg);
355		if (ret) {
356			len = 0;
357			continue;
358		}
359
360		if ((BCM_MSG_GET_FIELD(msg, SVC) != BCM_MSG_SVC_INIT) ||
361		    (BCM_MSG_GET_FIELD(msg, FUNC) != BCM_MSG_FUNC_LINK_START) ||
362		    (BCM_MSG_GET_FIELD(msg, SLOT) != 0) ||
363		    (BCM_MSG_GET_FIELD(msg, RPLY) != 1) ||
364		    (BCM_MSG_GET_FIELD(msg, REQ) != 0)) {
365			len = 0;
366			continue;
367		}
368
369		len = BCM_MSG_GET_FIELD(msg, LENGTH);
370
371		/* Make sure sequence is good */
372		if (len != (orig_len + 1)) {
373			len = 0;
374			continue;
375		}
376	} while (1);
377
378	return -EINVAL;
379}
380
381static int bcm74110_mbox_tx_msg_and_wait_ack(struct bcm74110_mbox *mbox, u32 msg)
382{
383	int ret;
384	u32 recv_msg;
385
386	ret = bcm74110_mbox_tx_msg(mbox, msg);
387	if (ret)
388		return ret;
389
390	ret = bcm74110_rx_pop_init_msg_block(mbox, BCM_MSG_GET_FIELD(msg, FUNC),
391					     &recv_msg);
392	if (ret)
393		return ret;
394
395	/*
396	 * Modify tx message to verify rx ack.
397	 * Flip RPLY/REQ for synchronous messages
398	 */
399	if (BCM_MSG_GET_FIELD(msg, REQ) == 1) {
400		BCM_MSG_SET_FIELD(msg, RPLY, 1);
401		BCM_MSG_SET_FIELD(msg, REQ, 0);
402	}
403
404	if (msg != recv_msg) {
405		dev_err(&mbox->pdev->dev, "Found ack, but ack is invalid\n");
406		return -EINVAL;
407	}
408
409	return 0;
410}
411
412/* Each index points to 0x100 of HAB MEM. IDX size counts from 0 */
413#define BCM_MBOX_HAB_MEM_IDX_START	0x30
414#define BCM_MBOX_HAB_MEM_IDX_SIZE	0x0
415static int bcm74110_mbox_shmem_init(struct bcm74110_mbox *mbox)
416{
417	u32 msg = 0;
418	int ret;
419
420	msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT,
421				       BCM_MSG_FUNC_SHMEM_STOP,
422				       0, BCM_MSG_SVC_INIT);
423	ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg);
424	if (ret)
425		return -EINVAL;
426
427	msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT,
428				       BCM_MSG_FUNC_SHMEM_TX,
429				       BCM_MBOX_HAB_MEM_IDX_START,
430				       BCM_MBOX_HAB_MEM_IDX_SIZE);
431	ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg);
432	if (ret)
433		return -EINVAL;
434
435	msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT,
436				       BCM_MSG_FUNC_SHMEM_RX,
437				       BCM_MBOX_HAB_MEM_IDX_START,
438				       BCM_MBOX_HAB_MEM_IDX_SIZE);
439	ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg);
440	if (ret)
441		return -EINVAL;
442
443	return 0;
444}
445
446static int bcm74110_mbox_init(struct bcm74110_mbox *mbox)
447{
448	int ret = 0;
449
450	/* Disable queues tx/rx */
451	bcm74110_tx_writel(mbox, 0x0, BCM_MBOX_CTRL);
452
453	/* Clear status & restart tx/rx*/
454	bcm74110_tx_writel(mbox, BCM_MBOX_CTRL_EN | BCM_MBOX_CTRL_CLR,
455			   BCM_MBOX_CTRL);
456
457	/* Unmask irq */
458	bcm74110_irq_writel(mbox, BCM_MBOX_IRQ_NOT_EMPTY, BCM_MBOX_IRQ_MASK_CLEAR);
459
460	ret = bcm74110_mbox_link_training(mbox);
461	if (ret) {
462		dev_err(&mbox->pdev->dev, "Training failed\n");
463		return ret;
464	}
465
466	return bcm74110_mbox_shmem_init(mbox);
467}
468
469static int bcm74110_mbox_send_data(struct mbox_chan *chan, void *data)
470{
471	struct bcm74110_mbox_chan *chan_priv = chan->con_priv;
472	u32 msg;
473
474	switch (chan_priv->type) {
475	case BCM_MSG_SVC_PMC:
476	case BCM_MSG_SVC_SCMI:
477	case BCM_MSG_SVC_DPFE:
478		msg = bcm74110_mbox_create_msg(1, 0, chan_priv->type, 0,
479					       128 + 28, chan_priv->slot);
480		break;
481	default:
482		return -EINVAL;
483	}
484
485	return bcm74110_mbox_tx_msg(chan_priv->mbox, msg);
486}
487
488static int bcm74110_mbox_chan_startup(struct mbox_chan *chan)
489{
490	struct bcm74110_mbox_chan *chan_priv = chan->con_priv;
491
492	chan_priv->en = true;
493
494	return 0;
495}
496
497static void bcm74110_mbox_chan_shutdown(struct mbox_chan *chan)
498{
499	struct bcm74110_mbox_chan *chan_priv = chan->con_priv;
500
501	chan_priv->en = false;
502}
503
504static const struct mbox_chan_ops bcm74110_mbox_chan_ops = {
505	.send_data = bcm74110_mbox_send_data,
506	.startup = bcm74110_mbox_chan_startup,
507	.shutdown = bcm74110_mbox_chan_shutdown,
508};
509
510static void bcm74110_mbox_shutdown(struct platform_device *pdev)
511{
512	struct bcm74110_mbox *mbox = dev_get_drvdata(&pdev->dev);
513	u32 msg;
514
515	msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT,
516				       BCM_MSG_FUNC_LINK_STOP,
517				       0, 0);
518
519	bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg);
520
521	/* Even if we don't receive ACK, lets shut it down */
522
523	bcm74110_mbox_mask_and_clear(mbox);
524
525	/* Disable queues tx/rx */
526	bcm74110_tx_writel(mbox, 0x0, BCM_MBOX_CTRL);
527
528	/* Flush queues */
529	bcm74110_rx_flush_msg(mbox);
530}
531
532static struct mbox_chan *bcm74110_mbox_of_xlate(struct mbox_controller *cntrl,
533						const struct of_phandle_args *p)
534{
535	struct bcm74110_mbox *mbox = bcm74110_mbox_from_cntrl(cntrl);
536	struct device *dev = &mbox->pdev->dev;
537	struct bcm74110_mbox_chan *chan_priv;
538	int slot, type;
539
540	if (p->args_count != 2) {
541		dev_err(dev, "Invalid arguments\n");
542		return ERR_PTR(-EINVAL);
543	}
544
545	type = p->args[0];
546	slot = p->args[1];
547
548	switch (type) {
549	case BCM_MSG_SVC_PMC:
550	case BCM_MSG_SVC_SCMI:
551	case BCM_MSG_SVC_DPFE:
552		if (slot > BCM_MBOX_HAB_MEM_IDX_SIZE) {
553			dev_err(dev, "Not enough shared memory\n");
554			return ERR_PTR(-EINVAL);
555		}
556		chan_priv = cntrl->chans[type].con_priv;
557		chan_priv->slot = slot;
558		chan_priv->type = type;
559		break;
560	default:
561		dev_err(dev, "Invalid channel type: %d\n", type);
562		return ERR_PTR(-EINVAL);
563	}
564
565	return &cntrl->chans[type];
566}
567
568static int bcm74110_mbox_probe(struct platform_device *pdev)
569{
570	struct device *dev = &pdev->dev;
571	struct bcm74110_mbox *mbox;
572	int i, ret;
573
574	mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
575	if (!mbox)
576		return -ENOMEM;
577
578	mbox->pdev = pdev;
579	platform_set_drvdata(pdev, mbox);
580
581	mbox->base = devm_platform_ioremap_resource(pdev, 0);
582	if (IS_ERR(mbox->base))
583		return dev_err_probe(dev, PTR_ERR(mbox->base), "Failed to iomap\n");
584
585	ret = of_property_read_u32(dev->of_node, "brcm,tx", &mbox->tx_chan);
586	if (ret)
587		return dev_err_probe(dev, ret, "Failed to find tx channel\n");
588
589	ret = of_property_read_u32(dev->of_node, "brcm,rx", &mbox->rx_chan);
590	if (ret)
591		return dev_err_probe(dev, ret, "Failed to find rx channel\n");
592
593	mbox->rx_irq = platform_get_irq(pdev, 0);
594	if (mbox->rx_irq < 0)
595		return mbox->rx_irq;
596
597	INIT_LIST_HEAD(&mbox->rx_svc_init_list);
598	spin_lock_init(&mbox->rx_svc_list_lock);
599	bcm74110_mbox_mask_and_clear(mbox);
600
601	ret = devm_request_irq(dev, mbox->rx_irq, bcm74110_mbox_isr,
602			       IRQF_NO_SUSPEND, pdev->name, mbox);
603	if (ret)
604		return dev_err_probe(dev, ret, "Failed to request irq\n");
605
606	mbox->controller.ops = &bcm74110_mbox_chan_ops;
607	mbox->controller.dev = dev;
608	mbox->controller.num_chans = BCM_MSG_SVC_MAX;
609	mbox->controller.of_xlate = &bcm74110_mbox_of_xlate;
610	mbox->controller.chans = devm_kcalloc(dev, BCM_MSG_SVC_MAX,
611					      sizeof(*mbox->controller.chans),
612					      GFP_KERNEL);
613	if (!mbox->controller.chans)
614		return -ENOMEM;
615
616	mbox->mbox_chan = devm_kcalloc(dev, BCM_MSG_SVC_MAX,
617				       sizeof(*mbox->mbox_chan),
618				       GFP_KERNEL);
619	if (!mbox->mbox_chan)
620		return -ENOMEM;
621
622	for (i = 0; i < BCM_MSG_SVC_MAX; i++) {
623		mbox->mbox_chan[i].mbox = mbox;
624		mbox->controller.chans[i].con_priv = &mbox->mbox_chan[i];
625	}
626
627	ret = devm_mbox_controller_register(dev, &mbox->controller);
628	if (ret)
629		return ret;
630
631	ret = bcm74110_mbox_init(mbox);
632	if (ret)
633		return ret;
634
635	return 0;
636}
637
638static const struct of_device_id bcm74110_mbox_of_match[] = {
639	{ .compatible = "brcm,bcm74110-mbox", },
640	{ /* sentinel */ },
641};
642MODULE_DEVICE_TABLE(of, bcm74110_mbox_of_match);
643
644static struct platform_driver bcm74110_mbox_driver = {
645	.driver = {
646		.name = "bcm74110-mbox",
647		.of_match_table = bcm74110_mbox_of_match,
648		},
649	.probe = bcm74110_mbox_probe,
650	.shutdown = bcm74110_mbox_shutdown,
651};
652module_platform_driver(bcm74110_mbox_driver);
653
654MODULE_AUTHOR("Justin Chen <justin.chen@broadcom.com>");
655MODULE_DESCRIPTION("BCM74110 mailbox driver");
656MODULE_LICENSE("GPL");
~/ Read-only file preview.