1 /*
2  * Copyright (c) 2015 Eric Holland
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 
9 #include <lk/err.h>
10 #include <platform.h>
11 #include <nrfx.h>
12 #include <platform/timer.h>
13 #include <platform/clock.h>
14 #include <nrfx_clock.h>
15 #include <hal/nrf_rtc.h>
16 #include <sys/types.h>
17 
18 /*
19  * This uses the RTC1 module for the LK system tick instead of using the ARM
20  * systick since systick is not available in all low power states.  This is also
21  * not utilizing the nrfx rtc driver as it adds unneeded functionality and thus
22  * overhead to the irq processing.  It is however using convenience functions
23  * in the nrf hal library (nrf hal should not be confused with nrfx driver library)
24  */
25 
26 // compare channel used by our timer.
27 #define LK_RTC_CC_CHAN (0)
28 
29 #define MULT_BY_32768(x)  ((x) << 15)
30 #define DIV_BY_32768(x) ((x) >> 15)
31 
32 /*
33  * base_counter is total number of ms elapsed in 49.15 format.  By choosing this
34  * format we take advantage of the fact our oscillator is 32768Hz, allowing the
35  * recurring math to be done with  shift by 15.  This preserves some precision in
36  * the base_counter for use in current_time_hires.
37  *
38  * base_counter will overflow every 17,851 years.  Plan accordingly.
39  */
40 static volatile uint64_t base_counter = 0;
41 
42 //cycles of our clock per tick interval
43 static uint32_t rtc_counts_per_tick = 0;
44 static volatile uint32_t last_cc = 0;
45 
46 //delta to be added to base counter (ms in 49.15 format) each lk tick
47 static uint32_t ms_scale = 0;
48 
49 static platform_timer_callback cb;
50 static void *cb_args;
51 
platform_set_periodic_timer(platform_timer_callback callback,void * arg,lk_time_t interval)52 status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, lk_time_t interval) {
53     ASSERT(nrfx_clock_lfclk_is_running());
54     cb = callback;
55     cb_args = arg;
56     // RTC clock frequency is always 32768Hz, so we take advantage of shifts vs. multiplicaiton
57     rtc_counts_per_tick = MULT_BY_32768(interval) / 1000 ;
58     ms_scale = rtc_counts_per_tick * 1000;
59 
60     // Stop the counter in case it was already running
61     nrf_rtc_task_trigger(NRF_RTC1, NRF_RTC_TASK_STOP);
62 
63     // Clear the counter
64     nrf_rtc_task_trigger(NRF_RTC1, NRF_RTC_TASK_CLEAR);
65 
66     // Set the first timer interval
67     nrf_rtc_cc_set(NRF_RTC1, LK_RTC_CC_CHAN, rtc_counts_per_tick);
68 
69     // Run at full 32768Hz rate, no prescale
70     nrf_rtc_prescaler_set(NRF_RTC1, 0);
71 
72     // Allow the CC channel to create interrupts when flagged
73     nrf_rtc_int_enable(NRF_RTC1, RTC_CHANNEL_INT_MASK(LK_RTC_CC_CHAN));
74 
75     // In order for this event to wake the device from low power/sleep state, the
76     // event must be enabled to do so.
77     nrf_rtc_event_enable(NRF_RTC1, RTC_CHANNEL_INT_MASK(LK_RTC_CC_CHAN));
78 
79     // Clear the event state prior to enabling the irq to prevent spurious irq
80     nrf_rtc_event_clear(NRF_RTC1, nrf_rtc_compare_event_get(LK_RTC_CC_CHAN));
81 
82     // Start the RTC counter.
83     nrf_rtc_task_trigger(NRF_RTC1, NRF_RTC_TASK_START);
84 
85     NVIC_EnableIRQ(RTC1_IRQn);
86 
87     return NO_ERROR;
88 }
89 
90 // time in msec
current_time(void)91 lk_time_t current_time(void) {
92     uint64_t t;
93 
94     do {
95         t = base_counter;
96     } while (base_counter != t);
97 
98     return DIV_BY_32768(t);
99 }
100 
101 // time in usec
current_time_hires(void)102 lk_bigtime_t current_time_hires(void) {
103     volatile uint64_t t;
104     volatile uint32_t delta;
105 
106     do {
107         // order of operations below is critical to catch if timer is updated
108         // during the calculations.
109         t = base_counter;
110         delta = (nrf_rtc_counter_get(NRF_RTC1) - last_cc);
111     } while ((t != base_counter));
112 
113     return DIV_BY_32768((t + delta * 1000) * 1000);
114 }
115 
nrf52_RTC1_IRQ(void)116 void nrf52_RTC1_IRQ(void) {
117     // update to this point in time
118     base_counter += ms_scale;
119     arm_cm_irq_entry();
120 
121     nrf_rtc_event_clear(NRF_RTC1, nrf_rtc_compare_event_get(LK_RTC_CC_CHAN));
122     // calculate time of next interrupt
123     last_cc = nrf_rtc_cc_get(NRF_RTC1, LK_RTC_CC_CHAN);
124     nrf_rtc_cc_set(NRF_RTC1, LK_RTC_CC_CHAN, last_cc + rtc_counts_per_tick);
125 
126     bool resched = false;
127     if (cb) {
128         lk_time_t now = DIV_BY_32768(base_counter);
129         if (cb(cb_args, now) == INT_RESCHEDULE)
130             resched = true;
131     }
132     arm_cm_irq_exit(resched);
133 }
134 
arm_cm_systick_init(uint32_t hz)135 void arm_cm_systick_init(uint32_t hz) {
136     ASSERT(hz == 32768);
137     //Try to enable the LF xtal oscillator
138     status_t status = nrf52_clock_lfclk_enable(NRF_CLOCK_LFCLK_Xtal);
139     if (status != NO_ERROR) {
140         // If  xtal won't start (might not be present on target) then
141         // use the internal RC oscillator which is always present.
142         nrf52_clock_lfclk_enable(NRF_CLOCK_LFCLK_RC);
143     }
144 }
145