From 31c11a5dc02a121c659567eeadced7b51a22f3e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Mon, 13 Feb 2023 14:43:13 +0100 Subject: [PATCH] drivers: nrf_rtc_timer: Fix handling of COMPARE events in set_alarm() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a follow-up to commits cf871aec645dc45503b20c2e7684a4babbc436dc and 205e684958ebea4d7f2a15d75b94aac880fb2390. It turns out that the current implementation of the nrf_rtc_timer may still fail to properly handle a timeout if that timeout is set in very specific conditions - when a previously set timeout is about to expire. When that happens, the new timeout is handled 512 seconds later (when the system timer overflows) than it should be. A recently added nrf_rtc_timer test case (test_tight_rescheduling) exposes this problem and this commit fixes it by adding examination of COMPARE events that appear during setting of the CC register value for a given timeout. Signed-off-by: Andrzej Głąbek --- drivers/timer/nrf_rtc_timer.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/timer/nrf_rtc_timer.c b/drivers/timer/nrf_rtc_timer.c index cca97484d8b..93d879cc98e 100644 --- a/drivers/timer/nrf_rtc_timer.c +++ b/drivers/timer/nrf_rtc_timer.c @@ -280,13 +280,28 @@ static void set_alarm(int32_t chan, uint32_t req_cc) * Increase the CC value by a larger number of cycles in each * trial to avoid spending too much time in this loop if it * continuously gets interrupted and delayed by something. - * But if the COMPARE event turns out to be already generated, - * there is obviously no need to continue the loop. */ - if ((counter_sub(cc_val, now + MIN_CYCLES_FROM_NOW) > - (COUNTER_HALF_SPAN - MIN_CYCLES_FROM_NOW)) - && - !event_check(chan)) { + if (counter_sub(cc_val, now + MIN_CYCLES_FROM_NOW) > + (COUNTER_HALF_SPAN - MIN_CYCLES_FROM_NOW)) { + /* If the COMPARE event turns out to be already + * generated, check if the loop can be finished. + */ + if (event_check(chan)) { + /* If the current counter value has not yet + * reached the requested CC value, the event + * must come from the previously set CC value + * (the alarm is apparently rescheduled). + * The event needs to be cleared then and the + * loop needs to be continued. + */ + now = counter(); + if (counter_sub(now, req_cc) > COUNTER_HALF_SPAN) { + event_clear(chan); + } else { + break; + } + } + cc_val = now + cc_inc; cc_inc++; } else {