Merge tag 'perf-core-for-mingo-5.2-20190517' of git://git.kernel.org/pub/scm/linux...
[linux-2.6-microblaze.git] / drivers / iommu / io-pgtable-arm.c
index d3700ec..4e21efb 100644 (file)
 #define ARM_LPAE_MAIR_ATTR_IDX_CACHE   1
 #define ARM_LPAE_MAIR_ATTR_IDX_DEV     2
 
+#define ARM_MALI_LPAE_TTBR_ADRMODE_TABLE (3u << 0)
+#define ARM_MALI_LPAE_TTBR_READ_INNER  BIT(2)
+#define ARM_MALI_LPAE_TTBR_SHARE_OUTER BIT(4)
+
 /* IOPTE accessors */
 #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d))
 
 
 #define iopte_prot(pte)        ((pte) & ARM_LPAE_PTE_ATTR_MASK)
 
-#define iopte_leaf(pte,l)                                      \
-       (l == (ARM_LPAE_MAX_LEVELS - 1) ?                       \
-               (iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_PAGE) : \
-               (iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_BLOCK))
-
 struct arm_lpae_io_pgtable {
        struct io_pgtable       iop;
 
@@ -198,6 +197,15 @@ struct arm_lpae_io_pgtable {
 
 typedef u64 arm_lpae_iopte;
 
+static inline bool iopte_leaf(arm_lpae_iopte pte, int lvl,
+                             enum io_pgtable_fmt fmt)
+{
+       if (lvl == (ARM_LPAE_MAX_LEVELS - 1) && fmt != ARM_MALI_LPAE)
+               return iopte_type(pte, lvl) == ARM_LPAE_PTE_TYPE_PAGE;
+
+       return iopte_type(pte, lvl) == ARM_LPAE_PTE_TYPE_BLOCK;
+}
+
 static arm_lpae_iopte paddr_to_iopte(phys_addr_t paddr,
                                     struct arm_lpae_io_pgtable *data)
 {
@@ -303,12 +311,14 @@ static void __arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
        if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS)
                pte |= ARM_LPAE_PTE_NS;
 
-       if (lvl == ARM_LPAE_MAX_LEVELS - 1)
+       if (data->iop.fmt != ARM_MALI_LPAE && lvl == ARM_LPAE_MAX_LEVELS - 1)
                pte |= ARM_LPAE_PTE_TYPE_PAGE;
        else
                pte |= ARM_LPAE_PTE_TYPE_BLOCK;
 
-       pte |= ARM_LPAE_PTE_AF | ARM_LPAE_PTE_SH_IS;
+       if (data->iop.fmt != ARM_MALI_LPAE)
+               pte |= ARM_LPAE_PTE_AF;
+       pte |= ARM_LPAE_PTE_SH_IS;
        pte |= paddr_to_iopte(paddr, data);
 
        __arm_lpae_set_pte(ptep, pte, &data->iop.cfg);
@@ -321,7 +331,7 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
 {
        arm_lpae_iopte pte = *ptep;
 
-       if (iopte_leaf(pte, lvl)) {
+       if (iopte_leaf(pte, lvl, data->iop.fmt)) {
                /* We require an unmap first */
                WARN_ON(!selftest_running);
                return -EEXIST;
@@ -409,7 +419,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
                __arm_lpae_sync_pte(ptep, cfg);
        }
 
-       if (pte && !iopte_leaf(pte, lvl)) {
+       if (pte && !iopte_leaf(pte, lvl, data->iop.fmt)) {
                cptep = iopte_deref(pte, data);
        } else if (pte) {
                /* We require an unmap first */
@@ -429,31 +439,37 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data,
        if (data->iop.fmt == ARM_64_LPAE_S1 ||
            data->iop.fmt == ARM_32_LPAE_S1) {
                pte = ARM_LPAE_PTE_nG;
-
                if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
                        pte |= ARM_LPAE_PTE_AP_RDONLY;
-
                if (!(prot & IOMMU_PRIV))
                        pte |= ARM_LPAE_PTE_AP_UNPRIV;
-
-               if (prot & IOMMU_MMIO)
-                       pte |= (ARM_LPAE_MAIR_ATTR_IDX_DEV
-                               << ARM_LPAE_PTE_ATTRINDX_SHIFT);
-               else if (prot & IOMMU_CACHE)
-                       pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE
-                               << ARM_LPAE_PTE_ATTRINDX_SHIFT);
        } else {
                pte = ARM_LPAE_PTE_HAP_FAULT;
                if (prot & IOMMU_READ)
                        pte |= ARM_LPAE_PTE_HAP_READ;
                if (prot & IOMMU_WRITE)
                        pte |= ARM_LPAE_PTE_HAP_WRITE;
+       }
+
+       /*
+        * Note that this logic is structured to accommodate Mali LPAE
+        * having stage-1-like attributes but stage-2-like permissions.
+        */
+       if (data->iop.fmt == ARM_64_LPAE_S2 ||
+           data->iop.fmt == ARM_32_LPAE_S2) {
                if (prot & IOMMU_MMIO)
                        pte |= ARM_LPAE_PTE_MEMATTR_DEV;
                else if (prot & IOMMU_CACHE)
                        pte |= ARM_LPAE_PTE_MEMATTR_OIWB;
                else
                        pte |= ARM_LPAE_PTE_MEMATTR_NC;
+       } else {
+               if (prot & IOMMU_MMIO)
+                       pte |= (ARM_LPAE_MAIR_ATTR_IDX_DEV
+                               << ARM_LPAE_PTE_ATTRINDX_SHIFT);
+               else if (prot & IOMMU_CACHE)
+                       pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE
+                               << ARM_LPAE_PTE_ATTRINDX_SHIFT);
        }
 
        if (prot & IOMMU_NOEXEC)
@@ -511,7 +527,7 @@ static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl,
        while (ptep != end) {
                arm_lpae_iopte pte = *ptep++;
 
-               if (!pte || iopte_leaf(pte, lvl))
+               if (!pte || iopte_leaf(pte, lvl, data->iop.fmt))
                        continue;
 
                __arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data));
@@ -602,7 +618,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
        if (size == ARM_LPAE_BLOCK_SIZE(lvl, data)) {
                __arm_lpae_set_pte(ptep, 0, &iop->cfg);
 
-               if (!iopte_leaf(pte, lvl)) {
+               if (!iopte_leaf(pte, lvl, iop->fmt)) {
                        /* Also flush any partial walks */
                        io_pgtable_tlb_add_flush(iop, iova, size,
                                                ARM_LPAE_GRANULE(data), false);
@@ -621,7 +637,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
                }
 
                return size;
-       } else if (iopte_leaf(pte, lvl)) {
+       } else if (iopte_leaf(pte, lvl, iop->fmt)) {
                /*
                 * Insert a table at the next level to map the old region,
                 * minus the part we want to unmap
@@ -669,7 +685,7 @@ static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,
                        return 0;
 
                /* Leaf entry? */
-               if (iopte_leaf(pte,lvl))
+               if (iopte_leaf(pte, lvl, data->iop.fmt))
                        goto found_translation;
 
                /* Take it to the next level */
@@ -995,6 +1011,32 @@ arm_32_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie)
        return iop;
 }
 
+static struct io_pgtable *
+arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
+{
+       struct io_pgtable *iop;
+
+       if (cfg->ias != 48 || cfg->oas > 40)
+               return NULL;
+
+       cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G);
+       iop = arm_64_lpae_alloc_pgtable_s1(cfg, cookie);
+       if (iop) {
+               u64 mair, ttbr;
+
+               /* Copy values as union fields overlap */
+               mair = cfg->arm_lpae_s1_cfg.mair[0];
+               ttbr = cfg->arm_lpae_s1_cfg.ttbr[0];
+
+               cfg->arm_mali_lpae_cfg.memattr = mair;
+               cfg->arm_mali_lpae_cfg.transtab = ttbr |
+                       ARM_MALI_LPAE_TTBR_READ_INNER |
+                       ARM_MALI_LPAE_TTBR_ADRMODE_TABLE;
+       }
+
+       return iop;
+}
+
 struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
        .alloc  = arm_64_lpae_alloc_pgtable_s1,
        .free   = arm_lpae_free_pgtable,
@@ -1015,6 +1057,11 @@ struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns = {
        .free   = arm_lpae_free_pgtable,
 };
 
+struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns = {
+       .alloc  = arm_mali_lpae_alloc_pgtable,
+       .free   = arm_lpae_free_pgtable,
+};
+
 #ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST
 
 static struct io_pgtable_cfg *cfg_cookie;