Improvements to ARM GIC driver
authorJuan Castillo <juan.castillo@arm.com>
Mon, 20 Oct 2014 11:27:28 +0000 (12:27 +0100)
committerJuan Castillo <juan.castillo@arm.com>
Fri, 31 Oct 2014 18:40:23 +0000 (18:40 +0000)
This patch introduces several improvements to the ARM GIC driver:

* In function gicd_set_itargetsr(), target CPU is specified using
  the same bit mask detailed in the GICD_ITARGETSRn register instead
  of the CPU linear ID, removing the dependency between bit position
  and linear ID in the platform porting. The current CPU bit mask
  may be obtained by reading GICD_ITARGETSR0.

* PPIs and SGIs are initialized in arm_gic_pcpu_distif_setup().
  SPIs are initialized in arm_gic_distif_setup().

* By default, non secure interrupts are assigned the maximum
  priority allowed to a non secure interrupt (defined by
  GIC_HIGHEST_NS_PRIORITY).

* GICR base address is allowed to be NULL for GICv1 and GICv2.

Change-Id: Ie2837fe860d43b2282e582dfdb13c39c6186f232

drivers/arm/gic/arm_gic.c
drivers/arm/gic/gic_v2.c
include/drivers/arm/gic_v2.h

index 86aaa9a1ff744ddaa1a0c5256ac86c8aecca9a3d..58fbc89a7f3c5446bdaa05a711a9e2673654b2d0 100644 (file)
 #include <platform.h>
 #include <stdint.h>
 
+/* Value used to initialize Non-Secure IRQ priorities four at a time */
+#define GICD_IPRIORITYR_DEF_VAL \
+       (GIC_HIGHEST_NS_PRIORITY | \
+       (GIC_HIGHEST_NS_PRIORITY << 8) | \
+       (GIC_HIGHEST_NS_PRIORITY << 16) | \
+       (GIC_HIGHEST_NS_PRIORITY << 24))
 
 static unsigned int g_gicc_base;
 static unsigned int g_gicd_base;
@@ -216,8 +222,16 @@ void arm_gic_pcpu_distif_setup(void)
        unsigned int index, irq_num;
 
        assert(g_gicd_base);
+
+       /* Mark all 32 SGI+PPI interrupts as Group 1 (non-secure) */
        gicd_write_igroupr(g_gicd_base, 0, ~0);
 
+       /* Setup PPI priorities doing four at a time */
+       for (index = 0; index < 32; index += 4) {
+               gicd_write_ipriorityr(g_gicd_base, index,
+                               GICD_IPRIORITYR_DEF_VAL);
+       }
+
        assert(g_irq_sec_ptr);
        for (index = 0; index < g_num_irqs; index++) {
                irq_num = g_irq_sec_ptr[index];
@@ -231,6 +245,17 @@ void arm_gic_pcpu_distif_setup(void)
        }
 }
 
+/*******************************************************************************
+ * Get the current CPU bit mask from GICD_ITARGETSR0
+ ******************************************************************************/
+static unsigned int arm_gic_get_cpuif_id(void)
+{
+       unsigned int val;
+
+       val = gicd_read_itargetsr(g_gicd_base, 0);
+       return val & GIC_TARGET_CPU_MASK;
+}
+
 /*******************************************************************************
  * Global gic distributor setup which will be done by the primary cpu after a
  * cold boot. It marks out the secure SPIs, PPIs & SGIs and enables them. It
@@ -239,6 +264,7 @@ void arm_gic_pcpu_distif_setup(void)
 static void arm_gic_distif_setup(void)
 {
        unsigned int num_ints, ctlr, index, irq_num;
+       uint8_t target_cpu;
 
        /* Disable the distributor before going further */
        assert(g_gicd_base);
@@ -247,16 +273,24 @@ static void arm_gic_distif_setup(void)
        gicd_write_ctlr(g_gicd_base, ctlr);
 
        /*
-        * Mark out non-secure interrupts. Calculate number of
-        * IGROUPR registers to consider. Will be equal to the
-        * number of IT_LINES
+        * Mark out non-secure SPI interrupts. The number of interrupts is
+        * calculated as 32 * (IT_LINES + 1). We do 32 at a time.
         */
        num_ints = gicd_read_typer(g_gicd_base) & IT_LINES_NO_MASK;
-       num_ints++;
-       for (index = 0; index < num_ints; index++)
-               gicd_write_igroupr(g_gicd_base, index << IGROUPR_SHIFT, ~0);
+       num_ints = (num_ints + 1) << 5;
+       for (index = MIN_SPI_ID; index < num_ints; index += 32)
+               gicd_write_igroupr(g_gicd_base, index, ~0);
+
+       /* Setup SPI priorities doing four at a time */
+       for (index = MIN_SPI_ID; index < num_ints; index += 4) {
+               gicd_write_ipriorityr(g_gicd_base, index,
+                               GICD_IPRIORITYR_DEF_VAL);
+       }
+
+       /* Read the target CPU mask */
+       target_cpu = arm_gic_get_cpuif_id();
 
-       /* Configure secure interrupts now */
+       /* Configure SPI secure interrupts now */
        assert(g_irq_sec_ptr);
        for (index = 0; index < g_num_irqs; index++) {
                irq_num = g_irq_sec_ptr[index];
@@ -265,11 +299,16 @@ static void arm_gic_distif_setup(void)
                        gicd_clr_igroupr(g_gicd_base, irq_num);
                        gicd_set_ipriorityr(g_gicd_base, irq_num,
                                GIC_HIGHEST_SEC_PRIORITY);
-                       gicd_set_itargetsr(g_gicd_base, irq_num,
-                                       platform_get_core_pos(read_mpidr()));
+                       gicd_set_itargetsr(g_gicd_base, irq_num, target_cpu);
                        gicd_set_isenabler(g_gicd_base, irq_num);
                }
        }
+
+       /*
+        * Configure the SGI and PPI. This is done in a separated function
+        * because each CPU is responsible for initializing its own private
+        * interrupts.
+        */
        arm_gic_pcpu_distif_setup();
 
        gicd_write_ctlr(g_gicd_base, ctlr | ENABLE_GRP0);
@@ -285,13 +324,22 @@ void arm_gic_init(unsigned int gicc_base,
                unsigned int num_irqs
                )
 {
+       unsigned int val;
+
        assert(gicc_base);
        assert(gicd_base);
-       assert(gicr_base);
        assert(irq_sec_ptr);
+
        g_gicc_base = gicc_base;
        g_gicd_base = gicd_base;
-       g_gicr_base = gicr_base;
+
+       val = gicc_read_iidr(g_gicc_base);
+
+       if (((val >> GICC_IIDR_ARCH_SHIFT) & GICC_IIDR_ARCH_MASK) >= 3) {
+               assert(gicr_base);
+               g_gicr_base = gicr_base;
+       }
+
        g_irq_sec_ptr = irq_sec_ptr;
        g_num_irqs = num_irqs;
 }
index 27a39b9c7379b1f09c7a95e36f4e9ae216706853..41603a98de0504abc69ade8f155430b8bca538a6 100644 (file)
@@ -283,13 +283,12 @@ void gicd_set_ipriorityr(unsigned int base, unsigned int id, unsigned int pri)
        mmio_write_32(reg, reg_val);
 }
 
-void gicd_set_itargetsr(unsigned int base, unsigned int id, unsigned int iface)
+void gicd_set_itargetsr(unsigned int base, unsigned int id, unsigned int target)
 {
        unsigned byte_off = id & ((1 << ITARGETSR_SHIFT) - 1);
        unsigned int reg_val = gicd_read_itargetsr(base, id);
 
-       gicd_write_itargetsr(base, id, reg_val |
-                            (1 << iface) << (byte_off << 3));
+       gicd_write_itargetsr(base, id, reg_val | (target << (byte_off << 3)));
 }
 
 /*******************************************************************************
index 4c6b0dcc161277f9d6b3d0f326ba6ceb24298148..a2d3eeec7a14b37237979a0e609b8b8f341db3bf 100644 (file)
@@ -48,6 +48,7 @@
 #define GIC_HIGHEST_NS_PRIORITY        128
 #define GIC_LOWEST_NS_PRIORITY 254 /* 255 would disable an interrupt */
 #define GIC_SPURIOUS_INTERRUPT 1023
+#define GIC_TARGET_CPU_MASK    0xff
 
 #define ENABLE_GRP0            (1 << 0)
 #define ENABLE_GRP1            (1 << 1)