1 /*
2 * Copyright (c) 2015, Freescale Semiconductor, Inc.
3 * Copyright 2016 - 2019, NXP
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9 #include "fsl_clock.h"
10
11 /*******************************************************************************
12 * Definitions
13 ******************************************************************************/
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 SCG_SIRC_LOW_RANGE_FREQ 2000000U /* Slow IRC low range clock frequency. */
21 #define SCG_SIRC_HIGH_RANGE_FREQ 8000000U /* Slow IRC high range clock frequency. */
22
23 #define SCG_FIRC_FREQ0 48000000U /* Fast IRC trimed clock frequency(48MHz). */
24 #define SCG_FIRC_FREQ1 52000000U /* Fast IRC trimed clock frequency(52MHz). */
25 #define SCG_FIRC_FREQ2 56000000U /* Fast IRC trimed clock frequency(56MHz). */
26 #define SCG_FIRC_FREQ3 60000000U /* Fast IRC trimed clock frequency(60MHz). */
27
28 /*
29 * System PLL base divider value, it is the PLL reference clock divider
30 * value when SCG_SPLLCFG[PREDIV]=0.
31 */
32 #define SCG_SPLL_PREDIV_BASE_VALUE 1U
33
34 /*
35 * System PLL base multiplier value, it is the PLL multiplier value
36 * when SCG_SPLLCFG[MULT]=0.
37 */
38 #define SCG_SPLL_MULT_BASE_VALUE 16U
39
40 #define SCG_SPLL_PREDIV_MAX_VALUE 7U /* Max value of SCG_SPLLCFG[PREDIV]. */
41 #define SCG_SPLL_MULT_MAX_VALUE 31U /* Max value of SCG_SPLLCFG[MULT]. */
42
43 /*
44 * System PLL reference clock after SCG_SPLLCFG[PREDIV] should be in
45 * the range of SCG_SPLL_REF_MIN to SCG_SPLL_REF_MAX.
46 */
47 #define SCG_SPLL_REF_MIN 8000000UL
48 #define SCG_SPLL_REF_MAX 32000000UL
49
50 #define SCG_CSR_SCS_VAL ((SCG->CSR & SCG_CSR_SCS_MASK) >> SCG_CSR_SCS_SHIFT)
51 #define SCG_SOSCDIV_SOSCDIV1_VAL ((SCG->SOSCDIV & SCG_SOSCDIV_SOSCDIV1_MASK) >> SCG_SOSCDIV_SOSCDIV1_SHIFT)
52 #define SCG_SOSCDIV_SOSCDIV2_VAL ((SCG->SOSCDIV & SCG_SOSCDIV_SOSCDIV2_MASK) >> SCG_SOSCDIV_SOSCDIV2_SHIFT)
53 #define SCG_SOSCDIV_SOSCDIV3_VAL ((SCG->SOSCDIV & SCG_SOSCDIV_SOSCDIV3_MASK) >> SCG_SOSCDIV_SOSCDIV3_SHIFT)
54 #define SCG_SIRCDIV_SIRCDIV1_VAL ((SCG->SIRCDIV & SCG_SIRCDIV_SIRCDIV1_MASK) >> SCG_SIRCDIV_SIRCDIV1_SHIFT)
55 #define SCG_SIRCDIV_SIRCDIV2_VAL ((SCG->SIRCDIV & SCG_SIRCDIV_SIRCDIV2_MASK) >> SCG_SIRCDIV_SIRCDIV2_SHIFT)
56 #define SCG_SIRCDIV_SIRCDIV3_VAL ((SCG->SIRCDIV & SCG_SIRCDIV_SIRCDIV3_MASK) >> SCG_SIRCDIV_SIRCDIV3_SHIFT)
57 #define SCG_FIRCDIV_FIRCDIV1_VAL ((SCG->FIRCDIV & SCG_FIRCDIV_FIRCDIV1_MASK) >> SCG_FIRCDIV_FIRCDIV1_SHIFT)
58 #define SCG_FIRCDIV_FIRCDIV2_VAL ((SCG->FIRCDIV & SCG_FIRCDIV_FIRCDIV2_MASK) >> SCG_FIRCDIV_FIRCDIV2_SHIFT)
59 #define SCG_FIRCDIV_FIRCDIV3_VAL ((SCG->FIRCDIV & SCG_FIRCDIV_FIRCDIV3_MASK) >> SCG_FIRCDIV_FIRCDIV3_SHIFT)
60
61 #define SCG_SPLLDIV_SPLLDIV1_VAL ((SCG->SPLLDIV & SCG_SPLLDIV_SPLLDIV1_MASK) >> SCG_SPLLDIV_SPLLDIV1_SHIFT)
62 #define SCG_SPLLDIV_SPLLDIV2_VAL ((SCG->SPLLDIV & SCG_SPLLDIV_SPLLDIV2_MASK) >> SCG_SPLLDIV_SPLLDIV2_SHIFT)
63 #define SCG_SPLLDIV_SPLLDIV3_VAL ((SCG->SPLLDIV & SCG_SPLLDIV_SPLLDIV3_MASK) >> SCG_SPLLDIV_SPLLDIV3_SHIFT)
64
65 #define SCG_SIRCCFG_RANGE_VAL ((SCG->SIRCCFG & SCG_SIRCCFG_RANGE_MASK) >> SCG_SIRCCFG_RANGE_SHIFT)
66 #define SCG_FIRCCFG_RANGE_VAL ((SCG->FIRCCFG & SCG_FIRCCFG_RANGE_MASK) >> SCG_FIRCCFG_RANGE_SHIFT)
67
68 #define SCG_SPLLCFG_PREDIV_VAL ((SCG->SPLLCFG & SCG_SPLLCFG_PREDIV_MASK) >> SCG_SPLLCFG_PREDIV_SHIFT)
69 #define SCG_SPLLCFG_MULT_VAL ((SCG->SPLLCFG & SCG_SPLLCFG_MULT_MASK) >> SCG_SPLLCFG_MULT_SHIFT)
70
71 #define PCC_PCS_VAL(reg) (((reg)&PCC_CLKCFG_PCS_MASK) >> PCC_CLKCFG_PCS_SHIFT)
72 #define PCC_FRAC_VAL(reg) (((reg)&PCC_CLKCFG_FRAC_MASK) >> PCC_CLKCFG_FRAC_SHIFT)
73 #define PCC_PCD_VAL(reg) (((reg)&PCC_CLKCFG_PCD_MASK) >> PCC_CLKCFG_PCD_SHIFT)
74
75 /*******************************************************************************
76 * Variables
77 ******************************************************************************/
78
79 /* External XTAL0 (OSC0) clock frequency. */
80 volatile uint32_t g_xtal0Freq;
81
82 /*******************************************************************************
83 * Prototypes
84 ******************************************************************************/
85
86 /*!
87 * @brief Get the common System PLL frequency for both RAW SPLL output and SPLL PFD output.
88 *
89 * The "raw" SPLL output is the clkout divided by postdiv1-2 of SAPLL.
90 * The "common" System PLL frequency is the common part for both RAW SPLL and SPLL PFD output.
91 * That is the frequency calculated based on the clock source which passes through POSTDIV & MULT.
92 * "Common" SPLL Frequency = Divided Reference Frequency * MULT
93 *
94 * @return Clock frequency; If the clock is invalid, returns 0.
95 */
96 static uint32_t CLOCK_GetSysPllCommonFreq(void);
97
98 /*******************************************************************************
99 * Code
100 ******************************************************************************/
101
102 /*!
103 * brief Get the external reference clock frequency (ERCLK).
104 *
105 * return Clock frequency in Hz.
106 */
CLOCK_GetErClkFreq(void)107 uint32_t CLOCK_GetErClkFreq(void)
108 {
109 uint32_t freq;
110
111 if ((SCG->SOSCCSR & SCG_SOSCCSR_SOSCEN_MASK) != 0UL)
112 {
113 /* Please call CLOCK_SetXtal0Freq base on board setting before using OSC0 clock. */
114 assert(g_xtal0Freq);
115 freq = g_xtal0Freq;
116 }
117 else
118 {
119 freq = 0U;
120 }
121
122 return freq;
123 }
124
125 /*!
126 * brief Get the OSC 32K clock frequency (OSC32KCLK).
127 *
128 * return Clock frequency in Hz.
129 */
CLOCK_GetOsc32kClkFreq(void)130 uint32_t CLOCK_GetOsc32kClkFreq(void)
131 {
132 return (CLOCK_GetErClkFreq() == 32768U) ? 32768U : 0U;
133 }
134
135 /*!
136 * brief Get the flash clock frequency.
137 *
138 * return Clock frequency in Hz.
139 */
CLOCK_GetFlashClkFreq(void)140 uint32_t CLOCK_GetFlashClkFreq(void)
141 {
142 return CLOCK_GetSysClkFreq(kSCG_SysClkSlow);
143 }
144
145 /*!
146 * brief Get the bus clock frequency.
147 *
148 * return Clock frequency in Hz.
149 */
CLOCK_GetBusClkFreq(void)150 uint32_t CLOCK_GetBusClkFreq(void)
151 {
152 return CLOCK_GetSysClkFreq(kSCG_SysClkSlow);
153 }
154
155 /*!
156 * brief Get the platform clock frequency.
157 *
158 * return Clock frequency in Hz.
159 */
CLOCK_GetPlatClkFreq(void)160 uint32_t CLOCK_GetPlatClkFreq(void)
161 {
162 return CLOCK_GetSysClkFreq(kSCG_SysClkCore);
163 }
164
165 /*!
166 * brief Get the core clock or system clock frequency.
167 *
168 * return Clock frequency in Hz.
169 */
CLOCK_GetCoreSysClkFreq(void)170 uint32_t CLOCK_GetCoreSysClkFreq(void)
171 {
172 return CLOCK_GetSysClkFreq(kSCG_SysClkCore);
173 }
174
175 /*!
176 * brief Gets the clock frequency for a specific clock name.
177 *
178 * This function checks the current clock configurations and then calculates
179 * the clock frequency for a specific clock name defined in clock_name_t.
180 *
181 * param clockName Clock names defined in clock_name_t
182 * return Clock frequency value in hertz
183 */
CLOCK_GetFreq(clock_name_t clockName)184 uint32_t CLOCK_GetFreq(clock_name_t clockName)
185 {
186 uint32_t freq;
187
188 switch (clockName)
189 {
190 case kCLOCK_CoreSysClk:
191 case kCLOCK_PlatClk:
192 freq = CLOCK_GetSysClkFreq(kSCG_SysClkCore);
193 break;
194 case kCLOCK_BusClk:
195 case kCLOCK_FlashClk:
196 freq = CLOCK_GetSysClkFreq(kSCG_SysClkSlow);
197 break;
198 case kCLOCK_ScgSysOscClk:
199 freq = CLOCK_GetSysOscFreq();
200 break;
201 case kCLOCK_ScgSircClk:
202 freq = CLOCK_GetSircFreq();
203 break;
204 case kCLOCK_ScgFircClk:
205 freq = CLOCK_GetFircFreq();
206 break;
207 case kCLOCK_ScgSysPllClk:
208 freq = CLOCK_GetSysPllFreq();
209 break;
210 case kCLOCK_ScgSysOscAsyncDiv1Clk:
211 freq = CLOCK_GetSysOscAsyncFreq(kSCG_AsyncDiv1Clk);
212 break;
213 case kCLOCK_ScgSysOscAsyncDiv2Clk:
214 freq = CLOCK_GetSysOscAsyncFreq(kSCG_AsyncDiv2Clk);
215 break;
216 case kCLOCK_ScgSysOscAsyncDiv3Clk:
217 freq = CLOCK_GetSysOscAsyncFreq(kSCG_AsyncDiv3Clk);
218 break;
219 case kCLOCK_ScgSircAsyncDiv1Clk:
220 freq = CLOCK_GetSircAsyncFreq(kSCG_AsyncDiv1Clk);
221 break;
222 case kCLOCK_ScgSircAsyncDiv2Clk:
223 freq = CLOCK_GetSircAsyncFreq(kSCG_AsyncDiv2Clk);
224 break;
225 case kCLOCK_ScgSircAsyncDiv3Clk:
226 freq = CLOCK_GetSircAsyncFreq(kSCG_AsyncDiv3Clk);
227 break;
228 case kCLOCK_ScgFircAsyncDiv1Clk:
229 freq = CLOCK_GetFircAsyncFreq(kSCG_AsyncDiv1Clk);
230 break;
231 case kCLOCK_ScgFircAsyncDiv2Clk:
232 freq = CLOCK_GetFircAsyncFreq(kSCG_AsyncDiv2Clk);
233 break;
234 case kCLOCK_ScgFircAsyncDiv3Clk:
235 freq = CLOCK_GetFircAsyncFreq(kSCG_AsyncDiv3Clk);
236 break;
237 case kCLOCK_ScgSysPllAsyncDiv1Clk:
238 freq = CLOCK_GetSysPllAsyncFreq(kSCG_AsyncDiv1Clk);
239 break;
240 case kCLOCK_ScgSysPllAsyncDiv2Clk:
241 freq = CLOCK_GetSysPllAsyncFreq(kSCG_AsyncDiv2Clk);
242 break;
243 case kCLOCK_ScgSysPllAsyncDiv3Clk:
244 freq = CLOCK_GetSysPllAsyncFreq(kSCG_AsyncDiv3Clk);
245 break;
246 case kCLOCK_LpoClk:
247 freq = LPO_CLK_FREQ;
248 break;
249 case kCLOCK_Osc32kClk:
250 freq = (CLOCK_GetErClkFreq() == 32768U) ? 32768U : 0U;
251 break;
252 case kCLOCK_ErClk:
253 freq = CLOCK_GetErClkFreq();
254 break;
255 default:
256 freq = 0U;
257 break;
258 }
259 return freq;
260 }
261
262 /*!
263 * brief Gets the clock frequency for a specific IP module.
264 *
265 * This function gets the IP module clock frequency based on PCC registers. It is
266 * only used for the IP modules which could select clock source by PCC[PCS].
267 *
268 * param name Which peripheral to get, see \ref clock_ip_name_t.
269 * return Clock frequency value in hertz
270 */
CLOCK_GetIpFreq(clock_ip_name_t name)271 uint32_t CLOCK_GetIpFreq(clock_ip_name_t name)
272 {
273 uint32_t reg = (*(volatile uint32_t *)(uint32_t)name);
274
275 scg_async_clk_t asycClk;
276 uint32_t freq;
277 uint32_t ret;
278
279 assert(reg & PCC_CLKCFG_PR_MASK);
280
281 /* USB uses SCG DIV1 clock, others use SCG DIV3 clock. */
282 if (kCLOCK_Usbfs0 == name)
283 {
284 asycClk = kSCG_AsyncDiv1Clk;
285 }
286 else
287 {
288 asycClk = kSCG_AsyncDiv3Clk;
289 }
290
291 switch (PCC_PCS_VAL(reg))
292 {
293 case (uint32_t)kCLOCK_IpSrcSysOscAsync:
294 freq = CLOCK_GetSysOscAsyncFreq(asycClk);
295 break;
296 case (uint32_t)kCLOCK_IpSrcSircAsync:
297 freq = CLOCK_GetSircAsyncFreq(asycClk);
298 break;
299 case (uint32_t)kCLOCK_IpSrcFircAsync:
300 freq = CLOCK_GetFircAsyncFreq(asycClk);
301 break;
302 case (uint32_t)kCLOCK_IpSrcSysPllAsync:
303 freq = CLOCK_GetSysPllAsyncFreq(asycClk);
304 break;
305 default:
306 freq = 0U;
307 break;
308 }
309
310 if ((reg & (PCC_CLKCFG_PCD_MASK | PCC_CLKCFG_FRAC_MASK)) != 0UL)
311 {
312 ret = freq * (PCC_FRAC_VAL(reg) + 1U) / (PCC_PCD_VAL(reg) + 1U);
313 }
314 else
315 {
316 ret = freq;
317 }
318
319 return ret;
320 }
321
322 /*! brief Enable USB FS clock.
323 *
324 * param src USB FS clock source.
325 * param freq The frequency specified by src.
326 * retval true The clock is set successfully.
327 * retval false The clock source is invalid to get proper USB FS clock.
328 */
CLOCK_EnableUsbfs0Clock(clock_ip_src_t src,uint32_t freq)329 bool CLOCK_EnableUsbfs0Clock(clock_ip_src_t src, uint32_t freq)
330 {
331 bool ret = true;
332
333 CLOCK_DisableClock(kCLOCK_Usbfs0);
334
335 if (kCLOCK_IpSrcNoneOrExt == src)
336 {
337 CLOCK_SetIpSrc(kCLOCK_Usbfs0, kCLOCK_IpSrcNoneOrExt);
338 }
339 else
340 {
341 switch (freq)
342 {
343 case 120000000U:
344 CLOCK_SetIpSrcDiv(kCLOCK_Usbfs0, src, 4U, 1U);
345 break;
346 case 96000000U:
347 CLOCK_SetIpSrcDiv(kCLOCK_Usbfs0, src, 1U, 0U);
348 break;
349 case 72000000U:
350 CLOCK_SetIpSrcDiv(kCLOCK_Usbfs0, src, 2U, 1U);
351 break;
352 case 48000000U:
353 CLOCK_SetIpSrcDiv(kCLOCK_Usbfs0, src, 0U, 0U);
354 break;
355 default:
356 ret = false;
357 break;
358 }
359 }
360
361 CLOCK_EnableClock(kCLOCK_Usbfs0);
362
363 return ret;
364 }
365
366 /*!
367 * brief Gets the SCG system clock frequency.
368 *
369 * This function gets the SCG system clock frequency. These clocks are used for
370 * core, platform, external, and bus clock domains.
371 *
372 * param type Which type of clock to get, core clock or slow clock.
373 * return Clock frequency.
374 */
CLOCK_GetSysClkFreq(scg_sys_clk_t type)375 uint32_t CLOCK_GetSysClkFreq(scg_sys_clk_t type)
376 {
377 uint32_t freq;
378
379 scg_sys_clk_config_t sysClkConfig;
380
381 CLOCK_GetCurSysClkConfig(&sysClkConfig); /* Get the main clock for SoC platform. */
382
383 switch (sysClkConfig.src)
384 {
385 case (uint8_t)kSCG_SysClkSrcSysOsc:
386 freq = CLOCK_GetSysOscFreq();
387 break;
388 case (uint8_t)kSCG_SysClkSrcSirc:
389 freq = CLOCK_GetSircFreq();
390 break;
391 case (uint8_t)kSCG_SysClkSrcFirc:
392 freq = CLOCK_GetFircFreq();
393 break;
394 case (uint8_t)kSCG_SysClkSrcSysPll:
395 freq = CLOCK_GetSysPllFreq();
396 break;
397 default:
398 freq = 0U;
399 break;
400 }
401
402 freq /= ((uint32_t)sysClkConfig.divCore + 1UL); /* divided by the DIVCORE firstly. */
403
404 if (kSCG_SysClkSlow == type)
405 {
406 freq /= ((uint32_t)sysClkConfig.divSlow + 1UL);
407 }
408 else
409 {
410 /* Add comment to prevent the case of MISRA C-2012 rule 15.7 */
411 }
412
413 return freq;
414 }
415
416 /*!
417 * brief Initializes the SCG system OSC.
418 *
419 * This function enables the SCG system OSC clock according to the
420 * configuration.
421 *
422 * param config Pointer to the configuration structure.
423 * retval kStatus_Success System OSC is initialized.
424 * retval kStatus_SCG_Busy System OSC has been enabled and is used by the system clock.
425 * retval kStatus_ReadOnly System OSC control register is locked.
426 *
427 * note This function can't detect whether the system OSC has been enabled and
428 * used by an IP.
429 */
CLOCK_InitSysOsc(const scg_sosc_config_t * config)430 status_t CLOCK_InitSysOsc(const scg_sosc_config_t *config)
431 {
432 assert(config);
433 uint8_t range = 0U; /* SCG_SOSCCFG[RANGE] */
434 status_t status;
435 uint8_t tmp8;
436
437 /* If crystal oscillator used, need to get RANGE value base on frequency. */
438 if (kSCG_SysOscModeExt != config->workMode)
439 {
440 if ((config->freq >= 32768U) && (config->freq <= 40000U))
441 {
442 range = 1U;
443 }
444 else if ((config->freq >= 1000000U) && (config->freq <= 8000000U))
445 {
446 range = 2U;
447 }
448 else if ((config->freq >= 8000000U) && (config->freq <= 32000000U))
449 {
450 range = 3U;
451 }
452 else
453 {
454 return kStatus_InvalidArgument;
455 }
456 }
457
458 /* De-init the SOSC first. */
459 status = CLOCK_DeinitSysOsc();
460
461 if (kStatus_Success != status)
462 {
463 return status;
464 }
465
466 /* Now start to set up OSC clock. */
467 /* Step 1. Setup dividers. */
468 SCG->SOSCDIV =
469 SCG_SOSCDIV_SOSCDIV1(config->div1) | SCG_SOSCDIV_SOSCDIV2(config->div2) | SCG_SOSCDIV_SOSCDIV3(config->div3);
470
471 /* Step 2. Set OSC configuration. */
472 SCG->SOSCCFG = config->capLoad | (uint32_t)config->workMode | SCG_SOSCCFG_RANGE(range);
473
474 /* Step 3. Enable clock. */
475 /* SCG->SOSCCSR = SCG_SOSCCSR_SOSCEN_MASK | (config->enableMode); */
476 tmp8 = config->enableMode;
477 tmp8 |= SCG_SOSCCSR_SOSCEN_MASK;
478 SCG->SOSCCSR = tmp8;
479
480 /* Step 4. Wait for OSC clock to be valid. */
481 while (0UL == (SCG->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK))
482 {
483 }
484
485 /* Step 5. Enabe monitor. */
486 SCG->SOSCCSR |= (uint32_t)config->monitorMode;
487
488 return kStatus_Success;
489 }
490
491 /*!
492 * brief De-initializes the SCG system OSC.
493 *
494 * This function disables the SCG system OSC clock.
495 *
496 * retval kStatus_Success System OSC is deinitialized.
497 * retval kStatus_SCG_Busy System OSC is used by the system clock.
498 * retval kStatus_ReadOnly System OSC control register is locked.
499 *
500 * note This function can't detect whether the system OSC is used by an IP.
501 */
CLOCK_DeinitSysOsc(void)502 status_t CLOCK_DeinitSysOsc(void)
503 {
504 uint32_t reg = SCG->SOSCCSR;
505 status_t status;
506
507 /* If clock is used by system, return error. */
508 if ((reg & SCG_SOSCCSR_SOSCSEL_MASK) != 0UL)
509 {
510 status = kStatus_SCG_Busy;
511 }
512
513 /* If configure register is locked, return error. */
514 else if ((reg & SCG_SOSCCSR_LK_MASK) != 0UL)
515 {
516 status = kStatus_ReadOnly;
517 }
518 else
519 {
520 SCG->SOSCCSR = SCG_SOSCCSR_SOSCERR_MASK;
521 status = kStatus_Success;
522 }
523
524 return status;
525 }
526
527 /*!
528 * brief Gets the SCG system OSC clock frequency (SYSOSC).
529 *
530 * return Clock frequency; If the clock is invalid, returns 0.
531 */
CLOCK_GetSysOscFreq(void)532 uint32_t CLOCK_GetSysOscFreq(void)
533 {
534 uint32_t freq;
535
536 if ((SCG->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK) != 0UL) /* System OSC clock is valid. */
537 {
538 /* Please call CLOCK_SetXtal0Freq base on board setting before using OSC0 clock. */
539 assert(g_xtal0Freq);
540 freq = g_xtal0Freq;
541 }
542 else
543 {
544 freq = 0U;
545 }
546
547 return freq;
548 }
549
550 /*!
551 * brief Gets the SCG asynchronous clock frequency from the system OSC.
552 *
553 * param type The asynchronous clock type.
554 * return Clock frequency; If the clock is invalid, returns 0.
555 */
CLOCK_GetSysOscAsyncFreq(scg_async_clk_t type)556 uint32_t CLOCK_GetSysOscAsyncFreq(scg_async_clk_t type)
557 {
558 uint32_t oscFreq = CLOCK_GetSysOscFreq();
559 uint32_t divider = 0U;
560 uint32_t freq;
561
562 /* Get divider. */
563 if (oscFreq != 0UL)
564 {
565 switch (type)
566 {
567 case kSCG_AsyncDiv3Clk: /* SOSCDIV3_CLK. */
568 divider = SCG_SOSCDIV_SOSCDIV3_VAL;
569 break;
570 case kSCG_AsyncDiv2Clk: /* SOSCDIV2_CLK. */
571 divider = SCG_SOSCDIV_SOSCDIV2_VAL;
572 break;
573 case kSCG_AsyncDiv1Clk: /* SOSCDIV1_CLK. */
574 divider = SCG_SOSCDIV_SOSCDIV1_VAL;
575 break;
576 default:
577 divider = 0U;
578 break;
579 }
580 }
581 if (divider != 0U)
582 {
583 freq = (oscFreq >> (divider - 1U));
584 }
585 else /* Output disabled. */
586 {
587 freq = 0U;
588 }
589
590 return freq;
591 }
592
593 /*!
594 * brief Initializes the SCG slow IRC clock.
595 *
596 * This function enables the SCG slow IRC clock according to the
597 * configuration.
598 *
599 * param config Pointer to the configuration structure.
600 * retval kStatus_Success SIRC is initialized.
601 * retval kStatus_SCG_Busy SIRC has been enabled and is used by system clock.
602 * retval kStatus_ReadOnly SIRC control register is locked.
603 *
604 * note This function can't detect whether the system OSC has been enabled and
605 * used by an IP.
606 */
CLOCK_InitSirc(const scg_sirc_config_t * config)607 status_t CLOCK_InitSirc(const scg_sirc_config_t *config)
608 {
609 assert(config);
610
611 status_t status;
612
613 /* De-init the SIRC first. */
614 status = CLOCK_DeinitSirc();
615
616 if (status == kStatus_Success)
617 {
618 /* Now start to set up SIRC clock. */
619 /* Step 1. Setup dividers. */
620 SCG->SIRCDIV = SCG_SIRCDIV_SIRCDIV1(config->div1) | SCG_SIRCDIV_SIRCDIV2(config->div2) |
621 SCG_SIRCDIV_SIRCDIV3(config->div3);
622
623 /* Step 2. Set SIRC configuration. */
624 SCG->SIRCCFG = SCG_SIRCCFG_RANGE(config->range);
625
626 /* Step 3. Enable clock. */
627 SCG->SIRCCSR = SCG_SIRCCSR_SIRCEN_MASK | config->enableMode;
628
629 /* Step 4. Wait for SIRC clock to be valid. */
630 while (0UL == (SCG->SIRCCSR & SCG_SIRCCSR_SIRCVLD_MASK))
631 {
632 }
633 }
634
635 return status;
636 }
637
638 /*!
639 * brief De-initializes the SCG slow IRC.
640 *
641 * This function disables the SCG slow IRC.
642 *
643 * retval kStatus_Success SIRC is deinitialized.
644 * retval kStatus_SCG_Busy SIRC is used by system clock.
645 * retval kStatus_ReadOnly SIRC control register is locked.
646 *
647 * note This function can't detect whether the SIRC is used by an IP.
648 */
CLOCK_DeinitSirc(void)649 status_t CLOCK_DeinitSirc(void)
650 {
651 uint32_t reg = SCG->SIRCCSR;
652 status_t status;
653
654 /* If clock is used by system, return error. */
655 if ((reg & SCG_SIRCCSR_SIRCSEL_MASK) != 0UL)
656 {
657 status = kStatus_SCG_Busy;
658 }
659 /* If configure register is locked, return error. */
660 else if ((reg & SCG_SIRCCSR_LK_MASK) != 0UL)
661 {
662 status = kStatus_ReadOnly;
663 }
664 else
665 {
666 SCG->SIRCCSR = 0U;
667 status = kStatus_Success;
668 }
669
670 return status;
671 }
672
673 /*!
674 * brief Gets the SCG SIRC clock frequency.
675 *
676 * return Clock frequency; If the clock is invalid, returns 0.
677 */
CLOCK_GetSircFreq(void)678 uint32_t CLOCK_GetSircFreq(void)
679 {
680 static const uint32_t sircFreq[] = {SCG_SIRC_LOW_RANGE_FREQ, SCG_SIRC_HIGH_RANGE_FREQ};
681 uint32_t freq;
682
683 if ((SCG->SIRCCSR & SCG_SIRCCSR_SIRCVLD_MASK) != 0UL) /* SIRC is valid. */
684 {
685 freq = sircFreq[SCG_SIRCCFG_RANGE_VAL];
686 }
687 else
688 {
689 freq = 0U;
690 }
691
692 return freq;
693 }
694
695 /*!
696 * brief Gets the SCG asynchronous clock frequency from the SIRC.
697 *
698 * param type The asynchronous clock type.
699 * return Clock frequency; If the clock is invalid, returns 0.
700 */
CLOCK_GetSircAsyncFreq(scg_async_clk_t type)701 uint32_t CLOCK_GetSircAsyncFreq(scg_async_clk_t type)
702 {
703 uint32_t sircFreq = CLOCK_GetSircFreq();
704 uint32_t divider = 0U;
705 uint32_t freq;
706
707 /* Get divider. */
708 if (sircFreq != 0UL)
709 {
710 switch (type)
711 {
712 case kSCG_AsyncDiv3Clk: /* SIRCDIV3_CLK. */
713 divider = SCG_SIRCDIV_SIRCDIV3_VAL;
714 break;
715 case kSCG_AsyncDiv2Clk: /* SIRCDIV2_CLK. */
716 divider = SCG_SIRCDIV_SIRCDIV2_VAL;
717 break;
718 case kSCG_AsyncDiv1Clk: /* SIRCDIV2_CLK. */
719 divider = SCG_SIRCDIV_SIRCDIV1_VAL;
720 break;
721 default:
722 divider = 0U;
723 break;
724 }
725 }
726 if (divider != 0UL)
727 {
728 freq = (sircFreq >> (divider - 1U));
729 }
730 else /* Output disabled. */
731 {
732 freq = 0U;
733 }
734
735 return freq;
736 }
737
738 /*!
739 * brief Initializes the SCG fast IRC clock.
740 *
741 * This function enables the SCG fast IRC clock according to the configuration.
742 *
743 * param config Pointer to the configuration structure.
744 * retval kStatus_Success FIRC is initialized.
745 * retval kStatus_SCG_Busy FIRC has been enabled and is used by the system clock.
746 * retval kStatus_ReadOnly FIRC control register is locked.
747 *
748 * note This function can't detect whether the FIRC has been enabled and
749 * used by an IP.
750 */
CLOCK_InitFirc(const scg_firc_config_t * config)751 status_t CLOCK_InitFirc(const scg_firc_config_t *config)
752 {
753 assert(config);
754
755 status_t status;
756
757 /* De-init the FIRC first. */
758 status = CLOCK_DeinitFirc();
759
760 if (kStatus_Success != status)
761 {
762 return status;
763 }
764
765 /* Now start to set up FIRC clock. */
766 /* Step 1. Setup dividers. */
767 SCG->FIRCDIV =
768 SCG_FIRCDIV_FIRCDIV1(config->div1) | SCG_FIRCDIV_FIRCDIV2(config->div2) | SCG_FIRCDIV_FIRCDIV3(config->div3);
769
770 /* Step 2. Set FIRC configuration. */
771 SCG->FIRCCFG = SCG_FIRCCFG_RANGE(config->range);
772
773 /* Step 3. Set trimming configuration. */
774 if ((config->trimConfig) != NULL)
775 {
776 SCG->FIRCTCFG =
777 SCG_FIRCTCFG_TRIMDIV(config->trimConfig->trimDiv) | SCG_FIRCTCFG_TRIMSRC(config->trimConfig->trimSrc);
778
779 /* TODO: Write FIRCSTAT cause bus error: TKT266932. */
780 if (kSCG_FircTrimNonUpdate == config->trimConfig->trimMode)
781 {
782 SCG->FIRCSTAT = SCG_FIRCSTAT_TRIMCOAR(config->trimConfig->trimCoar) |
783 SCG_FIRCSTAT_TRIMFINE(config->trimConfig->trimFine);
784 }
785
786 /* trim mode. */
787 SCG->FIRCCSR = (uint32_t)(config->trimConfig->trimMode);
788
789 if ((SCG->FIRCCSR & SCG_FIRCCSR_FIRCERR_MASK) != 0UL)
790 {
791 return kStatus_Fail;
792 }
793 }
794
795 /* Step 4. Enable clock. */
796 SCG->FIRCCSR |= (SCG_FIRCCSR_FIRCEN_MASK | config->enableMode);
797
798 /* Step 5. Wait for FIRC clock to be valid. */
799 while (0UL == (SCG->FIRCCSR & SCG_FIRCCSR_FIRCVLD_MASK))
800 {
801 }
802
803 return kStatus_Success;
804 }
805
806 /*!
807 * brief De-initializes the SCG fast IRC.
808 *
809 * This function disables the SCG fast IRC.
810 *
811 * retval kStatus_Success FIRC is deinitialized.
812 * retval kStatus_SCG_Busy FIRC is used by the system clock.
813 * retval kStatus_ReadOnly FIRC control register is locked.
814 *
815 * note This function can't detect whether the FIRC is used by an IP.
816 */
CLOCK_DeinitFirc(void)817 status_t CLOCK_DeinitFirc(void)
818 {
819 uint32_t reg = SCG->FIRCCSR;
820 status_t status = kStatus_Success;
821
822 /* If clock is used by system, return error. */
823 if ((reg & SCG_FIRCCSR_FIRCSEL_MASK) != 0UL)
824 {
825 status = kStatus_SCG_Busy;
826 }
827 /* If configure register is locked, return error. */
828 else if ((reg & SCG_FIRCCSR_LK_MASK) != 0UL)
829 {
830 status = kStatus_ReadOnly;
831 }
832 else
833 {
834 SCG->FIRCCSR = SCG_FIRCCSR_FIRCERR_MASK;
835 }
836
837 return status;
838 }
839
840 /*!
841 * brief Gets the SCG FIRC clock frequency.
842 *
843 * return Clock frequency; If the clock is invalid, returns 0.
844 */
CLOCK_GetFircFreq(void)845 uint32_t CLOCK_GetFircFreq(void)
846 {
847 uint32_t freq;
848
849 static const uint32_t fircFreq[] = {
850 SCG_FIRC_FREQ0,
851 SCG_FIRC_FREQ1,
852 SCG_FIRC_FREQ2,
853 SCG_FIRC_FREQ3,
854 };
855
856 if ((SCG->FIRCCSR & SCG_FIRCCSR_FIRCVLD_MASK) != 0UL) /* FIRC is valid. */
857 {
858 freq = fircFreq[SCG_FIRCCFG_RANGE_VAL];
859 }
860 else
861 {
862 freq = 0U;
863 }
864
865 return freq;
866 }
867
868 /*!
869 * brief Gets the SCG asynchronous clock frequency from the FIRC.
870 *
871 * param type The asynchronous clock type.
872 * return Clock frequency; If the clock is invalid, returns 0.
873 */
CLOCK_GetFircAsyncFreq(scg_async_clk_t type)874 uint32_t CLOCK_GetFircAsyncFreq(scg_async_clk_t type)
875 {
876 uint32_t fircFreq = CLOCK_GetFircFreq();
877 uint32_t divider = 0U;
878 uint32_t freq;
879
880 /* Get divider. */
881 if (fircFreq != 0UL)
882 {
883 switch (type)
884 {
885 case kSCG_AsyncDiv3Clk: /* FIRCDIV3_CLK. */
886 divider = SCG_FIRCDIV_FIRCDIV3_VAL;
887 break;
888 case kSCG_AsyncDiv2Clk: /* FIRCDIV2_CLK. */
889 divider = SCG_FIRCDIV_FIRCDIV2_VAL;
890 break;
891 case kSCG_AsyncDiv1Clk: /* FIRCDIV1_CLK. */
892 divider = SCG_FIRCDIV_FIRCDIV1_VAL;
893 break;
894 default:
895 divider = 0U;
896 break;
897 }
898 }
899 if (divider != 0U)
900 {
901 freq = (fircFreq >> (divider - 1U));
902 }
903 else /* Output disabled. */
904 {
905 freq = 0U;
906 }
907
908 return freq;
909 }
910
911 /*!
912 * brief Calculates the MULT and PREDIV for the PLL.
913 *
914 * This function calculates the proper MULT and PREDIV to generate the desired PLL
915 * output frequency with the input reference clock frequency. It returns the closest
916 * frequency match that the PLL can generate. The corresponding MULT/PREDIV are returned with
917 * parameters. If the desired frequency is not valid, this function returns 0.
918 *
919 * param refFreq The input reference clock frequency.
920 * param desireFreq The desired output clock frequency.
921 * param mult The value of MULT.
922 * param prediv The value of PREDIV.
923 * return The PLL output frequency with the MULT and PREDIV; If
924 * the desired frequency can't be generated, this function returns 0U.
925 */
CLOCK_GetSysPllMultDiv(uint32_t refFreq,uint32_t desireFreq,uint8_t * mult,uint8_t * prediv)926 uint32_t CLOCK_GetSysPllMultDiv(uint32_t refFreq, uint32_t desireFreq, uint8_t *mult, uint8_t *prediv)
927 {
928 uint8_t ret_prediv; /* PREDIV to return */
929 uint8_t ret_mult; /* MULT to return */
930 uint8_t prediv_min; /* Minimal PREDIV value to make reference clock in allowed range. */
931 uint8_t prediv_max; /* Max PREDIV value to make reference clock in allowed range. */
932 uint8_t prediv_cur; /* PREDIV value for iteration. */
933 uint8_t mult_cur; /* MULT value for iteration. */
934 uint32_t ret_freq = 0U; /* Output frequency to return .*/
935 uint32_t diff = 0xFFFFFFFFU; /* Difference between desireFreq and return frequency. */
936 uint32_t ref_div; /* Reference frequency after PREDIV. */
937
938 /*
939 * Steps:
940 * 1. Get allowed prediv with such rules:
941 * 1). refFreq / prediv >= SCG_PLL_REF_MIN.
942 * 2). refFreq / prediv <= SCG_PLL_REF_MAX.
943 * 2. For each allowed prediv, there are two candidate mult values:
944 * 1). (desireFreq / (refFreq / prediv)).
945 * 2). (desireFreq / (refFreq / prediv)) + 1.
946 * If could get the precise desired frequency, return current prediv and
947 * mult directly. Otherwise choose the one which is closer to desired
948 * frequency.
949 */
950
951 /* Reference frequency is out of range. */
952 if ((refFreq < SCG_SPLL_REF_MIN) ||
953 (refFreq > (SCG_SPLL_REF_MAX * (SCG_SPLL_PREDIV_MAX_VALUE + SCG_SPLL_PREDIV_BASE_VALUE))))
954 {
955 return 0U;
956 }
957
958 /* refFreq/PREDIV must in a range. First get the allowed PREDIV range. */
959 prediv_max = (uint8_t)(refFreq / SCG_SPLL_REF_MIN);
960 prediv_min = (uint8_t)((refFreq + SCG_SPLL_REF_MAX - 1UL) / SCG_SPLL_REF_MAX);
961
962 desireFreq *= 2U;
963
964 /* PREDIV traversal. */
965 for (prediv_cur = prediv_max; prediv_cur >= prediv_min; prediv_cur--)
966 {
967 /*
968 * For each PREDIV, the available MULT is (desireFreq*PREDIV/refFreq)
969 * or (desireFreq*PREDIV/refFreq + 1U). This function chooses the closer
970 * one.
971 */
972 /* Reference frequency after PREDIV. */
973 ref_div = refFreq / prediv_cur;
974
975 mult_cur = (uint8_t)(desireFreq / ref_div);
976
977 if ((mult_cur < SCG_SPLL_MULT_BASE_VALUE - 1U) ||
978 (mult_cur > SCG_SPLL_MULT_BASE_VALUE + SCG_SPLL_MULT_MAX_VALUE))
979 {
980 /* No MULT is available with this PREDIV. */
981 continue;
982 }
983
984 ret_freq = mult_cur * ref_div;
985
986 if (mult_cur >= SCG_SPLL_MULT_BASE_VALUE)
987 {
988 if (ret_freq == desireFreq) /* If desire frequency is got. */
989 {
990 *prediv = prediv_cur - SCG_SPLL_PREDIV_BASE_VALUE;
991 *mult = mult_cur - SCG_SPLL_MULT_BASE_VALUE;
992 return ret_freq / 2U;
993 }
994 if (diff > desireFreq - ret_freq) /* New PRDIV/VDIV is closer. */
995 {
996 diff = desireFreq - ret_freq;
997 ret_prediv = prediv_cur;
998 ret_mult = mult_cur;
999 }
1000 }
1001 mult_cur++;
1002 if (mult_cur <= (SCG_SPLL_MULT_BASE_VALUE + SCG_SPLL_MULT_MAX_VALUE))
1003 {
1004 ret_freq += ref_div;
1005 if (diff > ret_freq - desireFreq) /* New PRDIV/VDIV is closer. */
1006 {
1007 diff = ret_freq - desireFreq;
1008 ret_prediv = prediv_cur;
1009 ret_mult = mult_cur;
1010 }
1011 }
1012 }
1013
1014 if (0xFFFFFFFFU != diff)
1015 {
1016 /* PREDIV/MULT found. */
1017 *prediv = ret_prediv - SCG_SPLL_PREDIV_BASE_VALUE;
1018 *mult = ret_mult - SCG_SPLL_MULT_BASE_VALUE;
1019 return ((refFreq / ret_prediv) * ret_mult) / 2U;
1020 }
1021 else
1022 {
1023 return 0U; /* No proper PREDIV/MULT found. */
1024 }
1025 }
1026
1027 /*!
1028 * brief Initializes the SCG system PLL.
1029 *
1030 * This function enables the SCG system PLL clock according to the
1031 * configuration. The system PLL can use the system OSC or FIRC as
1032 * the clock source. Ensure that the source clock is valid before
1033 * calling this function.
1034 *
1035 * Example code for initializing SPLL clock output:
1036 * code
1037 * const scg_spll_config_t g_scgSysPllConfig = {.enableMode = kSCG_SysPllEnable,
1038 * .monitorMode = kSCG_SysPllMonitorDisable,
1039 * .div1 = kSCG_AsyncClkDivBy1,
1040 * .div2 = kSCG_AsyncClkDisable,
1041 * .div3 = kSCG_AsyncClkDivBy2,
1042 * .src = kSCG_SysPllSrcFirc,
1043 * .isBypassSelected = false,
1044 * .isPfdSelected = false,
1045 * .prediv = 5U,
1046 * .pfdClkout = kSCG_AuxPllPfd0Clk,
1047 * endcode
1048 *
1049 * param config Pointer to the configuration structure.
1050 * retval kStatus_Success System PLL is initialized.
1051 * retval kStatus_SCG_Busy System PLL has been enabled and is used by the system clock.
1052 * retval kStatus_ReadOnly System PLL control register is locked.
1053 *
1054 * note This function can't detect whether the system PLL has been enabled and
1055 * used by an IP.
1056 */
CLOCK_InitSysPll(const scg_spll_config_t * config)1057 status_t CLOCK_InitSysPll(const scg_spll_config_t *config)
1058 {
1059 assert(config);
1060
1061 status_t status;
1062
1063 /* De-init the SPLL first. */
1064 status = CLOCK_DeinitSysPll();
1065
1066 if (kStatus_Success != status)
1067 {
1068 return status;
1069 }
1070
1071 /* Now start to set up PLL clock. */
1072 /* Step 1. Setup dividers. */
1073 SCG->SPLLDIV =
1074 SCG_SPLLDIV_SPLLDIV1(config->div1) | SCG_SPLLDIV_SPLLDIV2(config->div2) | SCG_SPLLDIV_SPLLDIV3(config->div3);
1075
1076 /* Step 2. Set PLL configuration. */
1077 SCG->SPLLCFG =
1078 SCG_SPLLCFG_SOURCE(config->src) | SCG_SPLLCFG_PREDIV(config->prediv) | SCG_SPLLCFG_MULT(config->mult);
1079
1080 /* Step 3. Enable clock. */
1081 SCG->SPLLCSR = (uint32_t)SCG_SPLLCSR_SPLLEN_MASK | config->enableMode;
1082
1083 /* Step 4. Wait for PLL clock to be valid. */
1084 while (0UL == (SCG->SPLLCSR & SCG_SPLLCSR_SPLLVLD_MASK))
1085 {
1086 }
1087
1088 /* Step 5. Enabe monitor. */
1089 SCG->SPLLCSR |= (uint32_t)config->monitorMode;
1090
1091 return kStatus_Success;
1092 }
1093
1094 /*!
1095 * brief De-initializes the SCG system PLL.
1096 *
1097 * This function disables the SCG system PLL.
1098 *
1099 * retval kStatus_Success system PLL is deinitialized.
1100 * retval kStatus_SCG_Busy system PLL is used by the system clock.
1101 * retval kStatus_ReadOnly System PLL control register is locked.
1102 *
1103 * note This function can't detect whether the system PLL is used by an IP.
1104 */
CLOCK_DeinitSysPll(void)1105 status_t CLOCK_DeinitSysPll(void)
1106 {
1107 uint32_t reg = SCG->SPLLCSR;
1108 status_t status;
1109
1110 /* If clock is used by system, return error. */
1111 if ((reg & SCG_SPLLCSR_SPLLSEL_MASK) != 0UL)
1112 {
1113 status = kStatus_SCG_Busy;
1114 }
1115 /* If configure register is locked, return error. */
1116 else if ((reg & SCG_SPLLCSR_LK_MASK) != 0UL)
1117 {
1118 status = kStatus_ReadOnly;
1119 }
1120 else
1121 {
1122 /* Deinit and clear the error. */
1123 SCG->SPLLCSR = SCG_SPLLCSR_SPLLERR_MASK;
1124 status = kStatus_Success;
1125 }
1126
1127 return status;
1128 }
1129
CLOCK_GetSysPllCommonFreq(void)1130 static uint32_t CLOCK_GetSysPllCommonFreq(void)
1131 {
1132 uint32_t freq = 0U;
1133
1134 if ((SCG->SPLLCFG & SCG_SPLLCFG_SOURCE_MASK) != 0UL) /* If use FIRC */
1135 {
1136 freq = CLOCK_GetFircFreq();
1137 }
1138 else /* Use System OSC. */
1139 {
1140 freq = CLOCK_GetSysOscFreq();
1141 }
1142
1143 if (freq != 0UL) /* If source is valid. */
1144 {
1145 freq /= (SCG_SPLLCFG_PREDIV_VAL + SCG_SPLL_PREDIV_BASE_VALUE); /* Pre-divider. */
1146 freq *= (SCG_SPLLCFG_MULT_VAL + SCG_SPLL_MULT_BASE_VALUE); /* Multiplier. */
1147 }
1148
1149 return freq;
1150 }
1151
1152 /*!
1153 * brief Gets the SCG system PLL clock frequency.
1154 *
1155 * return Clock frequency; If the clock is invalid, returns 0.
1156 */
CLOCK_GetSysPllFreq(void)1157 uint32_t CLOCK_GetSysPllFreq(void)
1158 {
1159 uint32_t freq;
1160
1161 if ((SCG->SPLLCSR & SCG_SPLLCSR_SPLLVLD_MASK) != 0UL) /* System PLL is valid. */
1162 {
1163 freq = CLOCK_GetSysPllCommonFreq();
1164
1165 return freq >> 1U;
1166 }
1167 else
1168 {
1169 return 0U;
1170 }
1171 }
1172
1173 /*!
1174 * brief Gets the SCG asynchronous clock frequency from the system PLL.
1175 *
1176 * param type The asynchronous clock type.
1177 * return Clock frequency; If the clock is invalid, returns 0.
1178 */
CLOCK_GetSysPllAsyncFreq(scg_async_clk_t type)1179 uint32_t CLOCK_GetSysPllAsyncFreq(scg_async_clk_t type)
1180 {
1181 uint32_t pllFreq = CLOCK_GetSysPllFreq();
1182 uint32_t divider = 0U;
1183 uint32_t freq;
1184
1185 /* Get divider. */
1186 if (pllFreq != 0UL)
1187 {
1188 switch (type)
1189 {
1190 case kSCG_AsyncDiv3Clk: /* SPLLDIV3_CLK. */
1191 divider = SCG_SPLLDIV_SPLLDIV3_VAL;
1192 break;
1193 case kSCG_AsyncDiv2Clk: /* SPLLDIV2_CLK. */
1194 divider = SCG_SPLLDIV_SPLLDIV2_VAL;
1195 break;
1196 case kSCG_AsyncDiv1Clk: /* SPLLDIV1_CLK. */
1197 divider = SCG_SPLLDIV_SPLLDIV1_VAL;
1198 break;
1199 default:
1200 divider = 0U;
1201 break;
1202 }
1203 }
1204 if (divider != 0UL)
1205 {
1206 freq = (pllFreq >> (divider - 1U));
1207 }
1208 else /* Output disabled. */
1209 {
1210 freq = 0U;
1211 }
1212
1213 return freq;
1214 }
1215