[stage 0] mailbox: Add QEMU PCI mailboxes
authorNikita Shubin <nikita.shubin@maquefel.me>
Fri, 31 May 2024 07:16:46 +0000 (10:16 +0300)
committerNikita Shubin <nikita.shubin@maquefel.me>
Mon, 17 Jun 2024 10:32:04 +0000 (13:32 +0300)
Add a simple PCI mailbox driver dummy for testing purpose.

Signed-off-by: Nikita Shubin <nikita.shubin@maquefel.me>
drivers/mailbox/Kconfig
drivers/mailbox/Makefile
drivers/mailbox/qemu-mailbox.c [new file with mode: 0644]

index 3b8842c4a34015793bea4c116e6e3cd3c58b1427..020f32e85aa49d71125afdca7ca6c928157890b2 100644 (file)
@@ -286,4 +286,10 @@ config QCOM_IPCC
          acts as an interrupt controller for receiving interrupts from clients.
          Say Y here if you want to build this driver.
 
+config QEMU_MBOX
+       tristate "QEMU PCIe Mailbox"
+       select REGMAP_MMIO
+       help
+         QEMU Mailbox driver for PCIe MBOX model.
+
 endif
index 5cf2f54debaf4b8a238b39b3aedf2cc9fb452fa4..d99cc632ff459b1c4dac4cfb3ca4454fea500d75 100644 (file)
@@ -62,3 +62,5 @@ obj-$(CONFIG_SUN6I_MSGBOX)    += sun6i-msgbox.o
 obj-$(CONFIG_SPRD_MBOX)                += sprd-mailbox.o
 
 obj-$(CONFIG_QCOM_IPCC)                += qcom-ipcc.o
+
+obj-$(CONFIG_QEMU_MBOX)                += qemu-mailbox.o
diff --git a/drivers/mailbox/qemu-mailbox.c b/drivers/mailbox/qemu-mailbox.c
new file mode 100644 (file)
index 0000000..2f6ade5
--- /dev/null
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mailbox driver for QEMU Virtual PCI
+ *
+ * Author: Nikita Shubin <nikita.shubin@maquefel.me>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define QEMU_MBOX_MAX_CHAN_CNT         8
+
+#define QEMU_MBOX_CFG                  0x0
+#define QEMU_MBOX_ISTATUS              0x4
+
+#define QEMU_MBOX_CHAN_STRIDE          0x20
+#define QEMU_MBOX_CHAN_ADDR(chan)      (QEMU_MBOX_CHAN_STRIDE + (chan * QEMU_MBOX_CHAN_STRIDE))
+
+struct qemu_mbox {
+       struct device *dev;
+       struct regmap *map;
+       struct mbox_controller mbox;
+};
+
+static inline struct qemu_mbox *get_qemu_mbox(struct mbox_controller *mbox)
+{
+       return container_of(mbox, struct qemu_mbox, mbox);
+}
+
+static irqreturn_t qemu_mbox_isr(int virq, void *data)
+{
+       struct qemu_mbox *mbox = data;
+       struct mbox_chan *chan;
+       unsigned int stat = 0;
+       int offset;
+       u32 msg;
+
+       regmap_read(mbox->map, QEMU_MBOX_ISTATUS, &stat);
+
+       for_each_set_bit(offset, (unsigned long *)&stat, QEMU_MBOX_MAX_CHAN_CNT) {
+               regmap_read(mbox->map, QEMU_MBOX_CHAN_ADDR(offset), &msg);
+               chan = &mbox->mbox.chans[offset];
+               if (chan->cl)
+                       mbox_chan_received_data(chan, &msg);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int qemu_mbox_send_data(struct mbox_chan *link, void *data)
+{
+       unsigned long chan = (unsigned long)link->con_priv;
+       struct qemu_mbox *mbox = get_qemu_mbox(link->mbox);
+       u32 *msg = data;
+
+       return regmap_write(mbox->map, QEMU_MBOX_CHAN_ADDR(chan), *msg);
+}
+
+static const struct regmap_config qemu_mbox_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .use_raw_spinlock = true,
+};
+
+static const struct mbox_chan_ops qemu_mbox_chan_ops = {
+       .send_data      = qemu_mbox_send_data,
+};
+
+static int qemu_mbox_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+       const char *const name = pci_name(pdev);
+       struct device *const dev = &pdev->dev;
+       void __iomem *qemu_mbox_regs;
+       struct mbox_controller *mbox;
+       struct qemu_mbox *qemu_mbox;
+       unsigned long i;
+       int err;
+
+       err = pcim_enable_device(pdev);
+       if (err)
+               return dev_err_probe(dev, err, "Failed to enable PCI device");
+
+       err = pcim_iomap_regions(pdev, BIT(0), name);
+       if (err)
+               return dev_err_probe(dev, err, "Unable to map PCI I/O addresses");
+
+       qemu_mbox_regs = pcim_iomap_table(pdev)[0];
+
+       qemu_mbox = devm_kzalloc(dev, sizeof(*qemu_mbox), GFP_KERNEL);
+       if (!qemu_mbox)
+               return -ENOMEM;
+
+       qemu_mbox->dev = dev;
+       qemu_mbox->map = devm_regmap_init_mmio(dev, qemu_mbox_regs, &qemu_mbox_regmap_config);
+       if (IS_ERR(qemu_mbox->map))
+               return dev_err_probe(dev, PTR_ERR(qemu_mbox->map),
+                                    "Unable to initialize register map\n");
+
+       err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);
+       if (err < 0)
+               return dev_err_probe(dev, err,
+                                    "Unable to allocate irqs\n");
+
+       err =  devm_request_irq(&pdev->dev, pci_irq_vector(pdev, 0),
+                               qemu_mbox_isr, 0,
+                               "qemu_mbox_isr", qemu_mbox);
+       if (err)
+               return dev_err_probe(dev, err,
+                                    "Can't claim irq\n");
+
+       pci_set_master(pdev);
+
+       mbox = &qemu_mbox->mbox;
+       mbox->dev = dev;
+       mbox->ops = &qemu_mbox_chan_ops;
+       mbox->txdone_irq = true;
+       mbox->txdone_poll = false;
+       mbox->num_chans = QEMU_MBOX_MAX_CHAN_CNT;
+       mbox->chans = devm_kcalloc(dev, QEMU_MBOX_MAX_CHAN_CNT,
+                                  sizeof(*mbox->chans), GFP_KERNEL);
+       if (!mbox->chans)
+               return -ENOMEM;
+
+       for (i = 0; i < mbox->num_chans; i++)
+               mbox->chans[i].con_priv = (void*)i;
+
+       err = devm_mbox_controller_register(dev, &qemu_mbox->mbox);
+       if (err)
+               return dev_err_probe(dev, err,
+                                    "Can't claim irq\n");
+
+       /* TODO: make a list */
+       __mbox = qemu_mbox;
+
+       return 0;
+}
+
+static const struct pci_device_id qemu_mbox_pci_dev_id[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, 0x1111) },
+       { 0 }
+};
+MODULE_DEVICE_TABLE(pci, qemu_mbox_pci_dev_id);
+
+static struct pci_driver qemu_mbox_driver = {
+       .name = "qemu-mailbox",
+       .id_table = qemu_mbox_pci_dev_id,
+       .probe = qemu_mbox_probe
+};
+
+module_pci_driver(qemu_mbox_driver);
+
+MODULE_AUTHOR("Nikita Shubin <nikita.shubin@maquefel.me>");
+MODULE_DESCRIPTION("Mailbox driver for QEMU Virtual PCI");
+MODULE_LICENSE("GPL");