summaryrefslogtreecommitdiff
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