PCI: Rationalize pci_ats_queue_depth() error checking
[deliverable/linux.git] / drivers / pci / ats.c
CommitLineData
db3c33c6
JR
1/*
2 * drivers/pci/ats.c
3 *
4 * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
c320b976 5 * Copyright (C) 2011 Advanced Micro Devices,
db3c33c6
JR
6 *
7 * PCI Express I/O Virtualization (IOV) support.
8 * Address Translation Service 1.0
c320b976 9 * Page Request Interface added by Joerg Roedel <joerg.roedel@amd.com>
086ac11f 10 * PASID support added by Joerg Roedel <joerg.roedel@amd.com>
db3c33c6
JR
11 */
12
363c75db 13#include <linux/export.h>
db3c33c6
JR
14#include <linux/pci-ats.h>
15#include <linux/pci.h>
8c451945 16#include <linux/slab.h>
db3c33c6
JR
17
18#include "pci.h"
19
edc90fee 20static void ats_alloc_one(struct pci_dev *dev)
db3c33c6
JR
21{
22 int pos;
23 u16 cap;
db3c33c6
JR
24
25 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS);
26 if (!pos)
edc90fee 27 return;
db3c33c6 28
d544d75a
BH
29 dev->ats_cap = pos;
30 pci_read_config_word(dev, dev->ats_cap + PCI_ATS_CAP, &cap);
31 dev->ats_qdep = PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) :
db3c33c6 32 PCI_ATS_MAX_QDEP;
db3c33c6
JR
33}
34
edc90fee
BH
35void pci_ats_init(struct pci_dev *dev)
36{
37 ats_alloc_one(dev);
38}
39
db3c33c6
JR
40/**
41 * pci_enable_ats - enable the ATS capability
42 * @dev: the PCI device
43 * @ps: the IOMMU page shift
44 *
45 * Returns 0 on success, or negative on failure.
46 */
47int pci_enable_ats(struct pci_dev *dev, int ps)
48{
db3c33c6
JR
49 u16 ctrl;
50
d544d75a 51 BUG_ON(dev->ats_cap && dev->ats_enabled);
db3c33c6 52
d544d75a 53 if (!dev->ats_cap)
edc90fee
BH
54 return -EINVAL;
55
db3c33c6
JR
56 if (ps < PCI_ATS_MIN_STU)
57 return -EINVAL;
58
edc90fee
BH
59 /*
60 * Note that enabling ATS on a VF fails unless it's already enabled
61 * with the same STU on the PF.
62 */
63 ctrl = PCI_ATS_CTRL_ENABLE;
64 if (dev->is_virtfn) {
65 struct pci_dev *pdev = dev->physfn;
db3c33c6 66
d544d75a 67 if (pdev->ats_stu != ps)
edc90fee 68 return -EINVAL;
db3c33c6 69
d544d75a 70 atomic_inc(&pdev->ats_ref_cnt); /* count enabled VFs */
edc90fee 71 } else {
d544d75a
BH
72 dev->ats_stu = ps;
73 ctrl |= PCI_ATS_CTRL_STU(dev->ats_stu - PCI_ATS_MIN_STU);
db3c33c6 74 }
d544d75a 75 pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
db3c33c6 76
d544d75a 77 dev->ats_enabled = 1;
db3c33c6
JR
78 return 0;
79}
d4c0636c 80EXPORT_SYMBOL_GPL(pci_enable_ats);
db3c33c6
JR
81
82/**
83 * pci_disable_ats - disable the ATS capability
84 * @dev: the PCI device
85 */
86void pci_disable_ats(struct pci_dev *dev)
87{
88 u16 ctrl;
89
d544d75a 90 BUG_ON(!dev->ats_cap || !dev->ats_enabled);
db3c33c6 91
d544d75a 92 if (atomic_read(&dev->ats_ref_cnt))
edc90fee
BH
93 return; /* VFs still enabled */
94
95 if (dev->is_virtfn) {
96 struct pci_dev *pdev = dev->physfn;
97
d544d75a 98 atomic_dec(&pdev->ats_ref_cnt);
edc90fee
BH
99 }
100
d544d75a 101 pci_read_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, &ctrl);
db3c33c6 102 ctrl &= ~PCI_ATS_CTRL_ENABLE;
d544d75a 103 pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
db3c33c6 104
d544d75a 105 dev->ats_enabled = 0;
db3c33c6 106}
d4c0636c 107EXPORT_SYMBOL_GPL(pci_disable_ats);
db3c33c6 108
1900ca13
HX
109void pci_restore_ats_state(struct pci_dev *dev)
110{
111 u16 ctrl;
112
113 if (!pci_ats_enabled(dev))
114 return;
115 if (!pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS))
116 BUG();
117
118 ctrl = PCI_ATS_CTRL_ENABLE;
119 if (!dev->is_virtfn)
d544d75a
BH
120 ctrl |= PCI_ATS_CTRL_STU(dev->ats_stu - PCI_ATS_MIN_STU);
121 pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
1900ca13
HX
122}
123EXPORT_SYMBOL_GPL(pci_restore_ats_state);
124
db3c33c6
JR
125/**
126 * pci_ats_queue_depth - query the ATS Invalidate Queue Depth
127 * @dev: the PCI device
128 *
129 * Returns the queue depth on success, or negative on failure.
130 *
131 * The ATS spec uses 0 in the Invalidate Queue Depth field to
132 * indicate that the function can accept 32 Invalidate Request.
133 * But here we use the `real' values (i.e. 1~32) for the Queue
134 * Depth; and 0 indicates the function shares the Queue with
135 * other functions (doesn't exclusively own a Queue).
136 */
137int pci_ats_queue_depth(struct pci_dev *dev)
138{
3c765399
BH
139 if (!dev->ats_cap)
140 return -EINVAL;
141
db3c33c6
JR
142 if (dev->is_virtfn)
143 return 0;
144
3c765399 145 return dev->ats_qdep;
db3c33c6 146}
d4c0636c 147EXPORT_SYMBOL_GPL(pci_ats_queue_depth);
c320b976
JR
148
149#ifdef CONFIG_PCI_PRI
150/**
151 * pci_enable_pri - Enable PRI capability
152 * @ pdev: PCI device structure
153 *
154 * Returns 0 on success, negative value on error
155 */
156int pci_enable_pri(struct pci_dev *pdev, u32 reqs)
157{
158 u16 control, status;
159 u32 max_requests;
160 int pos;
161
69166fbf 162 pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
c320b976
JR
163 if (!pos)
164 return -EINVAL;
165
91f57d5e
AW
166 pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
167 pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status);
168 if ((control & PCI_PRI_CTRL_ENABLE) ||
169 !(status & PCI_PRI_STATUS_STOPPED))
c320b976
JR
170 return -EBUSY;
171
91f57d5e 172 pci_read_config_dword(pdev, pos + PCI_PRI_MAX_REQ, &max_requests);
c320b976 173 reqs = min(max_requests, reqs);
91f57d5e 174 pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ, reqs);
c320b976 175
91f57d5e
AW
176 control |= PCI_PRI_CTRL_ENABLE;
177 pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
c320b976
JR
178
179 return 0;
180}
181EXPORT_SYMBOL_GPL(pci_enable_pri);
182
183/**
184 * pci_disable_pri - Disable PRI capability
185 * @pdev: PCI device structure
186 *
187 * Only clears the enabled-bit, regardless of its former value
188 */
189void pci_disable_pri(struct pci_dev *pdev)
190{
191 u16 control;
192 int pos;
193
69166fbf 194 pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
c320b976
JR
195 if (!pos)
196 return;
197
91f57d5e
AW
198 pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
199 control &= ~PCI_PRI_CTRL_ENABLE;
200 pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
c320b976
JR
201}
202EXPORT_SYMBOL_GPL(pci_disable_pri);
203
c320b976
JR
204/**
205 * pci_reset_pri - Resets device's PRI state
206 * @pdev: PCI device structure
207 *
208 * The PRI capability must be disabled before this function is called.
209 * Returns 0 on success, negative value on error.
210 */
211int pci_reset_pri(struct pci_dev *pdev)
212{
213 u16 control;
214 int pos;
215
69166fbf 216 pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
c320b976
JR
217 if (!pos)
218 return -EINVAL;
219
91f57d5e
AW
220 pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
221 if (control & PCI_PRI_CTRL_ENABLE)
c320b976
JR
222 return -EBUSY;
223
91f57d5e 224 control |= PCI_PRI_CTRL_RESET;
c320b976 225
91f57d5e 226 pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
c320b976
JR
227
228 return 0;
229}
230EXPORT_SYMBOL_GPL(pci_reset_pri);
c320b976 231#endif /* CONFIG_PCI_PRI */
086ac11f
JR
232
233#ifdef CONFIG_PCI_PASID
234/**
235 * pci_enable_pasid - Enable the PASID capability
236 * @pdev: PCI device structure
237 * @features: Features to enable
238 *
239 * Returns 0 on success, negative value on error. This function checks
240 * whether the features are actually supported by the device and returns
241 * an error if not.
242 */
243int pci_enable_pasid(struct pci_dev *pdev, int features)
244{
245 u16 control, supported;
246 int pos;
247
69166fbf 248 pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
086ac11f
JR
249 if (!pos)
250 return -EINVAL;
251
91f57d5e
AW
252 pci_read_config_word(pdev, pos + PCI_PASID_CTRL, &control);
253 pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
086ac11f 254
91f57d5e 255 if (control & PCI_PASID_CTRL_ENABLE)
086ac11f
JR
256 return -EINVAL;
257
91f57d5e 258 supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
086ac11f
JR
259
260 /* User wants to enable anything unsupported? */
261 if ((supported & features) != features)
262 return -EINVAL;
263
91f57d5e 264 control = PCI_PASID_CTRL_ENABLE | features;
086ac11f 265
91f57d5e 266 pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control);
086ac11f
JR
267
268 return 0;
269}
270EXPORT_SYMBOL_GPL(pci_enable_pasid);
271
272/**
273 * pci_disable_pasid - Disable the PASID capability
274 * @pdev: PCI device structure
275 *
276 */
277void pci_disable_pasid(struct pci_dev *pdev)
278{
279 u16 control = 0;
280 int pos;
281
69166fbf 282 pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
086ac11f
JR
283 if (!pos)
284 return;
285
91f57d5e 286 pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control);
086ac11f
JR
287}
288EXPORT_SYMBOL_GPL(pci_disable_pasid);
289
290/**
291 * pci_pasid_features - Check which PASID features are supported
292 * @pdev: PCI device structure
293 *
294 * Returns a negative value when no PASI capability is present.
295 * Otherwise is returns a bitmask with supported features. Current
296 * features reported are:
91f57d5e 297 * PCI_PASID_CAP_EXEC - Execute permission supported
f7625980 298 * PCI_PASID_CAP_PRIV - Privileged mode supported
086ac11f
JR
299 */
300int pci_pasid_features(struct pci_dev *pdev)
301{
302 u16 supported;
303 int pos;
304
69166fbf 305 pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
086ac11f
JR
306 if (!pos)
307 return -EINVAL;
308
91f57d5e 309 pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
086ac11f 310
91f57d5e 311 supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
086ac11f
JR
312
313 return supported;
314}
315EXPORT_SYMBOL_GPL(pci_pasid_features);
316
317#define PASID_NUMBER_SHIFT 8
318#define PASID_NUMBER_MASK (0x1f << PASID_NUMBER_SHIFT)
319/**
320 * pci_max_pasid - Get maximum number of PASIDs supported by device
321 * @pdev: PCI device structure
322 *
323 * Returns negative value when PASID capability is not present.
324 * Otherwise it returns the numer of supported PASIDs.
325 */
326int pci_max_pasids(struct pci_dev *pdev)
327{
328 u16 supported;
329 int pos;
330
69166fbf 331 pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
086ac11f
JR
332 if (!pos)
333 return -EINVAL;
334
91f57d5e 335 pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
086ac11f
JR
336
337 supported = (supported & PASID_NUMBER_MASK) >> PASID_NUMBER_SHIFT;
338
339 return (1 << supported);
340}
341EXPORT_SYMBOL_GPL(pci_max_pasids);
342#endif /* CONFIG_PCI_PASID */
This page took 0.237968 seconds and 5 git commands to generate.