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 16d1b054daSYu Zhao 17*a28724b0SYu Zhao static inline u8 virtfn_bus(struct pci_dev *dev, int id) 18*a28724b0SYu Zhao { 19*a28724b0SYu Zhao return dev->bus->number + ((dev->devfn + dev->sriov->offset + 20*a28724b0SYu Zhao dev->sriov->stride * id) >> 8); 21*a28724b0SYu Zhao } 22*a28724b0SYu Zhao 23*a28724b0SYu Zhao static inline u8 virtfn_devfn(struct pci_dev *dev, int id) 24*a28724b0SYu Zhao { 25*a28724b0SYu Zhao return (dev->devfn + dev->sriov->offset + 26*a28724b0SYu Zhao dev->sriov->stride * id) & 0xff; 27*a28724b0SYu Zhao } 28*a28724b0SYu Zhao 29d1b054daSYu Zhao static int sriov_init(struct pci_dev *dev, int pos) 30d1b054daSYu Zhao { 31d1b054daSYu Zhao int i; 32d1b054daSYu Zhao int rc; 33d1b054daSYu Zhao int nres; 34d1b054daSYu Zhao u32 pgsz; 35d1b054daSYu Zhao u16 ctrl, total, offset, stride; 36d1b054daSYu Zhao struct pci_sriov *iov; 37d1b054daSYu Zhao struct resource *res; 38d1b054daSYu Zhao struct pci_dev *pdev; 39d1b054daSYu Zhao 40d1b054daSYu Zhao if (dev->pcie_type != PCI_EXP_TYPE_RC_END && 41d1b054daSYu Zhao dev->pcie_type != PCI_EXP_TYPE_ENDPOINT) 42d1b054daSYu Zhao return -ENODEV; 43d1b054daSYu Zhao 44d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl); 45d1b054daSYu Zhao if (ctrl & PCI_SRIOV_CTRL_VFE) { 46d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0); 47d1b054daSYu Zhao ssleep(1); 48d1b054daSYu Zhao } 49d1b054daSYu Zhao 50d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total); 51d1b054daSYu Zhao if (!total) 52d1b054daSYu Zhao return 0; 53d1b054daSYu Zhao 54d1b054daSYu Zhao ctrl = 0; 55d1b054daSYu Zhao list_for_each_entry(pdev, &dev->bus->devices, bus_list) 56d1b054daSYu Zhao if (pdev->is_physfn) 57d1b054daSYu Zhao goto found; 58d1b054daSYu Zhao 59d1b054daSYu Zhao pdev = NULL; 60d1b054daSYu Zhao if (pci_ari_enabled(dev->bus)) 61d1b054daSYu Zhao ctrl |= PCI_SRIOV_CTRL_ARI; 62d1b054daSYu Zhao 63d1b054daSYu Zhao found: 64d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl); 65d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, total); 66d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset); 67d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride); 68d1b054daSYu Zhao if (!offset || (total > 1 && !stride)) 69d1b054daSYu Zhao return -EIO; 70d1b054daSYu Zhao 71d1b054daSYu Zhao pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz); 72d1b054daSYu Zhao i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0; 73d1b054daSYu Zhao pgsz &= ~((1 << i) - 1); 74d1b054daSYu Zhao if (!pgsz) 75d1b054daSYu Zhao return -EIO; 76d1b054daSYu Zhao 77d1b054daSYu Zhao pgsz &= ~(pgsz - 1); 78d1b054daSYu Zhao pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz); 79d1b054daSYu Zhao 80d1b054daSYu Zhao nres = 0; 81d1b054daSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 82d1b054daSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 83d1b054daSYu Zhao i += __pci_read_base(dev, pci_bar_unknown, res, 84d1b054daSYu Zhao pos + PCI_SRIOV_BAR + i * 4); 85d1b054daSYu Zhao if (!res->flags) 86d1b054daSYu Zhao continue; 87d1b054daSYu Zhao if (resource_size(res) & (PAGE_SIZE - 1)) { 88d1b054daSYu Zhao rc = -EIO; 89d1b054daSYu Zhao goto failed; 90d1b054daSYu Zhao } 91d1b054daSYu Zhao res->end = res->start + resource_size(res) * total - 1; 92d1b054daSYu Zhao nres++; 93d1b054daSYu Zhao } 94d1b054daSYu Zhao 95d1b054daSYu Zhao iov = kzalloc(sizeof(*iov), GFP_KERNEL); 96d1b054daSYu Zhao if (!iov) { 97d1b054daSYu Zhao rc = -ENOMEM; 98d1b054daSYu Zhao goto failed; 99d1b054daSYu Zhao } 100d1b054daSYu Zhao 101d1b054daSYu Zhao iov->pos = pos; 102d1b054daSYu Zhao iov->nres = nres; 103d1b054daSYu Zhao iov->ctrl = ctrl; 104d1b054daSYu Zhao iov->total = total; 105d1b054daSYu Zhao iov->offset = offset; 106d1b054daSYu Zhao iov->stride = stride; 107d1b054daSYu Zhao iov->pgsz = pgsz; 108d1b054daSYu Zhao iov->self = dev; 109d1b054daSYu Zhao pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap); 110d1b054daSYu Zhao pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link); 111d1b054daSYu Zhao 112d1b054daSYu Zhao if (pdev) 113d1b054daSYu Zhao iov->dev = pci_dev_get(pdev); 114d1b054daSYu Zhao else { 115d1b054daSYu Zhao iov->dev = dev; 116d1b054daSYu Zhao mutex_init(&iov->lock); 117d1b054daSYu Zhao } 118d1b054daSYu Zhao 119d1b054daSYu Zhao dev->sriov = iov; 120d1b054daSYu Zhao dev->is_physfn = 1; 121d1b054daSYu Zhao 122d1b054daSYu Zhao return 0; 123d1b054daSYu Zhao 124d1b054daSYu Zhao failed: 125d1b054daSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 126d1b054daSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 127d1b054daSYu Zhao res->flags = 0; 128d1b054daSYu Zhao } 129d1b054daSYu Zhao 130d1b054daSYu Zhao return rc; 131d1b054daSYu Zhao } 132d1b054daSYu Zhao 133d1b054daSYu Zhao static void sriov_release(struct pci_dev *dev) 134d1b054daSYu Zhao { 135d1b054daSYu Zhao if (dev == dev->sriov->dev) 136d1b054daSYu Zhao mutex_destroy(&dev->sriov->lock); 137d1b054daSYu Zhao else 138d1b054daSYu Zhao pci_dev_put(dev->sriov->dev); 139d1b054daSYu Zhao 140d1b054daSYu Zhao kfree(dev->sriov); 141d1b054daSYu Zhao dev->sriov = NULL; 142d1b054daSYu Zhao } 143d1b054daSYu Zhao 1448c5cdb6aSYu Zhao static void sriov_restore_state(struct pci_dev *dev) 1458c5cdb6aSYu Zhao { 1468c5cdb6aSYu Zhao int i; 1478c5cdb6aSYu Zhao u16 ctrl; 1488c5cdb6aSYu Zhao struct pci_sriov *iov = dev->sriov; 1498c5cdb6aSYu Zhao 1508c5cdb6aSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl); 1518c5cdb6aSYu Zhao if (ctrl & PCI_SRIOV_CTRL_VFE) 1528c5cdb6aSYu Zhao return; 1538c5cdb6aSYu Zhao 1548c5cdb6aSYu Zhao for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) 1558c5cdb6aSYu Zhao pci_update_resource(dev, i); 1568c5cdb6aSYu Zhao 1578c5cdb6aSYu Zhao pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz); 1588c5cdb6aSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 1598c5cdb6aSYu Zhao if (iov->ctrl & PCI_SRIOV_CTRL_VFE) 1608c5cdb6aSYu Zhao msleep(100); 1618c5cdb6aSYu Zhao } 1628c5cdb6aSYu Zhao 163d1b054daSYu Zhao /** 164d1b054daSYu Zhao * pci_iov_init - initialize the IOV capability 165d1b054daSYu Zhao * @dev: the PCI device 166d1b054daSYu Zhao * 167d1b054daSYu Zhao * Returns 0 on success, or negative on failure. 168d1b054daSYu Zhao */ 169d1b054daSYu Zhao int pci_iov_init(struct pci_dev *dev) 170d1b054daSYu Zhao { 171d1b054daSYu Zhao int pos; 172d1b054daSYu Zhao 173d1b054daSYu Zhao if (!dev->is_pcie) 174d1b054daSYu Zhao return -ENODEV; 175d1b054daSYu Zhao 176d1b054daSYu Zhao pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); 177d1b054daSYu Zhao if (pos) 178d1b054daSYu Zhao return sriov_init(dev, pos); 179d1b054daSYu Zhao 180d1b054daSYu Zhao return -ENODEV; 181d1b054daSYu Zhao } 182d1b054daSYu Zhao 183d1b054daSYu Zhao /** 184d1b054daSYu Zhao * pci_iov_release - release resources used by the IOV capability 185d1b054daSYu Zhao * @dev: the PCI device 186d1b054daSYu Zhao */ 187d1b054daSYu Zhao void pci_iov_release(struct pci_dev *dev) 188d1b054daSYu Zhao { 189d1b054daSYu Zhao if (dev->is_physfn) 190d1b054daSYu Zhao sriov_release(dev); 191d1b054daSYu Zhao } 192d1b054daSYu Zhao 193d1b054daSYu Zhao /** 194d1b054daSYu Zhao * pci_iov_resource_bar - get position of the SR-IOV BAR 195d1b054daSYu Zhao * @dev: the PCI device 196d1b054daSYu Zhao * @resno: the resource number 197d1b054daSYu Zhao * @type: the BAR type to be filled in 198d1b054daSYu Zhao * 199d1b054daSYu Zhao * Returns position of the BAR encapsulated in the SR-IOV capability. 200d1b054daSYu Zhao */ 201d1b054daSYu Zhao int pci_iov_resource_bar(struct pci_dev *dev, int resno, 202d1b054daSYu Zhao enum pci_bar_type *type) 203d1b054daSYu Zhao { 204d1b054daSYu Zhao if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END) 205d1b054daSYu Zhao return 0; 206d1b054daSYu Zhao 207d1b054daSYu Zhao BUG_ON(!dev->is_physfn); 208d1b054daSYu Zhao 209d1b054daSYu Zhao *type = pci_bar_unknown; 210d1b054daSYu Zhao 211d1b054daSYu Zhao return dev->sriov->pos + PCI_SRIOV_BAR + 212d1b054daSYu Zhao 4 * (resno - PCI_IOV_RESOURCES); 213d1b054daSYu Zhao } 2148c5cdb6aSYu Zhao 2158c5cdb6aSYu Zhao /** 2168c5cdb6aSYu Zhao * pci_restore_iov_state - restore the state of the IOV capability 2178c5cdb6aSYu Zhao * @dev: the PCI device 2188c5cdb6aSYu Zhao */ 2198c5cdb6aSYu Zhao void pci_restore_iov_state(struct pci_dev *dev) 2208c5cdb6aSYu Zhao { 2218c5cdb6aSYu Zhao if (dev->is_physfn) 2228c5cdb6aSYu Zhao sriov_restore_state(dev); 2238c5cdb6aSYu Zhao } 224*a28724b0SYu Zhao 225*a28724b0SYu Zhao /** 226*a28724b0SYu Zhao * pci_iov_bus_range - find bus range used by Virtual Function 227*a28724b0SYu Zhao * @bus: the PCI bus 228*a28724b0SYu Zhao * 229*a28724b0SYu Zhao * Returns max number of buses (exclude current one) used by Virtual 230*a28724b0SYu Zhao * Functions. 231*a28724b0SYu Zhao */ 232*a28724b0SYu Zhao int pci_iov_bus_range(struct pci_bus *bus) 233*a28724b0SYu Zhao { 234*a28724b0SYu Zhao int max = 0; 235*a28724b0SYu Zhao u8 busnr; 236*a28724b0SYu Zhao struct pci_dev *dev; 237*a28724b0SYu Zhao 238*a28724b0SYu Zhao list_for_each_entry(dev, &bus->devices, bus_list) { 239*a28724b0SYu Zhao if (!dev->is_physfn) 240*a28724b0SYu Zhao continue; 241*a28724b0SYu Zhao busnr = virtfn_bus(dev, dev->sriov->total - 1); 242*a28724b0SYu Zhao if (busnr > max) 243*a28724b0SYu Zhao max = busnr; 244*a28724b0SYu Zhao } 245*a28724b0SYu Zhao 246*a28724b0SYu Zhao return max ? max - bus->number : 0; 247*a28724b0SYu Zhao } 248