summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--hw/xtensa_pic.c27
-rw-r--r--target-xtensa/cpu.h1
-rw-r--r--target-xtensa/op_helper.c18
-rw-r--r--tests/xtensa/test_timer.S63
4 files changed, 93 insertions, 16 deletions
diff --git a/hw/xtensa_pic.c b/hw/xtensa_pic.c
index 3033ae214a..e5085ea9e7 100644
--- a/hw/xtensa_pic.c
+++ b/hw/xtensa_pic.c
@@ -116,10 +116,35 @@ void xtensa_timer_irq(CPUState *env, uint32_t id, uint32_t active)
     qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active);
 }
 
+void xtensa_rearm_ccompare_timer(CPUState *env)
+{
+    int i;
+    uint32_t wake_ccount = env->sregs[CCOUNT] - 1;
+
+    for (i = 0; i < env->config->nccompare; ++i) {
+        if (env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] <
+                wake_ccount - env->sregs[CCOUNT]) {
+            wake_ccount = env->sregs[CCOMPARE + i];
+        }
+    }
+    env->wake_ccount = wake_ccount;
+    qemu_mod_timer(env->ccompare_timer, env->halt_clock +
+            muldiv64(wake_ccount - env->sregs[CCOUNT],
+                1000000, env->config->clock_freq_khz));
+}
+
 static void xtensa_ccompare_cb(void *opaque)
 {
     CPUState *env = opaque;
-    xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]);
+
+    if (env->halted) {
+        env->halt_clock = qemu_get_clock_ns(vm_clock);
+        xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]);
+        if (!cpu_has_work(env)) {
+            env->sregs[CCOUNT] = env->wake_ccount + 1;
+            xtensa_rearm_ccompare_timer(env);
+        }
+    }
 }
 
 void xtensa_irq_init(CPUState *env)
diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h
index 339075dda1..966f515249 100644
--- a/target-xtensa/cpu.h
+++ b/target-xtensa/cpu.h
@@ -313,6 +313,7 @@ void check_interrupts(CPUXtensaState *s);
 void xtensa_irq_init(CPUState *env);
 void xtensa_advance_ccount(CPUState *env, uint32_t d);
 void xtensa_timer_irq(CPUState *env, uint32_t id, uint32_t active);
+void xtensa_rearm_ccompare_timer(CPUState *env);
 int cpu_xtensa_signal_handler(int host_signum, void *pinfo, void *puc);
 void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf);
 void xtensa_sync_window_from_phys(CPUState *env);
diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c
index 64847fc8e6..0605611031 100644
--- a/target-xtensa/op_helper.c
+++ b/target-xtensa/op_helper.c
@@ -370,23 +370,11 @@ void HELPER(waiti)(uint32_t pc, uint32_t intlevel)
         return;
     }
 
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) {
-        int i;
-        uint32_t wake_ccount = env->sregs[CCOUNT] - 1;
-
-        for (i = 0; i < env->config->nccompare; ++i) {
-            if (env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] <
-                    wake_ccount - env->sregs[CCOUNT]) {
-                wake_ccount = env->sregs[CCOMPARE + i];
-            }
-        }
-        env->wake_ccount = wake_ccount;
-        qemu_mod_timer(env->ccompare_timer, qemu_get_clock_ns(vm_clock) +
-                muldiv64(wake_ccount - env->sregs[CCOUNT],
-                    1000000, env->config->clock_freq_khz));
-    }
     env->halt_clock = qemu_get_clock_ns(vm_clock);
     env->halted = 1;
+    if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) {
+        xtensa_rearm_ccompare_timer(env);
+    }
     HELPER(exception)(EXCP_HLT);
 }
 
diff --git a/tests/xtensa/test_timer.S b/tests/xtensa/test_timer.S
index ede63955cc..1041cc6658 100644
--- a/tests/xtensa/test_timer.S
+++ b/tests/xtensa/test_timer.S
@@ -14,6 +14,7 @@ test ccompare
     wsr     a2, intenable
     rsr     a2, interrupt
     wsr     a2, intclear
+    movi    a2, 0
     wsr     a2, ccompare1
     wsr     a2, ccompare2
 
@@ -37,6 +38,7 @@ test ccompare0_interrupt
     wsr     a2, intenable
     rsr     a2, interrupt
     wsr     a2, intclear
+    movi    a2, 0
     wsr     a2, ccompare1
     wsr     a2, ccompare2
 
@@ -66,6 +68,7 @@ test ccompare1_interrupt
     wsr     a2, intenable
     rsr     a2, interrupt
     wsr     a2, intclear
+    movi    a2, 0
     wsr     a2, ccompare0
     wsr     a2, ccompare2
 
@@ -92,6 +95,7 @@ test ccompare2_interrupt
     wsr     a2, intenable
     rsr     a2, interrupt
     wsr     a2, intclear
+    movi    a2, 0
     wsr     a2, ccompare0
     wsr     a2, ccompare1
 
@@ -112,4 +116,63 @@ test ccompare2_interrupt
 2:
 test_end
 
+test ccompare_interrupt_masked
+    set_vector kernel, 2f
+    movi    a2, 0
+    wsr     a2, intenable
+    rsr     a2, interrupt
+    wsr     a2, intclear
+    movi    a2, 0
+    wsr     a2, ccompare2
+
+    movi    a3, 40
+    rsr     a2, ccount
+    addi    a2, a2, 20
+    wsr     a2, ccompare1
+    addi    a2, a2, 20
+    wsr     a2, ccompare0
+    rsync
+    rsr     a2, interrupt
+    assert  eqi, a2, 0
+
+    movi    a2, 0x40
+    wsr     a2, intenable
+    rsil    a2, 0
+    loop    a3, 1f
+    nop
+1:
+    test_fail
+2:
+    rsr     a2, exccause
+    assert  eqi, a2, 4 /* LEVEL1_INTERRUPT_CAUSE */
+test_end
+
+test ccompare_interrupt_masked_waiti
+    set_vector kernel, 2f
+    movi    a2, 0
+    wsr     a2, intenable
+    rsr     a2, interrupt
+    wsr     a2, intclear
+    movi    a2, 0
+    wsr     a2, ccompare2
+
+    movi    a3, 40
+    rsr     a2, ccount
+    addi    a2, a2, 20
+    wsr     a2, ccompare1
+    addi    a2, a2, 20
+    wsr     a2, ccompare0
+    rsync
+    rsr     a2, interrupt
+    assert  eqi, a2, 0
+
+    movi    a2, 0x40
+    wsr     a2, intenable
+    waiti   0
+    test_fail
+2:
+    rsr     a2, exccause
+    assert  eqi, a2, 4 /* LEVEL1_INTERRUPT_CAUSE */
+test_end
+
 test_suite_end