1 //*****************************************************************************
2 //
3 //! @file am_hal_stimer.c
4 //!
5 //! @brief Functions for interfacing with the system timer (STIMER).
6 //!
7 //! @addtogroup stimer4_4p STIMER - System Timer
8 //! @ingroup apollo4p_hal
9 //! @{
10 //
11 //*****************************************************************************
12 
13 //*****************************************************************************
14 //
15 // Copyright (c) 2024, Ambiq Micro, Inc.
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are met:
20 //
21 // 1. Redistributions of source code must retain the above copyright notice,
22 // this list of conditions and the following disclaimer.
23 //
24 // 2. Redistributions in binary form must reproduce the above copyright
25 // notice, this list of conditions and the following disclaimer in the
26 // documentation and/or other materials provided with the distribution.
27 //
28 // 3. Neither the name of the copyright holder nor the names of its
29 // contributors may be used to endorse or promote products derived from this
30 // software without specific prior written permission.
31 //
32 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
33 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
36 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 // POSSIBILITY OF SUCH DAMAGE.
43 //
44 // This is part of revision stable-5d223aedc3 of the AmbiqSuite Development Package.
45 //
46 //*****************************************************************************
47 
48 #include <stdint.h>
49 #include <stdbool.h>
50 #include "am_mcu_apollo.h"
51 
52 //
53 //! Timer Currently Configured
54 //
55 static bool bStimerConfigured = false;
56 
57 //
58 //! Time at which last delta was set
59 //
60 static uint32_t g_lastStimer[8] =
61 {
62     0xFFFFFFFE, 0xFFFFFFFE, 0xFFFFFFFE, 0xFFFFFFFE,
63     0xFFFFFFFE, 0xFFFFFFFE, 0xFFFFFFFE, 0xFFFFFFFE
64 };
65 
66 //*****************************************************************************
67 //
68 // Set up the stimer.
69 //
70 // This function should be used to perform the initial set-up of the
71 // stimer.
72 //
73 //*****************************************************************************
74 uint32_t
am_hal_stimer_config(uint32_t ui32STimerConfig)75 am_hal_stimer_config(uint32_t ui32STimerConfig)
76 {
77     uint32_t ui32CurrVal;
78 
79     //
80     // Read the current config
81     //
82     ui32CurrVal = STIMER->STCFG;
83 
84     //
85     // Write our configuration value.
86     //
87     STIMER->STCFG = ui32STimerConfig;
88 
89     //
90     // Indication that the STIMER has been configured.
91     //
92     bStimerConfigured = true;
93 
94     return ui32CurrVal;
95 }
96 
97 //*****************************************************************************
98 //
99 // Check if the STIMER is running.
100 //
101 // This function should be used to perform the initial set-up of the
102 // stimer.
103 //
104 //*****************************************************************************
105 bool
am_hal_stimer_is_running(void)106 am_hal_stimer_is_running(void)
107 {
108   //
109   // Check the STIMER has been configured and is currently counting
110   //
111   return (bStimerConfigured &&
112       (STIMER_STCFG_CLKSEL_NOCLK != STIMER->STCFG_b.CLKSEL) &&
113       (STIMER_STCFG_FREEZE_THAW == STIMER->STCFG_b.FREEZE) &&
114       (STIMER_STCFG_CLEAR_RUN == STIMER->STCFG_b.CLEAR));
115 }
116 //*****************************************************************************
117 //
118 // Reset the current stimer block to power-up state.
119 //
120 //*****************************************************************************
121 void
am_hal_stimer_reset_config(void)122 am_hal_stimer_reset_config(void)
123 {
124     STIMER->STCFG       = _VAL2FLD(STIMER_STCFG_FREEZE, 1);
125     STIMER->SCAPCTRL0   = _VAL2FLD(STIMER_SCAPCTRL0_STSEL0, 0x7F);
126     STIMER->SCAPCTRL1   = _VAL2FLD(STIMER_SCAPCTRL1_STSEL1, 0x7F);
127     STIMER->SCAPCTRL2   = _VAL2FLD(STIMER_SCAPCTRL2_STSEL2, 0x7F);
128     STIMER->SCAPCTRL3   = _VAL2FLD(STIMER_SCAPCTRL3_STSEL3, 0x7F);
129     STIMER->SCMPR0      = 0;
130     STIMER->SCMPR1      = 0;
131     STIMER->SCMPR2      = 0;
132     STIMER->SCMPR3      = 0;
133     STIMER->SCMPR4      = 0;
134     STIMER->SCMPR5      = 0;
135     STIMER->SCMPR6      = 0;
136     STIMER->SCMPR7      = 0;
137     STIMER->SCAPT0      = 0;
138     STIMER->SCAPT1      = 0;
139     STIMER->SCAPT2      = 0;
140     STIMER->SCAPT3      = 0;
141     STIMER->SNVR0       = 0;
142     STIMER->SNVR1       = 0;
143     STIMER->SNVR2       = 0;
144     STIMER->STMINTEN    = 0;
145     STIMER->STMINTSTAT   = 0;
146     STIMER->STMINTCLR    = 0xFFF;
147 }
148 
149 //*****************************************************************************
150 //
151 // Get the current stimer value.
152 //
153 // This function can be used to read, uninvasively, the value in the stimer.
154 //
155 //*****************************************************************************
156 uint32_t
am_hal_stimer_counter_get(void)157 am_hal_stimer_counter_get(void)
158 {
159     uint32_t      ui32TimerAddr = (uint32_t)&STIMER->STTMR;
160     uint32_t      ui32TimerVals[3];
161 
162     //
163     // Read the register into ui32TimerVals[].
164     //
165     am_hal_triple_read(ui32TimerAddr, ui32TimerVals);
166 
167     //
168     // Now determine which of the three values is the correct value.
169     // If the first 2 match, then the values are both correct and we're done.
170     // Otherwise, the third value is taken to be the correct value.
171     //
172     if ( ui32TimerVals[0] == ui32TimerVals[1] )
173     {
174         //
175         // If the first two values match, then neither one was a bad read.
176         // We'll take this as the current time.
177         //
178         return ui32TimerVals[1];
179     }
180     else
181     {
182         return ui32TimerVals[2];
183     }
184 }
185 
186 //*****************************************************************************
187 //
188 // Clear the stimer counter.
189 //
190 // This function clears the STimer Counter and leaves the stimer running.
191 //
192 //*****************************************************************************
193 void
am_hal_stimer_counter_clear(void)194 am_hal_stimer_counter_clear(void)
195 {
196     //
197     // Set the clear bit
198     //
199     STIMER->STCFG |= STIMER_STCFG_CLEAR_Msk;
200 
201     //
202     // Reset the clear bit
203     //
204     STIMER->STCFG &= ~STIMER_STCFG_CLEAR_Msk;
205 }
206 
207 //*****************************************************************************
208 //
209 // Check if the compare value can be set without blocking.
210 //
211 //*****************************************************************************
212 bool
am_hal_stimer_check_compare_delta_set(uint32_t ui32CmprInstance)213 am_hal_stimer_check_compare_delta_set(uint32_t ui32CmprInstance)
214 {
215     uint32_t curTimer;
216 #ifndef AM_HAL_DISABLE_API_VALIDATION
217     if ( ui32CmprInstance > 7 )
218     {
219         return AM_HAL_STATUS_OUT_OF_RANGE;
220     }
221 #endif
222     // Take a snapshot of STIMER
223     curTimer = am_hal_stimer_counter_get();
224     // We cannot set COMPARE back to back
225     // Need to wait for previous write to complete, which takes 2 cycles
226     if ((curTimer != g_lastStimer[ui32CmprInstance]) &&
227         (curTimer != (g_lastStimer[ui32CmprInstance] + 1)))
228     {
229         return true;
230     }
231     else
232     {
233         return false;
234     }
235 }
236 
237 //*****************************************************************************
238 //
239 // Set the compare value.
240 //
241 //*****************************************************************************
242 uint32_t
am_hal_stimer_compare_delta_set(uint32_t ui32CmprInstance,uint32_t ui32Delta)243 am_hal_stimer_compare_delta_set(uint32_t ui32CmprInstance, uint32_t ui32Delta)
244 {
245     uint32_t curTimer, curTimer0;
246     uint32_t ui32Ret = AM_HAL_STATUS_SUCCESS;
247 
248     // Take a snapshot of STIMER at the beginning of the function
249     curTimer = curTimer0 = am_hal_stimer_counter_get();
250 
251 #ifndef AM_HAL_DISABLE_API_VALIDATION
252     if ( ui32CmprInstance > 7 )
253     {
254         return AM_HAL_STATUS_OUT_OF_RANGE;
255     }
256 #endif
257     //
258     //! @note Due to latency in write to COMPARE register to take effect, it is
259     //!  possible that the application could get a stale interrupt even after
260     //!  this API returns (a result of previous value of COMPARE).
261     //!
262     //! The application needs to handle these cases gracefully.
263     //
264 
265     do
266     {
267         // We cannot set COMPARE back to back
268         // Need to wait for previous write to complete, which takes 2 cycles
269         if ((curTimer != g_lastStimer[ui32CmprInstance]) &&
270             (curTimer != (g_lastStimer[ui32CmprInstance] + 1)))
271         {
272             //
273             // Start a critical section.
274             //
275             AM_CRITICAL_BEGIN
276             curTimer = am_hal_stimer_counter_get();
277             // Adjust Delta
278             // It takes 2 STIMER clock cycles for writes to COMPARE to be effective
279             // Also the interrupt itself is delayed by a cycle
280             // This effectively means we need to adjust the delta by 3
281             // Also adjust for the delay since we entered the function
282             if (ui32Delta > (3 + (curTimer - curTimer0)))
283             {
284                 ui32Delta -= 3 + (curTimer - curTimer0);
285             }
286             else
287             {
288                 // Need to floor delta to 1
289                 ui32Delta = 1;
290                 ui32Ret = AM_HAL_STIMER_DELTA_TOO_SMALL;
291             }
292             //
293             // Set the delta
294             //
295             AM_REGVAL(AM_REG_STIMER_COMPARE(0, ui32CmprInstance)) = ui32Delta;
296             //
297             // Get a snapshot when we set COMPARE
298             //
299             g_lastStimer[ui32CmprInstance] = am_hal_stimer_counter_get();
300             //
301             // End the critical section.
302             //
303             AM_CRITICAL_END
304             break;
305         }
306         curTimer = am_hal_stimer_counter_get();
307     } while (1);
308 
309     return ui32Ret;
310 }
311 
312 //*****************************************************************************
313 //
314 // Get the current stimer compare register value.
315 //
316 // This function can be used to read the value in an stimer compare register.
317 //
318 //*****************************************************************************
319 uint32_t
am_hal_stimer_compare_get(uint32_t ui32CmprInstance)320 am_hal_stimer_compare_get(uint32_t ui32CmprInstance)
321 {
322     uint32_t curTimer;
323 #ifndef AM_HAL_DISABLE_API_VALIDATION
324     if ( ui32CmprInstance > 7 )
325     {
326         return 0;
327     }
328 #endif
329     //
330     // Need to wait at least 3 cycle after setting the compare to read it
331     // reliably
332     //
333     do
334     {
335         curTimer = am_hal_stimer_counter_get();
336         if ((curTimer != g_lastStimer[ui32CmprInstance]) &&
337             (curTimer != (g_lastStimer[ui32CmprInstance] + 1)) &&
338             (curTimer != (g_lastStimer[ui32CmprInstance] + 2)))
339         {
340             return AM_REGVAL(AM_REG_STIMER_COMPARE(0, ui32CmprInstance));
341         }
342     } while (1);
343 }
344 
345 //*****************************************************************************
346 //
347 // Start capturing data with the specified capture register.
348 //
349 // Use this function to start capturing.
350 //
351 //*****************************************************************************
352 void
am_hal_stimer_capture_start(uint32_t ui32CaptureNum,uint32_t ui32GPIONumber,bool bPolarity)353 am_hal_stimer_capture_start(uint32_t ui32CaptureNum,
354                             uint32_t ui32GPIONumber,
355                             bool bPolarity)
356 {
357 #ifndef AM_HAL_DISABLE_API_VALIDATION
358     if ( ui32GPIONumber > (AM_HAL_GPIO_MAX_PADS-1) )
359     {
360         return;
361     }
362 #endif
363 
364     //
365     // Set the polarity and pin selection in the GPIO block.
366     //
367     switch (ui32CaptureNum)
368     {
369          case 0:
370             STIMER->SCAPCTRL0_b.STPOL0 = bPolarity;
371             STIMER->SCAPCTRL0_b.STSEL0 = ui32GPIONumber;
372             STIMER->SCAPCTRL0_b.CAPTURE0 = STIMER_SCAPCTRL0_CAPTURE0_ENABLE;
373             break;
374          case 1:
375             STIMER->SCAPCTRL1_b.STPOL1 = bPolarity;
376             STIMER->SCAPCTRL1_b.STSEL1 = ui32GPIONumber;
377             STIMER->SCAPCTRL1_b.CAPTURE1 = STIMER_SCAPCTRL1_CAPTURE1_ENABLE;
378             break;
379          case 2:
380             STIMER->SCAPCTRL2_b.STPOL2 = bPolarity;
381             STIMER->SCAPCTRL2_b.STSEL2 = ui32GPIONumber;
382             STIMER->SCAPCTRL2_b.CAPTURE2 = STIMER_SCAPCTRL2_CAPTURE2_ENABLE;
383             break;
384          case 3:
385             STIMER->SCAPCTRL3_b.STPOL3 = bPolarity;
386             STIMER->SCAPCTRL3_b.STSEL3 = ui32GPIONumber;
387             STIMER->SCAPCTRL3_b.CAPTURE3 = STIMER_SCAPCTRL3_CAPTURE3_ENABLE;
388             break;
389          default:
390             return;     // error concealment.
391     }
392 
393     //
394     // Set TIMER Global Enable for GPIO inputs.
395     //
396     TIMER->GLOBEN_b.ENABLEALLINPUTS = 1;
397 
398 }
399 
400 //*****************************************************************************
401 //
402 // Stop capturing data with the specified capture register.
403 //
404 // Use this function to stop capturing.
405 //
406 //*****************************************************************************
407 void
am_hal_stimer_capture_stop(uint32_t ui32CaptureNum)408 am_hal_stimer_capture_stop(uint32_t ui32CaptureNum)
409 {
410     //
411     // Disable it in the STIMER block.
412     //
413     switch (ui32CaptureNum)
414     {
415          case 0:
416              STIMER->SCAPCTRL0_b.CAPTURE0 = STIMER_SCAPCTRL0_CAPTURE0_DISABLE;
417             break;
418          case 1:
419              STIMER->SCAPCTRL1_b.CAPTURE1 = STIMER_SCAPCTRL1_CAPTURE1_DISABLE;
420             break;
421          case 2:
422              STIMER->SCAPCTRL2_b.CAPTURE2 = STIMER_SCAPCTRL2_CAPTURE2_DISABLE;
423             break;
424          case 3:
425              STIMER->SCAPCTRL3_b.CAPTURE3 = STIMER_SCAPCTRL3_CAPTURE3_DISABLE;
426             break;
427          default:
428             return;     // error concealment.
429     }
430 
431     //
432     // Start a critical section.
433     //
434     AM_CRITICAL_BEGIN
435 
436     //
437     // Set TIMER Global Disable for GPIO inputs if all are DISABLED.
438     //
439     if ((STIMER->SCAPCTRL0_b.CAPTURE0 == STIMER_SCAPCTRL0_CAPTURE0_DISABLE) &&
440             (STIMER->SCAPCTRL1_b.CAPTURE1 == STIMER_SCAPCTRL1_CAPTURE1_DISABLE) &&
441             (STIMER->SCAPCTRL2_b.CAPTURE2 == STIMER_SCAPCTRL2_CAPTURE2_DISABLE) &&
442             (STIMER->SCAPCTRL3_b.CAPTURE3 == STIMER_SCAPCTRL3_CAPTURE3_DISABLE))
443     {
444         TIMER->GLOBEN_b.ENABLEALLINPUTS = 0;
445     }
446 
447     AM_CRITICAL_END
448 }
449 
450 //*****************************************************************************
451 //
452 // Set the current stimer nvram register value.
453 //
454 // This function can be used to read the value in an stimer NVRAM register.
455 //
456 //*****************************************************************************
457 void
am_hal_stimer_nvram_set(uint32_t ui32NvramNum,uint32_t ui32NvramVal)458 am_hal_stimer_nvram_set(uint32_t ui32NvramNum, uint32_t ui32NvramVal)
459 {
460 #ifndef AM_HAL_DISABLE_API_VALIDATION
461     if ( ui32NvramNum > 3 )
462     {
463         return;
464     }
465 #endif
466 
467     AM_REGVAL(AM_REG_STIMER_NVRAM(0, ui32NvramNum)) = ui32NvramVal;
468 }
469 
470 //*****************************************************************************
471 //
472 // Get the current stimer nvram register value.
473 //
474 // This function can be used to read the value in an stimer NVRAM register.
475 //
476 //*****************************************************************************
am_hal_stimer_nvram_get(uint32_t ui32NvramNum)477 uint32_t am_hal_stimer_nvram_get(uint32_t ui32NvramNum)
478 {
479 #ifndef AM_HAL_DISABLE_API_VALIDATION
480     if ( ui32NvramNum > 3 )
481     {
482         return 0;
483     }
484 #endif
485 
486     return AM_REGVAL(AM_REG_STIMER_NVRAM(0, ui32NvramNum));
487 }
488 
489 //*****************************************************************************
490 //
491 // Get the current stimer capture register value.
492 //
493 // This function can be used to read the value in an stimer capture register.
494 //
495 //*****************************************************************************
am_hal_stimer_capture_get(uint32_t ui32CaptureNum)496 uint32_t am_hal_stimer_capture_get(uint32_t ui32CaptureNum)
497 {
498 #ifndef AM_HAL_DISABLE_API_VALIDATION
499     if ( ui32CaptureNum > 3 )
500     {
501         return 0;
502     }
503 #endif
504 
505     return AM_REGVAL(AM_REG_STIMER_CAPTURE(0, ui32CaptureNum));
506 }
507 
508 //*****************************************************************************
509 //
510 // Enables the selected system timer interrupt.
511 //
512 // This function will enable the selected interrupts in the STIMER interrupt
513 // enable register. In order to receive an interrupt from an stimer component,
514 // you will need to enable the interrupt for that component in this main
515 // register, as well as in the stimer configuration register (accessible though
516 // am_hal_stimer_config()), and in the NVIC.
517 //
518 //*****************************************************************************
519 void
am_hal_stimer_int_enable(uint32_t ui32Interrupt)520 am_hal_stimer_int_enable(uint32_t ui32Interrupt)
521 {
522     //
523     // Enable the interrupt at the module level.
524     //
525     STIMER->STMINTEN |= ui32Interrupt;
526 }
527 
528 //*****************************************************************************
529 //
530 // Return the enabled stimer interrupts.
531 //
532 // This function will return all enabled interrupts in the STIMER
533 // interrupt enable register.
534 //
535 //*****************************************************************************
536 uint32_t
am_hal_stimer_int_enable_get(void)537 am_hal_stimer_int_enable_get(void)
538 {
539     //
540     // Return enabled interrupts.
541     //
542     return STIMER->STMINTEN;
543 }
544 
545 //*****************************************************************************
546 //
547 // Disables the selected stimer interrupt.
548 //
549 // This function will disable the selected interrupts in the STIMER
550 // interrupt register.
551 //
552 //*****************************************************************************
553 void
am_hal_stimer_int_disable(uint32_t ui32Interrupt)554 am_hal_stimer_int_disable(uint32_t ui32Interrupt)
555 {
556     //
557     // Disable the interrupt at the module level.
558     //
559     STIMER->STMINTEN &= ~ui32Interrupt;
560 }
561 
562 //*****************************************************************************
563 //
564 // Sets the selected stimer interrupt.
565 //
566 // This function will set the selected interrupts in the STIMER
567 // interrupt register.
568 //
569 //*****************************************************************************
570 void
am_hal_stimer_int_set(uint32_t ui32Interrupt)571 am_hal_stimer_int_set(uint32_t ui32Interrupt)
572 {
573     //
574     // Set the interrupts.
575     //
576     STIMER->STMINTSET = ui32Interrupt;
577 }
578 
579 //*****************************************************************************
580 //
581 // Clears the selected stimer interrupt.
582 //
583 // This function will clear the selected interrupts in the STIMER
584 // interrupt register.
585 //
586 //*****************************************************************************
587 void
am_hal_stimer_int_clear(uint32_t ui32Interrupt)588 am_hal_stimer_int_clear(uint32_t ui32Interrupt)
589 {
590     //
591     // Disable the interrupt at the module level.
592     //
593     STIMER->STMINTCLR = ui32Interrupt;
594 }
595 
596 //*****************************************************************************
597 //
598 // Returns either the enabled or raw stimer interrupt status.
599 //
600 // This function will return the stimer interrupt status.
601 //
602 //*****************************************************************************
603 uint32_t
am_hal_stimer_int_status_get(bool bEnabledOnly)604 am_hal_stimer_int_status_get(bool bEnabledOnly)
605 {
606     //
607     // Return the desired status.
608     //
609     uint32_t ui32RetVal = STIMER->STMINTSTAT;
610 
611     if ( bEnabledOnly )
612     {
613         ui32RetVal &= STIMER->STMINTEN;
614     }
615 
616     return ui32RetVal;
617 }
618 
619 //*****************************************************************************
620 //
621 // End Doxygen group.
622 //! @}
623 //
624 //*****************************************************************************
625