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