Commit 522c532c authored by Vincent Whitchurch's avatar Vincent Whitchurch Committed by Richard Weinberger
Browse files

virt-pci: add platform bus support



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: default avatarVincent Whitchurch <vincent.whitchurch@axis.com>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent 935f8f7a
Loading
Loading
Loading
Loading
+91 −0
Original line number Diff line number Diff line
@@ -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"))