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