diff options
-rw-r--r-- | drivers/pci/Kconfig | 13 | ||||
-rw-r--r-- | drivers/pci/ats.c | 113 | ||||
-rw-r--r-- | include/linux/pci-ats.h | 31 | ||||
-rw-r--r-- | include/linux/pci_regs.h | 8 |
4 files changed, 165 insertions, 0 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index fb1e9707..cec6606 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -94,6 +94,19 @@ config PCI_PRI If unsure, say N. +config PCI_PASID + bool "PCI PASID support" + depends on PCI + select PCI_ATS + help + Process Address Space Identifiers (PASIDs) can be used by PCI devices + to access more than one IO address space at the same time. To make + use of this feature an IOMMU is required which also supports PASIDs. + Select this option if you have such an IOMMU and want to compile the + driver for it into your kernel. + + If unsure, say N. + config PCI_IOAPIC bool depends on PCI diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index bf892a0..f727a09 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -7,6 +7,7 @@ * PCI Express I/O Virtualization (IOV) support. * Address Translation Service 1.0 * Page Request Interface added by Joerg Roedel <joerg.roedel@amd.com> + * PASID support added by Joerg Roedel <joerg.roedel@amd.com> */ #include <linux/pci-ats.h> @@ -323,3 +324,115 @@ int pci_pri_status(struct pci_dev *pdev) } EXPORT_SYMBOL_GPL(pci_pri_status); #endif /* CONFIG_PCI_PRI */ + +#ifdef CONFIG_PCI_PASID +/** + * pci_enable_pasid - Enable the PASID capability + * @pdev: PCI device structure + * @features: Features to enable + * + * Returns 0 on success, negative value on error. This function checks + * whether the features are actually supported by the device and returns + * an error if not. + */ +int pci_enable_pasid(struct pci_dev *pdev, int features) +{ + u16 control, supported; + int pos; + + pos = pci_find_ext_capability(pdev, PCI_PASID_CAP); + if (!pos) + return -EINVAL; + + pci_read_config_word(pdev, pos + PCI_PASID_CONTROL_OFF, &control); + pci_read_config_word(pdev, pos + PCI_PASID_CAP_OFF, &supported); + + if (!(supported & PCI_PASID_ENABLE)) + return -EINVAL; + + supported &= PCI_PASID_EXEC | PCI_PASID_PRIV; + + /* User wants to enable anything unsupported? */ + if ((supported & features) != features) + return -EINVAL; + + control = PCI_PASID_ENABLE | features; + + pci_write_config_word(pdev, pos + PCI_PASID_CONTROL_OFF, control); + + return 0; +} +EXPORT_SYMBOL_GPL(pci_enable_pasid); + +/** + * pci_disable_pasid - Disable the PASID capability + * @pdev: PCI device structure + * + */ +void pci_disable_pasid(struct pci_dev *pdev) +{ + u16 control = 0; + int pos; + + pos = pci_find_ext_capability(pdev, PCI_PASID_CAP); + if (!pos) + return; + + pci_write_config_word(pdev, pos + PCI_PASID_CONTROL_OFF, control); +} +EXPORT_SYMBOL_GPL(pci_disable_pasid); + +/** + * pci_pasid_features - Check which PASID features are supported + * @pdev: PCI device structure + * + * Returns a negative value when no PASI capability is present. + * Otherwise is returns a bitmask with supported features. Current + * features reported are: + * PCI_PASID_ENABLE - PASID capability can be enabled + * PCI_PASID_EXEC - Execute permission supported + * PCI_PASID_PRIV - Priviledged mode supported + */ +int pci_pasid_features(struct pci_dev *pdev) +{ + u16 supported; + int pos; + + pos = pci_find_ext_capability(pdev, PCI_PASID_CAP); + if (!pos) + return -EINVAL; + + pci_read_config_word(pdev, pos + PCI_PASID_CAP_OFF, &supported); + + supported &= PCI_PASID_ENABLE | PCI_PASID_EXEC | PCI_PASID_PRIV; + + return supported; +} +EXPORT_SYMBOL_GPL(pci_pasid_features); + +#define PASID_NUMBER_SHIFT 8 +#define PASID_NUMBER_MASK (0x1f << PASID_NUMBER_SHIFT) +/** + * pci_max_pasid - Get maximum number of PASIDs supported by device + * @pdev: PCI device structure + * + * Returns negative value when PASID capability is not present. + * Otherwise it returns the numer of supported PASIDs. + */ +int pci_max_pasids(struct pci_dev *pdev) +{ + u16 supported; + int pos; + + pos = pci_find_ext_capability(pdev, PCI_PASID_CAP); + if (!pos) + return -EINVAL; + + pci_read_config_word(pdev, pos + PCI_PASID_CAP_OFF, &supported); + + supported = (supported & PASID_NUMBER_MASK) >> PASID_NUMBER_SHIFT; + + return (1 << supported); +} +EXPORT_SYMBOL_GPL(pci_max_pasids); +#endif /* CONFIG_PCI_PASID */ diff --git a/include/linux/pci-ats.h b/include/linux/pci-ats.h index 0713952..e3d0b38 100644 --- a/include/linux/pci-ats.h +++ b/include/linux/pci-ats.h @@ -93,4 +93,35 @@ static inline int pci_pri_status(struct pci_dev *pdev) } #endif /* CONFIG_PCI_PRI */ +#ifdef CONFIG_PCI_PASID + +extern int pci_enable_pasid(struct pci_dev *pdev, int features); +extern void pci_disable_pasid(struct pci_dev *pdev); +extern int pci_pasid_features(struct pci_dev *pdev); +extern int pci_max_pasids(struct pci_dev *pdev); + +#else /* CONFIG_PCI_PASID */ + +static inline int pci_enable_pasid(struct pci_dev *pdev, int features) +{ + return -EINVAL; +} + +static inline void pci_disable_pasid(struct pci_dev *pdev) +{ +} + +static inline int pci_pasid_features(struct pci_dev *pdev) +{ + return -EINVAL; +} + +static inline int pci_max_pasids(struct pci_dev *pdev) +{ + return -EINVAL; +} + +#endif /* CONFIG_PCI_PASID */ + + #endif /* LINUX_PCI_ATS_H*/ diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 7fc32af..b5d9657 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -675,6 +675,14 @@ #define PCI_PRI_MAX_REQ_OFF 0x08 /* Cap offset for max reqs supported */ #define PCI_PRI_ALLOC_REQ_OFF 0x0c /* Cap offset for max reqs allowed */ +/* PASID capability */ +#define PCI_PASID_CAP 0x1b /* PASID capability ID */ +#define PCI_PASID_CAP_OFF 0x04 /* PASID feature register */ +#define PCI_PASID_CONTROL_OFF 0x06 /* PASID control register */ +#define PCI_PASID_ENABLE 0x01 /* Enable/Supported bit */ +#define PCI_PASID_EXEC 0x02 /* Exec permissions Enable/Supported */ +#define PCI_PASID_PRIV 0x04 /* Priviledge Mode Enable/Support */ + /* Single Root I/O Virtualization */ #define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */ #define PCI_SRIOV_CAP_VFM 0x01 /* VF Migration Capable */ |