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