1 // ****************************************************************************
2 //
3 //! @file am_hal_clkgen.c
4 //!
5 //! @brief Functions for Interfacing with the CLKGEN.
6 //!
7 //! @addtogroup clkgen3p Clkgen - Clock Generator
8 //! @ingroup apollo3p_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 //
55 //  am_hal_clkgen_control()
56 //      Apply various specific commands/controls on the CLKGEN module.
57 //
58 // ****************************************************************************
59 uint32_t
am_hal_clkgen_control(am_hal_clkgen_control_e eControl,void * pArgs)60 am_hal_clkgen_control(am_hal_clkgen_control_e eControl, void *pArgs)
61 {
62     uint32_t ui32Regval;
63 
64     //
65     // Take a snapshot of the reset status, if not done already
66     //
67     if (!gAmHalResetStatus)
68     {
69         gAmHalResetStatus = RSTGEN->STAT;
70     }
71 
72     switch ( eControl )
73     {
74         case AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX:
75             //
76             // Unlock the clock control register.
77             // Set the HFRC divisor to the required operating value.
78             // Lock the clock configuration registers.
79             //
80             CLKGEN->CLKKEY         = CLKGEN_CLKKEY_CLKKEY_Key;
81             CLKGEN->CCTRL          = CLKGEN_CCTRL_CORESEL_HFRC;
82             CLKGEN->CLKKEY         = 0;
83             break;
84 
85         case AM_HAL_CLKGEN_CONTROL_SYSCLK_DIV2:
86             CLKGEN->CLKKEY         = CLKGEN_CLKKEY_CLKKEY_Key;
87             CLKGEN->CCTRL          = CLKGEN_CCTRL_CORESEL_HFRC_DIV2;
88             CLKGEN->CLKKEY         = 0;
89             break;
90 
91         case AM_HAL_CLKGEN_CONTROL_LFRC_START:
92             CLKGEN->OCTRL_b.STOPRC = CLKGEN_OCTRL_STOPRC_EN;
93             break;
94 
95         case AM_HAL_CLKGEN_CONTROL_XTAL_START:
96             CLKGEN->OCTRL_b.STOPXT = CLKGEN_OCTRL_STOPXT_EN;
97             break;
98 
99         case AM_HAL_CLKGEN_CONTROL_LFRC_STOP:
100             CLKGEN->OCTRL_b.STOPRC = CLKGEN_OCTRL_STOPRC_STOP;
101             break;
102 
103         case AM_HAL_CLKGEN_CONTROL_XTAL_STOP:
104             // Software Workaround to guarantee proper function of HFADJ.
105             if (APOLLO3_GE_B0)
106             {
107                 MCUCTRL->XTALCTRL_b.XTALICOMPTRIM = 1;
108             }
109             CLKGEN->OCTRL_b.STOPXT = CLKGEN_OCTRL_STOPXT_STOP;
110             break;
111 
112         case AM_HAL_CLKGEN_CONTROL_RTC_SEL_LFRC:
113             CLKGEN->OCTRL_b.OSEL = CLKGEN_OCTRL_OSEL_RTC_LFRC;
114             break;
115 
116         case AM_HAL_CLKGEN_CONTROL_RTC_SEL_XTAL:
117             CLKGEN->OCTRL_b.OSEL = CLKGEN_OCTRL_OSEL_RTC_XT;
118             break;
119 
120         case AM_HAL_CLKGEN_CONTROL_HFADJ_ENABLE:
121             // Software Workaround to guarantee proper function of HFADJ.
122             if (APOLLO3_GE_B0)
123             {
124                 MCUCTRL->XTALCTRL_b.XTALICOMPTRIM = 3;
125                 am_hal_flash_delay(FLASH_CYCLES_US(1000));
126             }
127             if ( pArgs == 0 )
128             {
129                 ui32Regval =
130                     _VAL2FLD(CLKGEN_HFADJ_HFADJGAIN, CLKGEN_HFADJ_HFADJGAIN_Gain_of_1_in_2) |   /* Default value (Apollo3) */
131                     _VAL2FLD(CLKGEN_HFADJ_HFWARMUP, CLKGEN_HFADJ_HFWARMUP_1SEC)             |   /* Default value */
132                     _VAL2FLD(CLKGEN_HFADJ_HFXTADJ, 0x5B8)                                   |   /* Default value */
133                     _VAL2FLD(CLKGEN_HFADJ_HFADJCK, CLKGEN_HFADJ_HFADJCK_4SEC)               |   /* Default value */
134                     _VAL2FLD(CLKGEN_HFADJ_HFADJEN, CLKGEN_HFADJ_HFADJEN_EN);
135             }
136             else
137             {
138                 ui32Regval = *(uint32_t*)pArgs;
139             }
140 
141             //
142             // Make sure the ENABLE bit is set.
143             //
144             ui32Regval |= _VAL2FLD(CLKGEN_HFADJ_HFADJEN, CLKGEN_HFADJ_HFADJEN_EN);
145             CLKGEN->HFADJ = ui32Regval;
146             break;
147 
148         case AM_HAL_CLKGEN_CONTROL_HFADJ_DISABLE:
149             CLKGEN->HFADJ_b.HFADJEN = 0;
150             break;
151 
152         default:
153             return AM_HAL_STATUS_INVALID_ARG;
154     }
155 
156     //
157     // Return success status.
158     //
159     return AM_HAL_STATUS_SUCCESS;
160 
161 } // am_hal_clkgen_control()
162 
163 // ****************************************************************************
164 //
165 //  am_hal_clkgen_status_get()
166 //  This function returns the current value of various CLKGEN statuses.
167 //
168 // ****************************************************************************
169 uint32_t
am_hal_clkgen_status_get(am_hal_clkgen_status_t * psStatus)170 am_hal_clkgen_status_get(am_hal_clkgen_status_t *psStatus)
171 {
172     uint32_t ui32Status;
173 
174     if ( psStatus == NULL )
175     {
176         return AM_HAL_STATUS_INVALID_ARG;
177     }
178 
179     psStatus->ui32SysclkFreq =
180         CLKGEN->CCTRL_b.CORESEL                     ?
181             AM_HAL_CLKGEN_FREQ_MAX_HZ / 2           :
182             AM_HAL_CLKGEN_FREQ_MAX_HZ;
183 
184     ui32Status = CLKGEN->STATUS;
185 
186     psStatus->eRTCOSC =
187         _FLD2VAL(CLKGEN_STATUS_OMODE, ui32Status)   ?
188             AM_HAL_CLKGEN_STATUS_RTCOSC_LFRC        :
189             AM_HAL_CLKGEN_STATUS_RTCOSC_XTAL;
190 
191     psStatus->bXtalFailure =
192         _FLD2VAL(CLKGEN_STATUS_OSCF, ui32Status);
193 
194     return AM_HAL_STATUS_SUCCESS;
195 
196 } // am_hal_clkgen_status_get()
197 
198 // ****************************************************************************
199 //
200 //  am_hal_clkgen_clkout_enable()
201 //  This function is used to select and enable CLKOUT.
202 //
203 // ****************************************************************************
204 uint32_t
am_hal_clkgen_clkout_enable(bool bEnable,am_hal_clkgen_clkout_e eClkSelect)205 am_hal_clkgen_clkout_enable(bool bEnable, am_hal_clkgen_clkout_e eClkSelect)
206 {
207     if ( !bEnable )
208     {
209         CLKGEN->CLKOUT_b.CKEN = 0;
210     }
211 
212     //
213     // Do a basic validation of the eClkSelect parameter.
214     // Not every value in the range is valid, but at least this simple check
215     //  provides a reasonable chance that the parameter is valid.
216     //
217     if ( eClkSelect <= (am_hal_clkgen_clkout_e)CLKGEN_CLKOUT_CKSEL_LFRCNE )
218     {
219         //
220         // Are we actually changing the frequency?
221         //
222         if ( CLKGEN->CLKOUT_b.CKSEL != eClkSelect )
223         {
224             //
225             // Disable before changing the clock
226             //
227             CLKGEN->CLKOUT_b.CKEN = 0;
228 
229             //
230             // Set the new clock select
231             //
232             CLKGEN->CLKOUT_b.CKSEL = eClkSelect;
233         }
234 
235         //
236         // Enable/disable as requested.
237         //
238         CLKGEN->CLKOUT_b.CKEN = bEnable;
239     }
240     else
241     {
242         return AM_HAL_STATUS_INVALID_ARG;
243     }
244 
245     //
246     // Return success status.
247     //
248     return AM_HAL_STATUS_SUCCESS;
249 
250 } // am_hal_clkgen_clkout_enable()
251 
252 // ****************************************************************************
253 //
254 //  am_hal_clkgen_interrupt_enable()
255 //  Enable selected CLKGEN Interrupts.
256 //
257 // ****************************************************************************
am_hal_clkgen_interrupt_enable(am_hal_clkgen_interrupt_e ui32IntMask)258 uint32_t am_hal_clkgen_interrupt_enable(am_hal_clkgen_interrupt_e ui32IntMask)
259 {
260     if ( (ui32IntMask &
261             (CLKGEN_INTRPTEN_OF_Msk         |
262              CLKGEN_INTRPTEN_ACC_Msk        |
263              CLKGEN_INTRPTEN_ACF_Msk)) == 0 )
264     {
265         return AM_HAL_STATUS_INVALID_ARG;
266     }
267 
268     //
269     // Set the interrupt enables according to the mask.
270     //
271     CLKGEN->INTRPTEN |= ui32IntMask;
272 
273     //
274     // Return success status.
275     //
276     return AM_HAL_STATUS_SUCCESS;
277 
278 } // am_hal_clkgen_interrupt_enable()
279 
280 // ****************************************************************************
281 //
282 //  am_hal_clkgen_interrupt_disable(
283 //  Disable selected CLKGEN Interrupts.
284 //
285 // ****************************************************************************
286 uint32_t
am_hal_clkgen_interrupt_disable(am_hal_clkgen_interrupt_e ui32IntMask)287 am_hal_clkgen_interrupt_disable(am_hal_clkgen_interrupt_e ui32IntMask)
288 {
289     if ( (ui32IntMask &
290             (CLKGEN_INTRPTEN_OF_Msk         |
291              CLKGEN_INTRPTEN_ACC_Msk        |
292              CLKGEN_INTRPTEN_ACF_Msk)) == 0 )
293     {
294         return AM_HAL_STATUS_INVALID_ARG;
295     }
296 
297     //
298     // Disable the interrupts.
299     //
300     CLKGEN->INTRPTEN &= ~ui32IntMask;
301 
302     //
303     // Return success status.
304     //
305     return AM_HAL_STATUS_SUCCESS;
306 
307 } // am_hal_clkgen_interrupt_disable()
308 
309 //*****************************************************************************
310 //
311 //  am_hal_clkgen_interrupt_clear()
312 //  IOM interrupt clear
313 //
314 //*****************************************************************************
315 uint32_t
am_hal_clkgen_interrupt_clear(am_hal_clkgen_interrupt_e ui32IntMask)316 am_hal_clkgen_interrupt_clear(am_hal_clkgen_interrupt_e ui32IntMask)
317 {
318     if ( (ui32IntMask &
319             (CLKGEN_INTRPTEN_OF_Msk         |
320              CLKGEN_INTRPTEN_ACC_Msk        |
321              CLKGEN_INTRPTEN_ACF_Msk)) == 0 )
322     {
323         return AM_HAL_STATUS_INVALID_ARG;
324     }
325 
326     //
327     // Clear the requested interrupts.
328     //
329     CLKGEN->INTRPTCLR = ui32IntMask;
330 
331     //
332     // Return success status.
333     //
334     return AM_HAL_STATUS_SUCCESS;
335 
336 } // am_hal_clkgen_interrupt_clear()
337 
338 // ****************************************************************************
339 //
340 //  am_hal_clkgen_interrupt_status_get()
341 //  Return CLKGEN interrupts.
342 //
343 // ****************************************************************************
344 uint32_t
am_hal_clkgen_interrupt_status_get(bool bEnabledOnly,uint32_t * pui32IntStatus)345 am_hal_clkgen_interrupt_status_get(bool bEnabledOnly,
346                                    uint32_t *pui32IntStatus)
347 {
348     uint32_t ui32IntStatus;
349 
350     if ( !pui32IntStatus )
351     {
352         return AM_HAL_STATUS_INVALID_ARG;
353     }
354 
355     ui32IntStatus = CLKGEN->INTRPTSTAT;
356 
357     if ( bEnabledOnly )
358     {
359         ui32IntStatus &= CLKGEN->INTRPTEN;
360     }
361 
362     *pui32IntStatus = ui32IntStatus;
363 
364     //
365     // Return success status.
366     //
367     return AM_HAL_STATUS_SUCCESS;
368 
369 } // am_hal_clkgen_interrupt_status_get)
370 
371 // ****************************************************************************
372 //
373 // This function sets the CLKGEN interrupts.
374 //
375 // ****************************************************************************
376 uint32_t
am_hal_clkgen_interrupt_set(am_hal_clkgen_interrupt_e ui32IntMask)377 am_hal_clkgen_interrupt_set(am_hal_clkgen_interrupt_e ui32IntMask)
378 {
379     if ( (ui32IntMask &
380             (CLKGEN_INTRPTEN_OF_Msk         |
381              CLKGEN_INTRPTEN_ACC_Msk        |
382              CLKGEN_INTRPTEN_ACF_Msk)) == 0 )
383     {
384         return AM_HAL_STATUS_INVALID_ARG;
385     }
386 
387     //
388     // Set the interrupt status.
389     //
390     CLKGEN->INTRPTSET = ui32IntMask;
391 
392     //
393     // Return success status.
394     //
395     return AM_HAL_STATUS_SUCCESS;
396 
397 } // am_hal_clkgen_interrupt_set()
398 
399 
400 
401 //*****************************************************************************
402 //
403 // End Doxygen group.
404 //! @}
405 //
406 //*****************************************************************************
407