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