]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/pci/ats.c
PCI: Add support for PASID capability
[karo-tx-linux.git] / drivers / pci / ats.c
index bf892a025d4f9065c54068daf12071b4a3218224..f727a09eb72fc9a04ffe0481eb7330d778d4f3d9 100644 (file)
@@ -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 */