]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/iommu/amd_iommu.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
[karo-tx-linux.git] / drivers / iommu / amd_iommu.c
index 71773d0fb7698c34f1796084f63a5a3d897ef112..cce1f03b8895324d7d6e92cd6094cc39e7673cbe 100644 (file)
 
 #define LOOP_TIMEOUT   100000
 
+/*
+ * This bitmap is used to advertise the page sizes our hardware support
+ * to the IOMMU core, which will then use this information to split
+ * physically contiguous memory regions it is mapping into page sizes
+ * that we support.
+ *
+ * Traditionally the IOMMU core just handed us the mappings directly,
+ * after making sure the size is an order of a 4KiB page and that the
+ * mapping has natural alignment.
+ *
+ * To retain this behavior, we currently advertise that we support
+ * all page sizes that are an order of 4KiB.
+ *
+ * If at some point we'd like to utilize the IOMMU core's new behavior,
+ * we could change this to advertise the real page sizes we support.
+ */
+#define AMD_IOMMU_PGSIZES      (~0xFFFUL)
+
 static DEFINE_RWLOCK(amd_iommu_devtable_lock);
 
 /* A list of preallocated protection domains */
@@ -158,8 +176,8 @@ static bool pci_iommuv2_capable(struct pci_dev *pdev)
 {
        static const int caps[] = {
                PCI_EXT_CAP_ID_ATS,
-               PCI_PRI_CAP,
-               PCI_PASID_CAP,
+               PCI_EXT_CAP_ID_PRI,
+               PCI_EXT_CAP_ID_PASID,
        };
        int i, pos;
 
@@ -172,6 +190,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
@@ -350,6 +377,11 @@ DECLARE_STATS_COUNTER(domain_flush_single);
 DECLARE_STATS_COUNTER(domain_flush_all);
 DECLARE_STATS_COUNTER(alloced_io_mem);
 DECLARE_STATS_COUNTER(total_map_requests);
+DECLARE_STATS_COUNTER(complete_ppr);
+DECLARE_STATS_COUNTER(invalidate_iotlb);
+DECLARE_STATS_COUNTER(invalidate_iotlb_all);
+DECLARE_STATS_COUNTER(pri_requests);
+
 
 static struct dentry *stats_dir;
 static struct dentry *de_fflush;
@@ -384,6 +416,10 @@ static void amd_iommu_stats_init(void)
        amd_iommu_stats_add(&domain_flush_all);
        amd_iommu_stats_add(&alloced_io_mem);
        amd_iommu_stats_add(&total_map_requests);
+       amd_iommu_stats_add(&complete_ppr);
+       amd_iommu_stats_add(&invalidate_iotlb);
+       amd_iommu_stats_add(&invalidate_iotlb_all);
+       amd_iommu_stats_add(&pri_requests);
 }
 
 #endif
@@ -500,6 +536,8 @@ static void iommu_handle_ppr_entry(struct amd_iommu *iommu, u32 head)
        volatile u64 *raw;
        int i;
 
+       INC_STATS_COUNTER(pri_requests);
+
        raw = (u64 *)(iommu->ppr_log + head);
 
        /*
@@ -1934,9 +1972,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_EXT_CAP_ID_PRI);
+       if (!pos)
+               return -EINVAL;
+
+       pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
+       control |= PCI_PRI_CTRL_RESET;
+       pci_write_config_word(pdev, pos + PCI_PRI_CTRL, 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 +2010,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;
@@ -1974,11 +2042,11 @@ bool pci_pri_tlp_required(struct pci_dev *pdev)
        u16 control;
        int pos;
 
-       pos = pci_find_ext_capability(pdev, PCI_PRI_CAP);
+       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
        if (!pos)
                return false;
 
-       pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control);
+       pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
 
        return (control & PCI_PRI_TLP_OFF) ? true : false;
 }
@@ -3043,9 +3111,8 @@ static int amd_iommu_attach_device(struct iommu_domain *dom,
 }
 
 static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova,
-                        phys_addr_t paddr, int gfp_order, int iommu_prot)
+                        phys_addr_t paddr, size_t page_size, int iommu_prot)
 {
-       unsigned long page_size = 0x1000UL << gfp_order;
        struct protection_domain *domain = dom->priv;
        int prot = 0;
        int ret;
@@ -3065,24 +3132,22 @@ static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova,
        return ret;
 }
 
-static int amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova,
-                          int gfp_order)
+static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova,
+                          size_t page_size)
 {
        struct protection_domain *domain = dom->priv;
-       unsigned long page_size, unmap_size;
+       size_t unmap_size;
 
        if (domain->mode == PAGE_MODE_NONE)
                return -EINVAL;
 
-       page_size  = 0x1000UL << gfp_order;
-
        mutex_lock(&domain->api_lock);
        unmap_size = iommu_unmap_page(domain, iova, page_size);
        mutex_unlock(&domain->api_lock);
 
        domain_flush_tlb_pde(domain);
 
-       return get_order(unmap_size);
+       return unmap_size;
 }
 
 static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom,
@@ -3123,6 +3188,26 @@ static int amd_iommu_domain_has_cap(struct iommu_domain *domain,
        return 0;
 }
 
+static int amd_iommu_device_group(struct device *dev, unsigned int *groupid)
+{
+       struct iommu_dev_data *dev_data = dev->archdata.iommu;
+       struct pci_dev *pdev = to_pci_dev(dev);
+       u16 devid;
+
+       if (!dev_data)
+               return -ENODEV;
+
+       if (pdev->is_virtfn || !iommu_group_mf)
+               devid = dev_data->devid;
+       else
+               devid = calc_devid(pdev->bus->number,
+                                  PCI_DEVFN(PCI_SLOT(pdev->devfn), 0));
+
+       *groupid = amd_iommu_alias_table[devid];
+
+       return 0;
+}
+
 static struct iommu_ops amd_iommu_ops = {
        .domain_init = amd_iommu_domain_init,
        .domain_destroy = amd_iommu_domain_destroy,
@@ -3132,6 +3217,8 @@ static struct iommu_ops amd_iommu_ops = {
        .unmap = amd_iommu_unmap,
        .iova_to_phys = amd_iommu_iova_to_phys,
        .domain_has_cap = amd_iommu_domain_has_cap,
+       .device_group = amd_iommu_device_group,
+       .pgsize_bitmap  = AMD_IOMMU_PGSIZES,
 };
 
 /*****************************************************************************
@@ -3172,6 +3259,8 @@ int __init amd_iommu_init_passthrough(void)
                attach_device(&dev->dev, pt_domain);
        }
 
+       amd_iommu_stats_init();
+
        pr_info("AMD-Vi: Initialized for Passthrough Mode\n");
 
        return 0;
@@ -3317,6 +3406,8 @@ out:
 static int __amd_iommu_flush_page(struct protection_domain *domain, int pasid,
                                  u64 address)
 {
+       INC_STATS_COUNTER(invalidate_iotlb);
+
        return __flush_pasid(domain, pasid, address, false);
 }
 
@@ -3337,6 +3428,8 @@ EXPORT_SYMBOL(amd_iommu_flush_page);
 
 static int __amd_iommu_flush_tlb(struct protection_domain *domain, int pasid)
 {
+       INC_STATS_COUNTER(invalidate_iotlb_all);
+
        return __flush_pasid(domain, pasid, CMD_INV_IOMMU_ALL_PAGES_ADDRESS,
                             true);
 }
@@ -3456,6 +3549,8 @@ int amd_iommu_complete_ppr(struct pci_dev *pdev, int pasid,
        struct amd_iommu *iommu;
        struct iommu_cmd cmd;
 
+       INC_STATS_COUNTER(complete_ppr);
+
        dev_data = get_dev_data(&pdev->dev);
        iommu    = amd_iommu_rlookup_table[dev_data->devid];
 
@@ -3481,3 +3576,58 @@ 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);
+
+int amd_iommu_device_info(struct pci_dev *pdev,
+                          struct amd_iommu_device_info *info)
+{
+       int max_pasids;
+       int pos;
+
+       if (pdev == NULL || info == NULL)
+               return -EINVAL;
+
+       if (!amd_iommu_v2_supported())
+               return -EINVAL;
+
+       memset(info, 0, sizeof(*info));
+
+       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ATS);
+       if (pos)
+               info->flags |= AMD_IOMMU_DEVICE_FLAG_ATS_SUP;
+
+       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
+       if (pos)
+               info->flags |= AMD_IOMMU_DEVICE_FLAG_PRI_SUP;
+
+       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
+       if (pos) {
+               int features;
+
+               max_pasids = 1 << (9 * (amd_iommu_max_glx_val + 1));
+               max_pasids = min(max_pasids, (1 << 20));
+
+               info->flags |= AMD_IOMMU_DEVICE_FLAG_PASID_SUP;
+               info->max_pasids = min(pci_max_pasids(pdev), max_pasids);
+
+               features = pci_pasid_features(pdev);
+               if (features & PCI_PASID_CAP_EXEC)
+                       info->flags |= AMD_IOMMU_DEVICE_FLAG_EXEC_SUP;
+               if (features & PCI_PASID_CAP_PRIV)
+                       info->flags |= AMD_IOMMU_DEVICE_FLAG_PRIV_SUP;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(amd_iommu_device_info);