| /* |
| * Copyright 2023 NXP |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/arch/cpu.h> |
| #include <zephyr/device.h> |
| #include <zephyr/init.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/linker/sections.h> |
| |
| #include <soc.h> |
| |
| #include <fsl_ccm32k.h> |
| #include <fsl_common.h> |
| #include <fsl_clock.h> |
| |
| extern uint32_t SystemCoreClock; |
| |
| static ALWAYS_INLINE void clock_init(void) |
| { |
| /* Unlock Reference Clock Status Registers to allow writes */ |
| CLOCK_UnlockFircControlStatusReg(); |
| CLOCK_UnlockSircControlStatusReg(); |
| CLOCK_UnlockRoscControlStatusReg(); |
| CLOCK_UnlockSysOscControlStatusReg(); |
| |
| /* |
| * Configuration for the 32 kHz Oscillator module |
| * Internal capatitor bank is required in order to use the more stable OSC32K source |
| */ |
| ccm32k_osc_config_t ccm32k_osc_config = { |
| .coarseAdjustment = kCCM32K_OscCoarseAdjustmentRange0, /* ESR_Range0 */ |
| .enableInternalCapBank = true, /* Internal capacitance bank is enabled */ |
| .xtalCap = kCCM32K_OscXtal8pFCap, /* 8 pF */ |
| .extalCap = kCCM32K_OscExtal8pFCap, /* 8 pF */ |
| }; |
| /* Enable OSC32K */ |
| CCM32K_Set32kOscConfig(CCM32K, kCCM32K_Enable32kHzCrystalOsc, &ccm32k_osc_config); |
| /* Disable ROSC Monitor, because switching the source would generate an expected error */ |
| CLOCK_SetRoscMonitorMode(kSCG_RoscMonitorDisable); |
| /* Select the Real Time Clock (RTC) source as OSC32K */ |
| CCM32K_SelectClockSource(CCM32K, kCCM32K_ClockSourceSelectOsc32k); |
| /* Wait for RTC Oscillator to be Valid */ |
| while (!CLOCK_IsRoscValid()) |
| ; |
| /* Re-enable monitor */ |
| CLOCK_SetRoscMonitorMode(kSCG_RoscMonitorInt); |
| /* Disable the FRO32K to save power */ |
| CCM32K_Enable32kFro(CCM32K, false); |
| |
| /* Configuration to set FIRC to maximum frequency */ |
| scg_firc_config_t scg_firc_config = { |
| .enableMode = kSCG_FircEnable, /* Fast IRC is enabled */ |
| .range = kSCG_FircRange96M, /* 96 Mhz FIRC clock selected */ |
| .trimConfig = NULL, |
| }; |
| |
| scg_sys_clk_config_t sys_clk_safe_config_source = { |
| .divSlow = (uint32_t)kSCG_SysClkDivBy4, |
| .divCore = (uint32_t)kSCG_SysClkDivBy1, |
| .src = (uint32_t)kSCG_SysClkSrcSirc, |
| }; |
| |
| CLOCK_SetRunModeSysClkConfig(&sys_clk_safe_config_source); |
| |
| scg_sys_clk_config_t cur_config; |
| |
| do { |
| CLOCK_GetCurSysClkConfig(&cur_config); |
| } while (cur_config.src != sys_clk_safe_config_source.src); |
| |
| (void)CLOCK_InitFirc(&scg_firc_config); |
| |
| scg_sys_clk_config_t sys_clk_config = { |
| .divSlow = (uint32_t)kSCG_SysClkDivBy4, /* Slow Clock Divider: divided by 4 */ |
| .divBus = (uint32_t)kSCG_SysClkDivBy1, /* Bus Clock Divider: divided by 1 */ |
| .divCore = (uint32_t)kSCG_SysClkDivBy1, /* Core Clock Divider: divided by 1 */ |
| .src = (uint32_t)kSCG_SysClkSrcFirc, /* Select Fast IRC as System Clock */ |
| }; |
| CLOCK_SetRunModeSysClkConfig(&sys_clk_config); |
| |
| /* Wait for clock source switch to finish */ |
| do { |
| CLOCK_GetCurSysClkConfig(&cur_config); |
| } while (cur_config.src != sys_clk_config.src); |
| |
| SystemCoreClock = 96000000U; |
| |
| /* OSC-RF / System Oscillator Configuration */ |
| scg_sosc_config_t sosc_config = { |
| .freq = 32000U, |
| .monitorMode = kSCG_SysOscMonitorDisable, |
| .enableMode = kSCG_SoscEnable, |
| }; |
| |
| /* Init OSC-RF / SOSC */ |
| (void)CLOCK_InitSysOsc(&sosc_config); |
| CLOCK_SetXtal0Freq(sosc_config.freq); |
| |
| /* Slow internal reference clock (SIRC) configuration */ |
| scg_sirc_config_t sirc_config = { |
| .enableMode = kSCG_SircDisableInSleep, |
| }; |
| |
| /* Init SIRC */ |
| (void)CLOCK_InitSirc(&sirc_config); |
| |
| /* Attach Clocks */ |
| CLOCK_SetIpSrc(kCLOCK_Lpuart0, kCLOCK_IpSrcFro192M); |
| CLOCK_SetIpSrc(kCLOCK_Lpuart1, kCLOCK_IpSrcFro192M); |
| CLOCK_SetIpSrc(kCLOCK_Lpspi0, kCLOCK_IpSrcFro192M); |
| CLOCK_SetIpSrc(kCLOCK_Lpspi1, kCLOCK_IpSrcFro192M); |
| CLOCK_SetIpSrc(kCLOCK_Can0, kCLOCK_IpSrcFro192M); |
| CLOCK_SetIpSrc(kCLOCK_Tpm0, kCLOCK_IpSrcFro192M); |
| CLOCK_SetIpSrc(kCLOCK_Tpm1, kCLOCK_IpSrcFro192M); |
| CLOCK_SetIpSrc(kCLOCK_Lpi2c0, kCLOCK_IpSrcFro192M); |
| CLOCK_SetIpSrcDiv(kCLOCK_Lpi2c0, kSCG_SysClkDivBy16); |
| CLOCK_SetIpSrc(kCLOCK_Lpi2c1, kCLOCK_IpSrcFro192M); |
| CLOCK_SetIpSrcDiv(kCLOCK_Lpi2c1, kSCG_SysClkDivBy16); |
| CLOCK_SetIpSrc(kCLOCK_Lpspi0, kCLOCK_IpSrcFro192M); |
| CLOCK_SetIpSrc(kCLOCK_Lpspi1, kCLOCK_IpSrcFro192M); |
| CLOCK_SetIpSrc(kCLOCK_Lpadc0, kCLOCK_IpSrcFro192M); |
| CLOCK_SetIpSrcDiv(kCLOCK_Lpadc0, kSCG_SysClkDivBy10); |
| |
| /* Ungate clocks if the peripheral is enabled in devicetree */ |
| if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(lpuart0), nxp_lpc_lpuart, okay)) { |
| CLOCK_EnableClock(kCLOCK_Lpuart0); |
| } |
| |
| if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(lpuart1), nxp_lpc_lpuart, okay)) { |
| CLOCK_EnableClock(kCLOCK_Lpuart1); |
| } |
| |
| if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(tpm0), nxp_kinetis_tpm, okay)) { |
| CLOCK_EnableClock(kCLOCK_Tpm0); |
| } |
| |
| if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(tpm1), nxp_kinetis_tpm, okay)) { |
| CLOCK_EnableClock(kCLOCK_Tpm1); |
| } |
| |
| if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(lpi2c0), nxp_imx_lpi2c, okay)) { |
| CLOCK_EnableClock(kCLOCK_Lpi2c0); |
| } |
| |
| if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(lpi2c1), nxp_imx_lpi2c, okay)) { |
| CLOCK_EnableClock(kCLOCK_Lpi2c1); |
| } |
| |
| if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(lpspi0), nxp_imx_lpspi, okay)) { |
| CLOCK_EnableClock(kCLOCK_Lpspi0); |
| } |
| |
| if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(lpspi1), nxp_imx_lpspi, okay)) { |
| CLOCK_EnableClock(kCLOCK_Lpspi1); |
| } |
| |
| if (IS_ENABLED(CONFIG_CAN_MCUX_FLEXCAN)) { |
| CLOCK_EnableClock(kCLOCK_Can0); |
| } |
| |
| if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(vref), nxp_vref, okay)) { |
| CLOCK_EnableClock(kCLOCK_Vref0); |
| } |
| |
| if (DT_NODE_HAS_COMPAT_STATUS(adc0, nxp_lpadc, okay)) { |
| CLOCK_EnableClock(kCLOCK_Lpadc0); |
| } |
| } |
| |
| static void vbat_init(void) |
| { |
| VBAT_Type *base = (VBAT_Type *)DT_REG_ADDR(DT_NODELABEL(vbat)); |
| |
| /* Write 1 to Clear POR detect status bit. |
| * |
| * Clearing this bit is acknowledement |
| * that software has recognized a power on reset. |
| * |
| * This avoids also niche issues with NVIC read/write |
| * when searching for available interrupt lines. |
| */ |
| base->STATUSA |= VBAT_STATUSA_POR_DET_MASK; |
| }; |
| |
| void soc_early_init_hook(void) |
| { |
| unsigned int oldLevel; /* old interrupt lock level */ |
| |
| /* disable interrupts */ |
| oldLevel = irq_lock(); |
| |
| /* Initialize system clock to 96 MHz */ |
| clock_init(); |
| |
| /* Smart power switch initialization */ |
| vbat_init(); |
| |
| /* restore interrupt state */ |
| irq_unlock(oldLevel); |
| } |