dev-arm: Add basic support for level sensitive SPIs in GICv2
authorAdrien Pesle <adrien.pesle@arm.com>
Mon, 3 Sep 2018 14:43:24 +0000 (16:43 +0200)
committerGiacomo Travaglini <giacomo.travaglini@arm.com>
Mon, 1 Oct 2018 08:28:51 +0000 (08:28 +0000)
For level sensitive interrupt IRQ line must be cleared when interrupt is
deasserted. This is not the case for edge-trigerred interrupt.

Change-Id: Ib1660da74a296750c0eb9e20878d4ee64bd23130
Reviewed-on: https://gem5-review.googlesource.com/12944
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>

src/dev/arm/gic_v2.cc
src/dev/arm/gic_v2.hh

index fe5e1eac844a770f095613022a5c0fd938d14f69..fd846387cd855bea77895c1dd09bd2f0df407b55 100644 (file)
@@ -346,8 +346,10 @@ GicV2::readCpu(ContextID ctx, Addr daddr)
                 uint32_t int_num = 1 << intNumToBit(cpuHighestInt[ctx]);
                 getActiveInt(ctx, intNumToWord(cpuHighestInt[ctx])) |= int_num;
                 updateRunPri();
-                getPendingInt(ctx, intNumToWord(cpuHighestInt[ctx]))
-                  &= ~int_num;
+                if (!isLevelSensitive(ctx, active_int)) {
+                    getPendingInt(ctx, intNumToWord(cpuHighestInt[ctx]))
+                      &= ~int_num;
+                }
             }
 
             DPRINTF(Interrupt,
@@ -783,10 +785,17 @@ GicV2::updateIntState(int hint)
             }
         }
 
+        uint32_t prev_highest = cpuHighestInt[cpu];
         cpuHighestInt[cpu] = highest_int;
 
-        if (highest_int == SPURIOUS_INT)
+        if (highest_int == SPURIOUS_INT) {
+            if (isLevelSensitive(cpu, prev_highest)) {
+
+                DPRINTF(Interrupt, "Clear IRQ for cpu%d\n", cpu);
+                platform->intrctrl->clear(cpu, ArmISA::INT_IRQ, 0);
+            }
             continue;
+        }
 
         /* @todo make this work for more than one cpu, need to handle 1:N, N:N
          * models */
@@ -855,9 +864,22 @@ GicV2::sendPPInt(uint32_t num, uint32_t cpu)
 }
 
 void
-GicV2::clearInt(uint32_t number)
+GicV2::clearInt(uint32_t num)
 {
-    /* @todo assume edge triggered only at the moment. Nothing to do. */
+    if (isLevelSensitive(0, num)) {
+        uint8_t target = getCpuTarget(0, num);
+
+        DPRINTF(Interrupt,
+                "Received Clear interrupt number %d, cpuTarget %#x:\n",
+                num, target);
+
+        getPendingInt(target, intNumToWord(num)) &= ~(1 << intNumToBit(num));
+        updateIntState(intNumToWord(num));
+    } else {
+        /* Nothing to do :
+         * Edge-triggered interrupt remain pending until software
+         * writes GICD_ICPENDR or reads GICC_IAR */
+    }
 }
 
 void
index 5791250d1bb2e8eac4941636823672f8b57931d9..352b108d08693bfaa171aa0f35baef22bcfb13b5 100644 (file)
@@ -262,6 +262,16 @@ class GicV2 : public BaseGic, public BaseGicRegisters
         }
     }
 
+    /** GICD_ICFGRn
+     * get 2 bit config associated to an interrupt.
+     */
+    uint8_t getIntConfig(ContextID ctx, uint32_t ix) {
+        assert(ix < INT_LINES_MAX);
+        const uint8_t cfg_low = intNumToBit(ix * 2);
+        const uint8_t cfg_hi = cfg_low + 1;
+        return bits(intConfig[intNumToWord(ix * 2)], cfg_hi, cfg_low);
+    }
+
     /** GICD_ITARGETSR{8..255}
      * an 8 bit cpu target id for each global interrupt.
      */
@@ -291,6 +301,14 @@ class GicV2 : public BaseGic, public BaseGicRegisters
      * and if it is 1:N or N:N */
     uint32_t intConfig[INT_BITS_MAX*2];
 
+    bool isLevelSensitive(ContextID ctx, uint32_t ix) {
+        if (ix == SPURIOUS_INT) {
+            return false;
+        } else {
+            return bits(getIntConfig(ctx, ix), 1) == 0;
+        }
+    }
+
     /** CPU enabled */
     bool cpuEnabled[CPU_MAX];