@@ -27,6 +27,7 @@
#include "hw/timer/a9gtimer.h"
#include "migration/vmstate.h"
#include "qapi/error.h"
+#include "qemu/error-report.h"
#include "qemu/timer.h"
#include "qemu/bitops.h"
#include "qemu/log.h"
@@ -62,9 +63,17 @@ static inline int a9_gtimer_get_current_cpu(A9GTimerState *s)
static inline uint64_t a9_gtimer_get_conv(A9GTimerState *s)
{
+ /*
+ * Referring to the ARM-Cortex-A9 MPCore TRM
+ *
+ * The a9 global timer relies on the PERIPHCLK as its clock source.
+ * The PERIPHCLK clock period must be configured as a multiple of the
+ * main clock CLK. The conversion from the qemu clock (1GHz) to a9
+ * gtimer ticks can be calculated like this:
+ */
uint64_t prescale = extract32(s->control, R_CONTROL_PRESCALER_SHIFT,
R_CONTROL_PRESCALER_LEN) + 1;
- uint64_t ret = NANOSECONDS_PER_SECOND * prescale * 10;
+ uint64_t ret = NANOSECONDS_PER_SECOND * prescale * s->periphclk_period;
return (uint32_t) (ret / s->cpu_clk_freq_hz);
}
@@ -312,6 +321,12 @@ static void a9_gtimer_realize(DeviceState *dev, Error **errp)
sysbus_init_mmio(sbd, &s->iomem);
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, a9_gtimer_update_no_sync, s);
+ if (s->periphclk_period < 2) {
+ error_report("Invalid periphclk-period (%lu), must be >= 2",
+ s->periphclk_period);
+ exit(1);
+ }
+
for (i = 0; i < s->num_cpu; i++) {
A9GTimerPerCPU *gtb = &s->per_cpu[i];
@@ -377,6 +392,8 @@ static const Property a9_gtimer_properties[] = {
DEFINE_PROP_UINT64("cpu-freq", A9GTimerState, cpu_clk_freq_hz,
NANOSECONDS_PER_SECOND),
DEFINE_PROP_UINT32("num-cpu", A9GTimerState, num_cpu, 0),
+ DEFINE_PROP_UINT64("periphclk-period", A9GTimerState,
+ periphclk_period, 10),
};
static void a9_gtimer_class_init(ObjectClass *klass, void *data)
@@ -27,6 +27,7 @@
#include "hw/timer/arm_mptimer.h"
#include "migration/vmstate.h"
#include "qapi/error.h"
+#include "qemu/error-report.h"
#include "qemu/module.h"
#include "hw/core/cpu.h"
@@ -61,8 +62,16 @@ static inline void timerblock_update_irq(TimerBlock *tb)
/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
static inline uint32_t timerblock_scale(TimerBlock *tb, uint32_t control)
{
+ /*
+ * Referring to the ARM-Cortex-A9 MPCore TRM
+ *
+ * The arm mp timer relies on the PERIPHCLK as its clock source.
+ * The PERIPHCLK clock period must be configured as a multiple of the
+ * main clock CLK. The conversion from the qemu clock (1GHz) to arm mp
+ * timer ticks can be calculated like this:
+ */
uint64_t prescale = (((control >> 8) & 0xff) + 1);
- uint64_t ret = NANOSECONDS_PER_SECOND * prescale * 10;
+ uint64_t ret = NANOSECONDS_PER_SECOND * prescale * tb->periphclk_period;
return (uint32_t) (ret / tb->freq_hz);
}
@@ -273,6 +282,12 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp)
for (i = 0; i < s->num_cpu; i++) {
TimerBlock *tb = &s->timerblock[i];
tb->freq_hz = s->clk_freq_hz;
+ if (s->periphclk_period < 2) {
+ error_report("Invalid periphclk-period (%lu), must be >= 2",
+ s->periphclk_period);
+ exit(1);
+ }
+ tb->periphclk_period = s->periphclk_period;
tb->timer = ptimer_init(timerblock_tick, tb, PTIMER_POLICY);
sysbus_init_irq(sbd, &tb->irq);
memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb,
@@ -288,6 +303,7 @@ static const VMStateDescription vmstate_timerblock = {
.fields = (const VMStateField[]) {
VMSTATE_UINT32(control, TimerBlock),
VMSTATE_UINT64(freq_hz, TimerBlock),
+ VMSTATE_UINT64(periphclk_period, TimerBlock),
VMSTATE_UINT32(status, TimerBlock),
VMSTATE_PTIMER(timer, TimerBlock),
VMSTATE_END_OF_LIST()
@@ -309,6 +325,8 @@ static const Property arm_mptimer_properties[] = {
DEFINE_PROP_UINT64("clk-freq", ARMMPTimerState, clk_freq_hz,
NANOSECONDS_PER_SECOND),
DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0),
+ DEFINE_PROP_UINT64("periphclk-period", ARMMPTimerState,
+ periphclk_period, 10),
};
static void arm_mptimer_class_init(ObjectClass *klass, void *data)
@@ -77,6 +77,7 @@ struct A9GTimerState {
MemoryRegion iomem;
/* static props */
uint64_t cpu_clk_freq_hz;
+ uint64_t periphclk_period;
uint32_t num_cpu;
QEMUTimer *timer;
@@ -32,6 +32,7 @@ typedef struct {
uint32_t status;
struct ptimer_state *timer;
uint64_t freq_hz;
+ uint64_t periphclk_period;
qemu_irq irq;
MemoryRegion iomem;
} TimerBlock;
@@ -45,6 +46,7 @@ struct ARMMPTimerState {
/*< public >*/
uint64_t clk_freq_hz;
+ uint64_t periphclk_period;
uint32_t num_cpu;
TimerBlock timerblock[ARM_MPTIMER_MAX_CPUS];
MemoryRegion iomem;