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