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