diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/Kconfig | 10 | ||||
-rw-r--r-- | drivers/pci/Makefile | 2 | ||||
-rw-r--r-- | drivers/pci/iov.c | 182 | ||||
-rw-r--r-- | drivers/pci/pci.c | 7 | ||||
-rw-r--r-- | drivers/pci/pci.h | 37 | ||||
-rw-r--r-- | drivers/pci/probe.c | 4 |
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 | |||
63 | config 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 | ||
30 | obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o | 30 | obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o |
31 | 31 | ||
32 | obj-$(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 | |||
17 | static 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 | |||
51 | found: | ||
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 | |||
112 | failed: | ||
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 | |||
121 | static 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 | */ | ||
138 | int 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 | */ | ||
156 | void 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 | */ | ||
170 | int 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 | */ |
2361 | int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) | 2361 | int 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); | |||
201 | extern void pci_disable_bridge_window(struct pci_dev *dev); | 201 | extern void pci_disable_bridge_window(struct pci_dev *dev); |
202 | #endif | 202 | #endif |
203 | 203 | ||
204 | /* Single Root I/O Virtualization */ | ||
205 | struct 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 | ||
221 | extern int pci_iov_init(struct pci_dev *dev); | ||
222 | extern void pci_iov_release(struct pci_dev *dev); | ||
223 | extern int pci_iov_resource_bar(struct pci_dev *dev, int resno, | ||
224 | enum pci_bar_type *type); | ||
225 | #else | ||
226 | static inline int pci_iov_init(struct pci_dev *dev) | ||
227 | { | ||
228 | return -ENODEV; | ||
229 | } | ||
230 | static inline void pci_iov_release(struct pci_dev *dev) | ||
231 | |||
232 | { | ||
233 | } | ||
234 | static 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) | |||
785 | static void pci_release_capabilities(struct pci_dev *dev) | 785 | static 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 | ||
984 | void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) | 988 | void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) |