100 lines
2.2 KiB
C
100 lines
2.2 KiB
C
|
// 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;
|
||
|
}
|