1 /*
2 * Copyright 2017-2019, 2021 NXP
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8 #include "fsl_clock.h"
9 /*******************************************************************************
10 * Definitions
11 ******************************************************************************/
12 /* Component ID definition, used by tools. */
13 #ifndef FSL_COMPONENT_ID
14 #define FSL_COMPONENT_ID "platform.drivers.clock"
15 #endif
16 #define SYSPLL_MIN_INPUT_FREQ_HZ (10000000U) /*!< Minimum PLL input rate */
17 #define SYSPLL_MAX_INPUT_FREQ_HZ (25000000U) /*!< Maximum PLL input rate */
18 #define SYSPLL_MAX_OUTPUT_FREQ_HZ (100000000U) /*!< Maximum PLL output rate */
19 #define SYSPLL_MIN_FCCO_FREQ_HZ (156000000U) /*!< Maximum FCCO output rate */
20 #define SYSPLL_MAX_FCCO_FREQ_HZ (320000000U) /*!< Maximum FCCO output rate */
21 #define SYSOSC_BOUNDARY_FREQ_HZ (15000000U) /*!< boundary frequency value */
22
23 /* External clock rate.
24 * Either external clk in rate or system oscillator frequency.
25 */
26 uint32_t g_Ext_Clk_Freq = 0U;
27 uint32_t g_Wdt_Osc_Freq = 0U;
28
29 /** Sys pll freq.*/
30 uint32_t g_Sys_Pll_Freq = 0U;
31 /*******************************************************************************
32 * Variables
33 ******************************************************************************/
34
35 /*******************************************************************************
36 * Prototypes
37 ******************************************************************************/
38 /*
39 * @brief select post divider for system pll according to the target frequency.
40 * @param outFreq: Value to be output
41 * @return post divider
42 */
43 static uint32_t findSyestemPllPsel(uint32_t outFreq);
44
45 /*
46 * @brief Update clock source.
47 * @param base clock register base address.
48 * @param mask clock source update enable bit mask value.
49 */
50 static void CLOCK_UpdateClkSrc(volatile uint32_t *base, uint32_t mask);
51
52 /*******************************************************************************
53 * Code
54 ******************************************************************************/
CLOCK_UpdateClkSrc(volatile uint32_t * base,uint32_t mask)55 static void CLOCK_UpdateClkSrc(volatile uint32_t *base, uint32_t mask)
56 {
57 assert(base);
58
59 *base &= ~mask;
60 *base |= mask;
61 while ((*base & mask) == 0U)
62 {
63 }
64 }
65
66 /*! brief Return Frequency of IRC
67 * return Frequency of IRC
68 */
CLOCK_GetIrcFreq(void)69 uint32_t CLOCK_GetIrcFreq(void)
70 {
71 return 12000000U;
72 }
73
74 /*! brief Return Frequency of SYSOSC
75 * return Frequency of SYSOSC
76 */
CLOCK_GetSysOscFreq(void)77 uint32_t CLOCK_GetSysOscFreq(void)
78 {
79 uint32_t freq = 0U;
80 if ((SYSCON->PDRUNCFG & SYSCON_PDRUNCFG_SYSOSC_PD_MASK) == 0U)
81 {
82 freq = CLOCK_GetExtClkFreq();
83 }
84 return freq;
85 }
86
87 /*! brief Return Frequency of Main Clock.
88 * return Frequency of Main Clock.
89 */
CLOCK_GetMainClkFreq(void)90 uint32_t CLOCK_GetMainClkFreq(void)
91 {
92 uint32_t freq = 0U;
93
94 switch (SYSCON->MAINCLKSEL)
95 {
96 case 0U:
97 freq = CLOCK_GetIrcFreq();
98 break;
99
100 case 1U:
101 freq = CLOCK_GetSystemPLLInClockRate();
102 break;
103
104 case 2U:
105 freq = CLOCK_GetWdtOscFreq();
106 break;
107
108 case 3U:
109 freq = CLOCK_GetSystemPLLFreq();
110 break;
111 default:
112 assert(false);
113 break;
114 }
115
116 return freq;
117 }
118
119 /*! brief Return Frequency of ClockOut
120 * return Frequency of ClockOut
121 */
CLOCK_GetClockOutClkFreq(void)122 uint32_t CLOCK_GetClockOutClkFreq(void)
123 {
124 uint32_t div = SYSCON->CLKOUTDIV & 0xffU, freq = 0U;
125
126 switch (SYSCON->CLKOUTSEL)
127 {
128 case 0U:
129 freq = CLOCK_GetIrcFreq();
130 break;
131
132 case 1U:
133 freq = CLOCK_GetSysOscFreq();
134 break;
135
136 case 2U:
137 freq = CLOCK_GetWdtOscFreq();
138 break;
139
140 case 3U:
141 freq = CLOCK_GetMainClkFreq();
142 break;
143
144 default:
145 assert(false);
146 break;
147 }
148
149 return div == 0U ? 0U : (freq / div);
150 }
151
152 /*! brief Return Frequency of UART
153 * return Frequency of UART
154 */
CLOCK_GetUartClkFreq(void)155 uint32_t CLOCK_GetUartClkFreq(void)
156 {
157 uint32_t freq = CLOCK_GetMainClkFreq();
158 uint32_t uartDiv = SYSCON->UARTCLKDIV & 0xffU;
159
160 return uartDiv == 0U ? 0U :
161 (uint32_t)((uint64_t)((uint64_t)freq << 8U) /
162 (uartDiv * (256U + ((SYSCON->UARTFRGMULT) & SYSCON_UARTFRGMULT_MULT_MASK))));
163 }
164
165 /*! brief Return Frequency of UART0
166 * return Frequency of UART0
167 */
CLOCK_GetUart0ClkFreq(void)168 uint32_t CLOCK_GetUart0ClkFreq(void)
169 {
170 return CLOCK_GetUartClkFreq();
171 }
172
173 /*! brief Return Frequency of UART1
174 * return Frequency of UART1
175 */
CLOCK_GetUart1ClkFreq(void)176 uint32_t CLOCK_GetUart1ClkFreq(void)
177 {
178 return CLOCK_GetUartClkFreq();
179 }
180
181 /*! brief Return Frequency of UART2
182 * return Frequency of UART2
183 */
CLOCK_GetUart2ClkFreq(void)184 uint32_t CLOCK_GetUart2ClkFreq(void)
185 {
186 return CLOCK_GetUartClkFreq();
187 }
188
189 /*! brief Return Frequency of selected clock
190 * return Frequency of selected clock
191 */
CLOCK_GetFreq(clock_name_t clockName)192 uint32_t CLOCK_GetFreq(clock_name_t clockName)
193 {
194 uint32_t freq;
195
196 switch (clockName)
197 {
198 case kCLOCK_CoreSysClk:
199 freq = CLOCK_GetCoreSysClkFreq();
200 break;
201 case kCLOCK_MainClk:
202 freq = CLOCK_GetMainClkFreq();
203 break;
204 case kCLOCK_Irc:
205 freq = CLOCK_GetIrcFreq();
206 break;
207 case kCLOCK_ExtClk:
208 freq = CLOCK_GetExtClkFreq();
209 break;
210 case kCLOCK_WdtOsc:
211 freq = CLOCK_GetWdtOscFreq();
212 break;
213 case kCLOCK_PllOut:
214 freq = CLOCK_GetSystemPLLFreq();
215 break;
216
217 default:
218 freq = 0U;
219 break;
220 }
221
222 return freq;
223 }
224
225 /*! brief Return System PLL input clock rate
226 * return System PLL input clock rate
227 */
CLOCK_GetSystemPLLInClockRate(void)228 uint32_t CLOCK_GetSystemPLLInClockRate(void)
229 {
230 uint32_t freq = 0U;
231
232 switch ((SYSCON->SYSPLLCLKSEL & SYSCON_SYSPLLCLKSEL_SEL_MASK))
233 {
234 /* source from external clock in */
235 case 0x00U:
236 freq = CLOCK_GetIrcFreq();
237 break;
238 /* source from the IRC clock */
239 case 0x01U:
240 freq = CLOCK_GetSysOscFreq();
241 break;
242 /* source from external clock clock */
243 case 0x03U:
244 freq = CLOCK_GetExtClkFreq();
245 break;
246
247 default:
248 assert(false);
249 break;
250 }
251
252 return freq;
253 }
254
findSyestemPllPsel(uint32_t outFreq)255 static uint32_t findSyestemPllPsel(uint32_t outFreq)
256 {
257 uint32_t pSel = 0U;
258
259 if (outFreq > (SYSPLL_MIN_FCCO_FREQ_HZ >> 1U))
260 {
261 pSel = 0U;
262 }
263 else if (outFreq > (SYSPLL_MIN_FCCO_FREQ_HZ >> 2U))
264 {
265 pSel = 1U;
266 }
267 else if (outFreq > (SYSPLL_MIN_FCCO_FREQ_HZ >> 3U))
268 {
269 pSel = 2U;
270 }
271 else
272 {
273 pSel = 3U;
274 }
275
276 return pSel;
277 }
278
279 /*! brief System PLL initialize.
280 * param config System PLL configurations.
281 */
CLOCK_InitSystemPll(const clock_sys_pll_t * config)282 void CLOCK_InitSystemPll(const clock_sys_pll_t *config)
283 {
284 assert(config->targetFreq <= SYSPLL_MAX_OUTPUT_FREQ_HZ);
285
286 uint32_t mSel = 0U, pSel = 0U, inputFreq = 0U;
287 uint32_t syspllclkseltmp;
288 /* Power off PLL during setup changes */
289 SYSCON->PDRUNCFG |= SYSCON_PDRUNCFG_SYSPLL_PD_MASK;
290
291 /*set system pll clock source select register */
292 syspllclkseltmp = (SYSCON->SYSPLLCLKSEL & (~SYSCON_SYSPLLCLKSEL_SEL_MASK)) | (uint32_t)config->src;
293 SYSCON->SYSPLLCLKSEL |= syspllclkseltmp;
294 /* system pll clock source update */
295 CLOCK_UpdateClkSrc((volatile uint32_t *)(&(SYSCON->SYSPLLCLKUEN)), SYSCON_SYSPLLCLKSEL_SEL_MASK);
296
297 inputFreq = CLOCK_GetSystemPLLInClockRate();
298 assert(inputFreq != 0U);
299
300 /* calucate the feedback divider value and post divider value*/
301 mSel = config->targetFreq / inputFreq;
302 pSel = findSyestemPllPsel(config->targetFreq);
303
304 /* configure PSEL and MSEL */
305 SYSCON->SYSPLLCTRL = (SYSCON->SYSPLLCTRL & (~(SYSCON_SYSPLLCTRL_MSEL_MASK | SYSCON_SYSPLLCTRL_PSEL_MASK))) |
306 SYSCON_SYSPLLCTRL_MSEL(mSel - 1U) | SYSCON_SYSPLLCTRL_PSEL(pSel);
307
308 /* Power up PLL after setup changes */
309 SYSCON->PDRUNCFG &= ~SYSCON_PDRUNCFG_SYSPLL_PD_MASK;
310
311 /* wait pll lock */
312 while ((SYSCON->SYSPLLSTAT & SYSCON_SYSPLLSTAT_LOCK_MASK) == 0U)
313 {
314 }
315
316 g_Sys_Pll_Freq = inputFreq * mSel;
317 }
318
319 /*! brief Init external CLK IN, select the CLKIN as the external clock source.
320 * param clkInFreq external clock in frequency.
321 */
CLOCK_InitExtClkin(uint32_t clkInFreq)322 void CLOCK_InitExtClkin(uint32_t clkInFreq)
323 {
324 /* remove the pull up and pull down resistors in the IOCON */
325 IOCON->PIO[IOCON_INDEX_PIO0_1] &= ~IOCON_PIO_MODE_MASK;
326 /* enable the 1 bit functions for CLKIN */
327 SWM0->PINENABLE0 &= ~SWM_PINENABLE0_CLKIN_MASK;
328 /* record the external clock rate */
329 g_Ext_Clk_Freq = clkInFreq;
330 }
331
332 /*! brief XTALIN init function
333 * system oscillator is bypassed, sys_osc_clk is fed driectly from the XTALIN.
334 * param xtalInFreq XTALIN frequency value
335 * return Frequency of PLL
336 */
CLOCK_InitXtalin(uint32_t xtalInFreq)337 void CLOCK_InitXtalin(uint32_t xtalInFreq)
338 {
339 /* remove the pull up and pull down resistors in the IOCON */
340 IOCON->PIO[IOCON_INDEX_PIO0_8] &= ~IOCON_PIO_MODE_MASK;
341 /* enable the 1 bit functions for XTALIN and XTALOUT */
342 SWM0->PINENABLE0 &= ~SWM_PINENABLE0_XTALIN_MASK;
343
344 /* system osc configure */
345 SYSCON->SYSOSCCTRL |= SYSCON_SYSOSCCTRL_BYPASS_MASK;
346 /* enable system osc power first */
347 SYSCON->PDRUNCFG &= ~SYSCON_PDRUNCFG_SYSOSC_PD_MASK;
348
349 /* software delay 500USs */
350 SDK_DelayAtLeastUs(500U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
351
352 /* record the external clock rate */
353 g_Ext_Clk_Freq = xtalInFreq;
354 }
355
356 /*! brief Init SYS OSC
357 * param oscFreq oscillator frequency value.
358 */
CLOCK_InitSysOsc(uint32_t oscFreq)359 void CLOCK_InitSysOsc(uint32_t oscFreq)
360 {
361 uint32_t sysoscctrltmp;
362 /* remove the pull up and pull down resistors in the IOCON */
363 IOCON->PIO[IOCON_INDEX_PIO0_9] &= ~IOCON_PIO_MODE_MASK;
364 IOCON->PIO[IOCON_INDEX_PIO0_8] &= ~IOCON_PIO_MODE_MASK;
365 /* enable the 1 bit functions for XTALIN and XTALOUT */
366 SWM0->PINENABLE0 &= ~(SWM_PINENABLE0_XTALIN_MASK | SWM_PINENABLE0_XTALOUT_MASK);
367
368 /* system osc configure */
369 sysoscctrltmp = (SYSCON->SYSOSCCTRL & (~(SYSCON_SYSOSCCTRL_BYPASS_MASK | SYSCON_SYSOSCCTRL_FREQ_RANGE_MASK))) |
370 (oscFreq > SYSOSC_BOUNDARY_FREQ_HZ ? SYSCON_SYSOSCCTRL_FREQ_RANGE_MASK : 0U);
371 SYSCON->SYSOSCCTRL |= sysoscctrltmp;
372
373 /* enable system osc power first */
374 SYSCON->PDRUNCFG &= ~SYSCON_PDRUNCFG_SYSOSC_PD_MASK;
375
376 /* software delay 500USs */
377 SDK_DelayAtLeastUs(500U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
378
379 /* record the external clock rate */
380 g_Ext_Clk_Freq = oscFreq;
381 }
382
383 /*! brief Init watch dog OSC
384 * Any setting of the FREQSEL bits will yield a Fclkana value within 40% of the
385 * listed frequency value. The watchdog oscillator is the clock source with the lowest power
386 * consumption. If accurate timing is required, use the FRO or system oscillator.
387 * The frequency of the watchdog oscillator is undefined after reset. The watchdog
388 * oscillator frequency must be programmed by writing to the WDTOSCCTRL register before
389 * using the watchdog oscillator.
390 * Watchdog osc output frequency = wdtOscFreq / wdtOscDiv, should in range 9.3KHZ to 2.3MHZ.
391 * param wdtOscFreq watch dog analog part output frequency, reference _wdt_analog_output_freq.
392 * param wdtOscDiv watch dog analog part output frequency divider, shoule be a value >= 2U and multiple of 2
393 */
CLOCK_InitWdtOsc(clock_wdt_analog_freq_t wdtOscFreq,uint32_t wdtOscDiv)394 void CLOCK_InitWdtOsc(clock_wdt_analog_freq_t wdtOscFreq, uint32_t wdtOscDiv)
395 {
396 assert(wdtOscDiv >= 2U);
397
398 uint32_t wdtOscCtrl = SYSCON->WDTOSCCTRL;
399
400 wdtOscCtrl &= ~(SYSCON_WDTOSCCTRL_DIVSEL_MASK | SYSCON_WDTOSCCTRL_FREQSEL_MASK);
401
402 wdtOscCtrl |=
403 SYSCON_WDTOSCCTRL_DIVSEL((wdtOscDiv >> 1U) - 1U) | SYSCON_WDTOSCCTRL_FREQSEL(CLK_WDT_OSC_GET_REG(wdtOscFreq));
404
405 SYSCON->WDTOSCCTRL = wdtOscCtrl;
406
407 /* power up watchdog oscillator */
408 SYSCON->PDRUNCFG &= ~SYSCON_PDRUNCFG_WDTOSC_PD_MASK;
409 /* update watch dog oscillator value */
410 g_Wdt_Osc_Freq = CLK_WDT_OSC_GET_FREQ(wdtOscFreq) / wdtOscDiv;
411 }
412
413 /*! brief Set main clock reference source.
414 * param src, reference clock_main_clk_src_t to set the main clock source.
415 */
CLOCK_SetMainClkSrc(clock_main_clk_src_t src)416 void CLOCK_SetMainClkSrc(clock_main_clk_src_t src)
417 {
418 uint32_t mainMux = CLK_MAIN_CLK_MUX_GET_MUX(src), mainPreMux = CLK_MAIN_CLK_MUX_GET_PRE_MUX(src);
419
420 if (((SYSCON->MAINCLKSEL & SYSCON_MAINCLKSEL_SEL_MASK) != mainPreMux) && (mainMux == 0U))
421 {
422 SYSCON->MAINCLKSEL = (SYSCON->MAINCLKSEL & (~SYSCON_MAINCLKSEL_SEL_MASK)) | SYSCON_MAINCLKSEL_SEL(mainPreMux);
423 CLOCK_UpdateClkSrc((volatile uint32_t *)(&(SYSCON->MAINCLKUEN)), SYSCON_MAINCLKUEN_ENA_MASK);
424 }
425 }
426
427 /*! brief Set UARTFRG
428 * param target UART clock src.
429 */
CLOCK_SetUARTFRGClkFreq(uint32_t freq)430 bool CLOCK_SetUARTFRGClkFreq(uint32_t freq)
431 {
432 uint32_t input = CLOCK_GetMainClkFreq();
433 uint32_t mul;
434
435 freq *= SYSCON->UARTCLKDIV;
436
437 /* The given frequency should not be 0. */
438 assert(0UL != freq);
439
440 if ((freq > input) || (input / freq >= 2U))
441 {
442 return false;
443 }
444
445 mul = (uint32_t)(((uint64_t)((uint64_t)input - freq) << 8U) / ((uint64_t)freq));
446
447 SYSCON->UARTFRGDIV = SYSCON_UARTFRGDIV_DIV_MASK;
448 SYSCON->UARTFRGMULT = SYSCON_UARTFRGMULT_MULT(mul);
449
450 return true;
451 }
452
453 /*! brief updates the clock source of the CLKOUT
454 */
CLOCK_UpdateClkOUTsrc(void)455 void CLOCK_UpdateClkOUTsrc(void)
456 {
457 CLOCK_UpdateClkSrc((volatile uint32_t *)(&(SYSCON->CLKOUTUEN)), SYSCON_CLKOUTUEN_ENA_MASK);
458 }
459