| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright IBM Corp. 2020 |
| * |
| * Author(s): |
| * Niklas Schnelle <schnelle@linux.ibm.com> |
| * |
| */ |
| |
| #define KMSG_COMPONENT "zpci" |
| #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
| |
| #include <linux/kernel.h> |
| #include <linux/pci.h> |
| |
| #include "pci_iov.h" |
| |
| static struct resource iov_res = { |
| .name = "PCI IOV res", |
| .start = 0, |
| .end = -1, |
| .flags = IORESOURCE_MEM, |
| }; |
| |
| void zpci_iov_map_resources(struct pci_dev *pdev) |
| { |
| resource_size_t len; |
| int i; |
| |
| for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { |
| int bar = i + PCI_IOV_RESOURCES; |
| |
| len = pci_resource_len(pdev, bar); |
| if (!len) |
| continue; |
| pdev->resource[bar].parent = &iov_res; |
| } |
| } |
| |
| void zpci_iov_remove_virtfn(struct pci_dev *pdev, int vfn) |
| { |
| pci_lock_rescan_remove(); |
| /* Linux' vfid's start at 0 vfn at 1 */ |
| pci_iov_remove_virtfn(pdev->physfn, vfn - 1); |
| pci_unlock_rescan_remove(); |
| } |
| |
| static int zpci_iov_link_virtfn(struct pci_dev *pdev, struct pci_dev *virtfn, int vfid) |
| { |
| int rc; |
| |
| rc = pci_iov_sysfs_link(pdev, virtfn, vfid); |
| if (rc) |
| return rc; |
| |
| virtfn->is_virtfn = 1; |
| virtfn->multifunction = 0; |
| virtfn->physfn = pci_dev_get(pdev); |
| |
| return 0; |
| } |
| |
| int zpci_iov_setup_virtfn(struct zpci_bus *zbus, struct pci_dev *virtfn, int vfn) |
| { |
| int i, cand_devfn; |
| struct zpci_dev *zdev; |
| struct pci_dev *pdev; |
| int vfid = vfn - 1; /* Linux' vfid's start at 0 vfn at 1*/ |
| int rc = 0; |
| |
| if (!zbus->multifunction) |
| return 0; |
| |
| /* If the parent PF for the given VF is also configured in the |
| * instance, it must be on the same zbus. |
| * We can then identify the parent PF by checking what |
| * devfn the VF would have if it belonged to that PF using the PF's |
| * stride and offset. Only if this candidate devfn matches the |
| * actual devfn will we link both functions. |
| */ |
| for (i = 0; i < ZPCI_FUNCTIONS_PER_BUS; i++) { |
| zdev = zbus->function[i]; |
| if (zdev && zdev->is_physfn) { |
| pdev = pci_get_slot(zbus->bus, zdev->devfn); |
| if (!pdev) |
| continue; |
| cand_devfn = pci_iov_virtfn_devfn(pdev, vfid); |
| if (cand_devfn == virtfn->devfn) { |
| rc = zpci_iov_link_virtfn(pdev, virtfn, vfid); |
| /* balance pci_get_slot() */ |
| pci_dev_put(pdev); |
| break; |
| } |
| /* balance pci_get_slot() */ |
| pci_dev_put(pdev); |
| } |
| } |
| return rc; |
| } |