141c0e939SKaihua Zhong // SPDX-License-Identifier: GPL-2.0
2c7701684SHao Fang // Copyright (c) 2017-2018 HiSilicon Limited.
341c0e939SKaihua Zhong // Copyright (c) 2017-2018 Linaro Limited.
441c0e939SKaihua Zhong 
541c0e939SKaihua Zhong #include <linux/bitops.h>
641c0e939SKaihua Zhong #include <linux/delay.h>
741c0e939SKaihua Zhong #include <linux/device.h>
841c0e939SKaihua Zhong #include <linux/err.h>
941c0e939SKaihua Zhong #include <linux/interrupt.h>
1041c0e939SKaihua Zhong #include <linux/io.h>
1141c0e939SKaihua Zhong #include <linux/iopoll.h>
1241c0e939SKaihua Zhong #include <linux/mailbox_controller.h>
1341c0e939SKaihua Zhong #include <linux/module.h>
14*e9803aacSRob Herring #include <linux/of.h>
1541c0e939SKaihua Zhong #include <linux/platform_device.h>
1641c0e939SKaihua Zhong #include <linux/slab.h>
1741c0e939SKaihua Zhong 
1841c0e939SKaihua Zhong #include "mailbox.h"
1941c0e939SKaihua Zhong 
2041c0e939SKaihua Zhong #define MBOX_CHAN_MAX			32
2141c0e939SKaihua Zhong 
2241c0e939SKaihua Zhong #define MBOX_RX				0x0
2341c0e939SKaihua Zhong #define MBOX_TX				0x1
2441c0e939SKaihua Zhong 
2541c0e939SKaihua Zhong #define MBOX_BASE(mbox, ch)		((mbox)->base + ((ch) * 0x40))
2641c0e939SKaihua Zhong #define MBOX_SRC_REG			0x00
2741c0e939SKaihua Zhong #define MBOX_DST_REG			0x04
2841c0e939SKaihua Zhong #define MBOX_DCLR_REG			0x08
2941c0e939SKaihua Zhong #define MBOX_DSTAT_REG			0x0c
3041c0e939SKaihua Zhong #define MBOX_MODE_REG			0x10
3141c0e939SKaihua Zhong #define MBOX_IMASK_REG			0x14
3241c0e939SKaihua Zhong #define MBOX_ICLR_REG			0x18
3341c0e939SKaihua Zhong #define MBOX_SEND_REG			0x1c
3441c0e939SKaihua Zhong #define MBOX_DATA_REG			0x20
3541c0e939SKaihua Zhong 
3641c0e939SKaihua Zhong #define MBOX_IPC_LOCK_REG		0xa00
3741c0e939SKaihua Zhong #define MBOX_IPC_UNLOCK			0x1acce551
3841c0e939SKaihua Zhong 
3941c0e939SKaihua Zhong #define MBOX_AUTOMATIC_ACK		1
4041c0e939SKaihua Zhong 
4141c0e939SKaihua Zhong #define MBOX_STATE_IDLE			BIT(4)
422e4ac7ccSKevin Wangtao #define MBOX_STATE_READY		BIT(5)
4341c0e939SKaihua Zhong #define MBOX_STATE_ACK			BIT(7)
4441c0e939SKaihua Zhong 
4541c0e939SKaihua Zhong #define MBOX_MSG_LEN			8
4641c0e939SKaihua Zhong 
4741c0e939SKaihua Zhong /**
4879daec8bSRandy Dunlap  * struct hi3660_chan_info - Hi3660 mailbox channel information
4979daec8bSRandy Dunlap  * @dst_irq:	Interrupt vector for remote processor
5079daec8bSRandy Dunlap  * @ack_irq:	Interrupt vector for local processor
5141c0e939SKaihua Zhong  *
5241c0e939SKaihua Zhong  * A channel can be used for TX or RX, it can trigger remote
5341c0e939SKaihua Zhong  * processor interrupt to notify remote processor and can receive
5479daec8bSRandy Dunlap  * interrupt if it has an incoming message.
5541c0e939SKaihua Zhong  */
5641c0e939SKaihua Zhong struct hi3660_chan_info {
5741c0e939SKaihua Zhong 	unsigned int dst_irq;
5841c0e939SKaihua Zhong 	unsigned int ack_irq;
5941c0e939SKaihua Zhong };
6041c0e939SKaihua Zhong 
6141c0e939SKaihua Zhong /**
6279daec8bSRandy Dunlap  * struct hi3660_mbox - Hi3660 mailbox controller data
6341c0e939SKaihua Zhong  * @dev:	Device to which it is attached
6441c0e939SKaihua Zhong  * @base:	Base address of the register mapping region
6541c0e939SKaihua Zhong  * @chan:	Representation of channels in mailbox controller
6641c0e939SKaihua Zhong  * @mchan:	Representation of channel info
6741c0e939SKaihua Zhong  * @controller:	Representation of a communication channel controller
6879daec8bSRandy Dunlap  *
6979daec8bSRandy Dunlap  * Mailbox controller includes 32 channels and can allocate
7079daec8bSRandy Dunlap  * channel for message transferring.
7141c0e939SKaihua Zhong  */
7241c0e939SKaihua Zhong struct hi3660_mbox {
7341c0e939SKaihua Zhong 	struct device *dev;
7441c0e939SKaihua Zhong 	void __iomem *base;
7541c0e939SKaihua Zhong 	struct mbox_chan chan[MBOX_CHAN_MAX];
7641c0e939SKaihua Zhong 	struct hi3660_chan_info mchan[MBOX_CHAN_MAX];
7741c0e939SKaihua Zhong 	struct mbox_controller controller;
7841c0e939SKaihua Zhong };
7941c0e939SKaihua Zhong 
to_hi3660_mbox(struct mbox_controller * mbox)8041c0e939SKaihua Zhong static struct hi3660_mbox *to_hi3660_mbox(struct mbox_controller *mbox)
8141c0e939SKaihua Zhong {
8241c0e939SKaihua Zhong 	return container_of(mbox, struct hi3660_mbox, controller);
8341c0e939SKaihua Zhong }
8441c0e939SKaihua Zhong 
hi3660_mbox_check_state(struct mbox_chan * chan)8541c0e939SKaihua Zhong static int hi3660_mbox_check_state(struct mbox_chan *chan)
8641c0e939SKaihua Zhong {
8741c0e939SKaihua Zhong 	unsigned long ch = (unsigned long)chan->con_priv;
8841c0e939SKaihua Zhong 	struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
8941c0e939SKaihua Zhong 	struct hi3660_chan_info *mchan = &mbox->mchan[ch];
9041c0e939SKaihua Zhong 	void __iomem *base = MBOX_BASE(mbox, ch);
9141c0e939SKaihua Zhong 	unsigned long val;
9241c0e939SKaihua Zhong 	unsigned int ret;
9341c0e939SKaihua Zhong 
942e4ac7ccSKevin Wangtao 	/* Mailbox is ready to use */
952e4ac7ccSKevin Wangtao 	if (readl(base + MBOX_MODE_REG) & MBOX_STATE_READY)
9641c0e939SKaihua Zhong 		return 0;
9741c0e939SKaihua Zhong 
9841c0e939SKaihua Zhong 	/* Wait for acknowledge from remote */
9941c0e939SKaihua Zhong 	ret = readx_poll_timeout_atomic(readl, base + MBOX_MODE_REG,
10041c0e939SKaihua Zhong 			val, (val & MBOX_STATE_ACK), 1000, 300000);
10141c0e939SKaihua Zhong 	if (ret) {
10241c0e939SKaihua Zhong 		dev_err(mbox->dev, "%s: timeout for receiving ack\n", __func__);
10341c0e939SKaihua Zhong 		return ret;
10441c0e939SKaihua Zhong 	}
10541c0e939SKaihua Zhong 
1062e4ac7ccSKevin Wangtao 	/* clear ack state, mailbox will get back to ready state */
1072e4ac7ccSKevin Wangtao 	writel(BIT(mchan->ack_irq), base + MBOX_ICLR_REG);
1082e4ac7ccSKevin Wangtao 
10941c0e939SKaihua Zhong 	return 0;
11041c0e939SKaihua Zhong }
11141c0e939SKaihua Zhong 
hi3660_mbox_unlock(struct mbox_chan * chan)11241c0e939SKaihua Zhong static int hi3660_mbox_unlock(struct mbox_chan *chan)
11341c0e939SKaihua Zhong {
11441c0e939SKaihua Zhong 	struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
11541c0e939SKaihua Zhong 	unsigned int val, retry = 3;
11641c0e939SKaihua Zhong 
11741c0e939SKaihua Zhong 	do {
11841c0e939SKaihua Zhong 		writel(MBOX_IPC_UNLOCK, mbox->base + MBOX_IPC_LOCK_REG);
11941c0e939SKaihua Zhong 
12041c0e939SKaihua Zhong 		val = readl(mbox->base + MBOX_IPC_LOCK_REG);
12141c0e939SKaihua Zhong 		if (!val)
12241c0e939SKaihua Zhong 			break;
12341c0e939SKaihua Zhong 
12441c0e939SKaihua Zhong 		udelay(10);
12541c0e939SKaihua Zhong 	} while (retry--);
12641c0e939SKaihua Zhong 
12741c0e939SKaihua Zhong 	if (val)
12841c0e939SKaihua Zhong 		dev_err(mbox->dev, "%s: failed to unlock mailbox\n", __func__);
12941c0e939SKaihua Zhong 
13041c0e939SKaihua Zhong 	return (!val) ? 0 : -ETIMEDOUT;
13141c0e939SKaihua Zhong }
13241c0e939SKaihua Zhong 
hi3660_mbox_acquire_channel(struct mbox_chan * chan)13341c0e939SKaihua Zhong static int hi3660_mbox_acquire_channel(struct mbox_chan *chan)
13441c0e939SKaihua Zhong {
13541c0e939SKaihua Zhong 	unsigned long ch = (unsigned long)chan->con_priv;
13641c0e939SKaihua Zhong 	struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
13741c0e939SKaihua Zhong 	struct hi3660_chan_info *mchan = &mbox->mchan[ch];
13841c0e939SKaihua Zhong 	void __iomem *base = MBOX_BASE(mbox, ch);
13941c0e939SKaihua Zhong 	unsigned int val, retry;
14041c0e939SKaihua Zhong 
14141c0e939SKaihua Zhong 	for (retry = 10; retry; retry--) {
14241c0e939SKaihua Zhong 		/* Check if channel is in idle state */
14341c0e939SKaihua Zhong 		if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE) {
14441c0e939SKaihua Zhong 			writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG);
14541c0e939SKaihua Zhong 
14641c0e939SKaihua Zhong 			/* Check ack bit has been set successfully */
14741c0e939SKaihua Zhong 			val = readl(base + MBOX_SRC_REG);
14841c0e939SKaihua Zhong 			if (val & BIT(mchan->ack_irq))
14941c0e939SKaihua Zhong 				break;
15041c0e939SKaihua Zhong 		}
15141c0e939SKaihua Zhong 	}
15241c0e939SKaihua Zhong 
15341c0e939SKaihua Zhong 	if (!retry)
15441c0e939SKaihua Zhong 		dev_err(mbox->dev, "%s: failed to acquire channel\n", __func__);
15541c0e939SKaihua Zhong 
15641c0e939SKaihua Zhong 	return retry ? 0 : -ETIMEDOUT;
15741c0e939SKaihua Zhong }
15841c0e939SKaihua Zhong 
hi3660_mbox_startup(struct mbox_chan * chan)15941c0e939SKaihua Zhong static int hi3660_mbox_startup(struct mbox_chan *chan)
16041c0e939SKaihua Zhong {
16141c0e939SKaihua Zhong 	int ret;
16241c0e939SKaihua Zhong 
16341c0e939SKaihua Zhong 	ret = hi3660_mbox_unlock(chan);
16441c0e939SKaihua Zhong 	if (ret)
16541c0e939SKaihua Zhong 		return ret;
16641c0e939SKaihua Zhong 
16741c0e939SKaihua Zhong 	ret = hi3660_mbox_acquire_channel(chan);
16841c0e939SKaihua Zhong 	if (ret)
16941c0e939SKaihua Zhong 		return ret;
17041c0e939SKaihua Zhong 
17141c0e939SKaihua Zhong 	return 0;
17241c0e939SKaihua Zhong }
17341c0e939SKaihua Zhong 
hi3660_mbox_send_data(struct mbox_chan * chan,void * msg)17441c0e939SKaihua Zhong static int hi3660_mbox_send_data(struct mbox_chan *chan, void *msg)
17541c0e939SKaihua Zhong {
17641c0e939SKaihua Zhong 	unsigned long ch = (unsigned long)chan->con_priv;
17741c0e939SKaihua Zhong 	struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
17841c0e939SKaihua Zhong 	struct hi3660_chan_info *mchan = &mbox->mchan[ch];
17941c0e939SKaihua Zhong 	void __iomem *base = MBOX_BASE(mbox, ch);
18041c0e939SKaihua Zhong 	u32 *buf = msg;
18141c0e939SKaihua Zhong 	unsigned int i;
1822e4ac7ccSKevin Wangtao 	int ret;
18341c0e939SKaihua Zhong 
1842e4ac7ccSKevin Wangtao 	ret = hi3660_mbox_check_state(chan);
1852e4ac7ccSKevin Wangtao 	if (ret)
1862e4ac7ccSKevin Wangtao 		return ret;
18741c0e939SKaihua Zhong 
18841c0e939SKaihua Zhong 	/* Clear mask for destination interrupt */
18941c0e939SKaihua Zhong 	writel_relaxed(~BIT(mchan->dst_irq), base + MBOX_IMASK_REG);
19041c0e939SKaihua Zhong 
19141c0e939SKaihua Zhong 	/* Config destination for interrupt vector */
19241c0e939SKaihua Zhong 	writel_relaxed(BIT(mchan->dst_irq), base + MBOX_DST_REG);
19341c0e939SKaihua Zhong 
19441c0e939SKaihua Zhong 	/* Automatic acknowledge mode */
19541c0e939SKaihua Zhong 	writel_relaxed(MBOX_AUTOMATIC_ACK, base + MBOX_MODE_REG);
19641c0e939SKaihua Zhong 
19741c0e939SKaihua Zhong 	/* Fill message data */
19841c0e939SKaihua Zhong 	for (i = 0; i < MBOX_MSG_LEN; i++)
19941c0e939SKaihua Zhong 		writel_relaxed(buf[i], base + MBOX_DATA_REG + i * 4);
20041c0e939SKaihua Zhong 
20141c0e939SKaihua Zhong 	/* Trigger data transferring */
20241c0e939SKaihua Zhong 	writel(BIT(mchan->ack_irq), base + MBOX_SEND_REG);
20341c0e939SKaihua Zhong 	return 0;
20441c0e939SKaihua Zhong }
20541c0e939SKaihua Zhong 
206b5452838SJulia Lawall static const struct mbox_chan_ops hi3660_mbox_ops = {
20741c0e939SKaihua Zhong 	.startup	= hi3660_mbox_startup,
20841c0e939SKaihua Zhong 	.send_data	= hi3660_mbox_send_data,
20941c0e939SKaihua Zhong };
21041c0e939SKaihua Zhong 
hi3660_mbox_xlate(struct mbox_controller * controller,const struct of_phandle_args * spec)21141c0e939SKaihua Zhong static struct mbox_chan *hi3660_mbox_xlate(struct mbox_controller *controller,
21241c0e939SKaihua Zhong 					   const struct of_phandle_args *spec)
21341c0e939SKaihua Zhong {
21441c0e939SKaihua Zhong 	struct hi3660_mbox *mbox = to_hi3660_mbox(controller);
21541c0e939SKaihua Zhong 	struct hi3660_chan_info *mchan;
21641c0e939SKaihua Zhong 	unsigned int ch = spec->args[0];
21741c0e939SKaihua Zhong 
21841c0e939SKaihua Zhong 	if (ch >= MBOX_CHAN_MAX) {
21941c0e939SKaihua Zhong 		dev_err(mbox->dev, "Invalid channel idx %d\n", ch);
22041c0e939SKaihua Zhong 		return ERR_PTR(-EINVAL);
22141c0e939SKaihua Zhong 	}
22241c0e939SKaihua Zhong 
22341c0e939SKaihua Zhong 	mchan = &mbox->mchan[ch];
22441c0e939SKaihua Zhong 	mchan->dst_irq = spec->args[1];
22541c0e939SKaihua Zhong 	mchan->ack_irq = spec->args[2];
22641c0e939SKaihua Zhong 
22741c0e939SKaihua Zhong 	return &mbox->chan[ch];
22841c0e939SKaihua Zhong }
22941c0e939SKaihua Zhong 
23041c0e939SKaihua Zhong static const struct of_device_id hi3660_mbox_of_match[] = {
23141c0e939SKaihua Zhong 	{ .compatible = "hisilicon,hi3660-mbox", },
23241c0e939SKaihua Zhong 	{},
23341c0e939SKaihua Zhong };
23441c0e939SKaihua Zhong 
23541c0e939SKaihua Zhong MODULE_DEVICE_TABLE(of, hi3660_mbox_of_match);
23641c0e939SKaihua Zhong 
hi3660_mbox_probe(struct platform_device * pdev)23741c0e939SKaihua Zhong static int hi3660_mbox_probe(struct platform_device *pdev)
23841c0e939SKaihua Zhong {
23941c0e939SKaihua Zhong 	struct device *dev = &pdev->dev;
24041c0e939SKaihua Zhong 	struct hi3660_mbox *mbox;
24141c0e939SKaihua Zhong 	struct mbox_chan *chan;
24241c0e939SKaihua Zhong 	unsigned long ch;
24341c0e939SKaihua Zhong 	int err;
24441c0e939SKaihua Zhong 
24541c0e939SKaihua Zhong 	mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
24641c0e939SKaihua Zhong 	if (!mbox)
24741c0e939SKaihua Zhong 		return -ENOMEM;
24841c0e939SKaihua Zhong 
2492801a33dSCai Huoqing 	mbox->base = devm_platform_ioremap_resource(pdev, 0);
25041c0e939SKaihua Zhong 	if (IS_ERR(mbox->base))
25141c0e939SKaihua Zhong 		return PTR_ERR(mbox->base);
25241c0e939SKaihua Zhong 
25341c0e939SKaihua Zhong 	mbox->dev = dev;
25441c0e939SKaihua Zhong 	mbox->controller.dev = dev;
25541c0e939SKaihua Zhong 	mbox->controller.chans = mbox->chan;
25641c0e939SKaihua Zhong 	mbox->controller.num_chans = MBOX_CHAN_MAX;
25741c0e939SKaihua Zhong 	mbox->controller.ops = &hi3660_mbox_ops;
25841c0e939SKaihua Zhong 	mbox->controller.of_xlate = hi3660_mbox_xlate;
25941c0e939SKaihua Zhong 
26041c0e939SKaihua Zhong 	/* Initialize mailbox channel data */
26141c0e939SKaihua Zhong 	chan = mbox->chan;
26241c0e939SKaihua Zhong 	for (ch = 0; ch < MBOX_CHAN_MAX; ch++)
26341c0e939SKaihua Zhong 		chan[ch].con_priv = (void *)ch;
26441c0e939SKaihua Zhong 
265e73cb83cSThierry Reding 	err = devm_mbox_controller_register(dev, &mbox->controller);
26641c0e939SKaihua Zhong 	if (err) {
26741c0e939SKaihua Zhong 		dev_err(dev, "Failed to register mailbox %d\n", err);
26841c0e939SKaihua Zhong 		return err;
26941c0e939SKaihua Zhong 	}
27041c0e939SKaihua Zhong 
27141c0e939SKaihua Zhong 	platform_set_drvdata(pdev, mbox);
27241c0e939SKaihua Zhong 	dev_info(dev, "Mailbox enabled\n");
27341c0e939SKaihua Zhong 	return 0;
27441c0e939SKaihua Zhong }
27541c0e939SKaihua Zhong 
27641c0e939SKaihua Zhong static struct platform_driver hi3660_mbox_driver = {
27741c0e939SKaihua Zhong 	.probe  = hi3660_mbox_probe,
27841c0e939SKaihua Zhong 	.driver = {
27941c0e939SKaihua Zhong 		.name = "hi3660-mbox",
28041c0e939SKaihua Zhong 		.of_match_table = hi3660_mbox_of_match,
28141c0e939SKaihua Zhong 	},
28241c0e939SKaihua Zhong };
28341c0e939SKaihua Zhong 
hi3660_mbox_init(void)28441c0e939SKaihua Zhong static int __init hi3660_mbox_init(void)
28541c0e939SKaihua Zhong {
28641c0e939SKaihua Zhong 	return platform_driver_register(&hi3660_mbox_driver);
28741c0e939SKaihua Zhong }
28841c0e939SKaihua Zhong core_initcall(hi3660_mbox_init);
28941c0e939SKaihua Zhong 
hi3660_mbox_exit(void)29041c0e939SKaihua Zhong static void __exit hi3660_mbox_exit(void)
29141c0e939SKaihua Zhong {
29241c0e939SKaihua Zhong 	platform_driver_unregister(&hi3660_mbox_driver);
29341c0e939SKaihua Zhong }
29441c0e939SKaihua Zhong module_exit(hi3660_mbox_exit);
29541c0e939SKaihua Zhong 
29641c0e939SKaihua Zhong MODULE_LICENSE("GPL");
29741c0e939SKaihua Zhong MODULE_DESCRIPTION("Hisilicon Hi3660 Mailbox Controller");
29841c0e939SKaihua Zhong MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");
299