|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | // Copyright (c) 2017-2018 Hisilicon Limited. | 
|  | // Copyright (c) 2017-2018 Linaro Limited. | 
|  |  | 
|  | #include <linux/bitops.h> | 
|  | #include <linux/delay.h> | 
|  | #include <linux/device.h> | 
|  | #include <linux/err.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/iopoll.h> | 
|  | #include <linux/mailbox_controller.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/slab.h> | 
|  |  | 
|  | #include "mailbox.h" | 
|  |  | 
|  | #define MBOX_CHAN_MAX			32 | 
|  |  | 
|  | #define MBOX_RX				0x0 | 
|  | #define MBOX_TX				0x1 | 
|  |  | 
|  | #define MBOX_BASE(mbox, ch)		((mbox)->base + ((ch) * 0x40)) | 
|  | #define MBOX_SRC_REG			0x00 | 
|  | #define MBOX_DST_REG			0x04 | 
|  | #define MBOX_DCLR_REG			0x08 | 
|  | #define MBOX_DSTAT_REG			0x0c | 
|  | #define MBOX_MODE_REG			0x10 | 
|  | #define MBOX_IMASK_REG			0x14 | 
|  | #define MBOX_ICLR_REG			0x18 | 
|  | #define MBOX_SEND_REG			0x1c | 
|  | #define MBOX_DATA_REG			0x20 | 
|  |  | 
|  | #define MBOX_IPC_LOCK_REG		0xa00 | 
|  | #define MBOX_IPC_UNLOCK			0x1acce551 | 
|  |  | 
|  | #define MBOX_AUTOMATIC_ACK		1 | 
|  |  | 
|  | #define MBOX_STATE_IDLE			BIT(4) | 
|  | #define MBOX_STATE_ACK			BIT(7) | 
|  |  | 
|  | #define MBOX_MSG_LEN			8 | 
|  |  | 
|  | /** | 
|  | * Hi3660 mailbox channel information | 
|  | * | 
|  | * A channel can be used for TX or RX, it can trigger remote | 
|  | * processor interrupt to notify remote processor and can receive | 
|  | * interrupt if has incoming message. | 
|  | * | 
|  | * @dst_irq:	Interrupt vector for remote processor | 
|  | * @ack_irq:	Interrupt vector for local processor | 
|  | */ | 
|  | struct hi3660_chan_info { | 
|  | unsigned int dst_irq; | 
|  | unsigned int ack_irq; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Hi3660 mailbox controller data | 
|  | * | 
|  | * Mailbox controller includes 32 channels and can allocate | 
|  | * channel for message transferring. | 
|  | * | 
|  | * @dev:	Device to which it is attached | 
|  | * @base:	Base address of the register mapping region | 
|  | * @chan:	Representation of channels in mailbox controller | 
|  | * @mchan:	Representation of channel info | 
|  | * @controller:	Representation of a communication channel controller | 
|  | */ | 
|  | struct hi3660_mbox { | 
|  | struct device *dev; | 
|  | void __iomem *base; | 
|  | struct mbox_chan chan[MBOX_CHAN_MAX]; | 
|  | struct hi3660_chan_info mchan[MBOX_CHAN_MAX]; | 
|  | struct mbox_controller controller; | 
|  | }; | 
|  |  | 
|  | static struct hi3660_mbox *to_hi3660_mbox(struct mbox_controller *mbox) | 
|  | { | 
|  | return container_of(mbox, struct hi3660_mbox, controller); | 
|  | } | 
|  |  | 
|  | static int hi3660_mbox_check_state(struct mbox_chan *chan) | 
|  | { | 
|  | unsigned long ch = (unsigned long)chan->con_priv; | 
|  | struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); | 
|  | struct hi3660_chan_info *mchan = &mbox->mchan[ch]; | 
|  | void __iomem *base = MBOX_BASE(mbox, ch); | 
|  | unsigned long val; | 
|  | unsigned int ret; | 
|  |  | 
|  | /* Mailbox is idle so directly bail out */ | 
|  | if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE) | 
|  | return 0; | 
|  |  | 
|  | /* Wait for acknowledge from remote */ | 
|  | ret = readx_poll_timeout_atomic(readl, base + MBOX_MODE_REG, | 
|  | val, (val & MBOX_STATE_ACK), 1000, 300000); | 
|  | if (ret) { | 
|  | dev_err(mbox->dev, "%s: timeout for receiving ack\n", __func__); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Ensure channel is released */ | 
|  | writel(0xffffffff, base + MBOX_IMASK_REG); | 
|  | writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int hi3660_mbox_unlock(struct mbox_chan *chan) | 
|  | { | 
|  | struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); | 
|  | unsigned int val, retry = 3; | 
|  |  | 
|  | do { | 
|  | writel(MBOX_IPC_UNLOCK, mbox->base + MBOX_IPC_LOCK_REG); | 
|  |  | 
|  | val = readl(mbox->base + MBOX_IPC_LOCK_REG); | 
|  | if (!val) | 
|  | break; | 
|  |  | 
|  | udelay(10); | 
|  | } while (retry--); | 
|  |  | 
|  | if (val) | 
|  | dev_err(mbox->dev, "%s: failed to unlock mailbox\n", __func__); | 
|  |  | 
|  | return (!val) ? 0 : -ETIMEDOUT; | 
|  | } | 
|  |  | 
|  | static int hi3660_mbox_acquire_channel(struct mbox_chan *chan) | 
|  | { | 
|  | unsigned long ch = (unsigned long)chan->con_priv; | 
|  | struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); | 
|  | struct hi3660_chan_info *mchan = &mbox->mchan[ch]; | 
|  | void __iomem *base = MBOX_BASE(mbox, ch); | 
|  | unsigned int val, retry; | 
|  |  | 
|  | for (retry = 10; retry; retry--) { | 
|  | /* Check if channel is in idle state */ | 
|  | if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE) { | 
|  | writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG); | 
|  |  | 
|  | /* Check ack bit has been set successfully */ | 
|  | val = readl(base + MBOX_SRC_REG); | 
|  | if (val & BIT(mchan->ack_irq)) | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!retry) | 
|  | dev_err(mbox->dev, "%s: failed to acquire channel\n", __func__); | 
|  |  | 
|  | return retry ? 0 : -ETIMEDOUT; | 
|  | } | 
|  |  | 
|  | static int hi3660_mbox_startup(struct mbox_chan *chan) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | ret = hi3660_mbox_check_state(chan); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | ret = hi3660_mbox_unlock(chan); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | ret = hi3660_mbox_acquire_channel(chan); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int hi3660_mbox_send_data(struct mbox_chan *chan, void *msg) | 
|  | { | 
|  | unsigned long ch = (unsigned long)chan->con_priv; | 
|  | struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); | 
|  | struct hi3660_chan_info *mchan = &mbox->mchan[ch]; | 
|  | void __iomem *base = MBOX_BASE(mbox, ch); | 
|  | u32 *buf = msg; | 
|  | unsigned int i; | 
|  |  | 
|  | /* Ensure channel is released */ | 
|  | writel_relaxed(0xffffffff, base + MBOX_IMASK_REG); | 
|  | writel_relaxed(BIT(mchan->ack_irq), base + MBOX_SRC_REG); | 
|  |  | 
|  | /* Clear mask for destination interrupt */ | 
|  | writel_relaxed(~BIT(mchan->dst_irq), base + MBOX_IMASK_REG); | 
|  |  | 
|  | /* Config destination for interrupt vector */ | 
|  | writel_relaxed(BIT(mchan->dst_irq), base + MBOX_DST_REG); | 
|  |  | 
|  | /* Automatic acknowledge mode */ | 
|  | writel_relaxed(MBOX_AUTOMATIC_ACK, base + MBOX_MODE_REG); | 
|  |  | 
|  | /* Fill message data */ | 
|  | for (i = 0; i < MBOX_MSG_LEN; i++) | 
|  | writel_relaxed(buf[i], base + MBOX_DATA_REG + i * 4); | 
|  |  | 
|  | /* Trigger data transferring */ | 
|  | writel(BIT(mchan->ack_irq), base + MBOX_SEND_REG); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct mbox_chan_ops hi3660_mbox_ops = { | 
|  | .startup	= hi3660_mbox_startup, | 
|  | .send_data	= hi3660_mbox_send_data, | 
|  | }; | 
|  |  | 
|  | static struct mbox_chan *hi3660_mbox_xlate(struct mbox_controller *controller, | 
|  | const struct of_phandle_args *spec) | 
|  | { | 
|  | struct hi3660_mbox *mbox = to_hi3660_mbox(controller); | 
|  | struct hi3660_chan_info *mchan; | 
|  | unsigned int ch = spec->args[0]; | 
|  |  | 
|  | if (ch >= MBOX_CHAN_MAX) { | 
|  | dev_err(mbox->dev, "Invalid channel idx %d\n", ch); | 
|  | return ERR_PTR(-EINVAL); | 
|  | } | 
|  |  | 
|  | mchan = &mbox->mchan[ch]; | 
|  | mchan->dst_irq = spec->args[1]; | 
|  | mchan->ack_irq = spec->args[2]; | 
|  |  | 
|  | return &mbox->chan[ch]; | 
|  | } | 
|  |  | 
|  | static const struct of_device_id hi3660_mbox_of_match[] = { | 
|  | { .compatible = "hisilicon,hi3660-mbox", }, | 
|  | {}, | 
|  | }; | 
|  |  | 
|  | MODULE_DEVICE_TABLE(of, hi3660_mbox_of_match); | 
|  |  | 
|  | static int hi3660_mbox_probe(struct platform_device *pdev) | 
|  | { | 
|  | struct device *dev = &pdev->dev; | 
|  | struct hi3660_mbox *mbox; | 
|  | struct mbox_chan *chan; | 
|  | struct resource *res; | 
|  | unsigned long ch; | 
|  | int err; | 
|  |  | 
|  | mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); | 
|  | if (!mbox) | 
|  | return -ENOMEM; | 
|  |  | 
|  | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | mbox->base = devm_ioremap_resource(dev, res); | 
|  | if (IS_ERR(mbox->base)) | 
|  | return PTR_ERR(mbox->base); | 
|  |  | 
|  | mbox->dev = dev; | 
|  | mbox->controller.dev = dev; | 
|  | mbox->controller.chans = mbox->chan; | 
|  | mbox->controller.num_chans = MBOX_CHAN_MAX; | 
|  | mbox->controller.ops = &hi3660_mbox_ops; | 
|  | mbox->controller.of_xlate = hi3660_mbox_xlate; | 
|  |  | 
|  | /* Initialize mailbox channel data */ | 
|  | chan = mbox->chan; | 
|  | for (ch = 0; ch < MBOX_CHAN_MAX; ch++) | 
|  | chan[ch].con_priv = (void *)ch; | 
|  |  | 
|  | err = mbox_controller_register(&mbox->controller); | 
|  | if (err) { | 
|  | dev_err(dev, "Failed to register mailbox %d\n", err); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | platform_set_drvdata(pdev, mbox); | 
|  | dev_info(dev, "Mailbox enabled\n"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int hi3660_mbox_remove(struct platform_device *pdev) | 
|  | { | 
|  | struct hi3660_mbox *mbox = platform_get_drvdata(pdev); | 
|  |  | 
|  | mbox_controller_unregister(&mbox->controller); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct platform_driver hi3660_mbox_driver = { | 
|  | .probe  = hi3660_mbox_probe, | 
|  | .remove = hi3660_mbox_remove, | 
|  | .driver = { | 
|  | .name = "hi3660-mbox", | 
|  | .of_match_table = hi3660_mbox_of_match, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static int __init hi3660_mbox_init(void) | 
|  | { | 
|  | return platform_driver_register(&hi3660_mbox_driver); | 
|  | } | 
|  | core_initcall(hi3660_mbox_init); | 
|  |  | 
|  | static void __exit hi3660_mbox_exit(void) | 
|  | { | 
|  | platform_driver_unregister(&hi3660_mbox_driver); | 
|  | } | 
|  | module_exit(hi3660_mbox_exit); | 
|  |  | 
|  | MODULE_LICENSE("GPL"); | 
|  | MODULE_DESCRIPTION("Hisilicon Hi3660 Mailbox Controller"); | 
|  | MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>"); |