Skip to content

Commit 205e684

Browse files
ananglcarlescufi
authored andcommitted
drivers: nrf_rtc_timer: Rework set_absolute_alarm()
Eliminate waiting for a potential COMPARE event when setting a CC value close to the previously set one and rely instead on checking target time when processing channel events in the ISR. Signed-off-by: Andrzej Głąbek <[email protected]>
1 parent bf1d3db commit 205e684

File tree

1 file changed

+55
-52
lines changed

1 file changed

+55
-52
lines changed

drivers/timer/nrf_rtc_timer.c

Lines changed: 55 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ static uint32_t get_comparator(int32_t chan)
7171
return nrf_rtc_cc_get(RTC, chan);
7272
}
7373

74+
static bool event_check(int32_t chan)
75+
{
76+
return nrf_rtc_event_check(RTC, RTC_CHANNEL_EVENT_ADDR(chan));
77+
}
78+
7479
static void event_clear(int32_t chan)
7580
{
7681
nrf_rtc_event_clear(RTC, RTC_CHANNEL_EVENT_ADDR(chan));
@@ -225,63 +230,56 @@ uint64_t z_nrf_rtc_timer_get_ticks(k_timeout_t t)
225230
* @param[in] chan A channel for which a new CC value is to be set.
226231
*
227232
* @param[in] abs_val An absolute value of CC register to be set.
228-
*
229-
* @returns CC value that was actually set. It is equal to @p abs_val or
230-
* shifted ahead if @p abs_val was too near in the future (+1 case).
231233
*/
232-
static uint32_t set_absolute_alarm(int32_t chan, uint32_t abs_val)
234+
static void set_absolute_alarm(int32_t chan, uint32_t abs_val)
233235
{
234-
uint32_t now;
235-
uint32_t now2;
236236
uint32_t cc_val = abs_val & COUNTER_MAX;
237-
uint32_t prev_cc = get_comparator(chan);
238-
uint32_t tick_inc = 2;
237+
uint32_t cc_inc = 2;
238+
239+
/* Disable event routing for the channel to avoid getting a COMPARE
240+
* event for the previous CC value before the new one takes effect
241+
* (however, even if such spurious event was generated, it would be
242+
* properly filtered out in process_channel(), where the target time
243+
* is checked).
244+
* Clear also the event as it may already be generated at this point.
245+
*/
246+
event_disable(chan);
247+
event_clear(chan);
239248

240-
do {
241-
now = counter();
249+
for (;;) {
250+
uint32_t now;
242251

243-
/* Handle case when previous event may generate an event.
244-
* It is handled by setting CC to now (far in the future),
245-
* in case previous event was set for next tick wait for half
246-
* LF tick and clear event that may have been generated.
252+
set_comparator(chan, cc_val);
253+
/* Enable event routing after the required CC value was set.
254+
* Even though the above operation may get repeated (see below),
255+
* there is no need to disable event routing in every iteration
256+
* of the loop, as the COMPARE event resulting from any attempt
257+
* of setting the CC register is acceptable (as mentioned above,
258+
* process_channel() does the proper filtering).
247259
*/
248-
set_comparator(chan, now);
249-
if (counter_sub(prev_cc, now) == 1) {
250-
/* It should wait for half of RTC tick 15.26us. As
251-
* busy wait runs from different clock source thus
252-
* wait longer to cover for discrepancy.
253-
*/
254-
k_busy_wait(19);
255-
}
260+
event_enable(chan);
256261

257-
/* RTC may not generate event if CC is set for 1 tick from now.
258-
* Because of that if requested cc_val is in the past or next tick,
259-
* set CC to further in future. Start with 2 ticks from now but
260-
* if that fails go even futher. It may fail if operation got
261-
* interrupted and RTC counter progressed or if optimization is
262-
* turned off.
263-
*/
264-
if (counter_sub(cc_val, now + 2) > COUNTER_HALF_SPAN) {
265-
cc_val = now + tick_inc;
266-
tick_inc++;
267-
}
262+
now = counter();
268263

269-
event_clear(chan);
270-
event_enable(chan);
271-
set_comparator(chan, cc_val);
272-
now2 = counter();
273-
prev_cc = cc_val;
274-
/* Rerun the algorithm if counter progressed during execution
275-
* and cc_val is in the past or one tick from now. In such
276-
* scenario, it is possible that event will not be generated.
277-
* Rerunning the algorithm will delay the alarm but ensure that
278-
* event will be generated at the moment indicated by value in
279-
* CC register.
264+
/* RTC may not generate a COMPARE event if its COUNTER value
265+
* is N and a given CC register is set to N or N+1. If it turns
266+
* out that the above configuration of the comparator resulted
267+
* in such CC value or even in a value that is considered to be
268+
* from the past, repeat the operation using a CC value that is
269+
* guaranteed to generate the event. Start with 2 RTC ticks from
270+
* now and if that fails (because the operation gets delayed),
271+
* go even futher in the next attempt.
272+
* But if the COMPARE event turns out to be already generated,
273+
* there is obviously no need to continue the loop.
280274
*/
281-
} while ((now2 != now) &&
282-
(counter_sub(cc_val, now2 + 2) > COUNTER_HALF_SPAN));
283-
284-
return cc_val;
275+
if ((counter_sub(cc_val, now + 2) > COUNTER_HALF_SPAN) &&
276+
!event_check(chan)) {
277+
cc_val = now + cc_inc;
278+
cc_inc++;
279+
} else {
280+
break;
281+
}
282+
}
285283
}
286284

287285
static int compare_set_nolocks(int32_t chan, uint64_t target_time,
@@ -302,9 +300,7 @@ static int compare_set_nolocks(int32_t chan, uint64_t target_time,
302300
/* Target time is valid and is different than currently set.
303301
* Set CC value.
304302
*/
305-
uint32_t cc_set = set_absolute_alarm(chan, cc_value);
306-
307-
target_time += counter_sub(cc_set, cc_value);
303+
set_absolute_alarm(chan, cc_value);
308304
}
309305
} else {
310306
/* Force ISR handling when exiting from critical section. */
@@ -454,7 +450,7 @@ static bool channel_processing_check_and_clear(int32_t chan)
454450
* or be forced.
455451
*/
456452
result = atomic_and(&force_isr_mask, ~BIT(chan)) ||
457-
nrf_rtc_event_check(RTC, RTC_CHANNEL_EVENT_ADDR(chan));
453+
event_check(chan);
458454

459455
if (result) {
460456
event_clear(chan);
@@ -493,6 +489,13 @@ static void process_channel(int32_t chan)
493489
cc_data[chan].callback = NULL;
494490
cc_data[chan].target_time = TARGET_TIME_INVALID;
495491
event_disable(chan);
492+
/* Because of the way set_absolute_alarm() sets the CC
493+
* register, it may turn out that another COMPARE event
494+
* has been generated for the same alarm. Make sure the
495+
* event is cleared, so that the ISR is not executed
496+
* again unnecessarily.
497+
*/
498+
event_clear(chan);
496499
}
497500

498501
full_int_unlock(mcu_critical_state);

0 commit comments

Comments
 (0)