[PATCH 2/3] arc_timer: fix tickless idle

Desfarges, Simon <simon.desfarges@...>

From: Simon Desfarges <simon.desfarges(a)intel.com>

When exiting from tickless idle uppon an external IRQ, the TICK timer
is set to fire at next TICK boundary. The current algorithm can lead
to a point that timer0_count register is higher than the timer0_limit

In this situation the next TICK will fire after the counter has
wrapped and performed another cycle (~133 seconds).

This condition appears when the counter reaches the limit after the
Interrupt Pending flag is checked. At this point the counter is
automatically wrapped to 0, but is set just next to the limit to fire
at next TICK boundary by SW. At exit of the _timer_idle_exit function,
the timer handler is called, and sets the limit to 1 TICK. At this
point the situation is:
- limit register == 1 TICK
- count register is just below the old limit register and higher than

To fix this issue, at _timer_idle_exit, the limit register is always
set to 1 TICK and the count register set such as the next TICK fires
on time.

Change-Id: Ifa002809d426aa04109592e53d2b02a224f51101
Tracked-On: https://jira.ndg.intel.com/browse/FIRE-4213
Signed-off-by: Simon Desfarges <simon.desfarges(a)intel.com>
drivers/timer/arcv2_timer0.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/drivers/timer/arcv2_timer0.c b/drivers/timer/arcv2_timer0.c
index 46c4612..ca25fea 100644
--- a/drivers/timer/arcv2_timer0.c
+++ b/drivers/timer/arcv2_timer0.c
@@ -276,9 +276,6 @@ void _timer_idle_exit(void)
* A non-timer interrupt occurred. Announce any
* ticks that have elapsed during the tickless idle.
- uint32_t remaining_cycles = programmed_limit - current_count;
_sys_idle_elapsed_ticks = current_count / cycles_per_tick;
if (_sys_idle_elapsed_ticks > 0) {
@@ -289,11 +286,8 @@ void _timer_idle_exit(void)
* Ensure the timer will expire at the end of the next tick in case
* the ISR makes any tasks and/or fibers ready to run.
- if (remaining_cycles >= cycles_per_tick) {
- timer0_count_register_set(programmed_limit -
- ((remaining_cycles - 1) % cycles_per_tick) - 1);
- }
+ timer0_limit_register_set(cycles_per_tick - 1);
+ timer0_count_register_set(current_count % cycles_per_tick);
static void tickless_idle_init(void) {}

Join {devel@lists.zephyrproject.org to automatically receive all group messages.