16
16
17
17
#include "lp_ticker_api.h"
18
18
19
- #if DEVICE_LOWPOWERTIMER
19
+ #if DEVICE_LPTICKER
20
20
21
21
#include "sleep_api.h"
22
22
#include "mbed_wait_api.h"
@@ -67,7 +67,14 @@ static const struct nu_modinit_s timer3_modinit = {TIMER_3, TMR3_MODULE, CLK_CLK
67
67
68
68
#endif
69
69
70
- static int ticker_inited = 0 ;
70
+ /* Timer interrupt enable/disable
71
+ *
72
+ * Because Timer interrupt enable/disable (TIMER_EnableInt/TIMER_DisableInt) needs wait for lp_ticker,
73
+ * we call NVIC_DisableIRQ/NVIC_EnableIRQ instead.
74
+ */
75
+
76
+ /* Track ticker status */
77
+ static volatile uint16_t ticker_inited = 0 ;
71
78
72
79
#define TMR_CMP_MIN 2
73
80
#define TMR_CMP_MAX 0xFFFFFFu
@@ -78,6 +85,11 @@ static int ticker_inited = 0;
78
85
void lp_ticker_init (void )
79
86
{
80
87
if (ticker_inited ) {
88
+ /* By HAL spec, ticker_init allows the ticker to keep counting and disables the
89
+ * ticker interrupt. */
90
+ lp_ticker_disable_interrupt ();
91
+ lp_ticker_clear_interrupt ();
92
+ NVIC_ClearPendingIRQ (TIMER_MODINIT .irq_n );
81
93
return ;
82
94
}
83
95
ticker_inited = 1 ;
@@ -120,7 +132,7 @@ void lp_ticker_init(void)
120
132
// Set vector
121
133
NVIC_SetVector (TIMER_MODINIT .irq_n , (uint32_t ) TIMER_MODINIT .var );
122
134
123
- NVIC_EnableIRQ (TIMER_MODINIT .irq_n );
135
+ NVIC_DisableIRQ (TIMER_MODINIT .irq_n );
124
136
125
137
TIMER_EnableInt (timer_base );
126
138
wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
@@ -135,6 +147,36 @@ void lp_ticker_init(void)
135
147
while (! (timer_base -> CTL & TIMER_CTL_ACTSTS_Msk ));
136
148
}
137
149
150
+ void lp_ticker_free (void )
151
+ {
152
+ TIMER_T * timer_base = (TIMER_T * ) NU_MODBASE (TIMER_MODINIT .modname );
153
+
154
+ /* Stop counting */
155
+ TIMER_Stop (timer_base );
156
+ wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
157
+
158
+ /* Wait for timer to stop counting and unset active flag */
159
+ while ((timer_base -> CTL & TIMER_CTL_ACTSTS_Msk ));
160
+
161
+ /* Disable wakeup */
162
+ TIMER_DisableWakeup (timer_base );
163
+ wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
164
+
165
+ /* Disable interrupt */
166
+ TIMER_DisableInt (timer_base );
167
+ wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
168
+
169
+ NVIC_DisableIRQ (TIMER_MODINIT .irq_n );
170
+
171
+ /* Disable IP clock
172
+ *
173
+ * NOTE: We must call secure version (from non-secure domain) because SYS/CLK regions are secure.
174
+ */
175
+ CLK_DisableModuleClock_S (TIMER_MODINIT .clkidx );
176
+
177
+ ticker_inited = 0 ;
178
+ }
179
+
138
180
timestamp_t lp_ticker_read ()
139
181
{
140
182
if (! ticker_inited ) {
@@ -163,27 +205,39 @@ void lp_ticker_set_interrupt(timestamp_t timestamp)
163
205
uint32_t cmp_timer = timestamp * NU_TMRCLK_PER_TICK ;
164
206
cmp_timer = NU_CLAMP (cmp_timer , TMR_CMP_MIN , TMR_CMP_MAX );
165
207
208
+ /* NOTE: Rely on LPTICKER_DELAY_TICKS to be non-blocking. */
166
209
timer_base -> CMP = cmp_timer ;
167
- wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
210
+
211
+ /* We can call ticker_irq_handler now. */
212
+ NVIC_EnableIRQ (TIMER_MODINIT .irq_n );
168
213
}
169
214
170
215
void lp_ticker_disable_interrupt (void )
171
216
{
172
- TIMER_DisableInt (( TIMER_T * ) NU_MODBASE ( TIMER_MODINIT . modname ));
173
- wait_us (( NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
217
+ /* We cannot call ticker_irq_handler now. */
218
+ NVIC_DisableIRQ ( TIMER_MODINIT . irq_n );
174
219
}
175
220
176
221
void lp_ticker_clear_interrupt (void )
177
222
{
178
- TIMER_ClearIntFlag ((TIMER_T * ) NU_MODBASE (TIMER_MODINIT .modname ));
179
- wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
223
+ /* To avoid sync issue, we clear TIF/TWKF simultaneously rather than call separate
224
+ * driver API:
225
+ *
226
+ * TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
227
+ * TIMER_ClearWakeupFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
228
+ */
229
+ TIMER_T * timer_base = (TIMER_T * ) NU_MODBASE (TIMER_MODINIT .modname );
230
+ timer_base -> INTSTS = TIMER_INTSTS_TIF_Msk | TIMER_INTSTS_TWKF_Msk ;
180
231
}
181
232
182
233
void lp_ticker_fire_interrupt (void )
183
234
{
184
235
// NOTE: This event was in the past. Set the interrupt as pending, but don't process it here.
185
236
// This prevents a recursive loop under heavy load which can lead to a stack overflow.
186
237
NVIC_SetPendingIRQ (TIMER_MODINIT .irq_n );
238
+
239
+ /* We can call ticker_irq_handler now. */
240
+ NVIC_EnableIRQ (TIMER_MODINIT .irq_n );
187
241
}
188
242
189
243
const ticker_info_t * lp_ticker_get_info ()
@@ -201,11 +255,25 @@ static void tmr1_vec(void)
201
255
static void tmr3_vec (void )
202
256
#endif
203
257
{
204
- TIMER_ClearIntFlag ((TIMER_T * ) NU_MODBASE (TIMER_MODINIT .modname ));
205
- wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
206
-
207
- TIMER_ClearWakeupFlag ((TIMER_T * ) NU_MODBASE (TIMER_MODINIT .modname ));
208
- wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
258
+ /* NOTE: Avoid blocking in ISR due to wait for "clear interrupt flag"
259
+ *
260
+ * "clear interrupt flag" needs wait to take effect which isn't added here to avoid
261
+ * blocking in ISR.
262
+ *
263
+ * Continuing above, we will get stuck in ISR due to dummy interrupt until
264
+ * "clear interrupt flag" takes effect. To avoid it, we disable interrupt here and enable
265
+ * interrupt in lp_ticker_fire_interrupt/lp_ticker_set_interrupt. There is another risk
266
+ * that we may get stuck in a loop of ISR and lp_ticker_fire_interrupt/
267
+ * lp_ticker_set_interrupt (called by lp_ticker_irq_handler), but actually we don't:
268
+ * 1. When lp_ticker_fire_interrupt gets called, it means there is a past event and so this
269
+ * interrupt isn't dummy.
270
+ * 2. With LPTICKER_DELAY_TICKS enabled, it is lp_ticker_set_interrupt_wrapper rather than
271
+ * lp_ticker_set_interrupt that gets straight called. lp_ticker_set_interrupt_wrapper
272
+ * guarantees that lp_ticker_set_interrupt won't get re-called in LPTICKER_DELAY_TICKS ticks
273
+ * which is just enough for "clear interrupt flag" to take effect.
274
+ */
275
+ lp_ticker_clear_interrupt ();
276
+ lp_ticker_disable_interrupt ();
209
277
210
278
// NOTE: lp_ticker_set_interrupt() may get called in lp_ticker_irq_handler();
211
279
lp_ticker_irq_handler ();
0 commit comments