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
19afdd596cSBjorn Helgaasvoid 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
3352137674SJean-Philippe Brucker/**
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 */
4052137674SJean-Philippe Bruckerbool 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 BruckerEXPORT_SYMBOL_GPL(pci_ats_supported);
4852137674SJean-Philippe Brucker
49db3c33c6SJoerg Roedel/**
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 */
56db3c33c6SJoerg Roedelint 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	 */
74edc90feeSBjorn Helgaas	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);
82db3c33c6SJoerg Roedel	}
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-HartmanEXPORT_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 */
94db3c33c6SJoerg Roedelvoid 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-HartmanEXPORT_SYMBOL_GPL(pci_disable_ats);
108db3c33c6SJoerg Roedel
1091900ca13SHao, Xudongvoid 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 */
134db3c33c6SJoerg Roedelint 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, 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 */
1598c938ddcSKuppuswamy Sathyanarayananint 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
175c065190bSKuppuswamy Sathyanarayananvoid 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
1919b41d19aSKrzysztof Kozlowski * @pdev: PCI device structure
1929b41d19aSKrzysztof Kozlowski * @reqs: outstanding requests
193c320b976SJoerg Roedel *
194c320b976SJoerg Roedel * Returns 0 on success, negative value on error
195c320b976SJoerg Roedel */
196c320b976SJoerg Roedelint 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 */
242c320b976SJoerg Roedelvoid 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 RoedelEXPORT_SYMBOL_GPL(pci_disable_pri);
264c320b976SJoerg Roedel
2654ebeb1ecSCQ Tang/**
2664ebeb1ecSCQ Tang * pci_restore_pri_state - Restore PRI
2674ebeb1ecSCQ Tang * @pdev: PCI device structure
2684ebeb1ecSCQ Tang */
2694ebeb1ecSCQ Tangvoid 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
288c320b976SJoerg Roedel/**
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 */
295c320b976SJoerg Roedelint 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 */
3228cbb8a93SBjorn Helgaasint 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 */
3363f9a7a13SAshok Rajbool 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 RajEXPORT_SYMBOL_GPL(pci_pri_supported);
344c320b976SJoerg Roedel#endif /* CONFIG_PCI_PRI */
345086ac11fSJoerg Roedel
346086ac11fSJoerg Roedel#ifdef CONFIG_PCI_PASID
347751035b8SKuppuswamy Sathyanarayananvoid 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 */
361086ac11fSJoerg Roedelint 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
385751035b8SKuppuswamy Sathyanarayanan	pci_read_config_word(pdev, pasid + PCI_PASID_CAP, &supported);
38691f57d5eSAlex Williamson	supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
387086ac11fSJoerg Roedel
388086ac11fSJoerg Roedel	/* User wants to enable anything unsupported? */
389086ac11fSJoerg Roedel	if ((supported & features) != features)
390086ac11fSJoerg Roedel		return -EINVAL;
391086ac11fSJoerg Roedel
39291f57d5eSAlex Williamson	control = PCI_PASID_CTRL_ENABLE | features;
3934ebeb1ecSCQ Tang	pdev->pasid_features = features;
394086ac11fSJoerg Roedel
395751035b8SKuppuswamy Sathyanarayanan	pci_write_config_word(pdev, pasid + PCI_PASID_CTRL, control);
396086ac11fSJoerg Roedel
397a4f4fa68SJean-Philippe Brucker	pdev->pasid_enabled = 1;
398a4f4fa68SJean-Philippe Brucker
399086ac11fSJoerg Roedel	return 0;
400086ac11fSJoerg Roedel}
4017682ce2bSJean-Philippe BruckerEXPORT_SYMBOL_GPL(pci_enable_pasid);
402086ac11fSJoerg Roedel
403086ac11fSJoerg Roedel/**
404086ac11fSJoerg Roedel * pci_disable_pasid - Disable the PASID capability
405086ac11fSJoerg Roedel * @pdev: PCI device structure
406086ac11fSJoerg Roedel */
407086ac11fSJoerg Roedelvoid pci_disable_pasid(struct pci_dev *pdev)
408086ac11fSJoerg Roedel{
409086ac11fSJoerg Roedel	u16 control = 0;
410751035b8SKuppuswamy Sathyanarayanan	int pasid = pdev->pasid_cap;
411086ac11fSJoerg Roedel
4122b0ae7ccSKuppuswamy Sathyanarayanan	/* VFs share the PF PASID configuration */
4132b0ae7ccSKuppuswamy Sathyanarayanan	if (pdev->is_virtfn)
4142b0ae7ccSKuppuswamy Sathyanarayanan		return;
4152b0ae7ccSKuppuswamy Sathyanarayanan
416a4f4fa68SJean-Philippe Brucker	if (WARN_ON(!pdev->pasid_enabled))
417a4f4fa68SJean-Philippe Brucker		return;
418a4f4fa68SJean-Philippe Brucker
419751035b8SKuppuswamy Sathyanarayanan	if (!pasid)
420086ac11fSJoerg Roedel		return;
421086ac11fSJoerg Roedel
422751035b8SKuppuswamy Sathyanarayanan	pci_write_config_word(pdev, pasid + PCI_PASID_CTRL, control);
423a4f4fa68SJean-Philippe Brucker
424a4f4fa68SJean-Philippe Brucker	pdev->pasid_enabled = 0;
425086ac11fSJoerg Roedel}
4267682ce2bSJean-Philippe BruckerEXPORT_SYMBOL_GPL(pci_disable_pasid);
427086ac11fSJoerg Roedel
4284ebeb1ecSCQ Tang/**
4294ebeb1ecSCQ Tang * pci_restore_pasid_state - Restore PASID capabilities
4304ebeb1ecSCQ Tang * @pdev: PCI device structure
4314ebeb1ecSCQ Tang */
4324ebeb1ecSCQ Tangvoid pci_restore_pasid_state(struct pci_dev *pdev)
4334ebeb1ecSCQ Tang{
4344ebeb1ecSCQ Tang	u16 control;
435751035b8SKuppuswamy Sathyanarayanan	int pasid = pdev->pasid_cap;
4364ebeb1ecSCQ Tang
4372b0ae7ccSKuppuswamy Sathyanarayanan	if (pdev->is_virtfn)
4382b0ae7ccSKuppuswamy Sathyanarayanan		return;
4392b0ae7ccSKuppuswamy Sathyanarayanan
4404ebeb1ecSCQ Tang	if (!pdev->pasid_enabled)
4414ebeb1ecSCQ Tang		return;
4424ebeb1ecSCQ Tang
443751035b8SKuppuswamy Sathyanarayanan	if (!pasid)
4444ebeb1ecSCQ Tang		return;
4454ebeb1ecSCQ Tang
4464ebeb1ecSCQ Tang	control = PCI_PASID_CTRL_ENABLE | pdev->pasid_features;
447751035b8SKuppuswamy Sathyanarayanan	pci_write_config_word(pdev, pasid + PCI_PASID_CTRL, control);
4484ebeb1ecSCQ Tang}
4494ebeb1ecSCQ Tang
450086ac11fSJoerg Roedel/**
451086ac11fSJoerg Roedel * pci_pasid_features - Check which PASID features are supported
452086ac11fSJoerg Roedel * @pdev: PCI device structure
453086ac11fSJoerg Roedel *
454086ac11fSJoerg Roedel * Returns a negative value when no PASI capability is present.
455086ac11fSJoerg Roedel * Otherwise is returns a bitmask with supported features. Current
456086ac11fSJoerg Roedel * features reported are:
45791f57d5eSAlex Williamson * PCI_PASID_CAP_EXEC - Execute permission supported
458f7625980SBjorn Helgaas * PCI_PASID_CAP_PRIV - Privileged mode supported
459086ac11fSJoerg Roedel */
460086ac11fSJoerg Roedelint pci_pasid_features(struct pci_dev *pdev)
461086ac11fSJoerg Roedel{
462086ac11fSJoerg Roedel	u16 supported;
4632e34673bSKuppuswamy Sathyanarayanan	int pasid;
464086ac11fSJoerg Roedel
4652b0ae7ccSKuppuswamy Sathyanarayanan	if (pdev->is_virtfn)
4662b0ae7ccSKuppuswamy Sathyanarayanan		pdev = pci_physfn(pdev);
4672b0ae7ccSKuppuswamy Sathyanarayanan
4682e34673bSKuppuswamy Sathyanarayanan	pasid = pdev->pasid_cap;
469751035b8SKuppuswamy Sathyanarayanan	if (!pasid)
470086ac11fSJoerg Roedel		return -EINVAL;
471086ac11fSJoerg Roedel
472751035b8SKuppuswamy Sathyanarayanan	pci_read_config_word(pdev, pasid + PCI_PASID_CAP, &supported);
473086ac11fSJoerg Roedel
47491f57d5eSAlex Williamson	supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
475086ac11fSJoerg Roedel
476086ac11fSJoerg Roedel	return supported;
477086ac11fSJoerg Roedel}
4787682ce2bSJean-Philippe BruckerEXPORT_SYMBOL_GPL(pci_pasid_features);
479086ac11fSJoerg Roedel
480086ac11fSJoerg Roedel#define PASID_NUMBER_SHIFT	8
481086ac11fSJoerg Roedel#define PASID_NUMBER_MASK	(0x1f << PASID_NUMBER_SHIFT)
482086ac11fSJoerg Roedel/**
48343395d9eSKrzysztof Wilczyński * pci_max_pasids - Get maximum number of PASIDs supported by device
484086ac11fSJoerg Roedel * @pdev: PCI device structure
485086ac11fSJoerg Roedel *
486086ac11fSJoerg Roedel * Returns negative value when PASID capability is not present.
487f6b6aefeSBjorn Helgaas * Otherwise it returns the number of supported PASIDs.
488086ac11fSJoerg Roedel */
489086ac11fSJoerg Roedelint pci_max_pasids(struct pci_dev *pdev)
490086ac11fSJoerg Roedel{
491086ac11fSJoerg Roedel	u16 supported;
4922e34673bSKuppuswamy Sathyanarayanan	int pasid;
493086ac11fSJoerg Roedel
4942b0ae7ccSKuppuswamy Sathyanarayanan	if (pdev->is_virtfn)
4952b0ae7ccSKuppuswamy Sathyanarayanan		pdev = pci_physfn(pdev);
4962b0ae7ccSKuppuswamy Sathyanarayanan
4972e34673bSKuppuswamy Sathyanarayanan	pasid = pdev->pasid_cap;
498751035b8SKuppuswamy Sathyanarayanan	if (!pasid)
499086ac11fSJoerg Roedel		return -EINVAL;
500086ac11fSJoerg Roedel
501751035b8SKuppuswamy Sathyanarayanan	pci_read_config_word(pdev, pasid + PCI_PASID_CAP, &supported);
502086ac11fSJoerg Roedel
503086ac11fSJoerg Roedel	supported = (supported & PASID_NUMBER_MASK) >> PASID_NUMBER_SHIFT;
504086ac11fSJoerg Roedel