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 8d1b054daSYu Zhao */ 9d1b054daSYu Zhao 10d1b054daSYu Zhao #include <linux/pci.h> 11d1b054daSYu Zhao #include <linux/mutex.h> 12d1b054daSYu Zhao #include <linux/string.h> 13d1b054daSYu Zhao #include <linux/delay.h> 14d1b054daSYu Zhao #include "pci.h" 15d1b054daSYu Zhao 16*dd7cc44dSYu Zhao #define VIRTFN_ID_LEN 16 17d1b054daSYu Zhao 18a28724b0SYu Zhao static inline u8 virtfn_bus(struct pci_dev *dev, int id) 19a28724b0SYu Zhao { 20a28724b0SYu Zhao return dev->bus->number + ((dev->devfn + dev->sriov->offset + 21a28724b0SYu Zhao dev->sriov->stride * id) >> 8); 22a28724b0SYu Zhao } 23a28724b0SYu Zhao 24a28724b0SYu Zhao static inline u8 virtfn_devfn(struct pci_dev *dev, int id) 25a28724b0SYu Zhao { 26a28724b0SYu Zhao return (dev->devfn + dev->sriov->offset + 27a28724b0SYu Zhao dev->sriov->stride * id) & 0xff; 28a28724b0SYu Zhao } 29a28724b0SYu Zhao 30*dd7cc44dSYu Zhao static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr) 31*dd7cc44dSYu Zhao { 32*dd7cc44dSYu Zhao int rc; 33*dd7cc44dSYu Zhao struct pci_bus *child; 34*dd7cc44dSYu Zhao 35*dd7cc44dSYu Zhao if (bus->number == busnr) 36*dd7cc44dSYu Zhao return bus; 37*dd7cc44dSYu Zhao 38*dd7cc44dSYu Zhao child = pci_find_bus(pci_domain_nr(bus), busnr); 39*dd7cc44dSYu Zhao if (child) 40*dd7cc44dSYu Zhao return child; 41*dd7cc44dSYu Zhao 42*dd7cc44dSYu Zhao child = pci_add_new_bus(bus, NULL, busnr); 43*dd7cc44dSYu Zhao if (!child) 44*dd7cc44dSYu Zhao return NULL; 45*dd7cc44dSYu Zhao 46*dd7cc44dSYu Zhao child->subordinate = busnr; 47*dd7cc44dSYu Zhao child->dev.parent = bus->bridge; 48*dd7cc44dSYu Zhao rc = pci_bus_add_child(child); 49*dd7cc44dSYu Zhao if (rc) { 50*dd7cc44dSYu Zhao pci_remove_bus(child); 51*dd7cc44dSYu Zhao return NULL; 52*dd7cc44dSYu Zhao } 53*dd7cc44dSYu Zhao 54*dd7cc44dSYu Zhao return child; 55*dd7cc44dSYu Zhao } 56*dd7cc44dSYu Zhao 57*dd7cc44dSYu Zhao static void virtfn_remove_bus(struct pci_bus *bus, int busnr) 58*dd7cc44dSYu Zhao { 59*dd7cc44dSYu Zhao struct pci_bus *child; 60*dd7cc44dSYu Zhao 61*dd7cc44dSYu Zhao if (bus->number == busnr) 62*dd7cc44dSYu Zhao return; 63*dd7cc44dSYu Zhao 64*dd7cc44dSYu Zhao child = pci_find_bus(pci_domain_nr(bus), busnr); 65*dd7cc44dSYu Zhao BUG_ON(!child); 66*dd7cc44dSYu Zhao 67*dd7cc44dSYu Zhao if (list_empty(&child->devices)) 68*dd7cc44dSYu Zhao pci_remove_bus(child); 69*dd7cc44dSYu Zhao } 70*dd7cc44dSYu Zhao 71*dd7cc44dSYu Zhao static int virtfn_add(struct pci_dev *dev, int id, int reset) 72*dd7cc44dSYu Zhao { 73*dd7cc44dSYu Zhao int i; 74*dd7cc44dSYu Zhao int rc; 75*dd7cc44dSYu Zhao u64 size; 76*dd7cc44dSYu Zhao char buf[VIRTFN_ID_LEN]; 77*dd7cc44dSYu Zhao struct pci_dev *virtfn; 78*dd7cc44dSYu Zhao struct resource *res; 79*dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 80*dd7cc44dSYu Zhao 81*dd7cc44dSYu Zhao virtfn = alloc_pci_dev(); 82*dd7cc44dSYu Zhao if (!virtfn) 83*dd7cc44dSYu Zhao return -ENOMEM; 84*dd7cc44dSYu Zhao 85*dd7cc44dSYu Zhao mutex_lock(&iov->dev->sriov->lock); 86*dd7cc44dSYu Zhao virtfn->bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id)); 87*dd7cc44dSYu Zhao if (!virtfn->bus) { 88*dd7cc44dSYu Zhao kfree(virtfn); 89*dd7cc44dSYu Zhao mutex_unlock(&iov->dev->sriov->lock); 90*dd7cc44dSYu Zhao return -ENOMEM; 91*dd7cc44dSYu Zhao } 92*dd7cc44dSYu Zhao virtfn->devfn = virtfn_devfn(dev, id); 93*dd7cc44dSYu Zhao virtfn->vendor = dev->vendor; 94*dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device); 95*dd7cc44dSYu Zhao pci_setup_device(virtfn); 96*dd7cc44dSYu Zhao virtfn->dev.parent = dev->dev.parent; 97*dd7cc44dSYu Zhao 98*dd7cc44dSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 99*dd7cc44dSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 100*dd7cc44dSYu Zhao if (!res->parent) 101*dd7cc44dSYu Zhao continue; 102*dd7cc44dSYu Zhao virtfn->resource[i].name = pci_name(virtfn); 103*dd7cc44dSYu Zhao virtfn->resource[i].flags = res->flags; 104*dd7cc44dSYu Zhao size = resource_size(res); 105*dd7cc44dSYu Zhao do_div(size, iov->total); 106*dd7cc44dSYu Zhao virtfn->resource[i].start = res->start + size * id; 107*dd7cc44dSYu Zhao virtfn->resource[i].end = virtfn->resource[i].start + size - 1; 108*dd7cc44dSYu Zhao rc = request_resource(res, &virtfn->resource[i]); 109*dd7cc44dSYu Zhao BUG_ON(rc); 110*dd7cc44dSYu Zhao } 111*dd7cc44dSYu Zhao 112*dd7cc44dSYu Zhao if (reset) 113*dd7cc44dSYu Zhao pci_execute_reset_function(virtfn); 114*dd7cc44dSYu Zhao 115*dd7cc44dSYu Zhao pci_device_add(virtfn, virtfn->bus); 116*dd7cc44dSYu Zhao mutex_unlock(&iov->dev->sriov->lock); 117*dd7cc44dSYu Zhao 118*dd7cc44dSYu Zhao virtfn->physfn = pci_dev_get(dev); 119*dd7cc44dSYu Zhao virtfn->is_virtfn = 1; 120*dd7cc44dSYu Zhao 121*dd7cc44dSYu Zhao rc = pci_bus_add_device(virtfn); 122*dd7cc44dSYu Zhao if (rc) 123*dd7cc44dSYu Zhao goto failed1; 124*dd7cc44dSYu Zhao sprintf(buf, "virtfn%u", id); 125*dd7cc44dSYu Zhao rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf); 126*dd7cc44dSYu Zhao if (rc) 127*dd7cc44dSYu Zhao goto failed1; 128*dd7cc44dSYu Zhao rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn"); 129*dd7cc44dSYu Zhao if (rc) 130*dd7cc44dSYu Zhao goto failed2; 131*dd7cc44dSYu Zhao 132*dd7cc44dSYu Zhao kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE); 133*dd7cc44dSYu Zhao 134*dd7cc44dSYu Zhao return 0; 135*dd7cc44dSYu Zhao 136*dd7cc44dSYu Zhao failed2: 137*dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, buf); 138*dd7cc44dSYu Zhao failed1: 139*dd7cc44dSYu Zhao pci_dev_put(dev); 140*dd7cc44dSYu Zhao mutex_lock(&iov->dev->sriov->lock); 141*dd7cc44dSYu Zhao pci_remove_bus_device(virtfn); 142*dd7cc44dSYu Zhao virtfn_remove_bus(dev->bus, virtfn_bus(dev, id)); 143*dd7cc44dSYu Zhao mutex_unlock(&iov->dev->sriov->lock); 144*dd7cc44dSYu Zhao 145*dd7cc44dSYu Zhao return rc; 146*dd7cc44dSYu Zhao } 147*dd7cc44dSYu Zhao 148*dd7cc44dSYu Zhao static void virtfn_remove(struct pci_dev *dev, int id, int reset) 149*dd7cc44dSYu Zhao { 150*dd7cc44dSYu Zhao char buf[VIRTFN_ID_LEN]; 151*dd7cc44dSYu Zhao struct pci_bus *bus; 152*dd7cc44dSYu Zhao struct pci_dev *virtfn; 153*dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 154*dd7cc44dSYu Zhao 155*dd7cc44dSYu Zhao bus = pci_find_bus(pci_domain_nr(dev->bus), virtfn_bus(dev, id)); 156*dd7cc44dSYu Zhao if (!bus) 157*dd7cc44dSYu Zhao return; 158*dd7cc44dSYu Zhao 159*dd7cc44dSYu Zhao virtfn = pci_get_slot(bus, virtfn_devfn(dev, id)); 160*dd7cc44dSYu Zhao if (!virtfn) 161*dd7cc44dSYu Zhao return; 162*dd7cc44dSYu Zhao 163*dd7cc44dSYu Zhao pci_dev_put(virtfn); 164*dd7cc44dSYu Zhao 165*dd7cc44dSYu Zhao if (reset) { 166*dd7cc44dSYu Zhao device_release_driver(&virtfn->dev); 167*dd7cc44dSYu Zhao pci_execute_reset_function(virtfn); 168*dd7cc44dSYu Zhao } 169*dd7cc44dSYu Zhao 170*dd7cc44dSYu Zhao sprintf(buf, "virtfn%u", id); 171*dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, buf); 172*dd7cc44dSYu Zhao sysfs_remove_link(&virtfn->dev.kobj, "physfn"); 173*dd7cc44dSYu Zhao 174*dd7cc44dSYu Zhao mutex_lock(&iov->dev->sriov->lock); 175*dd7cc44dSYu Zhao pci_remove_bus_device(virtfn); 176*dd7cc44dSYu Zhao virtfn_remove_bus(dev->bus, virtfn_bus(dev, id)); 177*dd7cc44dSYu Zhao mutex_unlock(&iov->dev->sriov->lock); 178*dd7cc44dSYu Zhao 179*dd7cc44dSYu Zhao pci_dev_put(dev); 180*dd7cc44dSYu Zhao } 181*dd7cc44dSYu Zhao 182*dd7cc44dSYu Zhao static int sriov_enable(struct pci_dev *dev, int nr_virtfn) 183*dd7cc44dSYu Zhao { 184*dd7cc44dSYu Zhao int rc; 185*dd7cc44dSYu Zhao int i, j; 186*dd7cc44dSYu Zhao int nres; 187*dd7cc44dSYu Zhao u16 offset, stride, initial; 188*dd7cc44dSYu Zhao struct resource *res; 189*dd7cc44dSYu Zhao struct pci_dev *pdev; 190*dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 191*dd7cc44dSYu Zhao 192*dd7cc44dSYu Zhao if (!nr_virtfn) 193*dd7cc44dSYu Zhao return 0; 194*dd7cc44dSYu Zhao 195*dd7cc44dSYu Zhao if (iov->nr_virtfn) 196*dd7cc44dSYu Zhao return -EINVAL; 197*dd7cc44dSYu Zhao 198*dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial); 199*dd7cc44dSYu Zhao if (initial > iov->total || 200*dd7cc44dSYu Zhao (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total))) 201*dd7cc44dSYu Zhao return -EIO; 202*dd7cc44dSYu Zhao 203*dd7cc44dSYu Zhao if (nr_virtfn < 0 || nr_virtfn > iov->total || 204*dd7cc44dSYu Zhao (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial))) 205*dd7cc44dSYu Zhao return -EINVAL; 206*dd7cc44dSYu Zhao 207*dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn); 208*dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset); 209*dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride); 210*dd7cc44dSYu Zhao if (!offset || (nr_virtfn > 1 && !stride)) 211*dd7cc44dSYu Zhao return -EIO; 212*dd7cc44dSYu Zhao 213*dd7cc44dSYu Zhao nres = 0; 214*dd7cc44dSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 215*dd7cc44dSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 216*dd7cc44dSYu Zhao if (res->parent) 217*dd7cc44dSYu Zhao nres++; 218*dd7cc44dSYu Zhao } 219*dd7cc44dSYu Zhao if (nres != iov->nres) { 220*dd7cc44dSYu Zhao dev_err(&dev->dev, "not enough MMIO resources for SR-IOV\n"); 221*dd7cc44dSYu Zhao return -ENOMEM; 222*dd7cc44dSYu Zhao } 223*dd7cc44dSYu Zhao 224*dd7cc44dSYu Zhao iov->offset = offset; 225*dd7cc44dSYu Zhao iov->stride = stride; 226*dd7cc44dSYu Zhao 227*dd7cc44dSYu Zhao if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->subordinate) { 228*dd7cc44dSYu Zhao dev_err(&dev->dev, "SR-IOV: bus number out of range\n"); 229*dd7cc44dSYu Zhao return -ENOMEM; 230*dd7cc44dSYu Zhao } 231*dd7cc44dSYu Zhao 232*dd7cc44dSYu Zhao if (iov->link != dev->devfn) { 233*dd7cc44dSYu Zhao pdev = pci_get_slot(dev->bus, iov->link); 234*dd7cc44dSYu Zhao if (!pdev) 235*dd7cc44dSYu Zhao return -ENODEV; 236*dd7cc44dSYu Zhao 237*dd7cc44dSYu Zhao pci_dev_put(pdev); 238*dd7cc44dSYu Zhao 239*dd7cc44dSYu Zhao if (!pdev->is_physfn) 240*dd7cc44dSYu Zhao return -ENODEV; 241*dd7cc44dSYu Zhao 242*dd7cc44dSYu Zhao rc = sysfs_create_link(&dev->dev.kobj, 243*dd7cc44dSYu Zhao &pdev->dev.kobj, "dep_link"); 244*dd7cc44dSYu Zhao if (rc) 245*dd7cc44dSYu Zhao return rc; 246*dd7cc44dSYu Zhao } 247*dd7cc44dSYu Zhao 248*dd7cc44dSYu Zhao iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE; 249*dd7cc44dSYu Zhao pci_block_user_cfg_access(dev); 250*dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 251*dd7cc44dSYu Zhao msleep(100); 252*dd7cc44dSYu Zhao pci_unblock_user_cfg_access(dev); 253*dd7cc44dSYu Zhao 254*dd7cc44dSYu Zhao iov->initial = initial; 255*dd7cc44dSYu Zhao if (nr_virtfn < initial) 256*dd7cc44dSYu Zhao initial = nr_virtfn; 257*dd7cc44dSYu Zhao 258*dd7cc44dSYu Zhao for (i = 0; i < initial; i++) { 259*dd7cc44dSYu Zhao rc = virtfn_add(dev, i, 0); 260*dd7cc44dSYu Zhao if (rc) 261*dd7cc44dSYu Zhao goto failed; 262*dd7cc44dSYu Zhao } 263*dd7cc44dSYu Zhao 264*dd7cc44dSYu Zhao kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); 265*dd7cc44dSYu Zhao iov->nr_virtfn = nr_virtfn; 266*dd7cc44dSYu Zhao 267*dd7cc44dSYu Zhao return 0; 268*dd7cc44dSYu Zhao 269*dd7cc44dSYu Zhao failed: 270*dd7cc44dSYu Zhao for (j = 0; j < i; j++) 271*dd7cc44dSYu Zhao virtfn_remove(dev, j, 0); 272*dd7cc44dSYu Zhao 273*dd7cc44dSYu Zhao iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); 274*dd7cc44dSYu Zhao pci_block_user_cfg_access(dev); 275*dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 276*dd7cc44dSYu Zhao ssleep(1); 277*dd7cc44dSYu Zhao pci_unblock_user_cfg_access(dev); 278*dd7cc44dSYu Zhao 279*dd7cc44dSYu Zhao if (iov->link != dev->devfn) 280*dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, "dep_link"); 281*dd7cc44dSYu Zhao 282*dd7cc44dSYu Zhao return rc; 283*dd7cc44dSYu Zhao } 284*dd7cc44dSYu Zhao 285*dd7cc44dSYu Zhao static void sriov_disable(struct pci_dev *dev) 286*dd7cc44dSYu Zhao { 287*dd7cc44dSYu Zhao int i; 288*dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 289*dd7cc44dSYu Zhao 290*dd7cc44dSYu Zhao if (!iov->nr_virtfn) 291*dd7cc44dSYu Zhao return; 292*dd7cc44dSYu Zhao 293*dd7cc44dSYu Zhao for (i = 0; i < iov->nr_virtfn; i++) 294*dd7cc44dSYu Zhao virtfn_remove(dev, i, 0); 295*dd7cc44dSYu Zhao 296*dd7cc44dSYu Zhao iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); 297*dd7cc44dSYu Zhao pci_block_user_cfg_access(dev); 298*dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 299*dd7cc44dSYu Zhao ssleep(1); 300*dd7cc44dSYu Zhao pci_unblock_user_cfg_access(dev); 301*dd7cc44dSYu Zhao 302*dd7cc44dSYu Zhao if (iov->link != dev->devfn) 303*dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, "dep_link"); 304*dd7cc44dSYu Zhao 305*dd7cc44dSYu Zhao iov->nr_virtfn = 0; 306*dd7cc44dSYu Zhao } 307*dd7cc44dSYu Zhao 308d1b054daSYu Zhao static int sriov_init(struct pci_dev *dev, int pos) 309d1b054daSYu Zhao { 310d1b054daSYu Zhao int i; 311d1b054daSYu Zhao int rc; 312d1b054daSYu Zhao int nres; 313d1b054daSYu Zhao u32 pgsz; 314d1b054daSYu Zhao u16 ctrl, total, offset, stride; 315d1b054daSYu Zhao struct pci_sriov *iov; 316d1b054daSYu Zhao struct resource *res; 317d1b054daSYu Zhao struct pci_dev *pdev; 318d1b054daSYu Zhao 319d1b054daSYu Zhao if (dev->pcie_type != PCI_EXP_TYPE_RC_END && 320d1b054daSYu Zhao dev->pcie_type != PCI_EXP_TYPE_ENDPOINT) 321d1b054daSYu Zhao return -ENODEV; 322d1b054daSYu Zhao 323d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl); 324d1b054daSYu Zhao if (ctrl & PCI_SRIOV_CTRL_VFE) { 325d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0); 326d1b054daSYu Zhao ssleep(1); 327d1b054daSYu Zhao } 328d1b054daSYu Zhao 329d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total); 330d1b054daSYu Zhao if (!total) 331d1b054daSYu Zhao return 0; 332d1b054daSYu Zhao 333d1b054daSYu Zhao ctrl = 0; 334d1b054daSYu Zhao list_for_each_entry(pdev, &dev->bus->devices, bus_list) 335d1b054daSYu Zhao if (pdev->is_physfn) 336d1b054daSYu Zhao goto found; 337d1b054daSYu Zhao 338d1b054daSYu Zhao pdev = NULL; 339d1b054daSYu Zhao if (pci_ari_enabled(dev->bus)) 340d1b054daSYu Zhao ctrl |= PCI_SRIOV_CTRL_ARI; 341d1b054daSYu Zhao 342d1b054daSYu Zhao found: 343d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl); 344d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, total); 345d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset); 346d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride); 347d1b054daSYu Zhao if (!offset || (total > 1 && !stride)) 348d1b054daSYu Zhao return -EIO; 349d1b054daSYu Zhao 350d1b054daSYu Zhao pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz); 351d1b054daSYu Zhao i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0; 352d1b054daSYu Zhao pgsz &= ~((1 << i) - 1); 353d1b054daSYu Zhao if (!pgsz) 354d1b054daSYu Zhao return -EIO; 355d1b054daSYu Zhao 356d1b054daSYu Zhao pgsz &= ~(pgsz - 1); 357d1b054daSYu Zhao pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz); 358d1b054daSYu Zhao 359d1b054daSYu Zhao nres = 0; 360d1b054daSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 361d1b054daSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 362d1b054daSYu Zhao i += __pci_read_base(dev, pci_bar_unknown, res, 363d1b054daSYu Zhao pos + PCI_SRIOV_BAR + i * 4); 364d1b054daSYu Zhao if (!res->flags) 365d1b054daSYu Zhao continue; 366d1b054daSYu Zhao if (resource_size(res) & (PAGE_SIZE - 1)) { 367d1b054daSYu Zhao rc = -EIO; 368d1b054daSYu Zhao goto failed; 369d1b054daSYu Zhao } 370d1b054daSYu Zhao res->end = res->start + resource_size(res) * total - 1; 371d1b054daSYu Zhao nres++; 372d1b054daSYu Zhao } 373d1b054daSYu Zhao 374d1b054daSYu Zhao iov = kzalloc(sizeof(*iov), GFP_KERNEL); 375d1b054daSYu Zhao if (!iov) { 376d1b054daSYu Zhao rc = -ENOMEM; 377d1b054daSYu Zhao goto failed; 378d1b054daSYu Zhao } 379d1b054daSYu Zhao 380d1b054daSYu Zhao iov->pos = pos; 381d1b054daSYu Zhao iov->nres = nres; 382d1b054daSYu Zhao iov->ctrl = ctrl; 383d1b054daSYu Zhao iov->total = total; 384d1b054daSYu Zhao iov->offset = offset; 385d1b054daSYu Zhao iov->stride = stride; 386d1b054daSYu Zhao iov->pgsz = pgsz; 387d1b054daSYu Zhao iov->self = dev; 388d1b054daSYu Zhao pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap); 389d1b054daSYu Zhao pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link); 390d1b054daSYu Zhao 391d1b054daSYu Zhao if (pdev) 392d1b054daSYu Zhao iov->dev = pci_dev_get(pdev); 393d1b054daSYu Zhao else { 394d1b054daSYu Zhao iov->dev = dev; 395d1b054daSYu Zhao mutex_init(&iov->lock); 396d1b054daSYu Zhao } 397d1b054daSYu Zhao 398d1b054daSYu Zhao dev->sriov = iov; 399d1b054daSYu Zhao dev->is_physfn = 1; 400d1b054daSYu Zhao 401d1b054daSYu Zhao return 0; 402d1b054daSYu Zhao 403d1b054daSYu Zhao failed: 404d1b054daSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 405d1b054daSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 406d1b054daSYu Zhao res->flags = 0; 407d1b054daSYu Zhao } 408d1b054daSYu Zhao 409d1b054daSYu Zhao return rc; 410d1b054daSYu Zhao } 411d1b054daSYu Zhao 412d1b054daSYu Zhao static void sriov_release(struct pci_dev *dev) 413d1b054daSYu Zhao { 414*dd7cc44dSYu Zhao BUG_ON(dev->sriov->nr_virtfn); 415*dd7cc44dSYu Zhao 416d1b054daSYu Zhao if (dev == dev->sriov->dev) 417d1b054daSYu Zhao mutex_destroy(&dev->sriov->lock); 418d1b054daSYu Zhao else 419d1b054daSYu Zhao pci_dev_put(dev->sriov->dev); 420d1b054daSYu Zhao 421d1b054daSYu Zhao kfree(dev->sriov); 422d1b054daSYu Zhao dev->sriov = NULL; 423d1b054daSYu Zhao } 424d1b054daSYu Zhao 4258c5cdb6aSYu Zhao static void sriov_restore_state(struct pci_dev *dev) 4268c5cdb6aSYu Zhao { 4278c5cdb6aSYu Zhao int i; 4288c5cdb6aSYu Zhao u16 ctrl; 4298c5cdb6aSYu Zhao struct pci_sriov *iov = dev->sriov; 4308c5cdb6aSYu Zhao 4318c5cdb6aSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl); 4328c5cdb6aSYu Zhao if (ctrl & PCI_SRIOV_CTRL_VFE) 4338c5cdb6aSYu Zhao return; 4348c5cdb6aSYu Zhao 4358c5cdb6aSYu Zhao for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) 4368c5cdb6aSYu Zhao pci_update_resource(dev, i); 4378c5cdb6aSYu Zhao 4388c5cdb6aSYu Zhao pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz); 439*dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->nr_virtfn); 4408c5cdb6aSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 4418c5cdb6aSYu Zhao if (iov->ctrl & PCI_SRIOV_CTRL_VFE) 4428c5cdb6aSYu Zhao msleep(100); 4438c5cdb6aSYu Zhao } 4448c5cdb6aSYu Zhao 445d1b054daSYu Zhao /** 446d1b054daSYu Zhao * pci_iov_init - initialize the IOV capability 447d1b054daSYu Zhao * @dev: the PCI device 448d1b054daSYu Zhao * 449d1b054daSYu Zhao * Returns 0 on success, or negative on failure. 450d1b054daSYu Zhao */ 451d1b054daSYu Zhao int pci_iov_init(struct pci_dev *dev) 452d1b054daSYu Zhao { 453d1b054daSYu Zhao int pos; 454d1b054daSYu Zhao 455d1b054daSYu Zhao if (!dev->is_pcie) 456d1b054daSYu Zhao return -ENODEV; 457d1b054daSYu Zhao 458d1b054daSYu Zhao pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); 459d1b054daSYu Zhao if (pos) 460d1b054daSYu Zhao return sriov_init(dev, pos); 461d1b054daSYu Zhao 462d1b054daSYu Zhao return -ENODEV; 463d1b054daSYu Zhao } 464d1b054daSYu Zhao 465d1b054daSYu Zhao /** 466d1b054daSYu Zhao * pci_iov_release - release resources used by the IOV capability 467d1b054daSYu Zhao * @dev: the PCI device 468d1b054daSYu Zhao */ 469d1b054daSYu Zhao void pci_iov_release(struct pci_dev *dev) 470d1b054daSYu Zhao { 471d1b054daSYu Zhao if (dev->is_physfn) 472d1b054daSYu Zhao sriov_release(dev); 473d1b054daSYu Zhao } 474d1b054daSYu Zhao 475d1b054daSYu Zhao /** 476d1b054daSYu Zhao * pci_iov_resource_bar - get position of the SR-IOV BAR 477d1b054daSYu Zhao * @dev: the PCI device 478d1b054daSYu Zhao * @resno: the resource number 479d1b054daSYu Zhao * @type: the BAR type to be filled in 480d1b054daSYu Zhao * 481d1b054daSYu Zhao * Returns position of the BAR encapsulated in the SR-IOV capability. 482d1b054daSYu Zhao */ 483d1b054daSYu Zhao int pci_iov_resource_bar(struct pci_dev *dev, int resno, 484d1b054daSYu Zhao enum pci_bar_type *type) 485d1b054daSYu Zhao { 486d1b054daSYu Zhao if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END) 487d1b054daSYu Zhao return 0; 488d1b054daSYu Zhao 489d1b054daSYu Zhao BUG_ON(!dev->is_physfn); 490d1b054daSYu Zhao 491d1b054daSYu Zhao *type = pci_bar_unknown; 492d1b054daSYu Zhao 493d1b054daSYu Zhao return dev->sriov->pos + PCI_SRIOV_BAR + 494d1b054daSYu Zhao 4 * (resno - PCI_IOV_RESOURCES); 495d1b054daSYu Zhao } 4968c5cdb6aSYu Zhao 4978c5cdb6aSYu Zhao /** 4988c5cdb6aSYu Zhao * pci_restore_iov_state - restore the state of the IOV capability 4998c5cdb6aSYu Zhao * @dev: the PCI device 5008c5cdb6aSYu Zhao */ 5018c5cdb6aSYu Zhao void pci_restore_iov_state(struct pci_dev *dev) 5028c5cdb6aSYu Zhao { 5038c5cdb6aSYu Zhao if (dev->is_physfn) 5048c5cdb6aSYu Zhao sriov_restore_state(dev); 5058c5cdb6aSYu Zhao } 506a28724b0SYu Zhao 507a28724b0SYu Zhao /** 508a28724b0SYu Zhao * pci_iov_bus_range - find bus range used by Virtual Function 509a28724b0SYu Zhao * @bus: the PCI bus 510a28724b0SYu Zhao * 511a28724b0SYu Zhao * Returns max number of buses (exclude current one) used by Virtual 512a28724b0SYu Zhao * Functions. 513a28724b0SYu Zhao */ 514a28724b0SYu Zhao int pci_iov_bus_range(struct pci_bus *bus) 515a28724b0SYu Zhao { 516a28724b0SYu Zhao int max = 0; 517a28724b0SYu Zhao u8 busnr; 518a28724b0SYu Zhao struct pci_dev *dev; 519a28724b0SYu Zhao 520a28724b0SYu Zhao list_for_each_entry(dev, &bus->devices, bus_list) { 521a28724b0SYu Zhao if (!dev->is_physfn) 522a28724b0SYu Zhao continue; 523a28724b0SYu Zhao busnr = virtfn_bus(dev, dev->sriov->total - 1); 524a28724b0SYu Zhao if (busnr > max) 525a28724b0SYu Zhao max = busnr; 526a28724b0SYu Zhao } 527a28724b0SYu Zhao 528a28724b0SYu Zhao return max ? max - bus->number : 0; 529a28724b0SYu Zhao } 530*dd7cc44dSYu Zhao 531*dd7cc44dSYu Zhao /** 532*dd7cc44dSYu Zhao * pci_enable_sriov - enable the SR-IOV capability 533*dd7cc44dSYu Zhao * @dev: the PCI device 534*dd7cc44dSYu Zhao * 535*dd7cc44dSYu Zhao * Returns 0 on success, or negative on failure. 536*dd7cc44dSYu Zhao */ 537*dd7cc44dSYu Zhao int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) 538*dd7cc44dSYu Zhao { 539*dd7cc44dSYu Zhao might_sleep(); 540*dd7cc44dSYu Zhao 541*dd7cc44dSYu Zhao if (!dev->is_physfn) 542*dd7cc44dSYu Zhao return -ENODEV; 543*dd7cc44dSYu Zhao 544*dd7cc44dSYu Zhao return sriov_enable(dev, nr_virtfn); 545*dd7cc44dSYu Zhao } 546*dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_enable_sriov); 547*dd7cc44dSYu Zhao 548*dd7cc44dSYu Zhao /** 549*dd7cc44dSYu Zhao * pci_disable_sriov - disable the SR-IOV capability 550*dd7cc44dSYu Zhao * @dev: the PCI device 551*dd7cc44dSYu Zhao */ 552*dd7cc44dSYu Zhao void pci_disable_sriov(struct pci_dev *dev) 553*dd7cc44dSYu Zhao { 554*dd7cc44dSYu Zhao might_sleep(); 555*dd7cc44dSYu Zhao 556*dd7cc44dSYu Zhao if (!dev->is_physfn) 557*dd7cc44dSYu Zhao return; 558*dd7cc44dSYu Zhao 559*dd7cc44dSYu Zhao sriov_disable(dev); 560*dd7cc44dSYu Zhao } 561*dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_disable_sriov); 562