From 550435edf810ad0a96a50e6a94d4ebf693fc5913 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 15 Oct 2018 02:21:23 -0700 Subject: virtio: pci: Support non-legacy PCI transport device By default QEMU creates legacy PCI transport devices, but we can ask QEMU to create non-legacy one if we pass additional device property/value pairs in the command line: -device virtio-blk-pci,disable-legacy=true,disable-modern=false This adds a new driver driver to support non-legacy (modern) device mode. Previous driver/file name is changed accordingly. Signed-off-by: Bin Meng Reviewed-by: Simon Glass --- drivers/virtio/Makefile | 2 +- drivers/virtio/virtio_pci.c | 421 ------------------------- drivers/virtio/virtio_pci_legacy.c | 421 +++++++++++++++++++++++++ drivers/virtio/virtio_pci_modern.c | 609 +++++++++++++++++++++++++++++++++++++ 4 files changed, 1031 insertions(+), 422 deletions(-) delete mode 100644 drivers/virtio/virtio_pci.c create mode 100644 drivers/virtio/virtio_pci_legacy.c create mode 100644 drivers/virtio/virtio_pci_modern.c (limited to 'drivers/virtio') diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index 5ee6183e60..072fb563b3 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -5,6 +5,6 @@ obj-y += virtio-uclass.o virtio_ring.o obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o -obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o +obj-$(CONFIG_VIRTIO_PCI) += virtio_pci_legacy.o virtio_pci_modern.o obj-$(CONFIG_VIRTIO_NET) += virtio_net.o obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c deleted file mode 100644 index a3f1083153..0000000000 --- a/drivers/virtio/virtio_pci.c +++ /dev/null @@ -1,421 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2018, Bin Meng - * - * VirtIO PCI bus transport driver - * Ported from Linux drivers/virtio/virtio_pci*.c - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "virtio_pci.h" - -#define VIRTIO_PCI_DRV_NAME "virtio-pci" - -/* PCI device ID in the range 0x1000 to 0x103f */ -#define VIRTIO_PCI_VENDOR_ID 0x1af4 -#define VIRTIO_PCI_DEVICE_ID00 0x1000 -#define VIRTIO_PCI_DEVICE_ID01 0x1001 -#define VIRTIO_PCI_DEVICE_ID02 0x1002 -#define VIRTIO_PCI_DEVICE_ID03 0x1003 -#define VIRTIO_PCI_DEVICE_ID04 0x1004 -#define VIRTIO_PCI_DEVICE_ID05 0x1005 -#define VIRTIO_PCI_DEVICE_ID06 0x1006 -#define VIRTIO_PCI_DEVICE_ID07 0x1007 -#define VIRTIO_PCI_DEVICE_ID08 0x1008 -#define VIRTIO_PCI_DEVICE_ID09 0x1009 -#define VIRTIO_PCI_DEVICE_ID0A 0x100a -#define VIRTIO_PCI_DEVICE_ID0B 0x100b -#define VIRTIO_PCI_DEVICE_ID0C 0x100c -#define VIRTIO_PCI_DEVICE_ID0D 0x100d -#define VIRTIO_PCI_DEVICE_ID0E 0x100e -#define VIRTIO_PCI_DEVICE_ID0F 0x100f -#define VIRTIO_PCI_DEVICE_ID10 0x1010 -#define VIRTIO_PCI_DEVICE_ID11 0x1011 -#define VIRTIO_PCI_DEVICE_ID12 0x1012 -#define VIRTIO_PCI_DEVICE_ID13 0x1013 -#define VIRTIO_PCI_DEVICE_ID14 0x1014 -#define VIRTIO_PCI_DEVICE_ID15 0x1015 -#define VIRTIO_PCI_DEVICE_ID16 0x1016 -#define VIRTIO_PCI_DEVICE_ID17 0x1017 -#define VIRTIO_PCI_DEVICE_ID18 0x1018 -#define VIRTIO_PCI_DEVICE_ID19 0x1019 -#define VIRTIO_PCI_DEVICE_ID1A 0x101a -#define VIRTIO_PCI_DEVICE_ID1B 0x101b -#define VIRTIO_PCI_DEVICE_ID1C 0x101c -#define VIRTIO_PCI_DEVICE_ID1D 0x101d -#define VIRTIO_PCI_DEVICE_ID1E 0x101e -#define VIRTIO_PCI_DEVICE_ID1F 0x101f -#define VIRTIO_PCI_DEVICE_ID20 0x1020 -#define VIRTIO_PCI_DEVICE_ID21 0x1021 -#define VIRTIO_PCI_DEVICE_ID22 0x1022 -#define VIRTIO_PCI_DEVICE_ID23 0x1023 -#define VIRTIO_PCI_DEVICE_ID24 0x1024 -#define VIRTIO_PCI_DEVICE_ID25 0x1025 -#define VIRTIO_PCI_DEVICE_ID26 0x1026 -#define VIRTIO_PCI_DEVICE_ID27 0x1027 -#define VIRTIO_PCI_DEVICE_ID28 0x1028 -#define VIRTIO_PCI_DEVICE_ID29 0x1029 -#define VIRTIO_PCI_DEVICE_ID2A 0x102a -#define VIRTIO_PCI_DEVICE_ID2B 0x102b -#define VIRTIO_PCI_DEVICE_ID2C 0x102c -#define VIRTIO_PCI_DEVICE_ID2D 0x102d -#define VIRTIO_PCI_DEVICE_ID2E 0x102e -#define VIRTIO_PCI_DEVICE_ID2F 0x102f -#define VIRTIO_PCI_DEVICE_ID30 0x1030 -#define VIRTIO_PCI_DEVICE_ID31 0x1031 -#define VIRTIO_PCI_DEVICE_ID32 0x1032 -#define VIRTIO_PCI_DEVICE_ID33 0x1033 -#define VIRTIO_PCI_DEVICE_ID34 0x1034 -#define VIRTIO_PCI_DEVICE_ID35 0x1035 -#define VIRTIO_PCI_DEVICE_ID36 0x1036 -#define VIRTIO_PCI_DEVICE_ID37 0x1037 -#define VIRTIO_PCI_DEVICE_ID38 0x1038 -#define VIRTIO_PCI_DEVICE_ID39 0x1039 -#define VIRTIO_PCI_DEVICE_ID3A 0x103a -#define VIRTIO_PCI_DEVICE_ID3B 0x103b -#define VIRTIO_PCI_DEVICE_ID3C 0x103c -#define VIRTIO_PCI_DEVICE_ID3D 0x103d -#define VIRTIO_PCI_DEVICE_ID3E 0x103e -#define VIRTIO_PCI_DEVICE_ID3F 0x103f - -/** - * virtio pci transport driver private data - * - * @ioaddr: pci transport device register base - * @version: pci transport device version - */ -struct virtio_pci_priv { - void __iomem *ioaddr; -}; - -static int virtio_pci_get_config(struct udevice *udev, unsigned int offset, - void *buf, unsigned int len) -{ - struct virtio_pci_priv *priv = dev_get_priv(udev); - void __iomem *ioaddr = priv->ioaddr + VIRTIO_PCI_CONFIG_OFF(false); - u8 *ptr = buf; - int i; - - for (i = 0; i < len; i++) - ptr[i] = ioread8(ioaddr + i); - - return 0; -} - -static int virtio_pci_set_config(struct udevice *udev, unsigned int offset, - const void *buf, unsigned int len) -{ - struct virtio_pci_priv *priv = dev_get_priv(udev); - void __iomem *ioaddr = priv->ioaddr + VIRTIO_PCI_CONFIG_OFF(false); - const u8 *ptr = buf; - int i; - - for (i = 0; i < len; i++) - iowrite8(ptr[i], ioaddr + i); - - return 0; -} - -static int virtio_pci_get_status(struct udevice *udev, u8 *status) -{ - struct virtio_pci_priv *priv = dev_get_priv(udev); - - *status = ioread8(priv->ioaddr + VIRTIO_PCI_STATUS); - - return 0; -} - -static int virtio_pci_set_status(struct udevice *udev, u8 status) -{ - struct virtio_pci_priv *priv = dev_get_priv(udev); - - /* We should never be setting status to 0 */ - WARN_ON(status == 0); - - iowrite8(status, priv->ioaddr + VIRTIO_PCI_STATUS); - - return 0; -} - -static int virtio_pci_reset(struct udevice *udev) -{ - struct virtio_pci_priv *priv = dev_get_priv(udev); - - /* 0 status means a reset */ - iowrite8(0, priv->ioaddr + VIRTIO_PCI_STATUS); - - /* - * Flush out the status write, and flush in device writes, - * including MSI-X interrupts, if any. - */ - ioread8(priv->ioaddr + VIRTIO_PCI_STATUS); - - return 0; -} - -static int virtio_pci_get_features(struct udevice *udev, u64 *features) -{ - struct virtio_pci_priv *priv = dev_get_priv(udev); - - /* - * When someone needs more than 32 feature bits, we'll need to - * steal a bit to indicate that the rest are somewhere else. - */ - *features = ioread32(priv->ioaddr + VIRTIO_PCI_HOST_FEATURES); - - return 0; -} - -static int virtio_pci_set_features(struct udevice *udev) -{ - struct virtio_pci_priv *priv = dev_get_priv(udev); - struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); - - /* Make sure we don't have any features > 32 bits! */ - WARN_ON((u32)uc_priv->features != uc_priv->features); - - /* We only support 32 feature bits */ - iowrite32(uc_priv->features, priv->ioaddr + VIRTIO_PCI_GUEST_FEATURES); - - return 0; -} - -static struct virtqueue *virtio_pci_setup_vq(struct udevice *udev, - unsigned int index) -{ - struct virtio_pci_priv *priv = dev_get_priv(udev); - struct virtqueue *vq; - unsigned int num; - int err; - - /* Select the queue we're interested in */ - iowrite16(index, priv->ioaddr + VIRTIO_PCI_QUEUE_SEL); - - /* Check if queue is either not available or already active */ - num = ioread16(priv->ioaddr + VIRTIO_PCI_QUEUE_NUM); - if (!num || ioread32(priv->ioaddr + VIRTIO_PCI_QUEUE_PFN)) { - err = -ENOENT; - goto error_available; - } - - /* Create the vring */ - vq = vring_create_virtqueue(index, num, VIRTIO_PCI_VRING_ALIGN, udev); - if (!vq) { - err = -ENOMEM; - goto error_available; - } - - /* Activate the queue */ - iowrite32(virtqueue_get_desc_addr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT, - priv->ioaddr + VIRTIO_PCI_QUEUE_PFN); - - return vq; - -error_available: - return ERR_PTR(err); -} - -static void virtio_pci_del_vq(struct virtqueue *vq) -{ - struct virtio_pci_priv *priv = dev_get_priv(vq->vdev); - unsigned int index = vq->index; - - iowrite16(index, priv->ioaddr + VIRTIO_PCI_QUEUE_SEL); - - /* Select and deactivate the queue */ - iowrite32(0, priv->ioaddr + VIRTIO_PCI_QUEUE_PFN); - - vring_del_virtqueue(vq); -} - -static int virtio_pci_del_vqs(struct udevice *udev) -{ - struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); - struct virtqueue *vq, *n; - - list_for_each_entry_safe(vq, n, &uc_priv->vqs, list) - virtio_pci_del_vq(vq); - - return 0; -} - -static int virtio_pci_find_vqs(struct udevice *udev, unsigned int nvqs, - struct virtqueue *vqs[]) -{ - int i; - - for (i = 0; i < nvqs; ++i) { - vqs[i] = virtio_pci_setup_vq(udev, i); - if (IS_ERR(vqs[i])) { - virtio_pci_del_vqs(udev); - return PTR_ERR(vqs[i]); - } - } - - return 0; -} - -static int virtio_pci_notify(struct udevice *udev, struct virtqueue *vq) -{ - struct virtio_pci_priv *priv = dev_get_priv(udev); - - /* - * We write the queue's selector into the notification register - * to signal the other end - */ - iowrite16(vq->index, priv->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY); - - return 0; -} - -static int virtio_pci_bind(struct udevice *udev) -{ - static int num_devs; - char name[20]; - - /* Create a unique device name for PCI type devices */ - sprintf(name, "%s#%u", VIRTIO_PCI_DRV_NAME, num_devs++); - device_set_name(udev, name); - - return 0; -} - -static int virtio_pci_probe(struct udevice *udev) -{ - struct pci_child_platdata *pplat = dev_get_parent_platdata(udev); - struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); - struct virtio_pci_priv *priv = dev_get_priv(udev); - u16 subvendor, subdevice; - u8 revision; - - /* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */ - if (pplat->device < 0x1000 || pplat->device > 0x103f) - return -ENODEV; - - /* Transitional devices must have a PCI revision ID of 0 */ - dm_pci_read_config8(udev, PCI_REVISION_ID, &revision); - if (revision != VIRTIO_PCI_ABI_VERSION) { - printf("(%s): virtio_pci expected ABI version %d, got %d\n", - udev->name, VIRTIO_PCI_ABI_VERSION, revision); - return -ENODEV; - } - - /* - * Transitional devices must have the PCI subsystem device ID matching - * the virtio device ID - */ - dm_pci_read_config16(udev, PCI_SUBSYSTEM_ID, &subdevice); - dm_pci_read_config16(udev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor); - uc_priv->device = subdevice; - uc_priv->vendor = subvendor; - - priv->ioaddr = dm_pci_map_bar(udev, PCI_BASE_ADDRESS_0, PCI_REGION_IO); - if (!priv->ioaddr) - return -ENXIO; - debug("(%s): virtio legacy device reg base %04lx\n", - udev->name, (ulong)priv->ioaddr); - - debug("(%s): device (%d) vendor (%08x) version (%d)\n", udev->name, - uc_priv->device, uc_priv->vendor, revision); - - return 0; -} - -static const struct dm_virtio_ops virtio_pci_ops = { - .get_config = virtio_pci_get_config, - .set_config = virtio_pci_set_config, - .get_status = virtio_pci_get_status, - .set_status = virtio_pci_set_status, - .reset = virtio_pci_reset, - .get_features = virtio_pci_get_features, - .set_features = virtio_pci_set_features, - .find_vqs = virtio_pci_find_vqs, - .del_vqs = virtio_pci_del_vqs, - .notify = virtio_pci_notify, -}; - -U_BOOT_DRIVER(virtio_pci) = { - .name = VIRTIO_PCI_DRV_NAME, - .id = UCLASS_VIRTIO, - .ops = &virtio_pci_ops, - .bind = virtio_pci_bind, - .probe = virtio_pci_probe, - .priv_auto_alloc_size = sizeof(struct virtio_pci_priv), -}; - -static struct pci_device_id virtio_pci_supported[] = { - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID00) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID01) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID02) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID03) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID04) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID05) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID06) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID07) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID08) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID09) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0A) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0B) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0C) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0D) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0E) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0F) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID10) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID11) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID12) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID13) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID14) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID15) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID16) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID17) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID18) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID19) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1A) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1B) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1C) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1D) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1E) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1F) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID20) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID21) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID22) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID23) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID24) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID25) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID26) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID27) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID28) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID29) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2A) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2B) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2C) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2D) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2E) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2F) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID30) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID31) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID32) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID33) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID34) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID35) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID36) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID37) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID38) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID39) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3A) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3B) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3C) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3D) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3E) }, - { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3F) }, - {}, -}; - -U_BOOT_PCI_DEVICE(virtio_pci, virtio_pci_supported); diff --git a/drivers/virtio/virtio_pci_legacy.c b/drivers/virtio/virtio_pci_legacy.c new file mode 100644 index 0000000000..08764ee6f2 --- /dev/null +++ b/drivers/virtio/virtio_pci_legacy.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, Bin Meng + * + * VirtIO PCI bus transport driver + * Ported from Linux drivers/virtio/virtio_pci*.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "virtio_pci.h" + +#define VIRTIO_PCI_DRV_NAME "virtio-pci.l" + +/* PCI device ID in the range 0x1000 to 0x103f */ +#define VIRTIO_PCI_VENDOR_ID 0x1af4 +#define VIRTIO_PCI_DEVICE_ID00 0x1000 +#define VIRTIO_PCI_DEVICE_ID01 0x1001 +#define VIRTIO_PCI_DEVICE_ID02 0x1002 +#define VIRTIO_PCI_DEVICE_ID03 0x1003 +#define VIRTIO_PCI_DEVICE_ID04 0x1004 +#define VIRTIO_PCI_DEVICE_ID05 0x1005 +#define VIRTIO_PCI_DEVICE_ID06 0x1006 +#define VIRTIO_PCI_DEVICE_ID07 0x1007 +#define VIRTIO_PCI_DEVICE_ID08 0x1008 +#define VIRTIO_PCI_DEVICE_ID09 0x1009 +#define VIRTIO_PCI_DEVICE_ID0A 0x100a +#define VIRTIO_PCI_DEVICE_ID0B 0x100b +#define VIRTIO_PCI_DEVICE_ID0C 0x100c +#define VIRTIO_PCI_DEVICE_ID0D 0x100d +#define VIRTIO_PCI_DEVICE_ID0E 0x100e +#define VIRTIO_PCI_DEVICE_ID0F 0x100f +#define VIRTIO_PCI_DEVICE_ID10 0x1010 +#define VIRTIO_PCI_DEVICE_ID11 0x1011 +#define VIRTIO_PCI_DEVICE_ID12 0x1012 +#define VIRTIO_PCI_DEVICE_ID13 0x1013 +#define VIRTIO_PCI_DEVICE_ID14 0x1014 +#define VIRTIO_PCI_DEVICE_ID15 0x1015 +#define VIRTIO_PCI_DEVICE_ID16 0x1016 +#define VIRTIO_PCI_DEVICE_ID17 0x1017 +#define VIRTIO_PCI_DEVICE_ID18 0x1018 +#define VIRTIO_PCI_DEVICE_ID19 0x1019 +#define VIRTIO_PCI_DEVICE_ID1A 0x101a +#define VIRTIO_PCI_DEVICE_ID1B 0x101b +#define VIRTIO_PCI_DEVICE_ID1C 0x101c +#define VIRTIO_PCI_DEVICE_ID1D 0x101d +#define VIRTIO_PCI_DEVICE_ID1E 0x101e +#define VIRTIO_PCI_DEVICE_ID1F 0x101f +#define VIRTIO_PCI_DEVICE_ID20 0x1020 +#define VIRTIO_PCI_DEVICE_ID21 0x1021 +#define VIRTIO_PCI_DEVICE_ID22 0x1022 +#define VIRTIO_PCI_DEVICE_ID23 0x1023 +#define VIRTIO_PCI_DEVICE_ID24 0x1024 +#define VIRTIO_PCI_DEVICE_ID25 0x1025 +#define VIRTIO_PCI_DEVICE_ID26 0x1026 +#define VIRTIO_PCI_DEVICE_ID27 0x1027 +#define VIRTIO_PCI_DEVICE_ID28 0x1028 +#define VIRTIO_PCI_DEVICE_ID29 0x1029 +#define VIRTIO_PCI_DEVICE_ID2A 0x102a +#define VIRTIO_PCI_DEVICE_ID2B 0x102b +#define VIRTIO_PCI_DEVICE_ID2C 0x102c +#define VIRTIO_PCI_DEVICE_ID2D 0x102d +#define VIRTIO_PCI_DEVICE_ID2E 0x102e +#define VIRTIO_PCI_DEVICE_ID2F 0x102f +#define VIRTIO_PCI_DEVICE_ID30 0x1030 +#define VIRTIO_PCI_DEVICE_ID31 0x1031 +#define VIRTIO_PCI_DEVICE_ID32 0x1032 +#define VIRTIO_PCI_DEVICE_ID33 0x1033 +#define VIRTIO_PCI_DEVICE_ID34 0x1034 +#define VIRTIO_PCI_DEVICE_ID35 0x1035 +#define VIRTIO_PCI_DEVICE_ID36 0x1036 +#define VIRTIO_PCI_DEVICE_ID37 0x1037 +#define VIRTIO_PCI_DEVICE_ID38 0x1038 +#define VIRTIO_PCI_DEVICE_ID39 0x1039 +#define VIRTIO_PCI_DEVICE_ID3A 0x103a +#define VIRTIO_PCI_DEVICE_ID3B 0x103b +#define VIRTIO_PCI_DEVICE_ID3C 0x103c +#define VIRTIO_PCI_DEVICE_ID3D 0x103d +#define VIRTIO_PCI_DEVICE_ID3E 0x103e +#define VIRTIO_PCI_DEVICE_ID3F 0x103f + +/** + * virtio pci transport driver private data + * + * @ioaddr: pci transport device register base + * @version: pci transport device version + */ +struct virtio_pci_priv { + void __iomem *ioaddr; +}; + +static int virtio_pci_get_config(struct udevice *udev, unsigned int offset, + void *buf, unsigned int len) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + void __iomem *ioaddr = priv->ioaddr + VIRTIO_PCI_CONFIG_OFF(false); + u8 *ptr = buf; + int i; + + for (i = 0; i < len; i++) + ptr[i] = ioread8(ioaddr + i); + + return 0; +} + +static int virtio_pci_set_config(struct udevice *udev, unsigned int offset, + const void *buf, unsigned int len) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + void __iomem *ioaddr = priv->ioaddr + VIRTIO_PCI_CONFIG_OFF(false); + const u8 *ptr = buf; + int i; + + for (i = 0; i < len; i++) + iowrite8(ptr[i], ioaddr + i); + + return 0; +} + +static int virtio_pci_get_status(struct udevice *udev, u8 *status) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + *status = ioread8(priv->ioaddr + VIRTIO_PCI_STATUS); + + return 0; +} + +static int virtio_pci_set_status(struct udevice *udev, u8 status) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + /* We should never be setting status to 0 */ + WARN_ON(status == 0); + + iowrite8(status, priv->ioaddr + VIRTIO_PCI_STATUS); + + return 0; +} + +static int virtio_pci_reset(struct udevice *udev) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + /* 0 status means a reset */ + iowrite8(0, priv->ioaddr + VIRTIO_PCI_STATUS); + + /* + * Flush out the status write, and flush in device writes, + * including MSI-X interrupts, if any. + */ + ioread8(priv->ioaddr + VIRTIO_PCI_STATUS); + + return 0; +} + +static int virtio_pci_get_features(struct udevice *udev, u64 *features) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + /* + * When someone needs more than 32 feature bits, we'll need to + * steal a bit to indicate that the rest are somewhere else. + */ + *features = ioread32(priv->ioaddr + VIRTIO_PCI_HOST_FEATURES); + + return 0; +} + +static int virtio_pci_set_features(struct udevice *udev) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); + + /* Make sure we don't have any features > 32 bits! */ + WARN_ON((u32)uc_priv->features != uc_priv->features); + + /* We only support 32 feature bits */ + iowrite32(uc_priv->features, priv->ioaddr + VIRTIO_PCI_GUEST_FEATURES); + + return 0; +} + +static struct virtqueue *virtio_pci_setup_vq(struct udevice *udev, + unsigned int index) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + struct virtqueue *vq; + unsigned int num; + int err; + + /* Select the queue we're interested in */ + iowrite16(index, priv->ioaddr + VIRTIO_PCI_QUEUE_SEL); + + /* Check if queue is either not available or already active */ + num = ioread16(priv->ioaddr + VIRTIO_PCI_QUEUE_NUM); + if (!num || ioread32(priv->ioaddr + VIRTIO_PCI_QUEUE_PFN)) { + err = -ENOENT; + goto error_available; + } + + /* Create the vring */ + vq = vring_create_virtqueue(index, num, VIRTIO_PCI_VRING_ALIGN, udev); + if (!vq) { + err = -ENOMEM; + goto error_available; + } + + /* Activate the queue */ + iowrite32(virtqueue_get_desc_addr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT, + priv->ioaddr + VIRTIO_PCI_QUEUE_PFN); + + return vq; + +error_available: + return ERR_PTR(err); +} + +static void virtio_pci_del_vq(struct virtqueue *vq) +{ + struct virtio_pci_priv *priv = dev_get_priv(vq->vdev); + unsigned int index = vq->index; + + iowrite16(index, priv->ioaddr + VIRTIO_PCI_QUEUE_SEL); + + /* Select and deactivate the queue */ + iowrite32(0, priv->ioaddr + VIRTIO_PCI_QUEUE_PFN); + + vring_del_virtqueue(vq); +} + +static int virtio_pci_del_vqs(struct udevice *udev) +{ + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); + struct virtqueue *vq, *n; + + list_for_each_entry_safe(vq, n, &uc_priv->vqs, list) + virtio_pci_del_vq(vq); + + return 0; +} + +static int virtio_pci_find_vqs(struct udevice *udev, unsigned int nvqs, + struct virtqueue *vqs[]) +{ + int i; + + for (i = 0; i < nvqs; ++i) { + vqs[i] = virtio_pci_setup_vq(udev, i); + if (IS_ERR(vqs[i])) { + virtio_pci_del_vqs(udev); + return PTR_ERR(vqs[i]); + } + } + + return 0; +} + +static int virtio_pci_notify(struct udevice *udev, struct virtqueue *vq) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + /* + * We write the queue's selector into the notification register + * to signal the other end + */ + iowrite16(vq->index, priv->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY); + + return 0; +} + +static int virtio_pci_bind(struct udevice *udev) +{ + static int num_devs; + char name[20]; + + /* Create a unique device name for PCI type devices */ + sprintf(name, "%s#%u", VIRTIO_PCI_DRV_NAME, num_devs++); + device_set_name(udev, name); + + return 0; +} + +static int virtio_pci_probe(struct udevice *udev) +{ + struct pci_child_platdata *pplat = dev_get_parent_platdata(udev); + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); + struct virtio_pci_priv *priv = dev_get_priv(udev); + u16 subvendor, subdevice; + u8 revision; + + /* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */ + if (pplat->device < 0x1000 || pplat->device > 0x103f) + return -ENODEV; + + /* Transitional devices must have a PCI revision ID of 0 */ + dm_pci_read_config8(udev, PCI_REVISION_ID, &revision); + if (revision != VIRTIO_PCI_ABI_VERSION) { + printf("(%s): virtio_pci expected ABI version %d, got %d\n", + udev->name, VIRTIO_PCI_ABI_VERSION, revision); + return -ENODEV; + } + + /* + * Transitional devices must have the PCI subsystem device ID matching + * the virtio device ID + */ + dm_pci_read_config16(udev, PCI_SUBSYSTEM_ID, &subdevice); + dm_pci_read_config16(udev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor); + uc_priv->device = subdevice; + uc_priv->vendor = subvendor; + + priv->ioaddr = dm_pci_map_bar(udev, PCI_BASE_ADDRESS_0, PCI_REGION_IO); + if (!priv->ioaddr) + return -ENXIO; + debug("(%s): virtio legacy device reg base %04lx\n", + udev->name, (ulong)priv->ioaddr); + + debug("(%s): device (%d) vendor (%08x) version (%d)\n", udev->name, + uc_priv->device, uc_priv->vendor, revision); + + return 0; +} + +static const struct dm_virtio_ops virtio_pci_ops = { + .get_config = virtio_pci_get_config, + .set_config = virtio_pci_set_config, + .get_status = virtio_pci_get_status, + .set_status = virtio_pci_set_status, + .reset = virtio_pci_reset, + .get_features = virtio_pci_get_features, + .set_features = virtio_pci_set_features, + .find_vqs = virtio_pci_find_vqs, + .del_vqs = virtio_pci_del_vqs, + .notify = virtio_pci_notify, +}; + +U_BOOT_DRIVER(virtio_pci_legacy) = { + .name = VIRTIO_PCI_DRV_NAME, + .id = UCLASS_VIRTIO, + .ops = &virtio_pci_ops, + .bind = virtio_pci_bind, + .probe = virtio_pci_probe, + .priv_auto_alloc_size = sizeof(struct virtio_pci_priv), +}; + +static struct pci_device_id virtio_pci_supported[] = { + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID00) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID01) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID02) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID03) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID04) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID05) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID06) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID07) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID08) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID09) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0A) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0B) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0C) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0D) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0E) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0F) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID10) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID11) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID12) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID13) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID14) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID15) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID16) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID17) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID18) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID19) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1A) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1B) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1C) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1D) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1E) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1F) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID20) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID21) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID22) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID23) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID24) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID25) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID26) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID27) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID28) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID29) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2A) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2B) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2C) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2D) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2E) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2F) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID30) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID31) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID32) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID33) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID34) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID35) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID36) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID37) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID38) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID39) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3A) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3B) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3C) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3D) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3E) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3F) }, + {}, +}; + +U_BOOT_PCI_DEVICE(virtio_pci_legacy, virtio_pci_supported); diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c new file mode 100644 index 0000000000..da76aea8d1 --- /dev/null +++ b/drivers/virtio/virtio_pci_modern.c @@ -0,0 +1,609 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, Bin Meng + * + * VirtIO PCI bus transport driver + * Ported from Linux drivers/virtio/virtio_pci*.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "virtio_pci.h" + +#define VIRTIO_PCI_DRV_NAME "virtio-pci.m" + +/* PCI device ID in the range 0x1040 to 0x107f */ +#define VIRTIO_PCI_VENDOR_ID 0x1af4 +#define VIRTIO_PCI_DEVICE_ID00 0x1040 +#define VIRTIO_PCI_DEVICE_ID01 0x1041 +#define VIRTIO_PCI_DEVICE_ID02 0x1042 +#define VIRTIO_PCI_DEVICE_ID03 0x1043 +#define VIRTIO_PCI_DEVICE_ID04 0x1044 +#define VIRTIO_PCI_DEVICE_ID05 0x1045 +#define VIRTIO_PCI_DEVICE_ID06 0x1046 +#define VIRTIO_PCI_DEVICE_ID07 0x1047 +#define VIRTIO_PCI_DEVICE_ID08 0x1048 +#define VIRTIO_PCI_DEVICE_ID09 0x1049 +#define VIRTIO_PCI_DEVICE_ID0A 0x104a +#define VIRTIO_PCI_DEVICE_ID0B 0x104b +#define VIRTIO_PCI_DEVICE_ID0C 0x104c +#define VIRTIO_PCI_DEVICE_ID0D 0x104d +#define VIRTIO_PCI_DEVICE_ID0E 0x104e +#define VIRTIO_PCI_DEVICE_ID0F 0x104f +#define VIRTIO_PCI_DEVICE_ID10 0x1050 +#define VIRTIO_PCI_DEVICE_ID11 0x1051 +#define VIRTIO_PCI_DEVICE_ID12 0x1052 +#define VIRTIO_PCI_DEVICE_ID13 0x1053 +#define VIRTIO_PCI_DEVICE_ID14 0x1054 +#define VIRTIO_PCI_DEVICE_ID15 0x1055 +#define VIRTIO_PCI_DEVICE_ID16 0x1056 +#define VIRTIO_PCI_DEVICE_ID17 0x1057 +#define VIRTIO_PCI_DEVICE_ID18 0x1058 +#define VIRTIO_PCI_DEVICE_ID19 0x1059 +#define VIRTIO_PCI_DEVICE_ID1A 0x105a +#define VIRTIO_PCI_DEVICE_ID1B 0x105b +#define VIRTIO_PCI_DEVICE_ID1C 0x105c +#define VIRTIO_PCI_DEVICE_ID1D 0x105d +#define VIRTIO_PCI_DEVICE_ID1E 0x105e +#define VIRTIO_PCI_DEVICE_ID1F 0x105f +#define VIRTIO_PCI_DEVICE_ID20 0x1060 +#define VIRTIO_PCI_DEVICE_ID21 0x1061 +#define VIRTIO_PCI_DEVICE_ID22 0x1062 +#define VIRTIO_PCI_DEVICE_ID23 0x1063 +#define VIRTIO_PCI_DEVICE_ID24 0x1064 +#define VIRTIO_PCI_DEVICE_ID25 0x1065 +#define VIRTIO_PCI_DEVICE_ID26 0x1066 +#define VIRTIO_PCI_DEVICE_ID27 0x1067 +#define VIRTIO_PCI_DEVICE_ID28 0x1068 +#define VIRTIO_PCI_DEVICE_ID29 0x1069 +#define VIRTIO_PCI_DEVICE_ID2A 0x106a +#define VIRTIO_PCI_DEVICE_ID2B 0x106b +#define VIRTIO_PCI_DEVICE_ID2C 0x106c +#define VIRTIO_PCI_DEVICE_ID2D 0x106d +#define VIRTIO_PCI_DEVICE_ID2E 0x106e +#define VIRTIO_PCI_DEVICE_ID2F 0x106f +#define VIRTIO_PCI_DEVICE_ID30 0x1070 +#define VIRTIO_PCI_DEVICE_ID31 0x1071 +#define VIRTIO_PCI_DEVICE_ID32 0x1072 +#define VIRTIO_PCI_DEVICE_ID33 0x1073 +#define VIRTIO_PCI_DEVICE_ID34 0x1074 +#define VIRTIO_PCI_DEVICE_ID35 0x1075 +#define VIRTIO_PCI_DEVICE_ID36 0x1076 +#define VIRTIO_PCI_DEVICE_ID37 0x1077 +#define VIRTIO_PCI_DEVICE_ID38 0x1078 +#define VIRTIO_PCI_DEVICE_ID39 0x1079 +#define VIRTIO_PCI_DEVICE_ID3A 0x107a +#define VIRTIO_PCI_DEVICE_ID3B 0x107b +#define VIRTIO_PCI_DEVICE_ID3C 0x107c +#define VIRTIO_PCI_DEVICE_ID3D 0x107d +#define VIRTIO_PCI_DEVICE_ID3E 0x107e +#define VIRTIO_PCI_DEVICE_ID3F 0x107f + +/** + * virtio pci transport driver private data + * + * @common: pci transport device common register block base + * @notify_base: pci transport device notify register block base + * @device: pci transport device device-specific register block base + * @device_len: pci transport device device-specific register block length + * @notify_offset_multiplier: multiply queue_notify_off by this value + */ +struct virtio_pci_priv { + struct virtio_pci_common_cfg __iomem *common; + void __iomem *notify_base; + void __iomem *device; + u32 device_len; + u32 notify_offset_multiplier; +}; + +static int virtio_pci_get_config(struct udevice *udev, unsigned int offset, + void *buf, unsigned int len) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + u8 b; + __le16 w; + __le32 l; + + WARN_ON(offset + len > priv->device_len); + + switch (len) { + case 1: + b = ioread8(priv->device + offset); + memcpy(buf, &b, sizeof(b)); + break; + case 2: + w = cpu_to_le16(ioread16(priv->device + offset)); + memcpy(buf, &w, sizeof(w)); + break; + case 4: + l = cpu_to_le32(ioread32(priv->device + offset)); + memcpy(buf, &l, sizeof(l)); + break; + case 8: + l = cpu_to_le32(ioread32(priv->device + offset)); + memcpy(buf, &l, sizeof(l)); + l = cpu_to_le32(ioread32(priv->device + offset + sizeof(l))); + memcpy(buf + sizeof(l), &l, sizeof(l)); + break; + default: + WARN_ON(true); + } + + return 0; +} + +static int virtio_pci_set_config(struct udevice *udev, unsigned int offset, + const void *buf, unsigned int len) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + u8 b; + __le16 w; + __le32 l; + + WARN_ON(offset + len > priv->device_len); + + switch (len) { + case 1: + memcpy(&b, buf, sizeof(b)); + iowrite8(b, priv->device + offset); + break; + case 2: + memcpy(&w, buf, sizeof(w)); + iowrite16(le16_to_cpu(w), priv->device + offset); + break; + case 4: + memcpy(&l, buf, sizeof(l)); + iowrite32(le32_to_cpu(l), priv->device + offset); + break; + case 8: + memcpy(&l, buf, sizeof(l)); + iowrite32(le32_to_cpu(l), priv->device + offset); + memcpy(&l, buf + sizeof(l), sizeof(l)); + iowrite32(le32_to_cpu(l), priv->device + offset + sizeof(l)); + break; + default: + WARN_ON(true); + } + + return 0; +} + +static int virtio_pci_generation(struct udevice *udev, u32 *counter) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + *counter = ioread8(&priv->common->config_generation); + + return 0; +} + +static int virtio_pci_get_status(struct udevice *udev, u8 *status) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + *status = ioread8(&priv->common->device_status); + + return 0; +} + +static int virtio_pci_set_status(struct udevice *udev, u8 status) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + /* We should never be setting status to 0 */ + WARN_ON(status == 0); + + iowrite8(status, &priv->common->device_status); + + return 0; +} + +static int virtio_pci_reset(struct udevice *udev) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + /* 0 status means a reset */ + iowrite8(0, &priv->common->device_status); + + /* + * After writing 0 to device_status, the driver MUST wait for a read + * of device_status to return 0 before reinitializing the device. + * This will flush out the status write, and flush in device writes, + * including MSI-X interrupts, if any. + */ + while (ioread8(&priv->common->device_status)) + udelay(1000); + + return 0; +} + +static int virtio_pci_get_features(struct udevice *udev, u64 *features) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + iowrite32(0, &priv->common->device_feature_select); + *features = ioread32(&priv->common->device_feature); + iowrite32(1, &priv->common->device_feature_select); + *features |= ((u64)ioread32(&priv->common->device_feature) << 32); + + return 0; +} + +static int virtio_pci_set_features(struct udevice *udev) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); + + if (!__virtio_test_bit(udev, VIRTIO_F_VERSION_1)) { + debug("virtio: device uses modern interface but does not have VIRTIO_F_VERSION_1\n"); + return -EINVAL; + } + + iowrite32(0, &priv->common->guest_feature_select); + iowrite32((u32)uc_priv->features, &priv->common->guest_feature); + iowrite32(1, &priv->common->guest_feature_select); + iowrite32(uc_priv->features >> 32, &priv->common->guest_feature); + + return 0; +} + +static struct virtqueue *virtio_pci_setup_vq(struct udevice *udev, + unsigned int index) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + struct virtio_pci_common_cfg __iomem *cfg = priv->common; + struct virtqueue *vq; + u16 num; + u64 addr; + int err; + + if (index >= ioread16(&cfg->num_queues)) + return ERR_PTR(-ENOENT); + + /* Select the queue we're interested in */ + iowrite16(index, &cfg->queue_select); + + /* Check if queue is either not available or already active */ + num = ioread16(&cfg->queue_size); + if (!num || ioread16(&cfg->queue_enable)) + return ERR_PTR(-ENOENT); + + if (num & (num - 1)) { + printf("(%s): bad queue size %u", udev->name, num); + return ERR_PTR(-EINVAL); + } + + /* Create the vring */ + vq = vring_create_virtqueue(index, num, VIRTIO_PCI_VRING_ALIGN, udev); + if (!vq) { + err = -ENOMEM; + goto error_available; + } + + /* Activate the queue */ + iowrite16(virtqueue_get_vring_size(vq), &cfg->queue_size); + + addr = virtqueue_get_desc_addr(vq); + iowrite32((u32)addr, &cfg->queue_desc_lo); + iowrite32(addr >> 32, &cfg->queue_desc_hi); + + addr = virtqueue_get_avail_addr(vq); + iowrite32((u32)addr, &cfg->queue_avail_lo); + iowrite32(addr >> 32, &cfg->queue_avail_hi); + + addr = virtqueue_get_used_addr(vq); + iowrite32((u32)addr, &cfg->queue_used_lo); + iowrite32(addr >> 32, &cfg->queue_used_hi); + + iowrite16(1, &cfg->queue_enable); + + return vq; + +error_available: + return ERR_PTR(err); +} + +static void virtio_pci_del_vq(struct virtqueue *vq) +{ + struct virtio_pci_priv *priv = dev_get_priv(vq->vdev); + unsigned int index = vq->index; + + iowrite16(index, &priv->common->queue_select); + + /* Select and deactivate the queue */ + iowrite16(0, &priv->common->queue_enable); + + vring_del_virtqueue(vq); +} + +static int virtio_pci_del_vqs(struct udevice *udev) +{ + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); + struct virtqueue *vq, *n; + + list_for_each_entry_safe(vq, n, &uc_priv->vqs, list) + virtio_pci_del_vq(vq); + + return 0; +} + +static int virtio_pci_find_vqs(struct udevice *udev, unsigned int nvqs, + struct virtqueue *vqs[]) +{ + int i; + + for (i = 0; i < nvqs; ++i) { + vqs[i] = virtio_pci_setup_vq(udev, i); + if (IS_ERR(vqs[i])) { + virtio_pci_del_vqs(udev); + return PTR_ERR(vqs[i]); + } + } + + return 0; +} + +static int virtio_pci_notify(struct udevice *udev, struct virtqueue *vq) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + u16 off; + + /* Select the queue we're interested in */ + iowrite16(vq->index, &priv->common->queue_select); + + /* get offset of notification word for this vq */ + off = ioread16(&priv->common->queue_notify_off); + + /* + * We write the queue's selector into the notification register + * to signal the other end + */ + iowrite16(vq->index, + priv->notify_base + off * priv->notify_offset_multiplier); + + return 0; +} + +/** + * virtio_pci_find_capability - walk capabilities to find device info + * + * @udev: the transport device + * @cfg_type: the VIRTIO_PCI_CAP_* value we seek + * + * @return offset of the configuration structure + */ +static int virtio_pci_find_capability(struct udevice *udev, u8 cfg_type) +{ + int pos; + int offset; + u8 type, bar; + + for (pos = dm_pci_find_capability(udev, PCI_CAP_ID_VNDR); + pos > 0; + pos = dm_pci_find_next_capability(udev, pos, PCI_CAP_ID_VNDR)) { + offset = pos + offsetof(struct virtio_pci_cap, cfg_type); + dm_pci_read_config8(udev, offset, &type); + offset = pos + offsetof(struct virtio_pci_cap, bar); + dm_pci_read_config8(udev, offset, &bar); + + /* Ignore structures with reserved BAR values */ + if (bar > 0x5) + continue; + + if (type == cfg_type) + return pos; + } + + return 0; +} + +/** + * virtio_pci_map_capability - map base address of the capability + * + * @udev: the transport device + * @off: offset of the configuration structure + * + * @return base address of the capability + */ +static void __iomem *virtio_pci_map_capability(struct udevice *udev, int off) +{ + u8 bar; + u32 offset; + ulong base; + void __iomem *p; + + if (!off) + return NULL; + + offset = off + offsetof(struct virtio_pci_cap, bar); + dm_pci_read_config8(udev, offset, &bar); + offset = off + offsetof(struct virtio_pci_cap, offset); + dm_pci_read_config32(udev, offset, &offset); + + /* + * TODO: adding 64-bit BAR support + * + * Per spec, the BAR is permitted to be either 32-bit or 64-bit. + * For simplicity, only read the BAR address as 32-bit. + */ + base = dm_pci_read_bar32(udev, bar); + p = (void __iomem *)base + offset; + + return p; +} + +static int virtio_pci_bind(struct udevice *udev) +{ + static int num_devs; + char name[20]; + + /* Create a unique device name */ + sprintf(name, "%s#%u", VIRTIO_PCI_DRV_NAME, num_devs++); + device_set_name(udev, name); + + return 0; +} + +static int virtio_pci_probe(struct udevice *udev) +{ + struct pci_child_platdata *pplat = dev_get_parent_platdata(udev); + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); + struct virtio_pci_priv *priv = dev_get_priv(udev); + u16 subvendor; + u8 revision; + int common, notify, device; + int offset; + + /* We only own devices >= 0x1040 and <= 0x107f: leave the rest. */ + if (pplat->device < 0x1040 || pplat->device > 0x107f) + return -ENODEV; + + /* Transitional devices must not have a PCI revision ID of 0 */ + dm_pci_read_config8(udev, PCI_REVISION_ID, &revision); + + /* Modern devices: simply use PCI device id, but start from 0x1040. */ + uc_priv->device = pplat->device - 0x1040; + dm_pci_read_config16(udev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor); + uc_priv->vendor = subvendor; + + /* Check for a common config: if not, use legacy mode (bar 0) */ + common = virtio_pci_find_capability(udev, VIRTIO_PCI_CAP_COMMON_CFG); + if (!common) { + printf("(%s): leaving for legacy driver\n", udev->name); + return -ENODEV; + } + + /* If common is there, notify should be too */ + notify = virtio_pci_find_capability(udev, VIRTIO_PCI_CAP_NOTIFY_CFG); + if (!notify) { + printf("(%s): missing capabilities %i/%i\n", udev->name, + common, notify); + return -EINVAL; + } + + /* + * Device capability is only mandatory for devices that have + * device-specific configuration. + */ + device = virtio_pci_find_capability(udev, VIRTIO_PCI_CAP_DEVICE_CFG); + if (device) { + offset = notify + offsetof(struct virtio_pci_cap, length); + dm_pci_read_config32(udev, offset, &priv->device_len); + } + + /* Map configuration structures */ + priv->common = virtio_pci_map_capability(udev, common); + priv->notify_base = virtio_pci_map_capability(udev, notify); + priv->device = virtio_pci_map_capability(udev, device); + debug("(%p): common @ %p, notify base @ %p, device @ %p\n", + udev, priv->common, priv->notify_base, priv->device); + + /* Read notify_off_multiplier from config space */ + offset = notify + offsetof(struct virtio_pci_notify_cap, + notify_off_multiplier); + dm_pci_read_config32(udev, offset, &priv->notify_offset_multiplier); + + debug("(%s): device (%d) vendor (%08x) version (%d)\n", udev->name, + uc_priv->device, uc_priv->vendor, revision); + + return 0; +} + +static const struct dm_virtio_ops virtio_pci_ops = { + .get_config = virtio_pci_get_config, + .set_config = virtio_pci_set_config, + .generation = virtio_pci_generation, + .get_status = virtio_pci_get_status, + .set_status = virtio_pci_set_status, + .reset = virtio_pci_reset, + .get_features = virtio_pci_get_features, + .set_features = virtio_pci_set_features, + .find_vqs = virtio_pci_find_vqs, + .del_vqs = virtio_pci_del_vqs, + .notify = virtio_pci_notify, +}; + +U_BOOT_DRIVER(virtio_pci_modern) = { + .name = VIRTIO_PCI_DRV_NAME, + .id = UCLASS_VIRTIO, + .ops = &virtio_pci_ops, + .bind = virtio_pci_bind, + .probe = virtio_pci_probe, + .priv_auto_alloc_size = sizeof(struct virtio_pci_priv), +}; + +static struct pci_device_id virtio_pci_supported[] = { + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID00) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID01) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID02) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID03) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID04) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID05) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID06) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID07) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID08) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID09) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0A) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0B) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0C) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0D) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0E) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0F) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID10) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID11) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID12) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID13) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID14) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID15) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID16) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID17) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID18) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID19) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1A) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1B) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1C) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1D) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1E) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1F) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID20) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID21) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID22) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID23) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID24) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID25) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID26) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID27) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID28) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID29) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2A) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2B) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2C) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2D) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2E) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2F) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID30) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID31) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID32) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID33) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID34) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID35) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID36) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID37) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID38) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID39) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3A) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3B) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3C) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3D) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3E) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3F) }, + {}, +}; + +U_BOOT_PCI_DEVICE(virtio_pci_modern, virtio_pci_supported); -- cgit v1.2.3