forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnrf_rtc_timer.c
576 lines (494 loc) · 15.9 KB
/
nrf_rtc_timer.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
/*
* Copyright (c) 2016-2017 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <soc.h>
#include <clock_control.h>
#include <system_timer.h>
#include <drivers/clock_control/nrf5_clock_control.h>
#include <arch/arm/cortex_m/cmsis.h>
#include <sys_clock.h>
extern void youve_print(void);
/*
* Convenience defines.
*/
#define SYS_CLOCK_RTC NRF_RTC1
#define RTC_COUNTER SYS_CLOCK_RTC->COUNTER
#define RTC_CC_VALUE SYS_CLOCK_RTC->CC[0]
#define RTC_CC_EVENT SYS_CLOCK_RTC->EVENTS_COMPARE[0]
/* Minimum delta between current counter and CC register that the RTC is able
* to handle
*/
#define RTC_MIN_DELTA 2
#define RTC_MASK 0x00FFFFFF
/* Maximum difference for RTC counter values used. Half the maximum value is
* selected to be able to detect overflow (a negative value has the same
* representation as a large positive value).
*/
#define RTC_HALF (RTC_MASK / 2)
#define RTC_TICKS_PER_SYS_TICK ((u32_t)((((u64_t)1000000UL / \
sys_clock_ticks_per_sec) * \
1000000000UL) / 30517578125UL) & RTC_MASK)
extern s32_t _sys_idle_elapsed_ticks;
/*
* rtc_past holds the value of RTC_COUNTER at the time the last sys tick was
* announced, in RTC ticks. It is therefore always a multiple of
* RTC_TICKS_PER_SYS_TICK.
*/
static u32_t rtc_past;
#ifdef CONFIG_TICKLESS_IDLE
/*
* Holds the maximum sys ticks the kernel expects to see in the next
* _sys_clock_tick_announce().
*/
static u32_t expected_sys_ticks;
#endif /* CONFIG_TICKLESS_IDLE */
#ifdef CONFIG_TICKLESS_KERNEL
s32_t _get_max_clock_time(void);
#endif /* CONFIG_TICKLESS_KERNEL */
/*
* Set RTC Counter Compare (CC) register to a given value in RTC ticks.
*/
static void rtc_compare_set(u32_t rtc_ticks)
{
u32_t rtc_now;
/* Try to set CC value. We assume the procedure is always successful. */
RTC_CC_VALUE = rtc_ticks;
rtc_now = RTC_COUNTER;
/* The following checks if the CC register was set to a valid value.
* The first test checks if the distance between the current RTC counter
* and the value (in the future) set in the CC register is too small to
* guarantee a compare event being triggered.
* The second test checks if the current RTC counter is higher than the
* value written to the CC register, i.e. the CC value is in the past,
* by checking if the unsigned subtraction wraps around.
* If either of the above are true then instead of waiting for the CC
* event to trigger in the form of an interrupt, trigger it directly
* using the NVIC.
*/
if ((((rtc_ticks - rtc_now) & RTC_MASK) < RTC_MIN_DELTA) ||
(((rtc_ticks - rtc_now) & RTC_MASK) > RTC_HALF)) {
NVIC_SetPendingIRQ(NRF5_IRQ_RTC1_IRQn);
}
}
#ifndef CONFIG_TICKLESS_KERNEL
/*
* @brief Announces the number of sys ticks, if any, that have passed since the
* last announcement, and programs the RTC to trigger the interrupt on the next
* sys tick.
*
* This function is not reentrant. It is called from:
*
* * _timer_idle_exit(), which in turn is called with interrupts disabled when
* an interrupt fires.
* * rtc1_nrf5_isr(), which runs with interrupts enabled but at that time the
* device cannot be idle and hence _timer_idle_exit() cannot be called.
*
* Since this function can be preempted, we need to take some provisions to
* announce all expected sys ticks that have passed.
*
*/
static void rtc_announce_set_next(void)
{
u32_t rtc_now, rtc_elapsed, sys_elapsed;
/* Read the RTC counter one single time in the beginning, so that an
* increase in the counter during this procedure leads to no race
* conditions.
*/
rtc_now = RTC_COUNTER;
/* Calculate how many RTC ticks elapsed since the last sys tick. */
rtc_elapsed = (rtc_now - rtc_past) & RTC_MASK;
/* If no sys ticks have elapsed, there is no point in incrementing the
* counters or announcing it.
*/
if (rtc_elapsed >= RTC_TICKS_PER_SYS_TICK) {
#ifdef CONFIG_TICKLESS_IDLE
/* Calculate how many sys ticks elapsed since the last sys tick
* and notify the kernel if necessary.
*/
sys_elapsed = rtc_elapsed / RTC_TICKS_PER_SYS_TICK;
if (sys_elapsed > expected_sys_ticks) {
/* Never announce more sys ticks than the kernel asked
* to be idle for. The remainder will be announced when
* the RTC ISR runs after rtc_compare_set() is called
* after the first announcement.
*/
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 */
/* Store RTC_COUNTER floored to the last sys tick. This is
* done, so that ISR can properly calculate that 1 sys tick
* has passed.
*/
rtc_past = (rtc_past +
(sys_elapsed * RTC_TICKS_PER_SYS_TICK)
) & RTC_MASK;
_sys_idle_elapsed_ticks = sys_elapsed;
_sys_clock_tick_announce();
}
/* Set the RTC to the next sys tick */
rtc_compare_set(rtc_past + RTC_TICKS_PER_SYS_TICK);
}
#endif
#ifdef CONFIG_TICKLESS_IDLE
/**
* @brief Place system timer into idle state.
*
* Re-program the timer to enter into the idle state for the given number of
* sys ticks, counted from the previous sys tick. The timer will fire in the
* number of sys ticks supplied or the maximum number of sys ticks (converted
* to RTC ticks) that can be programmed into the hardware.
*
* This will only be called from idle context, with IRQs disabled.
*
* A value of -1 will result in the maximum number of sys ticks.
*
* Example 1: Idle sleep is entered:
*
* sys tick timeline: (1) (2) (3) (4) (5) (6)
* rtc tick timeline : 0----100----200----300----400----500----600
* ******************
* 150
*
* a) The last sys tick was announced at 100
* b) The idle context enters sleep at 150, between sys tick 1 and 2, with
* sys_ticks = 3.
* c) The RTC is programmed to fire at sys tick 1 + 3 = 4 (RTC tick 400)
*
* @return N/A
*/
void _timer_idle_enter(s32_t sys_ticks)
{
#ifdef CONFIG_TICKLESS_KERNEL
if (sys_ticks != K_FOREVER) {
/* Need to reprograme timer if current program is smaller*/
if (sys_ticks > expected_sys_ticks) {
_set_time(sys_ticks);
}
} else {
expected_sys_ticks = 0;
/* Set time to largest possile RTC Tick*/
_set_time(_get_max_clock_time());
}
#else
/* Restrict ticks to max supported by RTC without risking overflow*/
if ((sys_ticks < 0) ||
(sys_ticks > (RTC_HALF / RTC_TICKS_PER_SYS_TICK))) {
sys_ticks = RTC_HALF / RTC_TICKS_PER_SYS_TICK;
}
expected_sys_ticks = sys_ticks;
/* If ticks is 0, the RTC interrupt handler will be set pending
* immediately, meaning that we will not go to sleep.
*/
rtc_compare_set(rtc_past + (sys_ticks * RTC_TICKS_PER_SYS_TICK));
#endif
}
#ifdef CONFIG_TICKLESS_KERNEL
/**
* @brief provides total systicks programmed.
*
* returns : total number of sys ticks programmed.
*/
u32_t _get_program_time(void)
{
return expected_sys_ticks;
}
/**
* @brief provides total systicks remaining since last programming of RTC.
*
* returns : total number of sys ticks remaining since last RTC programming.
*/
u32_t _get_remaining_program_time(void)
{
if (!expected_sys_ticks) {
return 0;
}
return (expected_sys_ticks - _get_elapsed_program_time());
}
/**
* @brief provides total systicks passed since last programming of RTC.
*
* returns : total number of sys ticks passed since last RTC programming.
*/
u32_t _get_elapsed_program_time(void)
{
u32_t rtc_now, rtc_prev, rtc_elapsed;
if (!expected_sys_ticks) {
return 0;
}
rtc_now = RTC_COUNTER;
/* Discard value of RTC_COUNTER read at LFCLK transition */
do {
/* Number of RTC cycles passed since last RTC Programing*/
rtc_elapsed = (rtc_now - rtc_past) & RTC_MASK;
rtc_prev = rtc_now;
rtc_now = RTC_COUNTER;
} while (rtc_prev != rtc_now);
/*Convert number of Machine cycles to SYS TICS*/
return (rtc_elapsed / RTC_TICKS_PER_SYS_TICK);
}
/**
* @brief Sets interrupt for rtc compare value for systick time.
*
* This Function does following:-
* 1. Updates expected_sys_ticks equal to time.
* 2. Update kernel book keeping for time passed since device bootup.
* 3. Calls routine to set rtc interrupt.
*/
void _set_time(u32_t time)
{
if (!time) {
expected_sys_ticks = 0;
return;
}
/* Update expected_sys_ticls to time to programe*/
expected_sys_ticks = time;
_sys_clock_tick_count = _get_elapsed_clock_time();
/* Update rtc_past to track rtc timer count*/
rtc_past = (_sys_clock_tick_count * RTC_TICKS_PER_SYS_TICK) & RTC_MASK;
expected_sys_ticks = expected_sys_ticks > _get_max_clock_time() ?
_get_max_clock_time() : expected_sys_ticks;
/* Programe RTC compare register to generate interrupt*/
rtc_compare_set(rtc_past +
(expected_sys_ticks * RTC_TICKS_PER_SYS_TICK));
}
/**
* @brief provides time remaining to reach rtc count overflow.
*
* This function returns how many sys RTC remaining for rtc to overflow.
* This will be required when we will program RTC compare value to maximum
* possible value.
*
* returns : difference between current systick and Maximum possible systick.
*/
s32_t _get_max_clock_time(void)
{
u32_t rtc_now, rtc_prev, rtc_away, sys_away = 0;
rtc_now = RTC_COUNTER;
/* Discard value of RTC_COUNTER read at LFCLK transition */
do {
rtc_away = (RTC_MASK - rtc_now);
rtc_away = rtc_away > RTC_HALF ? RTC_HALF : rtc_away;
rtc_prev = rtc_now;
/* Calculate time to programe into RTC*/
rtc_now = RTC_COUNTER;
} while (rtc_now != rtc_prev);
/* Convert RTC Ticks to SYS TICKS*/
if (rtc_away >= RTC_TICKS_PER_SYS_TICK) {
sys_away = rtc_away / RTC_TICKS_PER_SYS_TICK;
}
return sys_away;
}
/**
* @brief Enable sys Clock.
*
* This is used to program RTC clock to maximum Clock time in case Clock to
* remain On.
*/
void _enable_sys_clock(void)
{
if (!expected_sys_ticks) {
/* Programe sys tick to Maximum possible value*/
_set_time(_get_max_clock_time());
}
}
/**
* @brief provides total systicks passed since device bootup.
*
* returns : total number of sys ticks passed since device bootup.
*/
u64_t _get_elapsed_clock_time(void)
{
u64_t elapsed;
u32_t rtc_now, rtc_elapsed, rtc_prev, sys_elapsed;
rtc_now = RTC_COUNTER;
/* Discard value of RTC_COUNTER read at LFCLK transition */
do {
/* Calculate number of rtc cycles elapsed since RTC programing*/
rtc_elapsed = (rtc_now - rtc_past) & RTC_MASK;
elapsed = _sys_clock_tick_count;
rtc_prev = rtc_now;
rtc_now = RTC_COUNTER;
} while (rtc_now != rtc_prev);
if (rtc_elapsed >= RTC_TICKS_PER_SYS_TICK) {
/* Convert RTC cycles to SYS TICKS*/
sys_elapsed = rtc_elapsed / RTC_TICKS_PER_SYS_TICK;
/* Update total number of SYS_TICKS passed*/
elapsed += sys_elapsed;
}
return elapsed;
}
#endif
/**
*
* @brief Handling of tickless idle when interrupted
*
* The function will be called by _sys_power_save_idle_exit(), called from
* _arch_isr_direct_pm() for 'direct' interrupts, or from _isr_wrapper for
* regular ones, which is called on every IRQ handler if the device was
* idle, and optionally called when a 'direct' IRQ handler executes if the
* device was idle.
*
* Example 1: Idle sleep is interrupted before time:
*
* sys tick timeline: (1) (2) (3) (4) (5) (6)
* rtc tick timeline : 0----100----200----300----400----500----600
* **************!***
* 150 350
*
* Assume that _timer_idle_enter() is called at 150 (1) to sleep for 3
* sys ticks. The last sys tick was announced at 100.
*
* On wakeup (non-RTC IRQ at 350):
*
* a) Notify how many sys ticks have passed, i.e., 350 - 150 / 100 = 2.
* b) Schedule next sys tick at 400.
*
*/
void _timer_idle_exit(void)
{
#ifdef CONFIG_TICKLESS_KERNEL
if (!expected_sys_ticks && _sys_clock_always_on) {
_set_time(_get_max_clock_time());
}
#else
/* Clear the event flag and interrupt in case we woke up on the RTC
* interrupt. No need to run the RTC ISR since everything that needs
* to run in the ISR will be done in this call.
*/
RTC_CC_EVENT = 0;
NVIC_ClearPendingIRQ(NRF5_IRQ_RTC1_IRQn);
rtc_announce_set_next();
/* After exiting idle, the kernel no longer expects more than one sys
* ticks to have passed when _sys_clock_tick_announce() is called.
*/
expected_sys_ticks = 1;
#endif
}
#endif /* CONFIG_TICKLESS_IDLE */
/*
* @brief Announces the number of sys ticks that have passed since the last
* announcement, if any, and programs the RTC to trigger the interrupt on the
* next sys tick.
*
* The ISR is set pending due to a regular sys tick and after exiting idle mode
* as scheduled.
*
* Since this ISR can be preempted, we need to take some provisions to announce
* all expected sys ticks that have passed.
*
* Consider the following example:
*
* sys tick timeline: (1) (2) (3) (4) (5) (6)
* rtc tick timeline : 0----100----200----300----400----500----600
* !**********
* 450
*
* The last sys tick was anounced at 200, i.e, rtc_past = 200. The ISR is
* executed at the next sys tick, i.e. 300. The following sys tick is due at
* 400. However, the ISR is preempted for a number of sys ticks, until 450 in
* this example. The ISR will then announce the number of sys ticks it was
* delayed (2), and schedule the next sys tick (5) at 500.
*/
static void rtc1_nrf5_isr(void *arg)
{
ARG_UNUSED(arg);
RTC_CC_EVENT = 0;
#ifdef CONFIG_EXECUTION_BENCHMARKING
extern void read_timer_start_of_tick_handler(void);
read_timer_start_of_tick_handler();
#endif
#ifdef CONFIG_TICKLESS_KERNEL
_sys_idle_elapsed_ticks = expected_sys_ticks;
/* Initialize expected sys tick,
* It will be later updated based on next timeout.
*/
expected_sys_ticks = 0;
/* Anounce elapsed of _sys_idle_elapsed_ticks systicks*/
_sys_clock_tick_announce();
#else
rtc_announce_set_next();
#endif
#ifdef CONFIG_EXECUTION_BENCHMARKING
extern void read_timer_end_of_tick_handler(void);
read_timer_end_of_tick_handler();
#endif
}
int _sys_clock_driver_init(struct device *device)
{
struct device *clock;
ARG_UNUSED(device);
clock = device_get_binding(CONFIG_CLOCK_CONTROL_NRF5_K32SRC_DRV_NAME);
if (!clock) {
return -1;
}
clock_control_on(clock, (void *)CLOCK_CONTROL_NRF5_K32SRC);
rtc_past = 0;
#ifdef CONFIG_TICKLESS_IDLE
expected_sys_ticks = 1;
#endif /* CONFIG_TICKLESS_IDLE */
/* TODO: replace with counter driver to access RTC */
SYS_CLOCK_RTC->PRESCALER = 0;
SYS_CLOCK_RTC->CC[0] = RTC_TICKS_PER_SYS_TICK;
SYS_CLOCK_RTC->EVTENSET = RTC_EVTENSET_COMPARE0_Msk;
SYS_CLOCK_RTC->INTENSET = RTC_INTENSET_COMPARE0_Msk;
/* Clear the event flag and possible pending interrupt */
RTC_CC_EVENT = 0;
NVIC_ClearPendingIRQ(NRF5_IRQ_RTC1_IRQn);
IRQ_CONNECT(NRF5_IRQ_RTC1_IRQn, 1, rtc1_nrf5_isr, 0, 0);
irq_enable(NRF5_IRQ_RTC1_IRQn);
SYS_CLOCK_RTC->TASKS_CLEAR = 1;
SYS_CLOCK_RTC->TASKS_START = 1;
return 0;
}
u32_t _timer_cycle_get_32(void)
{
u32_t elapsed_cycles;
u32_t sys_clock_tick_count;
u32_t rtc_prev;
u32_t rtc_now;
rtc_now = RTC_COUNTER;
/* Discard value of RTC_COUNTER read at LFCLK transition */
do {
sys_clock_tick_count = _sys_clock_tick_count;
elapsed_cycles = (rtc_now - (sys_clock_tick_count *
RTC_TICKS_PER_SYS_TICK)) &
RTC_MASK;
rtc_prev = rtc_now;
rtc_now = RTC_COUNTER;
} while (rtc_now != rtc_prev);
return (sys_clock_tick_count * sys_clock_hw_cycles_per_tick) +
elapsed_cycles;
}
#ifdef CONFIG_SYSTEM_CLOCK_DISABLE
/**
*
* @brief Stop announcing sys ticks into the kernel
*
* This routine disables the RTC1 so that timer interrupts are no
* longer delivered.
*
* @return N/A
*/
void sys_clock_disable(void)
{
unsigned int key;
key = irq_lock();
irq_disable(NRF5_IRQ_RTC1_IRQn);
SYS_CLOCK_RTC->EVTENCLR = RTC_EVTENCLR_COMPARE0_Msk;
SYS_CLOCK_RTC->INTENCLR = RTC_INTENCLR_COMPARE0_Msk;
SYS_CLOCK_RTC->TASKS_STOP = 1;
SYS_CLOCK_RTC->TASKS_CLEAR = 1;
irq_unlock(key);
/* TODO: turn off (release) 32 KHz clock source.
* Turning off of 32 KHz clock source is not implemented in clock
* driver.
*/
}
#endif /* CONFIG_SYSTEM_CLOCK_DISABLE */