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