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