1d1b054daSYu Zhao /* 2d1b054daSYu Zhao * drivers/pci/iov.c 3d1b054daSYu Zhao * 4d1b054daSYu Zhao * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com> 5d1b054daSYu Zhao * 6d1b054daSYu Zhao * PCI Express I/O Virtualization (IOV) support. 7d1b054daSYu Zhao * Single Root IOV 1.0 8302b4215SYu Zhao * Address Translation Service 1.0 9d1b054daSYu Zhao */ 10d1b054daSYu Zhao 11d1b054daSYu Zhao #include <linux/pci.h> 125a0e3ad6STejun Heo #include <linux/slab.h> 13d1b054daSYu Zhao #include <linux/mutex.h> 14363c75dbSPaul Gortmaker #include <linux/export.h> 15d1b054daSYu Zhao #include <linux/string.h> 16d1b054daSYu Zhao #include <linux/delay.h> 175cdede24SJoerg Roedel #include <linux/pci-ats.h> 18d1b054daSYu Zhao #include "pci.h" 19d1b054daSYu Zhao 20dd7cc44dSYu Zhao #define VIRTFN_ID_LEN 16 21d1b054daSYu Zhao 22a28724b0SYu Zhao static inline u8 virtfn_bus(struct pci_dev *dev, int id) 23a28724b0SYu Zhao { 24a28724b0SYu Zhao return dev->bus->number + ((dev->devfn + dev->sriov->offset + 25a28724b0SYu Zhao dev->sriov->stride * id) >> 8); 26a28724b0SYu Zhao } 27a28724b0SYu Zhao 28a28724b0SYu Zhao static inline u8 virtfn_devfn(struct pci_dev *dev, int id) 29a28724b0SYu Zhao { 30a28724b0SYu Zhao return (dev->devfn + dev->sriov->offset + 31a28724b0SYu Zhao dev->sriov->stride * id) & 0xff; 32a28724b0SYu Zhao } 33a28724b0SYu Zhao 34dd7cc44dSYu Zhao static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr) 35dd7cc44dSYu Zhao { 36dd7cc44dSYu Zhao struct pci_bus *child; 37dd7cc44dSYu Zhao 38dd7cc44dSYu Zhao if (bus->number == busnr) 39dd7cc44dSYu Zhao return bus; 40dd7cc44dSYu Zhao 41dd7cc44dSYu Zhao child = pci_find_bus(pci_domain_nr(bus), busnr); 42dd7cc44dSYu Zhao if (child) 43dd7cc44dSYu Zhao return child; 44dd7cc44dSYu Zhao 45dd7cc44dSYu Zhao child = pci_add_new_bus(bus, NULL, busnr); 46dd7cc44dSYu Zhao if (!child) 47dd7cc44dSYu Zhao return NULL; 48dd7cc44dSYu Zhao 49b7eac055SYinghai Lu pci_bus_insert_busn_res(child, busnr, busnr); 50dd7cc44dSYu Zhao 51dd7cc44dSYu Zhao return child; 52dd7cc44dSYu Zhao } 53dd7cc44dSYu Zhao 54dc087f2fSJiang Liu static void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus) 55dd7cc44dSYu Zhao { 56dc087f2fSJiang Liu if (physbus != virtbus && list_empty(&virtbus->devices)) 57dc087f2fSJiang Liu pci_remove_bus(virtbus); 58dd7cc44dSYu Zhao } 59dd7cc44dSYu Zhao 60*0e6c9122SWei Yang resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno) 61*0e6c9122SWei Yang { 62*0e6c9122SWei Yang if (!dev->is_physfn) 63*0e6c9122SWei Yang return 0; 64*0e6c9122SWei Yang 65*0e6c9122SWei Yang return dev->sriov->barsz[resno - PCI_IOV_RESOURCES]; 66*0e6c9122SWei Yang } 67*0e6c9122SWei Yang 68dd7cc44dSYu Zhao static int virtfn_add(struct pci_dev *dev, int id, int reset) 69dd7cc44dSYu Zhao { 70dd7cc44dSYu Zhao int i; 71dc087f2fSJiang Liu int rc = -ENOMEM; 72dd7cc44dSYu Zhao u64 size; 73dd7cc44dSYu Zhao char buf[VIRTFN_ID_LEN]; 74dd7cc44dSYu Zhao struct pci_dev *virtfn; 75dd7cc44dSYu Zhao struct resource *res; 76dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 778b1fce04SGu Zheng struct pci_bus *bus; 78dd7cc44dSYu Zhao 79dd7cc44dSYu Zhao mutex_lock(&iov->dev->sriov->lock); 808b1fce04SGu Zheng bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id)); 81dc087f2fSJiang Liu if (!bus) 82dc087f2fSJiang Liu goto failed; 83dc087f2fSJiang Liu 84dc087f2fSJiang Liu virtfn = pci_alloc_dev(bus); 85dc087f2fSJiang Liu if (!virtfn) 86dc087f2fSJiang Liu goto failed0; 87dc087f2fSJiang Liu 88dd7cc44dSYu Zhao virtfn->devfn = virtfn_devfn(dev, id); 89dd7cc44dSYu Zhao virtfn->vendor = dev->vendor; 90dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device); 91dd7cc44dSYu Zhao pci_setup_device(virtfn); 92dd7cc44dSYu Zhao virtfn->dev.parent = dev->dev.parent; 93fbf33f51SXudong Hao virtfn->physfn = pci_dev_get(dev); 94fbf33f51SXudong Hao virtfn->is_virtfn = 1; 95aa931977SAlex Williamson virtfn->multifunction = 0; 96dd7cc44dSYu Zhao 97dd7cc44dSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 98dd7cc44dSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 99dd7cc44dSYu Zhao if (!res->parent) 100dd7cc44dSYu Zhao continue; 101dd7cc44dSYu Zhao virtfn->resource[i].name = pci_name(virtfn); 102dd7cc44dSYu Zhao virtfn->resource[i].flags = res->flags; 103*0e6c9122SWei Yang size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES); 104dd7cc44dSYu Zhao virtfn->resource[i].start = res->start + size * id; 105dd7cc44dSYu Zhao virtfn->resource[i].end = virtfn->resource[i].start + size - 1; 106dd7cc44dSYu Zhao rc = request_resource(res, &virtfn->resource[i]); 107dd7cc44dSYu Zhao BUG_ON(rc); 108dd7cc44dSYu Zhao } 109dd7cc44dSYu Zhao 110dd7cc44dSYu Zhao if (reset) 1118c1c699fSYu Zhao __pci_reset_function(virtfn); 112dd7cc44dSYu Zhao 113dd7cc44dSYu Zhao pci_device_add(virtfn, virtfn->bus); 114dd7cc44dSYu Zhao mutex_unlock(&iov->dev->sriov->lock); 115dd7cc44dSYu Zhao 116c893d133SYijing Wang pci_bus_add_device(virtfn); 117dd7cc44dSYu Zhao sprintf(buf, "virtfn%u", id); 118dd7cc44dSYu Zhao rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf); 119dd7cc44dSYu Zhao if (rc) 120dd7cc44dSYu Zhao goto failed1; 121dd7cc44dSYu Zhao rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn"); 122dd7cc44dSYu Zhao if (rc) 123dd7cc44dSYu Zhao goto failed2; 124dd7cc44dSYu Zhao 125dd7cc44dSYu Zhao kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE); 126dd7cc44dSYu Zhao 127dd7cc44dSYu Zhao return 0; 128dd7cc44dSYu Zhao 129dd7cc44dSYu Zhao failed2: 130dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, buf); 131dd7cc44dSYu Zhao failed1: 132dd7cc44dSYu Zhao pci_dev_put(dev); 133dd7cc44dSYu Zhao mutex_lock(&iov->dev->sriov->lock); 134210647afSYinghai Lu pci_stop_and_remove_bus_device(virtfn); 135dc087f2fSJiang Liu failed0: 136dc087f2fSJiang Liu virtfn_remove_bus(dev->bus, bus); 137dc087f2fSJiang Liu failed: 138dd7cc44dSYu Zhao mutex_unlock(&iov->dev->sriov->lock); 139dd7cc44dSYu Zhao 140dd7cc44dSYu Zhao return rc; 141dd7cc44dSYu Zhao } 142dd7cc44dSYu Zhao 143dd7cc44dSYu Zhao static void virtfn_remove(struct pci_dev *dev, int id, int reset) 144dd7cc44dSYu Zhao { 145dd7cc44dSYu Zhao char buf[VIRTFN_ID_LEN]; 146dd7cc44dSYu Zhao struct pci_dev *virtfn; 147dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 148dd7cc44dSYu Zhao 149dc087f2fSJiang Liu virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus), 150dc087f2fSJiang Liu virtfn_bus(dev, id), 151dc087f2fSJiang Liu virtfn_devfn(dev, id)); 152dd7cc44dSYu Zhao if (!virtfn) 153dd7cc44dSYu Zhao return; 154dd7cc44dSYu Zhao 155dd7cc44dSYu Zhao if (reset) { 156dd7cc44dSYu Zhao device_release_driver(&virtfn->dev); 1578c1c699fSYu Zhao __pci_reset_function(virtfn); 158dd7cc44dSYu Zhao } 159dd7cc44dSYu Zhao 160dd7cc44dSYu Zhao sprintf(buf, "virtfn%u", id); 161dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, buf); 16209cedbefSYinghai Lu /* 16309cedbefSYinghai Lu * pci_stop_dev() could have been called for this virtfn already, 16409cedbefSYinghai Lu * so the directory for the virtfn may have been removed before. 16509cedbefSYinghai Lu * Double check to avoid spurious sysfs warnings. 16609cedbefSYinghai Lu */ 16709cedbefSYinghai Lu if (virtfn->dev.kobj.sd) 168dd7cc44dSYu Zhao sysfs_remove_link(&virtfn->dev.kobj, "physfn"); 169dd7cc44dSYu Zhao 170dd7cc44dSYu Zhao mutex_lock(&iov->dev->sriov->lock); 171210647afSYinghai Lu pci_stop_and_remove_bus_device(virtfn); 172dc087f2fSJiang Liu virtfn_remove_bus(dev->bus, virtfn->bus); 173dd7cc44dSYu Zhao mutex_unlock(&iov->dev->sriov->lock); 174dd7cc44dSYu Zhao 175dc087f2fSJiang Liu /* balance pci_get_domain_bus_and_slot() */ 176dc087f2fSJiang Liu pci_dev_put(virtfn); 177dd7cc44dSYu Zhao pci_dev_put(dev); 178dd7cc44dSYu Zhao } 179dd7cc44dSYu Zhao 180dd7cc44dSYu Zhao static int sriov_enable(struct pci_dev *dev, int nr_virtfn) 181dd7cc44dSYu Zhao { 182dd7cc44dSYu Zhao int rc; 183dd7cc44dSYu Zhao int i, j; 184dd7cc44dSYu Zhao int nres; 185dd7cc44dSYu Zhao u16 offset, stride, initial; 186dd7cc44dSYu Zhao struct resource *res; 187dd7cc44dSYu Zhao struct pci_dev *pdev; 188dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 189bbef98abSRam Pai int bars = 0; 19068f8e9faSBjorn Helgaas u8 bus; 191dd7cc44dSYu Zhao 192dd7cc44dSYu Zhao if (!nr_virtfn) 193dd7cc44dSYu Zhao return 0; 194dd7cc44dSYu Zhao 1956b136724SBjorn Helgaas if (iov->num_VFs) 196dd7cc44dSYu Zhao return -EINVAL; 197dd7cc44dSYu Zhao 198dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial); 1996b136724SBjorn Helgaas if (initial > iov->total_VFs || 2006b136724SBjorn Helgaas (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs))) 201dd7cc44dSYu Zhao return -EIO; 202dd7cc44dSYu Zhao 2036b136724SBjorn Helgaas if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs || 204dd7cc44dSYu Zhao (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial))) 205dd7cc44dSYu Zhao return -EINVAL; 206dd7cc44dSYu Zhao 207dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset); 208dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride); 209dd7cc44dSYu Zhao if (!offset || (nr_virtfn > 1 && !stride)) 210dd7cc44dSYu Zhao return -EIO; 211dd7cc44dSYu Zhao 212dd7cc44dSYu Zhao nres = 0; 213dd7cc44dSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 214bbef98abSRam Pai bars |= (1 << (i + PCI_IOV_RESOURCES)); 215dd7cc44dSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 216dd7cc44dSYu Zhao if (res->parent) 217dd7cc44dSYu Zhao nres++; 218dd7cc44dSYu Zhao } 219dd7cc44dSYu Zhao if (nres != iov->nres) { 220dd7cc44dSYu Zhao dev_err(&dev->dev, "not enough MMIO resources for SR-IOV\n"); 221dd7cc44dSYu Zhao return -ENOMEM; 222dd7cc44dSYu Zhao } 223dd7cc44dSYu Zhao 224dd7cc44dSYu Zhao iov->offset = offset; 225dd7cc44dSYu Zhao iov->stride = stride; 226dd7cc44dSYu Zhao 22768f8e9faSBjorn Helgaas bus = virtfn_bus(dev, nr_virtfn - 1); 22868f8e9faSBjorn Helgaas if (bus > dev->bus->busn_res.end) { 22968f8e9faSBjorn Helgaas dev_err(&dev->dev, "can't enable %d VFs (bus %02x out of range of %pR)\n", 23068f8e9faSBjorn Helgaas nr_virtfn, bus, &dev->bus->busn_res); 231dd7cc44dSYu Zhao return -ENOMEM; 232dd7cc44dSYu Zhao } 233dd7cc44dSYu Zhao 234bbef98abSRam Pai if (pci_enable_resources(dev, bars)) { 235bbef98abSRam Pai dev_err(&dev->dev, "SR-IOV: IOV BARS not allocated\n"); 236bbef98abSRam Pai return -ENOMEM; 237bbef98abSRam Pai } 238bbef98abSRam Pai 239dd7cc44dSYu Zhao if (iov->link != dev->devfn) { 240dd7cc44dSYu Zhao pdev = pci_get_slot(dev->bus, iov->link); 241dd7cc44dSYu Zhao if (!pdev) 242dd7cc44dSYu Zhao return -ENODEV; 243dd7cc44dSYu Zhao 244dc087f2fSJiang Liu if (!pdev->is_physfn) { 245dd7cc44dSYu Zhao pci_dev_put(pdev); 246652d1100SStefan Assmann return -ENOSYS; 247dc087f2fSJiang Liu } 248dd7cc44dSYu Zhao 249dd7cc44dSYu Zhao rc = sysfs_create_link(&dev->dev.kobj, 250dd7cc44dSYu Zhao &pdev->dev.kobj, "dep_link"); 251dc087f2fSJiang Liu pci_dev_put(pdev); 252dd7cc44dSYu Zhao if (rc) 253dd7cc44dSYu Zhao return rc; 254dd7cc44dSYu Zhao } 255dd7cc44dSYu Zhao 25619b6984eSYijing Wang pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn); 257dd7cc44dSYu Zhao iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE; 258fb51ccbfSJan Kiszka pci_cfg_access_lock(dev); 259dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 260dd7cc44dSYu Zhao msleep(100); 261fb51ccbfSJan Kiszka pci_cfg_access_unlock(dev); 262dd7cc44dSYu Zhao 2636b136724SBjorn Helgaas iov->initial_VFs = initial; 264dd7cc44dSYu Zhao if (nr_virtfn < initial) 265dd7cc44dSYu Zhao initial = nr_virtfn; 266dd7cc44dSYu Zhao 267dd7cc44dSYu Zhao for (i = 0; i < initial; i++) { 268dd7cc44dSYu Zhao rc = virtfn_add(dev, i, 0); 269dd7cc44dSYu Zhao if (rc) 270dd7cc44dSYu Zhao goto failed; 271dd7cc44dSYu Zhao } 272dd7cc44dSYu Zhao 273dd7cc44dSYu Zhao kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); 2746b136724SBjorn Helgaas iov->num_VFs = nr_virtfn; 275dd7cc44dSYu Zhao 276dd7cc44dSYu Zhao return 0; 277dd7cc44dSYu Zhao 278dd7cc44dSYu Zhao failed: 279dd7cc44dSYu Zhao for (j = 0; j < i; j++) 280dd7cc44dSYu Zhao virtfn_remove(dev, j, 0); 281dd7cc44dSYu Zhao 282dd7cc44dSYu Zhao iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); 283fb51ccbfSJan Kiszka pci_cfg_access_lock(dev); 284dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 28519b6984eSYijing Wang pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, 0); 286dd7cc44dSYu Zhao ssleep(1); 287fb51ccbfSJan Kiszka pci_cfg_access_unlock(dev); 288dd7cc44dSYu Zhao 289dd7cc44dSYu Zhao if (iov->link != dev->devfn) 290dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, "dep_link"); 291dd7cc44dSYu Zhao 292dd7cc44dSYu Zhao return rc; 293dd7cc44dSYu Zhao } 294dd7cc44dSYu Zhao 295dd7cc44dSYu Zhao static void sriov_disable(struct pci_dev *dev) 296dd7cc44dSYu Zhao { 297dd7cc44dSYu Zhao int i; 298dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 299dd7cc44dSYu Zhao 3006b136724SBjorn Helgaas if (!iov->num_VFs) 301dd7cc44dSYu Zhao return; 302dd7cc44dSYu Zhao 3036b136724SBjorn Helgaas for (i = 0; i < iov->num_VFs; i++) 304dd7cc44dSYu Zhao virtfn_remove(dev, i, 0); 305dd7cc44dSYu Zhao 306dd7cc44dSYu Zhao iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); 307fb51ccbfSJan Kiszka pci_cfg_access_lock(dev); 308dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 309dd7cc44dSYu Zhao ssleep(1); 310fb51ccbfSJan Kiszka pci_cfg_access_unlock(dev); 311dd7cc44dSYu Zhao 312dd7cc44dSYu Zhao if (iov->link != dev->devfn) 313dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, "dep_link"); 314dd7cc44dSYu Zhao 3156b136724SBjorn Helgaas iov->num_VFs = 0; 31619b6984eSYijing Wang pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, 0); 317dd7cc44dSYu Zhao } 318dd7cc44dSYu Zhao 319d1b054daSYu Zhao static int sriov_init(struct pci_dev *dev, int pos) 320d1b054daSYu Zhao { 321*0e6c9122SWei Yang int i, bar64; 322d1b054daSYu Zhao int rc; 323d1b054daSYu Zhao int nres; 324d1b054daSYu Zhao u32 pgsz; 325d1b054daSYu Zhao u16 ctrl, total, offset, stride; 326d1b054daSYu Zhao struct pci_sriov *iov; 327d1b054daSYu Zhao struct resource *res; 328d1b054daSYu Zhao struct pci_dev *pdev; 329d1b054daSYu Zhao 33062f87c0eSYijing Wang if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_END && 33162f87c0eSYijing Wang pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT) 332d1b054daSYu Zhao return -ENODEV; 333d1b054daSYu Zhao 334d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl); 335d1b054daSYu Zhao if (ctrl & PCI_SRIOV_CTRL_VFE) { 336d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0); 337d1b054daSYu Zhao ssleep(1); 338d1b054daSYu Zhao } 339d1b054daSYu Zhao 340d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total); 341d1b054daSYu Zhao if (!total) 342d1b054daSYu Zhao return 0; 343d1b054daSYu Zhao 344d1b054daSYu Zhao ctrl = 0; 345d1b054daSYu Zhao list_for_each_entry(pdev, &dev->bus->devices, bus_list) 346d1b054daSYu Zhao if (pdev->is_physfn) 347d1b054daSYu Zhao goto found; 348d1b054daSYu Zhao 349d1b054daSYu Zhao pdev = NULL; 350d1b054daSYu Zhao if (pci_ari_enabled(dev->bus)) 351d1b054daSYu Zhao ctrl |= PCI_SRIOV_CTRL_ARI; 352d1b054daSYu Zhao 353d1b054daSYu Zhao found: 354d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl); 355045cc22eSethan.zhao pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, 0); 356d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset); 357d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride); 358d1b054daSYu Zhao if (!offset || (total > 1 && !stride)) 359d1b054daSYu Zhao return -EIO; 360d1b054daSYu Zhao 361d1b054daSYu Zhao pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz); 362d1b054daSYu Zhao i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0; 363d1b054daSYu Zhao pgsz &= ~((1 << i) - 1); 364d1b054daSYu Zhao if (!pgsz) 365d1b054daSYu Zhao return -EIO; 366d1b054daSYu Zhao 367d1b054daSYu Zhao pgsz &= ~(pgsz - 1); 3688161fe91SVaidyanathan Srinivasan pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz); 369d1b054daSYu Zhao 370*0e6c9122SWei Yang iov = kzalloc(sizeof(*iov), GFP_KERNEL); 371*0e6c9122SWei Yang if (!iov) 372*0e6c9122SWei Yang return -ENOMEM; 373*0e6c9122SWei Yang 374d1b054daSYu Zhao nres = 0; 375d1b054daSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 376d1b054daSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 377*0e6c9122SWei Yang bar64 = __pci_read_base(dev, pci_bar_unknown, res, 378d1b054daSYu Zhao pos + PCI_SRIOV_BAR + i * 4); 379d1b054daSYu Zhao if (!res->flags) 380d1b054daSYu Zhao continue; 381d1b054daSYu Zhao if (resource_size(res) & (PAGE_SIZE - 1)) { 382d1b054daSYu Zhao rc = -EIO; 383d1b054daSYu Zhao goto failed; 384d1b054daSYu Zhao } 385*0e6c9122SWei Yang iov->barsz[i] = resource_size(res); 386d1b054daSYu Zhao res->end = res->start + resource_size(res) * total - 1; 387e88ae01dSWei Yang dev_info(&dev->dev, "VF(n) BAR%d space: %pR (contains BAR%d for %d VFs)\n", 388e88ae01dSWei Yang i, res, i, total); 389*0e6c9122SWei Yang i += bar64; 390d1b054daSYu Zhao nres++; 391d1b054daSYu Zhao } 392d1b054daSYu Zhao 393d1b054daSYu Zhao iov->pos = pos; 394d1b054daSYu Zhao iov->nres = nres; 395d1b054daSYu Zhao iov->ctrl = ctrl; 3966b136724SBjorn Helgaas iov->total_VFs = total; 397d1b054daSYu Zhao iov->offset = offset; 398d1b054daSYu Zhao iov->stride = stride; 399d1b054daSYu Zhao iov->pgsz = pgsz; 400d1b054daSYu Zhao iov->self = dev; 401d1b054daSYu Zhao pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap); 402d1b054daSYu Zhao pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link); 40362f87c0eSYijing Wang if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) 4044d135dbeSYu Zhao iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link); 405d1b054daSYu Zhao 406d1b054daSYu Zhao if (pdev) 407d1b054daSYu Zhao iov->dev = pci_dev_get(pdev); 408e277d2fcSYu Zhao else 409d1b054daSYu Zhao iov->dev = dev; 410e277d2fcSYu Zhao 411d1b054daSYu Zhao mutex_init(&iov->lock); 412d1b054daSYu Zhao 413d1b054daSYu Zhao dev->sriov = iov; 414d1b054daSYu Zhao dev->is_physfn = 1; 415d1b054daSYu Zhao 416d1b054daSYu Zhao return 0; 417d1b054daSYu Zhao 418d1b054daSYu Zhao failed: 419d1b054daSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 420d1b054daSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 421d1b054daSYu Zhao res->flags = 0; 422d1b054daSYu Zhao } 423d1b054daSYu Zhao 424*0e6c9122SWei Yang kfree(iov); 425d1b054daSYu Zhao return rc; 426d1b054daSYu Zhao } 427d1b054daSYu Zhao 428d1b054daSYu Zhao static void sriov_release(struct pci_dev *dev) 429d1b054daSYu Zhao { 4306b136724SBjorn Helgaas BUG_ON(dev->sriov->num_VFs); 431dd7cc44dSYu Zhao 432e277d2fcSYu Zhao if (dev != dev->sriov->dev) 433d1b054daSYu Zhao pci_dev_put(dev->sriov->dev); 434d1b054daSYu Zhao 435e277d2fcSYu Zhao mutex_destroy(&dev->sriov->lock); 436e277d2fcSYu Zhao 437d1b054daSYu Zhao kfree(dev->sriov); 438d1b054daSYu Zhao dev->sriov = NULL; 439d1b054daSYu Zhao } 440d1b054daSYu Zhao 4418c5cdb6aSYu Zhao static void sriov_restore_state(struct pci_dev *dev) 4428c5cdb6aSYu Zhao { 4438c5cdb6aSYu Zhao int i; 4448c5cdb6aSYu Zhao u16 ctrl; 4458c5cdb6aSYu Zhao struct pci_sriov *iov = dev->sriov; 4468c5cdb6aSYu Zhao 4478c5cdb6aSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl); 4488c5cdb6aSYu Zhao if (ctrl & PCI_SRIOV_CTRL_VFE) 4498c5cdb6aSYu Zhao return; 4508c5cdb6aSYu Zhao 4518c5cdb6aSYu Zhao for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) 4528c5cdb6aSYu Zhao pci_update_resource(dev, i); 4538c5cdb6aSYu Zhao 4548c5cdb6aSYu Zhao pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz); 4556b136724SBjorn Helgaas pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->num_VFs); 4568c5cdb6aSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 4578c5cdb6aSYu Zhao if (iov->ctrl & PCI_SRIOV_CTRL_VFE) 4588c5cdb6aSYu Zhao msleep(100); 4598c5cdb6aSYu Zhao } 4608c5cdb6aSYu Zhao 461d1b054daSYu Zhao /** 462d1b054daSYu Zhao * pci_iov_init - initialize the IOV capability 463d1b054daSYu Zhao * @dev: the PCI device 464d1b054daSYu Zhao * 465d1b054daSYu Zhao * Returns 0 on success, or negative on failure. 466d1b054daSYu Zhao */ 467d1b054daSYu Zhao int pci_iov_init(struct pci_dev *dev) 468d1b054daSYu Zhao { 469d1b054daSYu Zhao int pos; 470d1b054daSYu Zhao 4715f4d91a1SKenji Kaneshige if (!pci_is_pcie(dev)) 472d1b054daSYu Zhao return -ENODEV; 473d1b054daSYu Zhao 474d1b054daSYu Zhao pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); 475d1b054daSYu Zhao if (pos) 476d1b054daSYu Zhao return sriov_init(dev, pos); 477d1b054daSYu Zhao 478d1b054daSYu Zhao return -ENODEV; 479d1b054daSYu Zhao } 480d1b054daSYu Zhao 481d1b054daSYu Zhao /** 482d1b054daSYu Zhao * pci_iov_release - release resources used by the IOV capability 483d1b054daSYu Zhao * @dev: the PCI device 484d1b054daSYu Zhao */ 485d1b054daSYu Zhao void pci_iov_release(struct pci_dev *dev) 486d1b054daSYu Zhao { 487d1b054daSYu Zhao if (dev->is_physfn) 488d1b054daSYu Zhao sriov_release(dev); 489d1b054daSYu Zhao } 490d1b054daSYu Zhao 491d1b054daSYu Zhao /** 492d1b054daSYu Zhao * pci_iov_resource_bar - get position of the SR-IOV BAR 493d1b054daSYu Zhao * @dev: the PCI device 494d1b054daSYu Zhao * @resno: the resource number 495d1b054daSYu Zhao * 496d1b054daSYu Zhao * Returns position of the BAR encapsulated in the SR-IOV capability. 497d1b054daSYu Zhao */ 49826ff46c6SMyron Stowe int pci_iov_resource_bar(struct pci_dev *dev, int resno) 499d1b054daSYu Zhao { 500d1b054daSYu Zhao if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END) 501d1b054daSYu Zhao return 0; 502d1b054daSYu Zhao 503d1b054daSYu Zhao BUG_ON(!dev->is_physfn); 504d1b054daSYu Zhao 505d1b054daSYu Zhao return dev->sriov->pos + PCI_SRIOV_BAR + 506d1b054daSYu Zhao 4 * (resno - PCI_IOV_RESOURCES); 507d1b054daSYu Zhao } 5088c5cdb6aSYu Zhao 5098c5cdb6aSYu Zhao /** 5106faf17f6SChris Wright * pci_sriov_resource_alignment - get resource alignment for VF BAR 5116faf17f6SChris Wright * @dev: the PCI device 5126faf17f6SChris Wright * @resno: the resource number 5136faf17f6SChris Wright * 5146faf17f6SChris Wright * Returns the alignment of the VF BAR found in the SR-IOV capability. 5156faf17f6SChris Wright * This is not the same as the resource size which is defined as 5166faf17f6SChris Wright * the VF BAR size multiplied by the number of VFs. The alignment 5176faf17f6SChris Wright * is just the VF BAR size. 5186faf17f6SChris Wright */ 5190e52247aSCam Macdonell resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno) 5206faf17f6SChris Wright { 521*0e6c9122SWei Yang return pci_iov_resource_size(dev, resno); 5226faf17f6SChris Wright } 5236faf17f6SChris Wright 5246faf17f6SChris Wright /** 5258c5cdb6aSYu Zhao * pci_restore_iov_state - restore the state of the IOV capability 5268c5cdb6aSYu Zhao * @dev: the PCI device 5278c5cdb6aSYu Zhao */ 5288c5cdb6aSYu Zhao void pci_restore_iov_state(struct pci_dev *dev) 5298c5cdb6aSYu Zhao { 5308c5cdb6aSYu Zhao if (dev->is_physfn) 5318c5cdb6aSYu Zhao sriov_restore_state(dev); 5328c5cdb6aSYu Zhao } 533a28724b0SYu Zhao 534a28724b0SYu Zhao /** 535a28724b0SYu Zhao * pci_iov_bus_range - find bus range used by Virtual Function 536a28724b0SYu Zhao * @bus: the PCI bus 537a28724b0SYu Zhao * 538a28724b0SYu Zhao * Returns max number of buses (exclude current one) used by Virtual 539a28724b0SYu Zhao * Functions. 540a28724b0SYu Zhao */ 541a28724b0SYu Zhao int pci_iov_bus_range(struct pci_bus *bus) 542a28724b0SYu Zhao { 543a28724b0SYu Zhao int max = 0; 544a28724b0SYu Zhao u8 busnr; 545a28724b0SYu Zhao struct pci_dev *dev; 546a28724b0SYu Zhao 547a28724b0SYu Zhao list_for_each_entry(dev, &bus->devices, bus_list) { 548a28724b0SYu Zhao if (!dev->is_physfn) 549a28724b0SYu Zhao continue; 5506b136724SBjorn Helgaas busnr = virtfn_bus(dev, dev->sriov->total_VFs - 1); 551a28724b0SYu Zhao if (busnr > max) 552a28724b0SYu Zhao max = busnr; 553a28724b0SYu Zhao } 554a28724b0SYu Zhao 555a28724b0SYu Zhao return max ? max - bus->number : 0; 556a28724b0SYu Zhao } 557dd7cc44dSYu Zhao 558dd7cc44dSYu Zhao /** 559dd7cc44dSYu Zhao * pci_enable_sriov - enable the SR-IOV capability 560dd7cc44dSYu Zhao * @dev: the PCI device 56152a8873bSRandy Dunlap * @nr_virtfn: number of virtual functions to enable 562dd7cc44dSYu Zhao * 563dd7cc44dSYu Zhao * Returns 0 on success, or negative on failure. 564dd7cc44dSYu Zhao */ 565dd7cc44dSYu Zhao int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) 566dd7cc44dSYu Zhao { 567dd7cc44dSYu Zhao might_sleep(); 568dd7cc44dSYu Zhao 569dd7cc44dSYu Zhao if (!dev->is_physfn) 570652d1100SStefan Assmann return -ENOSYS; 571dd7cc44dSYu Zhao 572dd7cc44dSYu Zhao return sriov_enable(dev, nr_virtfn); 573dd7cc44dSYu Zhao } 574dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_enable_sriov); 575dd7cc44dSYu Zhao 576dd7cc44dSYu Zhao /** 577dd7cc44dSYu Zhao * pci_disable_sriov - disable the SR-IOV capability 578dd7cc44dSYu Zhao * @dev: the PCI device 579dd7cc44dSYu Zhao */ 580dd7cc44dSYu Zhao void pci_disable_sriov(struct pci_dev *dev) 581dd7cc44dSYu Zhao { 582dd7cc44dSYu Zhao might_sleep(); 583dd7cc44dSYu Zhao 584dd7cc44dSYu Zhao if (!dev->is_physfn) 585dd7cc44dSYu Zhao return; 586dd7cc44dSYu Zhao 587dd7cc44dSYu Zhao sriov_disable(dev); 588dd7cc44dSYu Zhao } 589dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_disable_sriov); 59074bb1bccSYu Zhao 59174bb1bccSYu Zhao /** 592fb8a0d9dSWilliams, Mitch A * pci_num_vf - return number of VFs associated with a PF device_release_driver 593fb8a0d9dSWilliams, Mitch A * @dev: the PCI device 594fb8a0d9dSWilliams, Mitch A * 595fb8a0d9dSWilliams, Mitch A * Returns number of VFs, or 0 if SR-IOV is not enabled. 596fb8a0d9dSWilliams, Mitch A */ 597fb8a0d9dSWilliams, Mitch A int pci_num_vf(struct pci_dev *dev) 598fb8a0d9dSWilliams, Mitch A { 5991452cd76SBjorn Helgaas if (!dev->is_physfn) 600fb8a0d9dSWilliams, Mitch A return 0; 6011452cd76SBjorn Helgaas 6026b136724SBjorn Helgaas return dev->sriov->num_VFs; 603fb8a0d9dSWilliams, Mitch A } 604fb8a0d9dSWilliams, Mitch A EXPORT_SYMBOL_GPL(pci_num_vf); 605bff73156SDonald Dutile 606bff73156SDonald Dutile /** 6075a8eb242SAlexander Duyck * pci_vfs_assigned - returns number of VFs are assigned to a guest 6085a8eb242SAlexander Duyck * @dev: the PCI device 6095a8eb242SAlexander Duyck * 6105a8eb242SAlexander Duyck * Returns number of VFs belonging to this device that are assigned to a guest. 611652d1100SStefan Assmann * If device is not a physical function returns 0. 6125a8eb242SAlexander Duyck */ 6135a8eb242SAlexander Duyck int pci_vfs_assigned(struct pci_dev *dev) 6145a8eb242SAlexander Duyck { 6155a8eb242SAlexander Duyck struct pci_dev *vfdev; 6165a8eb242SAlexander Duyck unsigned int vfs_assigned = 0; 6175a8eb242SAlexander Duyck unsigned short dev_id; 6185a8eb242SAlexander Duyck 6195a8eb242SAlexander Duyck /* only search if we are a PF */ 6205a8eb242SAlexander Duyck if (!dev->is_physfn) 6215a8eb242SAlexander Duyck return 0; 6225a8eb242SAlexander Duyck 6235a8eb242SAlexander Duyck /* 6245a8eb242SAlexander Duyck * determine the device ID for the VFs, the vendor ID will be the 6255a8eb242SAlexander Duyck * same as the PF so there is no need to check for that one 6265a8eb242SAlexander Duyck */ 6275a8eb242SAlexander Duyck pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_VF_DID, &dev_id); 6285a8eb242SAlexander Duyck 6295a8eb242SAlexander Duyck /* loop through all the VFs to see if we own any that are assigned */ 6305a8eb242SAlexander Duyck vfdev = pci_get_device(dev->vendor, dev_id, NULL); 6315a8eb242SAlexander Duyck while (vfdev) { 6325a8eb242SAlexander Duyck /* 6335a8eb242SAlexander Duyck * It is considered assigned if it is a virtual function with 6345a8eb242SAlexander Duyck * our dev as the physical function and the assigned bit is set 6355a8eb242SAlexander Duyck */ 6365a8eb242SAlexander Duyck if (vfdev->is_virtfn && (vfdev->physfn == dev) && 637be63497cSEthan Zhao pci_is_dev_assigned(vfdev)) 6385a8eb242SAlexander Duyck vfs_assigned++; 6395a8eb242SAlexander Duyck 6405a8eb242SAlexander Duyck vfdev = pci_get_device(dev->vendor, dev_id, vfdev); 6415a8eb242SAlexander Duyck } 6425a8eb242SAlexander Duyck 6435a8eb242SAlexander Duyck return vfs_assigned; 6445a8eb242SAlexander Duyck } 6455a8eb242SAlexander Duyck EXPORT_SYMBOL_GPL(pci_vfs_assigned); 6465a8eb242SAlexander Duyck 6475a8eb242SAlexander Duyck /** 648bff73156SDonald Dutile * pci_sriov_set_totalvfs -- reduce the TotalVFs available 649bff73156SDonald Dutile * @dev: the PCI PF device 6502094f167SRandy Dunlap * @numvfs: number that should be used for TotalVFs supported 651bff73156SDonald Dutile * 652bff73156SDonald Dutile * Should be called from PF driver's probe routine with 653bff73156SDonald Dutile * device's mutex held. 654bff73156SDonald Dutile * 655bff73156SDonald Dutile * Returns 0 if PF is an SRIOV-capable device and 656652d1100SStefan Assmann * value of numvfs valid. If not a PF return -ENOSYS; 657652d1100SStefan Assmann * if numvfs is invalid return -EINVAL; 658bff73156SDonald Dutile * if VFs already enabled, return -EBUSY. 659bff73156SDonald Dutile */ 660bff73156SDonald Dutile int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs) 661bff73156SDonald Dutile { 662652d1100SStefan Assmann if (!dev->is_physfn) 663652d1100SStefan Assmann return -ENOSYS; 664652d1100SStefan Assmann if (numvfs > dev->sriov->total_VFs) 665bff73156SDonald Dutile return -EINVAL; 666bff73156SDonald Dutile 667bff73156SDonald Dutile /* Shouldn't change if VFs already enabled */ 668bff73156SDonald Dutile if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE) 669bff73156SDonald Dutile return -EBUSY; 670bff73156SDonald Dutile else 6716b136724SBjorn Helgaas dev->sriov->driver_max_VFs = numvfs; 672bff73156SDonald Dutile 673bff73156SDonald Dutile return 0; 674bff73156SDonald Dutile } 675bff73156SDonald Dutile EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs); 676bff73156SDonald Dutile 677bff73156SDonald Dutile /** 678ddc191f5SJonghwan Choi * pci_sriov_get_totalvfs -- get total VFs supported on this device 679bff73156SDonald Dutile * @dev: the PCI PF device 680bff73156SDonald Dutile * 681bff73156SDonald Dutile * For a PCIe device with SRIOV support, return the PCIe 6826b136724SBjorn Helgaas * SRIOV capability value of TotalVFs or the value of driver_max_VFs 683652d1100SStefan Assmann * if the driver reduced it. Otherwise 0. 684bff73156SDonald Dutile */ 685bff73156SDonald Dutile int pci_sriov_get_totalvfs(struct pci_dev *dev) 686bff73156SDonald Dutile { 6871452cd76SBjorn Helgaas if (!dev->is_physfn) 688652d1100SStefan Assmann return 0; 689bff73156SDonald Dutile 6906b136724SBjorn Helgaas if (dev->sriov->driver_max_VFs) 6916b136724SBjorn Helgaas return dev->sriov->driver_max_VFs; 6921452cd76SBjorn Helgaas 6936b136724SBjorn Helgaas return dev->sriov->total_VFs; 694bff73156SDonald Dutile } 695bff73156SDonald Dutile EXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs); 696