Blame view

drivers/pci/ats.c 8.82 KB
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
1
2
3
4
  /*
   * drivers/pci/ats.c
   *
   * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
c320b976d   Joerg Roedel   PCI: Add implemen...
5
   * Copyright (C) 2011 Advanced Micro Devices,
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
6
7
8
   *
   * PCI Express I/O Virtualization (IOV) support.
   *   Address Translation Service 1.0
c320b976d   Joerg Roedel   PCI: Add implemen...
9
   *   Page Request Interface added by Joerg Roedel <joerg.roedel@amd.com>
086ac11f6   Joerg Roedel   PCI: Add support ...
10
   *   PASID support added by Joerg Roedel <joerg.roedel@amd.com>
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
11
   */
363c75db1   Paul Gortmaker   pci: Fix files ne...
12
  #include <linux/export.h>
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
13
14
  #include <linux/pci-ats.h>
  #include <linux/pci.h>
8c4519456   James Bottomley   PCI: fix ats comp...
15
  #include <linux/slab.h>
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
16
17
  
  #include "pci.h"
afdd596c4   Bjorn Helgaas   PCI: Inline the A...
18
  void pci_ats_init(struct pci_dev *dev)
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
19
20
  {
  	int pos;
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
21
22
23
  
  	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS);
  	if (!pos)
edc90fee9   Bjorn Helgaas   PCI: Allocate ATS...
24
  		return;
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
25

d544d75ac   Bjorn Helgaas   PCI: Embed ATS in...
26
  	dev->ats_cap = pos;
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
27
28
29
30
31
32
33
34
35
36
37
  }
  
  /**
   * pci_enable_ats - enable the ATS capability
   * @dev: the PCI device
   * @ps: the IOMMU page shift
   *
   * Returns 0 on success, or negative on failure.
   */
  int pci_enable_ats(struct pci_dev *dev, int ps)
  {
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
38
  	u16 ctrl;
c39127dba   Bjorn Helgaas   PCI: Use pci_phys...
39
  	struct pci_dev *pdev;
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
40

d544d75ac   Bjorn Helgaas   PCI: Embed ATS in...
41
  	if (!dev->ats_cap)
edc90fee9   Bjorn Helgaas   PCI: Allocate ATS...
42
  		return -EINVAL;
f7ef1340b   Bjorn Helgaas   PCI: Remove pci_a...
43
  	if (WARN_ON(dev->ats_enabled))
a021f3019   Bjorn Helgaas   PCI: Clean up ATS...
44
  		return -EBUSY;
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
45
46
  	if (ps < PCI_ATS_MIN_STU)
  		return -EINVAL;
edc90fee9   Bjorn Helgaas   PCI: Allocate ATS...
47
48
49
50
51
52
  	/*
  	 * Note that enabling ATS on a VF fails unless it's already enabled
  	 * with the same STU on the PF.
  	 */
  	ctrl = PCI_ATS_CTRL_ENABLE;
  	if (dev->is_virtfn) {
c39127dba   Bjorn Helgaas   PCI: Use pci_phys...
53
  		pdev = pci_physfn(dev);
d544d75ac   Bjorn Helgaas   PCI: Embed ATS in...
54
  		if (pdev->ats_stu != ps)
edc90fee9   Bjorn Helgaas   PCI: Allocate ATS...
55
  			return -EINVAL;
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
56

d544d75ac   Bjorn Helgaas   PCI: Embed ATS in...
57
  		atomic_inc(&pdev->ats_ref_cnt);  /* count enabled VFs */
edc90fee9   Bjorn Helgaas   PCI: Allocate ATS...
58
  	} else {
d544d75ac   Bjorn Helgaas   PCI: Embed ATS in...
59
60
  		dev->ats_stu = ps;
  		ctrl |= PCI_ATS_CTRL_STU(dev->ats_stu - PCI_ATS_MIN_STU);
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
61
  	}
d544d75ac   Bjorn Helgaas   PCI: Embed ATS in...
62
  	pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
63

d544d75ac   Bjorn Helgaas   PCI: Embed ATS in...
64
  	dev->ats_enabled = 1;
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
65
66
  	return 0;
  }
d4c0636c2   Joerg Roedel   PCI: Export ATS f...
67
  EXPORT_SYMBOL_GPL(pci_enable_ats);
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
68
69
70
71
72
73
74
  
  /**
   * pci_disable_ats - disable the ATS capability
   * @dev: the PCI device
   */
  void pci_disable_ats(struct pci_dev *dev)
  {
c39127dba   Bjorn Helgaas   PCI: Use pci_phys...
75
  	struct pci_dev *pdev;
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
76
  	u16 ctrl;
f7ef1340b   Bjorn Helgaas   PCI: Remove pci_a...
77
  	if (WARN_ON(!dev->ats_enabled))
a021f3019   Bjorn Helgaas   PCI: Clean up ATS...
78
  		return;
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
79

d544d75ac   Bjorn Helgaas   PCI: Embed ATS in...
80
  	if (atomic_read(&dev->ats_ref_cnt))
edc90fee9   Bjorn Helgaas   PCI: Allocate ATS...
81
82
83
  		return;		/* VFs still enabled */
  
  	if (dev->is_virtfn) {
c39127dba   Bjorn Helgaas   PCI: Use pci_phys...
84
  		pdev = pci_physfn(dev);
d544d75ac   Bjorn Helgaas   PCI: Embed ATS in...
85
  		atomic_dec(&pdev->ats_ref_cnt);
edc90fee9   Bjorn Helgaas   PCI: Allocate ATS...
86
  	}
d544d75ac   Bjorn Helgaas   PCI: Embed ATS in...
87
  	pci_read_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, &ctrl);
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
88
  	ctrl &= ~PCI_ATS_CTRL_ENABLE;
d544d75ac   Bjorn Helgaas   PCI: Embed ATS in...
89
  	pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
90

d544d75ac   Bjorn Helgaas   PCI: Embed ATS in...
91
  	dev->ats_enabled = 0;
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
92
  }
d4c0636c2   Joerg Roedel   PCI: Export ATS f...
93
  EXPORT_SYMBOL_GPL(pci_disable_ats);
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
94

1900ca132   Hao, Xudong   PCI: Enable ATS a...
95
96
97
  void pci_restore_ats_state(struct pci_dev *dev)
  {
  	u16 ctrl;
f7ef1340b   Bjorn Helgaas   PCI: Remove pci_a...
98
  	if (!dev->ats_enabled)
1900ca132   Hao, Xudong   PCI: Enable ATS a...
99
  		return;
1900ca132   Hao, Xudong   PCI: Enable ATS a...
100
101
102
  
  	ctrl = PCI_ATS_CTRL_ENABLE;
  	if (!dev->is_virtfn)
d544d75ac   Bjorn Helgaas   PCI: Embed ATS in...
103
104
  		ctrl |= PCI_ATS_CTRL_STU(dev->ats_stu - PCI_ATS_MIN_STU);
  	pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
1900ca132   Hao, Xudong   PCI: Enable ATS a...
105
106
  }
  EXPORT_SYMBOL_GPL(pci_restore_ats_state);
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
  /**
   * pci_ats_queue_depth - query the ATS Invalidate Queue Depth
   * @dev: the PCI device
   *
   * Returns the queue depth on success, or negative on failure.
   *
   * The ATS spec uses 0 in the Invalidate Queue Depth field to
   * indicate that the function can accept 32 Invalidate Request.
   * But here we use the `real' values (i.e. 1~32) for the Queue
   * Depth; and 0 indicates the function shares the Queue with
   * other functions (doesn't exclusively own a Queue).
   */
  int pci_ats_queue_depth(struct pci_dev *dev)
  {
a71f938f3   Bjorn Helgaas   PCI: Stop caching...
121
  	u16 cap;
3c7653995   Bjorn Helgaas   PCI: Rationalize ...
122
123
  	if (!dev->ats_cap)
  		return -EINVAL;
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
124
125
  	if (dev->is_virtfn)
  		return 0;
a71f938f3   Bjorn Helgaas   PCI: Stop caching...
126
127
  	pci_read_config_word(dev, dev->ats_cap + PCI_ATS_CAP, &cap);
  	return PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : PCI_ATS_MAX_QDEP;
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
128
  }
d4c0636c2   Joerg Roedel   PCI: Export ATS f...
129
  EXPORT_SYMBOL_GPL(pci_ats_queue_depth);
c320b976d   Joerg Roedel   PCI: Add implemen...
130
131
132
133
134
135
136
137
138
139
140
141
142
  
  #ifdef CONFIG_PCI_PRI
  /**
   * pci_enable_pri - Enable PRI capability
   * @ pdev: PCI device structure
   *
   * Returns 0 on success, negative value on error
   */
  int pci_enable_pri(struct pci_dev *pdev, u32 reqs)
  {
  	u16 control, status;
  	u32 max_requests;
  	int pos;
a4f4fa681   Jean-Philippe Brucker   PCI: Cache PRI an...
143
144
  	if (WARN_ON(pdev->pri_enabled))
  		return -EBUSY;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
145
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
c320b976d   Joerg Roedel   PCI: Add implemen...
146
147
  	if (!pos)
  		return -EINVAL;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
148
  	pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status);
4ebeb1ec5   CQ Tang   PCI: Restore PRI ...
149
  	if (!(status & PCI_PRI_STATUS_STOPPED))
c320b976d   Joerg Roedel   PCI: Add implemen...
150
  		return -EBUSY;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
151
  	pci_read_config_dword(pdev, pos + PCI_PRI_MAX_REQ, &max_requests);
c320b976d   Joerg Roedel   PCI: Add implemen...
152
  	reqs = min(max_requests, reqs);
4ebeb1ec5   CQ Tang   PCI: Restore PRI ...
153
  	pdev->pri_reqs_alloc = reqs;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
154
  	pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ, reqs);
c320b976d   Joerg Roedel   PCI: Add implemen...
155

4ebeb1ec5   CQ Tang   PCI: Restore PRI ...
156
  	control = PCI_PRI_CTRL_ENABLE;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
157
  	pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
c320b976d   Joerg Roedel   PCI: Add implemen...
158

a4f4fa681   Jean-Philippe Brucker   PCI: Cache PRI an...
159
  	pdev->pri_enabled = 1;
c320b976d   Joerg Roedel   PCI: Add implemen...
160
161
162
163
164
165
166
167
168
169
170
171
172
173
  	return 0;
  }
  EXPORT_SYMBOL_GPL(pci_enable_pri);
  
  /**
   * pci_disable_pri - Disable PRI capability
   * @pdev: PCI device structure
   *
   * Only clears the enabled-bit, regardless of its former value
   */
  void pci_disable_pri(struct pci_dev *pdev)
  {
  	u16 control;
  	int pos;
a4f4fa681   Jean-Philippe Brucker   PCI: Cache PRI an...
174
175
  	if (WARN_ON(!pdev->pri_enabled))
  		return;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
176
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
c320b976d   Joerg Roedel   PCI: Add implemen...
177
178
  	if (!pos)
  		return;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
179
180
181
  	pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
  	control &= ~PCI_PRI_CTRL_ENABLE;
  	pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
a4f4fa681   Jean-Philippe Brucker   PCI: Cache PRI an...
182
183
  
  	pdev->pri_enabled = 0;
c320b976d   Joerg Roedel   PCI: Add implemen...
184
185
186
187
  }
  EXPORT_SYMBOL_GPL(pci_disable_pri);
  
  /**
4ebeb1ec5   CQ Tang   PCI: Restore PRI ...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
   * pci_restore_pri_state - Restore PRI
   * @pdev: PCI device structure
   */
  void pci_restore_pri_state(struct pci_dev *pdev)
  {
  	u16 control = PCI_PRI_CTRL_ENABLE;
  	u32 reqs = pdev->pri_reqs_alloc;
  	int pos;
  
  	if (!pdev->pri_enabled)
  		return;
  
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
  	if (!pos)
  		return;
  
  	pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ, reqs);
  	pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
  }
  EXPORT_SYMBOL_GPL(pci_restore_pri_state);
  
  /**
c320b976d   Joerg Roedel   PCI: Add implemen...
210
211
212
213
214
215
216
217
218
219
   * pci_reset_pri - Resets device's PRI state
   * @pdev: PCI device structure
   *
   * The PRI capability must be disabled before this function is called.
   * Returns 0 on success, negative value on error.
   */
  int pci_reset_pri(struct pci_dev *pdev)
  {
  	u16 control;
  	int pos;
a4f4fa681   Jean-Philippe Brucker   PCI: Cache PRI an...
220
221
  	if (WARN_ON(pdev->pri_enabled))
  		return -EBUSY;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
222
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
c320b976d   Joerg Roedel   PCI: Add implemen...
223
224
  	if (!pos)
  		return -EINVAL;
4ebeb1ec5   CQ Tang   PCI: Restore PRI ...
225
  	control = PCI_PRI_CTRL_RESET;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
226
  	pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
c320b976d   Joerg Roedel   PCI: Add implemen...
227
228
229
230
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(pci_reset_pri);
c320b976d   Joerg Roedel   PCI: Add implemen...
231
  #endif /* CONFIG_PCI_PRI */
086ac11f6   Joerg Roedel   PCI: Add support ...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  
  #ifdef CONFIG_PCI_PASID
  /**
   * pci_enable_pasid - Enable the PASID capability
   * @pdev: PCI device structure
   * @features: Features to enable
   *
   * Returns 0 on success, negative value on error. This function checks
   * whether the features are actually supported by the device and returns
   * an error if not.
   */
  int pci_enable_pasid(struct pci_dev *pdev, int features)
  {
  	u16 control, supported;
  	int pos;
a4f4fa681   Jean-Philippe Brucker   PCI: Cache PRI an...
247
248
  	if (WARN_ON(pdev->pasid_enabled))
  		return -EBUSY;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
249
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
086ac11f6   Joerg Roedel   PCI: Add support ...
250
251
  	if (!pos)
  		return -EINVAL;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
252
  	pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
253
  	supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
086ac11f6   Joerg Roedel   PCI: Add support ...
254
255
256
257
  
  	/* User wants to enable anything unsupported? */
  	if ((supported & features) != features)
  		return -EINVAL;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
258
  	control = PCI_PASID_CTRL_ENABLE | features;
4ebeb1ec5   CQ Tang   PCI: Restore PRI ...
259
  	pdev->pasid_features = features;
086ac11f6   Joerg Roedel   PCI: Add support ...
260

91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
261
  	pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control);
086ac11f6   Joerg Roedel   PCI: Add support ...
262

a4f4fa681   Jean-Philippe Brucker   PCI: Cache PRI an...
263
  	pdev->pasid_enabled = 1;
086ac11f6   Joerg Roedel   PCI: Add support ...
264
265
266
267
268
269
270
  	return 0;
  }
  EXPORT_SYMBOL_GPL(pci_enable_pasid);
  
  /**
   * pci_disable_pasid - Disable the PASID capability
   * @pdev: PCI device structure
086ac11f6   Joerg Roedel   PCI: Add support ...
271
272
273
274
275
   */
  void pci_disable_pasid(struct pci_dev *pdev)
  {
  	u16 control = 0;
  	int pos;
a4f4fa681   Jean-Philippe Brucker   PCI: Cache PRI an...
276
277
  	if (WARN_ON(!pdev->pasid_enabled))
  		return;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
278
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
086ac11f6   Joerg Roedel   PCI: Add support ...
279
280
  	if (!pos)
  		return;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
281
  	pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control);
a4f4fa681   Jean-Philippe Brucker   PCI: Cache PRI an...
282
283
  
  	pdev->pasid_enabled = 0;
086ac11f6   Joerg Roedel   PCI: Add support ...
284
285
286
287
  }
  EXPORT_SYMBOL_GPL(pci_disable_pasid);
  
  /**
4ebeb1ec5   CQ Tang   PCI: Restore PRI ...
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
   * pci_restore_pasid_state - Restore PASID capabilities
   * @pdev: PCI device structure
   */
  void pci_restore_pasid_state(struct pci_dev *pdev)
  {
  	u16 control;
  	int pos;
  
  	if (!pdev->pasid_enabled)
  		return;
  
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
  	if (!pos)
  		return;
  
  	control = PCI_PASID_CTRL_ENABLE | pdev->pasid_features;
  	pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control);
  }
  EXPORT_SYMBOL_GPL(pci_restore_pasid_state);
  
  /**
086ac11f6   Joerg Roedel   PCI: Add support ...
309
310
311
312
313
314
   * pci_pasid_features - Check which PASID features are supported
   * @pdev: PCI device structure
   *
   * Returns a negative value when no PASI capability is present.
   * Otherwise is returns a bitmask with supported features. Current
   * features reported are:
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
315
   * PCI_PASID_CAP_EXEC - Execute permission supported
f7625980f   Bjorn Helgaas   PCI: Fix whitespa...
316
   * PCI_PASID_CAP_PRIV - Privileged mode supported
086ac11f6   Joerg Roedel   PCI: Add support ...
317
318
319
320
321
   */
  int pci_pasid_features(struct pci_dev *pdev)
  {
  	u16 supported;
  	int pos;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
322
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
086ac11f6   Joerg Roedel   PCI: Add support ...
323
324
  	if (!pos)
  		return -EINVAL;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
325
  	pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
086ac11f6   Joerg Roedel   PCI: Add support ...
326

91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
327
  	supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
086ac11f6   Joerg Roedel   PCI: Add support ...
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
  
  	return supported;
  }
  EXPORT_SYMBOL_GPL(pci_pasid_features);
  
  #define PASID_NUMBER_SHIFT	8
  #define PASID_NUMBER_MASK	(0x1f << PASID_NUMBER_SHIFT)
  /**
   * pci_max_pasid - Get maximum number of PASIDs supported by device
   * @pdev: PCI device structure
   *
   * Returns negative value when PASID capability is not present.
   * Otherwise it returns the numer of supported PASIDs.
   */
  int pci_max_pasids(struct pci_dev *pdev)
  {
  	u16 supported;
  	int pos;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
346
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
086ac11f6   Joerg Roedel   PCI: Add support ...
347
348
  	if (!pos)
  		return -EINVAL;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
349
  	pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
086ac11f6   Joerg Roedel   PCI: Add support ...
350
351
352
353
354
355
356
  
  	supported = (supported & PASID_NUMBER_MASK) >> PASID_NUMBER_SHIFT;
  
  	return (1 << supported);
  }
  EXPORT_SYMBOL_GPL(pci_max_pasids);
  #endif /* CONFIG_PCI_PASID */