aboutsummaryrefslogtreecommitdiff
path: root/hw/timer/aspeed_timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/timer/aspeed_timer.c')
-rw-r--r--hw/timer/aspeed_timer.c17
1 files changed, 16 insertions, 1 deletions
diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c
index ed81d5c..59c2bbe 100644
--- a/hw/timer/aspeed_timer.c
+++ b/hw/timer/aspeed_timer.c
@@ -44,6 +44,13 @@ enum timer_ctrl_op {
op_pulse_enable
};
+/*
+ * Minimum value of the reload register to filter out short period
+ * timers which have a noticeable impact in emulation. 5us should be
+ * enough, use 20us for "safety".
+ */
+#define TIMER_MIN_NS (20 * SCALE_US)
+
/**
* Avoid mutual references between AspeedTimerCtrlState and AspeedTimer
* structs, as it's a waste of memory. The ptimer BH callback needs to know
@@ -98,6 +105,14 @@ static inline uint32_t calculate_ticks(struct AspeedTimer *t, uint64_t now_ns)
return t->reload - MIN(t->reload, ticks);
}
+static uint32_t calculate_min_ticks(AspeedTimer *t, uint32_t value)
+{
+ uint32_t rate = calculate_rate(t);
+ uint32_t min_ticks = muldiv64(TIMER_MIN_NS, rate, NANOSECONDS_PER_SECOND);
+
+ return value < min_ticks ? min_ticks : value;
+}
+
static inline uint64_t calculate_time(struct AspeedTimer *t, uint32_t ticks)
{
uint64_t delta_ns;
@@ -261,7 +276,7 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg,
switch (reg) {
case TIMER_REG_RELOAD:
old_reload = t->reload;
- t->reload = value;
+ t->reload = calculate_min_ticks(t, value);
/* If the reload value was not previously set, or zero, and
* the current value is valid, try to start the timer if it is