diff options
Diffstat (limited to 'drivers/pci/iov.c')
-rw-r--r-- | drivers/pci/iov.c | 182 |
1 files changed, 182 insertions, 0 deletions
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 | } | ||