| // 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; | 
 | } | 
 |  | 
 | /** | 
 |  * zpci_iov_find_parent_pf - Find the parent PF, if any, of the given function | 
 |  * @zbus:	The bus that the PCI function is on, or would be added on | 
 |  * @zdev:	The PCI function | 
 |  * | 
 |  * Finds the parent PF, if it exists and is configured, of the given PCI function | 
 |  * and increments its refcount. Th PF is searched for on the provided bus so the | 
 |  * caller has to ensure that this is the correct bus to search. This function may | 
 |  * be used before adding the PCI function to a zbus. | 
 |  * | 
 |  * Return: Pointer to the struct pci_dev of the parent PF or NULL if it not | 
 |  * found. If the function is not a VF or has no RequesterID information, | 
 |  * NULL is returned as well. | 
 |  */ | 
 | struct pci_dev *zpci_iov_find_parent_pf(struct zpci_bus *zbus, struct zpci_dev *zdev) | 
 | { | 
 | 	int i, vfid, devfn, cand_devfn; | 
 | 	struct pci_dev *pdev; | 
 |  | 
 | 	if (!zbus->multifunction) | 
 | 		return NULL; | 
 | 	/* Non-VFs and VFs without RID available don't have a parent */ | 
 | 	if (!zdev->vfn || !zdev->rid_available) | 
 | 		return NULL; | 
 | 	/* Linux vfid starts at 0 vfn at 1 */ | 
 | 	vfid = zdev->vfn - 1; | 
 | 	devfn = zdev->rid & ZPCI_RID_MASK_DEVFN; | 
 | 	/* | 
 | 	 * 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 == devfn) | 
 | 				return pdev; | 
 | 			/* balance pci_get_slot() */ | 
 | 			pci_dev_put(pdev); | 
 | 		} | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | int zpci_iov_setup_virtfn(struct zpci_bus *zbus, struct pci_dev *virtfn, int vfn) | 
 | { | 
 | 	struct zpci_dev *zdev = to_zpci(virtfn); | 
 | 	struct pci_dev *pdev_pf; | 
 | 	int rc = 0; | 
 |  | 
 | 	pdev_pf = zpci_iov_find_parent_pf(zbus, zdev); | 
 | 	if (pdev_pf) { | 
 | 		/* Linux' vfids start at 0 while zdev->vfn starts at 1 */ | 
 | 		rc = zpci_iov_link_virtfn(pdev_pf, virtfn, zdev->vfn - 1); | 
 | 		pci_dev_put(pdev_pf); | 
 | 	} | 
 | 	return rc; | 
 | } |