Blame view

drivers/pci/ats.c 10.8 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
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  
  #include "pci.h"
  
  static int ats_alloc_one(struct pci_dev *dev, int ps)
  {
  	int pos;
  	u16 cap;
  	struct pci_ats *ats;
  
  	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS);
  	if (!pos)
  		return -ENODEV;
  
  	ats = kzalloc(sizeof(*ats), GFP_KERNEL);
  	if (!ats)
  		return -ENOMEM;
  
  	ats->pos = pos;
  	ats->stu = ps;
  	pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap);
  	ats->qdep = PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) :
  					    PCI_ATS_MAX_QDEP;
  	dev->ats = ats;
  
  	return 0;
  }
  
  static void ats_free_one(struct pci_dev *dev)
  {
  	kfree(dev->ats);
  	dev->ats = NULL;
  }
  
  /**
   * 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)
  {
  	int rc;
  	u16 ctrl;
  
  	BUG_ON(dev->ats && dev->ats->is_enabled);
  
  	if (ps < PCI_ATS_MIN_STU)
  		return -EINVAL;
  
  	if (dev->is_physfn || dev->is_virtfn) {
  		struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn;
  
  		mutex_lock(&pdev->sriov->lock);
  		if (pdev->ats)
  			rc = pdev->ats->stu == ps ? 0 : -EINVAL;
  		else
  			rc = ats_alloc_one(pdev, ps);
  
  		if (!rc)
  			pdev->ats->ref_cnt++;
  		mutex_unlock(&pdev->sriov->lock);
  		if (rc)
  			return rc;
  	}
  
  	if (!dev->is_physfn) {
  		rc = ats_alloc_one(dev, ps);
  		if (rc)
  			return rc;
  	}
  
  	ctrl = PCI_ATS_CTRL_ENABLE;
  	if (!dev->is_virtfn)
  		ctrl |= PCI_ATS_CTRL_STU(ps - PCI_ATS_MIN_STU);
  	pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl);
  
  	dev->ats->is_enabled = 1;
  
  	return 0;
  }
d4c0636c2   Joerg Roedel   PCI: Export ATS f...
97
  EXPORT_SYMBOL_GPL(pci_enable_ats);
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  
  /**
   * pci_disable_ats - disable the ATS capability
   * @dev: the PCI device
   */
  void pci_disable_ats(struct pci_dev *dev)
  {
  	u16 ctrl;
  
  	BUG_ON(!dev->ats || !dev->ats->is_enabled);
  
  	pci_read_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, &ctrl);
  	ctrl &= ~PCI_ATS_CTRL_ENABLE;
  	pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl);
  
  	dev->ats->is_enabled = 0;
  
  	if (dev->is_physfn || dev->is_virtfn) {
  		struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn;
  
  		mutex_lock(&pdev->sriov->lock);
  		pdev->ats->ref_cnt--;
  		if (!pdev->ats->ref_cnt)
  			ats_free_one(pdev);
  		mutex_unlock(&pdev->sriov->lock);
  	}
  
  	if (!dev->is_physfn)
  		ats_free_one(dev);
  }
d4c0636c2   Joerg Roedel   PCI: Export ATS f...
128
  EXPORT_SYMBOL_GPL(pci_disable_ats);
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
129

1900ca132   Hao, Xudong   PCI: Enable ATS a...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
  void pci_restore_ats_state(struct pci_dev *dev)
  {
  	u16 ctrl;
  
  	if (!pci_ats_enabled(dev))
  		return;
  	if (!pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS))
  		BUG();
  
  	ctrl = PCI_ATS_CTRL_ENABLE;
  	if (!dev->is_virtfn)
  		ctrl |= PCI_ATS_CTRL_STU(dev->ats->stu - PCI_ATS_MIN_STU);
  
  	pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl);
  }
  EXPORT_SYMBOL_GPL(pci_restore_ats_state);
db3c33c6d   Joerg Roedel   PCI: Move ATS imp...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  /**
   * 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)
  {
  	int pos;
  	u16 cap;
  
  	if (dev->is_virtfn)
  		return 0;
  
  	if (dev->ats)
  		return dev->ats->qdep;
  
  	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS);
  	if (!pos)
  		return -ENODEV;
  
  	pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap);
  
  	return PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) :
  				       PCI_ATS_MAX_QDEP;
  }
d4c0636c2   Joerg Roedel   PCI: Export ATS f...
178
  EXPORT_SYMBOL_GPL(pci_ats_queue_depth);
c320b976d   Joerg Roedel   PCI: Add implemen...
179
180
181
182
183
184
185
186
187
188
189
190
191
  
  #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;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
192
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
c320b976d   Joerg Roedel   PCI: Add implemen...
193
194
  	if (!pos)
  		return -EINVAL;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
195
196
197
198
  	pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
  	pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status);
  	if ((control & PCI_PRI_CTRL_ENABLE) ||
  	    !(status & PCI_PRI_STATUS_STOPPED))
c320b976d   Joerg Roedel   PCI: Add implemen...
199
  		return -EBUSY;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
200
  	pci_read_config_dword(pdev, pos + PCI_PRI_MAX_REQ, &max_requests);
c320b976d   Joerg Roedel   PCI: Add implemen...
201
  	reqs = min(max_requests, reqs);
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
202
  	pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ, reqs);
c320b976d   Joerg Roedel   PCI: Add implemen...
203

91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
204
205
  	control |= PCI_PRI_CTRL_ENABLE;
  	pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
c320b976d   Joerg Roedel   PCI: Add implemen...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
  
  	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;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
221
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
c320b976d   Joerg Roedel   PCI: Add implemen...
222
223
  	if (!pos)
  		return;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
224
225
226
  	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);
c320b976d   Joerg Roedel   PCI: Add implemen...
227
228
229
230
231
232
233
234
235
236
237
238
239
  }
  EXPORT_SYMBOL_GPL(pci_disable_pri);
  
  /**
   * pci_pri_enabled - Checks if PRI capability is enabled
   * @pdev: PCI device structure
   *
   * Returns true if PRI is enabled on the device, false otherwise
   */
  bool pci_pri_enabled(struct pci_dev *pdev)
  {
  	u16 control;
  	int pos;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
240
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
c320b976d   Joerg Roedel   PCI: Add implemen...
241
242
  	if (!pos)
  		return false;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
243
  	pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
c320b976d   Joerg Roedel   PCI: Add implemen...
244

91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
245
  	return (control & PCI_PRI_CTRL_ENABLE) ? true : false;
c320b976d   Joerg Roedel   PCI: Add implemen...
246
247
248
249
250
251
252
253
254
255
256
257
258
259
  }
  EXPORT_SYMBOL_GPL(pci_pri_enabled);
  
  /**
   * 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;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
260
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
c320b976d   Joerg Roedel   PCI: Add implemen...
261
262
  	if (!pos)
  		return -EINVAL;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
263
264
  	pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
  	if (control & PCI_PRI_CTRL_ENABLE)
c320b976d   Joerg Roedel   PCI: Add implemen...
265
  		return -EBUSY;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
266
  	control |= PCI_PRI_CTRL_RESET;
c320b976d   Joerg Roedel   PCI: Add implemen...
267

91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
268
  	pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
c320b976d   Joerg Roedel   PCI: Add implemen...
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(pci_reset_pri);
  
  /**
   * pci_pri_stopped - Checks whether the PRI capability is stopped
   * @pdev: PCI device structure
   *
   * Returns true if the PRI capability on the device is disabled and the
   * device has no outstanding PRI requests, false otherwise. The device
   * indicates this via the STOPPED bit in the status register of the
   * capability.
   * The device internal state can be cleared by resetting the PRI state
   * with pci_reset_pri(). This can force the capability into the STOPPED
   * state.
   */
  bool pci_pri_stopped(struct pci_dev *pdev)
  {
  	u16 control, status;
  	int pos;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
290
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
c320b976d   Joerg Roedel   PCI: Add implemen...
291
292
  	if (!pos)
  		return true;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
293
294
  	pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
  	pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status);
c320b976d   Joerg Roedel   PCI: Add implemen...
295

91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
296
  	if (control & PCI_PRI_CTRL_ENABLE)
c320b976d   Joerg Roedel   PCI: Add implemen...
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  		return false;
  
  	return (status & PCI_PRI_STATUS_STOPPED) ? true : false;
  }
  EXPORT_SYMBOL_GPL(pci_pri_stopped);
  
  /**
   * pci_pri_status - Request PRI status of a device
   * @pdev: PCI device structure
   *
   * Returns negative value on failure, status on success. The status can
   * be checked against status-bits. Supported bits are currently:
   * PCI_PRI_STATUS_RF:      Response failure
   * PCI_PRI_STATUS_UPRGI:   Unexpected Page Request Group Index
   * PCI_PRI_STATUS_STOPPED: PRI has stopped
   */
  int pci_pri_status(struct pci_dev *pdev)
  {
  	u16 status, control;
  	int pos;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
317
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
c320b976d   Joerg Roedel   PCI: Add implemen...
318
319
  	if (!pos)
  		return -EINVAL;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
320
321
  	pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
  	pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status);
c320b976d   Joerg Roedel   PCI: Add implemen...
322
323
  
  	/* Stopped bit is undefined when enable == 1, so clear it */
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
324
  	if (control & PCI_PRI_CTRL_ENABLE)
c320b976d   Joerg Roedel   PCI: Add implemen...
325
326
327
328
329
330
  		status &= ~PCI_PRI_STATUS_STOPPED;
  
  	return status;
  }
  EXPORT_SYMBOL_GPL(pci_pri_status);
  #endif /* CONFIG_PCI_PRI */
086ac11f6   Joerg Roedel   PCI: Add support ...
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
  
  #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;
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
350
  	pci_read_config_word(pdev, pos + PCI_PASID_CTRL, &control);
  	pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
086ac11f6   Joerg Roedel   PCI: Add support ...
351

91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
352
  	if (control & PCI_PASID_CTRL_ENABLE)
086ac11f6   Joerg Roedel   PCI: Add support ...
353
  		return -EINVAL;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
354
  	supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
086ac11f6   Joerg Roedel   PCI: Add support ...
355
356
357
358
  
  	/* User wants to enable anything unsupported? */
  	if ((supported & features) != features)
  		return -EINVAL;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
359
  	control = PCI_PASID_CTRL_ENABLE | features;
086ac11f6   Joerg Roedel   PCI: Add support ...
360

91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
361
  	pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control);
086ac11f6   Joerg Roedel   PCI: Add support ...
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(pci_enable_pasid);
  
  /**
   * pci_disable_pasid - Disable the PASID capability
   * @pdev: PCI device structure
   *
   */
  void pci_disable_pasid(struct pci_dev *pdev)
  {
  	u16 control = 0;
  	int pos;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
376
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
086ac11f6   Joerg Roedel   PCI: Add support ...
377
378
  	if (!pos)
  		return;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
379
  	pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control);
086ac11f6   Joerg Roedel   PCI: Add support ...
380
381
382
383
384
385
386
387
388
389
  }
  EXPORT_SYMBOL_GPL(pci_disable_pasid);
  
  /**
   * 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...
390
391
   * PCI_PASID_CAP_EXEC - Execute permission supported
   * PCI_PASID_CAP_PRIV - Priviledged mode supported
086ac11f6   Joerg Roedel   PCI: Add support ...
392
393
394
395
396
   */
  int pci_pasid_features(struct pci_dev *pdev)
  {
  	u16 supported;
  	int pos;
69166fbf0   Alex Williamson   PCI: Fix PRI and ...
397
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
086ac11f6   Joerg Roedel   PCI: Add support ...
398
399
  	if (!pos)
  		return -EINVAL;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
400
  	pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
086ac11f6   Joerg Roedel   PCI: Add support ...
401

91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
402
  	supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
086ac11f6   Joerg Roedel   PCI: Add support ...
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
  
  	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 ...
421
  	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
086ac11f6   Joerg Roedel   PCI: Add support ...
422
423
  	if (!pos)
  		return -EINVAL;
91f57d5e1   Alex Williamson   PCI: More PRI/PAS...
424
  	pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
086ac11f6   Joerg Roedel   PCI: Add support ...
425
426
427
428
429
430
431
  
  	supported = (supported & PASID_NUMBER_MASK) >> PASID_NUMBER_SHIFT;
  
  	return (1 << supported);
  }
  EXPORT_SYMBOL_GPL(pci_max_pasids);
  #endif /* CONFIG_PCI_PASID */