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> 12d1b054daSYu Zhao #include <linux/mutex.h> 13d1b054daSYu Zhao #include <linux/string.h> 14d1b054daSYu Zhao #include <linux/delay.h> 15d1b054daSYu Zhao #include "pci.h" 16d1b054daSYu Zhao 17dd7cc44dSYu Zhao #define VIRTFN_ID_LEN 16 18d1b054daSYu Zhao 19a28724b0SYu Zhao static inline u8 virtfn_bus(struct pci_dev *dev, int id) 20a28724b0SYu Zhao { 21a28724b0SYu Zhao return dev->bus->number + ((dev->devfn + dev->sriov->offset + 22a28724b0SYu Zhao dev->sriov->stride * id) >> 8); 23a28724b0SYu Zhao } 24a28724b0SYu Zhao 25a28724b0SYu Zhao static inline u8 virtfn_devfn(struct pci_dev *dev, int id) 26a28724b0SYu Zhao { 27a28724b0SYu Zhao return (dev->devfn + dev->sriov->offset + 28a28724b0SYu Zhao dev->sriov->stride * id) & 0xff; 29a28724b0SYu Zhao } 30a28724b0SYu Zhao 31dd7cc44dSYu Zhao static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr) 32dd7cc44dSYu Zhao { 33dd7cc44dSYu Zhao int rc; 34dd7cc44dSYu Zhao struct pci_bus *child; 35dd7cc44dSYu Zhao 36dd7cc44dSYu Zhao if (bus->number == busnr) 37dd7cc44dSYu Zhao return bus; 38dd7cc44dSYu Zhao 39dd7cc44dSYu Zhao child = pci_find_bus(pci_domain_nr(bus), busnr); 40dd7cc44dSYu Zhao if (child) 41dd7cc44dSYu Zhao return child; 42dd7cc44dSYu Zhao 43dd7cc44dSYu Zhao child = pci_add_new_bus(bus, NULL, busnr); 44dd7cc44dSYu Zhao if (!child) 45dd7cc44dSYu Zhao return NULL; 46dd7cc44dSYu Zhao 47dd7cc44dSYu Zhao child->subordinate = busnr; 48dd7cc44dSYu Zhao child->dev.parent = bus->bridge; 49dd7cc44dSYu Zhao rc = pci_bus_add_child(child); 50dd7cc44dSYu Zhao if (rc) { 51dd7cc44dSYu Zhao pci_remove_bus(child); 52dd7cc44dSYu Zhao return NULL; 53dd7cc44dSYu Zhao } 54dd7cc44dSYu Zhao 55dd7cc44dSYu Zhao return child; 56dd7cc44dSYu Zhao } 57dd7cc44dSYu Zhao 58dd7cc44dSYu Zhao static void virtfn_remove_bus(struct pci_bus *bus, int busnr) 59dd7cc44dSYu Zhao { 60dd7cc44dSYu Zhao struct pci_bus *child; 61dd7cc44dSYu Zhao 62dd7cc44dSYu Zhao if (bus->number == busnr) 63dd7cc44dSYu Zhao return; 64dd7cc44dSYu Zhao 65dd7cc44dSYu Zhao child = pci_find_bus(pci_domain_nr(bus), busnr); 66dd7cc44dSYu Zhao BUG_ON(!child); 67dd7cc44dSYu Zhao 68dd7cc44dSYu Zhao if (list_empty(&child->devices)) 69dd7cc44dSYu Zhao pci_remove_bus(child); 70dd7cc44dSYu Zhao } 71dd7cc44dSYu Zhao 72dd7cc44dSYu Zhao static int virtfn_add(struct pci_dev *dev, int id, int reset) 73dd7cc44dSYu Zhao { 74dd7cc44dSYu Zhao int i; 75dd7cc44dSYu Zhao int rc; 76dd7cc44dSYu Zhao u64 size; 77dd7cc44dSYu Zhao char buf[VIRTFN_ID_LEN]; 78dd7cc44dSYu Zhao struct pci_dev *virtfn; 79dd7cc44dSYu Zhao struct resource *res; 80dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 81dd7cc44dSYu Zhao 82dd7cc44dSYu Zhao virtfn = alloc_pci_dev(); 83dd7cc44dSYu Zhao if (!virtfn) 84dd7cc44dSYu Zhao return -ENOMEM; 85dd7cc44dSYu Zhao 86dd7cc44dSYu Zhao mutex_lock(&iov->dev->sriov->lock); 87dd7cc44dSYu Zhao virtfn->bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id)); 88dd7cc44dSYu Zhao if (!virtfn->bus) { 89dd7cc44dSYu Zhao kfree(virtfn); 90dd7cc44dSYu Zhao mutex_unlock(&iov->dev->sriov->lock); 91dd7cc44dSYu Zhao return -ENOMEM; 92dd7cc44dSYu Zhao } 93dd7cc44dSYu Zhao virtfn->devfn = virtfn_devfn(dev, id); 94dd7cc44dSYu Zhao virtfn->vendor = dev->vendor; 95dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device); 96dd7cc44dSYu Zhao pci_setup_device(virtfn); 97dd7cc44dSYu Zhao virtfn->dev.parent = dev->dev.parent; 98dd7cc44dSYu Zhao 99dd7cc44dSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 100dd7cc44dSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 101dd7cc44dSYu Zhao if (!res->parent) 102dd7cc44dSYu Zhao continue; 103dd7cc44dSYu Zhao virtfn->resource[i].name = pci_name(virtfn); 104dd7cc44dSYu Zhao virtfn->resource[i].flags = res->flags; 105dd7cc44dSYu Zhao size = resource_size(res); 106dd7cc44dSYu Zhao do_div(size, iov->total); 107dd7cc44dSYu Zhao virtfn->resource[i].start = res->start + size * id; 108dd7cc44dSYu Zhao virtfn->resource[i].end = virtfn->resource[i].start + size - 1; 109dd7cc44dSYu Zhao rc = request_resource(res, &virtfn->resource[i]); 110dd7cc44dSYu Zhao BUG_ON(rc); 111dd7cc44dSYu Zhao } 112dd7cc44dSYu Zhao 113dd7cc44dSYu Zhao if (reset) 1148c1c699fSYu Zhao __pci_reset_function(virtfn); 115dd7cc44dSYu Zhao 116dd7cc44dSYu Zhao pci_device_add(virtfn, virtfn->bus); 117dd7cc44dSYu Zhao mutex_unlock(&iov->dev->sriov->lock); 118dd7cc44dSYu Zhao 119dd7cc44dSYu Zhao virtfn->physfn = pci_dev_get(dev); 120dd7cc44dSYu Zhao virtfn->is_virtfn = 1; 121dd7cc44dSYu Zhao 122dd7cc44dSYu Zhao rc = pci_bus_add_device(virtfn); 123dd7cc44dSYu Zhao if (rc) 124dd7cc44dSYu Zhao goto failed1; 125dd7cc44dSYu Zhao sprintf(buf, "virtfn%u", id); 126dd7cc44dSYu Zhao rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf); 127dd7cc44dSYu Zhao if (rc) 128dd7cc44dSYu Zhao goto failed1; 129dd7cc44dSYu Zhao rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn"); 130dd7cc44dSYu Zhao if (rc) 131dd7cc44dSYu Zhao goto failed2; 132dd7cc44dSYu Zhao 133dd7cc44dSYu Zhao kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE); 134dd7cc44dSYu Zhao 135dd7cc44dSYu Zhao return 0; 136dd7cc44dSYu Zhao 137dd7cc44dSYu Zhao failed2: 138dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, buf); 139dd7cc44dSYu Zhao failed1: 140dd7cc44dSYu Zhao pci_dev_put(dev); 141dd7cc44dSYu Zhao mutex_lock(&iov->dev->sriov->lock); 142dd7cc44dSYu Zhao pci_remove_bus_device(virtfn); 143dd7cc44dSYu Zhao virtfn_remove_bus(dev->bus, virtfn_bus(dev, id)); 144dd7cc44dSYu Zhao mutex_unlock(&iov->dev->sriov->lock); 145dd7cc44dSYu Zhao 146dd7cc44dSYu Zhao return rc; 147dd7cc44dSYu Zhao } 148dd7cc44dSYu Zhao 149dd7cc44dSYu Zhao static void virtfn_remove(struct pci_dev *dev, int id, int reset) 150dd7cc44dSYu Zhao { 151dd7cc44dSYu Zhao char buf[VIRTFN_ID_LEN]; 152dd7cc44dSYu Zhao struct pci_bus *bus; 153dd7cc44dSYu Zhao struct pci_dev *virtfn; 154dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 155dd7cc44dSYu Zhao 156dd7cc44dSYu Zhao bus = pci_find_bus(pci_domain_nr(dev->bus), virtfn_bus(dev, id)); 157dd7cc44dSYu Zhao if (!bus) 158dd7cc44dSYu Zhao return; 159dd7cc44dSYu Zhao 160dd7cc44dSYu Zhao virtfn = pci_get_slot(bus, virtfn_devfn(dev, id)); 161dd7cc44dSYu Zhao if (!virtfn) 162dd7cc44dSYu Zhao return; 163dd7cc44dSYu Zhao 164dd7cc44dSYu Zhao pci_dev_put(virtfn); 165dd7cc44dSYu Zhao 166dd7cc44dSYu Zhao if (reset) { 167dd7cc44dSYu Zhao device_release_driver(&virtfn->dev); 1688c1c699fSYu Zhao __pci_reset_function(virtfn); 169dd7cc44dSYu Zhao } 170dd7cc44dSYu Zhao 171dd7cc44dSYu Zhao sprintf(buf, "virtfn%u", id); 172dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, buf); 173dd7cc44dSYu Zhao sysfs_remove_link(&virtfn->dev.kobj, "physfn"); 174dd7cc44dSYu Zhao 175dd7cc44dSYu Zhao mutex_lock(&iov->dev->sriov->lock); 176dd7cc44dSYu Zhao pci_remove_bus_device(virtfn); 177dd7cc44dSYu Zhao virtfn_remove_bus(dev->bus, virtfn_bus(dev, id)); 178dd7cc44dSYu Zhao mutex_unlock(&iov->dev->sriov->lock); 179dd7cc44dSYu Zhao 180dd7cc44dSYu Zhao pci_dev_put(dev); 181dd7cc44dSYu Zhao } 182dd7cc44dSYu Zhao 18374bb1bccSYu Zhao static int sriov_migration(struct pci_dev *dev) 18474bb1bccSYu Zhao { 18574bb1bccSYu Zhao u16 status; 18674bb1bccSYu Zhao struct pci_sriov *iov = dev->sriov; 18774bb1bccSYu Zhao 18874bb1bccSYu Zhao if (!iov->nr_virtfn) 18974bb1bccSYu Zhao return 0; 19074bb1bccSYu Zhao 19174bb1bccSYu Zhao if (!(iov->cap & PCI_SRIOV_CAP_VFM)) 19274bb1bccSYu Zhao return 0; 19374bb1bccSYu Zhao 19474bb1bccSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_STATUS, &status); 19574bb1bccSYu Zhao if (!(status & PCI_SRIOV_STATUS_VFM)) 19674bb1bccSYu Zhao return 0; 19774bb1bccSYu Zhao 19874bb1bccSYu Zhao schedule_work(&iov->mtask); 19974bb1bccSYu Zhao 20074bb1bccSYu Zhao return 1; 20174bb1bccSYu Zhao } 20274bb1bccSYu Zhao 20374bb1bccSYu Zhao static void sriov_migration_task(struct work_struct *work) 20474bb1bccSYu Zhao { 20574bb1bccSYu Zhao int i; 20674bb1bccSYu Zhao u8 state; 20774bb1bccSYu Zhao u16 status; 20874bb1bccSYu Zhao struct pci_sriov *iov = container_of(work, struct pci_sriov, mtask); 20974bb1bccSYu Zhao 21074bb1bccSYu Zhao for (i = iov->initial; i < iov->nr_virtfn; i++) { 21174bb1bccSYu Zhao state = readb(iov->mstate + i); 21274bb1bccSYu Zhao if (state == PCI_SRIOV_VFM_MI) { 21374bb1bccSYu Zhao writeb(PCI_SRIOV_VFM_AV, iov->mstate + i); 21474bb1bccSYu Zhao state = readb(iov->mstate + i); 21574bb1bccSYu Zhao if (state == PCI_SRIOV_VFM_AV) 21674bb1bccSYu Zhao virtfn_add(iov->self, i, 1); 21774bb1bccSYu Zhao } else if (state == PCI_SRIOV_VFM_MO) { 21874bb1bccSYu Zhao virtfn_remove(iov->self, i, 1); 21974bb1bccSYu Zhao writeb(PCI_SRIOV_VFM_UA, iov->mstate + i); 22074bb1bccSYu Zhao state = readb(iov->mstate + i); 22174bb1bccSYu Zhao if (state == PCI_SRIOV_VFM_AV) 22274bb1bccSYu Zhao virtfn_add(iov->self, i, 0); 22374bb1bccSYu Zhao } 22474bb1bccSYu Zhao } 22574bb1bccSYu Zhao 22674bb1bccSYu Zhao pci_read_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, &status); 22774bb1bccSYu Zhao status &= ~PCI_SRIOV_STATUS_VFM; 22874bb1bccSYu Zhao pci_write_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, status); 22974bb1bccSYu Zhao } 23074bb1bccSYu Zhao 23174bb1bccSYu Zhao static int sriov_enable_migration(struct pci_dev *dev, int nr_virtfn) 23274bb1bccSYu Zhao { 23374bb1bccSYu Zhao int bir; 23474bb1bccSYu Zhao u32 table; 23574bb1bccSYu Zhao resource_size_t pa; 23674bb1bccSYu Zhao struct pci_sriov *iov = dev->sriov; 23774bb1bccSYu Zhao 23874bb1bccSYu Zhao if (nr_virtfn <= iov->initial) 23974bb1bccSYu Zhao return 0; 24074bb1bccSYu Zhao 24174bb1bccSYu Zhao pci_read_config_dword(dev, iov->pos + PCI_SRIOV_VFM, &table); 24274bb1bccSYu Zhao bir = PCI_SRIOV_VFM_BIR(table); 24374bb1bccSYu Zhao if (bir > PCI_STD_RESOURCE_END) 24474bb1bccSYu Zhao return -EIO; 24574bb1bccSYu Zhao 24674bb1bccSYu Zhao table = PCI_SRIOV_VFM_OFFSET(table); 24774bb1bccSYu Zhao if (table + nr_virtfn > pci_resource_len(dev, bir)) 24874bb1bccSYu Zhao return -EIO; 24974bb1bccSYu Zhao 25074bb1bccSYu Zhao pa = pci_resource_start(dev, bir) + table; 25174bb1bccSYu Zhao iov->mstate = ioremap(pa, nr_virtfn); 25274bb1bccSYu Zhao if (!iov->mstate) 25374bb1bccSYu Zhao return -ENOMEM; 25474bb1bccSYu Zhao 25574bb1bccSYu Zhao INIT_WORK(&iov->mtask, sriov_migration_task); 25674bb1bccSYu Zhao 25774bb1bccSYu Zhao iov->ctrl |= PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR; 25874bb1bccSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 25974bb1bccSYu Zhao 26074bb1bccSYu Zhao return 0; 26174bb1bccSYu Zhao } 26274bb1bccSYu Zhao 26374bb1bccSYu Zhao static void sriov_disable_migration(struct pci_dev *dev) 26474bb1bccSYu Zhao { 26574bb1bccSYu Zhao struct pci_sriov *iov = dev->sriov; 26674bb1bccSYu Zhao 26774bb1bccSYu Zhao iov->ctrl &= ~(PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR); 26874bb1bccSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 26974bb1bccSYu Zhao 27074bb1bccSYu Zhao cancel_work_sync(&iov->mtask); 27174bb1bccSYu Zhao iounmap(iov->mstate); 27274bb1bccSYu Zhao } 27374bb1bccSYu Zhao 274dd7cc44dSYu Zhao static int sriov_enable(struct pci_dev *dev, int nr_virtfn) 275dd7cc44dSYu Zhao { 276dd7cc44dSYu Zhao int rc; 277dd7cc44dSYu Zhao int i, j; 278dd7cc44dSYu Zhao int nres; 279dd7cc44dSYu Zhao u16 offset, stride, initial; 280dd7cc44dSYu Zhao struct resource *res; 281dd7cc44dSYu Zhao struct pci_dev *pdev; 282dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 283dd7cc44dSYu Zhao 284dd7cc44dSYu Zhao if (!nr_virtfn) 285dd7cc44dSYu Zhao return 0; 286dd7cc44dSYu Zhao 287dd7cc44dSYu Zhao if (iov->nr_virtfn) 288dd7cc44dSYu Zhao return -EINVAL; 289dd7cc44dSYu Zhao 290dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial); 291dd7cc44dSYu Zhao if (initial > iov->total || 292dd7cc44dSYu Zhao (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total))) 293dd7cc44dSYu Zhao return -EIO; 294dd7cc44dSYu Zhao 295dd7cc44dSYu Zhao if (nr_virtfn < 0 || nr_virtfn > iov->total || 296dd7cc44dSYu Zhao (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial))) 297dd7cc44dSYu Zhao return -EINVAL; 298dd7cc44dSYu Zhao 299dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn); 300dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset); 301dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride); 302dd7cc44dSYu Zhao if (!offset || (nr_virtfn > 1 && !stride)) 303dd7cc44dSYu Zhao return -EIO; 304dd7cc44dSYu Zhao 305dd7cc44dSYu Zhao nres = 0; 306dd7cc44dSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 307dd7cc44dSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 308dd7cc44dSYu Zhao if (res->parent) 309dd7cc44dSYu Zhao nres++; 310dd7cc44dSYu Zhao } 311dd7cc44dSYu Zhao if (nres != iov->nres) { 312dd7cc44dSYu Zhao dev_err(&dev->dev, "not enough MMIO resources for SR-IOV\n"); 313dd7cc44dSYu Zhao return -ENOMEM; 314dd7cc44dSYu Zhao } 315dd7cc44dSYu Zhao 316dd7cc44dSYu Zhao iov->offset = offset; 317dd7cc44dSYu Zhao iov->stride = stride; 318dd7cc44dSYu Zhao 319dd7cc44dSYu Zhao if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->subordinate) { 320dd7cc44dSYu Zhao dev_err(&dev->dev, "SR-IOV: bus number out of range\n"); 321dd7cc44dSYu Zhao return -ENOMEM; 322dd7cc44dSYu Zhao } 323dd7cc44dSYu Zhao 324dd7cc44dSYu Zhao if (iov->link != dev->devfn) { 325dd7cc44dSYu Zhao pdev = pci_get_slot(dev->bus, iov->link); 326dd7cc44dSYu Zhao if (!pdev) 327dd7cc44dSYu Zhao return -ENODEV; 328dd7cc44dSYu Zhao 329dd7cc44dSYu Zhao pci_dev_put(pdev); 330dd7cc44dSYu Zhao 331dd7cc44dSYu Zhao if (!pdev->is_physfn) 332dd7cc44dSYu Zhao return -ENODEV; 333dd7cc44dSYu Zhao 334dd7cc44dSYu Zhao rc = sysfs_create_link(&dev->dev.kobj, 335dd7cc44dSYu Zhao &pdev->dev.kobj, "dep_link"); 336dd7cc44dSYu Zhao if (rc) 337dd7cc44dSYu Zhao return rc; 338dd7cc44dSYu Zhao } 339dd7cc44dSYu Zhao 340dd7cc44dSYu Zhao iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE; 341dd7cc44dSYu Zhao pci_block_user_cfg_access(dev); 342dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 343dd7cc44dSYu Zhao msleep(100); 344dd7cc44dSYu Zhao pci_unblock_user_cfg_access(dev); 345dd7cc44dSYu Zhao 346dd7cc44dSYu Zhao iov->initial = initial; 347dd7cc44dSYu Zhao if (nr_virtfn < initial) 348dd7cc44dSYu Zhao initial = nr_virtfn; 349dd7cc44dSYu Zhao 350dd7cc44dSYu Zhao for (i = 0; i < initial; i++) { 351dd7cc44dSYu Zhao rc = virtfn_add(dev, i, 0); 352dd7cc44dSYu Zhao if (rc) 353dd7cc44dSYu Zhao goto failed; 354dd7cc44dSYu Zhao } 355dd7cc44dSYu Zhao 35674bb1bccSYu Zhao if (iov->cap & PCI_SRIOV_CAP_VFM) { 35774bb1bccSYu Zhao rc = sriov_enable_migration(dev, nr_virtfn); 35874bb1bccSYu Zhao if (rc) 35974bb1bccSYu Zhao goto failed; 36074bb1bccSYu Zhao } 36174bb1bccSYu Zhao 362dd7cc44dSYu Zhao kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); 363dd7cc44dSYu Zhao iov->nr_virtfn = nr_virtfn; 364dd7cc44dSYu Zhao 365dd7cc44dSYu Zhao return 0; 366dd7cc44dSYu Zhao 367dd7cc44dSYu Zhao failed: 368dd7cc44dSYu Zhao for (j = 0; j < i; j++) 369dd7cc44dSYu Zhao virtfn_remove(dev, j, 0); 370dd7cc44dSYu Zhao 371dd7cc44dSYu Zhao iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); 372dd7cc44dSYu Zhao pci_block_user_cfg_access(dev); 373dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 374dd7cc44dSYu Zhao ssleep(1); 375dd7cc44dSYu Zhao pci_unblock_user_cfg_access(dev); 376dd7cc44dSYu Zhao 377dd7cc44dSYu Zhao if (iov->link != dev->devfn) 378dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, "dep_link"); 379dd7cc44dSYu Zhao 380dd7cc44dSYu Zhao return rc; 381dd7cc44dSYu Zhao } 382dd7cc44dSYu Zhao 383dd7cc44dSYu Zhao static void sriov_disable(struct pci_dev *dev) 384dd7cc44dSYu Zhao { 385dd7cc44dSYu Zhao int i; 386dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 387dd7cc44dSYu Zhao 388dd7cc44dSYu Zhao if (!iov->nr_virtfn) 389dd7cc44dSYu Zhao return; 390dd7cc44dSYu Zhao 39174bb1bccSYu Zhao if (iov->cap & PCI_SRIOV_CAP_VFM) 39274bb1bccSYu Zhao sriov_disable_migration(dev); 39374bb1bccSYu Zhao 394dd7cc44dSYu Zhao for (i = 0; i < iov->nr_virtfn; i++) 395dd7cc44dSYu Zhao virtfn_remove(dev, i, 0); 396dd7cc44dSYu Zhao 397dd7cc44dSYu Zhao iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); 398dd7cc44dSYu Zhao pci_block_user_cfg_access(dev); 399dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 400dd7cc44dSYu Zhao ssleep(1); 401dd7cc44dSYu Zhao pci_unblock_user_cfg_access(dev); 402dd7cc44dSYu Zhao 403dd7cc44dSYu Zhao if (iov->link != dev->devfn) 404dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, "dep_link"); 405dd7cc44dSYu Zhao 406dd7cc44dSYu Zhao iov->nr_virtfn = 0; 407dd7cc44dSYu Zhao } 408dd7cc44dSYu Zhao 409d1b054daSYu Zhao static int sriov_init(struct pci_dev *dev, int pos) 410d1b054daSYu Zhao { 411d1b054daSYu Zhao int i; 412d1b054daSYu Zhao int rc; 413d1b054daSYu Zhao int nres; 414d1b054daSYu Zhao u32 pgsz; 415d1b054daSYu Zhao u16 ctrl, total, offset, stride; 416d1b054daSYu Zhao struct pci_sriov *iov; 417d1b054daSYu Zhao struct resource *res; 418d1b054daSYu Zhao struct pci_dev *pdev; 419d1b054daSYu Zhao 420d1b054daSYu Zhao if (dev->pcie_type != PCI_EXP_TYPE_RC_END && 421d1b054daSYu Zhao dev->pcie_type != PCI_EXP_TYPE_ENDPOINT) 422d1b054daSYu Zhao return -ENODEV; 423d1b054daSYu Zhao 424d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl); 425d1b054daSYu Zhao if (ctrl & PCI_SRIOV_CTRL_VFE) { 426d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0); 427d1b054daSYu Zhao ssleep(1); 428d1b054daSYu Zhao } 429d1b054daSYu Zhao 430d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total); 431d1b054daSYu Zhao if (!total) 432d1b054daSYu Zhao return 0; 433d1b054daSYu Zhao 434d1b054daSYu Zhao ctrl = 0; 435d1b054daSYu Zhao list_for_each_entry(pdev, &dev->bus->devices, bus_list) 436d1b054daSYu Zhao if (pdev->is_physfn) 437d1b054daSYu Zhao goto found; 438d1b054daSYu Zhao 439d1b054daSYu Zhao pdev = NULL; 440d1b054daSYu Zhao if (pci_ari_enabled(dev->bus)) 441d1b054daSYu Zhao ctrl |= PCI_SRIOV_CTRL_ARI; 442d1b054daSYu Zhao 443d1b054daSYu Zhao found: 444d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl); 445d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, total); 446d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset); 447d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride); 448d1b054daSYu Zhao if (!offset || (total > 1 && !stride)) 449d1b054daSYu Zhao return -EIO; 450d1b054daSYu Zhao 451d1b054daSYu Zhao pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz); 452d1b054daSYu Zhao i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0; 453d1b054daSYu Zhao pgsz &= ~((1 << i) - 1); 454d1b054daSYu Zhao if (!pgsz) 455d1b054daSYu Zhao return -EIO; 456d1b054daSYu Zhao 457d1b054daSYu Zhao pgsz &= ~(pgsz - 1); 458d1b054daSYu Zhao pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz); 459d1b054daSYu Zhao 460d1b054daSYu Zhao nres = 0; 461d1b054daSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 462d1b054daSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 463d1b054daSYu Zhao i += __pci_read_base(dev, pci_bar_unknown, res, 464d1b054daSYu Zhao pos + PCI_SRIOV_BAR + i * 4); 465d1b054daSYu Zhao if (!res->flags) 466d1b054daSYu Zhao continue; 467d1b054daSYu Zhao if (resource_size(res) & (PAGE_SIZE - 1)) { 468d1b054daSYu Zhao rc = -EIO; 469d1b054daSYu Zhao goto failed; 470d1b054daSYu Zhao } 471d1b054daSYu Zhao res->end = res->start + resource_size(res) * total - 1; 472d1b054daSYu Zhao nres++; 473d1b054daSYu Zhao } 474d1b054daSYu Zhao 475d1b054daSYu Zhao iov = kzalloc(sizeof(*iov), GFP_KERNEL); 476d1b054daSYu Zhao if (!iov) { 477d1b054daSYu Zhao rc = -ENOMEM; 478d1b054daSYu Zhao goto failed; 479d1b054daSYu Zhao } 480d1b054daSYu Zhao 481d1b054daSYu Zhao iov->pos = pos; 482d1b054daSYu Zhao iov->nres = nres; 483d1b054daSYu Zhao iov->ctrl = ctrl; 484d1b054daSYu Zhao iov->total = total; 485d1b054daSYu Zhao iov->offset = offset; 486d1b054daSYu Zhao iov->stride = stride; 487d1b054daSYu Zhao iov->pgsz = pgsz; 488d1b054daSYu Zhao iov->self = dev; 489d1b054daSYu Zhao pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap); 490d1b054daSYu Zhao pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link); 4914d135dbeSYu Zhao if (dev->pcie_type == PCI_EXP_TYPE_RC_END) 4924d135dbeSYu Zhao iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link); 493d1b054daSYu Zhao 494d1b054daSYu Zhao if (pdev) 495d1b054daSYu Zhao iov->dev = pci_dev_get(pdev); 496e277d2fcSYu Zhao else 497d1b054daSYu Zhao iov->dev = dev; 498e277d2fcSYu Zhao 499d1b054daSYu Zhao mutex_init(&iov->lock); 500d1b054daSYu Zhao 501d1b054daSYu Zhao dev->sriov = iov; 502d1b054daSYu Zhao dev->is_physfn = 1; 503d1b054daSYu Zhao 504d1b054daSYu Zhao return 0; 505d1b054daSYu Zhao 506d1b054daSYu Zhao failed: 507d1b054daSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 508d1b054daSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 509d1b054daSYu Zhao res->flags = 0; 510d1b054daSYu Zhao } 511d1b054daSYu Zhao 512d1b054daSYu Zhao return rc; 513d1b054daSYu Zhao } 514d1b054daSYu Zhao 515d1b054daSYu Zhao static void sriov_release(struct pci_dev *dev) 516d1b054daSYu Zhao { 517dd7cc44dSYu Zhao BUG_ON(dev->sriov->nr_virtfn); 518dd7cc44dSYu Zhao 519e277d2fcSYu Zhao if (dev != dev->sriov->dev) 520d1b054daSYu Zhao pci_dev_put(dev->sriov->dev); 521d1b054daSYu Zhao 522e277d2fcSYu Zhao mutex_destroy(&dev->sriov->lock); 523e277d2fcSYu Zhao 524d1b054daSYu Zhao kfree(dev->sriov); 525d1b054daSYu Zhao dev->sriov = NULL; 526d1b054daSYu Zhao } 527d1b054daSYu Zhao 5288c5cdb6aSYu Zhao static void sriov_restore_state(struct pci_dev *dev) 5298c5cdb6aSYu Zhao { 5308c5cdb6aSYu Zhao int i; 5318c5cdb6aSYu Zhao u16 ctrl; 5328c5cdb6aSYu Zhao struct pci_sriov *iov = dev->sriov; 5338c5cdb6aSYu Zhao 5348c5cdb6aSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl); 5358c5cdb6aSYu Zhao if (ctrl & PCI_SRIOV_CTRL_VFE) 5368c5cdb6aSYu Zhao return; 5378c5cdb6aSYu Zhao 5388c5cdb6aSYu Zhao for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) 5398c5cdb6aSYu Zhao pci_update_resource(dev, i); 5408c5cdb6aSYu Zhao 5418c5cdb6aSYu Zhao pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz); 542dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->nr_virtfn); 5438c5cdb6aSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 5448c5cdb6aSYu Zhao if (iov->ctrl & PCI_SRIOV_CTRL_VFE) 5458c5cdb6aSYu Zhao msleep(100); 5468c5cdb6aSYu Zhao } 5478c5cdb6aSYu Zhao 548d1b054daSYu Zhao /** 549d1b054daSYu Zhao * pci_iov_init - initialize the IOV capability 550d1b054daSYu Zhao * @dev: the PCI device 551d1b054daSYu Zhao * 552d1b054daSYu Zhao * Returns 0 on success, or negative on failure. 553d1b054daSYu Zhao */ 554d1b054daSYu Zhao int pci_iov_init(struct pci_dev *dev) 555d1b054daSYu Zhao { 556d1b054daSYu Zhao int pos; 557d1b054daSYu Zhao 558d1b054daSYu Zhao if (!dev->is_pcie) 559d1b054daSYu Zhao return -ENODEV; 560d1b054daSYu Zhao 561d1b054daSYu Zhao pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); 562d1b054daSYu Zhao if (pos) 563d1b054daSYu Zhao return sriov_init(dev, pos); 564d1b054daSYu Zhao 565d1b054daSYu Zhao return -ENODEV; 566d1b054daSYu Zhao } 567d1b054daSYu Zhao 568d1b054daSYu Zhao /** 569d1b054daSYu Zhao * pci_iov_release - release resources used by the IOV capability 570d1b054daSYu Zhao * @dev: the PCI device 571d1b054daSYu Zhao */ 572d1b054daSYu Zhao void pci_iov_release(struct pci_dev *dev) 573d1b054daSYu Zhao { 574d1b054daSYu Zhao if (dev->is_physfn) 575d1b054daSYu Zhao sriov_release(dev); 576d1b054daSYu Zhao } 577d1b054daSYu Zhao 578d1b054daSYu Zhao /** 579d1b054daSYu Zhao * pci_iov_resource_bar - get position of the SR-IOV BAR 580d1b054daSYu Zhao * @dev: the PCI device 581d1b054daSYu Zhao * @resno: the resource number 582d1b054daSYu Zhao * @type: the BAR type to be filled in 583d1b054daSYu Zhao * 584d1b054daSYu Zhao * Returns position of the BAR encapsulated in the SR-IOV capability. 585d1b054daSYu Zhao */ 586d1b054daSYu Zhao int pci_iov_resource_bar(struct pci_dev *dev, int resno, 587d1b054daSYu Zhao enum pci_bar_type *type) 588d1b054daSYu Zhao { 589d1b054daSYu Zhao if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END) 590d1b054daSYu Zhao return 0; 591d1b054daSYu Zhao 592d1b054daSYu Zhao BUG_ON(!dev->is_physfn); 593d1b054daSYu Zhao 594d1b054daSYu Zhao *type = pci_bar_unknown; 595d1b054daSYu Zhao 596d1b054daSYu Zhao return dev->sriov->pos + PCI_SRIOV_BAR + 597d1b054daSYu Zhao 4 * (resno - PCI_IOV_RESOURCES); 598d1b054daSYu Zhao } 5998c5cdb6aSYu Zhao 6008c5cdb6aSYu Zhao /** 601*6faf17f6SChris Wright * pci_sriov_resource_alignment - get resource alignment for VF BAR 602*6faf17f6SChris Wright * @dev: the PCI device 603*6faf17f6SChris Wright * @resno: the resource number 604*6faf17f6SChris Wright * 605*6faf17f6SChris Wright * Returns the alignment of the VF BAR found in the SR-IOV capability. 606*6faf17f6SChris Wright * This is not the same as the resource size which is defined as 607*6faf17f6SChris Wright * the VF BAR size multiplied by the number of VFs. The alignment 608*6faf17f6SChris Wright * is just the VF BAR size. 609*6faf17f6SChris Wright */ 610*6faf17f6SChris Wright int pci_sriov_resource_alignment(struct pci_dev *dev, int resno) 611*6faf17f6SChris Wright { 612*6faf17f6SChris Wright struct resource tmp; 613*6faf17f6SChris Wright enum pci_bar_type type; 614*6faf17f6SChris Wright int reg = pci_iov_resource_bar(dev, resno, &type); 615*6faf17f6SChris Wright 616*6faf17f6SChris Wright if (!reg) 617*6faf17f6SChris Wright return 0; 618*6faf17f6SChris Wright 619*6faf17f6SChris Wright __pci_read_base(dev, type, &tmp, reg); 620*6faf17f6SChris Wright return resource_alignment(&tmp); 621*6faf17f6SChris Wright } 622*6faf17f6SChris Wright 623*6faf17f6SChris Wright /** 6248c5cdb6aSYu Zhao * pci_restore_iov_state - restore the state of the IOV capability 6258c5cdb6aSYu Zhao * @dev: the PCI device 6268c5cdb6aSYu Zhao */ 6278c5cdb6aSYu Zhao void pci_restore_iov_state(struct pci_dev *dev) 6288c5cdb6aSYu Zhao { 6298c5cdb6aSYu Zhao if (dev->is_physfn) 6308c5cdb6aSYu Zhao sriov_restore_state(dev); 6318c5cdb6aSYu Zhao } 632a28724b0SYu Zhao 633a28724b0SYu Zhao /** 634a28724b0SYu Zhao * pci_iov_bus_range - find bus range used by Virtual Function 635a28724b0SYu Zhao * @bus: the PCI bus 636a28724b0SYu Zhao * 637a28724b0SYu Zhao * Returns max number of buses (exclude current one) used by Virtual 638a28724b0SYu Zhao * Functions. 639a28724b0SYu Zhao */ 640a28724b0SYu Zhao int pci_iov_bus_range(struct pci_bus *bus) 641a28724b0SYu Zhao { 642a28724b0SYu Zhao int max = 0; 643a28724b0SYu Zhao u8 busnr; 644a28724b0SYu Zhao struct pci_dev *dev; 645a28724b0SYu Zhao 646a28724b0SYu Zhao list_for_each_entry(dev, &bus->devices, bus_list) { 647a28724b0SYu Zhao if (!dev->is_physfn) 648a28724b0SYu Zhao continue; 649a28724b0SYu Zhao busnr = virtfn_bus(dev, dev->sriov->total - 1); 650a28724b0SYu Zhao if (busnr > max) 651a28724b0SYu Zhao max = busnr; 652a28724b0SYu Zhao } 653a28724b0SYu Zhao 654a28724b0SYu Zhao return max ? max - bus->number : 0; 655a28724b0SYu Zhao } 656dd7cc44dSYu Zhao 657dd7cc44dSYu Zhao /** 658dd7cc44dSYu Zhao * pci_enable_sriov - enable the SR-IOV capability 659dd7cc44dSYu Zhao * @dev: the PCI device 66052a8873bSRandy Dunlap * @nr_virtfn: number of virtual functions to enable 661dd7cc44dSYu Zhao * 662dd7cc44dSYu Zhao * Returns 0 on success, or negative on failure. 663dd7cc44dSYu Zhao */ 664dd7cc44dSYu Zhao int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) 665dd7cc44dSYu Zhao { 666dd7cc44dSYu Zhao might_sleep(); 667dd7cc44dSYu Zhao 668dd7cc44dSYu Zhao if (!dev->is_physfn) 669dd7cc44dSYu Zhao return -ENODEV; 670dd7cc44dSYu Zhao 671dd7cc44dSYu Zhao return sriov_enable(dev, nr_virtfn); 672dd7cc44dSYu Zhao } 673dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_enable_sriov); 674dd7cc44dSYu Zhao 675dd7cc44dSYu Zhao /** 676dd7cc44dSYu Zhao * pci_disable_sriov - disable the SR-IOV capability 677dd7cc44dSYu Zhao * @dev: the PCI device 678dd7cc44dSYu Zhao */ 679dd7cc44dSYu Zhao void pci_disable_sriov(struct pci_dev *dev) 680dd7cc44dSYu Zhao { 681dd7cc44dSYu Zhao might_sleep(); 682dd7cc44dSYu Zhao 683dd7cc44dSYu Zhao if (!dev->is_physfn) 684dd7cc44dSYu Zhao return; 685dd7cc44dSYu Zhao 686dd7cc44dSYu Zhao sriov_disable(dev); 687dd7cc44dSYu Zhao } 688dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_disable_sriov); 68974bb1bccSYu Zhao 69074bb1bccSYu Zhao /** 69174bb1bccSYu Zhao * pci_sriov_migration - notify SR-IOV core of Virtual Function Migration 69274bb1bccSYu Zhao * @dev: the PCI device 69374bb1bccSYu Zhao * 69474bb1bccSYu Zhao * Returns IRQ_HANDLED if the IRQ is handled, or IRQ_NONE if not. 69574bb1bccSYu Zhao * 69674bb1bccSYu Zhao * Physical Function driver is responsible to register IRQ handler using 69774bb1bccSYu Zhao * VF Migration Interrupt Message Number, and call this function when the 69874bb1bccSYu Zhao * interrupt is generated by the hardware. 69974bb1bccSYu Zhao */ 70074bb1bccSYu Zhao irqreturn_t pci_sriov_migration(struct pci_dev *dev) 70174bb1bccSYu Zhao { 70274bb1bccSYu Zhao if (!dev->is_physfn) 70374bb1bccSYu Zhao return IRQ_NONE; 70474bb1bccSYu Zhao 70574bb1bccSYu Zhao return sriov_migration(dev) ? IRQ_HANDLED : IRQ_NONE; 70674bb1bccSYu Zhao } 70774bb1bccSYu Zhao EXPORT_SYMBOL_GPL(pci_sriov_migration); 708302b4215SYu Zhao 709302b4215SYu Zhao static int ats_alloc_one(struct pci_dev *dev, int ps) 710302b4215SYu Zhao { 711302b4215SYu Zhao int pos; 712302b4215SYu Zhao u16 cap; 713302b4215SYu Zhao struct pci_ats *ats; 714302b4215SYu Zhao 715302b4215SYu Zhao pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS); 716302b4215SYu Zhao if (!pos) 717302b4215SYu Zhao return -ENODEV; 718302b4215SYu Zhao 719302b4215SYu Zhao ats = kzalloc(sizeof(*ats), GFP_KERNEL); 720302b4215SYu Zhao if (!ats) 721302b4215SYu Zhao return -ENOMEM; 722302b4215SYu Zhao 723302b4215SYu Zhao ats->pos = pos; 724302b4215SYu Zhao ats->stu = ps; 725302b4215SYu Zhao pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap); 726302b4215SYu Zhao ats->qdep = PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : 727302b4215SYu Zhao PCI_ATS_MAX_QDEP; 728302b4215SYu Zhao dev->ats = ats; 729302b4215SYu Zhao 730302b4215SYu Zhao return 0; 731302b4215SYu Zhao } 732302b4215SYu Zhao 733302b4215SYu Zhao static void ats_free_one(struct pci_dev *dev) 734302b4215SYu Zhao { 735302b4215SYu Zhao kfree(dev->ats); 736302b4215SYu Zhao dev->ats = NULL; 737302b4215SYu Zhao } 738302b4215SYu Zhao 739302b4215SYu Zhao /** 740302b4215SYu Zhao * pci_enable_ats - enable the ATS capability 741302b4215SYu Zhao * @dev: the PCI device 742302b4215SYu Zhao * @ps: the IOMMU page shift 743302b4215SYu Zhao * 744302b4215SYu Zhao * Returns 0 on success, or negative on failure. 745302b4215SYu Zhao */ 746302b4215SYu Zhao int pci_enable_ats(struct pci_dev *dev, int ps) 747302b4215SYu Zhao { 748302b4215SYu Zhao int rc; 749302b4215SYu Zhao u16 ctrl; 750302b4215SYu Zhao 751e277d2fcSYu Zhao BUG_ON(dev->ats && dev->ats->is_enabled); 752302b4215SYu Zhao 753302b4215SYu Zhao if (ps < PCI_ATS_MIN_STU) 754302b4215SYu Zhao return -EINVAL; 755302b4215SYu Zhao 756e277d2fcSYu Zhao if (dev->is_physfn || dev->is_virtfn) { 757e277d2fcSYu Zhao struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn; 758e277d2fcSYu Zhao 759e277d2fcSYu Zhao mutex_lock(&pdev->sriov->lock); 760e277d2fcSYu Zhao if (pdev->ats) 761e277d2fcSYu Zhao rc = pdev->ats->stu == ps ? 0 : -EINVAL; 762e277d2fcSYu Zhao else 763e277d2fcSYu Zhao rc = ats_alloc_one(pdev, ps); 764e277d2fcSYu Zhao 765e277d2fcSYu Zhao if (!rc) 766e277d2fcSYu Zhao pdev->ats->ref_cnt++; 767e277d2fcSYu Zhao mutex_unlock(&pdev->sriov->lock); 768e277d2fcSYu Zhao if (rc) 769e277d2fcSYu Zhao return rc; 770e277d2fcSYu Zhao } 771e277d2fcSYu Zhao 772e277d2fcSYu Zhao if (!dev->is_physfn) { 773302b4215SYu Zhao rc = ats_alloc_one(dev, ps); 774302b4215SYu Zhao if (rc) 775302b4215SYu Zhao return rc; 776e277d2fcSYu Zhao } 777302b4215SYu Zhao 778302b4215SYu Zhao ctrl = PCI_ATS_CTRL_ENABLE; 779e277d2fcSYu Zhao if (!dev->is_virtfn) 780302b4215SYu Zhao ctrl |= PCI_ATS_CTRL_STU(ps - PCI_ATS_MIN_STU); 781302b4215SYu Zhao pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); 782302b4215SYu Zhao 783e277d2fcSYu Zhao dev->ats->is_enabled = 1; 784e277d2fcSYu Zhao 785302b4215SYu Zhao return 0; 786302b4215SYu Zhao } 787302b4215SYu Zhao 788302b4215SYu Zhao /** 789302b4215SYu Zhao * pci_disable_ats - disable the ATS capability 790302b4215SYu Zhao * @dev: the PCI device 791302b4215SYu Zhao */ 792302b4215SYu Zhao void pci_disable_ats(struct pci_dev *dev) 793302b4215SYu Zhao { 794302b4215SYu Zhao u16 ctrl; 795302b4215SYu Zhao 796e277d2fcSYu Zhao BUG_ON(!dev->ats || !dev->ats->is_enabled); 797302b4215SYu Zhao 798302b4215SYu Zhao pci_read_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, &ctrl); 799302b4215SYu Zhao ctrl &= ~PCI_ATS_CTRL_ENABLE; 800302b4215SYu Zhao pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); 801302b4215SYu Zhao 802e277d2fcSYu Zhao dev->ats->is_enabled = 0; 803e277d2fcSYu Zhao 804e277d2fcSYu Zhao if (dev->is_physfn || dev->is_virtfn) { 805e277d2fcSYu Zhao struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn; 806e277d2fcSYu Zhao 807e277d2fcSYu Zhao mutex_lock(&pdev->sriov->lock); 808e277d2fcSYu Zhao pdev->ats->ref_cnt--; 809e277d2fcSYu Zhao if (!pdev->ats->ref_cnt) 810e277d2fcSYu Zhao ats_free_one(pdev); 811e277d2fcSYu Zhao mutex_unlock(&pdev->sriov->lock); 812e277d2fcSYu Zhao } 813e277d2fcSYu Zhao 814e277d2fcSYu Zhao if (!dev->is_physfn) 815302b4215SYu Zhao ats_free_one(dev); 816302b4215SYu Zhao } 817302b4215SYu Zhao 818302b4215SYu Zhao /** 819302b4215SYu Zhao * pci_ats_queue_depth - query the ATS Invalidate Queue Depth 820302b4215SYu Zhao * @dev: the PCI device 821302b4215SYu Zhao * 822302b4215SYu Zhao * Returns the queue depth on success, or negative on failure. 823302b4215SYu Zhao * 824302b4215SYu Zhao * The ATS spec uses 0 in the Invalidate Queue Depth field to 825302b4215SYu Zhao * indicate that the function can accept 32 Invalidate Request. 826302b4215SYu Zhao * But here we use the `real' values (i.e. 1~32) for the Queue 827e277d2fcSYu Zhao * Depth; and 0 indicates the function shares the Queue with 828e277d2fcSYu Zhao * other functions (doesn't exclusively own a Queue). 829302b4215SYu Zhao */ 830302b4215SYu Zhao int pci_ats_queue_depth(struct pci_dev *dev) 831302b4215SYu Zhao { 832302b4215SYu Zhao int pos; 833302b4215SYu Zhao u16 cap; 834302b4215SYu Zhao 835e277d2fcSYu Zhao if (dev->is_virtfn) 836e277d2fcSYu Zhao return 0; 837e277d2fcSYu Zhao 838302b4215SYu Zhao if (dev->ats) 839302b4215SYu Zhao return dev->ats->qdep; 840302b4215SYu Zhao 841302b4215SYu Zhao pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS); 842302b4215SYu Zhao if (!pos) 843302b4215SYu Zhao return -ENODEV; 844302b4215SYu Zhao 845302b4215SYu Zhao pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap); 846302b4215SYu Zhao 847302b4215SYu Zhao return PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : 848302b4215SYu Zhao PCI_ATS_MAX_QDEP; 849302b4215SYu Zhao } 850