blob: 2a0aecbb93005cca57dde40d79ac90b64441f3b2 [file] [log] [blame]
From 80a06cb0b905e9a85752f1d6e136836a36ad7780 Mon Sep 17 00:00:00 2001
From: David Wang <davidwang@quantatw.com>
Date: Tue, 7 Nov 2023 21:42:10 +0800
Subject: [PATCH 19/23] drivers: mailbox: sync npcm-mailbox
---
drivers/mailbox/Kconfig | 9 ++
drivers/mailbox/Makefile | 2 +
drivers/mailbox/npcm-mailbox.c | 264 +++++++++++++++++++++++++++++++++
3 files changed, 275 insertions(+)
create mode 100644 drivers/mailbox/npcm-mailbox.c
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index c9fc06c7e685..7681dee17e01 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -273,4 +273,13 @@ config QCOM_IPCC
acts as an interrupt controller for receiving interrupts from clients.
Say Y here if you want to build this driver.
+config NPCM_MBOX
+ tristate "NPCM Mailbox"
+ depends on ARCH_NPCM || COMPILE_TEST
+ help
+ An implementation of the NPCM mailbox controller.
+ It is used to send message between the Core processor and other
+ processors on the BMC such as TIP and CP.
+ Select Y here if you want to use NPCM mailbox controller.
+
endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index c2089f04887e..d99c9a0732a2 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -58,3 +58,5 @@ obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o
obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o
obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o
+
+obj-$(CONFIG_NPCM_MBOX) += npcm-mailbox.o
diff --git a/drivers/mailbox/npcm-mailbox.c b/drivers/mailbox/npcm-mailbox.c
new file mode 100644
index 000000000000..befb0e3409b6
--- /dev/null
+++ b/drivers/mailbox/npcm-mailbox.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2022, Microsoft Corporation
+// Copyright (c) 2022 Nuvoton Technology corporation.
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#define NPCM_CP2BST_REG_OFFSET 0x04
+#define NPCM_B2CPNT_REG_OFFSET 0x08
+#define NPCM_NUM_DOORBELLS 32
+
+struct npcm_mbox_hw {
+ void __iomem *base;
+ struct mbox_controller controller;
+};
+
+/**
+ * NPCM mailbox allocated channel information
+ *
+ * @mhw: Pointer to parent mailbox device
+ * @doorbell: doorbell number pertaining to this channel
+ */
+struct npcm_mbox_channel {
+ struct npcm_mbox_hw *mhw;
+ unsigned int doorbell;
+};
+
+static inline struct mbox_chan *
+npcm_mbox_to_channel(struct mbox_controller *controller, unsigned int doorbell)
+{
+ struct npcm_mbox_channel *chan_info = controller->chans[0].con_priv;
+ if (chan_info && chan_info->doorbell == doorbell)
+ return &controller->chans[0];
+
+ return NULL;
+}
+
+static void npcm_mbox_clear_irq(struct mbox_chan *chan)
+{
+ struct npcm_mbox_channel *chan_info = chan->con_priv;
+ void __iomem *base = chan_info->mhw->base;
+
+ writel_relaxed(BIT(chan_info->doorbell), base + NPCM_CP2BST_REG_OFFSET);
+}
+
+static struct mbox_chan *npcm_mbox_irq_to_channel(struct npcm_mbox_hw *mhw)
+{
+ unsigned long bits;
+ unsigned int doorbell;
+ struct mbox_chan *chan = NULL;
+ struct mbox_controller *controller = &mhw->controller;
+ void __iomem *base = mhw->base;
+
+ bits = readl_relaxed(base + NPCM_CP2BST_REG_OFFSET);
+
+ /* No IRQs fired in specified physical channel */
+ if (!bits)
+ return NULL;
+
+ /* An IRQ has fired, find the associated channel */
+ for (doorbell = 0; bits; doorbell++) {
+ if (!test_and_clear_bit(doorbell, &bits))
+ continue;
+
+ chan = npcm_mbox_to_channel(controller, doorbell);
+ if (chan)
+ break;
+ dev_err(controller->dev,
+ "Channel 0 not registered yet, doorbell: %d\n",
+ doorbell);
+ }
+ return chan;
+}
+
+static irqreturn_t npcm_mbox_rx_handler(int irq, void *data)
+{
+ struct npcm_mbox_hw *mhw = data;
+ struct mbox_chan *chan = NULL;
+
+ while ((chan = npcm_mbox_irq_to_channel(mhw)) != NULL) {
+ mbox_chan_received_data(chan, NULL);
+ npcm_mbox_clear_irq(chan);
+ }
+ return IRQ_HANDLED;
+}
+
+static int npcm_send_data(struct mbox_chan *chan, void *data)
+{
+ struct npcm_mbox_channel *chan_info = chan->con_priv;
+ void __iomem *base = chan_info->mhw->base;
+
+ /* Send event to co-processor */
+ writel_relaxed(BIT(chan_info->doorbell),
+ base + NPCM_B2CPNT_REG_OFFSET);
+
+ return 0;
+}
+
+static int npcm_startup(struct mbox_chan *chan)
+{
+ npcm_mbox_clear_irq(chan);
+ return 0;
+}
+
+static void npcm_shutdown(struct mbox_chan *chan)
+{
+ struct npcm_mbox_channel *chan_info = chan->con_priv;
+ struct mbox_controller *controller = &chan_info->mhw->controller;
+
+ /* Reset channel */
+ npcm_mbox_clear_irq(chan);
+ devm_kfree(controller->dev, chan->con_priv);
+ chan->con_priv = NULL;
+}
+
+static bool npcm_last_tx_done(struct mbox_chan *chan)
+{
+ struct npcm_mbox_channel *chan_info = chan->con_priv;
+ void __iomem *base = chan_info->mhw->base;
+
+ if (readl_relaxed(base + NPCM_B2CPNT_REG_OFFSET) &
+ BIT(chan_info->doorbell))
+ return false;
+
+ return true;
+}
+
+static struct mbox_chan *npcm_mbox_xlate(struct mbox_controller *controller,
+ const struct of_phandle_args *spec)
+{
+ struct npcm_mbox_hw *mhw = dev_get_drvdata(controller->dev);
+ struct npcm_mbox_channel *chan_info;
+ struct mbox_chan *chan;
+ unsigned int pchan, doorbell;
+
+ if (spec->args_count != 2) {
+ dev_err(controller->dev, "Invalid argumment count: %d\n",
+ spec->args_count);
+ return ERR_PTR(-EINVAL);
+ }
+
+ pchan = spec->args[0];
+ doorbell = spec->args[1];
+ /* Bounds checking */
+ if (pchan != 0 || doorbell >= NPCM_NUM_DOORBELLS) {
+ dev_err(controller->dev,
+ "Invalid channel requested pchan: %d doorbell: %d\n",
+ pchan, doorbell);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Check if the requested channel free */
+ chan = npcm_mbox_to_channel(controller, doorbell);
+ if (chan) {
+ dev_err(controller->dev,
+ "Channel in use: pchan: %d doorbell: %d\n", pchan,
+ doorbell);
+ return ERR_PTR(-EBUSY);
+ }
+
+ chan = &controller->chans[0];
+ chan_info =
+ devm_kzalloc(controller->dev, sizeof(*chan_info), GFP_KERNEL);
+ if (!chan_info)
+ return ERR_PTR(-ENOMEM);
+
+ chan_info->mhw = mhw;
+ chan_info->doorbell = doorbell;
+ chan->con_priv = chan_info;
+
+ dev_info(controller->dev,
+ "controller: created channel 0, doorbell: %d\n", doorbell);
+
+ return chan;
+}
+
+static const struct mbox_chan_ops npcm_ops = {
+ .send_data = npcm_send_data,
+ .startup = npcm_startup,
+ .shutdown = npcm_shutdown,
+ .last_tx_done = npcm_last_tx_done,
+};
+
+static int npcm_mbox_probe(struct platform_device *pdev)
+{
+ struct npcm_mbox_hw *mhw;
+ struct mbox_chan *chans;
+ struct resource *res;
+ int err = 0;
+
+ mhw = devm_kzalloc(&pdev->dev, sizeof(*mhw), GFP_KERNEL);
+ if (!mhw)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mhw->base = devm_ioremap_resource(&pdev->dev, res);
+
+ if (IS_ERR(mhw->base))
+ return PTR_ERR(mhw->base);
+
+ chans = devm_kcalloc(&pdev->dev, 1, sizeof(*chans), GFP_KERNEL);
+ if (!chans)
+ return -ENOMEM;
+
+ mhw->controller.dev = &pdev->dev;
+ mhw->controller.chans = chans;
+ mhw->controller.num_chans = 1;
+ mhw->controller.txdone_irq = false;
+ mhw->controller.txdone_poll = true;
+ mhw->controller.txpoll_period = 1;
+ mhw->controller.of_xlate = npcm_mbox_xlate;
+ mhw->controller.ops = &npcm_ops;
+
+ err = devm_mbox_controller_register(&pdev->dev, &mhw->controller);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register mailboxes %d\n", err);
+ return err;
+ }
+
+ platform_set_drvdata(pdev, mhw);
+
+ /* Clear all IRQs before claiming IRQ handler */
+ writel_relaxed(0xFFFFFFFF, mhw->base + NPCM_CP2BST_REG_OFFSET);
+
+ err = devm_request_threaded_irq(&pdev->dev, platform_get_irq(pdev, 0),
+ NULL, npcm_mbox_rx_handler,
+ IRQF_ONESHOT, "npcm_mbox", mhw);
+ if (err) {
+ dev_err(&pdev->dev, "Can't claim IRQ handler %d\n", err);
+ mbox_controller_unregister(&mhw->controller);
+ return err;
+ }
+
+ dev_info(&pdev->dev, "Mailbox registered\n");
+ return 0;
+}
+
+static const struct of_device_id npcm_mbox_match[] = {
+ { .compatible = "nuvoton,npcm750-mbox" },
+ { .compatible = "nuvoton,npcm845-mbox" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, npcm_mbox_match);
+
+static struct platform_driver npcm_mbox_driver = {
+ .probe = npcm_mbox_probe,
+ .driver = {
+ .name = "npcm-mailbox",
+ .of_match_table = npcm_mbox_match,
+ },
+};
+module_platform_driver(npcm_mbox_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("NPCM Mailbox controller Driver");
+MODULE_AUTHOR("Xiling Sun <xiling.sun@microsoft.com>");
--
2.25.1