1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016 - 2019 , 2022 NXP
4  * All rights reserved.
5  *
6  *
7  * SPDX-License-Identifier: BSD-3-Clause
8  */
9 
10 #include "fsl_clock.h"
11 
12 /*******************************************************************************
13  * Definitions
14  ******************************************************************************/
15 /* Component ID definition, used by tools. */
16 #ifndef FSL_COMPONENT_ID
17 #define FSL_COMPONENT_ID "platform.drivers.clock"
18 #endif
19 
20 #define ICS_C2_BDIV_VAL  ((ICS->C2 & ICS_C2_BDIV_MASK) >> ICS_C2_BDIV_SHIFT)
21 #define ICS_S_CLKST_VAL  ((ICS->S & ICS_S_CLKST_MASK) >> ICS_S_CLKST_SHIFT)
22 #define ICS_S_IREFST_VAL ((ICS->S & ICS_S_IREFST_MASK) >> ICS_S_IREFST_SHIFT)
23 #define ICS_C1_RDIV_VAL  ((ICS->C1 & ICS_C1_RDIV_MASK) >> ICS_C1_RDIV_SHIFT)
24 #define OSC_CR_RANGE_VAL ((OSC0->CR & OSC_CR_RANGE_MASK) >> OSC_CR_RANGE_SHIFT)
25 #define OSC_MODE_MASK \
26     (OSC_CR_OSCOS_MASK | OSC_CR_HGO_MASK | OSC_CR_RANGE_MASK | OSC_CR_OSCEN_MASK | OSC_CR_OSCSTEN_MASK)
27 #define ICS_C2_LP_VAL  ((ICS->C2 & ICS_C2_LP_MASK) >> ICS_C2_LP_SHIFT)
28 #define SIM_BUSDIV_VAL ((SIM->BUSDIV & SIM_BUSDIV_BUSDIV_MASK) >> SIM_BUSDIV_BUSDIV_SHIFT)
29 
30 /* ICS_S_CLKST definition. */
31 enum
32 {
33     kICS_ClkOutStatFll, /* FLL.            */
34     kICS_ClkOutStatInt, /* Internal clock. */
35     kICS_ClkOutStatExt, /* External clock. */
36 };
37 
38 /* ICS fll clock factor. */
39 #define ICS_FLL_CLOCK_FACTOR (1024U)
40 
41 /*******************************************************************************
42  * Variables
43  ******************************************************************************/
44 
45 /* Slow internal reference clock frequency. */
46 static uint32_t s_slowIrcFreq = 32000U;
47 
48 /* External XTAL0 (OSC0) clock frequency. */
49 volatile uint32_t g_xtal0Freq;
50 
51 /*******************************************************************************
52  * Prototypes
53  ******************************************************************************/
54 
55 /*!
56  * @brief Get the ICS external reference clock frequency.
57  *
58  * Get the current ICS external reference clock frequency in Hz.This is an internal function.
59  *
60  * @return ICS external reference clock frequency in Hz.
61  */
62 static uint32_t CLOCK_GetICSExtClkFreq(void);
63 
64 /*!
65  * @brief Get the ICS FLL external reference clock frequency.
66  *
67  * Get the current ICS FLL external reference clock frequency in Hz. It is
68  * the frequency after by ICS_C1[RDIV]. This is an internal function.
69  *
70  * @return ICS FLL external reference clock frequency in Hz.
71  */
72 static uint32_t CLOCK_GetFllExtRefClkFreq(void);
73 
74 /*!
75  * @brief Get the ICS FLL reference clock frequency.
76  *
77  * Get the current ICS FLL reference clock frequency in Hz. It is
78  * the frequency select by ICS_C1[IREFS]. This is an internal function.
79  *
80  * @return ICS FLL reference clock frequency in Hz.
81  */
82 static uint32_t CLOCK_GetFllRefClkFreq(void);
83 
84 /*!
85  * @brief Calculate the RANGE value base on crystal frequency.
86  *
87  * To setup external crystal oscillator, must set the register bits RANGE
88  * base on the crystal frequency. This function returns the RANGE base on the
89  * input frequency. This is an internal function.
90  *
91  * @param freq Crystal frequency in Hz.
92  * @return The RANGE value.
93  */
94 static uint8_t CLOCK_GetOscRangeFromFreq(uint32_t freq);
95 
96 /*******************************************************************************
97  * Code
98  ******************************************************************************/
99 
CLOCK_GetICSExtClkFreq(void)100 static uint32_t CLOCK_GetICSExtClkFreq(void)
101 {
102     /* Please call CLOCK_SetXtal0Freq base on board setting before using OSC0 clock. */
103     assert(g_xtal0Freq);
104     return g_xtal0Freq;
105 }
106 
CLOCK_GetFllExtRefClkFreq(void)107 static uint32_t CLOCK_GetFllExtRefClkFreq(void)
108 {
109     /* FllExtRef = ICSExtRef / FllExtRefDiv */
110     uint8_t rDiv;
111     uint8_t range;
112 
113     uint32_t freq = CLOCK_GetICSExtClkFreq();
114 
115     if (freq == 0UL)
116     {
117         return freq;
118     }
119     /* get reference clock divider */
120     rDiv = ICS_C1_RDIV_VAL;
121 
122     freq >>= rDiv;
123     /* OSC clock range */
124     range = OSC_CR_RANGE_VAL;
125 
126     /*
127        When should use divider 32, 64, 128, 256, 512, 1024.
128     */
129     if (((0U != range)))
130     {
131         switch (rDiv)
132         {
133             case 0:
134             case 1:
135             case 2:
136             case 3:
137             case 4:
138             case 5:
139                 freq >>= 5u;
140                 break;
141             case 6:
142             case 7:
143                 break;
144             default:
145                 freq = 0u;
146                 break;
147         }
148     }
149 
150     return freq;
151 }
152 
CLOCK_GetFllRefClkFreq(void)153 static uint32_t CLOCK_GetFllRefClkFreq(void)
154 {
155     uint32_t freq;
156 
157     /* If use external reference clock. */
158     if ((uint8_t)kICS_FllSrcExternal == ICS_S_IREFST_VAL)
159     {
160         freq = CLOCK_GetFllExtRefClkFreq();
161     }
162     /* If use internal reference clock. */
163     else
164     {
165         freq = s_slowIrcFreq;
166     }
167 
168     return freq;
169 }
170 
CLOCK_GetOscRangeFromFreq(uint32_t freq)171 static uint8_t CLOCK_GetOscRangeFromFreq(uint32_t freq)
172 {
173     assert((freq <= 39063U) || (freq >= 4000000U));
174 
175     uint8_t range = 0U;
176 
177     if (freq <= 39063U)
178     {
179         range = 0U;
180     }
181     /* high freq range 4M-20M */
182     else
183     {
184         range = 1U;
185     }
186 
187     return range;
188 }
189 
190 /*!
191  * brief Get the OSC0 external reference clock frequency (OSC0ERCLK).
192  *
193  * return Clock frequency in Hz.
194  */
CLOCK_GetOsc0ErClkFreq(void)195 uint32_t CLOCK_GetOsc0ErClkFreq(void)
196 {
197     uint32_t freq;
198 
199     if ((OSC0->CR & OSC_CR_OSCEN_MASK) != 0U)
200     {
201         /* Please call CLOCK_SetXtal0Freq base on board setting before using OSC0 clock. */
202         assert(g_xtal0Freq);
203         freq = g_xtal0Freq;
204     }
205     else
206     {
207         freq = 0U;
208     }
209 
210     return freq;
211 }
212 
213 /*!
214  * brief Get the flash clock frequency.
215  *
216  * return Clock frequency in Hz.
217  */
CLOCK_GetFlashClkFreq(void)218 uint32_t CLOCK_GetFlashClkFreq(void)
219 {
220     uint32_t freq;
221 
222     freq = CLOCK_GetICSOutClkFreq() / (SIM_BUSDIV_VAL + 1U);
223 
224     return freq;
225 }
226 
227 /*!
228  * brief Get the bus clock frequency.
229  *
230  * return Clock frequency in Hz.
231  */
CLOCK_GetBusClkFreq(void)232 uint32_t CLOCK_GetBusClkFreq(void)
233 {
234     return CLOCK_GetFlashClkFreq();
235 }
236 
237 /*!
238  * brief Get the core clock or system clock frequency.
239  *
240  * return Clock frequency in Hz.
241  */
CLOCK_GetCoreSysClkFreq(void)242 uint32_t CLOCK_GetCoreSysClkFreq(void)
243 {
244     return CLOCK_GetICSOutClkFreq();
245 }
246 
247 /*!
248  * brief Gets the clock frequency for a specific clock name.
249  *
250  * This function checks the current clock configurations and then calculates
251  * the clock frequency for a specific clock name defined in clock_name_t.
252  * The ICS must be properly configured before using this function.
253  *
254  * param clockName Clock names defined in clock_name_t
255  * return Clock frequency value in Hertz
256  */
CLOCK_GetFreq(clock_name_t clockName)257 uint32_t CLOCK_GetFreq(clock_name_t clockName)
258 {
259     uint32_t freq;
260 
261     switch (clockName)
262     {
263         case kCLOCK_CoreSysClk:
264         case kCLOCK_PlatClk:
265             freq = CLOCK_GetCoreSysClkFreq();
266             break;
267 
268         case kCLOCK_BusClk:
269         case kCLOCK_FlashClk:
270             freq = CLOCK_GetFlashClkFreq();
271             break;
272 
273         case kCLOCK_Osc0ErClk:
274             freq = CLOCK_GetOsc0ErClkFreq();
275             break;
276 
277         case kCLOCK_ICSInternalRefClk:
278             freq = CLOCK_GetInternalRefClkFreq();
279             break;
280         case kCLOCK_ICSFixedFreqClk:
281             freq = CLOCK_GetICSFixedFreqClkFreq();
282             break;
283         case kCLOCK_ICSFllClk:
284             freq = CLOCK_GetFllFreq();
285             break;
286         case kCLOCK_ICSOutClk:
287             freq = CLOCK_GetICSOutClkFreq();
288             break;
289 
290         case kCLOCK_LpoClk:
291             freq = LPO_CLK_FREQ;
292             break;
293         default:
294             freq = 0U;
295             break;
296     }
297 
298     return freq;
299 }
300 
301 /*!
302  * brief Set the clock configure in SIM module.
303  *
304  * This function sets system layer clock settings in SIM module.
305  *
306  * param config Pointer to the configure structure.
307  */
CLOCK_SetSimConfig(sim_clock_config_t const * config)308 void CLOCK_SetSimConfig(sim_clock_config_t const *config)
309 {
310     /* config divider */
311     SIM->BUSDIV = config->busDiv;
312     /* config bus clock prescaler optional */
313     SIM->SOPT |= SIM_SOPT_BUSREF(config->busClkPrescaler);
314 }
315 
316 /*!
317  * brief Gets the ICS output clock (ICSOUTCLK) frequency.
318  *
319  * This function gets the ICS output clock frequency in Hz based on the current ICS
320  * register value.
321  *
322  * return The frequency of ICSOUTCLK.
323  */
CLOCK_GetICSOutClkFreq(void)324 uint32_t CLOCK_GetICSOutClkFreq(void)
325 {
326     uint32_t icsoutclk;
327     uint8_t clkst = ICS_S_CLKST_VAL;
328 
329     switch (clkst)
330     {
331         case kICS_ClkOutStatFll:
332             icsoutclk = CLOCK_GetFllFreq();
333             break;
334         case kICS_ClkOutStatInt:
335             icsoutclk = s_slowIrcFreq;
336             break;
337         case kICS_ClkOutStatExt:
338             icsoutclk = CLOCK_GetICSExtClkFreq();
339             break;
340         default:
341             icsoutclk = 0U;
342             break;
343     }
344 
345     return (icsoutclk / (1UL << ICS_C2_BDIV_VAL));
346 }
347 
348 /*!
349  * brief Gets the ICS FLL clock (ICSFLLCLK) frequency.
350  *
351  * This function gets the ICS FLL clock frequency in Hz based on the current ICS
352  * register value. The FLL is enabled in FEI/FBI/FEE/FBE mode and
353  * disabled in low power state in other modes.
354  *
355  * return The frequency of ICSFLLCLK.
356  */
CLOCK_GetFllFreq(void)357 uint32_t CLOCK_GetFllFreq(void)
358 {
359     uint32_t freq;
360 
361     /* If FLL is not enabled currently, then return 0U. */
362     if ((ICS->C2 & ICS_C2_LP_MASK) != 0U)
363     {
364         freq = 0U;
365     }
366     else
367     {
368         /* Get FLL reference clock frequency. */
369         freq = CLOCK_GetFllRefClkFreq() * ICS_FLL_CLOCK_FACTOR;
370     }
371 
372     return freq;
373 }
374 
375 /*!
376  * brief Gets the ICS internal reference clock (ICSIRCLK) frequency.
377  *
378  * This function gets the ICS internal reference clock frequency in Hz based
379  * on the current ICS register value.
380  *
381  * return The frequency of ICSIRCLK.
382  */
CLOCK_GetInternalRefClkFreq(void)383 uint32_t CLOCK_GetInternalRefClkFreq(void)
384 {
385     uint32_t freq;
386 
387     /* If ICSIRCLK is gated. */
388     if ((ICS->C1 & ICS_C1_IRCLKEN_MASK) == 0U)
389     {
390         freq = 0U;
391     }
392     else
393     {
394         freq = s_slowIrcFreq;
395     }
396 
397     return freq;
398 }
399 
400 /*!
401  * brief Gets the ICS fixed frequency clock (ICSFFCLK) frequency.
402  *
403  * This function gets the ICS fixed frequency clock frequency in Hz based
404  * on the current ICS register value.
405  *
406  * return The frequency of ICSFFCLK.
407  */
CLOCK_GetICSFixedFreqClkFreq(void)408 uint32_t CLOCK_GetICSFixedFreqClkFreq(void)
409 {
410     uint32_t freq = CLOCK_GetFllRefClkFreq();
411     uint32_t ret;
412     uint32_t ICSOUTCLK;
413 
414     ICSOUTCLK = CLOCK_GetICSOutClkFreq();
415     /* ICSFFCLK must be no more than ICSOUTCLK/4. */
416     if ((freq != 0UL) && (freq <= (ICSOUTCLK / 4U)))
417     {
418         ret = freq;
419     }
420     else
421     {
422         ret = 0U;
423     }
424 
425     return ret;
426 }
427 
428 /*!
429  * brief Initializes the OSC0.
430  *
431  * This function initializes the OSC0 according to the board configuration.
432  *
433  * param  config Pointer to the OSC0 configuration structure.
434  */
CLOCK_InitOsc0(osc_config_t const * config)435 void CLOCK_InitOsc0(osc_config_t const *config)
436 {
437     uint8_t range = CLOCK_GetOscRangeFromFreq(config->freq);
438 
439     OSC0->CR = ((OSC0->CR & (uint8_t)(~OSC_MODE_MASK)) | (uint8_t)(OSC_CR_RANGE(range)) | ((uint8_t)config->workMode) |
440                 ((uint8_t)config->enableMode));
441 
442     if (((uint8_t)kOSC_ModeExt != config->workMode) && ((OSC0->CR & OSC_CR_OSCEN_MASK) != 0U))
443     {
444         /* Wait for stable. */
445         while (0U == (OSC0->CR & OSC_CR_OSCINIT_MASK))
446         {
447         }
448     }
449 }
450 
451 /*!
452  * brief Deinitializes the OSC0.
453  *
454  * This function deinitializes the OSC0.
455  */
CLOCK_DeinitOsc0(void)456 void CLOCK_DeinitOsc0(void)
457 {
458     OSC0->CR = 0U;
459 }
460 
461 /*!
462  * brief Gets the current ICS mode.
463  *
464  * This function checks the ICS registers and determines the current ICS mode.
465  *
466  * return Current ICS mode or error code; See ref ics_mode_t.
467  */
CLOCK_GetMode(void)468 ics_mode_t CLOCK_GetMode(void)
469 {
470     ics_mode_t mode = kICS_ModeError;
471     uint8_t clkst   = ICS_S_CLKST_VAL;
472     uint8_t irefst  = ICS_S_IREFST_VAL;
473     uint8_t lp      = ICS_C2_LP_VAL;
474 
475     /*------------------------------------------------------------------
476                            Mode and Registers
477     ____________________________________________________________________
478 
479       Mode   |   CLKST    |   IREFST   |      LP
480     ____________________________________________________________________
481 
482       FEI    |  00(FLL)   |   1(INT)   |      X
483     ____________________________________________________________________
484 
485       FEE    |  00(FLL)   |   0(EXT)   |      X
486     ____________________________________________________________________
487 
488       FBE    |  10(EXT)   |   0(EXT)   |   0(NORMAL)
489     ____________________________________________________________________
490 
491       FBI    |  01(INT)   |   1(INT)   |   0(NORMAL)
492     ____________________________________________________________________
493 
494       FBILP   |  01(INT)   |   1(INT)   |   1(LOW POWER)
495     ____________________________________________________________________
496 
497       FBELP   |  10(EXT)   |   0(EXT)   |   1(LOW POWER)
498     ____________________________________________________________________
499 
500     ----------------------------------------------------------------------*/
501 
502     switch (clkst)
503     {
504         case kICS_ClkOutStatFll:
505             if ((uint8_t)kICS_FllSrcExternal == irefst)
506             {
507                 mode = kICS_ModeFEE;
508             }
509             else
510             {
511                 mode = kICS_ModeFEI;
512             }
513             break;
514         case kICS_ClkOutStatInt:
515             if (lp != 0U)
516             {
517                 mode = kICS_ModeBILP;
518             }
519             else
520             {
521                 mode = kICS_ModeFBI;
522             }
523             break;
524         case kICS_ClkOutStatExt:
525             if (lp != 0U)
526             {
527                 mode = kICS_ModeBELP;
528             }
529             else
530             {
531                 mode = kICS_ModeFBE;
532             }
533             break;
534         default:
535             mode = kICS_ModeError;
536             break;
537     }
538 
539     return mode;
540 }
541 
542 /*!
543  * brief Sets the ICS to FEI mode.
544  *
545  * This function sets the ICS to FEI mode. If setting to FEI mode fails
546  * from the current mode, this function returns an error.
547  *
548  * param       bDiv bus clock divider
549  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
550  * retval kStatus_Success Switched to the target mode successfully.
551  */
CLOCK_SetFeiMode(uint8_t bDiv)552 status_t CLOCK_SetFeiMode(uint8_t bDiv)
553 {
554 #if (defined(ICS_CONFIG_CHECK_PARAM) && ICS_CONFIG_CHECK_PARAM)
555     ics_mode_t mode = CLOCK_GetMode();
556     if (!((kICS_ModeFEI == mode) || (kICS_ModeFBI == mode) || (kICS_ModeFBE == mode) || (kICS_ModeFEE == mode)))
557     {
558         return kStatus_ICS_ModeUnreachable;
559     }
560 #endif
561 
562     /* Set IREFS. */
563     ICS->C1 = (uint8_t)((ICS->C1 & ~(ICS_C1_IREFS_MASK)) | ICS_C1_IREFS(kICS_FllSrcInternal)); /* IREFS = 1 */
564 
565     /* Set CLKS */
566     ICS->C1 = (uint8_t)((ICS->C1 & (~ICS_C1_CLKS_MASK)) | ICS_C1_CLKS(kICS_ClkOutSrcFll)); /* CLKS = 0 */
567     /* set bus clock divider */
568     ICS->C2 = (uint8_t)((ICS->C2 & (~ICS_C2_BDIV_MASK)) | ICS_C2_BDIV(bDiv));
569 
570     /* Wait and check status. */
571     while ((uint8_t)kICS_FllSrcInternal != ICS_S_IREFST_VAL)
572     {
573     }
574     /* Check ICS_S[CLKST] */
575     while ((uint8_t)kICS_ClkOutStatFll != ICS_S_CLKST_VAL)
576     {
577     }
578 
579     /* wait for FLL to lock */
580     while (0U == (ICS->S & ICS_S_LOCK_MASK))
581     {
582     }
583 
584     /* clear Loss of lock sticky bit */
585     ICS->S |= ICS_S_LOLS_MASK;
586 
587     return kStatus_Success;
588 }
589 
590 /*!
591  * brief Sets the ICS to FEE mode.
592  *
593  * This function sets the ICS to FEE mode. If setting to FEE mode fails
594  * from the current mode, this function returns an error.
595  *
596  * param   bDiv bus clock divider
597  * param   rdiv  FLL reference clock divider setting, RDIV.
598  *
599  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
600  * retval kStatus_Success Switched to the target mode successfully.
601  */
CLOCK_SetFeeMode(uint8_t bDiv,uint8_t rDiv)602 status_t CLOCK_SetFeeMode(uint8_t bDiv, uint8_t rDiv)
603 {
604 #if (defined(ICS_CONFIG_CHECK_PARAM) && ICS_CONFIG_CHECK_PARAM)
605     ics_mode_t mode = CLOCK_GetMode();
606     if (!((kICS_ModeFEE == mode) || (kICS_ModeFBI == mode) || (kICS_ModeFBE == mode) || (kICS_ModeFEI == mode)))
607     {
608         return kStatus_ICS_ModeUnreachable;
609     }
610 #endif
611 
612     /* Set CLKS, rDiv and IREFS. */
613     ICS->C1 = (uint8_t)((ICS->C1 & ~(ICS_C1_CLKS_MASK | ICS_C1_RDIV_MASK | ICS_C1_IREFS_MASK)) |
614                         (ICS_C1_CLKS(kICS_ClkOutSrcFll)         /* CLKS = 0 */
615                          | ICS_C1_RDIV(rDiv)                    /* FRDIV */
616                          | ICS_C1_IREFS(kICS_FllSrcExternal))); /* IREFS = 0 */
617     /* set bus clock divider */
618     ICS->C2 = (uint8_t)((ICS->C2 & (~ICS_C2_BDIV_MASK)) | ICS_C2_BDIV(bDiv));
619 
620     /* If use external crystal as clock source, wait for it stable. */
621     {
622         if ((OSC0->CR & OSC_CR_OSCOS_MASK) != 0U)
623         {
624             while (0U == (OSC0->CR & OSC_CR_OSCINIT_MASK))
625             {
626             }
627         }
628     }
629 
630     /* Wait and check status. */
631     while ((uint8_t)kICS_FllSrcExternal != ICS_S_IREFST_VAL)
632     {
633     }
634 
635     /* Check ICS_S[CLKST] */
636     while ((uint8_t)kICS_ClkOutStatFll != ICS_S_CLKST_VAL)
637     {
638     }
639 
640     /* wait for FLL to lock */
641     while (0U == (ICS->S & ICS_S_LOCK_MASK))
642     {
643     }
644 
645     /* clear Loss of lock sticky bit */
646     ICS->S |= ICS_S_LOLS_MASK;
647 
648     return kStatus_Success;
649 }
650 
651 /*!
652  * brief Sets the ICS to FBI mode.
653  *
654  * This function sets the ICS to FBI mode. If setting to FBI mode fails
655  * from the current mode, this function returns an error.
656  *
657  * param bDiv bus clock divider
658  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
659  * retval kStatus_Success Switched to the target mode successfully.s
660  */
CLOCK_SetFbiMode(uint8_t bDiv)661 status_t CLOCK_SetFbiMode(uint8_t bDiv)
662 {
663 #if (defined(ICS_CONFIG_CHECK_PARAM) && ICS_CONFIG_CHECK_PARAM)
664     ics_mode_t mode = CLOCK_GetMode();
665 
666     if (!((kICS_ModeFEE == mode) || (kICS_ModeFBI == mode) || (kICS_ModeFBE == mode) || (kICS_ModeFEI == mode) ||
667           (kICS_ModeBILP == mode)))
668 
669     {
670         return kStatus_ICS_ModeUnreachable;
671     }
672 #endif
673 
674     /* set bus clock divider and disable low power */
675     ICS->C2 = (uint8_t)((ICS->C2 & (~(ICS_C2_BDIV_MASK | ICS_C2_LP_MASK))) | ICS_C2_BDIV(bDiv));
676     /* Set CLKS and IREFS. */
677     ICS->C1 = (uint8_t)((ICS->C1 & ~(ICS_C1_CLKS_MASK | ICS_C1_IREFS_MASK)) |
678                         (ICS_C1_CLKS(kICS_ClkOutSrcInternal)    /* CLKS = 1 */
679                          | ICS_C1_IREFS(kICS_FllSrcInternal))); /* IREFS = 1 */
680 
681     /* Wait and check status. */
682     while ((uint8_t)kICS_FllSrcInternal != ICS_S_IREFST_VAL)
683     {
684     }
685 
686     while ((uint8_t)kICS_ClkOutStatInt != ICS_S_CLKST_VAL)
687     {
688     }
689 
690     /* clear Loss of lock sticky bit */
691     ICS->S |= ICS_S_LOLS_MASK;
692 
693     return kStatus_Success;
694 }
695 
696 /*!
697  * brief Sets the ICS to FBE mode.
698  *
699  * This function sets the ICS to FBE mode. If setting to FBE mode fails
700  * from the current mode, this function returns an error.
701  *
702  * param   bDiv bus clock divider
703  * param   rdiv  FLL reference clock divider setting, RDIV.
704  *
705  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
706  * retval kStatus_Success Switched to the target mode successfully.
707  */
CLOCK_SetFbeMode(uint8_t bDiv,uint8_t rDiv)708 status_t CLOCK_SetFbeMode(uint8_t bDiv, uint8_t rDiv)
709 {
710 #if (defined(ICS_CONFIG_CHECK_PARAM) && ICS_CONFIG_CHECK_PARAM)
711     ics_mode_t mode = CLOCK_GetMode();
712     if (!((kICS_ModeFEE == mode) || (kICS_ModeFBI == mode) || (kICS_ModeFBE == mode) || (kICS_ModeFEI == mode) ||
713           (kICS_ModeBELP == mode)))
714     {
715         return kStatus_ICS_ModeUnreachable;
716     }
717 #endif
718 
719     /* set bus clock divider and disable low power */
720     ICS->C2 = (uint8_t)((ICS->C2 & (~(ICS_C2_BDIV_MASK | ICS_C2_LP_MASK))) | ICS_C2_BDIV(bDiv));
721 
722     /* Set CLKS and IREFS. */
723     ICS->C1 = (uint8_t)((ICS->C1 & ~(ICS_C1_CLKS_MASK | ICS_C1_RDIV_MASK | ICS_C1_IREFS_MASK)) |
724                         (ICS_C1_CLKS(kICS_ClkOutSrcExternal)    /* CLKS = 2 */
725                          | ICS_C1_RDIV(rDiv)                    /* FRDIV = frDiv */
726                          | ICS_C1_IREFS(kICS_FllSrcExternal))); /* IREFS = 0 */
727 
728     /* If use external crystal as clock source, wait for it stable. */
729     {
730         if ((OSC0->CR & OSC_CR_OSCOS_MASK) != 0U)
731         {
732             while (0U == (OSC0->CR & OSC_CR_OSCINIT_MASK))
733             {
734             }
735         }
736     }
737 
738     /* Wait for Reference clock Status bit to clear */
739     while ((uint8_t)kICS_FllSrcExternal != ICS_S_IREFST_VAL)
740     {
741     }
742 
743     /* Wait for clock status bits to show clock source is ext ref clk */
744     while ((uint8_t)kICS_ClkOutStatExt != ICS_S_CLKST_VAL)
745     {
746     }
747 
748     /* clear Loss of lock sticky bit */
749     ICS->S |= ICS_S_LOLS_MASK;
750 
751     return kStatus_Success;
752 }
753 
754 /*!
755  * brief Sets the ICS to BILP mode.
756  *
757  * This function sets the ICS to BILP mode. If setting to BILP mode fails
758  * from the current mode, this function returns an error.
759  *
760  * param   bDiv bus clock divider
761  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
762  * retval kStatus_Success Switched to the target mode successfully.
763  */
CLOCK_SetBilpMode(uint8_t bDiv)764 status_t CLOCK_SetBilpMode(uint8_t bDiv)
765 {
766 #if (defined(ICS_CONFIG_CHECK_PARAM) && ICS_CONFIG_CHECK_PARAM)
767     if (ICS_S_CLKST_VAL != kICS_ClkOutStatInt)
768     {
769         return kStatus_ICS_ModeUnreachable;
770     }
771 #endif /* ICS_CONFIG_CHECK_PARAM */
772 
773     /* set bus clock divider and enable low power */
774     ICS->C2 = (uint8_t)((ICS->C2 & (~ICS_C2_BDIV_MASK)) | ICS_C2_BDIV(bDiv) | ICS_C2_LP_MASK);
775 
776     return kStatus_Success;
777 }
778 
779 /*!
780  * brief Sets the ICS to BELP mode.
781  *
782  * This function sets the ICS to BELP mode. If setting to BELP mode fails
783  * from the current mode, this function returns an error.
784  *
785  * param   bDiv bus clock divider
786  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
787  * retval kStatus_Success Switched to the target mode successfully.
788  */
CLOCK_SetBelpMode(uint8_t bDiv)789 status_t CLOCK_SetBelpMode(uint8_t bDiv)
790 {
791 #if (defined(ICS_CONFIG_CHECK_PARAM) && ICS_CONFIG_CHECK_PARAM)
792     if (ICS_S_CLKST_VAL != kICS_ClkOutStatExt)
793     {
794         return kStatus_ICS_ModeUnreachable;
795     }
796 #endif
797 
798     /* set bus clock divider and enable low power */
799     ICS->C2 = (uint8_t)((ICS->C2 & (~ICS_C2_BDIV_MASK)) | ICS_C2_BDIV(bDiv) | ICS_C2_LP_MASK);
800 
801     return kStatus_Success;
802 }
803 
804 /*!
805  * brief Sets the ICS to FEI mode during system boot up.
806  *
807  * This function sets the ICS to FEI mode from the reset mode. It can also be used to
808  * set up ICS during system boot up.
809  *
810  * param  bDiv bus clock divider.
811  *
812  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
813  * retval kStatus_Success Switched to the target mode successfully.
814  */
CLOCK_BootToFeiMode(uint8_t bDiv)815 status_t CLOCK_BootToFeiMode(uint8_t bDiv)
816 {
817     return CLOCK_SetFeiMode(bDiv);
818 }
819 
820 /*!
821  * brief Sets the ICS to FEE mode during system bootup.
822  *
823  * This function sets ICS to FEE mode from the reset mode. It can also be used to
824  * set up the ICS during system boot up.
825  *
826  * param   bDiv bus clock divider.
827  * param   rdiv  FLL reference clock divider setting, RDIV.
828  *
829  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
830  * retval kStatus_Success Switched to the target mode successfully.
831  */
CLOCK_BootToFeeMode(uint8_t bDiv,uint8_t rDiv)832 status_t CLOCK_BootToFeeMode(uint8_t bDiv, uint8_t rDiv)
833 {
834     return CLOCK_SetFeeMode(bDiv, rDiv);
835 }
836 
837 /*!
838  * brief Sets the ICS to BILP mode during system boot up.
839  *
840  * This function sets the ICS to BILP mode from the reset mode. It can also be used to
841  * set up the ICS during system boot up.
842  *
843  * param   bDiv bus clock divider.
844  * retval kStatus_ICS_SourceUsed Could not change ICSIRCLK setting.
845  * retval kStatus_Success Switched to the target mode successfully.
846  */
CLOCK_BootToBilpMode(uint8_t bDiv)847 status_t CLOCK_BootToBilpMode(uint8_t bDiv)
848 {
849     /* If reset mode is not BILP, first enter FBI mode. */
850     ICS->C1 = (uint8_t)((ICS->C1 & ~ICS_C1_CLKS_MASK) | ICS_C1_CLKS(kICS_ClkOutSrcInternal));
851     while (ICS_S_CLKST_VAL != (uint8_t)kICS_ClkOutStatInt)
852     {
853     }
854 
855     /* set bus clock divider and enable low power */
856     ICS->C2 = (uint8_t)((ICS->C2 & (~ICS_C2_BDIV_MASK)) | ICS_C2_BDIV(bDiv) | ICS_C2_LP_MASK);
857 
858     return kStatus_Success;
859 }
860 
861 /*!
862  * brief Sets the ICS to BELP mode during system boot up.
863  *
864  * This function sets the ICS to BELP mode from the reset mode. It can also be used to
865  * set up the ICS during system boot up.
866  *
867  * param   bDiv bus clock divider.
868  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
869  * retval kStatus_Success Switched to the target mode successfully.
870  */
CLOCK_BootToBelpMode(uint8_t bDiv)871 status_t CLOCK_BootToBelpMode(uint8_t bDiv)
872 {
873     /* Set to FBE mode. */
874     ICS->C1 = (uint8_t)((ICS->C1 & ~(ICS_C1_CLKS_MASK | ICS_C1_IREFS_MASK)) |
875                         (ICS_C1_CLKS(kICS_ClkOutSrcExternal)    /* CLKS = 2 */
876                          | ICS_C1_IREFS(kICS_FllSrcExternal))); /* IREFS = 0 */
877 
878     /* If use external crystal as clock source, wait for it stable. */
879     {
880         if ((OSC0->CR & OSC_CR_OSCOS_MASK) != 0U)
881         {
882             while (0U == (OSC0->CR & OSC_CR_OSCINIT_MASK))
883             {
884             }
885         }
886     }
887 
888     /* Wait for ICS_S[CLKST] and ICS_S[IREFST]. */
889     while ((ICS->S & (ICS_S_IREFST_MASK | ICS_S_CLKST_MASK)) !=
890            (ICS_S_IREFST(kICS_FllSrcExternal) | ICS_S_CLKST(kICS_ClkOutStatExt)))
891     {
892     }
893 
894     /* set bus clock divider and enable low power */
895     ICS->C2 = (uint8_t)((ICS->C2 & (~ICS_C2_BDIV_MASK)) | ICS_C2_BDIV(bDiv) | ICS_C2_LP_MASK);
896 
897     return kStatus_Success;
898 }
899 
900 /*
901    The transaction matrix. It defines the path for mode switch, the row is for
902    current mode and the column is target mode.
903    For example, switch from FEI to BELP:
904    1. Current mode FEI, next mode is ICSModeMatrix[FEI][BELP] = FBE, so swith to FBE.
905    2. Current mode FBE, next mode is ICSModeMatrix[FBE][BELP] = BELP, so swith to BELP.
906    Thus the ICS mode has changed from FEI to BELP.
907  */
908 static const ics_mode_t ICSModeMatrix[6][6] = {
909     {kICS_ModeFEI, kICS_ModeFBI, kICS_ModeFBI, kICS_ModeFEE, kICS_ModeFBE, kICS_ModeFBE},  /* FEI */
910     {kICS_ModeFEI, kICS_ModeFBI, kICS_ModeBILP, kICS_ModeFEE, kICS_ModeFBE, kICS_ModeFBE}, /* FBI */
911     {kICS_ModeFBI, kICS_ModeFBI, kICS_ModeBILP, kICS_ModeFBI, kICS_ModeFBI, kICS_ModeFBI}, /* BILP */
912     {kICS_ModeFEI, kICS_ModeFBI, kICS_ModeFBI, kICS_ModeFEE, kICS_ModeFBE, kICS_ModeFBE},  /* FEE */
913     {kICS_ModeFEI, kICS_ModeFBI, kICS_ModeFBI, kICS_ModeFEE, kICS_ModeFBE, kICS_ModeBELP}, /* FBE */
914     {kICS_ModeFBE, kICS_ModeFBE, kICS_ModeFBE, kICS_ModeFBE, kICS_ModeFBE, kICS_ModeBELP}, /* BELP */
915     /*      FEI           FBI           BILP          FEE           FBE           BELP      */
916 };
917 
918 /*!
919  * brief Sets the ICS to a target mode.
920  *
921  * This function sets ICS to a target mode defined by the configuration
922  * structure. If switching to the target mode fails, this function
923  * chooses the correct path.
924  *
925  * param  config Pointer to the target ICS mode configuration structure.
926  * return Return kStatus_Success if switched successfully; Otherwise, it returns an error code #_ICS_status.
927  *
928  * note If the external clock is used in the target mode, ensure that it is
929  * enabled. For example, if the OSC0 is used, set up OSC0 correctly before calling this
930  * function.
931  */
CLOCK_SetIcsConfig(const ics_config_t * config)932 status_t CLOCK_SetIcsConfig(const ics_config_t *config)
933 {
934     assert(config->icsMode != kICS_ModeError);
935 
936     ics_mode_t next_mode;
937     status_t status = kStatus_Success;
938 
939     /* Configure ICSIRCLK. */
940     CLOCK_SetInternalRefClkConfig(config->irClkEnableMode);
941 
942     next_mode = CLOCK_GetMode();
943 
944     if (next_mode == kICS_ModeError)
945     {
946         return kStatus_ICS_ModeUnreachable;
947     }
948 
949     do
950     {
951         next_mode = ICSModeMatrix[next_mode][config->icsMode];
952 
953         switch (next_mode)
954         {
955             case kICS_ModeFEI:
956                 status = CLOCK_SetFeiMode(config->bDiv);
957                 break;
958             case kICS_ModeFEE:
959                 status = CLOCK_SetFeeMode(config->bDiv, config->rDiv);
960                 break;
961             case kICS_ModeFBI:
962                 status = CLOCK_SetFbiMode(config->bDiv);
963                 break;
964             case kICS_ModeFBE:
965                 status = CLOCK_SetFbeMode(config->bDiv, config->rDiv);
966                 break;
967             case kICS_ModeBILP:
968                 status = CLOCK_SetBilpMode(config->bDiv);
969                 break;
970             case kICS_ModeBELP:
971                 status = CLOCK_SetBelpMode(config->bDiv);
972                 break;
973             default:
974                 status = kStatus_Success;
975                 break;
976         }
977         if (kStatus_Success != status)
978         {
979             return status;
980         }
981     } while (next_mode != config->icsMode);
982 
983     return kStatus_Success;
984 }
985