@@ -84,6 +84,14 @@ config UIO_SERCOS3
If you compile this as a module, it will be called uio_sercos3.
+config UIO_IVSHMEM
+ tristate "KVM shared memory PCI driver"
+ default n
+ help
+ Userspace I/O interface for the KVM shared memory device. This
+ driver will make available two memory regions, the first is
+ registers and the second is a region for sharing between VMs.
+
config UIO_PCI_GENERIC
tristate "Generic driver for PCI 2.3 and PCI Express cards"
depends on PCI
@@ -6,3 +6,4 @@ obj-$(CONFIG_UIO_SMX) += uio_smx.o
obj-$(CONFIG_UIO_AEC) += uio_aec.o
obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o
obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o
+obj-$(CONFIG_UIO_IVSHMEM) += uio_ivshmem.o
new file mode 100644
@@ -0,0 +1,143 @@
+/*
+ * UIO IVShmem Driver
+ *
+ * (C) 2009 Cam Macdonell
+ * based on Hilscher CIF card driver (C) 2007 Hans J. Koch <hjk@linutronix.de>
+ *
+ * Licensed under GPL version 2 only.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/uio_driver.h>
+
+#include <asm/io.h>
+
+#define IntrStatus 0x02
+#define IntrMask 0x00
+
+static irqreturn_t ivshmem_handler(int irq, struct uio_info *dev_info)
+{
+
+ void __iomem *plx_intscr = dev_info->mem[0].internal_addr
+ + IntrStatus;
+ u16 val;
+
+ val = readw(plx_intscr);
+ if (val == 0)
+ return IRQ_NONE;
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit ivshmem_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ struct uio_info *info;
+
+ info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ if (pci_enable_device(dev))
+ goto out_free;
+
+ if (pci_request_regions(dev, "ivshmem"))
+ goto out_disable;
+
+ info->mem[0].addr = pci_resource_start(dev, 0);
+ if (!info->mem[0].addr)
+ goto out_release;
+ info->mem[0].internal_addr = pci_ioremap_bar(dev, 0);
+ if (!info->mem[0].internal_addr)
+ goto out_release;
+
+ info->mem[0].size = pci_resource_len(dev, 0);
+ info->mem[0].memtype = UIO_MEM_PHYS;
+
+ info->mem[1].addr = pci_resource_start(dev, 1);
+ if (!info->mem[0].addr)
+ goto out_unmap;
+ info->mem[1].internal_addr = pci_ioremap_bar(dev, 1);
+ if (!info->mem[1].internal_addr)
+ goto out_unmap;
+
+ info->mem[1].size = pci_resource_len(dev, 1);
+ info->mem[1].memtype = UIO_MEM_PHYS;
+
+
+ info->name = "ivshmem";
+ info->version = "0.0.1";
+ info->irq = dev->irq;
+ info->irq_flags = IRQF_DISABLED | IRQF_SHARED;
+ info->handler = ivshmem_handler;
+
+ if (uio_register_device(&dev->dev, info))
+ goto out_unmap2;
+
+ pci_set_drvdata(dev, info);
+
+ writew(0xffff, info->mem[0].internal_addr + IntrMask);
+
+ return 0;
+out_unmap2:
+ iounmap(info->mem[1].internal_addr);
+out_unmap:
+ iounmap(info->mem[0].internal_addr);
+out_release:
+ pci_release_regions(dev);
+out_disable:
+ pci_disable_device(dev);
+out_free:
+ kfree (info);
+ return -ENODEV;
+}
+
+static void ivshmem_pci_remove(struct pci_dev *dev)
+{
+ struct uio_info *info = pci_get_drvdata(dev);
+
+ uio_unregister_device(info);
+ pci_release_regions(dev);
+ pci_disable_device(dev);
+ pci_set_drvdata(dev, NULL);
+ iounmap(info->mem[0].internal_addr);
+
+ kfree (info);
+}
+
+static struct pci_device_id ivshmem_pci_ids[] __devinitdata = {
+ {
+ .vendor = 0x1af4,
+ .device = 0x1110,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { 0, }
+};
+
+static struct pci_driver ivshmem_pci_driver = {
+ .name = "uio_ivshmem",
+ .id_table = ivshmem_pci_ids,
+ .probe = ivshmem_pci_probe,
+ .remove = ivshmem_pci_remove,
+};
+
+static int __init ivshmem_init_module(void)
+{
+ return pci_register_driver(&ivshmem_pci_driver);
+}
+
+static void __exit ivshmem_exit_module(void)
+{
+ pci_unregister_driver(&ivshmem_pci_driver);
+}
+
+module_init(ivshmem_init_module);
+module_exit(ivshmem_exit_module);
+
+MODULE_DEVICE_TABLE(pci, ivshmem_pci_ids);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Cam Macdonell");