aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorYu Zhao <yu.zhao@intel.com>2009-03-19 23:25:11 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-03-20 13:48:22 -0400
commitd1b054da8f599905f3c18a218961dcf17f9d5f13 (patch)
tree99b62e6771c3b73142dd0622463bed0e19724342 /drivers/pci
parent8293b0f629095efbe7c7e3f9b437f8c040c19eb5 (diff)
PCI: initialize and release SR-IOV capability
If a device has the SR-IOV capability, initialize it (set the ARI Capable Hierarchy in the lowest numbered PF if necessary; calculate the System Page Size for the VF MMIO, probe the VF Offset, Stride and BARs). A lock for the VF bus allocation is also initialized if a PF is the lowest numbered PF. Reviewed-by: Matthew Wilcox <willy@linux.intel.com> Signed-off-by: Yu Zhao <yu.zhao@intel.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/Kconfig10
-rw-r--r--drivers/pci/Makefile2
-rw-r--r--drivers/pci/iov.c182
-rw-r--r--drivers/pci/pci.c7
-rw-r--r--drivers/pci/pci.h37
-rw-r--r--drivers/pci/probe.c4
6 files changed, 242 insertions, 0 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 2a4501dd2515..fdc864f9cf23 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -59,3 +59,13 @@ config HT_IRQ
59 This allows native hypertransport devices to use interrupts. 59 This allows native hypertransport devices to use interrupts.
60 60
61 If unsure say Y. 61 If unsure say Y.
62
63config PCI_IOV
64 bool "PCI IOV support"
65 depends on PCI
66 help
67 I/O Virtualization is a PCI feature supported by some devices
68 which allows them to create virtual devices which share their
69 physical resources.
70
71 If unsure, say N.
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 3d07ce24f6a8..ba6af162fd39 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -29,6 +29,8 @@ obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o
29 29
30obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o 30obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o
31 31
32obj-$(CONFIG_PCI_IOV) += iov.o
33
32# 34#
33# Some architectures use the generic PCI setup functions 35# Some architectures use the generic PCI setup functions
34# 36#
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
new file mode 100644
index 000000000000..66cc414ed15f
--- /dev/null
+++ b/drivers/pci/iov.c
@@ -0,0 +1,182 @@
1/*
2 * drivers/pci/iov.c
3 *
4 * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
5 *
6 * PCI Express I/O Virtualization (IOV) support.
7 * Single Root IOV 1.0
8 */
9
10#include <linux/pci.h>
11#include <linux/mutex.h>
12#include <linux/string.h>
13#include <linux/delay.h>
14#include "pci.h"
15
16
17static int sriov_init(struct pci_dev *dev, int pos)
18{
19 int i;
20 int rc;
21 int nres;
22 u32 pgsz;
23 u16 ctrl, total, offset, stride;
24 struct pci_sriov *iov;
25 struct resource *res;
26 struct pci_dev *pdev;
27
28 if (dev->pcie_type != PCI_EXP_TYPE_RC_END &&
29 dev->pcie_type != PCI_EXP_TYPE_ENDPOINT)
30 return -ENODEV;
31
32 pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
33 if (ctrl & PCI_SRIOV_CTRL_VFE) {
34 pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);
35 ssleep(1);
36 }
37
38 pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
39 if (!total)
40 return 0;
41
42 ctrl = 0;
43 list_for_each_entry(pdev, &dev->bus->devices, bus_list)
44 if (pdev->is_physfn)
45 goto found;
46
47 pdev = NULL;
48 if (pci_ari_enabled(dev->bus))
49 ctrl |= PCI_SRIOV_CTRL_ARI;
50
51found:
52 pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
53 pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, total);
54 pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
55 pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
56 if (!offset || (total > 1 && !stride))
57 return -EIO;
58
59 pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
60 i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0;
61 pgsz &= ~((1 << i) - 1);
62 if (!pgsz)
63 return -EIO;
64
65 pgsz &= ~(pgsz - 1);
66 pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz);
67
68 nres = 0;
69 for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
70 res = dev->resource + PCI_IOV_RESOURCES + i;
71 i += __pci_read_base(dev, pci_bar_unknown, res,
72 pos + PCI_SRIOV_BAR + i * 4);
73 if (!res->flags)
74 continue;
75 if (resource_size(res) & (PAGE_SIZE - 1)) {
76 rc = -EIO;
77 goto failed;
78 }
79 res->end = res->start + resource_size(res) * total - 1;
80 nres++;
81 }
82
83 iov = kzalloc(sizeof(*iov), GFP_KERNEL);
84 if (!iov) {
85 rc = -ENOMEM;
86 goto failed;
87 }
88
89 iov->pos = pos;
90 iov->nres = nres;
91 iov->ctrl = ctrl;
92 iov->total = total;
93 iov->offset = offset;
94 iov->stride = stride;
95 iov->pgsz = pgsz;
96 iov->self = dev;
97 pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
98 pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
99
100 if (pdev)
101 iov->dev = pci_dev_get(pdev);
102 else {
103 iov->dev = dev;
104 mutex_init(&iov->lock);
105 }
106
107 dev->sriov = iov;
108 dev->is_physfn = 1;
109
110 return 0;
111
112failed:
113 for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
114 res = dev->resource + PCI_IOV_RESOURCES + i;
115 res->flags = 0;
116 }
117
118 return rc;
119}
120
121static void sriov_release(struct pci_dev *dev)
122{
123 if (dev == dev->sriov->dev)
124 mutex_destroy(&dev->sriov->lock);
125 else
126 pci_dev_put(dev->sriov->dev);
127
128 kfree(dev->sriov);
129 dev->sriov = NULL;
130}
131
132/**
133 * pci_iov_init - initialize the IOV capability
134 * @dev: the PCI device
135 *
136 * Returns 0 on success, or negative on failure.
137 */
138int pci_iov_init(struct pci_dev *dev)
139{
140 int pos;
141
142 if (!dev->is_pcie)
143 return -ENODEV;
144
145 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
146 if (pos)
147 return sriov_init(dev, pos);
148
149 return -ENODEV;
150}
151
152/**
153 * pci_iov_release - release resources used by the IOV capability
154 * @dev: the PCI device
155 */
156void pci_iov_release(struct pci_dev *dev)
157{
158 if (dev->is_physfn)
159 sriov_release(dev);
160}
161
162/**
163 * pci_iov_resource_bar - get position of the SR-IOV BAR
164 * @dev: the PCI device
165 * @resno: the resource number
166 * @type: the BAR type to be filled in
167 *
168 * Returns position of the BAR encapsulated in the SR-IOV capability.
169 */
170int pci_iov_resource_bar(struct pci_dev *dev, int resno,
171 enum pci_bar_type *type)
172{
173 if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END)
174 return 0;
175
176 BUG_ON(!dev->is_physfn);
177
178 *type = pci_bar_unknown;
179
180 return dev->sriov->pos + PCI_SRIOV_BAR +
181 4 * (resno - PCI_IOV_RESOURCES);
182}
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index a35a8b2ba631..2b3201ec2b05 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -2360,12 +2360,19 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags)
2360 */ 2360 */
2361int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) 2361int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type)
2362{ 2362{
2363 int reg;
2364
2363 if (resno < PCI_ROM_RESOURCE) { 2365 if (resno < PCI_ROM_RESOURCE) {
2364 *type = pci_bar_unknown; 2366 *type = pci_bar_unknown;
2365 return PCI_BASE_ADDRESS_0 + 4 * resno; 2367 return PCI_BASE_ADDRESS_0 + 4 * resno;
2366 } else if (resno == PCI_ROM_RESOURCE) { 2368 } else if (resno == PCI_ROM_RESOURCE) {
2367 *type = pci_bar_mem32; 2369 *type = pci_bar_mem32;
2368 return dev->rom_base_reg; 2370 return dev->rom_base_reg;
2371 } else if (resno < PCI_BRIDGE_RESOURCES) {
2372 /* device specific resource */
2373 reg = pci_iov_resource_bar(dev, resno, type);
2374 if (reg)
2375 return reg;
2369 } 2376 }
2370 2377
2371 dev_err(&dev->dev, "BAR: invalid resource #%d\n", resno); 2378 dev_err(&dev->dev, "BAR: invalid resource #%d\n", resno);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 2cd1cba7236f..7d5327c986f5 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -201,4 +201,41 @@ resource_size_t pci_specified_resource_alignment(struct pci_dev *dev);
201extern void pci_disable_bridge_window(struct pci_dev *dev); 201extern void pci_disable_bridge_window(struct pci_dev *dev);
202#endif 202#endif
203 203
204/* Single Root I/O Virtualization */
205struct pci_sriov {
206 int pos; /* capability position */
207 int nres; /* number of resources */
208 u32 cap; /* SR-IOV Capabilities */
209 u16 ctrl; /* SR-IOV Control */
210 u16 total; /* total VFs associated with the PF */
211 u16 offset; /* first VF Routing ID offset */
212 u16 stride; /* following VF stride */
213 u32 pgsz; /* page size for BAR alignment */
214 u8 link; /* Function Dependency Link */
215 struct pci_dev *dev; /* lowest numbered PF */
216 struct pci_dev *self; /* this PF */
217 struct mutex lock; /* lock for VF bus */
218};
219
220#ifdef CONFIG_PCI_IOV
221extern int pci_iov_init(struct pci_dev *dev);
222extern void pci_iov_release(struct pci_dev *dev);
223extern int pci_iov_resource_bar(struct pci_dev *dev, int resno,
224 enum pci_bar_type *type);
225#else
226static inline int pci_iov_init(struct pci_dev *dev)
227{
228 return -ENODEV;
229}
230static inline void pci_iov_release(struct pci_dev *dev)
231
232{
233}
234static inline int pci_iov_resource_bar(struct pci_dev *dev, int resno,
235 enum pci_bar_type *type)
236{
237 return 0;
238}
239#endif /* CONFIG_PCI_IOV */
240
204#endif /* DRIVERS_PCI_H */ 241#endif /* DRIVERS_PCI_H */
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 579a56c8181f..0471f6ea1466 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -785,6 +785,7 @@ static int pci_setup_device(struct pci_dev * dev)
785static void pci_release_capabilities(struct pci_dev *dev) 785static void pci_release_capabilities(struct pci_dev *dev)
786{ 786{
787 pci_vpd_release(dev); 787 pci_vpd_release(dev);
788 pci_iov_release(dev);
788} 789}
789 790
790/** 791/**
@@ -979,6 +980,9 @@ static void pci_init_capabilities(struct pci_dev *dev)
979 980
980 /* Alternative Routing-ID Forwarding */ 981 /* Alternative Routing-ID Forwarding */
981 pci_enable_ari(dev); 982 pci_enable_ari(dev);
983
984 /* Single Root I/O Virtualization */
985 pci_iov_init(dev);
982} 986}
983 987
984void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) 988void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)