]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/iommu/arm-smmu-v3.c
Merge branches 'iommu/fixes', 'arm/exynos', 'arm/renesas', 'arm/smmu', 'arm/mediatek...
[karo-tx-linux.git] / drivers / iommu / arm-smmu-v3.c
index 4d6ec444a9d63aa18232088bc9415c1f1eb6dfaa..5806a6acc94ecd7543c2435558a0907ec0934ff2 100644 (file)
 #define STRTAB_STE_1_SHCFG_INCOMING    1UL
 #define STRTAB_STE_1_SHCFG_SHIFT       44
 
-#define STRTAB_STE_1_PRIVCFG_UNPRIV    2UL
-#define STRTAB_STE_1_PRIVCFG_SHIFT     48
-
 #define STRTAB_STE_2_S2VMID_SHIFT      0
 #define STRTAB_STE_2_S2VMID_MASK       0xffffUL
 #define STRTAB_STE_2_VTCR_SHIFT                32
 /* High-level queue structures */
 #define ARM_SMMU_POLL_TIMEOUT_US       100
 
+#define MSI_IOVA_BASE                  0x8000000
+#define MSI_IOVA_LENGTH                        0x100000
+
 static bool disable_bypass;
 module_param_named(disable_bypass, disable_bypass, bool, S_IRUGO);
 MODULE_PARM_DESC(disable_bypass,
@@ -616,6 +616,9 @@ struct arm_smmu_device {
        unsigned int                    sid_bits;
 
        struct arm_smmu_strtab_cfg      strtab_cfg;
+
+       /* IOMMU core code handle */
+       struct iommu_device             iommu;
 };
 
 /* SMMU private data for each master */
@@ -1042,13 +1045,8 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
                }
        }
 
-       /* Nuke the existing Config, as we're going to rewrite it */
-       val &= ~(STRTAB_STE_0_CFG_MASK << STRTAB_STE_0_CFG_SHIFT);
-
-       if (ste->valid)
-               val |= STRTAB_STE_0_V;
-       else
-               val &= ~STRTAB_STE_0_V;
+       /* Nuke the existing STE_0 value, as we're going to rewrite it */
+       val = ste->valid ? STRTAB_STE_0_V : 0;
 
        if (ste->bypass) {
                val |= disable_bypass ? STRTAB_STE_0_CFG_ABORT
@@ -1073,9 +1071,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
 #ifdef CONFIG_PCI_ATS
                         STRTAB_STE_1_EATS_TRANS << STRTAB_STE_1_EATS_SHIFT |
 #endif
-                        STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT |
-                        STRTAB_STE_1_PRIVCFG_UNPRIV <<
-                        STRTAB_STE_1_PRIVCFG_SHIFT);
+                        STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT);
 
                if (smmu->features & ARM_SMMU_FEAT_STALLS)
                        dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD);
@@ -1083,7 +1079,6 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
                val |= (ste->s1_cfg->cdptr_dma & STRTAB_STE_0_S1CTXPTR_MASK
                        << STRTAB_STE_0_S1CTXPTR_SHIFT) |
                        STRTAB_STE_0_CFG_S1_TRANS;
-
        }
 
        if (ste->s2_cfg) {
@@ -1372,8 +1367,6 @@ static bool arm_smmu_capable(enum iommu_cap cap)
        switch (cap) {
        case IOMMU_CAP_CACHE_COHERENCY:
                return true;
-       case IOMMU_CAP_INTR_REMAP:
-               return true; /* MSIs are just memory writes */
        case IOMMU_CAP_NOEXEC:
                return true;
        default:
@@ -1795,8 +1788,10 @@ static int arm_smmu_add_device(struct device *dev)
        }
 
        group = iommu_group_get_for_dev(dev);
-       if (!IS_ERR(group))
+       if (!IS_ERR(group)) {
                iommu_group_put(group);
+               iommu_device_link(&smmu->iommu, dev);
+       }
 
        return PTR_ERR_OR_ZERO(group);
 }
@@ -1805,14 +1800,17 @@ static void arm_smmu_remove_device(struct device *dev)
 {
        struct iommu_fwspec *fwspec = dev->iommu_fwspec;
        struct arm_smmu_master_data *master;
+       struct arm_smmu_device *smmu;
 
        if (!fwspec || fwspec->ops != &arm_smmu_ops)
                return;
 
        master = fwspec->iommu_priv;
+       smmu = master->smmu;
        if (master && master->ste.valid)
                arm_smmu_detach_dev(dev);
        iommu_group_remove_device(dev);
+       iommu_device_unlink(&smmu->iommu, dev);
        kfree(master);
        iommu_fwspec_free(dev);
 }
@@ -1883,6 +1881,29 @@ static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
        return iommu_fwspec_add_ids(dev, args->args, 1);
 }
 
+static void arm_smmu_get_resv_regions(struct device *dev,
+                                     struct list_head *head)
+{
+       struct iommu_resv_region *region;
+       int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+
+       region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH,
+                                        prot, IOMMU_RESV_MSI);
+       if (!region)
+               return;
+
+       list_add_tail(&region->list, head);
+}
+
+static void arm_smmu_put_resv_regions(struct device *dev,
+                                     struct list_head *head)
+{
+       struct iommu_resv_region *entry, *next;
+
+       list_for_each_entry_safe(entry, next, head, list)
+               kfree(entry);
+}
+
 static struct iommu_ops arm_smmu_ops = {
        .capable                = arm_smmu_capable,
        .domain_alloc           = arm_smmu_domain_alloc,
@@ -1898,6 +1919,8 @@ static struct iommu_ops arm_smmu_ops = {
        .domain_get_attr        = arm_smmu_domain_get_attr,
        .domain_set_attr        = arm_smmu_domain_set_attr,
        .of_xlate               = arm_smmu_of_xlate,
+       .get_resv_regions       = arm_smmu_get_resv_regions,
+       .put_resv_regions       = arm_smmu_put_resv_regions,
        .pgsize_bitmap          = -1UL, /* Restricted during device attach */
 };
 
@@ -1983,17 +2006,9 @@ static int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu)
        u32 size, l1size;
        struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
 
-       /*
-        * If we can resolve everything with a single L2 table, then we
-        * just need a single L1 descriptor. Otherwise, calculate the L1
-        * size, capped to the SIDSIZE.
-        */
-       if (smmu->sid_bits < STRTAB_SPLIT) {
-               size = 0;
-       } else {
-               size = STRTAB_L1_SZ_SHIFT - (ilog2(STRTAB_L1_DESC_DWORDS) + 3);
-               size = min(size, smmu->sid_bits - STRTAB_SPLIT);
-       }
+       /* Calculate the L1 size, capped to the SIDSIZE. */
+       size = STRTAB_L1_SZ_SHIFT - (ilog2(STRTAB_L1_DESC_DWORDS) + 3);
+       size = min(size, smmu->sid_bits - STRTAB_SPLIT);
        cfg->num_l1_ents = 1 << size;
 
        size += STRTAB_SPLIT;
@@ -2504,6 +2519,13 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
        smmu->ssid_bits = reg >> IDR1_SSID_SHIFT & IDR1_SSID_MASK;
        smmu->sid_bits = reg >> IDR1_SID_SHIFT & IDR1_SID_MASK;
 
+       /*
+        * If the SMMU supports fewer bits than would fill a single L2 stream
+        * table, use a linear table instead.
+        */
+       if (smmu->sid_bits <= STRTAB_SPLIT)
+               smmu->features &= ~ARM_SMMU_FEAT_2_LVL_STRTAB;
+
        /* IDR5 */
        reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5);
 
@@ -2613,6 +2635,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
 {
        int irq, ret;
        struct resource *res;
+       resource_size_t ioaddr;
        struct arm_smmu_device *smmu;
        struct device *dev = &pdev->dev;
        bool bypass;
@@ -2630,6 +2653,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
                dev_err(dev, "MMIO region too small (%pr)\n", res);
                return -EINVAL;
        }
+       ioaddr = res->start;
 
        smmu->base = devm_ioremap_resource(dev, res);
        if (IS_ERR(smmu->base))
@@ -2682,7 +2706,15 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
                return ret;
 
        /* And we're up. Go go go! */
-       iommu_register_instance(dev->fwnode, &arm_smmu_ops);
+       ret = iommu_device_sysfs_add(&smmu->iommu, dev, NULL,
+                                    "smmu3.%pa", &ioaddr);
+       if (ret)
+               return ret;
+
+       iommu_device_set_ops(&smmu->iommu, &arm_smmu_ops);
+       iommu_device_set_fwnode(&smmu->iommu, dev->fwnode);
+
+       ret = iommu_device_register(&smmu->iommu);
 
 #ifdef CONFIG_PCI
        if (pci_bus_type.iommu_ops != &arm_smmu_ops) {