mirror of https://github.com/proxmox/mirror_qemu
ptimer: Add TRIGGER_ONLY_ON_DECREMENT policy option
The CMSDK timer behaviour is that an interrupt is triggered when the counter counts down from 1 to 0; however one is not triggered if the counter is manually set to 0 by a guest write to the counter register. Currently ptimer can't handle this; add a policy option to allow a ptimer user to request this behaviour. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Guenter Roeck <linux@roeck-us.net> Message-id: 20180703171044.9503-2-peter.maydell@linaro.orgmaster
parent
b78aae9bb6
commit
086ede32af
|
@ -45,8 +45,20 @@ static void ptimer_reload(ptimer_state *s, int delta_adjust)
|
||||||
uint32_t period_frac = s->period_frac;
|
uint32_t period_frac = s->period_frac;
|
||||||
uint64_t period = s->period;
|
uint64_t period = s->period;
|
||||||
uint64_t delta = s->delta;
|
uint64_t delta = s->delta;
|
||||||
|
bool suppress_trigger = false;
|
||||||
|
|
||||||
if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) {
|
/*
|
||||||
|
* Note that if delta_adjust is 0 then we must be here because of
|
||||||
|
* a count register write or timer start, not because of timer expiry.
|
||||||
|
* In that case the policy might require us to suppress the timer trigger
|
||||||
|
* that we would otherwise generate for a zero delta.
|
||||||
|
*/
|
||||||
|
if (delta_adjust == 0 &&
|
||||||
|
(s->policy_mask & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT)) {
|
||||||
|
suppress_trigger = true;
|
||||||
|
}
|
||||||
|
if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)
|
||||||
|
&& !suppress_trigger) {
|
||||||
ptimer_trigger(s);
|
ptimer_trigger(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,6 +365,14 @@ ptimer_state *ptimer_init(QEMUBH *bh, uint8_t policy_mask)
|
||||||
s->bh = bh;
|
s->bh = bh;
|
||||||
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ptimer_tick, s);
|
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ptimer_tick, s);
|
||||||
s->policy_mask = policy_mask;
|
s->policy_mask = policy_mask;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These two policies are incompatible -- trigger-on-decrement implies
|
||||||
|
* a timer trigger when the count becomes 0, but no-immediate-trigger
|
||||||
|
* implies a trigger when the count stops being 0.
|
||||||
|
*/
|
||||||
|
assert(!((policy_mask & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) &&
|
||||||
|
(policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)));
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,15 @@
|
||||||
* not the one less. */
|
* not the one less. */
|
||||||
#define PTIMER_POLICY_NO_COUNTER_ROUND_DOWN (1 << 4)
|
#define PTIMER_POLICY_NO_COUNTER_ROUND_DOWN (1 << 4)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Starting to run with a zero counter, or setting the counter to "0" via
|
||||||
|
* ptimer_set_count() or ptimer_set_limit() will not trigger the timer
|
||||||
|
* (though it will cause a reload). Only a counter decrement to "0"
|
||||||
|
* will cause a trigger. Not compatible with NO_IMMEDIATE_TRIGGER;
|
||||||
|
* ptimer_init() will assert() that you don't set both.
|
||||||
|
*/
|
||||||
|
#define PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT (1 << 5)
|
||||||
|
|
||||||
/* ptimer.c */
|
/* ptimer.c */
|
||||||
typedef struct ptimer_state ptimer_state;
|
typedef struct ptimer_state ptimer_state;
|
||||||
typedef void (*ptimer_cb)(void *opaque);
|
typedef void (*ptimer_cb)(void *opaque);
|
||||||
|
|
|
@ -208,6 +208,7 @@ static void check_periodic(gconstpointer arg)
|
||||||
bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
|
bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
|
||||||
bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD);
|
bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD);
|
||||||
bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
|
bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
|
||||||
|
bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT);
|
||||||
|
|
||||||
triggered = false;
|
triggered = false;
|
||||||
|
|
||||||
|
@ -311,7 +312,7 @@ static void check_periodic(gconstpointer arg)
|
||||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||||
no_immediate_reload ? 0 : 10);
|
no_immediate_reload ? 0 : 10);
|
||||||
|
|
||||||
if (no_immediate_trigger) {
|
if (no_immediate_trigger || trig_only_on_dec) {
|
||||||
g_assert_false(triggered);
|
g_assert_false(triggered);
|
||||||
} else {
|
} else {
|
||||||
g_assert_true(triggered);
|
g_assert_true(triggered);
|
||||||
|
@ -506,6 +507,7 @@ static void check_run_with_delta_0(gconstpointer arg)
|
||||||
bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
|
bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
|
||||||
bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD);
|
bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD);
|
||||||
bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
|
bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
|
||||||
|
bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT);
|
||||||
|
|
||||||
triggered = false;
|
triggered = false;
|
||||||
|
|
||||||
|
@ -515,7 +517,7 @@ static void check_run_with_delta_0(gconstpointer arg)
|
||||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||||
no_immediate_reload ? 0 : 99);
|
no_immediate_reload ? 0 : 99);
|
||||||
|
|
||||||
if (no_immediate_trigger) {
|
if (no_immediate_trigger || trig_only_on_dec) {
|
||||||
g_assert_false(triggered);
|
g_assert_false(triggered);
|
||||||
} else {
|
} else {
|
||||||
g_assert_true(triggered);
|
g_assert_true(triggered);
|
||||||
|
@ -563,7 +565,7 @@ static void check_run_with_delta_0(gconstpointer arg)
|
||||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||||
no_immediate_reload ? 0 : 99);
|
no_immediate_reload ? 0 : 99);
|
||||||
|
|
||||||
if (no_immediate_trigger) {
|
if (no_immediate_trigger || trig_only_on_dec) {
|
||||||
g_assert_false(triggered);
|
g_assert_false(triggered);
|
||||||
} else {
|
} else {
|
||||||
g_assert_true(triggered);
|
g_assert_true(triggered);
|
||||||
|
@ -609,6 +611,7 @@ static void check_periodic_with_load_0(gconstpointer arg)
|
||||||
ptimer_state *ptimer = ptimer_init(bh, *policy);
|
ptimer_state *ptimer = ptimer_init(bh, *policy);
|
||||||
bool continuous_trigger = (*policy & PTIMER_POLICY_CONTINUOUS_TRIGGER);
|
bool continuous_trigger = (*policy & PTIMER_POLICY_CONTINUOUS_TRIGGER);
|
||||||
bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
|
bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
|
||||||
|
bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT);
|
||||||
|
|
||||||
triggered = false;
|
triggered = false;
|
||||||
|
|
||||||
|
@ -617,7 +620,7 @@ static void check_periodic_with_load_0(gconstpointer arg)
|
||||||
|
|
||||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||||
|
|
||||||
if (no_immediate_trigger) {
|
if (no_immediate_trigger || trig_only_on_dec) {
|
||||||
g_assert_false(triggered);
|
g_assert_false(triggered);
|
||||||
} else {
|
} else {
|
||||||
g_assert_true(triggered);
|
g_assert_true(triggered);
|
||||||
|
@ -667,6 +670,7 @@ static void check_oneshot_with_load_0(gconstpointer arg)
|
||||||
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
|
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
|
||||||
ptimer_state *ptimer = ptimer_init(bh, *policy);
|
ptimer_state *ptimer = ptimer_init(bh, *policy);
|
||||||
bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
|
bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
|
||||||
|
bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT);
|
||||||
|
|
||||||
triggered = false;
|
triggered = false;
|
||||||
|
|
||||||
|
@ -675,7 +679,7 @@ static void check_oneshot_with_load_0(gconstpointer arg)
|
||||||
|
|
||||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||||
|
|
||||||
if (no_immediate_trigger) {
|
if (no_immediate_trigger || trig_only_on_dec) {
|
||||||
g_assert_false(triggered);
|
g_assert_false(triggered);
|
||||||
} else {
|
} else {
|
||||||
g_assert_true(triggered);
|
g_assert_true(triggered);
|
||||||
|
@ -725,6 +729,10 @@ static void add_ptimer_tests(uint8_t policy)
|
||||||
g_strlcat(policy_name, "no_counter_rounddown,", 256);
|
g_strlcat(policy_name, "no_counter_rounddown,", 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) {
|
||||||
|
g_strlcat(policy_name, "trigger_only_on_decrement,", 256);
|
||||||
|
}
|
||||||
|
|
||||||
g_test_add_data_func_full(
|
g_test_add_data_func_full(
|
||||||
tmp = g_strdup_printf("/ptimer/set_count policy=%s", policy_name),
|
tmp = g_strdup_printf("/ptimer/set_count policy=%s", policy_name),
|
||||||
g_memdup(&policy, 1), check_set_count, g_free);
|
g_memdup(&policy, 1), check_set_count, g_free);
|
||||||
|
@ -790,10 +798,15 @@ static void add_ptimer_tests(uint8_t policy)
|
||||||
|
|
||||||
static void add_all_ptimer_policies_comb_tests(void)
|
static void add_all_ptimer_policies_comb_tests(void)
|
||||||
{
|
{
|
||||||
int last_policy = PTIMER_POLICY_NO_COUNTER_ROUND_DOWN;
|
int last_policy = PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT;
|
||||||
int policy = PTIMER_POLICY_DEFAULT;
|
int policy = PTIMER_POLICY_DEFAULT;
|
||||||
|
|
||||||
for (; policy < (last_policy << 1); policy++) {
|
for (; policy < (last_policy << 1); policy++) {
|
||||||
|
if ((policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) &&
|
||||||
|
(policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) {
|
||||||
|
/* Incompatible policy flag settings -- don't try to test them */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
add_ptimer_tests(policy);
|
add_ptimer_tests(policy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue