irqchip/gic-v4.1: Ensure L2 vPE table is allocated at RD level
[linux-2.6-microblaze.git] / drivers / irqchip / irq-gic-v3-its.c
index e5a25d9..ae4e7b3 100644 (file)
@@ -2376,6 +2376,8 @@ static u64 inherit_vpe_l1_table_from_its(void)
                        continue;
 
                /* We have a winner! */
+               gic_data_rdist()->vpe_l1_base = its->tables[2].base;
+
                val  = GICR_VPROPBASER_4_1_VALID;
                if (baser & GITS_BASER_INDIRECT)
                        val |= GICR_VPROPBASER_4_1_INDIRECT;
@@ -2432,6 +2434,7 @@ static u64 inherit_vpe_l1_table_from_rd(cpumask_t **mask)
                val = gits_read_vpropbaser(base + SZ_128K + GICR_VPROPBASER);
                val &= ~GICR_VPROPBASER_4_1_Z;
 
+               gic_data_rdist()->vpe_l1_base = gic_data_rdist_cpu(cpu)->vpe_l1_base;
                *mask = gic_data_rdist_cpu(cpu)->vpe_table_mask;
 
                return val;
@@ -2440,6 +2443,72 @@ static u64 inherit_vpe_l1_table_from_rd(cpumask_t **mask)
        return 0;
 }
 
+static bool allocate_vpe_l2_table(int cpu, u32 id)
+{
+       void __iomem *base = gic_data_rdist_cpu(cpu)->rd_base;
+       u64 val, gpsz, npg;
+       unsigned int psz, esz, idx;
+       struct page *page;
+       __le64 *table;
+
+       if (!gic_rdists->has_rvpeid)
+               return true;
+
+       val  = gits_read_vpropbaser(base + SZ_128K + GICR_VPROPBASER);
+
+       esz  = FIELD_GET(GICR_VPROPBASER_4_1_ENTRY_SIZE, val) + 1;
+       gpsz = FIELD_GET(GICR_VPROPBASER_4_1_PAGE_SIZE, val);
+       npg  = FIELD_GET(GICR_VPROPBASER_4_1_SIZE, val) + 1;
+
+       switch (gpsz) {
+       default:
+               WARN_ON(1);
+               /* fall through */
+       case GIC_PAGE_SIZE_4K:
+               psz = SZ_4K;
+               break;
+       case GIC_PAGE_SIZE_16K:
+               psz = SZ_16K;
+               break;
+       case GIC_PAGE_SIZE_64K:
+               psz = SZ_64K;
+               break;
+       }
+
+       /* Don't allow vpe_id that exceeds single, flat table limit */
+       if (!(val & GICR_VPROPBASER_4_1_INDIRECT))
+               return (id < (npg * psz / (esz * SZ_8)));
+
+       /* Compute 1st level table index & check if that exceeds table limit */
+       idx = id >> ilog2(psz / (esz * SZ_8));
+       if (idx >= (npg * psz / GITS_LVL1_ENTRY_SIZE))
+               return false;
+
+       table = gic_data_rdist_cpu(cpu)->vpe_l1_base;
+
+       /* Allocate memory for 2nd level table */
+       if (!table[idx]) {
+               page = alloc_pages(GFP_KERNEL | __GFP_ZERO, get_order(psz));
+               if (!page)
+                       return false;
+
+               /* Flush Lvl2 table to PoC if hw doesn't support coherency */
+               if (!(val & GICR_VPROPBASER_SHAREABILITY_MASK))
+                       gic_flush_dcache_to_poc(page_address(page), psz);
+
+               table[idx] = cpu_to_le64(page_to_phys(page) | GITS_BASER_VALID);
+
+               /* Flush Lvl1 entry to PoC if hw doesn't support coherency */
+               if (!(val & GICR_VPROPBASER_SHAREABILITY_MASK))
+                       gic_flush_dcache_to_poc(table + idx, GITS_LVL1_ENTRY_SIZE);
+
+               /* Ensure updated table contents are visible to RD hardware */
+               dsb(sy);
+       }
+
+       return true;
+}
+
 static int allocate_vpe_l1_table(void)
 {
        void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
@@ -2531,7 +2600,7 @@ static int allocate_vpe_l1_table(void)
                npg = 1;
        }
 
-       val |= FIELD_PREP(GICR_VPROPBASER_4_1_SIZE, npg);
+       val |= FIELD_PREP(GICR_VPROPBASER_4_1_SIZE, npg - 1);
 
        /* Right, that's the number of CPU pages we need for L1 */
        np = DIV_ROUND_UP(npg * psz, PAGE_SIZE);
@@ -2542,7 +2611,7 @@ static int allocate_vpe_l1_table(void)
        if (!page)
                return -ENOMEM;
 
-       gic_data_rdist()->vpe_l1_page = page;
+       gic_data_rdist()->vpe_l1_base = page_address(page);
        pa = virt_to_phys(page_address(page));
        WARN_ON(!IS_ALIGNED(pa, psz));
 
@@ -2954,6 +3023,7 @@ static bool its_alloc_device_table(struct its_node *its, u32 dev_id)
 static bool its_alloc_vpe_table(u32 vpe_id)
 {
        struct its_node *its;
+       int cpu;
 
        /*
         * Make sure the L2 tables are allocated on *all* v4 ITSs. We
@@ -2976,6 +3046,19 @@ static bool its_alloc_vpe_table(u32 vpe_id)
                        return false;
        }
 
+       /* Non v4.1? No need to iterate RDs and go back early. */
+       if (!gic_rdists->has_rvpeid)
+               return true;
+
+       /*
+        * Make sure the L2 tables are allocated for all copies of
+        * the L1 table on *all* v4.1 RDs.
+        */
+       for_each_possible_cpu(cpu) {
+               if (!allocate_vpe_l2_table(cpu, vpe_id))
+                       return false;
+       }
+
        return true;
 }