1
1
/*******************************************************************************
2
- * Copyright (C) 2016,2018 Maxim Integrated Products, Inc., All Rights Reserved.
2
+ * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
3
3
*
4
4
* Permission is hereby granted, free of charge, to any person obtaining a
5
5
* copy of this software and associated documentation files (the "Software"),
36
36
#include "rtc.h"
37
37
#include "lp.h"
38
38
39
- // LOG2 for 32-bit powers of 2
40
- #define LOG2_1 (n ) (((n) >= (1 << 1)) ? 1 : 0)
41
- #define LOG2_2 (n ) (((n) >= (1 << 2)) ? ( 2 + (LOG2_1((n) >> 2))) : LOG2_1(n))
42
- #define LOG2_4 (n ) (((n) >= (1 << 4)) ? ( 4 + (LOG2_2((n) >> 4))) : LOG2_2(n))
43
- #define LOG2_8 (n ) (((n) >= (1 << 8)) ? ( 8 + (LOG2_4((n) >> 8))) : LOG2_4(n))
44
- #define LOG2 (n ) (((n) >= (1 << 16)) ? (16 + (LOG2_8((n) >> 16))) : LOG2_8(n))
39
+ #define PRESCALE_VAL RTC_PRESCALE_DIV_2_0 // Set the divider for the 4kHz clock
40
+ #define SHIFT_AMT (RTC_PRESCALE_DIV_2_12 - PRESCALE_VAL)
45
41
46
- #define LP_TIMER_FREQ_HZ 4096
47
- #define LP_TIMER_PRESCALE RTC_PRESCALE_DIV_2_0
48
- #define LP_TIMER_RATE_HZ (LP_TIMER_FREQ_HZ >> LP_TIMER_PRESCALE)
49
- #define LP_TIMER_WIDTH 32
42
+ #define WINDOW 1000
50
43
51
- static volatile int rtc_inited = 0 ;
52
- static volatile int lp_ticker_inited = 0 ;
44
+ static int rtc_inited = 0 ;
45
+ static volatile uint32_t overflow_cnt = 0 ;
46
+
47
+ static uint64_t rtc_read64 (void );
48
+
49
+ //******************************************************************************
50
+ static void overflow_handler (void )
51
+ {
52
+ overflow_cnt ++ ;
53
+ RTC_ClearFlags (MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS );
54
+ }
53
55
54
56
//******************************************************************************
55
- static void init_rtc (void )
57
+ void rtc_init (void )
56
58
{
59
+ if (rtc_inited ) {
60
+ return ;
61
+ }
62
+ rtc_inited = 1 ;
63
+
64
+ overflow_cnt = 0 ;
65
+
57
66
/* Enable power for RTC for all LPx states */
58
67
MXC_PWRSEQ -> reg0 |= (MXC_F_PWRSEQ_REG0_PWR_RTCEN_RUN |
59
68
MXC_F_PWRSEQ_REG0_PWR_RTCEN_SLP );
60
69
61
70
/* Enable clock to synchronizers */
62
71
CLKMAN_SetClkScale (CLKMAN_CLK_SYNC , CLKMAN_SCALE_DIV_1 );
63
72
73
+ // Prepare interrupt handlers
74
+ NVIC_SetVector (RTC0_IRQn , (uint32_t )lp_ticker_irq_handler );
75
+ NVIC_EnableIRQ (RTC0_IRQn );
76
+ NVIC_SetVector (RTC3_IRQn , (uint32_t )overflow_handler );
77
+ NVIC_EnableIRQ (RTC3_IRQn );
78
+
79
+ // Enable wakeup on RTC rollover
80
+ LP_ConfigRTCWakeUp (0 , 0 , 0 , 1 );
81
+
64
82
/* RTC registers are only reset on a power cycle. Do not reconfigure the RTC
65
83
* if it is already running.
66
84
*/
67
85
if (!RTC_IsActive ()) {
68
- rtc_cfg_t cfg = { 0 };
69
- cfg .prescaler = LP_TIMER_PRESCALE ;
86
+ rtc_cfg_t cfg = {0 };
87
+ cfg .prescaler = PRESCALE_VAL ;
70
88
cfg .snoozeMode = RTC_SNOOZE_DISABLE ;
71
89
72
90
int retval = RTC_Init (& cfg );
@@ -78,128 +96,163 @@ static void init_rtc(void)
78
96
}
79
97
80
98
//******************************************************************************
81
- static void overflow_handler (void )
82
- {
83
- MXC_RTCTMR -> comp [1 ] += ((UINT32_MAX >> LOG2 (LP_TIMER_RATE_HZ )) + 1 );
84
- RTC_ClearFlags (MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS );
85
- }
86
-
87
- //******************************************************************************
88
- void rtc_init (void )
99
+ void lp_ticker_init (void )
89
100
{
90
- if (rtc_inited ) {
91
- return ;
92
- }
93
-
94
- NVIC_SetVector (RTC3_IRQn , (uint32_t )overflow_handler );
95
- NVIC_EnableIRQ (RTC3_IRQn );
96
- // Enable wakeup on RTC overflow
97
- LP_ConfigRTCWakeUp (lp_ticker_inited , 0 , 0 , 1 );
98
- init_rtc ();
99
- rtc_inited = 1 ;
101
+ rtc_init ();
100
102
}
101
103
102
104
//******************************************************************************
103
105
void rtc_free (void )
104
106
{
105
- if (rtc_inited ) {
106
- rtc_inited = 0 ;
107
- if (lp_ticker_inited ) {
108
- RTC_DisableINT (MXC_F_RTC_FLAGS_OVERFLOW );
109
- } else {
110
- MXC_RTCTMR -> ctrl |= MXC_F_RTC_CTRL_CLEAR ;
111
- RTC_Stop ();
112
- }
107
+ if (RTC_IsActive ()) {
108
+ // Clear and disable RTC
109
+ MXC_RTCTMR -> ctrl |= MXC_F_RTC_CTRL_CLEAR ;
110
+ RTC_Stop ();
113
111
}
114
112
}
115
113
116
114
//******************************************************************************
117
115
int rtc_isenabled (void )
118
116
{
119
- return rtc_inited ;
117
+ return RTC_IsActive () ;
120
118
}
121
119
122
120
//******************************************************************************
123
- void rtc_write ( time_t t )
121
+ time_t rtc_read ( void )
124
122
{
123
+ uint32_t ovf_cnt_1 , ovf_cnt_2 , timer_cnt ;
124
+ uint32_t ovf1 , ovf2 ;
125
+
126
+ // Make sure RTC is setup before trying to read
125
127
if (!rtc_inited ) {
126
128
rtc_init ();
127
129
}
128
130
129
- MXC_RTCTMR -> comp [1 ] = t - (MXC_RTCTMR -> timer >> LOG2 (LP_TIMER_RATE_HZ ));
131
+ // Ensure coherency between overflow_cnt and timer
132
+ do {
133
+ ovf_cnt_1 = overflow_cnt ;
134
+ ovf1 = RTC_GetFlags () & MXC_F_RTC_FLAGS_OVERFLOW ;
135
+ timer_cnt = RTC_GetCount ();
136
+ ovf2 = RTC_GetFlags () & MXC_F_RTC_FLAGS_OVERFLOW ;
137
+ ovf_cnt_2 = overflow_cnt ;
138
+ } while ((ovf_cnt_1 != ovf_cnt_2 ) || (ovf1 != ovf2 ));
130
139
131
- // Wait for pending transactions
132
- while (MXC_RTCTMR -> ctrl & MXC_F_RTC_CTRL_PENDING );
140
+ // Account for an unserviced interrupt
141
+ if (ovf1 ) {
142
+ ovf_cnt_1 ++ ;
143
+ }
144
+
145
+ return (timer_cnt >> SHIFT_AMT ) + (ovf_cnt_1 << (32 - SHIFT_AMT ));
133
146
}
134
147
135
148
//******************************************************************************
136
- time_t rtc_read (void )
149
+ static uint64_t rtc_read64 (void )
137
150
{
151
+ uint32_t ovf_cnt_1 , ovf_cnt_2 , timer_cnt ;
152
+ uint32_t ovf1 , ovf2 ;
153
+ uint64_t current_us ;
154
+
155
+ // Make sure RTC is setup before trying to read
138
156
if (!rtc_inited ) {
139
157
rtc_init ();
140
158
}
141
159
142
- return (MXC_RTCTMR -> timer >> LOG2 (LP_TIMER_RATE_HZ )) + MXC_RTCTMR -> comp [1 ];
143
- }
160
+ // Ensure coherency between overflow_cnt and timer
161
+ do {
162
+ ovf_cnt_1 = overflow_cnt ;
163
+ ovf1 = RTC_GetFlags () & MXC_F_RTC_FLAGS_OVERFLOW ;
164
+ timer_cnt = RTC_GetCount ();
165
+ ovf2 = RTC_GetFlags () & MXC_F_RTC_FLAGS_OVERFLOW ;
166
+ ovf_cnt_2 = overflow_cnt ;
167
+ } while ((ovf_cnt_1 != ovf_cnt_2 ) || (ovf1 != ovf2 ));
144
168
145
- //******************************************************************************
146
- void lp_ticker_init (void )
147
- {
148
- if (lp_ticker_inited ) {
149
- return ;
169
+ // Account for an unserviced interrupt
170
+ if (ovf1 ) {
171
+ ovf_cnt_1 ++ ;
150
172
}
151
173
152
- NVIC_SetVector (RTC0_IRQn , (uint32_t )lp_ticker_irq_handler );
153
- NVIC_EnableIRQ (RTC0_IRQn );
154
- init_rtc ();
155
- lp_ticker_inited = 1 ;
174
+ current_us = (((uint64_t )timer_cnt * 1000000 ) >> SHIFT_AMT ) + (((uint64_t )ovf_cnt_1 * 1000000 ) << (32 - SHIFT_AMT ));
175
+
176
+ return current_us ;
156
177
}
157
178
158
179
//******************************************************************************
159
- uint32_t lp_ticker_read ( void )
180
+ void rtc_write ( time_t t )
160
181
{
161
- return MXC_RTCTMR -> timer ;
182
+ // Make sure RTC is setup before accessing
183
+ if (!rtc_inited ) {
184
+ rtc_init ();
185
+ }
186
+
187
+ RTC_Stop ();
188
+ RTC_SetCount (t << SHIFT_AMT );
189
+ overflow_cnt = t >> (32 - SHIFT_AMT );
190
+ RTC_Start ();
162
191
}
163
192
164
193
//******************************************************************************
165
194
void lp_ticker_set_interrupt (timestamp_t timestamp )
166
195
{
167
- MXC_RTCTMR -> comp [0 ] = timestamp ;
196
+ uint32_t comp_value ;
197
+ uint64_t curr_ts64 ;
198
+ uint64_t ts64 ;
199
+
200
+ // Note: interrupts are disabled before this function is called.
201
+
202
+ // Disable the alarm while it is prepared
203
+ RTC_DisableINT (MXC_F_RTC_INTEN_COMP0 );
204
+
205
+ curr_ts64 = rtc_read64 ();
206
+ ts64 = (uint64_t )timestamp | (curr_ts64 & 0xFFFFFFFF00000000ULL );
207
+
208
+ // If this event is older than a recent window, it must be in the future
209
+ if ((ts64 < (curr_ts64 - WINDOW )) && ((curr_ts64 - WINDOW ) < curr_ts64 )) {
210
+ ts64 += 0x100000000ULL ;
211
+ }
212
+
213
+ uint32_t timer = RTC_GetCount ();
214
+ if (ts64 <= curr_ts64 ) {
215
+ // This event has already occurred. Set the alarm to expire immediately.
216
+ comp_value = timer + 1 ;
217
+ } else {
218
+ comp_value = (ts64 << SHIFT_AMT ) / 1000000 ;
219
+ }
220
+
221
+ // Ensure that the compare value is far enough in the future to guarantee the interrupt occurs.
222
+ if ((comp_value < (timer + 2 )) && (comp_value > (timer - 10 ))) {
223
+ comp_value = timer + 2 ;
224
+ }
225
+
226
+ MXC_RTCTMR -> comp [0 ] = comp_value ;
168
227
MXC_RTCTMR -> flags = MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS ;
169
- MXC_RTCTMR -> inten |= MXC_F_RTC_INTEN_COMP0 ;
228
+ RTC_EnableINT ( MXC_F_RTC_INTEN_COMP0 ) ;
170
229
171
- // Enable wakeup from RTC compare 0
172
- LP_ConfigRTCWakeUp (1 , 0 , 0 , rtc_inited );
230
+ // Enable wakeup from RTC
231
+ LP_ConfigRTCWakeUp (1 , 0 , 0 , 1 );
173
232
174
233
// Wait for pending transactions
175
234
while (MXC_RTCTMR -> ctrl & MXC_F_RTC_CTRL_PENDING );
176
235
}
177
236
178
- //******************************************************************************
179
- void lp_ticker_disable_interrupt (void )
237
+ void lp_ticker_fire_interrupt (void )
180
238
{
181
- RTC_DisableINT ( MXC_F_RTC_INTEN_COMP0 );
239
+ NVIC_SetPendingIRQ ( RTC0_IRQn );
182
240
}
183
241
184
242
//******************************************************************************
185
- void lp_ticker_clear_interrupt (void )
243
+ inline void lp_ticker_disable_interrupt (void )
186
244
{
187
- RTC_ClearFlags ( MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS );
245
+ RTC_DisableINT ( MXC_F_RTC_INTEN_COMP0 );
188
246
}
189
247
190
248
//******************************************************************************
191
- void lp_ticker_fire_interrupt (void )
249
+ inline void lp_ticker_clear_interrupt (void )
192
250
{
193
- NVIC_SetPendingIRQ ( RTC0_IRQn );
251
+ RTC_ClearFlags ( MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS );
194
252
}
195
253
196
254
//******************************************************************************
197
- const ticker_info_t * lp_ticker_get_info (void )
255
+ inline uint32_t lp_ticker_read (void )
198
256
{
199
- static const ticker_info_t info = {
200
- LP_TIMER_RATE_HZ ,
201
- LP_TIMER_WIDTH
202
- };
203
-
204
- return & info ;
257
+ return rtc_read64 ();
205
258
}
0 commit comments