drivers/timer: Fixing issue in nRF RTC driver when RTC handler is blocked.

Fixes an unlikely issue that could arise if the RTC handler in the nRF RTC
driver was blocked for more than one sys tick interval. This could lead to
_sys_clock_tick_announce() being called with more than one sys tick when the
kernel did not expect it.

Jira: ZEP-1763

Change-ID: I5608fca6f0ac97a17c1ce452c1c5c67696a49a9a
Signed-off-by: Øyvind Hovdsveen <oyvind.hovdsveen@nordicsemi.no>
This commit is contained in:
Øyvind Hovdsveen 2017-02-20 18:15:30 +01:00 committed by Anas Nashif
commit d787099c90

View file

@ -47,7 +47,7 @@ static uint32_t rtc_past;
* Holds the maximum sys ticks the kernel expects to see in the next * Holds the maximum sys ticks the kernel expects to see in the next
* _sys_clock_tick_announce(). * _sys_clock_tick_announce().
*/ */
static uint32_t allowed_idle_sys_ticks; static uint32_t expected_sys_ticks;
#endif /* CONFIG_TICKLESS_IDLE */ #endif /* CONFIG_TICKLESS_IDLE */
/* /*
@ -111,21 +111,25 @@ static void rtc_announce_set_next(void)
* counters or announcing it. * counters or announcing it.
*/ */
if (rtc_elapsed >= RTC_TICKS_PER_SYS_TICK) { if (rtc_elapsed >= RTC_TICKS_PER_SYS_TICK) {
#ifdef CONFIG_TICKLESS_IDLE
/* Calculate how many sys ticks elapsed since the last sys tick /* Calculate how many sys ticks elapsed since the last sys tick
* and notify the kernel if necessary. * and notify the kernel if necessary.
*/ */
sys_elapsed = rtc_elapsed / RTC_TICKS_PER_SYS_TICK; sys_elapsed = rtc_elapsed / RTC_TICKS_PER_SYS_TICK;
#ifdef CONFIG_TICKLESS_IDLE if (sys_elapsed > expected_sys_ticks) {
if (sys_elapsed > allowed_idle_sys_ticks) {
/* Never announce more sys ticks than the kernel asked /* Never announce more sys ticks than the kernel asked
* to be idle for. The remainder will be announced when * to be idle for. The remainder will be announced when
* the RTC ISR runs after rtc_compare_set() is called * the RTC ISR runs after rtc_compare_set() is called
* after the first announcement. * after the first announcement.
*/ */
sys_elapsed = allowed_idle_sys_ticks; sys_elapsed = expected_sys_ticks;
} }
#else
/* Never announce more than one sys tick if tickless idle is not
* configured.
*/
sys_elapsed = 1;
#endif /* CONFIG_TICKLESS_IDLE */ #endif /* CONFIG_TICKLESS_IDLE */
/* Store RTC_COUNTER floored to the last sys tick. This is /* Store RTC_COUNTER floored to the last sys tick. This is
@ -179,7 +183,7 @@ void _timer_idle_enter(int32_t sys_ticks)
sys_ticks = RTC_HALF / RTC_TICKS_PER_SYS_TICK; sys_ticks = RTC_HALF / RTC_TICKS_PER_SYS_TICK;
} }
allowed_idle_sys_ticks = sys_ticks; expected_sys_ticks = sys_ticks;
/* If ticks is 0, the RTC interrupt handler will be set pending /* If ticks is 0, the RTC interrupt handler will be set pending
* immediately, meaning that we will not go to sleep. * immediately, meaning that we will not go to sleep.
@ -224,10 +228,10 @@ void _timer_idle_exit(void)
rtc_announce_set_next(); rtc_announce_set_next();
/* After exiting idle, the kernel no longer expects a maximum amount of /* After exiting idle, the kernel no longer expects more than one sys
* sys ticks to have passed when _sys_clock_tick_announce() is called. * ticks to have passed when _sys_clock_tick_announce() is called.
*/ */
allowed_idle_sys_ticks = RTC_HALF; expected_sys_ticks = 1;
} }
#endif /* CONFIG_TICKLESS_IDLE */ #endif /* CONFIG_TICKLESS_IDLE */
@ -279,7 +283,7 @@ int _sys_clock_driver_init(struct device *device)
rtc_past = 0; rtc_past = 0;
#ifdef CONFIG_TICKLESS_IDLE #ifdef CONFIG_TICKLESS_IDLE
allowed_idle_sys_ticks = RTC_HALF; expected_sys_ticks = 1;
#endif /* CONFIG_TICKLESS_IDLE */ #endif /* CONFIG_TICKLESS_IDLE */
/* TODO: replace with counter driver to access RTC */ /* TODO: replace with counter driver to access RTC */