iommu/amd: Add device errata handling
authorJoerg Roedel <joerg.roedel@amd.com>
Thu, 1 Dec 2011 11:04:58 +0000 (12:04 +0100)
committerJoerg Roedel <joerg.roedel@amd.com>
Mon, 12 Dec 2011 14:19:06 +0000 (15:19 +0100)
Add infrastructure for errata-handling and handle two known
erratas in the IOMMUv2 code.

Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
drivers/iommu/amd_iommu.c
drivers/iommu/amd_iommu_types.h
include/linux/amd-iommu.h

index 71773d0..e453bbd 100644 (file)
@@ -172,6 +172,15 @@ static bool pci_iommuv2_capable(struct pci_dev *pdev)
        return true;
 }
 
+static bool pdev_pri_erratum(struct pci_dev *pdev, u32 erratum)
+{
+       struct iommu_dev_data *dev_data;
+
+       dev_data = get_dev_data(&pdev->dev);
+
+       return dev_data->errata & (1 << erratum) ? true : false;
+}
+
 /*
  * In this function the list of preallocated protection domains is traversed to
  * find the domain for a specific device
@@ -1934,9 +1943,33 @@ static void pdev_iommuv2_disable(struct pci_dev *pdev)
        pci_disable_pasid(pdev);
 }
 
+/* FIXME: Change generic reset-function to do the same */
+static int pri_reset_while_enabled(struct pci_dev *pdev)
+{
+       u16 control;
+       int pos;
+
+       pos = pci_find_ext_capability(pdev, PCI_PRI_CAP);
+       if (!pos)
+               return -EINVAL;
+
+       pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control);
+       control |= PCI_PRI_RESET;
+       pci_write_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, control);
+
+       return 0;
+}
+
 static int pdev_iommuv2_enable(struct pci_dev *pdev)
 {
-       int ret;
+       bool reset_enable;
+       int reqs, ret;
+
+       /* FIXME: Hardcode number of outstanding requests for now */
+       reqs = 32;
+       if (pdev_pri_erratum(pdev, AMD_PRI_DEV_ERRATUM_LIMIT_REQ_ONE))
+               reqs = 1;
+       reset_enable = pdev_pri_erratum(pdev, AMD_PRI_DEV_ERRATUM_ENABLE_RESET);
 
        /* Only allow access to user-accessible pages */
        ret = pci_enable_pasid(pdev, 0);
@@ -1948,11 +1981,17 @@ static int pdev_iommuv2_enable(struct pci_dev *pdev)
        if (ret)
                goto out_err;
 
-       /* FIXME: Hardcode number of outstanding requests for now */
-       ret = pci_enable_pri(pdev, 32);
+       /* Enable PRI */
+       ret = pci_enable_pri(pdev, reqs);
        if (ret)
                goto out_err;
 
+       if (reset_enable) {
+               ret = pri_reset_while_enabled(pdev);
+               if (ret)
+                       goto out_err;
+       }
+
        ret = pci_enable_ats(pdev, PAGE_SHIFT);
        if (ret)
                goto out_err;
@@ -3481,3 +3520,15 @@ struct iommu_domain *amd_iommu_get_v2_domain(struct pci_dev *pdev)
        return domain->iommu_domain;
 }
 EXPORT_SYMBOL(amd_iommu_get_v2_domain);
+
+void amd_iommu_enable_device_erratum(struct pci_dev *pdev, u32 erratum)
+{
+       struct iommu_dev_data *dev_data;
+
+       if (!amd_iommu_v2_supported())
+               return;
+
+       dev_data = get_dev_data(&pdev->dev);
+       dev_data->errata |= (1 << erratum);
+}
+EXPORT_SYMBOL(amd_iommu_enable_device_erratum);
index c39988f..6ad8b10 100644 (file)
@@ -404,6 +404,7 @@ struct iommu_dev_data {
        } ats;                            /* ATS state */
        bool pri_tlp;                     /* PASID TLB required for
                                             PPR completions */
+       u32 errata;                       /* Bitmap for errata to apply */
 };
 
 /*
index a6863a2..4152c30 100644 (file)
 
 extern int amd_iommu_detect(void);
 
+
+/**
+ * amd_iommu_enable_device_erratum() - Enable erratum workaround for device
+ *                                    in the IOMMUv2 driver
+ * @pdev: The PCI device the workaround is necessary for
+ * @erratum: The erratum workaround to enable
+ *
+ * Possible values for the erratum number are for now:
+ * - AMD_PRI_DEV_ERRATUM_ENABLE_RESET - Reset PRI capability when PRI
+ *                                     is enabled
+ * - AMD_PRI_DEV_ERRATUM_LIMIT_REQ_ONE - Limit number of outstanding PRI
+ *                                      requests to one
+ */
+#define AMD_PRI_DEV_ERRATUM_ENABLE_RESET               0
+#define AMD_PRI_DEV_ERRATUM_LIMIT_REQ_ONE              1
+
+extern void amd_iommu_enable_device_erratum(struct pci_dev *pdev, u32 erratum);
+
 #else
 
 static inline int amd_iommu_detect(void) { return -ENODEV; }