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