Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Zhong Kaihua | 1351 | 100.00% | 1 | 100.00% |
Total | 1351 | 1 |
// 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>");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1