arm/gic-v3: Parse per-cpu redistributor entry in GICC subtable
authorShanker Donthineni <shankerd@codeaurora.org>
Mon, 27 Jun 2016 20:33:36 +0000 (15:33 -0500)
committerStefano Stabellini <sstabellini@kernel.org>
Thu, 14 Jul 2016 14:13:58 +0000 (15:13 +0100)
The redistributor address can be specified either as part of GICC or
GICR subtable depending on the power domain. The current driver
doesn't support parsing redistributor entry that is defined in GICC
subtable. The GIC CPU subtable entry holds the associated Redistributor
base address if it is not on always-on power domain.

The per CPU Redistributor size is not defined in ACPI specification.
Set the GICR region size to SZ_256K if the GIC hardware is capable of
Direct Virtual LPI Injection feature, SZ_128K otherwise.

This patch adds necessary code to handle both types of Redistributors
base addresses.

Signed-off-by: Shanker Donthineni <shankerd@codeaurora.org>
Acked-by: Julien Grall <julien.grall@arm.com>
Acked-by: Stefano Stabellini <sstabellini@kernel.org>
xen/arch/arm/gic-v3.c
xen/include/asm-arm/gic.h
xen/include/asm-arm/gic_v3_defs.h

index 8aac65ddd35d47ca7d66f19d195e2cbf4d1263dc..fffde469b5eff2a9aaef9cf68f1f8dde36b8c029 100644 (file)
@@ -662,6 +662,10 @@ static int __init gicv3_populate_rdist(void)
                         smp_processor_id(), i, ptr);
                 return 0;
             }
+
+            if ( gicv3.rdist_regions[i].single_rdist )
+                break;
+
             if ( gicv3.rdist_stride )
                 ptr += gicv3.rdist_stride;
             else
@@ -1274,14 +1278,21 @@ static int gicv3_iomem_deny_access(const struct domain *d)
 }
 
 #ifdef CONFIG_ACPI
-static void __init gic_acpi_add_rdist_region(paddr_t base, paddr_t size)
+static void __init
+gic_acpi_add_rdist_region(paddr_t base, paddr_t size, bool single_rdist)
 {
     unsigned int idx = gicv3.rdist_count++;
 
+    gicv3.rdist_regions[idx].single_rdist = single_rdist;
     gicv3.rdist_regions[idx].base = base;
     gicv3.rdist_regions[idx].size = size;
 }
 
+static inline bool gic_dist_supports_dvis(void)
+{
+    return !!(readl_relaxed(GICD + GICD_TYPER) & GICD_TYPER_DVIS);
+}
+
 static int gicv3_make_hwdom_madt(const struct domain *d, u32 offset)
 {
     struct acpi_subtable_header *header;
@@ -1388,6 +1399,36 @@ gic_acpi_parse_madt_distributor(struct acpi_subtable_header *header,
     return 0;
 }
 
+static int __init
+gic_acpi_parse_cpu_redistributor(struct acpi_subtable_header *header,
+                                 const unsigned long end)
+{
+    struct acpi_madt_generic_interrupt *processor;
+    u32 size;
+
+    processor = (struct acpi_madt_generic_interrupt *)header;
+    if ( !(processor->flags & ACPI_MADT_ENABLED) )
+        return 0;
+
+    size = gic_dist_supports_dvis() ? 4 * SZ_64K : 2 * SZ_64K;
+    gic_acpi_add_rdist_region(processor->gicr_base_address, size, true);
+
+    return 0;
+}
+
+static int __init
+gic_acpi_get_madt_cpu_num(struct acpi_subtable_header *header,
+                          const unsigned long end)
+{
+    struct acpi_madt_generic_interrupt *cpuif;
+
+    cpuif = (struct acpi_madt_generic_interrupt *)header;
+    if ( BAD_MADT_ENTRY(cpuif, end) || !cpuif->gicr_base_address )
+        return -EINVAL;
+
+    return 0;
+}
+
 static int __init
 gic_acpi_parse_madt_redistributor(struct acpi_subtable_header *header,
                                   const unsigned long end)
@@ -1398,7 +1439,7 @@ gic_acpi_parse_madt_redistributor(struct acpi_subtable_header *header,
     if ( BAD_MADT_ENTRY(rdist, end) )
         return -EINVAL;
 
-    gic_acpi_add_rdist_region(rdist->base_address, rdist->length);
+    gic_acpi_add_rdist_region(rdist->base_address, rdist->length, false);
 
     return 0;
 }
@@ -1416,6 +1457,7 @@ gic_acpi_get_madt_redistributor_num(struct acpi_subtable_header *header,
 static void __init gicv3_acpi_init(void)
 {
     struct rdist_region *rdist_regs;
+    bool gicr_table = true;
     int count;
 
     /*
@@ -1432,8 +1474,15 @@ static void __init gicv3_acpi_init(void)
     /* Get number of redistributor */
     count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR,
                                   gic_acpi_get_madt_redistributor_num, 0);
-    if ( count <= 0 )
-        panic("GICv3: No valid GICR entries exists");
+    /* Count the total number of CPU interface entries */
+    if ( count <= 0 ) {
+        count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
+                                      gic_acpi_get_madt_cpu_num, 0);
+        if (count <= 0)
+            panic("GICv3: No valid GICR entries exists");
+
+        gicr_table = false;
+    }
 
     if ( count > MAX_RDIST_COUNT )
         panic("GICv3: Number of redistributor regions is more than"
@@ -1445,9 +1494,14 @@ static void __init gicv3_acpi_init(void)
 
     gicv3.rdist_regions = rdist_regs;
 
-    /* Parse always-on power domain Re-distributor entries */
-    count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR,
-                                  gic_acpi_parse_madt_redistributor, count);
+    if ( gicr_table )
+        /* Parse always-on power domain Re-distributor entries */
+        count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR,
+                                      gic_acpi_parse_madt_redistributor, count);
+    else
+        /* Parse Re-distributor entries described in CPU interface table */
+        count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
+                                      gic_acpi_parse_cpu_redistributor, count);
     if ( count <= 0 )
         panic("GICv3: Can't get Redistributor entry");
 
index 836f1ad0816a80c73e55ebc17a8aadc7b986be6d..f42b77c6a4d63e2baea97853733e3dd3ac5ef226 100644 (file)
 #define GICD_TYPE_CPUS_SHIFT 5
 #define GICD_TYPE_CPUS  0x0e0
 #define GICD_TYPE_SEC   0x400
+#define GICD_TYPER_DVIS (1U << 18)
 
 #define GICC_CTL_ENABLE 0x1
 #define GICC_CTL_EOI    (0x1 << 9)
index 6d98491e566f689ca132f996d926a2dc5148476b..6bd25a54a29329c3259033b61535ae3286bf3cad 100644 (file)
@@ -141,6 +141,7 @@ struct rdist_region {
     paddr_t base;
     paddr_t size;
     void __iomem *map_base;
+    bool single_rdist;
 };
 
 #endif /* __ASM_ARM_GIC_V3_DEFS_H__ */