18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2020 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author(s): 68c2ecf20Sopenharmony_ci * Niklas Schnelle <schnelle@linux.ibm.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "zpci" 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "pci_iov.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic struct resource iov_res = { 198c2ecf20Sopenharmony_ci .name = "PCI IOV res", 208c2ecf20Sopenharmony_ci .start = 0, 218c2ecf20Sopenharmony_ci .end = -1, 228c2ecf20Sopenharmony_ci .flags = IORESOURCE_MEM, 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_civoid zpci_iov_map_resources(struct pci_dev *pdev) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci resource_size_t len; 288c2ecf20Sopenharmony_ci int i; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 318c2ecf20Sopenharmony_ci int bar = i + PCI_IOV_RESOURCES; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci len = pci_resource_len(pdev, bar); 348c2ecf20Sopenharmony_ci if (!len) 358c2ecf20Sopenharmony_ci continue; 368c2ecf20Sopenharmony_ci pdev->resource[bar].parent = &iov_res; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_civoid zpci_iov_remove_virtfn(struct pci_dev *pdev, int vfn) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci pci_lock_rescan_remove(); 438c2ecf20Sopenharmony_ci /* Linux' vfid's start at 0 vfn at 1 */ 448c2ecf20Sopenharmony_ci pci_iov_remove_virtfn(pdev->physfn, vfn - 1); 458c2ecf20Sopenharmony_ci pci_unlock_rescan_remove(); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int zpci_iov_link_virtfn(struct pci_dev *pdev, struct pci_dev *virtfn, int vfid) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci int rc; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci rc = pci_iov_sysfs_link(pdev, virtfn, vfid); 538c2ecf20Sopenharmony_ci if (rc) 548c2ecf20Sopenharmony_ci return rc; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci virtfn->is_virtfn = 1; 578c2ecf20Sopenharmony_ci virtfn->multifunction = 0; 588c2ecf20Sopenharmony_ci virtfn->physfn = pci_dev_get(pdev); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciint zpci_iov_setup_virtfn(struct zpci_bus *zbus, struct pci_dev *virtfn, int vfn) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci int i, cand_devfn; 668c2ecf20Sopenharmony_ci struct zpci_dev *zdev; 678c2ecf20Sopenharmony_ci struct pci_dev *pdev; 688c2ecf20Sopenharmony_ci int vfid = vfn - 1; /* Linux' vfid's start at 0 vfn at 1*/ 698c2ecf20Sopenharmony_ci int rc = 0; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (!zbus->multifunction) 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* If the parent PF for the given VF is also configured in the 758c2ecf20Sopenharmony_ci * instance, it must be on the same zbus. 768c2ecf20Sopenharmony_ci * We can then identify the parent PF by checking what 778c2ecf20Sopenharmony_ci * devfn the VF would have if it belonged to that PF using the PF's 788c2ecf20Sopenharmony_ci * stride and offset. Only if this candidate devfn matches the 798c2ecf20Sopenharmony_ci * actual devfn will we link both functions. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci for (i = 0; i < ZPCI_FUNCTIONS_PER_BUS; i++) { 828c2ecf20Sopenharmony_ci zdev = zbus->function[i]; 838c2ecf20Sopenharmony_ci if (zdev && zdev->is_physfn) { 848c2ecf20Sopenharmony_ci pdev = pci_get_slot(zbus->bus, zdev->devfn); 858c2ecf20Sopenharmony_ci if (!pdev) 868c2ecf20Sopenharmony_ci continue; 878c2ecf20Sopenharmony_ci cand_devfn = pci_iov_virtfn_devfn(pdev, vfid); 888c2ecf20Sopenharmony_ci if (cand_devfn == virtfn->devfn) { 898c2ecf20Sopenharmony_ci rc = zpci_iov_link_virtfn(pdev, virtfn, vfid); 908c2ecf20Sopenharmony_ci /* balance pci_get_slot() */ 918c2ecf20Sopenharmony_ci pci_dev_put(pdev); 928c2ecf20Sopenharmony_ci break; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci /* balance pci_get_slot() */ 958c2ecf20Sopenharmony_ci pci_dev_put(pdev); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci return rc; 998c2ecf20Sopenharmony_ci} 100