Now we can calculate when the next event stream event is we can re-use
the wfxt_timer and configure it to fire as we enter a WFE that is
going to sleep. Reverse the M-profile logic so we can enter a sleep
state in both profiles.
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
target/arm/cpu.h | 2 ++
target/arm/cpu.c | 5 +++++
target/arm/tcg/op_helper.c | 37 ++++++++++++++++++-------------------
3 files changed, 25 insertions(+), 19 deletions(-)
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 9c25b60ae83..6a7d92d73db 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -967,6 +967,8 @@ struct ArchCPU {
QEMUTimer *pmu_timer;
/* Timer used for WFxT timeouts */
QEMUTimer *wfxt_timer;
+ /* Are we in a WFE */
+ bool waiting_for_event;
/* GPIO outputs for generic timer */
qemu_irq gt_timer_outputs[NUM_GTIMERS];
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index d51599dfdab..a0569f74cd5 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -780,6 +780,11 @@ static void arm_wfxt_timer_cb(void *opaque)
ARMCPU *cpu = opaque;
CPUState *cs = CPU(cpu);
+ if (cpu->waiting_for_event) {
+ CPUARMState *env = &cpu->env;
+ env->event_register.as_bool = true;
+ }
+
/*
* We expect the CPU to be halted; this will cause arm_cpu_is_work()
* to return true (so we will come out of halt even with no other
diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c
index d513045269c..fbe160ab70a 100644
--- a/target/arm/tcg/op_helper.c
+++ b/target/arm/tcg/op_helper.c
@@ -494,31 +494,30 @@ void HELPER(wfe)(CPUARMState *env)
#else
/*
* WFE (Wait For Event) is a hint instruction.
- * For Cortex-M (M-profile), we implement the strict architectural behavior:
+ *
* 1. Check the Event Register (set by SEV or SEVONPEND).
* 2. If set, clear it and continue (consume the event).
*/
- if (arm_feature(env, ARM_FEATURE_M)) {
- CPUState *cs = env_cpu(env);
+ CPUState *cs = env_cpu(env);
+ ARMCPU *cpu = ARM_CPU(cs);
- if (env->event_register.as_bool) {
- env->event_register.as_bool = false;
- return;
- }
+ if (env->event_register.as_bool) {
+ env->event_register.as_bool = false;
+ return;
+ }
- cs->exception_index = EXCP_HLT;
- cs->halted = 1;
- cpu_loop_exit(cs);
- } else {
- /*
- * For A-profile and others, we rely on the existing "yield" behavior.
- * Don't actually halt the CPU, just yield back to top
- * level loop. This is not going into a "low power state"
- * (ie halting until some event occurs), so we never take
- * a configurable trap to a different exception level
- */
- HELPER(yield)(env);
+ /* For A-profile we also can be woken by the event stream */
+ if (arm_feature(env, ARM_FEATURE_AARCH64) && cpu->wfxt_timer) {
+ int64_t next_event = gt_calc_next_event_stream(env);
+ if (next_event > 0) {
+ cpu->waiting_for_event = true;
+ timer_mod(cpu->wfxt_timer, next_event);
+ }
}
+
+ cs->exception_index = EXCP_HLT;
+ cs->halted = 1;
+ cpu_loop_exit(cs);
#endif
}
--
2.47.3