virt-pci: add platform bus support
authorVincent Whitchurch <vincent.whitchurch@axis.com>
Fri, 27 Jan 2023 14:30:27 +0000 (15:30 +0100)
committerRichard Weinberger <richard@nod.at>
Mon, 13 Feb 2023 09:14:31 +0000 (10:14 +0100)
This driver registers PCI busses, but the underlying virtio protocol
could just as easily be used to provide a platform bus instead.  If the
virtio device node in the devicetree indicates that it's compatible with
simple-bus, register platform devices instead of handling it as a PCI
bus.

Only one platform bus is allowed and the logic MMIO region for the
platform bus is placed at an arbitrarily-chosen address away from the
PCI region.

Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
arch/um/drivers/virt-pci.c

index 0aa71c0..2ba8934 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/virtio.h>
 #include <linux/virtio_config.h>
 #include <linux/logic_iomem.h>
+#include <linux/of_platform.h>
 #include <linux/irqdomain.h>
 #include <linux/virtio_pcidev.h>
 #include <linux/virtio-uml.h>
@@ -39,6 +40,8 @@ struct um_pci_device {
        unsigned long status;
 
        int irq;
+
+       bool platform;
 };
 
 struct um_pci_device_reg {
@@ -48,6 +51,7 @@ struct um_pci_device_reg {
 
 static struct pci_host_bridge *bridge;
 static DEFINE_MUTEX(um_pci_mtx);
+static struct um_pci_device *um_pci_platform_device;
 static struct um_pci_device_reg um_pci_devices[MAX_DEVICES];
 static struct fwnode_handle *um_pci_fwnode;
 static struct irq_domain *um_pci_inner_domain;
@@ -481,6 +485,9 @@ static void um_pci_handle_irq_message(struct virtqueue *vq,
        struct virtio_device *vdev = vq->vdev;
        struct um_pci_device *dev = vdev->priv;
 
+       if (!dev->irq)
+               return;
+
        /* we should properly chain interrupts, but on ARCH=um we don't care */
 
        switch (msg->op) {
@@ -581,6 +588,55 @@ static int um_pci_init_vqs(struct um_pci_device *dev)
        return 0;
 }
 
+static void __um_pci_virtio_platform_remove(struct virtio_device *vdev,
+                                           struct um_pci_device *dev)
+{
+       virtio_reset_device(vdev);
+       vdev->config->del_vqs(vdev);
+
+       mutex_lock(&um_pci_mtx);
+       um_pci_platform_device = NULL;
+       mutex_unlock(&um_pci_mtx);
+
+       kfree(dev);
+}
+
+static int um_pci_virtio_platform_probe(struct virtio_device *vdev,
+                                       struct um_pci_device *dev)
+{
+       int ret;
+
+       dev->platform = true;
+
+       mutex_lock(&um_pci_mtx);
+
+       if (um_pci_platform_device) {
+               mutex_unlock(&um_pci_mtx);
+               ret = -EBUSY;
+               goto out_free;
+       }
+
+       ret = um_pci_init_vqs(dev);
+       if (ret) {
+               mutex_unlock(&um_pci_mtx);
+               goto out_free;
+       }
+
+       um_pci_platform_device = dev;
+
+       mutex_unlock(&um_pci_mtx);
+
+       ret = of_platform_default_populate(vdev->dev.of_node, NULL, &vdev->dev);
+       if (ret)
+               __um_pci_virtio_platform_remove(vdev, dev);
+
+       return ret;
+
+out_free:
+       kfree(dev);
+       return ret;
+}
+
 static int um_pci_virtio_probe(struct virtio_device *vdev)
 {
        struct um_pci_device *dev;
@@ -594,6 +650,9 @@ static int um_pci_virtio_probe(struct virtio_device *vdev)
        dev->vdev = vdev;
        vdev->priv = dev;
 
+       if (of_device_is_compatible(vdev->dev.of_node, "simple-bus"))
+               return um_pci_virtio_platform_probe(vdev, dev);
+
        mutex_lock(&um_pci_mtx);
        for (i = 0; i < MAX_DEVICES; i++) {
                if (um_pci_devices[i].dev)
@@ -643,6 +702,12 @@ static void um_pci_virtio_remove(struct virtio_device *vdev)
        struct um_pci_device *dev = vdev->priv;
        int i;
 
+       if (dev->platform) {
+               of_platform_depopulate(&vdev->dev);
+               __um_pci_virtio_platform_remove(vdev, dev);
+               return;
+       }
+
         /* Stop all virtqueues */
         virtio_reset_device(vdev);
         vdev->config->del_vqs(vdev);
@@ -880,6 +945,30 @@ void *pci_root_bus_fwnode(struct pci_bus *bus)
        return um_pci_fwnode;
 }
 
+static long um_pci_map_platform(unsigned long offset, size_t size,
+                               const struct logic_iomem_ops **ops,
+                               void **priv)
+{
+       if (!um_pci_platform_device)
+               return -ENOENT;
+
+       *ops = &um_pci_device_bar_ops;
+       *priv = &um_pci_platform_device->resptr[0];
+
+       return 0;
+}
+
+static const struct logic_iomem_region_ops um_pci_platform_ops = {
+       .map = um_pci_map_platform,
+};
+
+static struct resource virt_platform_resource = {
+       .name = "platform",
+       .start = 0x10000000,
+       .end = 0x1fffffff,
+       .flags = IORESOURCE_MEM,
+};
+
 static int __init um_pci_init(void)
 {
        int err, i;
@@ -888,6 +977,8 @@ static int __init um_pci_init(void)
                                       &um_pci_cfgspace_ops));
        WARN_ON(logic_iomem_add_region(&virt_iomem_resource,
                                       &um_pci_iomem_ops));
+       WARN_ON(logic_iomem_add_region(&virt_platform_resource,
+                                      &um_pci_platform_ops));
 
        if (WARN(CONFIG_UML_PCI_OVER_VIRTIO_DEVICE_ID < 0,
                 "No virtio device ID configured for PCI - no PCI support\n"))