Two generic timers (K and H) are capable of generating timer event
stream events. Provide a helper to calculate when the nearest one will
happen. This will eventually be used when implementing WFE/WFET.
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
target/arm/cpu.h | 10 ++++++
target/arm/helper.c | 85 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 95 insertions(+)
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 657ff4ab20b..bf6cf74c2e1 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -1208,6 +1208,16 @@ void arm_gt_sel2vtimer_cb(void *opaque);
unsigned int gt_cntfrq_period_ns(ARMCPU *cpu);
void gt_rme_post_el_change(ARMCPU *cpu, void *opaque);
+/**
+ * gt_calc_next_event_stream() - calculate and arm event stream timer
+ * @env: CPUArmState
+ *
+ * Calculate the next event stream time and return it. Returns -1 if
+ * no event streams are enabled. It is up to the WFE helpers to decide
+ * on the next time.
+ */
+int64_t gt_calc_next_event_stream(CPUARMState *env);
+
#define ARM_AFF0_SHIFT 0
#define ARM_AFF0_MASK (0xFFULL << ARM_AFF0_SHIFT)
#define ARM_AFF1_SHIFT 8
diff --git a/target/arm/helper.c b/target/arm/helper.c
index ba6db46d453..3627ffcdcfd 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -25,6 +25,7 @@
#include "exec/icount.h"
#include "system/kvm.h"
#include "system/tcg.h"
+#include "system/cpus.h"
#include "qapi/error.h"
#include "qemu/guest-random.h"
#ifdef CONFIG_TCG
@@ -2022,6 +2023,90 @@ void arm_gt_hvtimer_cb(void *opaque)
gt_recalc_timer(cpu, GTIMER_HYPVIRT);
}
+/*
+ * Event Stream events don't do anything apart from wake up sleeping
+ * cores. These helpers calculate the next event stream event time so
+ * the WFE helper can decide when its next wake up tick will be.
+ */
+static int64_t gt_recalc_one_evt(CPUARMState *env, uint32_t control, uint64_t offset)
+{
+ ARMCPU *cpu = env_archcpu(env);
+ bool evnten = FIELD_EX32(control, CNTxCTL, EVNTEN);
+
+ if (evnten) {
+ int evnti = FIELD_EX32(control, CNTxCTL, EVNTI);
+ bool evntis = FIELD_EX32(control, CNTxCTL, EVNTIS);
+ bool evntdir = FIELD_EX32(control, CNTxCTL, EVNTDIR);
+ /*
+ * To figure out when the next event timer should fire we need
+ * to calculate which bit of the counter we want to flip and
+ * which transition counts.
+ *
+ * So we calculate 1 << bit - current lower bits and then add
+ * 1 << bit if the bit needs to flip twice to meet evntdir
+ */
+ int bit = evntis ? evnti + 8 : evnti;
+ uint64_t count = gt_get_countervalue(env) - offset;
+ uint64_t target_bit = BIT_ULL(bit);
+ uint64_t lower_bits = MAKE_64BIT_MASK(0, bit - 1);
+ uint64_t next_tick = target_bit - (count & lower_bits);
+ uint64_t abstick;
+
+ /* do we need to bit flip twice? */
+ if (((count & target_bit) != 0) ^ evntdir) {
+ next_tick += target_bit;
+ }
+
+ /*
+ * Note that the desired next expiry time might be beyond the
+ * signed-64-bit range of a QEMUTimer -- in this case we just
+ * set the timer for as far in the future as possible. When the
+ * timer expires we will reset the timer for any remaining period.
+ */
+ if (uadd64_overflow(next_tick, offset, &abstick)) {
+ abstick = UINT64_MAX;
+ }
+ if (abstick > INT64_MAX / gt_cntfrq_period_ns(cpu)) {
+ return INT64_MAX;
+ } else {
+ return abstick;
+ }
+ }
+
+ return -1;
+}
+
+int64_t gt_calc_next_event_stream(CPUARMState *env)
+{
+ ARMCPU *cpu = env_archcpu(env);
+ uint64_t hcr = arm_hcr_el2_eff(env);
+ int64_t next_time = -1;
+ uint64_t offset;
+
+ /* Unless we are missing EL2 this can generate events */
+ if (arm_feature(env, ARM_FEATURE_EL2)) {
+ offset = gt_direct_access_timer_offset(env, GTIMER_PHYS);
+ next_time = gt_recalc_one_evt(env, env->cp15.cnthctl_el2, offset);
+ }
+
+ /* Event stream events from virtual counter enabled? */
+ if (!cpu_isar_feature(aa64_vh, cpu) ||
+ !((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE))) {
+ int64_t next_virt_time;
+ offset = gt_direct_access_timer_offset(env, GTIMER_VIRT);
+ next_virt_time = gt_recalc_one_evt(env, env->cp15.c14_cntkctl, offset);
+
+ /* is this earlier than the next physical event? */
+ if (next_virt_time > 0) {
+ if (next_time < 0 || next_virt_time < next_time) {
+ next_time = next_virt_time;
+ }
+ }
+ }
+
+ return next_time;
+}
+
static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
/*
* Note that CNTFRQ is purely reads-as-written for the benefit
--
2.47.3