xref: /openbmc/linux/drivers/pci/ats.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
17328c8f4SBjorn Helgaas // SPDX-License-Identifier: GPL-2.0
2db3c33c6SJoerg Roedel /*
3df62ab5eSBjorn Helgaas  * PCI Express I/O Virtualization (IOV) support
4db3c33c6SJoerg Roedel  *   Address Translation Service 1.0
5c320b976SJoerg Roedel  *   Page Request Interface added by Joerg Roedel <joerg.roedel@amd.com>
6086ac11fSJoerg Roedel  *   PASID support added by Joerg Roedel <joerg.roedel@amd.com>
7df62ab5eSBjorn Helgaas  *
8df62ab5eSBjorn Helgaas  * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
9df62ab5eSBjorn Helgaas  * Copyright (C) 2011 Advanced Micro Devices,
10db3c33c6SJoerg Roedel  */
11db3c33c6SJoerg Roedel 
12363c75dbSPaul Gortmaker #include <linux/export.h>
13db3c33c6SJoerg Roedel #include <linux/pci-ats.h>
14db3c33c6SJoerg Roedel #include <linux/pci.h>
158c451945SJames Bottomley #include <linux/slab.h>
16db3c33c6SJoerg Roedel 
17db3c33c6SJoerg Roedel #include "pci.h"
18db3c33c6SJoerg Roedel 
pci_ats_init(struct pci_dev * dev)19afdd596cSBjorn Helgaas void pci_ats_init(struct pci_dev *dev)
20db3c33c6SJoerg Roedel {
21db3c33c6SJoerg Roedel 	int pos;
22db3c33c6SJoerg Roedel 
23cef74409SGil Kupfer 	if (pci_ats_disabled())
24cef74409SGil Kupfer 		return;
25cef74409SGil Kupfer 
26db3c33c6SJoerg Roedel 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS);
27db3c33c6SJoerg Roedel 	if (!pos)
28edc90feeSBjorn Helgaas 		return;
29db3c33c6SJoerg Roedel 
30d544d75aSBjorn Helgaas 	dev->ats_cap = pos;
31db3c33c6SJoerg Roedel }
32db3c33c6SJoerg Roedel 
33db3c33c6SJoerg Roedel /**
3452137674SJean-Philippe Brucker  * pci_ats_supported - check if the device can use ATS
3552137674SJean-Philippe Brucker  * @dev: the PCI device
3652137674SJean-Philippe Brucker  *
3752137674SJean-Philippe Brucker  * Returns true if the device supports ATS and is allowed to use it, false
3852137674SJean-Philippe Brucker  * otherwise.
3952137674SJean-Philippe Brucker  */
pci_ats_supported(struct pci_dev * dev)4052137674SJean-Philippe Brucker bool pci_ats_supported(struct pci_dev *dev)
4152137674SJean-Philippe Brucker {
4252137674SJean-Philippe Brucker 	if (!dev->ats_cap)
4352137674SJean-Philippe Brucker 		return false;
4452137674SJean-Philippe Brucker 
4552137674SJean-Philippe Brucker 	return (dev->untrusted == 0);
4652137674SJean-Philippe Brucker }
4752137674SJean-Philippe Brucker EXPORT_SYMBOL_GPL(pci_ats_supported);
4852137674SJean-Philippe Brucker 
4952137674SJean-Philippe Brucker /**
50db3c33c6SJoerg Roedel  * pci_enable_ats - enable the ATS capability
51db3c33c6SJoerg Roedel  * @dev: the PCI device
52db3c33c6SJoerg Roedel  * @ps: the IOMMU page shift
53db3c33c6SJoerg Roedel  *
54db3c33c6SJoerg Roedel  * Returns 0 on success, or negative on failure.
55db3c33c6SJoerg Roedel  */
pci_enable_ats(struct pci_dev * dev,int ps)56db3c33c6SJoerg Roedel int pci_enable_ats(struct pci_dev *dev, int ps)
57db3c33c6SJoerg Roedel {
58db3c33c6SJoerg Roedel 	u16 ctrl;
59c39127dbSBjorn Helgaas 	struct pci_dev *pdev;
60db3c33c6SJoerg Roedel 
6152137674SJean-Philippe Brucker 	if (!pci_ats_supported(dev))
62edc90feeSBjorn Helgaas 		return -EINVAL;
63edc90feeSBjorn Helgaas 
64f7ef1340SBjorn Helgaas 	if (WARN_ON(dev->ats_enabled))
65a021f301SBjorn Helgaas 		return -EBUSY;
66a021f301SBjorn Helgaas 
67db3c33c6SJoerg Roedel 	if (ps < PCI_ATS_MIN_STU)
68db3c33c6SJoerg Roedel 		return -EINVAL;
69db3c33c6SJoerg Roedel 
70edc90feeSBjorn Helgaas 	/*
71edc90feeSBjorn Helgaas 	 * Note that enabling ATS on a VF fails unless it's already enabled
72edc90feeSBjorn Helgaas 	 * with the same STU on the PF.
73edc90feeSBjorn Helgaas 	 */
74db3c33c6SJoerg Roedel 	ctrl = PCI_ATS_CTRL_ENABLE;
75edc90feeSBjorn Helgaas 	if (dev->is_virtfn) {
76c39127dbSBjorn Helgaas 		pdev = pci_physfn(dev);
77d544d75aSBjorn Helgaas 		if (pdev->ats_stu != ps)
78edc90feeSBjorn Helgaas 			return -EINVAL;
79edc90feeSBjorn Helgaas 	} else {
80d544d75aSBjorn Helgaas 		dev->ats_stu = ps;
81d544d75aSBjorn Helgaas 		ctrl |= PCI_ATS_CTRL_STU(dev->ats_stu - PCI_ATS_MIN_STU);
82edc90feeSBjorn Helgaas 	}
83d544d75aSBjorn Helgaas 	pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
84db3c33c6SJoerg Roedel 
85d544d75aSBjorn Helgaas 	dev->ats_enabled = 1;
86db3c33c6SJoerg Roedel 	return 0;
87db3c33c6SJoerg Roedel }
88bb950bcaSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(pci_enable_ats);
89db3c33c6SJoerg Roedel 
90db3c33c6SJoerg Roedel /**
91db3c33c6SJoerg Roedel  * pci_disable_ats - disable the ATS capability
92db3c33c6SJoerg Roedel  * @dev: the PCI device
93db3c33c6SJoerg Roedel  */
pci_disable_ats(struct pci_dev * dev)94db3c33c6SJoerg Roedel void pci_disable_ats(struct pci_dev *dev)
95db3c33c6SJoerg Roedel {
96db3c33c6SJoerg Roedel 	u16 ctrl;
97db3c33c6SJoerg Roedel 
98f7ef1340SBjorn Helgaas 	if (WARN_ON(!dev->ats_enabled))
99a021f301SBjorn Helgaas 		return;
100db3c33c6SJoerg Roedel 
101d544d75aSBjorn Helgaas 	pci_read_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, &ctrl);
102db3c33c6SJoerg Roedel 	ctrl &= ~PCI_ATS_CTRL_ENABLE;
103d544d75aSBjorn Helgaas 	pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
104db3c33c6SJoerg Roedel 
105d544d75aSBjorn Helgaas 	dev->ats_enabled = 0;
106db3c33c6SJoerg Roedel }
107bb950bcaSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(pci_disable_ats);
108db3c33c6SJoerg Roedel 
pci_restore_ats_state(struct pci_dev * dev)1091900ca13SHao, Xudong void pci_restore_ats_state(struct pci_dev *dev)
1101900ca13SHao, Xudong {
1111900ca13SHao, Xudong 	u16 ctrl;
1121900ca13SHao, Xudong 
113f7ef1340SBjorn Helgaas 	if (!dev->ats_enabled)
1141900ca13SHao, Xudong 		return;
1151900ca13SHao, Xudong 
1161900ca13SHao, Xudong 	ctrl = PCI_ATS_CTRL_ENABLE;
1171900ca13SHao, Xudong 	if (!dev->is_virtfn)
118d544d75aSBjorn Helgaas 		ctrl |= PCI_ATS_CTRL_STU(dev->ats_stu - PCI_ATS_MIN_STU);
119d544d75aSBjorn Helgaas 	pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
1201900ca13SHao, Xudong }
1211900ca13SHao, Xudong 
122db3c33c6SJoerg Roedel /**
123db3c33c6SJoerg Roedel  * pci_ats_queue_depth - query the ATS Invalidate Queue Depth
124db3c33c6SJoerg Roedel  * @dev: the PCI device
125db3c33c6SJoerg Roedel  *
126db3c33c6SJoerg Roedel  * Returns the queue depth on success, or negative on failure.
127db3c33c6SJoerg Roedel  *
128db3c33c6SJoerg Roedel  * The ATS spec uses 0 in the Invalidate Queue Depth field to
129db3c33c6SJoerg Roedel  * indicate that the function can accept 32 Invalidate Request.
130db3c33c6SJoerg Roedel  * But here we use the `real' values (i.e. 1~32) for the Queue
131db3c33c6SJoerg Roedel  * Depth; and 0 indicates the function shares the Queue with
132db3c33c6SJoerg Roedel  * other functions (doesn't exclusively own a Queue).
133db3c33c6SJoerg Roedel  */
pci_ats_queue_depth(struct pci_dev * dev)134db3c33c6SJoerg Roedel int pci_ats_queue_depth(struct pci_dev *dev)
135db3c33c6SJoerg Roedel {
136a71f938fSBjorn Helgaas 	u16 cap;
137a71f938fSBjorn Helgaas 
1383c765399SBjorn Helgaas 	if (!dev->ats_cap)
1393c765399SBjorn Helgaas 		return -EINVAL;
1403c765399SBjorn Helgaas 
141db3c33c6SJoerg Roedel 	if (dev->is_virtfn)
142db3c33c6SJoerg Roedel 		return 0;
143db3c33c6SJoerg Roedel 
144a71f938fSBjorn Helgaas 	pci_read_config_word(dev, dev->ats_cap + PCI_ATS_CAP, &cap);
145a71f938fSBjorn Helgaas 	return PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : PCI_ATS_MAX_QDEP;
146db3c33c6SJoerg Roedel }
147c320b976SJoerg Roedel 
1488c938ddcSKuppuswamy Sathyanarayanan /**
1498c938ddcSKuppuswamy Sathyanarayanan  * pci_ats_page_aligned - Return Page Aligned Request bit status.
1508c938ddcSKuppuswamy Sathyanarayanan  * @pdev: the PCI device
1518c938ddcSKuppuswamy Sathyanarayanan  *
1528c938ddcSKuppuswamy Sathyanarayanan  * Returns 1, if the Untranslated Addresses generated by the device
1538c938ddcSKuppuswamy Sathyanarayanan  * are always aligned or 0 otherwise.
1548c938ddcSKuppuswamy Sathyanarayanan  *
1558c938ddcSKuppuswamy Sathyanarayanan  * Per PCIe spec r4.0, sec 10.5.1.2, if the Page Aligned Request bit
1568c938ddcSKuppuswamy Sathyanarayanan  * is set, it indicates the Untranslated Addresses generated by the
1578c938ddcSKuppuswamy Sathyanarayanan  * device are always aligned to a 4096 byte boundary.
1588c938ddcSKuppuswamy Sathyanarayanan  */
pci_ats_page_aligned(struct pci_dev * pdev)1598c938ddcSKuppuswamy Sathyanarayanan int pci_ats_page_aligned(struct pci_dev *pdev)
1608c938ddcSKuppuswamy Sathyanarayanan {
1618c938ddcSKuppuswamy Sathyanarayanan 	u16 cap;
1628c938ddcSKuppuswamy Sathyanarayanan 
1638c938ddcSKuppuswamy Sathyanarayanan 	if (!pdev->ats_cap)
1648c938ddcSKuppuswamy Sathyanarayanan 		return 0;
1658c938ddcSKuppuswamy Sathyanarayanan 
1668c938ddcSKuppuswamy Sathyanarayanan 	pci_read_config_word(pdev, pdev->ats_cap + PCI_ATS_CAP, &cap);
1678c938ddcSKuppuswamy Sathyanarayanan 
1688c938ddcSKuppuswamy Sathyanarayanan 	if (cap & PCI_ATS_CAP_PAGE_ALIGNED)
1698c938ddcSKuppuswamy Sathyanarayanan 		return 1;
1708c938ddcSKuppuswamy Sathyanarayanan 
1718c938ddcSKuppuswamy Sathyanarayanan 	return 0;
1728c938ddcSKuppuswamy Sathyanarayanan }
1738c938ddcSKuppuswamy Sathyanarayanan 
174c320b976SJoerg Roedel #ifdef CONFIG_PCI_PRI
pci_pri_init(struct pci_dev * pdev)175c065190bSKuppuswamy Sathyanarayanan void pci_pri_init(struct pci_dev *pdev)
176c065190bSKuppuswamy Sathyanarayanan {
177e5adf79aSBjorn Helgaas 	u16 status;
178e5adf79aSBjorn Helgaas 
179c065190bSKuppuswamy Sathyanarayanan 	pdev->pri_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
180e5adf79aSBjorn Helgaas 
181e5adf79aSBjorn Helgaas 	if (!pdev->pri_cap)
182e5adf79aSBjorn Helgaas 		return;
183e5adf79aSBjorn Helgaas 
184e5adf79aSBjorn Helgaas 	pci_read_config_word(pdev, pdev->pri_cap + PCI_PRI_STATUS, &status);
185e5adf79aSBjorn Helgaas 	if (status & PCI_PRI_STATUS_PASID)
186e5adf79aSBjorn Helgaas 		pdev->pasid_required = 1;
187c065190bSKuppuswamy Sathyanarayanan }
188c065190bSKuppuswamy Sathyanarayanan 
189c320b976SJoerg Roedel /**
190c320b976SJoerg Roedel  * pci_enable_pri - Enable PRI capability
191c320b976SJoerg Roedel  * @pdev: PCI device structure
1929b41d19aSKrzysztof Kozlowski  * @reqs: outstanding requests
193c320b976SJoerg Roedel  *
194c320b976SJoerg Roedel  * Returns 0 on success, negative value on error
195c320b976SJoerg Roedel  */
pci_enable_pri(struct pci_dev * pdev,u32 reqs)196c320b976SJoerg Roedel int pci_enable_pri(struct pci_dev *pdev, u32 reqs)
197c320b976SJoerg Roedel {
198c320b976SJoerg Roedel 	u16 control, status;
199c320b976SJoerg Roedel 	u32 max_requests;
200c065190bSKuppuswamy Sathyanarayanan 	int pri = pdev->pri_cap;
201c320b976SJoerg Roedel 
2029bf49e36SKuppuswamy Sathyanarayanan 	/*
2039bf49e36SKuppuswamy Sathyanarayanan 	 * VFs must not implement the PRI Capability.  If their PF
2049bf49e36SKuppuswamy Sathyanarayanan 	 * implements PRI, it is shared by the VFs, so if the PF PRI is
2059bf49e36SKuppuswamy Sathyanarayanan 	 * enabled, it is also enabled for the VF.
2069bf49e36SKuppuswamy Sathyanarayanan 	 */
2079bf49e36SKuppuswamy Sathyanarayanan 	if (pdev->is_virtfn) {
2089bf49e36SKuppuswamy Sathyanarayanan 		if (pci_physfn(pdev)->pri_enabled)
2099bf49e36SKuppuswamy Sathyanarayanan 			return 0;
2109bf49e36SKuppuswamy Sathyanarayanan 		return -EINVAL;
2119bf49e36SKuppuswamy Sathyanarayanan 	}
2129bf49e36SKuppuswamy Sathyanarayanan 
213a4f4fa68SJean-Philippe Brucker 	if (WARN_ON(pdev->pri_enabled))
214a4f4fa68SJean-Philippe Brucker 		return -EBUSY;
215a4f4fa68SJean-Philippe Brucker 
216c065190bSKuppuswamy Sathyanarayanan 	if (!pri)
217c320b976SJoerg Roedel 		return -EINVAL;
218c320b976SJoerg Roedel 
219c065190bSKuppuswamy Sathyanarayanan 	pci_read_config_word(pdev, pri + PCI_PRI_STATUS, &status);
2204ebeb1ecSCQ Tang 	if (!(status & PCI_PRI_STATUS_STOPPED))
221c320b976SJoerg Roedel 		return -EBUSY;
222c320b976SJoerg Roedel 
223c065190bSKuppuswamy Sathyanarayanan 	pci_read_config_dword(pdev, pri + PCI_PRI_MAX_REQ, &max_requests);
224c320b976SJoerg Roedel 	reqs = min(max_requests, reqs);
2254ebeb1ecSCQ Tang 	pdev->pri_reqs_alloc = reqs;
226c065190bSKuppuswamy Sathyanarayanan 	pci_write_config_dword(pdev, pri + PCI_PRI_ALLOC_REQ, reqs);
227c320b976SJoerg Roedel 
2284ebeb1ecSCQ Tang 	control = PCI_PRI_CTRL_ENABLE;
229c065190bSKuppuswamy Sathyanarayanan 	pci_write_config_word(pdev, pri + PCI_PRI_CTRL, control);
230c320b976SJoerg Roedel 
231a4f4fa68SJean-Philippe Brucker 	pdev->pri_enabled = 1;
232a4f4fa68SJean-Philippe Brucker 
233c320b976SJoerg Roedel 	return 0;
234c320b976SJoerg Roedel }
235c320b976SJoerg Roedel 
236c320b976SJoerg Roedel /**
237c320b976SJoerg Roedel  * pci_disable_pri - Disable PRI capability
238c320b976SJoerg Roedel  * @pdev: PCI device structure
239c320b976SJoerg Roedel  *
240c320b976SJoerg Roedel  * Only clears the enabled-bit, regardless of its former value
241c320b976SJoerg Roedel  */
pci_disable_pri(struct pci_dev * pdev)242c320b976SJoerg Roedel void pci_disable_pri(struct pci_dev *pdev)
243c320b976SJoerg Roedel {
244c320b976SJoerg Roedel 	u16 control;
245c065190bSKuppuswamy Sathyanarayanan 	int pri = pdev->pri_cap;
246c320b976SJoerg Roedel 
2479bf49e36SKuppuswamy Sathyanarayanan 	/* VFs share the PF PRI */
2489bf49e36SKuppuswamy Sathyanarayanan 	if (pdev->is_virtfn)
2499bf49e36SKuppuswamy Sathyanarayanan 		return;
2509bf49e36SKuppuswamy Sathyanarayanan 
251a4f4fa68SJean-Philippe Brucker 	if (WARN_ON(!pdev->pri_enabled))
252a4f4fa68SJean-Philippe Brucker 		return;
253a4f4fa68SJean-Philippe Brucker 
254c065190bSKuppuswamy Sathyanarayanan 	if (!pri)
255c320b976SJoerg Roedel 		return;
256c320b976SJoerg Roedel 
257c065190bSKuppuswamy Sathyanarayanan 	pci_read_config_word(pdev, pri + PCI_PRI_CTRL, &control);
25891f57d5eSAlex Williamson 	control &= ~PCI_PRI_CTRL_ENABLE;
259c065190bSKuppuswamy Sathyanarayanan 	pci_write_config_word(pdev, pri + PCI_PRI_CTRL, control);
260a4f4fa68SJean-Philippe Brucker 
261a4f4fa68SJean-Philippe Brucker 	pdev->pri_enabled = 0;
262c320b976SJoerg Roedel }
263c320b976SJoerg Roedel EXPORT_SYMBOL_GPL(pci_disable_pri);
264c320b976SJoerg Roedel 
265c320b976SJoerg Roedel /**
2664ebeb1ecSCQ Tang  * pci_restore_pri_state - Restore PRI
2674ebeb1ecSCQ Tang  * @pdev: PCI device structure
2684ebeb1ecSCQ Tang  */
pci_restore_pri_state(struct pci_dev * pdev)2694ebeb1ecSCQ Tang void pci_restore_pri_state(struct pci_dev *pdev)
2704ebeb1ecSCQ Tang {
2714ebeb1ecSCQ Tang 	u16 control = PCI_PRI_CTRL_ENABLE;
2724ebeb1ecSCQ Tang 	u32 reqs = pdev->pri_reqs_alloc;
273c065190bSKuppuswamy Sathyanarayanan 	int pri = pdev->pri_cap;
2744ebeb1ecSCQ Tang 
2759bf49e36SKuppuswamy Sathyanarayanan 	if (pdev->is_virtfn)
2769bf49e36SKuppuswamy Sathyanarayanan 		return;
2779bf49e36SKuppuswamy Sathyanarayanan 
2784ebeb1ecSCQ Tang 	if (!pdev->pri_enabled)
2794ebeb1ecSCQ Tang 		return;
2804ebeb1ecSCQ Tang 
281c065190bSKuppuswamy Sathyanarayanan 	if (!pri)
2824ebeb1ecSCQ Tang 		return;
2834ebeb1ecSCQ Tang 
284c065190bSKuppuswamy Sathyanarayanan 	pci_write_config_dword(pdev, pri + PCI_PRI_ALLOC_REQ, reqs);
285c065190bSKuppuswamy Sathyanarayanan 	pci_write_config_word(pdev, pri + PCI_PRI_CTRL, control);
2864ebeb1ecSCQ Tang }
2874ebeb1ecSCQ Tang 
2884ebeb1ecSCQ Tang /**
289c320b976SJoerg Roedel  * pci_reset_pri - Resets device's PRI state
290c320b976SJoerg Roedel  * @pdev: PCI device structure
291c320b976SJoerg Roedel  *
292c320b976SJoerg Roedel  * The PRI capability must be disabled before this function is called.
293c320b976SJoerg Roedel  * Returns 0 on success, negative value on error.
294c320b976SJoerg Roedel  */
pci_reset_pri(struct pci_dev * pdev)295c320b976SJoerg Roedel int pci_reset_pri(struct pci_dev *pdev)
296c320b976SJoerg Roedel {
297c320b976SJoerg Roedel 	u16 control;
298c065190bSKuppuswamy Sathyanarayanan 	int pri = pdev->pri_cap;
299c320b976SJoerg Roedel 
3009bf49e36SKuppuswamy Sathyanarayanan 	if (pdev->is_virtfn)
3019bf49e36SKuppuswamy Sathyanarayanan 		return 0;
3029bf49e36SKuppuswamy Sathyanarayanan 
303a4f4fa68SJean-Philippe Brucker 	if (WARN_ON(pdev->pri_enabled))
304a4f4fa68SJean-Philippe Brucker 		return -EBUSY;
305a4f4fa68SJean-Philippe Brucker 
306c065190bSKuppuswamy Sathyanarayanan 	if (!pri)
307c320b976SJoerg Roedel 		return -EINVAL;
308c320b976SJoerg Roedel 
3094ebeb1ecSCQ Tang 	control = PCI_PRI_CTRL_RESET;
310c065190bSKuppuswamy Sathyanarayanan 	pci_write_config_word(pdev, pri + PCI_PRI_CTRL, control);
311c320b976SJoerg Roedel 
312c320b976SJoerg Roedel 	return 0;
313c320b976SJoerg Roedel }
3148cbb8a93SBjorn Helgaas 
3158cbb8a93SBjorn Helgaas /**
3168cbb8a93SBjorn Helgaas  * pci_prg_resp_pasid_required - Return PRG Response PASID Required bit
3178cbb8a93SBjorn Helgaas  *				 status.
3188cbb8a93SBjorn Helgaas  * @pdev: PCI device structure
3198cbb8a93SBjorn Helgaas  *
3208cbb8a93SBjorn Helgaas  * Returns 1 if PASID is required in PRG Response Message, 0 otherwise.
3218cbb8a93SBjorn Helgaas  */
pci_prg_resp_pasid_required(struct pci_dev * pdev)3228cbb8a93SBjorn Helgaas int pci_prg_resp_pasid_required(struct pci_dev *pdev)
3238cbb8a93SBjorn Helgaas {
3249bf49e36SKuppuswamy Sathyanarayanan 	if (pdev->is_virtfn)
3259bf49e36SKuppuswamy Sathyanarayanan 		pdev = pci_physfn(pdev);
3269bf49e36SKuppuswamy Sathyanarayanan 
327e5adf79aSBjorn Helgaas 	return pdev->pasid_required;
3288cbb8a93SBjorn Helgaas }
3293f9a7a13SAshok Raj 
3303f9a7a13SAshok Raj /**
3313f9a7a13SAshok Raj  * pci_pri_supported - Check if PRI is supported.
3323f9a7a13SAshok Raj  * @pdev: PCI device structure
3333f9a7a13SAshok Raj  *
3343f9a7a13SAshok Raj  * Returns true if PRI capability is present, false otherwise.
3353f9a7a13SAshok Raj  */
pci_pri_supported(struct pci_dev * pdev)3363f9a7a13SAshok Raj bool pci_pri_supported(struct pci_dev *pdev)
3373f9a7a13SAshok Raj {
3383f9a7a13SAshok Raj 	/* VFs share the PF PRI */
3393f9a7a13SAshok Raj 	if (pci_physfn(pdev)->pri_cap)
3403f9a7a13SAshok Raj 		return true;
3413f9a7a13SAshok Raj 	return false;
3423f9a7a13SAshok Raj }
3433f9a7a13SAshok Raj EXPORT_SYMBOL_GPL(pci_pri_supported);
344c320b976SJoerg Roedel #endif /* CONFIG_PCI_PRI */
345086ac11fSJoerg Roedel 
346086ac11fSJoerg Roedel #ifdef CONFIG_PCI_PASID
pci_pasid_init(struct pci_dev * pdev)347751035b8SKuppuswamy Sathyanarayanan void pci_pasid_init(struct pci_dev *pdev)
348751035b8SKuppuswamy Sathyanarayanan {
349751035b8SKuppuswamy Sathyanarayanan 	pdev->pasid_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
350751035b8SKuppuswamy Sathyanarayanan }
351751035b8SKuppuswamy Sathyanarayanan 
352086ac11fSJoerg Roedel /**
353086ac11fSJoerg Roedel  * pci_enable_pasid - Enable the PASID capability
354086ac11fSJoerg Roedel  * @pdev: PCI device structure
355086ac11fSJoerg Roedel  * @features: Features to enable
356086ac11fSJoerg Roedel  *
357086ac11fSJoerg Roedel  * Returns 0 on success, negative value on error. This function checks
358086ac11fSJoerg Roedel  * whether the features are actually supported by the device and returns
359086ac11fSJoerg Roedel  * an error if not.
360086ac11fSJoerg Roedel  */
pci_enable_pasid(struct pci_dev * pdev,int features)361086ac11fSJoerg Roedel int pci_enable_pasid(struct pci_dev *pdev, int features)
362086ac11fSJoerg Roedel {
363086ac11fSJoerg Roedel 	u16 control, supported;
364751035b8SKuppuswamy Sathyanarayanan 	int pasid = pdev->pasid_cap;
365086ac11fSJoerg Roedel 
3662b0ae7ccSKuppuswamy Sathyanarayanan 	/*
3672b0ae7ccSKuppuswamy Sathyanarayanan 	 * VFs must not implement the PASID Capability, but if a PF
3682b0ae7ccSKuppuswamy Sathyanarayanan 	 * supports PASID, its VFs share the PF PASID configuration.
3692b0ae7ccSKuppuswamy Sathyanarayanan 	 */
3702b0ae7ccSKuppuswamy Sathyanarayanan 	if (pdev->is_virtfn) {
3712b0ae7ccSKuppuswamy Sathyanarayanan 		if (pci_physfn(pdev)->pasid_enabled)
3722b0ae7ccSKuppuswamy Sathyanarayanan 			return 0;
3732b0ae7ccSKuppuswamy Sathyanarayanan 		return -EINVAL;
3742b0ae7ccSKuppuswamy Sathyanarayanan 	}
3752b0ae7ccSKuppuswamy Sathyanarayanan 
376a4f4fa68SJean-Philippe Brucker 	if (WARN_ON(pdev->pasid_enabled))
377a4f4fa68SJean-Philippe Brucker 		return -EBUSY;
378a4f4fa68SJean-Philippe Brucker 
3798c09e896SZhangfei Gao 	if (!pdev->eetlp_prefix_path && !pdev->pasid_no_tlp)
3807ce3f912SSinan Kaya 		return -EINVAL;
3817ce3f912SSinan Kaya 
382751035b8SKuppuswamy Sathyanarayanan 	if (!pasid)
383086ac11fSJoerg Roedel 		return -EINVAL;
384086ac11fSJoerg Roedel 
385*201007efSLu Baolu 	if (!pci_acs_path_enabled(pdev, NULL, PCI_ACS_RR | PCI_ACS_UF))
386*201007efSLu Baolu 		return -EINVAL;
387*201007efSLu Baolu 
388751035b8SKuppuswamy Sathyanarayanan 	pci_read_config_word(pdev, pasid + PCI_PASID_CAP, &supported);
38991f57d5eSAlex Williamson 	supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
390086ac11fSJoerg Roedel 
391086ac11fSJoerg Roedel 	/* User wants to enable anything unsupported? */
392086ac11fSJoerg Roedel 	if ((supported & features) != features)
393086ac11fSJoerg Roedel 		return -EINVAL;
394086ac11fSJoerg Roedel 
39591f57d5eSAlex Williamson 	control = PCI_PASID_CTRL_ENABLE | features;
3964ebeb1ecSCQ Tang 	pdev->pasid_features = features;
397086ac11fSJoerg Roedel 
398751035b8SKuppuswamy Sathyanarayanan 	pci_write_config_word(pdev, pasid + PCI_PASID_CTRL, control);
399086ac11fSJoerg Roedel 
400a4f4fa68SJean-Philippe Brucker 	pdev->pasid_enabled = 1;
401a4f4fa68SJean-Philippe Brucker 
402086ac11fSJoerg Roedel 	return 0;
403086ac11fSJoerg Roedel }
4047682ce2bSJean-Philippe Brucker EXPORT_SYMBOL_GPL(pci_enable_pasid);
405086ac11fSJoerg Roedel 
406086ac11fSJoerg Roedel /**
407086ac11fSJoerg Roedel  * pci_disable_pasid - Disable the PASID capability
408086ac11fSJoerg Roedel  * @pdev: PCI device structure
409086ac11fSJoerg Roedel  */
pci_disable_pasid(struct pci_dev * pdev)410086ac11fSJoerg Roedel void pci_disable_pasid(struct pci_dev *pdev)
411086ac11fSJoerg Roedel {
412086ac11fSJoerg Roedel 	u16 control = 0;
413751035b8SKuppuswamy Sathyanarayanan 	int pasid = pdev->pasid_cap;
414086ac11fSJoerg Roedel 
4152b0ae7ccSKuppuswamy Sathyanarayanan 	/* VFs share the PF PASID configuration */
4162b0ae7ccSKuppuswamy Sathyanarayanan 	if (pdev->is_virtfn)
4172b0ae7ccSKuppuswamy Sathyanarayanan 		return;
4182b0ae7ccSKuppuswamy Sathyanarayanan 
419a4f4fa68SJean-Philippe Brucker 	if (WARN_ON(!pdev->pasid_enabled))
420a4f4fa68SJean-Philippe Brucker 		return;
421a4f4fa68SJean-Philippe Brucker 
422751035b8SKuppuswamy Sathyanarayanan 	if (!pasid)
423086ac11fSJoerg Roedel 		return;
424086ac11fSJoerg Roedel 
425751035b8SKuppuswamy Sathyanarayanan 	pci_write_config_word(pdev, pasid + PCI_PASID_CTRL, control);
426a4f4fa68SJean-Philippe Brucker 
427a4f4fa68SJean-Philippe Brucker 	pdev->pasid_enabled = 0;
428086ac11fSJoerg Roedel }
4297682ce2bSJean-Philippe Brucker EXPORT_SYMBOL_GPL(pci_disable_pasid);
430086ac11fSJoerg Roedel 
431086ac11fSJoerg Roedel /**
4324ebeb1ecSCQ Tang  * pci_restore_pasid_state - Restore PASID capabilities
4334ebeb1ecSCQ Tang  * @pdev: PCI device structure
4344ebeb1ecSCQ Tang  */
pci_restore_pasid_state(struct pci_dev * pdev)4354ebeb1ecSCQ Tang void pci_restore_pasid_state(struct pci_dev *pdev)
4364ebeb1ecSCQ Tang {
4374ebeb1ecSCQ Tang 	u16 control;
438751035b8SKuppuswamy Sathyanarayanan 	int pasid = pdev->pasid_cap;
4394ebeb1ecSCQ Tang 
4402b0ae7ccSKuppuswamy Sathyanarayanan 	if (pdev->is_virtfn)
4412b0ae7ccSKuppuswamy Sathyanarayanan 		return;
4422b0ae7ccSKuppuswamy Sathyanarayanan 
4434ebeb1ecSCQ Tang 	if (!pdev->pasid_enabled)
4444ebeb1ecSCQ Tang 		return;
4454ebeb1ecSCQ Tang 
446751035b8SKuppuswamy Sathyanarayanan 	if (!pasid)
4474ebeb1ecSCQ Tang 		return;
4484ebeb1ecSCQ Tang 
4494ebeb1ecSCQ Tang 	control = PCI_PASID_CTRL_ENABLE | pdev->pasid_features;
450751035b8SKuppuswamy Sathyanarayanan 	pci_write_config_word(pdev, pasid + PCI_PASID_CTRL, control);
4514ebeb1ecSCQ Tang }
4524ebeb1ecSCQ Tang 
4534ebeb1ecSCQ Tang /**
454086ac11fSJoerg Roedel  * pci_pasid_features - Check which PASID features are supported
455086ac11fSJoerg Roedel  * @pdev: PCI device structure
456086ac11fSJoerg Roedel  *
457086ac11fSJoerg Roedel  * Returns a negative value when no PASI capability is present.
458086ac11fSJoerg Roedel  * Otherwise is returns a bitmask with supported features. Current
459086ac11fSJoerg Roedel  * features reported are:
46091f57d5eSAlex Williamson  * PCI_PASID_CAP_EXEC - Execute permission supported
461f7625980SBjorn Helgaas  * PCI_PASID_CAP_PRIV - Privileged mode supported
462086ac11fSJoerg Roedel  */
pci_pasid_features(struct pci_dev * pdev)463086ac11fSJoerg Roedel int pci_pasid_features(struct pci_dev *pdev)
464086ac11fSJoerg Roedel {
465086ac11fSJoerg Roedel 	u16 supported;
4662e34673bSKuppuswamy Sathyanarayanan 	int pasid;
467086ac11fSJoerg Roedel 
4682b0ae7ccSKuppuswamy Sathyanarayanan 	if (pdev->is_virtfn)
4692b0ae7ccSKuppuswamy Sathyanarayanan 		pdev = pci_physfn(pdev);
4702b0ae7ccSKuppuswamy Sathyanarayanan 
4712e34673bSKuppuswamy Sathyanarayanan 	pasid = pdev->pasid_cap;
472751035b8SKuppuswamy Sathyanarayanan 	if (!pasid)
473086ac11fSJoerg Roedel 		return -EINVAL;
474086ac11fSJoerg Roedel 
475751035b8SKuppuswamy Sathyanarayanan 	pci_read_config_word(pdev, pasid + PCI_PASID_CAP, &supported);
476086ac11fSJoerg Roedel 
47791f57d5eSAlex Williamson 	supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
478086ac11fSJoerg Roedel 
479086ac11fSJoerg Roedel 	return supported;
480086ac11fSJoerg Roedel }
4817682ce2bSJean-Philippe Brucker EXPORT_SYMBOL_GPL(pci_pasid_features);
482086ac11fSJoerg Roedel 
483086ac11fSJoerg Roedel #define PASID_NUMBER_SHIFT	8
484086ac11fSJoerg Roedel #define PASID_NUMBER_MASK	(0x1f << PASID_NUMBER_SHIFT)
485086ac11fSJoerg Roedel /**
48643395d9eSKrzysztof Wilczyński  * pci_max_pasids - Get maximum number of PASIDs supported by device
487086ac11fSJoerg Roedel  * @pdev: PCI device structure
488086ac11fSJoerg Roedel  *
489086ac11fSJoerg Roedel  * Returns negative value when PASID capability is not present.
490f6b6aefeSBjorn Helgaas  * Otherwise it returns the number of supported PASIDs.
491086ac11fSJoerg Roedel  */
pci_max_pasids(struct pci_dev * pdev)492086ac11fSJoerg Roedel int pci_max_pasids(struct pci_dev *pdev)
493086ac11fSJoerg Roedel {
494086ac11fSJoerg Roedel 	u16 supported;
4952e34673bSKuppuswamy Sathyanarayanan 	int pasid;
496086ac11fSJoerg Roedel 
4972b0ae7ccSKuppuswamy Sathyanarayanan 	if (pdev->is_virtfn)
4982b0ae7ccSKuppuswamy Sathyanarayanan 		pdev = pci_physfn(pdev);
4992b0ae7ccSKuppuswamy Sathyanarayanan 
5002e34673bSKuppuswamy Sathyanarayanan 	pasid = pdev->pasid_cap;
501751035b8SKuppuswamy Sathyanarayanan 	if (!pasid)
502086ac11fSJoerg Roedel 		return -EINVAL;
503086ac11fSJoerg Roedel 
504751035b8SKuppuswamy Sathyanarayanan 	pci_read_config_word(pdev, pasid + PCI_PASID_CAP, &supported);
505086ac11fSJoerg Roedel 
506086ac11fSJoerg Roedel 	supported = (supported & PASID_NUMBER_MASK) >> PASID_NUMBER_SHIFT;
507086ac11fSJoerg Roedel 
508086ac11fSJoerg Roedel 	return (1 << supported);
509086ac11fSJoerg Roedel }
5107682ce2bSJean-Philippe Brucker EXPORT_SYMBOL_GPL(pci_max_pasids);
511086ac11fSJoerg Roedel #endif /* CONFIG_PCI_PASID */
512