From b8852dcfcb0facb37839a8cf9b01cb549153fba8 Mon Sep 17 00:00:00 2001 From: Suneel Garapati Date: Sat, 19 Oct 2019 16:07:20 -0700 Subject: pci: pci-uclass: Add support for Single-Root I/O Virtualization SR-IOV - Single Root I/O Virtualization PF - Physical Function VF - Virtual Function If SR-IOV capability is present, use it to initialize Virtual Function PCI device instances. pci_sriov_init function will read SR-IOV registers to create VF devices under the PF PCI device and also bind driver if available. This function needs to be invoked from Physical function device driver which expects VF device support, creating minimal impact on existing framework. Signed-off-by: Suneel Garapati Cc: Simon Glass Cc: Bin Meng --- drivers/pci/Kconfig | 10 +++++ drivers/pci/pci-uclass.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 1c47a21101d..4635752a95c 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -53,6 +53,16 @@ config PCI_REGION_MULTI_ENTRY region type. This helps to add support for SoC's like OcteonTX/TX2 where every peripheral is on the PCI bus. +config PCI_SRIOV + bool "Enable Single Root I/O Virtualization support for PCI" + depends on PCI || DM_PCI + default n + help + Say Y here if you want to enable PCI Single Root I/O Virtualization + capability support. This helps to enumerate Virtual Function devices + if available on a PCI Physical Function device and probe for + applicable drivers. + config PCIE_ECAM_GENERIC bool "Generic ECAM-based PCI host controller support" default n diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index fa6115a105e..51871bfe624 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1586,6 +1586,120 @@ int dm_pci_flr(struct udevice *dev) return 0; } +#if defined(CONFIG_PCI_SRIOV) +int pci_sriov_init(struct udevice *pdev, int vf_en) +{ + u16 vendor, device; + struct udevice *bus; + struct udevice *dev; + pci_dev_t bdf; + u16 ctrl; + u16 num_vfs; + u16 total_vf; + u16 vf_offset; + u16 vf_stride; + int vf, ret; + int pos; + + pos = dm_pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); + if (!pos) { + debug("Error: SRIOV capability not found\n"); + return -ENOENT; + } + + dm_pci_read_config16(pdev, pos + PCI_SRIOV_CTRL, &ctrl); + + dm_pci_read_config16(pdev, pos + PCI_SRIOV_TOTAL_VF, &total_vf); + if (vf_en > total_vf) + vf_en = total_vf; + dm_pci_write_config16(pdev, pos + PCI_SRIOV_NUM_VF, vf_en); + + ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE; + dm_pci_write_config16(pdev, pos + PCI_SRIOV_CTRL, ctrl); + + dm_pci_read_config16(pdev, pos + PCI_SRIOV_NUM_VF, &num_vfs); + if (num_vfs > vf_en) + num_vfs = vf_en; + + dm_pci_read_config16(pdev, pos + PCI_SRIOV_VF_OFFSET, &vf_offset); + dm_pci_read_config16(pdev, pos + PCI_SRIOV_VF_STRIDE, &vf_stride); + + dm_pci_read_config16(pdev, PCI_VENDOR_ID, &vendor); + dm_pci_read_config16(pdev, pos + PCI_SRIOV_VF_DID, &device); + + bdf = dm_pci_get_bdf(pdev); + + pci_get_bus(PCI_BUS(bdf), &bus); + + if (!bus) + return -ENODEV; + + bdf += PCI_BDF(0, 0, vf_offset); + + for (vf = 0; vf < num_vfs; vf++) { + struct pci_child_platdata *pplat; + ulong class; + + pci_bus_read_config(bus, bdf, PCI_CLASS_DEVICE, + &class, PCI_SIZE_16); + + debug("%s: bus %d/%s: found VF %x:%x\n", __func__, + bus->seq, bus->name, PCI_DEV(bdf), PCI_FUNC(bdf)); + + /* Find this device in the device tree */ + ret = pci_bus_find_devfn(bus, PCI_MASK_BUS(bdf), &dev); + + if (ret == -ENODEV) { + struct pci_device_id find_id; + + memset(&find_id, '\0', sizeof(find_id)); + find_id.vendor = vendor; + find_id.device = device; + find_id.class = class; + + ret = pci_find_and_bind_driver(bus, &find_id, + bdf, &dev); + + if (ret) + return ret; + } + + /* Update the platform data */ + pplat = dev_get_parent_platdata(dev); + pplat->devfn = PCI_MASK_BUS(bdf); + pplat->vendor = vendor; + pplat->device = device; + pplat->class = class; + pplat->is_virtfn = true; + pplat->pfdev = pdev; + pplat->virtid = vf * vf_stride + vf_offset; + + debug("%s: bus %d/%s: found VF %x:%x %x:%x class %lx id %x\n", + __func__, dev->seq, dev->name, PCI_DEV(bdf), + PCI_FUNC(bdf), vendor, device, class, pplat->virtid); + bdf += PCI_BDF(0, 0, vf_stride); + } + + return 0; +} + +int pci_sriov_get_totalvfs(struct udevice *pdev) +{ + u16 total_vf; + int pos; + + pos = dm_pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); + if (!pos) { + debug("Error: SRIOV capability not found\n"); + return -ENOENT; + } + + dm_pci_read_config16(pdev, pos + PCI_SRIOV_TOTAL_VF, &total_vf); + + return total_vf; +} +#endif /* SRIOV */ + UCLASS_DRIVER(pci) = { .id = UCLASS_PCI, .name = "pci", -- cgit v1.2.3