@@ -42,17 +42,42 @@ static const struct nu_modinit_s timer1_modinit = {TIMER_1, TMR1_MODULE, CLK_CLK
42
42
43
43
#define TIMER_MODINIT timer1_modinit
44
44
45
- static int ticker_inited = 0 ;
45
+ /* Timer interrupt enable/disable
46
+ *
47
+ * Because Timer interrupt enable/disable (TIMER_EnableInt/TIMER_DisableInt) needs wait for lp_ticker,
48
+ * we call NVIC_DisableIRQ/NVIC_EnableIRQ instead.
49
+ */
50
+
51
+ /* Track ticker status */
52
+ static volatile uint16_t ticker_inited = 0 ;
46
53
47
54
#define TMR_CMP_MIN 2
48
55
#define TMR_CMP_MAX 0xFFFFFFu
49
56
50
- /* NOTE: When system clock is higher than timer clock, we need to add 3 engine clock
51
- * (recommended by designer) delay to wait for above timer control to take effect. */
57
+ /* Synchronization issue with LXT/LIRC-clocked Timer
58
+ *
59
+ * PCLK : typical HCLK/2
60
+ * ECLK (engine clock) : LXT/LIRC for Timer used to implement lp_ticker
61
+ *
62
+ * When system clock is higher than Timer clock (LXT/LIRC), we need to add delay for ECLK
63
+ * domain to take effect:
64
+ * 1. Write : typical 1PCLK + 2ECLK
65
+ * Read-check doesn't work because it just checks PCLK domain and doesn't check into
66
+ * ECLK domain.
67
+ * 2. Clear interrupt flag : typical 2PCLK
68
+ * It is very rare that we would meet dummy interrupt and get stuck in ISR until
69
+ * 'clear interrupt flag' takes effect. The issue is ignorable because the pending
70
+ * time is very short (at most 1 dummy interrupt). We won't take special handling for it.
71
+ */
52
72
53
73
void lp_ticker_init (void )
54
74
{
55
75
if (ticker_inited ) {
76
+ /* By HAL spec, ticker_init allows the ticker to keep counting and disables the
77
+ * ticker interrupt. */
78
+ lp_ticker_disable_interrupt ();
79
+ lp_ticker_clear_interrupt ();
80
+ NVIC_ClearPendingIRQ (TIMER_MODINIT .irq_n );
56
81
return ;
57
82
}
58
83
ticker_inited = 1 ;
@@ -86,7 +111,7 @@ void lp_ticker_init(void)
86
111
// Set vector
87
112
NVIC_SetVector (TIMER_MODINIT .irq_n , (uint32_t ) TIMER_MODINIT .var );
88
113
89
- NVIC_EnableIRQ (TIMER_MODINIT .irq_n );
114
+ NVIC_DisableIRQ (TIMER_MODINIT .irq_n );
90
115
91
116
TIMER_EnableInt (timer_base );
92
117
wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
@@ -101,6 +126,33 @@ void lp_ticker_init(void)
101
126
while (! (timer_base -> CTL & TIMER_CTL_ACTSTS_Msk ));
102
127
}
103
128
129
+ void lp_ticker_free (void )
130
+ {
131
+ TIMER_T * timer_base = (TIMER_T * ) NU_MODBASE (TIMER_MODINIT .modname );
132
+
133
+ /* Stop counting */
134
+ TIMER_Stop (timer_base );
135
+ wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
136
+
137
+ /* Wait for timer to stop counting and unset active flag */
138
+ while ((timer_base -> CTL & TIMER_CTL_ACTSTS_Msk ));
139
+
140
+ /* Disable wakeup */
141
+ TIMER_DisableWakeup (timer_base );
142
+ wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
143
+
144
+ /* Disable interrupt */
145
+ TIMER_DisableInt (timer_base );
146
+ wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
147
+
148
+ NVIC_DisableIRQ (TIMER_MODINIT .irq_n );
149
+
150
+ /* Disable IP clock */
151
+ CLK_DisableModuleClock (TIMER_MODINIT .clkidx );
152
+
153
+ ticker_inited = 0 ;
154
+ }
155
+
104
156
timestamp_t lp_ticker_read ()
105
157
{
106
158
if (! ticker_inited ) {
@@ -129,27 +181,39 @@ void lp_ticker_set_interrupt(timestamp_t timestamp)
129
181
uint32_t cmp_timer = timestamp * NU_TMRCLK_PER_TICK ;
130
182
cmp_timer = NU_CLAMP (cmp_timer , TMR_CMP_MIN , TMR_CMP_MAX );
131
183
184
+ /* NOTE: Rely on LPTICKER_DELAY_TICKS to be non-blocking. */
132
185
timer_base -> CMP = cmp_timer ;
133
- wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
186
+
187
+ /* We can call ticker_irq_handler now. */
188
+ NVIC_EnableIRQ (TIMER_MODINIT .irq_n );
134
189
}
135
190
136
191
void lp_ticker_disable_interrupt (void )
137
192
{
138
- TIMER_DisableInt (( TIMER_T * ) NU_MODBASE ( TIMER_MODINIT . modname ));
139
- wait_us (( NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
193
+ /* We cannot call ticker_irq_handler now. */
194
+ NVIC_DisableIRQ ( TIMER_MODINIT . irq_n );
140
195
}
141
196
142
197
void lp_ticker_clear_interrupt (void )
143
198
{
144
- TIMER_ClearIntFlag ((TIMER_T * ) NU_MODBASE (TIMER_MODINIT .modname ));
145
- wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
199
+ /* To avoid sync issue, we clear TIF/TWKF simultaneously rather than call separate
200
+ * driver API:
201
+ *
202
+ * TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
203
+ * TIMER_ClearWakeupFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
204
+ */
205
+ TIMER_T * timer_base = (TIMER_T * ) NU_MODBASE (TIMER_MODINIT .modname );
206
+ timer_base -> INTSTS = TIMER_INTSTS_TIF_Msk | TIMER_INTSTS_TWKF_Msk ;
146
207
}
147
208
148
209
void lp_ticker_fire_interrupt (void )
149
210
{
150
211
// NOTE: This event was in the past. Set the interrupt as pending, but don't process it here.
151
212
// This prevents a recursive loop under heavy load which can lead to a stack overflow.
152
213
NVIC_SetPendingIRQ (TIMER_MODINIT .irq_n );
214
+
215
+ /* We can call ticker_irq_handler now. */
216
+ NVIC_EnableIRQ (TIMER_MODINIT .irq_n );
153
217
}
154
218
155
219
const ticker_info_t * lp_ticker_get_info ()
@@ -163,11 +227,7 @@ const ticker_info_t* lp_ticker_get_info()
163
227
164
228
static void tmr1_vec (void )
165
229
{
166
- TIMER_ClearIntFlag ((TIMER_T * ) NU_MODBASE (TIMER_MODINIT .modname ));
167
- wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
168
-
169
- TIMER_ClearWakeupFlag ((TIMER_T * ) NU_MODBASE (TIMER_MODINIT .modname ));
170
- wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
230
+ lp_ticker_clear_interrupt ();
171
231
172
232
// NOTE: lp_ticker_set_interrupt() may get called in lp_ticker_irq_handler();
173
233
lp_ticker_irq_handler ();
0 commit comments