162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * OMAP mailbox driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved.
662306a36Sopenharmony_ci * Copyright (C) 2013-2021 Texas Instruments Incorporated - https://www.ti.com
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
962306a36Sopenharmony_ci *          Suman Anna <s-anna@ti.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/spinlock.h>
1462306a36Sopenharmony_ci#include <linux/mutex.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/kfifo.h>
1762306a36Sopenharmony_ci#include <linux/err.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/of.h>
2062306a36Sopenharmony_ci#include <linux/platform_device.h>
2162306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2262306a36Sopenharmony_ci#include <linux/omap-mailbox.h>
2362306a36Sopenharmony_ci#include <linux/mailbox_controller.h>
2462306a36Sopenharmony_ci#include <linux/mailbox_client.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "mailbox.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define MAILBOX_REVISION		0x000
2962306a36Sopenharmony_ci#define MAILBOX_MESSAGE(m)		(0x040 + 4 * (m))
3062306a36Sopenharmony_ci#define MAILBOX_FIFOSTATUS(m)		(0x080 + 4 * (m))
3162306a36Sopenharmony_ci#define MAILBOX_MSGSTATUS(m)		(0x0c0 + 4 * (m))
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define OMAP2_MAILBOX_IRQSTATUS(u)	(0x100 + 8 * (u))
3462306a36Sopenharmony_ci#define OMAP2_MAILBOX_IRQENABLE(u)	(0x104 + 8 * (u))
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define OMAP4_MAILBOX_IRQSTATUS(u)	(0x104 + 0x10 * (u))
3762306a36Sopenharmony_ci#define OMAP4_MAILBOX_IRQENABLE(u)	(0x108 + 0x10 * (u))
3862306a36Sopenharmony_ci#define OMAP4_MAILBOX_IRQENABLE_CLR(u)	(0x10c + 0x10 * (u))
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define MAILBOX_IRQSTATUS(type, u)	(type ? OMAP4_MAILBOX_IRQSTATUS(u) : \
4162306a36Sopenharmony_ci						OMAP2_MAILBOX_IRQSTATUS(u))
4262306a36Sopenharmony_ci#define MAILBOX_IRQENABLE(type, u)	(type ? OMAP4_MAILBOX_IRQENABLE(u) : \
4362306a36Sopenharmony_ci						OMAP2_MAILBOX_IRQENABLE(u))
4462306a36Sopenharmony_ci#define MAILBOX_IRQDISABLE(type, u)	(type ? OMAP4_MAILBOX_IRQENABLE_CLR(u) \
4562306a36Sopenharmony_ci						: OMAP2_MAILBOX_IRQENABLE(u))
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define MAILBOX_IRQ_NEWMSG(m)		(1 << (2 * (m)))
4862306a36Sopenharmony_ci#define MAILBOX_IRQ_NOTFULL(m)		(1 << (2 * (m) + 1))
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* Interrupt register configuration types */
5162306a36Sopenharmony_ci#define MBOX_INTR_CFG_TYPE1		0
5262306a36Sopenharmony_ci#define MBOX_INTR_CFG_TYPE2		1
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistruct omap_mbox_fifo {
5562306a36Sopenharmony_ci	unsigned long msg;
5662306a36Sopenharmony_ci	unsigned long fifo_stat;
5762306a36Sopenharmony_ci	unsigned long msg_stat;
5862306a36Sopenharmony_ci	unsigned long irqenable;
5962306a36Sopenharmony_ci	unsigned long irqstatus;
6062306a36Sopenharmony_ci	unsigned long irqdisable;
6162306a36Sopenharmony_ci	u32 intr_bit;
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistruct omap_mbox_queue {
6562306a36Sopenharmony_ci	spinlock_t		lock;
6662306a36Sopenharmony_ci	struct kfifo		fifo;
6762306a36Sopenharmony_ci	struct work_struct	work;
6862306a36Sopenharmony_ci	struct omap_mbox	*mbox;
6962306a36Sopenharmony_ci	bool full;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistruct omap_mbox_match_data {
7362306a36Sopenharmony_ci	u32 intr_type;
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistruct omap_mbox_device {
7762306a36Sopenharmony_ci	struct device *dev;
7862306a36Sopenharmony_ci	struct mutex cfg_lock;
7962306a36Sopenharmony_ci	void __iomem *mbox_base;
8062306a36Sopenharmony_ci	u32 *irq_ctx;
8162306a36Sopenharmony_ci	u32 num_users;
8262306a36Sopenharmony_ci	u32 num_fifos;
8362306a36Sopenharmony_ci	u32 intr_type;
8462306a36Sopenharmony_ci	struct omap_mbox **mboxes;
8562306a36Sopenharmony_ci	struct mbox_controller controller;
8662306a36Sopenharmony_ci	struct list_head elem;
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistruct omap_mbox_fifo_info {
9062306a36Sopenharmony_ci	int tx_id;
9162306a36Sopenharmony_ci	int tx_usr;
9262306a36Sopenharmony_ci	int tx_irq;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	int rx_id;
9562306a36Sopenharmony_ci	int rx_usr;
9662306a36Sopenharmony_ci	int rx_irq;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	const char *name;
9962306a36Sopenharmony_ci	bool send_no_irq;
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistruct omap_mbox {
10362306a36Sopenharmony_ci	const char		*name;
10462306a36Sopenharmony_ci	int			irq;
10562306a36Sopenharmony_ci	struct omap_mbox_queue	*rxq;
10662306a36Sopenharmony_ci	struct device		*dev;
10762306a36Sopenharmony_ci	struct omap_mbox_device *parent;
10862306a36Sopenharmony_ci	struct omap_mbox_fifo	tx_fifo;
10962306a36Sopenharmony_ci	struct omap_mbox_fifo	rx_fifo;
11062306a36Sopenharmony_ci	u32			intr_type;
11162306a36Sopenharmony_ci	struct mbox_chan	*chan;
11262306a36Sopenharmony_ci	bool			send_no_irq;
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/* global variables for the mailbox devices */
11662306a36Sopenharmony_cistatic DEFINE_MUTEX(omap_mbox_devices_lock);
11762306a36Sopenharmony_cistatic LIST_HEAD(omap_mbox_devices);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE;
12062306a36Sopenharmony_cimodule_param(mbox_kfifo_size, uint, S_IRUGO);
12162306a36Sopenharmony_ciMODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)");
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic struct omap_mbox *mbox_chan_to_omap_mbox(struct mbox_chan *chan)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	if (!chan || !chan->con_priv)
12662306a36Sopenharmony_ci		return NULL;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return (struct omap_mbox *)chan->con_priv;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic inline
13262306a36Sopenharmony_ciunsigned int mbox_read_reg(struct omap_mbox_device *mdev, size_t ofs)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	return __raw_readl(mdev->mbox_base + ofs);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic inline
13862306a36Sopenharmony_civoid mbox_write_reg(struct omap_mbox_device *mdev, u32 val, size_t ofs)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	__raw_writel(val, mdev->mbox_base + ofs);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/* Mailbox FIFO handle functions */
14462306a36Sopenharmony_cistatic u32 mbox_fifo_read(struct omap_mbox *mbox)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct omap_mbox_fifo *fifo = &mbox->rx_fifo;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return mbox_read_reg(mbox->parent, fifo->msg);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic void mbox_fifo_write(struct omap_mbox *mbox, u32 msg)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct omap_mbox_fifo *fifo = &mbox->tx_fifo;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	mbox_write_reg(mbox->parent, msg, fifo->msg);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic int mbox_fifo_empty(struct omap_mbox *mbox)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct omap_mbox_fifo *fifo = &mbox->rx_fifo;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return (mbox_read_reg(mbox->parent, fifo->msg_stat) == 0);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic int mbox_fifo_full(struct omap_mbox *mbox)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct omap_mbox_fifo *fifo = &mbox->tx_fifo;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return mbox_read_reg(mbox->parent, fifo->fifo_stat);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci/* Mailbox IRQ handle functions */
17362306a36Sopenharmony_cistatic void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
17662306a36Sopenharmony_ci				&mbox->tx_fifo : &mbox->rx_fifo;
17762306a36Sopenharmony_ci	u32 bit = fifo->intr_bit;
17862306a36Sopenharmony_ci	u32 irqstatus = fifo->irqstatus;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	mbox_write_reg(mbox->parent, bit, irqstatus);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* Flush posted write for irq status to avoid spurious interrupts */
18362306a36Sopenharmony_ci	mbox_read_reg(mbox->parent, irqstatus);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
18962306a36Sopenharmony_ci				&mbox->tx_fifo : &mbox->rx_fifo;
19062306a36Sopenharmony_ci	u32 bit = fifo->intr_bit;
19162306a36Sopenharmony_ci	u32 irqenable = fifo->irqenable;
19262306a36Sopenharmony_ci	u32 irqstatus = fifo->irqstatus;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	u32 enable = mbox_read_reg(mbox->parent, irqenable);
19562306a36Sopenharmony_ci	u32 status = mbox_read_reg(mbox->parent, irqstatus);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return (int)(enable & status & bit);
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic void _omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	u32 l;
20362306a36Sopenharmony_ci	struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
20462306a36Sopenharmony_ci				&mbox->tx_fifo : &mbox->rx_fifo;
20562306a36Sopenharmony_ci	u32 bit = fifo->intr_bit;
20662306a36Sopenharmony_ci	u32 irqenable = fifo->irqenable;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	l = mbox_read_reg(mbox->parent, irqenable);
20962306a36Sopenharmony_ci	l |= bit;
21062306a36Sopenharmony_ci	mbox_write_reg(mbox->parent, l, irqenable);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic void _omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
21662306a36Sopenharmony_ci				&mbox->tx_fifo : &mbox->rx_fifo;
21762306a36Sopenharmony_ci	u32 bit = fifo->intr_bit;
21862306a36Sopenharmony_ci	u32 irqdisable = fifo->irqdisable;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/*
22162306a36Sopenharmony_ci	 * Read and update the interrupt configuration register for pre-OMAP4.
22262306a36Sopenharmony_ci	 * OMAP4 and later SoCs have a dedicated interrupt disabling register.
22362306a36Sopenharmony_ci	 */
22462306a36Sopenharmony_ci	if (!mbox->intr_type)
22562306a36Sopenharmony_ci		bit = mbox_read_reg(mbox->parent, irqdisable) & ~bit;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	mbox_write_reg(mbox->parent, bit, irqdisable);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_civoid omap_mbox_enable_irq(struct mbox_chan *chan, omap_mbox_irq_t irq)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (WARN_ON(!mbox))
23562306a36Sopenharmony_ci		return;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	_omap_mbox_enable_irq(mbox, irq);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ciEXPORT_SYMBOL(omap_mbox_enable_irq);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_civoid omap_mbox_disable_irq(struct mbox_chan *chan, omap_mbox_irq_t irq)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (WARN_ON(!mbox))
24662306a36Sopenharmony_ci		return;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	_omap_mbox_disable_irq(mbox, irq);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ciEXPORT_SYMBOL(omap_mbox_disable_irq);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/*
25362306a36Sopenharmony_ci * Message receiver(workqueue)
25462306a36Sopenharmony_ci */
25562306a36Sopenharmony_cistatic void mbox_rx_work(struct work_struct *work)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct omap_mbox_queue *mq =
25862306a36Sopenharmony_ci			container_of(work, struct omap_mbox_queue, work);
25962306a36Sopenharmony_ci	mbox_msg_t data;
26062306a36Sopenharmony_ci	u32 msg;
26162306a36Sopenharmony_ci	int len;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	while (kfifo_len(&mq->fifo) >= sizeof(msg)) {
26462306a36Sopenharmony_ci		len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
26562306a36Sopenharmony_ci		WARN_ON(len != sizeof(msg));
26662306a36Sopenharmony_ci		data = msg;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		mbox_chan_received_data(mq->mbox->chan, (void *)data);
26962306a36Sopenharmony_ci		spin_lock_irq(&mq->lock);
27062306a36Sopenharmony_ci		if (mq->full) {
27162306a36Sopenharmony_ci			mq->full = false;
27262306a36Sopenharmony_ci			_omap_mbox_enable_irq(mq->mbox, IRQ_RX);
27362306a36Sopenharmony_ci		}
27462306a36Sopenharmony_ci		spin_unlock_irq(&mq->lock);
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/*
27962306a36Sopenharmony_ci * Mailbox interrupt handler
28062306a36Sopenharmony_ci */
28162306a36Sopenharmony_cistatic void __mbox_tx_interrupt(struct omap_mbox *mbox)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	_omap_mbox_disable_irq(mbox, IRQ_TX);
28462306a36Sopenharmony_ci	ack_mbox_irq(mbox, IRQ_TX);
28562306a36Sopenharmony_ci	mbox_chan_txdone(mbox->chan, 0);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic void __mbox_rx_interrupt(struct omap_mbox *mbox)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct omap_mbox_queue *mq = mbox->rxq;
29162306a36Sopenharmony_ci	u32 msg;
29262306a36Sopenharmony_ci	int len;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	while (!mbox_fifo_empty(mbox)) {
29562306a36Sopenharmony_ci		if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) {
29662306a36Sopenharmony_ci			_omap_mbox_disable_irq(mbox, IRQ_RX);
29762306a36Sopenharmony_ci			mq->full = true;
29862306a36Sopenharmony_ci			goto nomem;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		msg = mbox_fifo_read(mbox);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
30462306a36Sopenharmony_ci		WARN_ON(len != sizeof(msg));
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* no more messages in the fifo. clear IRQ source. */
30862306a36Sopenharmony_ci	ack_mbox_irq(mbox, IRQ_RX);
30962306a36Sopenharmony_cinomem:
31062306a36Sopenharmony_ci	schedule_work(&mbox->rxq->work);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic irqreturn_t mbox_interrupt(int irq, void *p)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct omap_mbox *mbox = p;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (is_mbox_irq(mbox, IRQ_TX))
31862306a36Sopenharmony_ci		__mbox_tx_interrupt(mbox);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (is_mbox_irq(mbox, IRQ_RX))
32162306a36Sopenharmony_ci		__mbox_rx_interrupt(mbox);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	return IRQ_HANDLED;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox,
32762306a36Sopenharmony_ci					void (*work)(struct work_struct *))
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct omap_mbox_queue *mq;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (!work)
33262306a36Sopenharmony_ci		return NULL;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	mq = kzalloc(sizeof(*mq), GFP_KERNEL);
33562306a36Sopenharmony_ci	if (!mq)
33662306a36Sopenharmony_ci		return NULL;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	spin_lock_init(&mq->lock);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL))
34162306a36Sopenharmony_ci		goto error;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	INIT_WORK(&mq->work, work);
34462306a36Sopenharmony_ci	return mq;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cierror:
34762306a36Sopenharmony_ci	kfree(mq);
34862306a36Sopenharmony_ci	return NULL;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic void mbox_queue_free(struct omap_mbox_queue *q)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	kfifo_free(&q->fifo);
35462306a36Sopenharmony_ci	kfree(q);
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic int omap_mbox_startup(struct omap_mbox *mbox)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	int ret = 0;
36062306a36Sopenharmony_ci	struct omap_mbox_queue *mq;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	mq = mbox_queue_alloc(mbox, mbox_rx_work);
36362306a36Sopenharmony_ci	if (!mq)
36462306a36Sopenharmony_ci		return -ENOMEM;
36562306a36Sopenharmony_ci	mbox->rxq = mq;
36662306a36Sopenharmony_ci	mq->mbox = mbox;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED,
36962306a36Sopenharmony_ci			  mbox->name, mbox);
37062306a36Sopenharmony_ci	if (unlikely(ret)) {
37162306a36Sopenharmony_ci		pr_err("failed to register mailbox interrupt:%d\n", ret);
37262306a36Sopenharmony_ci		goto fail_request_irq;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (mbox->send_no_irq)
37662306a36Sopenharmony_ci		mbox->chan->txdone_method = TXDONE_BY_ACK;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	_omap_mbox_enable_irq(mbox, IRQ_RX);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return 0;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cifail_request_irq:
38362306a36Sopenharmony_ci	mbox_queue_free(mbox->rxq);
38462306a36Sopenharmony_ci	return ret;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic void omap_mbox_fini(struct omap_mbox *mbox)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	_omap_mbox_disable_irq(mbox, IRQ_RX);
39062306a36Sopenharmony_ci	free_irq(mbox->irq, mbox);
39162306a36Sopenharmony_ci	flush_work(&mbox->rxq->work);
39262306a36Sopenharmony_ci	mbox_queue_free(mbox->rxq);
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic struct omap_mbox *omap_mbox_device_find(struct omap_mbox_device *mdev,
39662306a36Sopenharmony_ci					       const char *mbox_name)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct omap_mbox *_mbox, *mbox = NULL;
39962306a36Sopenharmony_ci	struct omap_mbox **mboxes = mdev->mboxes;
40062306a36Sopenharmony_ci	int i;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (!mboxes)
40362306a36Sopenharmony_ci		return NULL;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	for (i = 0; (_mbox = mboxes[i]); i++) {
40662306a36Sopenharmony_ci		if (!strcmp(_mbox->name, mbox_name)) {
40762306a36Sopenharmony_ci			mbox = _mbox;
40862306a36Sopenharmony_ci			break;
40962306a36Sopenharmony_ci		}
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci	return mbox;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistruct mbox_chan *omap_mbox_request_channel(struct mbox_client *cl,
41562306a36Sopenharmony_ci					    const char *chan_name)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct device *dev = cl->dev;
41862306a36Sopenharmony_ci	struct omap_mbox *mbox = NULL;
41962306a36Sopenharmony_ci	struct omap_mbox_device *mdev;
42062306a36Sopenharmony_ci	int ret;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (!dev)
42362306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (dev->of_node) {
42662306a36Sopenharmony_ci		pr_err("%s: please use mbox_request_channel(), this API is supported only for OMAP non-DT usage\n",
42762306a36Sopenharmony_ci		       __func__);
42862306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	mutex_lock(&omap_mbox_devices_lock);
43262306a36Sopenharmony_ci	list_for_each_entry(mdev, &omap_mbox_devices, elem) {
43362306a36Sopenharmony_ci		mbox = omap_mbox_device_find(mdev, chan_name);
43462306a36Sopenharmony_ci		if (mbox)
43562306a36Sopenharmony_ci			break;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci	mutex_unlock(&omap_mbox_devices_lock);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (!mbox || !mbox->chan)
44062306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	ret = mbox_bind_client(mbox->chan, cl);
44362306a36Sopenharmony_ci	if (ret)
44462306a36Sopenharmony_ci		return ERR_PTR(ret);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	return mbox->chan;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ciEXPORT_SYMBOL(omap_mbox_request_channel);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic struct class omap_mbox_class = { .name = "mbox", };
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int omap_mbox_register(struct omap_mbox_device *mdev)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	int ret;
45562306a36Sopenharmony_ci	int i;
45662306a36Sopenharmony_ci	struct omap_mbox **mboxes;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (!mdev || !mdev->mboxes)
45962306a36Sopenharmony_ci		return -EINVAL;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	mboxes = mdev->mboxes;
46262306a36Sopenharmony_ci	for (i = 0; mboxes[i]; i++) {
46362306a36Sopenharmony_ci		struct omap_mbox *mbox = mboxes[i];
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		mbox->dev = device_create(&omap_mbox_class, mdev->dev,
46662306a36Sopenharmony_ci					0, mbox, "%s", mbox->name);
46762306a36Sopenharmony_ci		if (IS_ERR(mbox->dev)) {
46862306a36Sopenharmony_ci			ret = PTR_ERR(mbox->dev);
46962306a36Sopenharmony_ci			goto err_out;
47062306a36Sopenharmony_ci		}
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	mutex_lock(&omap_mbox_devices_lock);
47462306a36Sopenharmony_ci	list_add(&mdev->elem, &omap_mbox_devices);
47562306a36Sopenharmony_ci	mutex_unlock(&omap_mbox_devices_lock);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	ret = devm_mbox_controller_register(mdev->dev, &mdev->controller);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cierr_out:
48062306a36Sopenharmony_ci	if (ret) {
48162306a36Sopenharmony_ci		while (i--)
48262306a36Sopenharmony_ci			device_unregister(mboxes[i]->dev);
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci	return ret;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic int omap_mbox_unregister(struct omap_mbox_device *mdev)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	int i;
49062306a36Sopenharmony_ci	struct omap_mbox **mboxes;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if (!mdev || !mdev->mboxes)
49362306a36Sopenharmony_ci		return -EINVAL;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	mutex_lock(&omap_mbox_devices_lock);
49662306a36Sopenharmony_ci	list_del(&mdev->elem);
49762306a36Sopenharmony_ci	mutex_unlock(&omap_mbox_devices_lock);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	mboxes = mdev->mboxes;
50062306a36Sopenharmony_ci	for (i = 0; mboxes[i]; i++)
50162306a36Sopenharmony_ci		device_unregister(mboxes[i]->dev);
50262306a36Sopenharmony_ci	return 0;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic int omap_mbox_chan_startup(struct mbox_chan *chan)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
50862306a36Sopenharmony_ci	struct omap_mbox_device *mdev = mbox->parent;
50962306a36Sopenharmony_ci	int ret = 0;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	mutex_lock(&mdev->cfg_lock);
51262306a36Sopenharmony_ci	pm_runtime_get_sync(mdev->dev);
51362306a36Sopenharmony_ci	ret = omap_mbox_startup(mbox);
51462306a36Sopenharmony_ci	if (ret)
51562306a36Sopenharmony_ci		pm_runtime_put_sync(mdev->dev);
51662306a36Sopenharmony_ci	mutex_unlock(&mdev->cfg_lock);
51762306a36Sopenharmony_ci	return ret;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic void omap_mbox_chan_shutdown(struct mbox_chan *chan)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
52362306a36Sopenharmony_ci	struct omap_mbox_device *mdev = mbox->parent;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	mutex_lock(&mdev->cfg_lock);
52662306a36Sopenharmony_ci	omap_mbox_fini(mbox);
52762306a36Sopenharmony_ci	pm_runtime_put_sync(mdev->dev);
52862306a36Sopenharmony_ci	mutex_unlock(&mdev->cfg_lock);
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic int omap_mbox_chan_send_noirq(struct omap_mbox *mbox, u32 msg)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	int ret = -EBUSY;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (!mbox_fifo_full(mbox)) {
53662306a36Sopenharmony_ci		_omap_mbox_enable_irq(mbox, IRQ_RX);
53762306a36Sopenharmony_ci		mbox_fifo_write(mbox, msg);
53862306a36Sopenharmony_ci		ret = 0;
53962306a36Sopenharmony_ci		_omap_mbox_disable_irq(mbox, IRQ_RX);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		/* we must read and ack the interrupt directly from here */
54262306a36Sopenharmony_ci		mbox_fifo_read(mbox);
54362306a36Sopenharmony_ci		ack_mbox_irq(mbox, IRQ_RX);
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	return ret;
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic int omap_mbox_chan_send(struct omap_mbox *mbox, u32 msg)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	int ret = -EBUSY;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (!mbox_fifo_full(mbox)) {
55462306a36Sopenharmony_ci		mbox_fifo_write(mbox, msg);
55562306a36Sopenharmony_ci		ret = 0;
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/* always enable the interrupt */
55962306a36Sopenharmony_ci	_omap_mbox_enable_irq(mbox, IRQ_TX);
56062306a36Sopenharmony_ci	return ret;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
56662306a36Sopenharmony_ci	int ret;
56762306a36Sopenharmony_ci	u32 msg = omap_mbox_message(data);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (!mbox)
57062306a36Sopenharmony_ci		return -EINVAL;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (mbox->send_no_irq)
57362306a36Sopenharmony_ci		ret = omap_mbox_chan_send_noirq(mbox, msg);
57462306a36Sopenharmony_ci	else
57562306a36Sopenharmony_ci		ret = omap_mbox_chan_send(mbox, msg);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	return ret;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic const struct mbox_chan_ops omap_mbox_chan_ops = {
58162306a36Sopenharmony_ci	.startup        = omap_mbox_chan_startup,
58262306a36Sopenharmony_ci	.send_data      = omap_mbox_chan_send_data,
58362306a36Sopenharmony_ci	.shutdown       = omap_mbox_chan_shutdown,
58462306a36Sopenharmony_ci};
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
58762306a36Sopenharmony_cistatic int omap_mbox_suspend(struct device *dev)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	struct omap_mbox_device *mdev = dev_get_drvdata(dev);
59062306a36Sopenharmony_ci	u32 usr, fifo, reg;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	if (pm_runtime_status_suspended(dev))
59362306a36Sopenharmony_ci		return 0;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	for (fifo = 0; fifo < mdev->num_fifos; fifo++) {
59662306a36Sopenharmony_ci		if (mbox_read_reg(mdev, MAILBOX_MSGSTATUS(fifo))) {
59762306a36Sopenharmony_ci			dev_err(mdev->dev, "fifo %d has unexpected unread messages\n",
59862306a36Sopenharmony_ci				fifo);
59962306a36Sopenharmony_ci			return -EBUSY;
60062306a36Sopenharmony_ci		}
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	for (usr = 0; usr < mdev->num_users; usr++) {
60462306a36Sopenharmony_ci		reg = MAILBOX_IRQENABLE(mdev->intr_type, usr);
60562306a36Sopenharmony_ci		mdev->irq_ctx[usr] = mbox_read_reg(mdev, reg);
60662306a36Sopenharmony_ci	}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	return 0;
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_cistatic int omap_mbox_resume(struct device *dev)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	struct omap_mbox_device *mdev = dev_get_drvdata(dev);
61462306a36Sopenharmony_ci	u32 usr, reg;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (pm_runtime_status_suspended(dev))
61762306a36Sopenharmony_ci		return 0;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	for (usr = 0; usr < mdev->num_users; usr++) {
62062306a36Sopenharmony_ci		reg = MAILBOX_IRQENABLE(mdev->intr_type, usr);
62162306a36Sopenharmony_ci		mbox_write_reg(mdev, mdev->irq_ctx[usr], reg);
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	return 0;
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci#endif
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cistatic const struct dev_pm_ops omap_mbox_pm_ops = {
62962306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(omap_mbox_suspend, omap_mbox_resume)
63062306a36Sopenharmony_ci};
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic const struct omap_mbox_match_data omap2_data = { MBOX_INTR_CFG_TYPE1 };
63362306a36Sopenharmony_cistatic const struct omap_mbox_match_data omap4_data = { MBOX_INTR_CFG_TYPE2 };
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic const struct of_device_id omap_mailbox_of_match[] = {
63662306a36Sopenharmony_ci	{
63762306a36Sopenharmony_ci		.compatible	= "ti,omap2-mailbox",
63862306a36Sopenharmony_ci		.data		= &omap2_data,
63962306a36Sopenharmony_ci	},
64062306a36Sopenharmony_ci	{
64162306a36Sopenharmony_ci		.compatible	= "ti,omap3-mailbox",
64262306a36Sopenharmony_ci		.data		= &omap2_data,
64362306a36Sopenharmony_ci	},
64462306a36Sopenharmony_ci	{
64562306a36Sopenharmony_ci		.compatible	= "ti,omap4-mailbox",
64662306a36Sopenharmony_ci		.data		= &omap4_data,
64762306a36Sopenharmony_ci	},
64862306a36Sopenharmony_ci	{
64962306a36Sopenharmony_ci		.compatible	= "ti,am654-mailbox",
65062306a36Sopenharmony_ci		.data		= &omap4_data,
65162306a36Sopenharmony_ci	},
65262306a36Sopenharmony_ci	{
65362306a36Sopenharmony_ci		.compatible	= "ti,am64-mailbox",
65462306a36Sopenharmony_ci		.data		= &omap4_data,
65562306a36Sopenharmony_ci	},
65662306a36Sopenharmony_ci	{
65762306a36Sopenharmony_ci		/* end */
65862306a36Sopenharmony_ci	},
65962306a36Sopenharmony_ci};
66062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_mailbox_of_match);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic struct mbox_chan *omap_mbox_of_xlate(struct mbox_controller *controller,
66362306a36Sopenharmony_ci					    const struct of_phandle_args *sp)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	phandle phandle = sp->args[0];
66662306a36Sopenharmony_ci	struct device_node *node;
66762306a36Sopenharmony_ci	struct omap_mbox_device *mdev;
66862306a36Sopenharmony_ci	struct omap_mbox *mbox;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	mdev = container_of(controller, struct omap_mbox_device, controller);
67162306a36Sopenharmony_ci	if (WARN_ON(!mdev))
67262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	node = of_find_node_by_phandle(phandle);
67562306a36Sopenharmony_ci	if (!node) {
67662306a36Sopenharmony_ci		pr_err("%s: could not find node phandle 0x%x\n",
67762306a36Sopenharmony_ci		       __func__, phandle);
67862306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	mbox = omap_mbox_device_find(mdev, node->name);
68262306a36Sopenharmony_ci	of_node_put(node);
68362306a36Sopenharmony_ci	return mbox ? mbox->chan : ERR_PTR(-ENOENT);
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic int omap_mbox_probe(struct platform_device *pdev)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	int ret;
68962306a36Sopenharmony_ci	struct mbox_chan *chnls;
69062306a36Sopenharmony_ci	struct omap_mbox **list, *mbox, *mboxblk;
69162306a36Sopenharmony_ci	struct omap_mbox_fifo_info *finfo, *finfoblk;
69262306a36Sopenharmony_ci	struct omap_mbox_device *mdev;
69362306a36Sopenharmony_ci	struct omap_mbox_fifo *fifo;
69462306a36Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
69562306a36Sopenharmony_ci	struct device_node *child;
69662306a36Sopenharmony_ci	const struct omap_mbox_match_data *match_data;
69762306a36Sopenharmony_ci	u32 intr_type, info_count;
69862306a36Sopenharmony_ci	u32 num_users, num_fifos;
69962306a36Sopenharmony_ci	u32 tmp[3];
70062306a36Sopenharmony_ci	u32 l;
70162306a36Sopenharmony_ci	int i;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	if (!node) {
70462306a36Sopenharmony_ci		pr_err("%s: only DT-based devices are supported\n", __func__);
70562306a36Sopenharmony_ci		return -ENODEV;
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	match_data = of_device_get_match_data(&pdev->dev);
70962306a36Sopenharmony_ci	if (!match_data)
71062306a36Sopenharmony_ci		return -ENODEV;
71162306a36Sopenharmony_ci	intr_type = match_data->intr_type;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	if (of_property_read_u32(node, "ti,mbox-num-users", &num_users))
71462306a36Sopenharmony_ci		return -ENODEV;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	if (of_property_read_u32(node, "ti,mbox-num-fifos", &num_fifos))
71762306a36Sopenharmony_ci		return -ENODEV;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	info_count = of_get_available_child_count(node);
72062306a36Sopenharmony_ci	if (!info_count) {
72162306a36Sopenharmony_ci		dev_err(&pdev->dev, "no available mbox devices found\n");
72262306a36Sopenharmony_ci		return -ENODEV;
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	finfoblk = devm_kcalloc(&pdev->dev, info_count, sizeof(*finfoblk),
72662306a36Sopenharmony_ci				GFP_KERNEL);
72762306a36Sopenharmony_ci	if (!finfoblk)
72862306a36Sopenharmony_ci		return -ENOMEM;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	finfo = finfoblk;
73162306a36Sopenharmony_ci	child = NULL;
73262306a36Sopenharmony_ci	for (i = 0; i < info_count; i++, finfo++) {
73362306a36Sopenharmony_ci		child = of_get_next_available_child(node, child);
73462306a36Sopenharmony_ci		ret = of_property_read_u32_array(child, "ti,mbox-tx", tmp,
73562306a36Sopenharmony_ci						 ARRAY_SIZE(tmp));
73662306a36Sopenharmony_ci		if (ret)
73762306a36Sopenharmony_ci			return ret;
73862306a36Sopenharmony_ci		finfo->tx_id = tmp[0];
73962306a36Sopenharmony_ci		finfo->tx_irq = tmp[1];
74062306a36Sopenharmony_ci		finfo->tx_usr = tmp[2];
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		ret = of_property_read_u32_array(child, "ti,mbox-rx", tmp,
74362306a36Sopenharmony_ci						 ARRAY_SIZE(tmp));
74462306a36Sopenharmony_ci		if (ret)
74562306a36Sopenharmony_ci			return ret;
74662306a36Sopenharmony_ci		finfo->rx_id = tmp[0];
74762306a36Sopenharmony_ci		finfo->rx_irq = tmp[1];
74862306a36Sopenharmony_ci		finfo->rx_usr = tmp[2];
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci		finfo->name = child->name;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci		finfo->send_no_irq = of_property_read_bool(child, "ti,mbox-send-noirq");
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		if (finfo->tx_id >= num_fifos || finfo->rx_id >= num_fifos ||
75562306a36Sopenharmony_ci		    finfo->tx_usr >= num_users || finfo->rx_usr >= num_users)
75662306a36Sopenharmony_ci			return -EINVAL;
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL);
76062306a36Sopenharmony_ci	if (!mdev)
76162306a36Sopenharmony_ci		return -ENOMEM;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	mdev->mbox_base = devm_platform_ioremap_resource(pdev, 0);
76462306a36Sopenharmony_ci	if (IS_ERR(mdev->mbox_base))
76562306a36Sopenharmony_ci		return PTR_ERR(mdev->mbox_base);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	mdev->irq_ctx = devm_kcalloc(&pdev->dev, num_users, sizeof(u32),
76862306a36Sopenharmony_ci				     GFP_KERNEL);
76962306a36Sopenharmony_ci	if (!mdev->irq_ctx)
77062306a36Sopenharmony_ci		return -ENOMEM;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	/* allocate one extra for marking end of list */
77362306a36Sopenharmony_ci	list = devm_kcalloc(&pdev->dev, info_count + 1, sizeof(*list),
77462306a36Sopenharmony_ci			    GFP_KERNEL);
77562306a36Sopenharmony_ci	if (!list)
77662306a36Sopenharmony_ci		return -ENOMEM;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	chnls = devm_kcalloc(&pdev->dev, info_count + 1, sizeof(*chnls),
77962306a36Sopenharmony_ci			     GFP_KERNEL);
78062306a36Sopenharmony_ci	if (!chnls)
78162306a36Sopenharmony_ci		return -ENOMEM;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	mboxblk = devm_kcalloc(&pdev->dev, info_count, sizeof(*mbox),
78462306a36Sopenharmony_ci			       GFP_KERNEL);
78562306a36Sopenharmony_ci	if (!mboxblk)
78662306a36Sopenharmony_ci		return -ENOMEM;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	mbox = mboxblk;
78962306a36Sopenharmony_ci	finfo = finfoblk;
79062306a36Sopenharmony_ci	for (i = 0; i < info_count; i++, finfo++) {
79162306a36Sopenharmony_ci		fifo = &mbox->tx_fifo;
79262306a36Sopenharmony_ci		fifo->msg = MAILBOX_MESSAGE(finfo->tx_id);
79362306a36Sopenharmony_ci		fifo->fifo_stat = MAILBOX_FIFOSTATUS(finfo->tx_id);
79462306a36Sopenharmony_ci		fifo->intr_bit = MAILBOX_IRQ_NOTFULL(finfo->tx_id);
79562306a36Sopenharmony_ci		fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->tx_usr);
79662306a36Sopenharmony_ci		fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->tx_usr);
79762306a36Sopenharmony_ci		fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->tx_usr);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci		fifo = &mbox->rx_fifo;
80062306a36Sopenharmony_ci		fifo->msg = MAILBOX_MESSAGE(finfo->rx_id);
80162306a36Sopenharmony_ci		fifo->msg_stat =  MAILBOX_MSGSTATUS(finfo->rx_id);
80262306a36Sopenharmony_ci		fifo->intr_bit = MAILBOX_IRQ_NEWMSG(finfo->rx_id);
80362306a36Sopenharmony_ci		fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->rx_usr);
80462306a36Sopenharmony_ci		fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->rx_usr);
80562306a36Sopenharmony_ci		fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->rx_usr);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci		mbox->send_no_irq = finfo->send_no_irq;
80862306a36Sopenharmony_ci		mbox->intr_type = intr_type;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci		mbox->parent = mdev;
81162306a36Sopenharmony_ci		mbox->name = finfo->name;
81262306a36Sopenharmony_ci		mbox->irq = platform_get_irq(pdev, finfo->tx_irq);
81362306a36Sopenharmony_ci		if (mbox->irq < 0)
81462306a36Sopenharmony_ci			return mbox->irq;
81562306a36Sopenharmony_ci		mbox->chan = &chnls[i];
81662306a36Sopenharmony_ci		chnls[i].con_priv = mbox;
81762306a36Sopenharmony_ci		list[i] = mbox++;
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	mutex_init(&mdev->cfg_lock);
82162306a36Sopenharmony_ci	mdev->dev = &pdev->dev;
82262306a36Sopenharmony_ci	mdev->num_users = num_users;
82362306a36Sopenharmony_ci	mdev->num_fifos = num_fifos;
82462306a36Sopenharmony_ci	mdev->intr_type = intr_type;
82562306a36Sopenharmony_ci	mdev->mboxes = list;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	/*
82862306a36Sopenharmony_ci	 * OMAP/K3 Mailbox IP does not have a Tx-Done IRQ, but rather a Tx-Ready
82962306a36Sopenharmony_ci	 * IRQ and is needed to run the Tx state machine
83062306a36Sopenharmony_ci	 */
83162306a36Sopenharmony_ci	mdev->controller.txdone_irq = true;
83262306a36Sopenharmony_ci	mdev->controller.dev = mdev->dev;
83362306a36Sopenharmony_ci	mdev->controller.ops = &omap_mbox_chan_ops;
83462306a36Sopenharmony_ci	mdev->controller.chans = chnls;
83562306a36Sopenharmony_ci	mdev->controller.num_chans = info_count;
83662306a36Sopenharmony_ci	mdev->controller.of_xlate = omap_mbox_of_xlate;
83762306a36Sopenharmony_ci	ret = omap_mbox_register(mdev);
83862306a36Sopenharmony_ci	if (ret)
83962306a36Sopenharmony_ci		return ret;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	platform_set_drvdata(pdev, mdev);
84262306a36Sopenharmony_ci	pm_runtime_enable(mdev->dev);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(mdev->dev);
84562306a36Sopenharmony_ci	if (ret < 0)
84662306a36Sopenharmony_ci		goto unregister;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	/*
84962306a36Sopenharmony_ci	 * just print the raw revision register, the format is not
85062306a36Sopenharmony_ci	 * uniform across all SoCs
85162306a36Sopenharmony_ci	 */
85262306a36Sopenharmony_ci	l = mbox_read_reg(mdev, MAILBOX_REVISION);
85362306a36Sopenharmony_ci	dev_info(mdev->dev, "omap mailbox rev 0x%x\n", l);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	ret = pm_runtime_put_sync(mdev->dev);
85662306a36Sopenharmony_ci	if (ret < 0 && ret != -ENOSYS)
85762306a36Sopenharmony_ci		goto unregister;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	devm_kfree(&pdev->dev, finfoblk);
86062306a36Sopenharmony_ci	return 0;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ciunregister:
86362306a36Sopenharmony_ci	pm_runtime_disable(mdev->dev);
86462306a36Sopenharmony_ci	omap_mbox_unregister(mdev);
86562306a36Sopenharmony_ci	return ret;
86662306a36Sopenharmony_ci}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_cistatic int omap_mbox_remove(struct platform_device *pdev)
86962306a36Sopenharmony_ci{
87062306a36Sopenharmony_ci	struct omap_mbox_device *mdev = platform_get_drvdata(pdev);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	pm_runtime_disable(mdev->dev);
87362306a36Sopenharmony_ci	omap_mbox_unregister(mdev);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	return 0;
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic struct platform_driver omap_mbox_driver = {
87962306a36Sopenharmony_ci	.probe	= omap_mbox_probe,
88062306a36Sopenharmony_ci	.remove	= omap_mbox_remove,
88162306a36Sopenharmony_ci	.driver	= {
88262306a36Sopenharmony_ci		.name = "omap-mailbox",
88362306a36Sopenharmony_ci		.pm = &omap_mbox_pm_ops,
88462306a36Sopenharmony_ci		.of_match_table = of_match_ptr(omap_mailbox_of_match),
88562306a36Sopenharmony_ci	},
88662306a36Sopenharmony_ci};
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cistatic int __init omap_mbox_init(void)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	int err;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	err = class_register(&omap_mbox_class);
89362306a36Sopenharmony_ci	if (err)
89462306a36Sopenharmony_ci		return err;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	/* kfifo size sanity check: alignment and minimal size */
89762306a36Sopenharmony_ci	mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(u32));
89862306a36Sopenharmony_ci	mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size, sizeof(u32));
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	err = platform_driver_register(&omap_mbox_driver);
90162306a36Sopenharmony_ci	if (err)
90262306a36Sopenharmony_ci		class_unregister(&omap_mbox_class);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	return err;
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_cisubsys_initcall(omap_mbox_init);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic void __exit omap_mbox_exit(void)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	platform_driver_unregister(&omap_mbox_driver);
91162306a36Sopenharmony_ci	class_unregister(&omap_mbox_class);
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_cimodule_exit(omap_mbox_exit);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
91662306a36Sopenharmony_ciMODULE_DESCRIPTION("omap mailbox: interrupt driven messaging");
91762306a36Sopenharmony_ciMODULE_AUTHOR("Toshihiro Kobayashi");
91862306a36Sopenharmony_ciMODULE_AUTHOR("Hiroshi DOYU");
919