1 //*****************************************************************************
2 //
3 //! @file am_hal_stimer.c
4 //!
5 //! @brief Functions for interfacing with the system timer (STIMER).
6 //!
7 //! @addtogroup stimer3 STIMER - System Timer
8 //! @ingroup apollo3_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 release_sdk_3_2_0-dd5f40c14b 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 //
54 // @brief Set up the stimer.
55 //
56 // @param ui32STimerConfig is the value to load into the configuration reg.
57 //
58 // This function should be used to perform the initial set-up of the
59 // stimer.
60 //
61 // @return The 32-bit current config of the STimer Config register
62 //
63 //*****************************************************************************
64 uint32_t
am_hal_stimer_config(uint32_t ui32STimerConfig)65 am_hal_stimer_config(uint32_t ui32STimerConfig)
66 {
67     uint32_t ui32CurrVal;
68 
69     //
70     // Read the current config
71     //
72     ui32CurrVal = CTIMER->STCFG;
73 
74     //
75     // Write our configuration value.
76     //
77     CTIMER->STCFG = ui32STimerConfig;
78 
79 #if AM_PART_APOLLO2
80     //
81     // If all of the clock sources are not HFRC, disable LDO when sleeping if timers are enabled.
82     //
83     if ( (CTIMER->STCFG_b.CLKSELCTIMER->STCFG_b.CLKSEL == AM_REG_CTIMER_STCFG_CLKSEL_HFRC_DIV16)    ||
84          (CTIMER->STCFG_b.CLKSELCTIMER->STCFG_b.CLKSEL == AM_REG_CTIMER_STCFG_CLKSEL_HFRC_DIV256) )
85     {
86         PWRCTRL->MISC_b.FORCEMEMVRLPTIMERS = 0;
87     }
88     else
89     {
90         PWRCTRL->MISC_b.FORCEMEMVRLPTIMERS = 1;
91     }
92 #endif
93 
94     return ui32CurrVal;
95 }
96 
97 //*****************************************************************************
98 //
99 // @brief Get the current stimer value.
100 //
101 // This function can be used to read, uninvasively, the value in the stimer.
102 //
103 // @return The 32-bit value from the STimer counter register.
104 //
105 //*****************************************************************************
106 uint32_t
am_hal_stimer_counter_get(void)107 am_hal_stimer_counter_get(void)
108 {
109     uint32_t ui32TmrAddr = AM_REGADDR(CTIMER, STTMR);
110     uint32_t ui32Values[3];
111     uint32_t ui32RetVal;
112 
113     //
114     // Read the register into ui32Values[].
115     //
116     am_hal_triple_read(ui32TmrAddr, ui32Values);
117 
118     //
119     // Now determine which of the three values is the correct value.
120     // If the first 2 match, then the values are both correct and we're done.
121     // Otherwise, the third value is taken to be the correct value.
122     //
123     if ( ui32Values[0] == ui32Values[1] )
124     {
125         //
126         // If the first two values match, then neither one was a bad read.
127         // We'll take this as the current time.
128         //
129         ui32RetVal = ui32Values[1];
130     }
131     else
132     {
133         ui32RetVal = ui32Values[2];
134     }
135     return ui32RetVal;
136 }
137 
138 //*****************************************************************************
139 //
140 // @brief Clear the stimer counter.
141 //
142 // This function clears the STimer Counter and leaves the stimer running.
143 //
144 //*****************************************************************************
145 void
am_hal_stimer_counter_clear(void)146 am_hal_stimer_counter_clear(void)
147 {
148     //
149     // Set the clear bit
150     //
151     CTIMER->STCFG |= CTIMER_STCFG_CLEAR_Msk;
152 
153     //
154     // Reset the clear bit
155     //
156     CTIMER->STCFG &= ~CTIMER_STCFG_CLEAR_Msk;
157 }
158 
159 //*****************************************************************************
160 //
161 // @brief Set the compare value.
162 //
163 // @param ui32CmprInstance is the compare register instance number (0-7).
164 // @param ui32Delta is the value to add to the STimer counter and load into
165 //        the comparator register.
166 //
167 // NOTE: There is no way to set an absolute value into a comparator register.
168 //       Only deltas added to the STimer counter can be written to the compare
169 //       registers.
170 //
171 //*****************************************************************************
172 void
am_hal_stimer_compare_delta_set(uint32_t ui32CmprInstance,uint32_t ui32Delta)173 am_hal_stimer_compare_delta_set(uint32_t ui32CmprInstance, uint32_t ui32Delta)
174 {
175     uint32_t cfgVal;
176     uint32_t numTries = 0;
177 
178     if ( ui32CmprInstance > 7 )
179     {
180         return;
181     }
182 
183     // We need to disable the compare temporarily while setting the delta value
184     // That leaves a corner case where we could miss the trigger if setting a very
185     // small delta. To avoid this, we take critical section, and we should ensure
186     // that delta value is at least > 1
187 
188     //
189     // Start a critical section.
190     //
191     AM_CRITICAL_BEGIN
192 
193     //
194     // Get current CFG value
195     //
196     cfgVal = CTIMER->STCFG;
197 
198     //
199     // Disable the compare if already enabled, when setting the new value
200     //
201     CTIMER->STCFG &= ~((AM_HAL_STIMER_CFG_COMPARE_A_ENABLE << ui32CmprInstance));
202 
203     // In rare case the delta might not be effective
204     // We retry if that is the case.
205     // Allow for some variability in the value owing to execution latency
206     while (numTries++ < 4)
207     {
208         uint32_t expVal;
209         uint32_t expMax;
210         uint32_t cmpVal;
211 
212         // Expected value
213         expVal = CTIMER->STTMR + ui32Delta;
214 
215         // Max allowed - taking care of latency
216         expMax = expVal + 10;
217 
218         //
219         // Set the delta
220         //
221         AM_REGVAL(AM_REG_STIMER_COMPARE(0, ui32CmprInstance)) = ui32Delta;
222 
223         // Read back the compare value
224         cmpVal = AM_REGVAL(AM_REG_STIMER_COMPARE(0, ui32CmprInstance));
225 
226         // Make sure the value is in expected range
227         if (!AM_HAL_U32_SMALLER(cmpVal, expVal) && !AM_HAL_U32_GREATER(cmpVal, expMax))
228         {
229             break;
230         }
231     }
232 
233 
234     //
235     // Restore Compare Enable bit
236     //
237     CTIMER->STCFG |= cfgVal & (AM_HAL_STIMER_CFG_COMPARE_A_ENABLE << ui32CmprInstance);
238 
239     //
240     // End the critical section.
241     //
242     AM_CRITICAL_END
243 }
244 
245 //*****************************************************************************
246 //
247 // @brief Get the current stimer compare register value.
248 //
249 // @param ui32CmprInstance is the compare register instance number (0-7).
250 //
251 // This function can be used to read the value in an stimer compare register.
252 //
253 //*****************************************************************************
254 uint32_t
am_hal_stimer_compare_get(uint32_t ui32CmprInstance)255 am_hal_stimer_compare_get(uint32_t ui32CmprInstance)
256 {
257     if ( ui32CmprInstance > 7 )
258     {
259         return 0;
260     }
261 
262     return AM_REGVAL(AM_REG_STIMER_COMPARE(0, ui32CmprInstance));
263 }
264 
265 //*****************************************************************************
266 //
267 // @brief Start capturing data with the specified capture register.
268 //
269 // @param ui32CaptureNum is the Capture Register Number to read (0-3).
270 // @param ui32GPIONumber is the pin number.
271 // @param bPolarity: false (0) = Capture on low to high transition.
272 //                   true  (1) = Capture on high to low transition.
273 //
274 // Use this function to start capturing.
275 //
276 //*****************************************************************************
277 void
am_hal_stimer_capture_start(uint32_t ui32CaptureNum,uint32_t ui32GPIONumber,bool bPolarity)278 am_hal_stimer_capture_start(uint32_t ui32CaptureNum,
279                             uint32_t ui32GPIONumber,
280                             bool bPolarity)
281 {
282     uint32_t ui32CapCtrl;
283 
284     if ( ui32GPIONumber > (AM_HAL_GPIO_MAX_PADS-1) )
285     {
286         return;
287     }
288 
289     //
290     // Set the polarity and pin selection in the GPIO block.
291     //
292     switch (ui32CaptureNum)
293     {
294          case 0:
295             GPIO->STMRCAP_b.STPOL0 = bPolarity;
296             GPIO->STMRCAP_b.STSEL0 = ui32GPIONumber;
297             ui32CapCtrl = CTIMER_CAPTURECONTROL_CAPTURE0_Msk;
298             break;
299          case 1:
300             GPIO->STMRCAP_b.STPOL1 = bPolarity;
301             GPIO->STMRCAP_b.STSEL1 = ui32GPIONumber;
302             ui32CapCtrl = CTIMER_CAPTURECONTROL_CAPTURE1_Msk;
303             break;
304          case 2:
305             GPIO->STMRCAP_b.STPOL2 = bPolarity;
306             GPIO->STMRCAP_b.STSEL2 = ui32GPIONumber;
307             ui32CapCtrl = CTIMER_CAPTURECONTROL_CAPTURE2_Msk;
308             break;
309          case 3:
310             GPIO->STMRCAP_b.STPOL3 = bPolarity;
311             GPIO->STMRCAP_b.STSEL3 = ui32GPIONumber;
312             ui32CapCtrl = CTIMER_CAPTURECONTROL_CAPTURE3_Msk;
313             break;
314          default:
315             return;     // error concealment.
316     }
317 
318     //
319     // Enable it in the CTIMER Block
320     //
321     CTIMER->CAPTURECONTROL |= ui32CapCtrl;
322 }
323 
324 //*****************************************************************************
325 //
326 // @brief Start capturing data with the specified capture register.
327 //
328 // @param ui32CaptureNum is the Capture Register Number to read.
329 //
330 // Use this function to start capturing.
331 //
332 //*****************************************************************************
am_hal_stimer_capture_stop(uint32_t ui32CaptureNum)333 void am_hal_stimer_capture_stop(uint32_t ui32CaptureNum)
334 {
335     //
336     // Disable it in the CTIMER block.
337     //
338     CTIMER->CAPTURECONTROL &=
339         ~(CTIMER_CAPTURECONTROL_CAPTURE0_Msk <<
340           ((CTIMER_CAPTURECONTROL_CAPTURE1_Pos -
341             CTIMER_CAPTURECONTROL_CAPTURE0_Pos) * ui32CaptureNum));
342 }
343 
344 //*****************************************************************************
345 //
346 // @brief Get the current stimer nvram register value.
347 //
348 // @param ui32NvramNum is the NVRAM Register Number to read.
349 // @param ui32NvramVal is the value to write to NVRAM.
350 //
351 // This function can be used to read the value in an stimer NVRAM register.
352 //
353 //*****************************************************************************
354 void
am_hal_stimer_nvram_set(uint32_t ui32NvramNum,uint32_t ui32NvramVal)355 am_hal_stimer_nvram_set(uint32_t ui32NvramNum, uint32_t ui32NvramVal)
356 {
357     if ( ui32NvramNum > 3 )
358     {
359         return;
360     }
361 
362     //AM_REGn(CTIMER, 0, SNVR)
363     //AM_REG_STIMER_NVRAM(0, ui32NvramNum) = ui32NvramVal;
364 }
365 
366 //*****************************************************************************
367 //
368 // @brief Get the current stimer nvram register value.
369 //
370 // @param ui32NvramNum is the NVRAM Register Number to read.
371 //
372 // This function can be used to read the value in an stimer NVRAM register.
373 //
374 // @return NVRAM Register Value
375 //
376 //
377 //*****************************************************************************
am_hal_stimer_nvram_get(uint32_t ui32NvramNum)378 uint32_t am_hal_stimer_nvram_get(uint32_t ui32NvramNum)
379 {
380     if ( ui32NvramNum > 3 )
381     {
382         return 0;
383     }
384 
385     return AM_REGVAL(AM_REG_STIMER_NVRAM(0, ui32NvramNum));
386 }
387 
388 //*****************************************************************************
389 //
390 // @brief Get the current stimer capture register value.
391 //
392 // @param ui32CaptureNum is the Capture Register Number to read.
393 //
394 // This function can be used to read the value in an stimer capture register.
395 //
396 // @return Stimer Capture Register Value.
397 //
398 //*****************************************************************************
am_hal_stimer_capture_get(uint32_t ui32CaptureNum)399 uint32_t am_hal_stimer_capture_get(uint32_t ui32CaptureNum)
400 {
401     if ( ui32CaptureNum > 3 )
402     {
403         return 0;
404     }
405 
406     return AM_REGVAL(AM_REG_STIMER_CAPTURE(0, ui32CaptureNum));
407 }
408 
409 //*****************************************************************************
410 //
411 // @brief Enables the selected system timer interrupt.
412 //
413 // @param ui32Interrupt is the interrupt to be used.
414 //
415 // This function will enable the selected interrupts in the STIMER interrupt
416 // enable register. In order to receive an interrupt from an stimer component,
417 // you will need to enable the interrupt for that component in this main
418 // register, as well as in the stimer configuration register (accessible though
419 // am_hal_stimer_config()), and in the NVIC.
420 //
421 // ui32Interrupt should be the logical OR of one or more of the following
422 // values:
423 //
424 //     AM_HAL_STIMER_INT_COMPAREA
425 //     AM_HAL_STIMER_INT_COMPAREB
426 //     AM_HAL_STIMER_INT_COMPAREC
427 //     AM_HAL_STIMER_INT_COMPARED
428 //     AM_HAL_STIMER_INT_COMPAREE
429 //     AM_HAL_STIMER_INT_COMPAREF
430 //     AM_HAL_STIMER_INT_COMPAREG
431 //     AM_HAL_STIMER_INT_COMPAREH
432 //
433 //     AM_HAL_STIMER_INT_OVERFLOW
434 //
435 //     AM_HAL_STIMER_INT_CAPTUREA
436 //     AM_HAL_STIMER_INT_CAPTUREB
437 //     AM_HAL_STIMER_INT_CAPTUREC
438 //     AM_HAL_STIMER_INT_CAPTURED
439 //
440 //*****************************************************************************
441 void
am_hal_stimer_int_enable(uint32_t ui32Interrupt)442 am_hal_stimer_int_enable(uint32_t ui32Interrupt)
443 {
444     //
445     // Enable the interrupt at the module level.
446     //
447     CTIMERn(0)->STMINTEN |= ui32Interrupt;
448 }
449 
450 //*****************************************************************************
451 //
452 // @brief Return the enabled stimer interrupts.
453 //
454 // This function will return all enabled interrupts in the STIMER
455 // interrupt enable register.
456 //
457 // @return return enabled interrupts. This will be a logical or of:
458 //
459 //     AM_HAL_STIMER_INT_COMPAREA
460 //     AM_HAL_STIMER_INT_COMPAREB
461 //     AM_HAL_STIMER_INT_COMPAREC
462 //     AM_HAL_STIMER_INT_COMPARED
463 //     AM_HAL_STIMER_INT_COMPAREE
464 //     AM_HAL_STIMER_INT_COMPAREF
465 //     AM_HAL_STIMER_INT_COMPAREG
466 //     AM_HAL_STIMER_INT_COMPAREH
467 //
468 //     AM_HAL_STIMER_INT_OVERFLOW
469 //
470 //     AM_HAL_STIMER_INT_CAPTUREA
471 //     AM_HAL_STIMER_INT_CAPTUREB
472 //     AM_HAL_STIMER_INT_CAPTUREC
473 //     AM_HAL_STIMER_INT_CAPTURED
474 //
475 // @return Return the enabled timer interrupts.
476 //
477 //*****************************************************************************
478 uint32_t
am_hal_stimer_int_enable_get(void)479 am_hal_stimer_int_enable_get(void)
480 {
481     //
482     // Return enabled interrupts.
483     //
484     return CTIMERn(0)->STMINTEN;
485 }
486 
487 //*****************************************************************************
488 //
489 // @brief Disables the selected stimer interrupt.
490 //
491 // @param ui32Interrupt is the interrupt to be used.
492 //
493 // This function will disable the selected interrupts in the STIMER
494 // interrupt register.
495 //
496 // ui32Interrupt should be the logical OR of one or more of the following
497 // values:
498 //
499 //     AM_HAL_STIMER_INT_COMPAREA
500 //     AM_HAL_STIMER_INT_COMPAREB
501 //     AM_HAL_STIMER_INT_COMPAREC
502 //     AM_HAL_STIMER_INT_COMPARED
503 //     AM_HAL_STIMER_INT_COMPAREE
504 //     AM_HAL_STIMER_INT_COMPAREF
505 //     AM_HAL_STIMER_INT_COMPAREG
506 //     AM_HAL_STIMER_INT_COMPAREH
507 //
508 //     AM_HAL_STIMER_INT_OVERFLOW
509 //
510 //     AM_HAL_STIMER_INT_CAPTUREA
511 //     AM_HAL_STIMER_INT_CAPTUREB
512 //     AM_HAL_STIMER_INT_CAPTUREC
513 //     AM_HAL_STIMER_INT_CAPTURED
514 //
515 //*****************************************************************************
516 void
am_hal_stimer_int_disable(uint32_t ui32Interrupt)517 am_hal_stimer_int_disable(uint32_t ui32Interrupt)
518 {
519     //
520     // Disable the interrupt at the module level.
521     //
522     CTIMERn(0)->STMINTEN &= ~ui32Interrupt;
523 }
524 
525 //*****************************************************************************
526 //
527 // @brief Sets the selected stimer interrupt.
528 //
529 // @param ui32Interrupt is the interrupt to be used.
530 //
531 // This function will set the selected interrupts in the STIMER
532 // interrupt register.
533 //
534 // ui32Interrupt should be the logical OR of one or more of the following
535 // values:
536 //
537 //     AM_HAL_STIMER_INT_COMPAREA
538 //     AM_HAL_STIMER_INT_COMPAREB
539 //     AM_HAL_STIMER_INT_COMPAREC
540 //     AM_HAL_STIMER_INT_COMPARED
541 //     AM_HAL_STIMER_INT_COMPAREE
542 //     AM_HAL_STIMER_INT_COMPAREF
543 //     AM_HAL_STIMER_INT_COMPAREG
544 //     AM_HAL_STIMER_INT_COMPAREH
545 //
546 //     AM_HAL_STIMER_INT_OVERFLOW
547 //
548 //     AM_HAL_STIMER_INT_CAPTUREA
549 //     AM_HAL_STIMER_INT_CAPTUREB
550 //     AM_HAL_STIMER_INT_CAPTUREC
551 //     AM_HAL_STIMER_INT_CAPTURED
552 //
553 //*****************************************************************************
554 void
am_hal_stimer_int_set(uint32_t ui32Interrupt)555 am_hal_stimer_int_set(uint32_t ui32Interrupt)
556 {
557     //
558     // Set the interrupts.
559     //
560     CTIMERn(0)->STMINTSET = ui32Interrupt;
561 }
562 
563 //*****************************************************************************
564 //
565 // @brief Clears the selected stimer interrupt.
566 //
567 // @param ui32Interrupt is the interrupt to be used.
568 //
569 // This function will clear the selected interrupts in the STIMER
570 // interrupt register.
571 //
572 // ui32Interrupt should be the logical OR of one or more of the following
573 // values:
574 //
575 //     AM_HAL_STIMER_INT_COMPAREA
576 //     AM_HAL_STIMER_INT_COMPAREB
577 //     AM_HAL_STIMER_INT_COMPAREC
578 //     AM_HAL_STIMER_INT_COMPARED
579 //     AM_HAL_STIMER_INT_COMPAREE
580 //     AM_HAL_STIMER_INT_COMPAREF
581 //     AM_HAL_STIMER_INT_COMPAREG
582 //     AM_HAL_STIMER_INT_COMPAREH
583 //
584 //     AM_HAL_STIMER_INT_OVERFLOW
585 //
586 //     AM_HAL_STIMER_INT_CAPTUREA
587 //     AM_HAL_STIMER_INT_CAPTUREB
588 //     AM_HAL_STIMER_INT_CAPTUREC
589 //     AM_HAL_STIMER_INT_CAPTURED
590 //
591 //*****************************************************************************
592 void
am_hal_stimer_int_clear(uint32_t ui32Interrupt)593 am_hal_stimer_int_clear(uint32_t ui32Interrupt)
594 {
595     //
596     // Disable the interrupt at the module level.
597     //
598     CTIMERn(0)->STMINTCLR = ui32Interrupt;
599 }
600 
601 
602 //*****************************************************************************
603 //
604 // @brief Returns either the enabled or raw stimer interrupt status.
605 //
606 // This function will return the stimer interrupt status.
607 //
608 // @param bEnabledOnly if true returns the status of the enabled interrupts
609 // only.
610 //
611 // The return value will be the logical OR of one or more of the following
612 // values:
613 //
614 // @return Returns the stimer interrupt status.
615 //
616 //*****************************************************************************
617 uint32_t
am_hal_stimer_int_status_get(bool bEnabledOnly)618 am_hal_stimer_int_status_get(bool bEnabledOnly)
619 {
620     //
621     // Return the desired status.
622     //
623     uint32_t ui32RetVal = CTIMERn(0)->STMINTSTAT;
624 
625     if ( bEnabledOnly )
626     {
627         ui32RetVal &= CTIMERn(0)->STMINTEN;
628     }
629 
630     return ui32RetVal;
631 }
632 
633 //*****************************************************************************
634 //
635 // End Doxygen group.
636 //! @}
637 //
638 //*****************************************************************************
639