@@ -40,6 +40,8 @@
#include "disas/capstone.h"
#include "fpu/softfloat.h"
+#include <inttypes.h>
+
static void arm_cpu_set_pc(CPUState *cs, vaddr value)
{
ARMCPU *cpu = ARM_CPU(cs);
@@ -976,6 +978,10 @@ static void arm_cpu_initfn(Object *obj)
}
}
+static Property arm_cpu_cp_cntfrq_property =
+ DEFINE_PROP_UINT64("cntfrq", ARMCPU, env.cp15.c14_cntfrq,
+ (1000 * 1000 * 1000) / GTIMER_SCALE);
+
static Property arm_cpu_reset_cbar_property =
DEFINE_PROP_UINT64("reset-cbar", ARMCPU, reset_cbar, 0);
@@ -1172,6 +1178,11 @@ void arm_cpu_post_init(Object *obj)
qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property,
&error_abort);
+
+ if (arm_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER)) {
+ qdev_property_add_static(DEVICE(cpu), &arm_cpu_cp_cntfrq_property,
+ &error_abort);
+ }
}
static void arm_cpu_finalizefn(Object *obj)
@@ -1238,14 +1249,23 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
}
}
- cpu->gt_timer[GTIMER_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE,
- arm_gt_ptimer_cb, cpu);
- cpu->gt_timer[GTIMER_VIRT] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE,
- arm_gt_vtimer_cb, cpu);
- cpu->gt_timer[GTIMER_HYP] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE,
- arm_gt_htimer_cb, cpu);
- cpu->gt_timer[GTIMER_SEC] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE,
- arm_gt_stimer_cb, cpu);
+ if (!env->cp15.c14_cntfrq) {
+ error_setg(errp, "Invalid CNTFRQ: %"PRIx64"Hz", env->cp15.c14_cntfrq);
+ return;
+ }
+
+ {
+ uint64_t scale = MAX(1, NANOSECONDS_PER_SECOND / env->cp15.c14_cntfrq);
+
+ cpu->gt_timer[GTIMER_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, scale,
+ arm_gt_ptimer_cb, cpu);
+ cpu->gt_timer[GTIMER_VIRT] = timer_new(QEMU_CLOCK_VIRTUAL, scale,
+ arm_gt_vtimer_cb, cpu);
+ cpu->gt_timer[GTIMER_HYP] = timer_new(QEMU_CLOCK_VIRTUAL, scale,
+ arm_gt_htimer_cb, cpu);
+ cpu->gt_timer[GTIMER_SEC] = timer_new(QEMU_CLOCK_VIRTUAL, scale,
+ arm_gt_stimer_cb, cpu);
+ }
#endif
cpu_exec_realizefn(cs, &local_err);
@@ -2409,7 +2409,21 @@ static CPAccessResult gt_stimer_access(CPUARMState *env,
static uint64_t gt_get_countervalue(CPUARMState *env)
{
- return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / GTIMER_SCALE;
+ uint64_t effective;
+
+ /*
+ * Deal with quantized clock scaling by calculating the effective frequency
+ * in terms of the timer scale.
+ */
+ if (env->cp15.c14_cntfrq <= NANOSECONDS_PER_SECOND) {
+ uint32_t scale = NANOSECONDS_PER_SECOND / env->cp15.c14_cntfrq;
+ effective = NANOSECONDS_PER_SECOND / scale;
+ } else {
+ effective = NANOSECONDS_PER_SECOND;
+ }
+
+ return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), effective,
+ NANOSECONDS_PER_SECOND);
}
static void gt_recalc_timer(ARMCPU *cpu, int timeridx)
@@ -2445,8 +2459,8 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx)
* 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 (nexttick > INT64_MAX / GTIMER_SCALE) {
- nexttick = INT64_MAX / GTIMER_SCALE;
+ if (nexttick > INT64_MAX / cpu->gt_timer[timeridx]->scale) {
+ nexttick = INT64_MAX / cpu->gt_timer[timeridx]->scale;
}
timer_mod(cpu->gt_timer[timeridx], nexttick);
trace_arm_gt_recalc(timeridx, irqstate, nexttick);
Allow machines to configure CNTFRQ via a property if the ARM core supports the generic timer. This is necessary on e.g. the ASPEED AST2600 SoC where the generic timer clock is run at 800MHz or above. CNTFRQ is a read-as-written co-processor register; the property sets the register's initial value which is used during realize() to configure the QEMUTimers that back the generic timers. Beyond that the firmware can to do whatever it sees fit with the CNTFRQ register though changes to the value will not be reflected in the timers' rate. Signed-off-by: Andrew Jeffery <andrew@aj.id.au> --- Peter - I think this addresses most of your feedback. I still reach into the QEMUTimer to fetch out scale when adjusting the nexttick calculation, but we no longer need to update the scale member and force a recalculation of the period. --- target/arm/cpu.c | 36 ++++++++++++++++++++++++++++-------- target/arm/helper.c | 20 +++++++++++++++++--- 2 files changed, 45 insertions(+), 11 deletions(-)