162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright IBM Corp. 2020 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author(s): 662306a36Sopenharmony_ci * Niklas Schnelle <schnelle@linux.ibm.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define KMSG_COMPONENT "zpci" 1162306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/pci.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "pci_iov.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic struct resource iov_res = { 1962306a36Sopenharmony_ci .name = "PCI IOV res", 2062306a36Sopenharmony_ci .start = 0, 2162306a36Sopenharmony_ci .end = -1, 2262306a36Sopenharmony_ci .flags = IORESOURCE_MEM, 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_civoid zpci_iov_map_resources(struct pci_dev *pdev) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci resource_size_t len; 2862306a36Sopenharmony_ci int i; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 3162306a36Sopenharmony_ci int bar = i + PCI_IOV_RESOURCES; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci len = pci_resource_len(pdev, bar); 3462306a36Sopenharmony_ci if (!len) 3562306a36Sopenharmony_ci continue; 3662306a36Sopenharmony_ci pdev->resource[bar].parent = &iov_res; 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_civoid zpci_iov_remove_virtfn(struct pci_dev *pdev, int vfn) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci pci_lock_rescan_remove(); 4362306a36Sopenharmony_ci /* Linux' vfid's start at 0 vfn at 1 */ 4462306a36Sopenharmony_ci pci_iov_remove_virtfn(pdev->physfn, vfn - 1); 4562306a36Sopenharmony_ci pci_unlock_rescan_remove(); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int zpci_iov_link_virtfn(struct pci_dev *pdev, struct pci_dev *virtfn, int vfid) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci int rc; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci rc = pci_iov_sysfs_link(pdev, virtfn, vfid); 5362306a36Sopenharmony_ci if (rc) 5462306a36Sopenharmony_ci return rc; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci virtfn->is_virtfn = 1; 5762306a36Sopenharmony_ci virtfn->multifunction = 0; 5862306a36Sopenharmony_ci virtfn->physfn = pci_dev_get(pdev); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ciint zpci_iov_setup_virtfn(struct zpci_bus *zbus, struct pci_dev *virtfn, int vfn) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci int i, cand_devfn; 6662306a36Sopenharmony_ci struct zpci_dev *zdev; 6762306a36Sopenharmony_ci struct pci_dev *pdev; 6862306a36Sopenharmony_ci int vfid = vfn - 1; /* Linux' vfid's start at 0 vfn at 1*/ 6962306a36Sopenharmony_ci int rc = 0; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (!zbus->multifunction) 7262306a36Sopenharmony_ci return 0; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* If the parent PF for the given VF is also configured in the 7562306a36Sopenharmony_ci * instance, it must be on the same zbus. 7662306a36Sopenharmony_ci * We can then identify the parent PF by checking what 7762306a36Sopenharmony_ci * devfn the VF would have if it belonged to that PF using the PF's 7862306a36Sopenharmony_ci * stride and offset. Only if this candidate devfn matches the 7962306a36Sopenharmony_ci * actual devfn will we link both functions. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci for (i = 0; i < ZPCI_FUNCTIONS_PER_BUS; i++) { 8262306a36Sopenharmony_ci zdev = zbus->function[i]; 8362306a36Sopenharmony_ci if (zdev && zdev->is_physfn) { 8462306a36Sopenharmony_ci pdev = pci_get_slot(zbus->bus, zdev->devfn); 8562306a36Sopenharmony_ci if (!pdev) 8662306a36Sopenharmony_ci continue; 8762306a36Sopenharmony_ci cand_devfn = pci_iov_virtfn_devfn(pdev, vfid); 8862306a36Sopenharmony_ci if (cand_devfn == virtfn->devfn) { 8962306a36Sopenharmony_ci rc = zpci_iov_link_virtfn(pdev, virtfn, vfid); 9062306a36Sopenharmony_ci /* balance pci_get_slot() */ 9162306a36Sopenharmony_ci pci_dev_put(pdev); 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci /* balance pci_get_slot() */ 9562306a36Sopenharmony_ci pci_dev_put(pdev); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci return rc; 9962306a36Sopenharmony_ci} 100