// 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 #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 unsigned 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_plat *pplat = dev_get_parent_plat(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, 0, 0, PCI_REGION_TYPE, 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 = 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);