From 2b70f3a6cc0baee8f43c25f92ca007cc206dafef Mon Sep 17 00:00:00 2001 From: Nikita Shubin <nikita.shubin@maquefel.me> Date: Fri, 31 May 2024 10:16:46 +0300 Subject: [PATCH] [stage 0] mailbox: Add QEMU PCI mailboxes Add a simple PCI mailbox driver dummy for testing purpose. Signed-off-by: Nikita Shubin <nikita.shubin@maquefel.me> --- drivers/mailbox/Kconfig | 6 ++ drivers/mailbox/Makefile | 2 + drivers/mailbox/qemu-mailbox.c | 162 +++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 drivers/mailbox/qemu-mailbox.c diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 3b8842c4a3401..020f32e85aa49 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -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 diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 5cf2f54debaf4..d99cc632ff459 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -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 index 0000000000000..2f6ade55126ff --- /dev/null +++ b/drivers/mailbox/qemu-mailbox.c @@ -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"); -- 2.30.2