PCI: restore saved SR-IOV state
[linux-2.6-block.git] / drivers / pci / iov.c
CommitLineData
d1b054da
YZ
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
8c5cdb6a
YZ
132static void sriov_restore_state(struct pci_dev *dev)
133{
134 int i;
135 u16 ctrl;
136 struct pci_sriov *iov = dev->sriov;
137
138 pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl);
139 if (ctrl & PCI_SRIOV_CTRL_VFE)
140 return;
141
142 for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++)
143 pci_update_resource(dev, i);
144
145 pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
146 pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
147 if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
148 msleep(100);
149}
150
d1b054da
YZ
151/**
152 * pci_iov_init - initialize the IOV capability
153 * @dev: the PCI device
154 *
155 * Returns 0 on success, or negative on failure.
156 */
157int pci_iov_init(struct pci_dev *dev)
158{
159 int pos;
160
161 if (!dev->is_pcie)
162 return -ENODEV;
163
164 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
165 if (pos)
166 return sriov_init(dev, pos);
167
168 return -ENODEV;
169}
170
171/**
172 * pci_iov_release - release resources used by the IOV capability
173 * @dev: the PCI device
174 */
175void pci_iov_release(struct pci_dev *dev)
176{
177 if (dev->is_physfn)
178 sriov_release(dev);
179}
180
181/**
182 * pci_iov_resource_bar - get position of the SR-IOV BAR
183 * @dev: the PCI device
184 * @resno: the resource number
185 * @type: the BAR type to be filled in
186 *
187 * Returns position of the BAR encapsulated in the SR-IOV capability.
188 */
189int pci_iov_resource_bar(struct pci_dev *dev, int resno,
190 enum pci_bar_type *type)
191{
192 if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END)
193 return 0;
194
195 BUG_ON(!dev->is_physfn);
196
197 *type = pci_bar_unknown;
198
199 return dev->sriov->pos + PCI_SRIOV_BAR +
200 4 * (resno - PCI_IOV_RESOURCES);
201}
8c5cdb6a
YZ
202
203/**
204 * pci_restore_iov_state - restore the state of the IOV capability
205 * @dev: the PCI device
206 */
207void pci_restore_iov_state(struct pci_dev *dev)
208{
209 if (dev->is_physfn)
210 sriov_restore_state(dev);
211}