The samsung exynos mailbox controller has 16 flag bits for hardware
interrupt generation and a shared register for passing mailbox messages.
When the controller is used by the ACPM protocol the shared register is
ignored and the mailbox controller acts as a doorbell. The controller
just raises the interrupt to APM after the ACPM protocol has written
the message to SRAM.
Add support for the samsung exynos mailbox controller.
Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org>
---
drivers/mailbox/Kconfig | 11 +++
drivers/mailbox/Makefile | 2 +
drivers/mailbox/exynos-mailbox.c | 143 +++++++++++++++++++++++++++++++
3 files changed, 156 insertions(+)
create mode 100644 drivers/mailbox/exynos-mailbox.c
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 8ecba7fb999e..44b808c4d97f 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -36,6 +36,17 @@ config ARM_MHU_V3
that provides different means of transports: supported extensions
will be discovered and possibly managed at probe-time.
+config EXYNOS_MBOX
+ tristate "Exynos Mailbox"
+ depends on ARCH_EXYNOS || COMPILE_TEST
+ help
+ Say Y here if you want to build the Samsung Exynos Mailbox controller
+ driver. The controller has 16 flag bits for hardware interrupt
+ generation and a shared register for passing mailbox messages.
+ When the controller is used by the ACPM protocol the shared register
+ is ignored and the mailbox controller acts as a doorbell that raises
+ the interrupt to the ACPM firmware.
+
config IMX_MBOX
tristate "i.MX Mailbox"
depends on ARCH_MXC || COMPILE_TEST
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 5f4f5b0ce2cc..86192b5c7c32 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -11,6 +11,8 @@ obj-$(CONFIG_ARM_MHU_V2) += arm_mhuv2.o
obj-$(CONFIG_ARM_MHU_V3) += arm_mhuv3.o
+obj-$(CONFIG_EXYNOS_MBOX) += exynos-mailbox.o
+
obj-$(CONFIG_IMX_MBOX) += imx-mailbox.o
obj-$(CONFIG_ARMADA_37XX_RWTM_MBOX) += armada-37xx-rwtm-mailbox.o
diff --git a/drivers/mailbox/exynos-mailbox.c b/drivers/mailbox/exynos-mailbox.c
new file mode 100644
index 000000000000..6d4e9b3106b2
--- /dev/null
+++ b/drivers/mailbox/exynos-mailbox.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2020 Samsung Electronics Co., Ltd.
+ * Copyright 2020 Google LLC.
+ * Copyright 2024 Linaro Ltd.
+ */
+
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define EXYNOS_MBOX_MCUCTRL 0x0 /* Mailbox Control Register */
+#define EXYNOS_MBOX_INTCR0 0x24 /* Interrupt Clear Register 0 */
+#define EXYNOS_MBOX_INTMR0 0x28 /* Interrupt Mask Register 0 */
+#define EXYNOS_MBOX_INTSR0 0x2c /* Interrupt Status Register 0 */
+#define EXYNOS_MBOX_INTMSR0 0x30 /* Interrupt Mask Status Register 0 */
+#define EXYNOS_MBOX_INTGR1 0x40 /* Interrupt Generation Register 1 */
+#define EXYNOS_MBOX_INTMR1 0x48 /* Interrupt Mask Register 1 */
+#define EXYNOS_MBOX_INTSR1 0x4c /* Interrupt Status Register 1 */
+#define EXYNOS_MBOX_INTMSR1 0x50 /* Interrupt Mask Status Register 1 */
+
+#define EXYNOS_MBOX_INTMR0_MASK GENMASK(15, 0)
+#define EXYNOS_MBOX_INTGR1_MASK GENMASK(15, 0)
+
+#define EXYNOS_MBOX_CHAN_COUNT HWEIGHT32(EXYNOS_MBOX_INTGR1_MASK)
+
+/**
+ * struct exynos_mbox - driver's private data.
+ * @regs: mailbox registers base address.
+ * @mbox: pointer to the mailbox controller.
+ * @dev: pointer to the mailbox device.
+ * @pclk: pointer to the mailbox peripheral clock.
+ */
+struct exynos_mbox {
+ void __iomem *regs;
+ struct mbox_controller *mbox;
+ struct device *dev;
+ struct clk *pclk;
+};
+
+static int exynos_mbox_chan_index(struct mbox_chan *chan)
+{
+ struct mbox_controller *mbox = chan->mbox;
+ int i;
+
+ for (i = 0; i < mbox->num_chans; i++)
+ if (chan == &mbox->chans[i])
+ return i;
+ return -EINVAL;
+}
+
+static int exynos_mbox_send_data(struct mbox_chan *chan, void *data)
+{
+ struct exynos_mbox *exynos_mbox = dev_get_drvdata(chan->mbox->dev);
+ int index;
+
+ index = exynos_mbox_chan_index(chan);
+ if (index < 0)
+ return index;
+
+ writel_relaxed(BIT(index), exynos_mbox->regs + EXYNOS_MBOX_INTGR1);
+
+ return 0;
+}
+
+static const struct mbox_chan_ops exynos_mbox_chan_ops = {
+ .send_data = exynos_mbox_send_data,
+};
+
+static const struct of_device_id exynos_mbox_match[] = {
+ { .compatible = "google,gs101-acpm-mbox" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_mbox_match);
+
+static int exynos_mbox_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct exynos_mbox *exynos_mbox;
+ struct mbox_controller *mbox;
+ struct mbox_chan *chans;
+ int i;
+
+ exynos_mbox = devm_kzalloc(dev, sizeof(*exynos_mbox), GFP_KERNEL);
+ if (!exynos_mbox)
+ return -ENOMEM;
+
+ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+
+ chans = devm_kcalloc(dev, EXYNOS_MBOX_CHAN_COUNT, sizeof(*chans),
+ GFP_KERNEL);
+ if (!chans)
+ return -ENOMEM;
+
+ exynos_mbox->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(exynos_mbox->regs))
+ return PTR_ERR(exynos_mbox->regs);
+
+ exynos_mbox->pclk = devm_clk_get_enabled(dev, "pclk");
+ if (IS_ERR(exynos_mbox->pclk))
+ return dev_err_probe(dev, PTR_ERR(exynos_mbox->pclk),
+ "Failed to enable clock.\n");
+
+ mbox->num_chans = EXYNOS_MBOX_CHAN_COUNT;
+ mbox->chans = chans;
+ mbox->dev = dev;
+ mbox->ops = &exynos_mbox_chan_ops;
+
+ for (i = 0; i < EXYNOS_MBOX_CHAN_COUNT; i++)
+ chans[i].mbox = mbox;
+
+ exynos_mbox->dev = dev;
+ exynos_mbox->mbox = mbox;
+
+ platform_set_drvdata(pdev, exynos_mbox);
+
+ /* Mask out all interrupts. We support just polling channels for now. */
+ writel_relaxed(EXYNOS_MBOX_INTMR0_MASK,
+ exynos_mbox->regs + EXYNOS_MBOX_INTMR0);
+
+ return devm_mbox_controller_register(dev, mbox);
+}
+
+static struct platform_driver exynos_mbox_driver = {
+ .probe = exynos_mbox_probe,
+ .driver = {
+ .name = "exynos-acpm-mbox",
+ .of_match_table = of_match_ptr(exynos_mbox_match),
+ },
+};
+module_platform_driver(exynos_mbox_driver);
+
+MODULE_AUTHOR("Tudor Ambarus <tudor.ambarus@linaro.org>");
+MODULE_DESCRIPTION("Exynos mailbox driver");
+MODULE_LICENSE("GPL");
--
2.47.0.338.g60cca15819-goog
On Thu, Dec 05, 2024 at 05:41:36PM +0000, Tudor Ambarus wrote:
> The samsung exynos mailbox controller has 16 flag bits for hardware
Here, subject and other text/descriptions:
s/samsung/Samsung/
s/exynos/Exynos/
> interrupt generation and a shared register for passing mailbox messages.
> When the controller is used by the ACPM protocol the shared register is
> ignored and the mailbox controller acts as a doorbell. The controller
> just raises the interrupt to APM after the ACPM protocol has written
> the message to SRAM.
...
> +static int exynos_mbox_send_data(struct mbox_chan *chan, void *data)
> +{
> + struct exynos_mbox *exynos_mbox = dev_get_drvdata(chan->mbox->dev);
> + int index;
> +
> + index = exynos_mbox_chan_index(chan);
> + if (index < 0)
> + return index;
> +
> + writel_relaxed(BIT(index), exynos_mbox->regs + EXYNOS_MBOX_INTGR1);
writel()
> +
> + return 0;
> +}
> +
> +static const struct mbox_chan_ops exynos_mbox_chan_ops = {
> + .send_data = exynos_mbox_send_data,
> +};
> +
> +static const struct of_device_id exynos_mbox_match[] = {
> + { .compatible = "google,gs101-acpm-mbox" },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, exynos_mbox_match);
> +
> +static int exynos_mbox_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct exynos_mbox *exynos_mbox;
> + struct mbox_controller *mbox;
> + struct mbox_chan *chans;
> + int i;
> +
> + exynos_mbox = devm_kzalloc(dev, sizeof(*exynos_mbox), GFP_KERNEL);
> + if (!exynos_mbox)
> + return -ENOMEM;
> +
> + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
> + if (!mbox)
> + return -ENOMEM;
> +
> + chans = devm_kcalloc(dev, EXYNOS_MBOX_CHAN_COUNT, sizeof(*chans),
> + GFP_KERNEL);
> + if (!chans)
> + return -ENOMEM;
> +
> + exynos_mbox->regs = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(exynos_mbox->regs))
> + return PTR_ERR(exynos_mbox->regs);
> +
> + exynos_mbox->pclk = devm_clk_get_enabled(dev, "pclk");
> + if (IS_ERR(exynos_mbox->pclk))
> + return dev_err_probe(dev, PTR_ERR(exynos_mbox->pclk),
> + "Failed to enable clock.\n");
> +
> + mbox->num_chans = EXYNOS_MBOX_CHAN_COUNT;
> + mbox->chans = chans;
> + mbox->dev = dev;
> + mbox->ops = &exynos_mbox_chan_ops;
> +
> + for (i = 0; i < EXYNOS_MBOX_CHAN_COUNT; i++)
> + chans[i].mbox = mbox;
> +
> + exynos_mbox->dev = dev;
> + exynos_mbox->mbox = mbox;
> +
> + platform_set_drvdata(pdev, exynos_mbox);
> +
> + /* Mask out all interrupts. We support just polling channels for now. */
> + writel_relaxed(EXYNOS_MBOX_INTMR0_MASK,
writel()
> + exynos_mbox->regs + EXYNOS_MBOX_INTMR0);
> +
> + return devm_mbox_controller_register(dev, mbox);
> +}
> +
> +static struct platform_driver exynos_mbox_driver = {
> + .probe = exynos_mbox_probe,
> + .driver = {
> + .name = "exynos-acpm-mbox",
> + .of_match_table = of_match_ptr(exynos_mbox_match),
Drop of_match_ptr() - unused symbol warnings.
Best regards,
Krzysztof
© 2016 - 2025 Red Hat, Inc.