diff options
author | Antonio Nino Diaz <antonio.ninodiaz@arm.com> | 2016-04-25 14:14:17 +0100 |
---|---|---|
committer | Antonio Nino Diaz <antonio.ninodiaz@arm.com> | 2016-04-27 09:55:54 +0100 |
commit | 32b7578e829fc4086bc8efe2ebfdf603264dbae0 (patch) | |
tree | becb6edbf8a6acb6469b4eecce946ad90cbd77fa /framework | |
parent | 1cab674f905a1fb2ce08537aa415f600bf91259b (diff) |
Fix TFTF timer tests on FVP Foundation model
When running the TFTF timer test cases on FVP Foundation model
sometimes they hang. This only happens when the TFTF is built in
debug mode.
For the test case stress_test_timer_framework() the following happens:
do_stress_test() consists of a loop in which a timer is set up, then
the CPU enters suspend mode and the timer IRQ wakes the CPU up. The
instant the timers wake up is a random instant based on the current
system timer plus a random delay.
The reason they failed is that, in debug mode, the debug output
sometimes takes too long to complete, as printing text to the console
can take a relatively long time. As the Foundation model is using the
--use-real-time option, this effect was magnified. During this time,
the timer IRQ is sometimes handled, preventing the CPU from waking up.
When compiling in release mode, this output isn't printed, so the CPU
has enough time to enter suspend mode before the interrupt.
IRQs are now masked when trying to enter suspend mode. That way, even
if it takes some time to actually enter suspend mode, an early
interrupt will become pending and the CPU just won't be able to enter
suspend mode. An extra API called `tftf_program_timer_and_suspend` has
been added to make it easier to develop other test cases where the CPU
may enter suspend mode and a timer is supposed to wake it up.
The timer framework used to unconditionally disable IRQs upon entry
and enabling them upon exit. This means that IRQs used to always be
unmasked, even in the case where they were masked upon entry. This
behaviour has been fixed to preserve the DAIF flags instead (their
state is saved upon entry and restored upon exit).
The test, disabled in a previous commit, has been enabled again.
Change-Id: Ia86619a7f30483d77029e583c94265993f578a0f
Diffstat (limited to 'framework')
-rw-r--r-- | framework/timer/timer_framework.c | 60 |
1 files changed, 57 insertions, 3 deletions
diff --git a/framework/timer/timer_framework.c b/framework/timer/timer_framework.c index 51ec44a..ad22b42 100644 --- a/framework/timer/timer_framework.c +++ b/framework/timer/timer_framework.c @@ -37,11 +37,15 @@ #include <irq.h> #include <platform.h> #include <platform_def.h> +#include <power_management.h> #include <sgi.h> #include <spinlock.h> +#include <stddef.h> +#include <sys/types.h> #include <tftf.h> #include <timer.h> + /* Helper macros */ #define TIMER_STEP_VALUE (plat_timer_info->timer_step_value) #define TIMER_IRQ (plat_timer_info->timer_irq) @@ -149,6 +153,7 @@ int tftf_program_timer(unsigned long time_out_ms) { unsigned int core_pos; unsigned long long current_time; + u_register_t flags; int rc = 0; /* @@ -172,6 +177,7 @@ int tftf_program_timer(unsigned long time_out_ms) /* A timer interrupt request is already available for the core */ assert(interrupt_req_time[core_pos] == INVALID_TIME); + flags = read_daif(); disable_irq(); isb(); spin_lock(&timer_lock); @@ -213,9 +219,55 @@ int tftf_program_timer(unsigned long time_out_ms) } spin_unlock(&timer_lock); - enable_irq(); + /* Restore DAIF flags */ + write_daif(flags); + isb(); + + return rc; +} + +int tftf_program_timer_and_suspend(unsigned long milli_secs, + unsigned int pwr_state) +{ + int rc; + u_register_t flags; + + /* Preserve DAIF flags. IRQs need to be disabled for this to work. */ + flags = read_daif(); + disable_irq(); isb(); + /* + * Even with IRQs masked, the timer IRQ will wake the CPU up. + * + * If the timer IRQ happens before entering suspend mode (because the + * timer took too long to program, for example) the fact that the IRQ is + * pending will prevent the CPU from entering suspend mode and not being + * able to wake up. + */ + rc = tftf_program_timer(milli_secs); + if (rc == 0) { + rc = tftf_cpu_suspend(pwr_state); + if (rc != PSCI_E_SUCCESS) { + ERROR("%s %d: rc = %d\n", __func__, __LINE__, rc); + rc = -1; + } else { + rc = 0; + } + } else { + ERROR("%s %d: rc = %d\n", __func__, __LINE__, rc); + } + + /* Restore previous DAIF flags */ + write_daif(flags); + isb(); + + /* + * If IRQs were disabled when calling this function, the timer IRQ + * handler won't be called and the timer interrupt will be pending, but + * that isn't necessarily a problem. + */ + return rc; } @@ -224,6 +276,7 @@ int tftf_cancel_timer(void) unsigned int core_pos = platform_get_core_pos(read_mpidr_el1()); unsigned int next_timer_req_core_pos; unsigned long long current_time; + u_register_t flags; int rc = 0; /* @@ -231,6 +284,7 @@ int tftf_cancel_timer(void) * it will remain pending and a core does not hit IRQ handler trying * to acquire an already locked spin_lock causing dead lock. */ + flags = read_daif(); disable_irq(); isb(); spin_lock(&timer_lock); @@ -293,8 +347,8 @@ int tftf_cancel_timer(void) exit: spin_unlock(&timer_lock); - /* Enable IRQ */ - enable_irq(); + /* Restore DAIF flags */ + write_daif(flags); isb(); return rc; |