2020-08-17 10:29:23 +02:00
// 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>
2020-09-15 10:42:41 +02:00
# include "pci_iov.h"
2020-08-17 10:29:23 +02:00
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 ;
}