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 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 /** 34db3c33c6SJoerg Roedel * pci_enable_ats - enable the ATS capability 35db3c33c6SJoerg Roedel * @dev: the PCI device 36db3c33c6SJoerg Roedel * @ps: the IOMMU page shift 37db3c33c6SJoerg Roedel * 38db3c33c6SJoerg Roedel * Returns 0 on success, or negative on failure. 39db3c33c6SJoerg Roedel */ 40db3c33c6SJoerg Roedel int pci_enable_ats(struct pci_dev *dev, int ps) 41db3c33c6SJoerg Roedel { 42db3c33c6SJoerg Roedel u16 ctrl; 43c39127dbSBjorn Helgaas struct pci_dev *pdev; 44db3c33c6SJoerg Roedel 45d544d75aSBjorn Helgaas if (!dev->ats_cap) 46edc90feeSBjorn Helgaas return -EINVAL; 47edc90feeSBjorn Helgaas 48f7ef1340SBjorn Helgaas if (WARN_ON(dev->ats_enabled)) 49a021f301SBjorn Helgaas return -EBUSY; 50a021f301SBjorn Helgaas 51db3c33c6SJoerg Roedel if (ps < PCI_ATS_MIN_STU) 52db3c33c6SJoerg Roedel return -EINVAL; 53db3c33c6SJoerg Roedel 54edc90feeSBjorn Helgaas /* 55edc90feeSBjorn Helgaas * Note that enabling ATS on a VF fails unless it's already enabled 56edc90feeSBjorn Helgaas * with the same STU on the PF. 57edc90feeSBjorn Helgaas */ 58db3c33c6SJoerg Roedel ctrl = PCI_ATS_CTRL_ENABLE; 59edc90feeSBjorn Helgaas if (dev->is_virtfn) { 60c39127dbSBjorn Helgaas pdev = pci_physfn(dev); 61d544d75aSBjorn Helgaas if (pdev->ats_stu != ps) 62edc90feeSBjorn Helgaas return -EINVAL; 63edc90feeSBjorn Helgaas } else { 64d544d75aSBjorn Helgaas dev->ats_stu = ps; 65d544d75aSBjorn Helgaas ctrl |= PCI_ATS_CTRL_STU(dev->ats_stu - PCI_ATS_MIN_STU); 66edc90feeSBjorn Helgaas } 67d544d75aSBjorn Helgaas pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl); 68db3c33c6SJoerg Roedel 69d544d75aSBjorn Helgaas dev->ats_enabled = 1; 70db3c33c6SJoerg Roedel return 0; 71db3c33c6SJoerg Roedel } 72db3c33c6SJoerg Roedel 73db3c33c6SJoerg Roedel /** 74db3c33c6SJoerg Roedel * pci_disable_ats - disable the ATS capability 75db3c33c6SJoerg Roedel * @dev: the PCI device 76db3c33c6SJoerg Roedel */ 77db3c33c6SJoerg Roedel void pci_disable_ats(struct pci_dev *dev) 78db3c33c6SJoerg Roedel { 79db3c33c6SJoerg Roedel u16 ctrl; 80db3c33c6SJoerg Roedel 81f7ef1340SBjorn Helgaas if (WARN_ON(!dev->ats_enabled)) 82a021f301SBjorn Helgaas return; 83db3c33c6SJoerg Roedel 84d544d75aSBjorn Helgaas pci_read_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, &ctrl); 85db3c33c6SJoerg Roedel ctrl &= ~PCI_ATS_CTRL_ENABLE; 86d544d75aSBjorn Helgaas pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl); 87db3c33c6SJoerg Roedel 88d544d75aSBjorn Helgaas dev->ats_enabled = 0; 89db3c33c6SJoerg Roedel } 90db3c33c6SJoerg Roedel 911900ca13SHao, Xudong void pci_restore_ats_state(struct pci_dev *dev) 921900ca13SHao, Xudong { 931900ca13SHao, Xudong u16 ctrl; 941900ca13SHao, Xudong 95f7ef1340SBjorn Helgaas if (!dev->ats_enabled) 961900ca13SHao, Xudong return; 971900ca13SHao, Xudong 981900ca13SHao, Xudong ctrl = PCI_ATS_CTRL_ENABLE; 991900ca13SHao, Xudong if (!dev->is_virtfn) 100d544d75aSBjorn Helgaas ctrl |= PCI_ATS_CTRL_STU(dev->ats_stu - PCI_ATS_MIN_STU); 101d544d75aSBjorn Helgaas pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl); 1021900ca13SHao, Xudong } 1031900ca13SHao, Xudong 104db3c33c6SJoerg Roedel /** 105db3c33c6SJoerg Roedel * pci_ats_queue_depth - query the ATS Invalidate Queue Depth 106db3c33c6SJoerg Roedel * @dev: the PCI device 107db3c33c6SJoerg Roedel * 108db3c33c6SJoerg Roedel * Returns the queue depth on success, or negative on failure. 109db3c33c6SJoerg Roedel * 110db3c33c6SJoerg Roedel * The ATS spec uses 0 in the Invalidate Queue Depth field to 111db3c33c6SJoerg Roedel * indicate that the function can accept 32 Invalidate Request. 112db3c33c6SJoerg Roedel * But here we use the `real' values (i.e. 1~32) for the Queue 113db3c33c6SJoerg Roedel * Depth; and 0 indicates the function shares the Queue with 114db3c33c6SJoerg Roedel * other functions (doesn't exclusively own a Queue). 115db3c33c6SJoerg Roedel */ 116db3c33c6SJoerg Roedel int pci_ats_queue_depth(struct pci_dev *dev) 117db3c33c6SJoerg Roedel { 118a71f938fSBjorn Helgaas u16 cap; 119a71f938fSBjorn Helgaas 1203c765399SBjorn Helgaas if (!dev->ats_cap) 1213c765399SBjorn Helgaas return -EINVAL; 1223c765399SBjorn Helgaas 123db3c33c6SJoerg Roedel if (dev->is_virtfn) 124db3c33c6SJoerg Roedel return 0; 125db3c33c6SJoerg Roedel 126a71f938fSBjorn Helgaas pci_read_config_word(dev, dev->ats_cap + PCI_ATS_CAP, &cap); 127a71f938fSBjorn Helgaas return PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : PCI_ATS_MAX_QDEP; 128db3c33c6SJoerg Roedel } 129c320b976SJoerg Roedel 1308c938ddcSKuppuswamy Sathyanarayanan /** 1318c938ddcSKuppuswamy Sathyanarayanan * pci_ats_page_aligned - Return Page Aligned Request bit status. 1328c938ddcSKuppuswamy Sathyanarayanan * @pdev: the PCI device 1338c938ddcSKuppuswamy Sathyanarayanan * 1348c938ddcSKuppuswamy Sathyanarayanan * Returns 1, if the Untranslated Addresses generated by the device 1358c938ddcSKuppuswamy Sathyanarayanan * are always aligned or 0 otherwise. 1368c938ddcSKuppuswamy Sathyanarayanan * 1378c938ddcSKuppuswamy Sathyanarayanan * Per PCIe spec r4.0, sec 10.5.1.2, if the Page Aligned Request bit 1388c938ddcSKuppuswamy Sathyanarayanan * is set, it indicates the Untranslated Addresses generated by the 1398c938ddcSKuppuswamy Sathyanarayanan * device are always aligned to a 4096 byte boundary. 1408c938ddcSKuppuswamy Sathyanarayanan */ 1418c938ddcSKuppuswamy Sathyanarayanan int pci_ats_page_aligned(struct pci_dev *pdev) 1428c938ddcSKuppuswamy Sathyanarayanan { 1438c938ddcSKuppuswamy Sathyanarayanan u16 cap; 1448c938ddcSKuppuswamy Sathyanarayanan 1458c938ddcSKuppuswamy Sathyanarayanan if (!pdev->ats_cap) 1468c938ddcSKuppuswamy Sathyanarayanan return 0; 1478c938ddcSKuppuswamy Sathyanarayanan 1488c938ddcSKuppuswamy Sathyanarayanan pci_read_config_word(pdev, pdev->ats_cap + PCI_ATS_CAP, &cap); 1498c938ddcSKuppuswamy Sathyanarayanan 1508c938ddcSKuppuswamy Sathyanarayanan if (cap & PCI_ATS_CAP_PAGE_ALIGNED) 1518c938ddcSKuppuswamy Sathyanarayanan return 1; 1528c938ddcSKuppuswamy Sathyanarayanan 1538c938ddcSKuppuswamy Sathyanarayanan return 0; 1548c938ddcSKuppuswamy Sathyanarayanan } 1558c938ddcSKuppuswamy Sathyanarayanan 156c320b976SJoerg Roedel #ifdef CONFIG_PCI_PRI 157c065190bSKuppuswamy Sathyanarayanan void pci_pri_init(struct pci_dev *pdev) 158c065190bSKuppuswamy Sathyanarayanan { 159e5adf79aSBjorn Helgaas u16 status; 160e5adf79aSBjorn Helgaas 161c065190bSKuppuswamy Sathyanarayanan pdev->pri_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); 162e5adf79aSBjorn Helgaas 163e5adf79aSBjorn Helgaas if (!pdev->pri_cap) 164e5adf79aSBjorn Helgaas return; 165e5adf79aSBjorn Helgaas 166e5adf79aSBjorn Helgaas pci_read_config_word(pdev, pdev->pri_cap + PCI_PRI_STATUS, &status); 167e5adf79aSBjorn Helgaas if (status & PCI_PRI_STATUS_PASID) 168e5adf79aSBjorn Helgaas pdev->pasid_required = 1; 169c065190bSKuppuswamy Sathyanarayanan } 170c065190bSKuppuswamy Sathyanarayanan 171c320b976SJoerg Roedel /** 172c320b976SJoerg Roedel * pci_enable_pri - Enable PRI capability 173c320b976SJoerg Roedel * @ pdev: PCI device structure 174c320b976SJoerg Roedel * 175c320b976SJoerg Roedel * Returns 0 on success, negative value on error 176c320b976SJoerg Roedel */ 177c320b976SJoerg Roedel int pci_enable_pri(struct pci_dev *pdev, u32 reqs) 178c320b976SJoerg Roedel { 179c320b976SJoerg Roedel u16 control, status; 180c320b976SJoerg Roedel u32 max_requests; 181c065190bSKuppuswamy Sathyanarayanan int pri = pdev->pri_cap; 182c320b976SJoerg Roedel 1839bf49e36SKuppuswamy Sathyanarayanan /* 1849bf49e36SKuppuswamy Sathyanarayanan * VFs must not implement the PRI Capability. If their PF 1859bf49e36SKuppuswamy Sathyanarayanan * implements PRI, it is shared by the VFs, so if the PF PRI is 1869bf49e36SKuppuswamy Sathyanarayanan * enabled, it is also enabled for the VF. 1879bf49e36SKuppuswamy Sathyanarayanan */ 1889bf49e36SKuppuswamy Sathyanarayanan if (pdev->is_virtfn) { 1899bf49e36SKuppuswamy Sathyanarayanan if (pci_physfn(pdev)->pri_enabled) 1909bf49e36SKuppuswamy Sathyanarayanan return 0; 1919bf49e36SKuppuswamy Sathyanarayanan return -EINVAL; 1929bf49e36SKuppuswamy Sathyanarayanan } 1939bf49e36SKuppuswamy Sathyanarayanan 194a4f4fa68SJean-Philippe Brucker if (WARN_ON(pdev->pri_enabled)) 195a4f4fa68SJean-Philippe Brucker return -EBUSY; 196a4f4fa68SJean-Philippe Brucker 197c065190bSKuppuswamy Sathyanarayanan if (!pri) 198c320b976SJoerg Roedel return -EINVAL; 199c320b976SJoerg Roedel 200c065190bSKuppuswamy Sathyanarayanan pci_read_config_word(pdev, pri + PCI_PRI_STATUS, &status); 2014ebeb1ecSCQ Tang if (!(status & PCI_PRI_STATUS_STOPPED)) 202c320b976SJoerg Roedel return -EBUSY; 203c320b976SJoerg Roedel 204c065190bSKuppuswamy Sathyanarayanan pci_read_config_dword(pdev, pri + PCI_PRI_MAX_REQ, &max_requests); 205c320b976SJoerg Roedel reqs = min(max_requests, reqs); 2064ebeb1ecSCQ Tang pdev->pri_reqs_alloc = reqs; 207c065190bSKuppuswamy Sathyanarayanan pci_write_config_dword(pdev, pri + PCI_PRI_ALLOC_REQ, reqs); 208c320b976SJoerg Roedel 2094ebeb1ecSCQ Tang control = PCI_PRI_CTRL_ENABLE; 210c065190bSKuppuswamy Sathyanarayanan pci_write_config_word(pdev, pri + PCI_PRI_CTRL, control); 211c320b976SJoerg Roedel 212a4f4fa68SJean-Philippe Brucker pdev->pri_enabled = 1; 213a4f4fa68SJean-Philippe Brucker 214c320b976SJoerg Roedel return 0; 215c320b976SJoerg Roedel } 216c320b976SJoerg Roedel 217c320b976SJoerg Roedel /** 218c320b976SJoerg Roedel * pci_disable_pri - Disable PRI capability 219c320b976SJoerg Roedel * @pdev: PCI device structure 220c320b976SJoerg Roedel * 221c320b976SJoerg Roedel * Only clears the enabled-bit, regardless of its former value 222c320b976SJoerg Roedel */ 223c320b976SJoerg Roedel void pci_disable_pri(struct pci_dev *pdev) 224c320b976SJoerg Roedel { 225c320b976SJoerg Roedel u16 control; 226c065190bSKuppuswamy Sathyanarayanan int pri = pdev->pri_cap; 227c320b976SJoerg Roedel 2289bf49e36SKuppuswamy Sathyanarayanan /* VFs share the PF PRI */ 2299bf49e36SKuppuswamy Sathyanarayanan if (pdev->is_virtfn) 2309bf49e36SKuppuswamy Sathyanarayanan return; 2319bf49e36SKuppuswamy Sathyanarayanan 232a4f4fa68SJean-Philippe Brucker if (WARN_ON(!pdev->pri_enabled)) 233a4f4fa68SJean-Philippe Brucker return; 234a4f4fa68SJean-Philippe Brucker 235c065190bSKuppuswamy Sathyanarayanan if (!pri) 236c320b976SJoerg Roedel return; 237c320b976SJoerg Roedel 238c065190bSKuppuswamy Sathyanarayanan pci_read_config_word(pdev, pri + PCI_PRI_CTRL, &control); 23991f57d5eSAlex Williamson control &= ~PCI_PRI_CTRL_ENABLE; 240c065190bSKuppuswamy Sathyanarayanan pci_write_config_word(pdev, pri + PCI_PRI_CTRL, control); 241a4f4fa68SJean-Philippe Brucker 242a4f4fa68SJean-Philippe Brucker pdev->pri_enabled = 0; 243c320b976SJoerg Roedel } 244c320b976SJoerg Roedel EXPORT_SYMBOL_GPL(pci_disable_pri); 245c320b976SJoerg Roedel 246c320b976SJoerg Roedel /** 2474ebeb1ecSCQ Tang * pci_restore_pri_state - Restore PRI 2484ebeb1ecSCQ Tang * @pdev: PCI device structure 2494ebeb1ecSCQ Tang */ 2504ebeb1ecSCQ Tang void pci_restore_pri_state(struct pci_dev *pdev) 2514ebeb1ecSCQ Tang { 2524ebeb1ecSCQ Tang u16 control = PCI_PRI_CTRL_ENABLE; 2534ebeb1ecSCQ Tang u32 reqs = pdev->pri_reqs_alloc; 254c065190bSKuppuswamy Sathyanarayanan int pri = pdev->pri_cap; 2554ebeb1ecSCQ Tang 2569bf49e36SKuppuswamy Sathyanarayanan if (pdev->is_virtfn) 2579bf49e36SKuppuswamy Sathyanarayanan return; 2589bf49e36SKuppuswamy Sathyanarayanan 2594ebeb1ecSCQ Tang if (!pdev->pri_enabled) 2604ebeb1ecSCQ Tang return; 2614ebeb1ecSCQ Tang 262c065190bSKuppuswamy Sathyanarayanan if (!pri) 2634ebeb1ecSCQ Tang return; 2644ebeb1ecSCQ Tang 265c065190bSKuppuswamy Sathyanarayanan pci_write_config_dword(pdev, pri + PCI_PRI_ALLOC_REQ, reqs); 266c065190bSKuppuswamy Sathyanarayanan pci_write_config_word(pdev, pri + PCI_PRI_CTRL, control); 2674ebeb1ecSCQ Tang } 2684ebeb1ecSCQ Tang 2694ebeb1ecSCQ Tang /** 270c320b976SJoerg Roedel * pci_reset_pri - Resets device's PRI state 271c320b976SJoerg Roedel * @pdev: PCI device structure 272c320b976SJoerg Roedel * 273c320b976SJoerg Roedel * The PRI capability must be disabled before this function is called. 274c320b976SJoerg Roedel * Returns 0 on success, negative value on error. 275c320b976SJoerg Roedel */ 276c320b976SJoerg Roedel int pci_reset_pri(struct pci_dev *pdev) 277c320b976SJoerg Roedel { 278c320b976SJoerg Roedel u16 control; 279c065190bSKuppuswamy Sathyanarayanan int pri = pdev->pri_cap; 280c320b976SJoerg Roedel 2819bf49e36SKuppuswamy Sathyanarayanan if (pdev->is_virtfn) 2829bf49e36SKuppuswamy Sathyanarayanan return 0; 2839bf49e36SKuppuswamy Sathyanarayanan 284a4f4fa68SJean-Philippe Brucker if (WARN_ON(pdev->pri_enabled)) 285a4f4fa68SJean-Philippe Brucker return -EBUSY; 286a4f4fa68SJean-Philippe Brucker 287c065190bSKuppuswamy Sathyanarayanan if (!pri) 288c320b976SJoerg Roedel return -EINVAL; 289c320b976SJoerg Roedel 2904ebeb1ecSCQ Tang control = PCI_PRI_CTRL_RESET; 291c065190bSKuppuswamy Sathyanarayanan pci_write_config_word(pdev, pri + PCI_PRI_CTRL, control); 292c320b976SJoerg Roedel 293c320b976SJoerg Roedel return 0; 294c320b976SJoerg Roedel } 2958cbb8a93SBjorn Helgaas 2968cbb8a93SBjorn Helgaas /** 2978cbb8a93SBjorn Helgaas * pci_prg_resp_pasid_required - Return PRG Response PASID Required bit 2988cbb8a93SBjorn Helgaas * status. 2998cbb8a93SBjorn Helgaas * @pdev: PCI device structure 3008cbb8a93SBjorn Helgaas * 3018cbb8a93SBjorn Helgaas * Returns 1 if PASID is required in PRG Response Message, 0 otherwise. 3028cbb8a93SBjorn Helgaas */ 3038cbb8a93SBjorn Helgaas int pci_prg_resp_pasid_required(struct pci_dev *pdev) 3048cbb8a93SBjorn Helgaas { 3059bf49e36SKuppuswamy Sathyanarayanan if (pdev->is_virtfn) 3069bf49e36SKuppuswamy Sathyanarayanan pdev = pci_physfn(pdev); 3079bf49e36SKuppuswamy Sathyanarayanan 308e5adf79aSBjorn Helgaas return pdev->pasid_required; 3098cbb8a93SBjorn Helgaas } 310c320b976SJoerg Roedel #endif /* CONFIG_PCI_PRI */ 311086ac11fSJoerg Roedel 312086ac11fSJoerg Roedel #ifdef CONFIG_PCI_PASID 313751035b8SKuppuswamy Sathyanarayanan void pci_pasid_init(struct pci_dev *pdev) 314751035b8SKuppuswamy Sathyanarayanan { 315751035b8SKuppuswamy Sathyanarayanan pdev->pasid_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID); 316751035b8SKuppuswamy Sathyanarayanan } 317751035b8SKuppuswamy Sathyanarayanan 318086ac11fSJoerg Roedel /** 319086ac11fSJoerg Roedel * pci_enable_pasid - Enable the PASID capability 320086ac11fSJoerg Roedel * @pdev: PCI device structure 321086ac11fSJoerg Roedel * @features: Features to enable 322086ac11fSJoerg Roedel * 323086ac11fSJoerg Roedel * Returns 0 on success, negative value on error. This function checks 324086ac11fSJoerg Roedel * whether the features are actually supported by the device and returns 325086ac11fSJoerg Roedel * an error if not. 326086ac11fSJoerg Roedel */ 327086ac11fSJoerg Roedel int pci_enable_pasid(struct pci_dev *pdev, int features) 328086ac11fSJoerg Roedel { 329086ac11fSJoerg Roedel u16 control, supported; 330751035b8SKuppuswamy Sathyanarayanan int pasid = pdev->pasid_cap; 331086ac11fSJoerg Roedel 3322b0ae7ccSKuppuswamy Sathyanarayanan /* 3332b0ae7ccSKuppuswamy Sathyanarayanan * VFs must not implement the PASID Capability, but if a PF 3342b0ae7ccSKuppuswamy Sathyanarayanan * supports PASID, its VFs share the PF PASID configuration. 3352b0ae7ccSKuppuswamy Sathyanarayanan */ 3362b0ae7ccSKuppuswamy Sathyanarayanan if (pdev->is_virtfn) { 3372b0ae7ccSKuppuswamy Sathyanarayanan if (pci_physfn(pdev)->pasid_enabled) 3382b0ae7ccSKuppuswamy Sathyanarayanan return 0; 3392b0ae7ccSKuppuswamy Sathyanarayanan return -EINVAL; 3402b0ae7ccSKuppuswamy Sathyanarayanan } 3412b0ae7ccSKuppuswamy Sathyanarayanan 342a4f4fa68SJean-Philippe Brucker if (WARN_ON(pdev->pasid_enabled)) 343a4f4fa68SJean-Philippe Brucker return -EBUSY; 344a4f4fa68SJean-Philippe Brucker 3457ce3f912SSinan Kaya if (!pdev->eetlp_prefix_path) 3467ce3f912SSinan Kaya return -EINVAL; 3477ce3f912SSinan Kaya 348751035b8SKuppuswamy Sathyanarayanan if (!pasid) 349086ac11fSJoerg Roedel return -EINVAL; 350086ac11fSJoerg Roedel 351751035b8SKuppuswamy Sathyanarayanan pci_read_config_word(pdev, pasid + PCI_PASID_CAP, &supported); 35291f57d5eSAlex Williamson supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV; 353086ac11fSJoerg Roedel 354086ac11fSJoerg Roedel /* User wants to enable anything unsupported? */ 355086ac11fSJoerg Roedel if ((supported & features) != features) 356086ac11fSJoerg Roedel return -EINVAL; 357086ac11fSJoerg Roedel 35891f57d5eSAlex Williamson control = PCI_PASID_CTRL_ENABLE | features; 3594ebeb1ecSCQ Tang pdev->pasid_features = features; 360086ac11fSJoerg Roedel 361751035b8SKuppuswamy Sathyanarayanan pci_write_config_word(pdev, pasid + PCI_PASID_CTRL, control); 362086ac11fSJoerg Roedel 363a4f4fa68SJean-Philippe Brucker pdev->pasid_enabled = 1; 364a4f4fa68SJean-Philippe Brucker 365086ac11fSJoerg Roedel return 0; 366086ac11fSJoerg Roedel } 367086ac11fSJoerg Roedel 368086ac11fSJoerg Roedel /** 369086ac11fSJoerg Roedel * pci_disable_pasid - Disable the PASID capability 370086ac11fSJoerg Roedel * @pdev: PCI device structure 371086ac11fSJoerg Roedel */ 372086ac11fSJoerg Roedel void pci_disable_pasid(struct pci_dev *pdev) 373086ac11fSJoerg Roedel { 374086ac11fSJoerg Roedel u16 control = 0; 375751035b8SKuppuswamy Sathyanarayanan int pasid = pdev->pasid_cap; 376086ac11fSJoerg Roedel 3772b0ae7ccSKuppuswamy Sathyanarayanan /* VFs share the PF PASID configuration */ 3782b0ae7ccSKuppuswamy Sathyanarayanan if (pdev->is_virtfn) 3792b0ae7ccSKuppuswamy Sathyanarayanan return; 3802b0ae7ccSKuppuswamy Sathyanarayanan 381a4f4fa68SJean-Philippe Brucker if (WARN_ON(!pdev->pasid_enabled)) 382a4f4fa68SJean-Philippe Brucker return; 383a4f4fa68SJean-Philippe Brucker 384751035b8SKuppuswamy Sathyanarayanan if (!pasid) 385086ac11fSJoerg Roedel return; 386086ac11fSJoerg Roedel 387751035b8SKuppuswamy Sathyanarayanan pci_write_config_word(pdev, pasid + PCI_PASID_CTRL, control); 388a4f4fa68SJean-Philippe Brucker 389a4f4fa68SJean-Philippe Brucker pdev->pasid_enabled = 0; 390086ac11fSJoerg Roedel } 391086ac11fSJoerg Roedel 392086ac11fSJoerg Roedel /** 3934ebeb1ecSCQ Tang * pci_restore_pasid_state - Restore PASID capabilities 3944ebeb1ecSCQ Tang * @pdev: PCI device structure 3954ebeb1ecSCQ Tang */ 3964ebeb1ecSCQ Tang void pci_restore_pasid_state(struct pci_dev *pdev) 3974ebeb1ecSCQ Tang { 3984ebeb1ecSCQ Tang u16 control; 399751035b8SKuppuswamy Sathyanarayanan int pasid = pdev->pasid_cap; 4004ebeb1ecSCQ Tang 4012b0ae7ccSKuppuswamy Sathyanarayanan if (pdev->is_virtfn) 4022b0ae7ccSKuppuswamy Sathyanarayanan return; 4032b0ae7ccSKuppuswamy Sathyanarayanan 4044ebeb1ecSCQ Tang if (!pdev->pasid_enabled) 4054ebeb1ecSCQ Tang return; 4064ebeb1ecSCQ Tang 407751035b8SKuppuswamy Sathyanarayanan if (!pasid) 4084ebeb1ecSCQ Tang return; 4094ebeb1ecSCQ Tang 4104ebeb1ecSCQ Tang control = PCI_PASID_CTRL_ENABLE | pdev->pasid_features; 411751035b8SKuppuswamy Sathyanarayanan pci_write_config_word(pdev, pasid + PCI_PASID_CTRL, control); 4124ebeb1ecSCQ Tang } 4134ebeb1ecSCQ Tang 4144ebeb1ecSCQ Tang /** 415086ac11fSJoerg Roedel * pci_pasid_features - Check which PASID features are supported 416086ac11fSJoerg Roedel * @pdev: PCI device structure 417086ac11fSJoerg Roedel * 418086ac11fSJoerg Roedel * Returns a negative value when no PASI capability is present. 419086ac11fSJoerg Roedel * Otherwise is returns a bitmask with supported features. Current 420086ac11fSJoerg Roedel * features reported are: 42191f57d5eSAlex Williamson * PCI_PASID_CAP_EXEC - Execute permission supported 422f7625980SBjorn Helgaas * PCI_PASID_CAP_PRIV - Privileged mode supported 423086ac11fSJoerg Roedel */ 424086ac11fSJoerg Roedel int pci_pasid_features(struct pci_dev *pdev) 425086ac11fSJoerg Roedel { 426086ac11fSJoerg Roedel u16 supported; 427*2e34673bSKuppuswamy Sathyanarayanan int pasid; 428086ac11fSJoerg Roedel 4292b0ae7ccSKuppuswamy Sathyanarayanan if (pdev->is_virtfn) 4302b0ae7ccSKuppuswamy Sathyanarayanan pdev = pci_physfn(pdev); 4312b0ae7ccSKuppuswamy Sathyanarayanan 432*2e34673bSKuppuswamy Sathyanarayanan pasid = pdev->pasid_cap; 433751035b8SKuppuswamy Sathyanarayanan if (!pasid) 434086ac11fSJoerg Roedel return -EINVAL; 435086ac11fSJoerg Roedel 436751035b8SKuppuswamy Sathyanarayanan pci_read_config_word(pdev, pasid + PCI_PASID_CAP, &supported); 437086ac11fSJoerg Roedel 43891f57d5eSAlex Williamson supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV; 439086ac11fSJoerg Roedel 440086ac11fSJoerg Roedel return supported; 441086ac11fSJoerg Roedel } 442086ac11fSJoerg Roedel 443086ac11fSJoerg Roedel #define PASID_NUMBER_SHIFT 8 444086ac11fSJoerg Roedel #define PASID_NUMBER_MASK (0x1f << PASID_NUMBER_SHIFT) 445086ac11fSJoerg Roedel /** 446086ac11fSJoerg Roedel * pci_max_pasid - Get maximum number of PASIDs supported by device 447086ac11fSJoerg Roedel * @pdev: PCI device structure 448086ac11fSJoerg Roedel * 449086ac11fSJoerg Roedel * Returns negative value when PASID capability is not present. 450f6b6aefeSBjorn Helgaas * Otherwise it returns the number of supported PASIDs. 451086ac11fSJoerg Roedel */ 452086ac11fSJoerg Roedel int pci_max_pasids(struct pci_dev *pdev) 453086ac11fSJoerg Roedel { 454086ac11fSJoerg Roedel u16 supported; 455*2e34673bSKuppuswamy Sathyanarayanan int pasid; 456086ac11fSJoerg Roedel 4572b0ae7ccSKuppuswamy Sathyanarayanan if (pdev->is_virtfn) 4582b0ae7ccSKuppuswamy Sathyanarayanan pdev = pci_physfn(pdev); 4592b0ae7ccSKuppuswamy Sathyanarayanan 460*2e34673bSKuppuswamy Sathyanarayanan pasid = pdev->pasid_cap; 461751035b8SKuppuswamy Sathyanarayanan if (!pasid) 462086ac11fSJoerg Roedel return -EINVAL; 463086ac11fSJoerg Roedel 464751035b8SKuppuswamy Sathyanarayanan pci_read_config_word(pdev, pasid + PCI_PASID_CAP, &supported); 465086ac11fSJoerg Roedel 466086ac11fSJoerg Roedel supported = (supported & PASID_NUMBER_MASK) >> PASID_NUMBER_SHIFT; 467086ac11fSJoerg Roedel 468086ac11fSJoerg Roedel return (1 << supported); 469086ac11fSJoerg Roedel } 470086ac11fSJoerg Roedel #endif /* CONFIG_PCI_PASID */ 471