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_ICSFllClk:
297             freq = CLOCK_GetFllFreq();
298             break;
299         case kCLOCK_ICSFixedFreqClk:
300             freq = CLOCK_GetICSFixedFreqClkFreq();
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->SOPT |= SIM_SOPT_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     /* Set IREFS. */
583     ICS->C1 = (uint8_t)((ICS->C1 & ~(ICS_C1_IREFS_MASK)) | ICS_C1_IREFS(kICS_FllSrcInternal)); /* IREFS = 1 */
584 
585     /* Set CLKS */
586     ICS->C1 = (uint8_t)((ICS->C1 & (~ICS_C1_CLKS_MASK)) | ICS_C1_CLKS(kICS_ClkOutSrcFll)); /* CLKS = 0 */
587     /* set bus clock divider */
588     ICS->C2 = (uint8_t)((ICS->C2 & (~ICS_C2_BDIV_MASK)) | ICS_C2_BDIV(bDiv));
589 
590     /* Wait and check status. */
591     while ((uint8_t)kICS_FllSrcInternal != ICS_S_IREFST_VAL)
592     {
593     }
594 
595     /* Check ICS_S[CLKST] */
596     while ((uint8_t)kICS_ClkOutStatFll != ICS_S_CLKST_VAL)
597     {
598     }
599 
600     /* wait for FLL to lock */
601     while (0U == (ICS->S & ICS_S_LOCK_MASK))
602     {
603     }
604 
605     /* clear Loss of lock sticky bit */
606     ICS->S |= ICS_S_LOLS_MASK;
607 
608     return kStatus_Success;
609 }
610 
611 /*!
612  * brief Sets the ICS to FEE mode.
613  *
614  * This function sets the ICS to FEE mode. If setting to FEE mode fails
615  * from the current mode, this function returns an error.
616  *
617  * param   bDiv bus clock divider
618  * param   rdiv  FLL reference clock divider setting, RDIV.
619  *
620  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
621  * retval kStatus_Success Switched to the target mode successfully.
622  */
CLOCK_SetFeeMode(uint8_t bDiv,uint8_t rDiv)623 status_t CLOCK_SetFeeMode(uint8_t bDiv, uint8_t rDiv)
624 {
625 #if (defined(ICS_CONFIG_CHECK_PARAM) && ICS_CONFIG_CHECK_PARAM)
626     ics_mode_t mode = CLOCK_GetMode();
627     if (!((kICS_ModeFEE == mode) || (kICS_ModeFBI == mode) || (kICS_ModeFBE == mode) || (kICS_ModeFEI == mode)))
628     {
629         return kStatus_ICS_ModeUnreachable;
630     }
631 #endif
632 
633     /* Set CLKS, rDiv and IREFS. */
634     ICS->C1 = (uint8_t)((ICS->C1 & ~(ICS_C1_CLKS_MASK | ICS_C1_RDIV_MASK | ICS_C1_IREFS_MASK)) |
635                         (ICS_C1_CLKS(kICS_ClkOutSrcFll)         /* CLKS = 0 */
636                          | ICS_C1_RDIV(rDiv)                    /* FRDIV */
637                          | ICS_C1_IREFS(kICS_FllSrcExternal))); /* IREFS = 0 */
638     /* set bus clock divider */
639     ICS->C2 = (uint8_t)((ICS->C2 & (~ICS_C2_BDIV_MASK)) | ICS_C2_BDIV(bDiv));
640 
641     /* If use external crystal as clock source, wait for it stable. */
642     {
643         if ((OSC0->CR & OSC_CR_OSCOS_MASK) != 0U)
644         {
645             while (0U == (OSC0->CR & OSC_CR_OSCINIT_MASK))
646             {
647             }
648         }
649     }
650 
651     /* Wait and check status. */
652     while ((uint8_t)kICS_FllSrcExternal != ICS_S_IREFST_VAL)
653     {
654     }
655 
656     /* Check ICS_S[CLKST] */
657     while ((uint8_t)kICS_ClkOutStatFll != ICS_S_CLKST_VAL)
658     {
659     }
660 
661     /* wait for FLL to lock */
662     while (0U == (ICS->S & ICS_S_LOCK_MASK))
663     {
664     }
665 
666     /* clear Loss of lock sticky bit */
667     ICS->S |= ICS_S_LOLS_MASK;
668 
669     return kStatus_Success;
670 }
671 
672 /*!
673  * brief Sets the ICS to FBI mode.
674  *
675  * This function sets the ICS to FBI mode. If setting to FBI mode fails
676  * from the current mode, this function returns an error.
677  *
678  * param bDiv bus clock divider
679  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
680  * retval kStatus_Success Switched to the target mode successfully.
681  */
CLOCK_SetFbiMode(uint8_t bDiv)682 status_t CLOCK_SetFbiMode(uint8_t bDiv)
683 {
684 #if (defined(ICS_CONFIG_CHECK_PARAM) && ICS_CONFIG_CHECK_PARAM)
685     ics_mode_t mode = CLOCK_GetMode();
686 
687     if (!((kICS_ModeFEE == mode) || (kICS_ModeFBI == mode) || (kICS_ModeFBE == mode) || (kICS_ModeFEI == mode) ||
688           (kICS_ModeBILP == mode)))
689 
690     {
691         return kStatus_ICS_ModeUnreachable;
692     }
693 #endif
694 
695     /* set bus clock divider and disable low power */
696     ICS->C2 = (uint8_t)((ICS->C2 & (~(ICS_C2_BDIV_MASK | ICS_C2_LP_MASK))) | ICS_C2_BDIV(bDiv));
697     /* Set CLKS and IREFS. */
698     ICS->C1 = (uint8_t)((ICS->C1 & ~(ICS_C1_CLKS_MASK | ICS_C1_IREFS_MASK)) |
699                         (ICS_C1_CLKS(kICS_ClkOutSrcInternal)    /* CLKS = 1 */
700                          | ICS_C1_IREFS(kICS_FllSrcInternal))); /* IREFS = 1 */
701 
702     /* Wait and check status. */
703     while ((uint8_t)kICS_FllSrcInternal != ICS_S_IREFST_VAL)
704     {
705     }
706 
707     while ((uint8_t)kICS_ClkOutStatInt != ICS_S_CLKST_VAL)
708     {
709     }
710 
711     /* clear Loss of lock sticky bit */
712     ICS->S |= ICS_S_LOLS_MASK;
713 
714     return kStatus_Success;
715 }
716 
717 /*!
718  * brief Sets the ICS to FBE mode.
719  *
720  * This function sets the ICS to FBE mode. If setting to FBE mode fails
721  * from the current mode, this function returns an error.
722  *
723  * param   bDiv bus clock divider
724  * param   rdiv  FLL reference clock divider setting, RDIV.
725  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
726  * retval kStatus_Success Switched to the target mode successfully.
727  */
CLOCK_SetFbeMode(uint8_t bDiv,uint8_t rDiv)728 status_t CLOCK_SetFbeMode(uint8_t bDiv, uint8_t rDiv)
729 {
730 #if (defined(ICS_CONFIG_CHECK_PARAM) && ICS_CONFIG_CHECK_PARAM)
731     ics_mode_t mode = CLOCK_GetMode();
732     if (!((kICS_ModeFEE == mode) || (kICS_ModeFBI == mode) || (kICS_ModeFBE == mode) || (kICS_ModeFEI == mode) ||
733           (kICS_ModeBELP == mode)))
734     {
735         return kStatus_ICS_ModeUnreachable;
736     }
737 #endif
738 
739     /* set bus clock divider and disable low power */
740     ICS->C2 = (uint8_t)((ICS->C2 & (~(ICS_C2_BDIV_MASK | ICS_C2_LP_MASK))) | ICS_C2_BDIV(bDiv));
741 
742     /* Set CLKS and IREFS. */
743     ICS->C1 = (uint8_t)((ICS->C1 & ~(ICS_C1_CLKS_MASK | ICS_C1_RDIV_MASK | ICS_C1_IREFS_MASK)) |
744                         (ICS_C1_CLKS(kICS_ClkOutSrcExternal)    /* CLKS = 2 */
745                          | ICS_C1_RDIV(rDiv)                    /* FRDIV = frDiv */
746                          | ICS_C1_IREFS(kICS_FllSrcExternal))); /* IREFS = 0 */
747 
748     /* If use external crystal as clock source, wait for it stable. */
749     {
750         if ((OSC0->CR & OSC_CR_OSCOS_MASK) != 0U)
751         {
752             while (0U == (OSC0->CR & OSC_CR_OSCINIT_MASK))
753             {
754             }
755         }
756     }
757 
758     /* Wait for Reference clock Status bit to clear */
759     while ((uint8_t)kICS_FllSrcExternal != ICS_S_IREFST_VAL)
760     {
761     }
762 
763     /* Wait for clock status bits to show clock source is ext ref clk */
764     while ((uint8_t)kICS_ClkOutStatExt != ICS_S_CLKST_VAL)
765     {
766     }
767 
768     /* clear Loss of lock sticky bit */
769     ICS->S |= ICS_S_LOLS_MASK;
770 
771     return kStatus_Success;
772 }
773 
774 /*!
775  * brief Sets the ICS to BILP mode.
776  *
777  * This function sets the ICS to BILP mode. If setting to BILP mode fails
778  * from the current mode, this function returns an error.
779  *
780  * param   bDiv bus clock divider
781  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
782  * retval kStatus_Success Switched to the target mode successfully.
783  */
CLOCK_SetBilpMode(uint8_t bDiv)784 status_t CLOCK_SetBilpMode(uint8_t bDiv)
785 {
786 #if (defined(ICS_CONFIG_CHECK_PARAM) && ICS_CONFIG_CHECK_PARAM)
787     if (ICS_S_CLKST_VAL != kICS_ClkOutStatInt)
788     {
789         return kStatus_ICS_ModeUnreachable;
790     }
791 #endif /* ICS_CONFIG_CHECK_PARAM */
792 
793     /* set bus clock divider and enable low power */
794     ICS->C2 = (uint8_t)((ICS->C2 & (~ICS_C2_BDIV_MASK)) | ICS_C2_BDIV(bDiv) | ICS_C2_LP_MASK);
795 
796     return kStatus_Success;
797 }
798 
799 /*!
800  * brief Sets the ICS to BELP mode.
801  *
802  * This function sets the ICS to BELP mode. If setting to BELP mode fails
803  * from the current mode, this function returns an error.
804  *
805  * param   bDiv bus clock divider
806  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
807  * retval kStatus_Success Switched to the target mode successfully.
808  */
CLOCK_SetBelpMode(uint8_t bDiv)809 status_t CLOCK_SetBelpMode(uint8_t bDiv)
810 {
811 #if (defined(ICS_CONFIG_CHECK_PARAM) && ICS_CONFIG_CHECK_PARAM)
812     if (ICS_S_CLKST_VAL != kICS_ClkOutStatExt)
813     {
814         return kStatus_ICS_ModeUnreachable;
815     }
816 #endif
817 
818     /* set bus clock divider and enable low power */
819     ICS->C2 = (uint8_t)((ICS->C2 & (~ICS_C2_BDIV_MASK)) | ICS_C2_BDIV(bDiv) | ICS_C2_LP_MASK);
820 
821     return kStatus_Success;
822 }
823 
824 /*!
825  * brief Sets the ICS to FEI mode during system boot up.
826  *
827  * This function sets the ICS to FEI mode from the reset mode. It can also be used to
828  * set up ICS during system boot up.
829  *
830  * param  bDiv bus clock divider.
831  *
832  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
833  * retval kStatus_Success Switched to the target mode successfully.
834  */
CLOCK_BootToFeiMode(uint8_t bDiv)835 status_t CLOCK_BootToFeiMode(uint8_t bDiv)
836 {
837     return CLOCK_SetFeiMode(bDiv);
838 }
839 
840 /*!
841  * brief Sets the ICS to FEE mode during system bootup.
842  *
843  * This function sets ICS to FEE mode from the reset mode. It can also be used to
844  * set up the ICS during system boot up.
845  *
846  * param   bDiv bus clock divider.
847  * param   rdiv  FLL reference clock divider setting, RDIV.
848  *
849  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
850  * retval kStatus_Success Switched to the target mode successfully.
851  */
CLOCK_BootToFeeMode(uint8_t bDiv,uint8_t rDiv)852 status_t CLOCK_BootToFeeMode(uint8_t bDiv, uint8_t rDiv)
853 {
854     return CLOCK_SetFeeMode(bDiv, rDiv);
855 }
856 
857 /*!
858  * brief Sets the ICS to BILP mode during system boot up.
859  *
860  * This function sets the ICS to BILP mode from the reset mode. It can also be used to
861  * set up the ICS during system boot up.
862  *
863  * param   bDiv bus clock divider.
864  * retval kStatus_ICS_SourceUsed Could not change ICSIRCLK setting.
865  * retval kStatus_Success Switched to the target mode successfully.
866  */
CLOCK_BootToBilpMode(uint8_t bDiv)867 status_t CLOCK_BootToBilpMode(uint8_t bDiv)
868 {
869     /* If reset mode is not BILP, first enter FBI mode. */
870     ICS->C1 = (uint8_t)((ICS->C1 & ~ICS_C1_CLKS_MASK) | ICS_C1_CLKS(kICS_ClkOutSrcInternal));
871     while (ICS_S_CLKST_VAL != (uint8_t)kICS_ClkOutStatInt)
872     {
873     }
874 
875     /* set bus clock divider and enable low power */
876     ICS->C2 = (uint8_t)((ICS->C2 & (~ICS_C2_BDIV_MASK)) | ICS_C2_BDIV(bDiv) | ICS_C2_LP_MASK);
877 
878     return kStatus_Success;
879 }
880 
881 /*!
882  * brief Sets the ICS to BELP mode during system boot up.
883  *
884  * This function sets the ICS to BELP mode from the reset mode. It can also be used to
885  * set up the ICS during system boot up.
886  *
887  * param   bDiv bus clock divider.
888  *
889  * retval kStatus_ICS_ModeUnreachable Could not switch to the target mode.
890  * retval kStatus_Success Switched to the target mode successfully.
891  */
CLOCK_BootToBelpMode(uint8_t bDiv)892 status_t CLOCK_BootToBelpMode(uint8_t bDiv)
893 {
894     /* Set to FBE mode. */
895     ICS->C1 = (uint8_t)((ICS->C1 & ~(ICS_C1_CLKS_MASK | ICS_C1_IREFS_MASK)) |
896                         (ICS_C1_CLKS(kICS_ClkOutSrcExternal)    /* CLKS = 2 */
897                          | ICS_C1_IREFS(kICS_FllSrcExternal))); /* IREFS = 0 */
898 
899     /* If use external crystal as clock source, wait for it stable. */
900     {
901         if ((OSC0->CR & OSC_CR_OSCOS_MASK) != 0U)
902         {
903             while (0U == (OSC0->CR & OSC_CR_OSCINIT_MASK))
904             {
905             }
906         }
907     }
908 
909     /* Wait for ICS_S[CLKST] and ICS_S[IREFST]. */
910     while ((ICS->S & (ICS_S_IREFST_MASK | ICS_S_CLKST_MASK)) !=
911            (ICS_S_IREFST(kICS_FllSrcExternal) | ICS_S_CLKST(kICS_ClkOutStatExt)))
912     {
913     }
914 
915     /* set bus clock divider and enable low power */
916     ICS->C2 = (uint8_t)((ICS->C2 & (~ICS_C2_BDIV_MASK)) | ICS_C2_BDIV(bDiv) | ICS_C2_LP_MASK);
917 
918     return kStatus_Success;
919 }
920 
921 /*
922    The transaction matrix. It defines the path for mode switch, the row is for
923    current mode and the column is target mode.
924    For example, switch from FEI to BELP:
925    1. Current mode FEI, next mode is ICSModeMatrix[FEI][BELP] = FBE, so swith to FBE.
926    2. Current mode FBE, next mode is ICSModeMatrix[FBE][BELP] = BELP, so swith to BELP.
927    Thus the ICS mode has changed from FEI to BELP.
928  */
929 static const ics_mode_t ICSModeMatrix[6][6] = {
930     {kICS_ModeFEI, kICS_ModeFBI, kICS_ModeFBI, kICS_ModeFEE, kICS_ModeFBE, kICS_ModeFBE},  /* FEI */
931     {kICS_ModeFEI, kICS_ModeFBI, kICS_ModeBILP, kICS_ModeFEE, kICS_ModeFBE, kICS_ModeFBE}, /* FBI */
932     {kICS_ModeFBI, kICS_ModeFBI, kICS_ModeBILP, kICS_ModeFBI, kICS_ModeFBI, kICS_ModeFBI}, /* BILP */
933     {kICS_ModeFEI, kICS_ModeFBI, kICS_ModeFBI, kICS_ModeFEE, kICS_ModeFBE, kICS_ModeFBE},  /* FEE */
934     {kICS_ModeFEI, kICS_ModeFBI, kICS_ModeFBI, kICS_ModeFEE, kICS_ModeFBE, kICS_ModeBELP}, /* FBE */
935     {kICS_ModeFBE, kICS_ModeFBE, kICS_ModeFBE, kICS_ModeFBE, kICS_ModeFBE, kICS_ModeBELP}, /* BELP */
936     /*      FEI           FBI           BILP          FEE           FBE           BELP      */
937 };
938 
939 /*!
940  * brief Sets the ICS to a target mode.
941  *
942  * This function sets ICS to a target mode defined by the configuration
943  * structure. If switching to the target mode fails, this function
944  * chooses the correct path.
945  *
946  * param  config Pointer to the target ICS mode configuration structure.
947  * return Return kStatus_Success if switched successfully; Otherwise, it returns an error code #_ICS_status.
948  *
949  * note If the external clock is used in the target mode, ensure that it is
950  * enabled. For example, if the OSC0 is used, set up OSC0 correctly before calling this
951  * function.
952  */
CLOCK_SetIcsConfig(const ics_config_t * config)953 status_t CLOCK_SetIcsConfig(const ics_config_t *config)
954 {
955     ics_mode_t next_mode;
956     status_t status = kStatus_Success;
957 
958     /* Configure ICSIRCLK. */
959     CLOCK_SetInternalRefClkConfig(config->irClkEnableMode);
960 
961     next_mode = CLOCK_GetMode();
962 
963     do
964     {
965         next_mode = ICSModeMatrix[next_mode][config->icsMode];
966 
967         switch (next_mode)
968         {
969             case kICS_ModeFEI:
970                 status = CLOCK_SetFeiMode(config->bDiv);
971                 break;
972             case kICS_ModeFEE:
973                 status = CLOCK_SetFeeMode(config->bDiv, config->rDiv);
974                 break;
975             case kICS_ModeFBI:
976                 status = CLOCK_SetFbiMode(config->bDiv);
977                 break;
978             case kICS_ModeFBE:
979                 status = CLOCK_SetFbeMode(config->bDiv, config->rDiv);
980                 break;
981             case kICS_ModeBILP:
982                 status = CLOCK_SetBilpMode(config->bDiv);
983                 break;
984             case kICS_ModeBELP:
985                 status = CLOCK_SetBelpMode(config->bDiv);
986                 break;
987             default:
988                 status = kStatus_Success;
989                 break;
990         }
991         if (kStatus_Success != status)
992         {
993             return status;
994         }
995     } while (next_mode != config->icsMode);
996 
997     return kStatus_Success;
998 }
999