18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * OMAP mailbox driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved.
68c2ecf20Sopenharmony_ci * Copyright (C) 2013-2019 Texas Instruments Incorporated - https://www.ti.com
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
98c2ecf20Sopenharmony_ci *          Suman Anna <s-anna@ti.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
148c2ecf20Sopenharmony_ci#include <linux/mutex.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/kfifo.h>
178c2ecf20Sopenharmony_ci#include <linux/err.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/of_device.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
228c2ecf20Sopenharmony_ci#include <linux/omap-mailbox.h>
238c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h>
248c2ecf20Sopenharmony_ci#include <linux/mailbox_client.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "mailbox.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define MAILBOX_REVISION		0x000
298c2ecf20Sopenharmony_ci#define MAILBOX_MESSAGE(m)		(0x040 + 4 * (m))
308c2ecf20Sopenharmony_ci#define MAILBOX_FIFOSTATUS(m)		(0x080 + 4 * (m))
318c2ecf20Sopenharmony_ci#define MAILBOX_MSGSTATUS(m)		(0x0c0 + 4 * (m))
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define OMAP2_MAILBOX_IRQSTATUS(u)	(0x100 + 8 * (u))
348c2ecf20Sopenharmony_ci#define OMAP2_MAILBOX_IRQENABLE(u)	(0x104 + 8 * (u))
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define OMAP4_MAILBOX_IRQSTATUS(u)	(0x104 + 0x10 * (u))
378c2ecf20Sopenharmony_ci#define OMAP4_MAILBOX_IRQENABLE(u)	(0x108 + 0x10 * (u))
388c2ecf20Sopenharmony_ci#define OMAP4_MAILBOX_IRQENABLE_CLR(u)	(0x10c + 0x10 * (u))
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define MAILBOX_IRQSTATUS(type, u)	(type ? OMAP4_MAILBOX_IRQSTATUS(u) : \
418c2ecf20Sopenharmony_ci						OMAP2_MAILBOX_IRQSTATUS(u))
428c2ecf20Sopenharmony_ci#define MAILBOX_IRQENABLE(type, u)	(type ? OMAP4_MAILBOX_IRQENABLE(u) : \
438c2ecf20Sopenharmony_ci						OMAP2_MAILBOX_IRQENABLE(u))
448c2ecf20Sopenharmony_ci#define MAILBOX_IRQDISABLE(type, u)	(type ? OMAP4_MAILBOX_IRQENABLE_CLR(u) \
458c2ecf20Sopenharmony_ci						: OMAP2_MAILBOX_IRQENABLE(u))
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define MAILBOX_IRQ_NEWMSG(m)		(1 << (2 * (m)))
488c2ecf20Sopenharmony_ci#define MAILBOX_IRQ_NOTFULL(m)		(1 << (2 * (m) + 1))
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* Interrupt register configuration types */
518c2ecf20Sopenharmony_ci#define MBOX_INTR_CFG_TYPE1		0
528c2ecf20Sopenharmony_ci#define MBOX_INTR_CFG_TYPE2		1
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistruct omap_mbox_fifo {
558c2ecf20Sopenharmony_ci	unsigned long msg;
568c2ecf20Sopenharmony_ci	unsigned long fifo_stat;
578c2ecf20Sopenharmony_ci	unsigned long msg_stat;
588c2ecf20Sopenharmony_ci	unsigned long irqenable;
598c2ecf20Sopenharmony_ci	unsigned long irqstatus;
608c2ecf20Sopenharmony_ci	unsigned long irqdisable;
618c2ecf20Sopenharmony_ci	u32 intr_bit;
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistruct omap_mbox_queue {
658c2ecf20Sopenharmony_ci	spinlock_t		lock;
668c2ecf20Sopenharmony_ci	struct kfifo		fifo;
678c2ecf20Sopenharmony_ci	struct work_struct	work;
688c2ecf20Sopenharmony_ci	struct omap_mbox	*mbox;
698c2ecf20Sopenharmony_ci	bool full;
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct omap_mbox_match_data {
738c2ecf20Sopenharmony_ci	u32 intr_type;
748c2ecf20Sopenharmony_ci};
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistruct omap_mbox_device {
778c2ecf20Sopenharmony_ci	struct device *dev;
788c2ecf20Sopenharmony_ci	struct mutex cfg_lock;
798c2ecf20Sopenharmony_ci	void __iomem *mbox_base;
808c2ecf20Sopenharmony_ci	u32 *irq_ctx;
818c2ecf20Sopenharmony_ci	u32 num_users;
828c2ecf20Sopenharmony_ci	u32 num_fifos;
838c2ecf20Sopenharmony_ci	u32 intr_type;
848c2ecf20Sopenharmony_ci	struct omap_mbox **mboxes;
858c2ecf20Sopenharmony_ci	struct mbox_controller controller;
868c2ecf20Sopenharmony_ci	struct list_head elem;
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistruct omap_mbox_fifo_info {
908c2ecf20Sopenharmony_ci	int tx_id;
918c2ecf20Sopenharmony_ci	int tx_usr;
928c2ecf20Sopenharmony_ci	int tx_irq;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	int rx_id;
958c2ecf20Sopenharmony_ci	int rx_usr;
968c2ecf20Sopenharmony_ci	int rx_irq;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	const char *name;
998c2ecf20Sopenharmony_ci	bool send_no_irq;
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistruct omap_mbox {
1038c2ecf20Sopenharmony_ci	const char		*name;
1048c2ecf20Sopenharmony_ci	int			irq;
1058c2ecf20Sopenharmony_ci	struct omap_mbox_queue	*rxq;
1068c2ecf20Sopenharmony_ci	struct device		*dev;
1078c2ecf20Sopenharmony_ci	struct omap_mbox_device *parent;
1088c2ecf20Sopenharmony_ci	struct omap_mbox_fifo	tx_fifo;
1098c2ecf20Sopenharmony_ci	struct omap_mbox_fifo	rx_fifo;
1108c2ecf20Sopenharmony_ci	u32			intr_type;
1118c2ecf20Sopenharmony_ci	struct mbox_chan	*chan;
1128c2ecf20Sopenharmony_ci	bool			send_no_irq;
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/* global variables for the mailbox devices */
1168c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(omap_mbox_devices_lock);
1178c2ecf20Sopenharmony_cistatic LIST_HEAD(omap_mbox_devices);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE;
1208c2ecf20Sopenharmony_cimodule_param(mbox_kfifo_size, uint, S_IRUGO);
1218c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)");
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic struct omap_mbox *mbox_chan_to_omap_mbox(struct mbox_chan *chan)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	if (!chan || !chan->con_priv)
1268c2ecf20Sopenharmony_ci		return NULL;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	return (struct omap_mbox *)chan->con_priv;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic inline
1328c2ecf20Sopenharmony_ciunsigned int mbox_read_reg(struct omap_mbox_device *mdev, size_t ofs)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	return __raw_readl(mdev->mbox_base + ofs);
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic inline
1388c2ecf20Sopenharmony_civoid mbox_write_reg(struct omap_mbox_device *mdev, u32 val, size_t ofs)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	__raw_writel(val, mdev->mbox_base + ofs);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/* Mailbox FIFO handle functions */
1448c2ecf20Sopenharmony_cistatic u32 mbox_fifo_read(struct omap_mbox *mbox)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct omap_mbox_fifo *fifo = &mbox->rx_fifo;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return mbox_read_reg(mbox->parent, fifo->msg);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic void mbox_fifo_write(struct omap_mbox *mbox, u32 msg)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct omap_mbox_fifo *fifo = &mbox->tx_fifo;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	mbox_write_reg(mbox->parent, msg, fifo->msg);
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int mbox_fifo_empty(struct omap_mbox *mbox)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	struct omap_mbox_fifo *fifo = &mbox->rx_fifo;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return (mbox_read_reg(mbox->parent, fifo->msg_stat) == 0);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic int mbox_fifo_full(struct omap_mbox *mbox)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct omap_mbox_fifo *fifo = &mbox->tx_fifo;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return mbox_read_reg(mbox->parent, fifo->fifo_stat);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/* Mailbox IRQ handle functions */
1738c2ecf20Sopenharmony_cistatic void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
1768c2ecf20Sopenharmony_ci				&mbox->tx_fifo : &mbox->rx_fifo;
1778c2ecf20Sopenharmony_ci	u32 bit = fifo->intr_bit;
1788c2ecf20Sopenharmony_ci	u32 irqstatus = fifo->irqstatus;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	mbox_write_reg(mbox->parent, bit, irqstatus);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	/* Flush posted write for irq status to avoid spurious interrupts */
1838c2ecf20Sopenharmony_ci	mbox_read_reg(mbox->parent, irqstatus);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
1898c2ecf20Sopenharmony_ci				&mbox->tx_fifo : &mbox->rx_fifo;
1908c2ecf20Sopenharmony_ci	u32 bit = fifo->intr_bit;
1918c2ecf20Sopenharmony_ci	u32 irqenable = fifo->irqenable;
1928c2ecf20Sopenharmony_ci	u32 irqstatus = fifo->irqstatus;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	u32 enable = mbox_read_reg(mbox->parent, irqenable);
1958c2ecf20Sopenharmony_ci	u32 status = mbox_read_reg(mbox->parent, irqstatus);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return (int)(enable & status & bit);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic void _omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	u32 l;
2038c2ecf20Sopenharmony_ci	struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
2048c2ecf20Sopenharmony_ci				&mbox->tx_fifo : &mbox->rx_fifo;
2058c2ecf20Sopenharmony_ci	u32 bit = fifo->intr_bit;
2068c2ecf20Sopenharmony_ci	u32 irqenable = fifo->irqenable;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	l = mbox_read_reg(mbox->parent, irqenable);
2098c2ecf20Sopenharmony_ci	l |= bit;
2108c2ecf20Sopenharmony_ci	mbox_write_reg(mbox->parent, l, irqenable);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic void _omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
2168c2ecf20Sopenharmony_ci				&mbox->tx_fifo : &mbox->rx_fifo;
2178c2ecf20Sopenharmony_ci	u32 bit = fifo->intr_bit;
2188c2ecf20Sopenharmony_ci	u32 irqdisable = fifo->irqdisable;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/*
2218c2ecf20Sopenharmony_ci	 * Read and update the interrupt configuration register for pre-OMAP4.
2228c2ecf20Sopenharmony_ci	 * OMAP4 and later SoCs have a dedicated interrupt disabling register.
2238c2ecf20Sopenharmony_ci	 */
2248c2ecf20Sopenharmony_ci	if (!mbox->intr_type)
2258c2ecf20Sopenharmony_ci		bit = mbox_read_reg(mbox->parent, irqdisable) & ~bit;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	mbox_write_reg(mbox->parent, bit, irqdisable);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_civoid omap_mbox_enable_irq(struct mbox_chan *chan, omap_mbox_irq_t irq)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (WARN_ON(!mbox))
2358c2ecf20Sopenharmony_ci		return;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	_omap_mbox_enable_irq(mbox, irq);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(omap_mbox_enable_irq);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_civoid omap_mbox_disable_irq(struct mbox_chan *chan, omap_mbox_irq_t irq)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (WARN_ON(!mbox))
2468c2ecf20Sopenharmony_ci		return;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	_omap_mbox_disable_irq(mbox, irq);
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(omap_mbox_disable_irq);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci/*
2538c2ecf20Sopenharmony_ci * Message receiver(workqueue)
2548c2ecf20Sopenharmony_ci */
2558c2ecf20Sopenharmony_cistatic void mbox_rx_work(struct work_struct *work)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct omap_mbox_queue *mq =
2588c2ecf20Sopenharmony_ci			container_of(work, struct omap_mbox_queue, work);
2598c2ecf20Sopenharmony_ci	mbox_msg_t data;
2608c2ecf20Sopenharmony_ci	u32 msg;
2618c2ecf20Sopenharmony_ci	int len;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	while (kfifo_len(&mq->fifo) >= sizeof(msg)) {
2648c2ecf20Sopenharmony_ci		len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
2658c2ecf20Sopenharmony_ci		WARN_ON(len != sizeof(msg));
2668c2ecf20Sopenharmony_ci		data = msg;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		mbox_chan_received_data(mq->mbox->chan, (void *)data);
2698c2ecf20Sopenharmony_ci		spin_lock_irq(&mq->lock);
2708c2ecf20Sopenharmony_ci		if (mq->full) {
2718c2ecf20Sopenharmony_ci			mq->full = false;
2728c2ecf20Sopenharmony_ci			_omap_mbox_enable_irq(mq->mbox, IRQ_RX);
2738c2ecf20Sopenharmony_ci		}
2748c2ecf20Sopenharmony_ci		spin_unlock_irq(&mq->lock);
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci/*
2798c2ecf20Sopenharmony_ci * Mailbox interrupt handler
2808c2ecf20Sopenharmony_ci */
2818c2ecf20Sopenharmony_cistatic void __mbox_tx_interrupt(struct omap_mbox *mbox)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	_omap_mbox_disable_irq(mbox, IRQ_TX);
2848c2ecf20Sopenharmony_ci	ack_mbox_irq(mbox, IRQ_TX);
2858c2ecf20Sopenharmony_ci	mbox_chan_txdone(mbox->chan, 0);
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic void __mbox_rx_interrupt(struct omap_mbox *mbox)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	struct omap_mbox_queue *mq = mbox->rxq;
2918c2ecf20Sopenharmony_ci	u32 msg;
2928c2ecf20Sopenharmony_ci	int len;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	while (!mbox_fifo_empty(mbox)) {
2958c2ecf20Sopenharmony_ci		if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) {
2968c2ecf20Sopenharmony_ci			_omap_mbox_disable_irq(mbox, IRQ_RX);
2978c2ecf20Sopenharmony_ci			mq->full = true;
2988c2ecf20Sopenharmony_ci			goto nomem;
2998c2ecf20Sopenharmony_ci		}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci		msg = mbox_fifo_read(mbox);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
3048c2ecf20Sopenharmony_ci		WARN_ON(len != sizeof(msg));
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/* no more messages in the fifo. clear IRQ source. */
3088c2ecf20Sopenharmony_ci	ack_mbox_irq(mbox, IRQ_RX);
3098c2ecf20Sopenharmony_cinomem:
3108c2ecf20Sopenharmony_ci	schedule_work(&mbox->rxq->work);
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic irqreturn_t mbox_interrupt(int irq, void *p)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct omap_mbox *mbox = p;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	if (is_mbox_irq(mbox, IRQ_TX))
3188c2ecf20Sopenharmony_ci		__mbox_tx_interrupt(mbox);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (is_mbox_irq(mbox, IRQ_RX))
3218c2ecf20Sopenharmony_ci		__mbox_rx_interrupt(mbox);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox,
3278c2ecf20Sopenharmony_ci					void (*work)(struct work_struct *))
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct omap_mbox_queue *mq;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	if (!work)
3328c2ecf20Sopenharmony_ci		return NULL;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	mq = kzalloc(sizeof(*mq), GFP_KERNEL);
3358c2ecf20Sopenharmony_ci	if (!mq)
3368c2ecf20Sopenharmony_ci		return NULL;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	spin_lock_init(&mq->lock);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL))
3418c2ecf20Sopenharmony_ci		goto error;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	INIT_WORK(&mq->work, work);
3448c2ecf20Sopenharmony_ci	return mq;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cierror:
3478c2ecf20Sopenharmony_ci	kfree(mq);
3488c2ecf20Sopenharmony_ci	return NULL;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic void mbox_queue_free(struct omap_mbox_queue *q)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	kfifo_free(&q->fifo);
3548c2ecf20Sopenharmony_ci	kfree(q);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic int omap_mbox_startup(struct omap_mbox *mbox)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	int ret = 0;
3608c2ecf20Sopenharmony_ci	struct omap_mbox_queue *mq;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	mq = mbox_queue_alloc(mbox, mbox_rx_work);
3638c2ecf20Sopenharmony_ci	if (!mq)
3648c2ecf20Sopenharmony_ci		return -ENOMEM;
3658c2ecf20Sopenharmony_ci	mbox->rxq = mq;
3668c2ecf20Sopenharmony_ci	mq->mbox = mbox;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED,
3698c2ecf20Sopenharmony_ci			  mbox->name, mbox);
3708c2ecf20Sopenharmony_ci	if (unlikely(ret)) {
3718c2ecf20Sopenharmony_ci		pr_err("failed to register mailbox interrupt:%d\n", ret);
3728c2ecf20Sopenharmony_ci		goto fail_request_irq;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if (mbox->send_no_irq)
3768c2ecf20Sopenharmony_ci		mbox->chan->txdone_method = TXDONE_BY_ACK;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	_omap_mbox_enable_irq(mbox, IRQ_RX);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return 0;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cifail_request_irq:
3838c2ecf20Sopenharmony_ci	mbox_queue_free(mbox->rxq);
3848c2ecf20Sopenharmony_ci	return ret;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic void omap_mbox_fini(struct omap_mbox *mbox)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	_omap_mbox_disable_irq(mbox, IRQ_RX);
3908c2ecf20Sopenharmony_ci	free_irq(mbox->irq, mbox);
3918c2ecf20Sopenharmony_ci	flush_work(&mbox->rxq->work);
3928c2ecf20Sopenharmony_ci	mbox_queue_free(mbox->rxq);
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic struct omap_mbox *omap_mbox_device_find(struct omap_mbox_device *mdev,
3968c2ecf20Sopenharmony_ci					       const char *mbox_name)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	struct omap_mbox *_mbox, *mbox = NULL;
3998c2ecf20Sopenharmony_ci	struct omap_mbox **mboxes = mdev->mboxes;
4008c2ecf20Sopenharmony_ci	int i;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	if (!mboxes)
4038c2ecf20Sopenharmony_ci		return NULL;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	for (i = 0; (_mbox = mboxes[i]); i++) {
4068c2ecf20Sopenharmony_ci		if (!strcmp(_mbox->name, mbox_name)) {
4078c2ecf20Sopenharmony_ci			mbox = _mbox;
4088c2ecf20Sopenharmony_ci			break;
4098c2ecf20Sopenharmony_ci		}
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci	return mbox;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistruct mbox_chan *omap_mbox_request_channel(struct mbox_client *cl,
4158c2ecf20Sopenharmony_ci					    const char *chan_name)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct device *dev = cl->dev;
4188c2ecf20Sopenharmony_ci	struct omap_mbox *mbox = NULL;
4198c2ecf20Sopenharmony_ci	struct omap_mbox_device *mdev;
4208c2ecf20Sopenharmony_ci	struct mbox_chan *chan;
4218c2ecf20Sopenharmony_ci	unsigned long flags;
4228c2ecf20Sopenharmony_ci	int ret;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (!dev)
4258c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (dev->of_node) {
4288c2ecf20Sopenharmony_ci		pr_err("%s: please use mbox_request_channel(), this API is supported only for OMAP non-DT usage\n",
4298c2ecf20Sopenharmony_ci		       __func__);
4308c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	mutex_lock(&omap_mbox_devices_lock);
4348c2ecf20Sopenharmony_ci	list_for_each_entry(mdev, &omap_mbox_devices, elem) {
4358c2ecf20Sopenharmony_ci		mbox = omap_mbox_device_find(mdev, chan_name);
4368c2ecf20Sopenharmony_ci		if (mbox)
4378c2ecf20Sopenharmony_ci			break;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci	mutex_unlock(&omap_mbox_devices_lock);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (!mbox || !mbox->chan)
4428c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOENT);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	chan = mbox->chan;
4458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
4468c2ecf20Sopenharmony_ci	chan->msg_free = 0;
4478c2ecf20Sopenharmony_ci	chan->msg_count = 0;
4488c2ecf20Sopenharmony_ci	chan->active_req = NULL;
4498c2ecf20Sopenharmony_ci	chan->cl = cl;
4508c2ecf20Sopenharmony_ci	init_completion(&chan->tx_complete);
4518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	ret = chan->mbox->ops->startup(chan);
4548c2ecf20Sopenharmony_ci	if (ret) {
4558c2ecf20Sopenharmony_ci		pr_err("Unable to startup the chan (%d)\n", ret);
4568c2ecf20Sopenharmony_ci		mbox_free_channel(chan);
4578c2ecf20Sopenharmony_ci		chan = ERR_PTR(ret);
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	return chan;
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(omap_mbox_request_channel);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic struct class omap_mbox_class = { .name = "mbox", };
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic int omap_mbox_register(struct omap_mbox_device *mdev)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	int ret;
4698c2ecf20Sopenharmony_ci	int i;
4708c2ecf20Sopenharmony_ci	struct omap_mbox **mboxes;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	if (!mdev || !mdev->mboxes)
4738c2ecf20Sopenharmony_ci		return -EINVAL;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	mboxes = mdev->mboxes;
4768c2ecf20Sopenharmony_ci	for (i = 0; mboxes[i]; i++) {
4778c2ecf20Sopenharmony_ci		struct omap_mbox *mbox = mboxes[i];
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci		mbox->dev = device_create(&omap_mbox_class, mdev->dev,
4808c2ecf20Sopenharmony_ci					0, mbox, "%s", mbox->name);
4818c2ecf20Sopenharmony_ci		if (IS_ERR(mbox->dev)) {
4828c2ecf20Sopenharmony_ci			ret = PTR_ERR(mbox->dev);
4838c2ecf20Sopenharmony_ci			goto err_out;
4848c2ecf20Sopenharmony_ci		}
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	mutex_lock(&omap_mbox_devices_lock);
4888c2ecf20Sopenharmony_ci	list_add(&mdev->elem, &omap_mbox_devices);
4898c2ecf20Sopenharmony_ci	mutex_unlock(&omap_mbox_devices_lock);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	ret = devm_mbox_controller_register(mdev->dev, &mdev->controller);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cierr_out:
4948c2ecf20Sopenharmony_ci	if (ret) {
4958c2ecf20Sopenharmony_ci		while (i--)
4968c2ecf20Sopenharmony_ci			device_unregister(mboxes[i]->dev);
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci	return ret;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic int omap_mbox_unregister(struct omap_mbox_device *mdev)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	int i;
5048c2ecf20Sopenharmony_ci	struct omap_mbox **mboxes;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (!mdev || !mdev->mboxes)
5078c2ecf20Sopenharmony_ci		return -EINVAL;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	mutex_lock(&omap_mbox_devices_lock);
5108c2ecf20Sopenharmony_ci	list_del(&mdev->elem);
5118c2ecf20Sopenharmony_ci	mutex_unlock(&omap_mbox_devices_lock);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	mboxes = mdev->mboxes;
5148c2ecf20Sopenharmony_ci	for (i = 0; mboxes[i]; i++)
5158c2ecf20Sopenharmony_ci		device_unregister(mboxes[i]->dev);
5168c2ecf20Sopenharmony_ci	return 0;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic int omap_mbox_chan_startup(struct mbox_chan *chan)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
5228c2ecf20Sopenharmony_ci	struct omap_mbox_device *mdev = mbox->parent;
5238c2ecf20Sopenharmony_ci	int ret = 0;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	mutex_lock(&mdev->cfg_lock);
5268c2ecf20Sopenharmony_ci	pm_runtime_get_sync(mdev->dev);
5278c2ecf20Sopenharmony_ci	ret = omap_mbox_startup(mbox);
5288c2ecf20Sopenharmony_ci	if (ret)
5298c2ecf20Sopenharmony_ci		pm_runtime_put_sync(mdev->dev);
5308c2ecf20Sopenharmony_ci	mutex_unlock(&mdev->cfg_lock);
5318c2ecf20Sopenharmony_ci	return ret;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic void omap_mbox_chan_shutdown(struct mbox_chan *chan)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
5378c2ecf20Sopenharmony_ci	struct omap_mbox_device *mdev = mbox->parent;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	mutex_lock(&mdev->cfg_lock);
5408c2ecf20Sopenharmony_ci	omap_mbox_fini(mbox);
5418c2ecf20Sopenharmony_ci	pm_runtime_put_sync(mdev->dev);
5428c2ecf20Sopenharmony_ci	mutex_unlock(&mdev->cfg_lock);
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic int omap_mbox_chan_send_noirq(struct omap_mbox *mbox, u32 msg)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	int ret = -EBUSY;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	if (!mbox_fifo_full(mbox)) {
5508c2ecf20Sopenharmony_ci		_omap_mbox_enable_irq(mbox, IRQ_RX);
5518c2ecf20Sopenharmony_ci		mbox_fifo_write(mbox, msg);
5528c2ecf20Sopenharmony_ci		ret = 0;
5538c2ecf20Sopenharmony_ci		_omap_mbox_disable_irq(mbox, IRQ_RX);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci		/* we must read and ack the interrupt directly from here */
5568c2ecf20Sopenharmony_ci		mbox_fifo_read(mbox);
5578c2ecf20Sopenharmony_ci		ack_mbox_irq(mbox, IRQ_RX);
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	return ret;
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cistatic int omap_mbox_chan_send(struct omap_mbox *mbox, u32 msg)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	int ret = -EBUSY;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	if (!mbox_fifo_full(mbox)) {
5688c2ecf20Sopenharmony_ci		mbox_fifo_write(mbox, msg);
5698c2ecf20Sopenharmony_ci		ret = 0;
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	/* always enable the interrupt */
5738c2ecf20Sopenharmony_ci	_omap_mbox_enable_irq(mbox, IRQ_TX);
5748c2ecf20Sopenharmony_ci	return ret;
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_cistatic int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
5808c2ecf20Sopenharmony_ci	int ret;
5818c2ecf20Sopenharmony_ci	u32 msg = omap_mbox_message(data);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	if (!mbox)
5848c2ecf20Sopenharmony_ci		return -EINVAL;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	if (mbox->send_no_irq)
5878c2ecf20Sopenharmony_ci		ret = omap_mbox_chan_send_noirq(mbox, msg);
5888c2ecf20Sopenharmony_ci	else
5898c2ecf20Sopenharmony_ci		ret = omap_mbox_chan_send(mbox, msg);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	return ret;
5928c2ecf20Sopenharmony_ci}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_cistatic const struct mbox_chan_ops omap_mbox_chan_ops = {
5958c2ecf20Sopenharmony_ci	.startup        = omap_mbox_chan_startup,
5968c2ecf20Sopenharmony_ci	.send_data      = omap_mbox_chan_send_data,
5978c2ecf20Sopenharmony_ci	.shutdown       = omap_mbox_chan_shutdown,
5988c2ecf20Sopenharmony_ci};
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
6018c2ecf20Sopenharmony_cistatic int omap_mbox_suspend(struct device *dev)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	struct omap_mbox_device *mdev = dev_get_drvdata(dev);
6048c2ecf20Sopenharmony_ci	u32 usr, fifo, reg;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	if (pm_runtime_status_suspended(dev))
6078c2ecf20Sopenharmony_ci		return 0;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	for (fifo = 0; fifo < mdev->num_fifos; fifo++) {
6108c2ecf20Sopenharmony_ci		if (mbox_read_reg(mdev, MAILBOX_MSGSTATUS(fifo))) {
6118c2ecf20Sopenharmony_ci			dev_err(mdev->dev, "fifo %d has unexpected unread messages\n",
6128c2ecf20Sopenharmony_ci				fifo);
6138c2ecf20Sopenharmony_ci			return -EBUSY;
6148c2ecf20Sopenharmony_ci		}
6158c2ecf20Sopenharmony_ci	}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	for (usr = 0; usr < mdev->num_users; usr++) {
6188c2ecf20Sopenharmony_ci		reg = MAILBOX_IRQENABLE(mdev->intr_type, usr);
6198c2ecf20Sopenharmony_ci		mdev->irq_ctx[usr] = mbox_read_reg(mdev, reg);
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	return 0;
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic int omap_mbox_resume(struct device *dev)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	struct omap_mbox_device *mdev = dev_get_drvdata(dev);
6288c2ecf20Sopenharmony_ci	u32 usr, reg;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	if (pm_runtime_status_suspended(dev))
6318c2ecf20Sopenharmony_ci		return 0;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	for (usr = 0; usr < mdev->num_users; usr++) {
6348c2ecf20Sopenharmony_ci		reg = MAILBOX_IRQENABLE(mdev->intr_type, usr);
6358c2ecf20Sopenharmony_ci		mbox_write_reg(mdev, mdev->irq_ctx[usr], reg);
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	return 0;
6398c2ecf20Sopenharmony_ci}
6408c2ecf20Sopenharmony_ci#endif
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cistatic const struct dev_pm_ops omap_mbox_pm_ops = {
6438c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(omap_mbox_suspend, omap_mbox_resume)
6448c2ecf20Sopenharmony_ci};
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic const struct omap_mbox_match_data omap2_data = { MBOX_INTR_CFG_TYPE1 };
6478c2ecf20Sopenharmony_cistatic const struct omap_mbox_match_data omap4_data = { MBOX_INTR_CFG_TYPE2 };
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic const struct of_device_id omap_mailbox_of_match[] = {
6508c2ecf20Sopenharmony_ci	{
6518c2ecf20Sopenharmony_ci		.compatible	= "ti,omap2-mailbox",
6528c2ecf20Sopenharmony_ci		.data		= &omap2_data,
6538c2ecf20Sopenharmony_ci	},
6548c2ecf20Sopenharmony_ci	{
6558c2ecf20Sopenharmony_ci		.compatible	= "ti,omap3-mailbox",
6568c2ecf20Sopenharmony_ci		.data		= &omap2_data,
6578c2ecf20Sopenharmony_ci	},
6588c2ecf20Sopenharmony_ci	{
6598c2ecf20Sopenharmony_ci		.compatible	= "ti,omap4-mailbox",
6608c2ecf20Sopenharmony_ci		.data		= &omap4_data,
6618c2ecf20Sopenharmony_ci	},
6628c2ecf20Sopenharmony_ci	{
6638c2ecf20Sopenharmony_ci		.compatible	= "ti,am654-mailbox",
6648c2ecf20Sopenharmony_ci		.data		= &omap4_data,
6658c2ecf20Sopenharmony_ci	},
6668c2ecf20Sopenharmony_ci	{
6678c2ecf20Sopenharmony_ci		/* end */
6688c2ecf20Sopenharmony_ci	},
6698c2ecf20Sopenharmony_ci};
6708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_mailbox_of_match);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cistatic struct mbox_chan *omap_mbox_of_xlate(struct mbox_controller *controller,
6738c2ecf20Sopenharmony_ci					    const struct of_phandle_args *sp)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	phandle phandle = sp->args[0];
6768c2ecf20Sopenharmony_ci	struct device_node *node;
6778c2ecf20Sopenharmony_ci	struct omap_mbox_device *mdev;
6788c2ecf20Sopenharmony_ci	struct omap_mbox *mbox;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	mdev = container_of(controller, struct omap_mbox_device, controller);
6818c2ecf20Sopenharmony_ci	if (WARN_ON(!mdev))
6828c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	node = of_find_node_by_phandle(phandle);
6858c2ecf20Sopenharmony_ci	if (!node) {
6868c2ecf20Sopenharmony_ci		pr_err("%s: could not find node phandle 0x%x\n",
6878c2ecf20Sopenharmony_ci		       __func__, phandle);
6888c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
6898c2ecf20Sopenharmony_ci	}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	mbox = omap_mbox_device_find(mdev, node->name);
6928c2ecf20Sopenharmony_ci	of_node_put(node);
6938c2ecf20Sopenharmony_ci	return mbox ? mbox->chan : ERR_PTR(-ENOENT);
6948c2ecf20Sopenharmony_ci}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_cistatic int omap_mbox_probe(struct platform_device *pdev)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	struct resource *mem;
6998c2ecf20Sopenharmony_ci	int ret;
7008c2ecf20Sopenharmony_ci	struct mbox_chan *chnls;
7018c2ecf20Sopenharmony_ci	struct omap_mbox **list, *mbox, *mboxblk;
7028c2ecf20Sopenharmony_ci	struct omap_mbox_fifo_info *finfo, *finfoblk;
7038c2ecf20Sopenharmony_ci	struct omap_mbox_device *mdev;
7048c2ecf20Sopenharmony_ci	struct omap_mbox_fifo *fifo;
7058c2ecf20Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
7068c2ecf20Sopenharmony_ci	struct device_node *child;
7078c2ecf20Sopenharmony_ci	const struct omap_mbox_match_data *match_data;
7088c2ecf20Sopenharmony_ci	u32 intr_type, info_count;
7098c2ecf20Sopenharmony_ci	u32 num_users, num_fifos;
7108c2ecf20Sopenharmony_ci	u32 tmp[3];
7118c2ecf20Sopenharmony_ci	u32 l;
7128c2ecf20Sopenharmony_ci	int i;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	if (!node) {
7158c2ecf20Sopenharmony_ci		pr_err("%s: only DT-based devices are supported\n", __func__);
7168c2ecf20Sopenharmony_ci		return -ENODEV;
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	match_data = of_device_get_match_data(&pdev->dev);
7208c2ecf20Sopenharmony_ci	if (!match_data)
7218c2ecf20Sopenharmony_ci		return -ENODEV;
7228c2ecf20Sopenharmony_ci	intr_type = match_data->intr_type;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	if (of_property_read_u32(node, "ti,mbox-num-users", &num_users))
7258c2ecf20Sopenharmony_ci		return -ENODEV;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	if (of_property_read_u32(node, "ti,mbox-num-fifos", &num_fifos))
7288c2ecf20Sopenharmony_ci		return -ENODEV;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	info_count = of_get_available_child_count(node);
7318c2ecf20Sopenharmony_ci	if (!info_count) {
7328c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no available mbox devices found\n");
7338c2ecf20Sopenharmony_ci		return -ENODEV;
7348c2ecf20Sopenharmony_ci	}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	finfoblk = devm_kcalloc(&pdev->dev, info_count, sizeof(*finfoblk),
7378c2ecf20Sopenharmony_ci				GFP_KERNEL);
7388c2ecf20Sopenharmony_ci	if (!finfoblk)
7398c2ecf20Sopenharmony_ci		return -ENOMEM;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	finfo = finfoblk;
7428c2ecf20Sopenharmony_ci	child = NULL;
7438c2ecf20Sopenharmony_ci	for (i = 0; i < info_count; i++, finfo++) {
7448c2ecf20Sopenharmony_ci		child = of_get_next_available_child(node, child);
7458c2ecf20Sopenharmony_ci		ret = of_property_read_u32_array(child, "ti,mbox-tx", tmp,
7468c2ecf20Sopenharmony_ci						 ARRAY_SIZE(tmp));
7478c2ecf20Sopenharmony_ci		if (ret)
7488c2ecf20Sopenharmony_ci			return ret;
7498c2ecf20Sopenharmony_ci		finfo->tx_id = tmp[0];
7508c2ecf20Sopenharmony_ci		finfo->tx_irq = tmp[1];
7518c2ecf20Sopenharmony_ci		finfo->tx_usr = tmp[2];
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci		ret = of_property_read_u32_array(child, "ti,mbox-rx", tmp,
7548c2ecf20Sopenharmony_ci						 ARRAY_SIZE(tmp));
7558c2ecf20Sopenharmony_ci		if (ret)
7568c2ecf20Sopenharmony_ci			return ret;
7578c2ecf20Sopenharmony_ci		finfo->rx_id = tmp[0];
7588c2ecf20Sopenharmony_ci		finfo->rx_irq = tmp[1];
7598c2ecf20Sopenharmony_ci		finfo->rx_usr = tmp[2];
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci		finfo->name = child->name;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci		if (of_find_property(child, "ti,mbox-send-noirq", NULL))
7648c2ecf20Sopenharmony_ci			finfo->send_no_irq = true;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci		if (finfo->tx_id >= num_fifos || finfo->rx_id >= num_fifos ||
7678c2ecf20Sopenharmony_ci		    finfo->tx_usr >= num_users || finfo->rx_usr >= num_users)
7688c2ecf20Sopenharmony_ci			return -EINVAL;
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL);
7728c2ecf20Sopenharmony_ci	if (!mdev)
7738c2ecf20Sopenharmony_ci		return -ENOMEM;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
7768c2ecf20Sopenharmony_ci	mdev->mbox_base = devm_ioremap_resource(&pdev->dev, mem);
7778c2ecf20Sopenharmony_ci	if (IS_ERR(mdev->mbox_base))
7788c2ecf20Sopenharmony_ci		return PTR_ERR(mdev->mbox_base);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	mdev->irq_ctx = devm_kcalloc(&pdev->dev, num_users, sizeof(u32),
7818c2ecf20Sopenharmony_ci				     GFP_KERNEL);
7828c2ecf20Sopenharmony_ci	if (!mdev->irq_ctx)
7838c2ecf20Sopenharmony_ci		return -ENOMEM;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	/* allocate one extra for marking end of list */
7868c2ecf20Sopenharmony_ci	list = devm_kcalloc(&pdev->dev, info_count + 1, sizeof(*list),
7878c2ecf20Sopenharmony_ci			    GFP_KERNEL);
7888c2ecf20Sopenharmony_ci	if (!list)
7898c2ecf20Sopenharmony_ci		return -ENOMEM;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	chnls = devm_kcalloc(&pdev->dev, info_count + 1, sizeof(*chnls),
7928c2ecf20Sopenharmony_ci			     GFP_KERNEL);
7938c2ecf20Sopenharmony_ci	if (!chnls)
7948c2ecf20Sopenharmony_ci		return -ENOMEM;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	mboxblk = devm_kcalloc(&pdev->dev, info_count, sizeof(*mbox),
7978c2ecf20Sopenharmony_ci			       GFP_KERNEL);
7988c2ecf20Sopenharmony_ci	if (!mboxblk)
7998c2ecf20Sopenharmony_ci		return -ENOMEM;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	mbox = mboxblk;
8028c2ecf20Sopenharmony_ci	finfo = finfoblk;
8038c2ecf20Sopenharmony_ci	for (i = 0; i < info_count; i++, finfo++) {
8048c2ecf20Sopenharmony_ci		fifo = &mbox->tx_fifo;
8058c2ecf20Sopenharmony_ci		fifo->msg = MAILBOX_MESSAGE(finfo->tx_id);
8068c2ecf20Sopenharmony_ci		fifo->fifo_stat = MAILBOX_FIFOSTATUS(finfo->tx_id);
8078c2ecf20Sopenharmony_ci		fifo->intr_bit = MAILBOX_IRQ_NOTFULL(finfo->tx_id);
8088c2ecf20Sopenharmony_ci		fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->tx_usr);
8098c2ecf20Sopenharmony_ci		fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->tx_usr);
8108c2ecf20Sopenharmony_ci		fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->tx_usr);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci		fifo = &mbox->rx_fifo;
8138c2ecf20Sopenharmony_ci		fifo->msg = MAILBOX_MESSAGE(finfo->rx_id);
8148c2ecf20Sopenharmony_ci		fifo->msg_stat =  MAILBOX_MSGSTATUS(finfo->rx_id);
8158c2ecf20Sopenharmony_ci		fifo->intr_bit = MAILBOX_IRQ_NEWMSG(finfo->rx_id);
8168c2ecf20Sopenharmony_ci		fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->rx_usr);
8178c2ecf20Sopenharmony_ci		fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->rx_usr);
8188c2ecf20Sopenharmony_ci		fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->rx_usr);
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci		mbox->send_no_irq = finfo->send_no_irq;
8218c2ecf20Sopenharmony_ci		mbox->intr_type = intr_type;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci		mbox->parent = mdev;
8248c2ecf20Sopenharmony_ci		mbox->name = finfo->name;
8258c2ecf20Sopenharmony_ci		mbox->irq = platform_get_irq(pdev, finfo->tx_irq);
8268c2ecf20Sopenharmony_ci		if (mbox->irq < 0)
8278c2ecf20Sopenharmony_ci			return mbox->irq;
8288c2ecf20Sopenharmony_ci		mbox->chan = &chnls[i];
8298c2ecf20Sopenharmony_ci		chnls[i].con_priv = mbox;
8308c2ecf20Sopenharmony_ci		list[i] = mbox++;
8318c2ecf20Sopenharmony_ci	}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	mutex_init(&mdev->cfg_lock);
8348c2ecf20Sopenharmony_ci	mdev->dev = &pdev->dev;
8358c2ecf20Sopenharmony_ci	mdev->num_users = num_users;
8368c2ecf20Sopenharmony_ci	mdev->num_fifos = num_fifos;
8378c2ecf20Sopenharmony_ci	mdev->intr_type = intr_type;
8388c2ecf20Sopenharmony_ci	mdev->mboxes = list;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	/*
8418c2ecf20Sopenharmony_ci	 * OMAP/K3 Mailbox IP does not have a Tx-Done IRQ, but rather a Tx-Ready
8428c2ecf20Sopenharmony_ci	 * IRQ and is needed to run the Tx state machine
8438c2ecf20Sopenharmony_ci	 */
8448c2ecf20Sopenharmony_ci	mdev->controller.txdone_irq = true;
8458c2ecf20Sopenharmony_ci	mdev->controller.dev = mdev->dev;
8468c2ecf20Sopenharmony_ci	mdev->controller.ops = &omap_mbox_chan_ops;
8478c2ecf20Sopenharmony_ci	mdev->controller.chans = chnls;
8488c2ecf20Sopenharmony_ci	mdev->controller.num_chans = info_count;
8498c2ecf20Sopenharmony_ci	mdev->controller.of_xlate = omap_mbox_of_xlate;
8508c2ecf20Sopenharmony_ci	ret = omap_mbox_register(mdev);
8518c2ecf20Sopenharmony_ci	if (ret)
8528c2ecf20Sopenharmony_ci		return ret;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, mdev);
8558c2ecf20Sopenharmony_ci	pm_runtime_enable(mdev->dev);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	ret = pm_runtime_get_sync(mdev->dev);
8588c2ecf20Sopenharmony_ci	if (ret < 0) {
8598c2ecf20Sopenharmony_ci		pm_runtime_put_noidle(mdev->dev);
8608c2ecf20Sopenharmony_ci		goto unregister;
8618c2ecf20Sopenharmony_ci	}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	/*
8648c2ecf20Sopenharmony_ci	 * just print the raw revision register, the format is not
8658c2ecf20Sopenharmony_ci	 * uniform across all SoCs
8668c2ecf20Sopenharmony_ci	 */
8678c2ecf20Sopenharmony_ci	l = mbox_read_reg(mdev, MAILBOX_REVISION);
8688c2ecf20Sopenharmony_ci	dev_info(mdev->dev, "omap mailbox rev 0x%x\n", l);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	ret = pm_runtime_put_sync(mdev->dev);
8718c2ecf20Sopenharmony_ci	if (ret < 0 && ret != -ENOSYS)
8728c2ecf20Sopenharmony_ci		goto unregister;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	devm_kfree(&pdev->dev, finfoblk);
8758c2ecf20Sopenharmony_ci	return 0;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ciunregister:
8788c2ecf20Sopenharmony_ci	pm_runtime_disable(mdev->dev);
8798c2ecf20Sopenharmony_ci	omap_mbox_unregister(mdev);
8808c2ecf20Sopenharmony_ci	return ret;
8818c2ecf20Sopenharmony_ci}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_cistatic int omap_mbox_remove(struct platform_device *pdev)
8848c2ecf20Sopenharmony_ci{
8858c2ecf20Sopenharmony_ci	struct omap_mbox_device *mdev = platform_get_drvdata(pdev);
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	pm_runtime_disable(mdev->dev);
8888c2ecf20Sopenharmony_ci	omap_mbox_unregister(mdev);
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	return 0;
8918c2ecf20Sopenharmony_ci}
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_cistatic struct platform_driver omap_mbox_driver = {
8948c2ecf20Sopenharmony_ci	.probe	= omap_mbox_probe,
8958c2ecf20Sopenharmony_ci	.remove	= omap_mbox_remove,
8968c2ecf20Sopenharmony_ci	.driver	= {
8978c2ecf20Sopenharmony_ci		.name = "omap-mailbox",
8988c2ecf20Sopenharmony_ci		.pm = &omap_mbox_pm_ops,
8998c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(omap_mailbox_of_match),
9008c2ecf20Sopenharmony_ci	},
9018c2ecf20Sopenharmony_ci};
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_cistatic int __init omap_mbox_init(void)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	int err;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	err = class_register(&omap_mbox_class);
9088c2ecf20Sopenharmony_ci	if (err)
9098c2ecf20Sopenharmony_ci		return err;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	/* kfifo size sanity check: alignment and minimal size */
9128c2ecf20Sopenharmony_ci	mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(u32));
9138c2ecf20Sopenharmony_ci	mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size, sizeof(u32));
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	err = platform_driver_register(&omap_mbox_driver);
9168c2ecf20Sopenharmony_ci	if (err)
9178c2ecf20Sopenharmony_ci		class_unregister(&omap_mbox_class);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	return err;
9208c2ecf20Sopenharmony_ci}
9218c2ecf20Sopenharmony_cisubsys_initcall(omap_mbox_init);
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_cistatic void __exit omap_mbox_exit(void)
9248c2ecf20Sopenharmony_ci{
9258c2ecf20Sopenharmony_ci	platform_driver_unregister(&omap_mbox_driver);
9268c2ecf20Sopenharmony_ci	class_unregister(&omap_mbox_class);
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_cimodule_exit(omap_mbox_exit);
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
9318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("omap mailbox: interrupt driven messaging");
9328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Toshihiro Kobayashi");
9338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hiroshi DOYU");
934