|
| 1 | +/* |
| 2 | + * Copyright 2023 NXP |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#include <zephyr/arch/cpu.h> |
| 8 | +#include <zephyr/device.h> |
| 9 | +#include <zephyr/init.h> |
| 10 | +#include <zephyr/kernel.h> |
| 11 | +#include <zephyr/linker/sections.h> |
| 12 | + |
| 13 | +#include <soc.h> |
| 14 | + |
| 15 | +#include <fsl_ccm32k.h> |
| 16 | +#include <fsl_common.h> |
| 17 | +#include <fsl_clock.h> |
| 18 | + |
| 19 | +extern uint32_t SystemCoreClock; |
| 20 | + |
| 21 | +static ALWAYS_INLINE void clock_init(void) |
| 22 | +{ |
| 23 | + /* Unlock Reference Clock Status Registers to allow writes */ |
| 24 | + CLOCK_UnlockFircControlStatusReg(); |
| 25 | + CLOCK_UnlockSircControlStatusReg(); |
| 26 | + CLOCK_UnlockRoscControlStatusReg(); |
| 27 | + CLOCK_UnlockSysOscControlStatusReg(); |
| 28 | + |
| 29 | + /* |
| 30 | + * Configuration for the 32 kHz Oscillator module |
| 31 | + * Internal capatitor bank is required in order to use the more stable OSC32K source |
| 32 | + */ |
| 33 | + ccm32k_osc_config_t ccm32k_osc_config = { |
| 34 | + .coarseAdjustment = kCCM32K_OscCoarseAdjustmentRange0, /* ESR_Range0 */ |
| 35 | + .enableInternalCapBank = true, /* Internal capacitance bank is enabled */ |
| 36 | + .xtalCap = kCCM32K_OscXtal8pFCap, /* 8 pF */ |
| 37 | + .extalCap = kCCM32K_OscExtal8pFCap, /* 8 pF */ |
| 38 | + }; |
| 39 | + /* Enable OSC32K */ |
| 40 | + CCM32K_Set32kOscConfig(CCM32K, kCCM32K_Enable32kHzCrystalOsc, &ccm32k_osc_config); |
| 41 | + /* Disable ROSC Monitor, because switching the source would generate an expected error */ |
| 42 | + CLOCK_SetRoscMonitorMode(kSCG_RoscMonitorDisable); |
| 43 | + /* Select the Real Time Clock (RTC) source as OSC32K */ |
| 44 | + CCM32K_SelectClockSource(CCM32K, kCCM32K_ClockSourceSelectOsc32k); |
| 45 | + /* Wait for RTC Oscillator to be Valid */ |
| 46 | + while (!CLOCK_IsRoscValid()) |
| 47 | + ; |
| 48 | + /* Re-enable monitor */ |
| 49 | + CLOCK_SetRoscMonitorMode(kSCG_RoscMonitorInt); |
| 50 | + /* Disable the FRO32K to save power */ |
| 51 | + CCM32K_Enable32kFro(CCM32K, false); |
| 52 | + |
| 53 | + /* Configuration to set FIRC to maximum frequency */ |
| 54 | + scg_firc_config_t scg_firc_config = { |
| 55 | + .enableMode = kSCG_FircEnable, /* Fast IRC is enabled */ |
| 56 | + .range = kSCG_FircRange96M, /* 96 Mhz FIRC clock selected */ |
| 57 | + .trimConfig = NULL, |
| 58 | + }; |
| 59 | + |
| 60 | + scg_sys_clk_config_t sys_clk_safe_config_source = { |
| 61 | + .divSlow = (uint32_t)kSCG_SysClkDivBy4, |
| 62 | + .divCore = (uint32_t)kSCG_SysClkDivBy1, |
| 63 | + .src = (uint32_t)kSCG_SysClkSrcSirc, |
| 64 | + }; |
| 65 | + |
| 66 | + CLOCK_SetRunModeSysClkConfig(&sys_clk_safe_config_source); |
| 67 | + |
| 68 | + scg_sys_clk_config_t cur_config; |
| 69 | + |
| 70 | + do { |
| 71 | + CLOCK_GetCurSysClkConfig(&cur_config); |
| 72 | + } while (cur_config.src != sys_clk_safe_config_source.src); |
| 73 | + |
| 74 | + (void)CLOCK_InitFirc(&scg_firc_config); |
| 75 | + |
| 76 | + scg_sys_clk_config_t sys_clk_config = { |
| 77 | + .divSlow = (uint32_t)kSCG_SysClkDivBy4, /* Slow Clock Divider: divided by 4 */ |
| 78 | + .divBus = (uint32_t)kSCG_SysClkDivBy1, /* Bus Clock Divider: divided by 1 */ |
| 79 | + .divCore = (uint32_t)kSCG_SysClkDivBy1, /* Core Clock Divider: divided by 1 */ |
| 80 | + .src = (uint32_t)kSCG_SysClkSrcFirc, /* Select Fast IRC as System Clock */ |
| 81 | + }; |
| 82 | + CLOCK_SetRunModeSysClkConfig(&sys_clk_config); |
| 83 | + |
| 84 | + /* Wait for clock source switch to finish */ |
| 85 | + do { |
| 86 | + CLOCK_GetCurSysClkConfig(&cur_config); |
| 87 | + } while (cur_config.src != sys_clk_config.src); |
| 88 | + |
| 89 | + SystemCoreClock = 96000000U; |
| 90 | + |
| 91 | + /* OSC-RF / System Oscillator Configuration */ |
| 92 | + scg_sosc_config_t sosc_config = { |
| 93 | + .freq = 32000U, |
| 94 | + .monitorMode = kSCG_SysOscMonitorDisable, |
| 95 | + .enableMode = kSCG_SoscEnable, |
| 96 | + }; |
| 97 | + |
| 98 | + /* Init OSC-RF / SOSC */ |
| 99 | + (void)CLOCK_InitSysOsc(&sosc_config); |
| 100 | + CLOCK_SetXtal0Freq(sosc_config.freq); |
| 101 | + |
| 102 | + /* Slow internal reference clock (SIRC) configuration */ |
| 103 | + scg_sirc_config_t sirc_config = { |
| 104 | + .enableMode = kSCG_SircDisableInSleep, |
| 105 | + }; |
| 106 | + |
| 107 | + /* Init SIRC */ |
| 108 | + (void)CLOCK_InitSirc(&sirc_config); |
| 109 | + |
| 110 | + /* Attach Clocks */ |
| 111 | + CLOCK_SetIpSrc(kCLOCK_Lpuart0, kCLOCK_IpSrcFro192M); |
| 112 | + CLOCK_SetIpSrc(kCLOCK_Lpuart1, kCLOCK_IpSrcFro192M); |
| 113 | + CLOCK_SetIpSrc(kCLOCK_Lpspi0, kCLOCK_IpSrcFro192M); |
| 114 | + CLOCK_SetIpSrc(kCLOCK_Lpspi1, kCLOCK_IpSrcFro192M); |
| 115 | + CLOCK_SetIpSrc(kCLOCK_Can0, kCLOCK_IpSrcFro192M); |
| 116 | + CLOCK_SetIpSrc(kCLOCK_Tpm0, kCLOCK_IpSrcFro192M); |
| 117 | + CLOCK_SetIpSrc(kCLOCK_Tpm1, kCLOCK_IpSrcFro192M); |
| 118 | + CLOCK_SetIpSrc(kCLOCK_Lpi2c0, kCLOCK_IpSrcFro192M); |
| 119 | + CLOCK_SetIpSrcDiv(kCLOCK_Lpi2c0, kSCG_SysClkDivBy16); |
| 120 | + CLOCK_SetIpSrc(kCLOCK_Lpi2c1, kCLOCK_IpSrcFro192M); |
| 121 | + CLOCK_SetIpSrcDiv(kCLOCK_Lpi2c1, kSCG_SysClkDivBy16); |
| 122 | + |
| 123 | + /* Ungate clocks if the peripheral is enabled in devicetree */ |
| 124 | +#if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(lpuart0), nxp_lpc_lpuart, okay)) |
| 125 | + CLOCK_EnableClock(kCLOCK_Lpuart0); |
| 126 | +#endif |
| 127 | +#if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(lpuart1), nxp_lpc_lpuart, okay)) |
| 128 | + CLOCK_EnableClock(kCLOCK_Lpuart1); |
| 129 | +#endif |
| 130 | +} |
| 131 | + |
| 132 | +static void vbat_init(void) |
| 133 | +{ |
| 134 | + VBAT_Type *base = (VBAT_Type *)DT_REG_ADDR(DT_NODELABEL(vbat)); |
| 135 | + |
| 136 | + /* Write 1 to Clear POR detect status bit. |
| 137 | + * |
| 138 | + * Clearing this bit is acknowledement |
| 139 | + * that software has recognized a power on reset. |
| 140 | + * |
| 141 | + * This avoids also niche issues with NVIC read/write |
| 142 | + * when searching for available interrupt lines. |
| 143 | + */ |
| 144 | + base->STATUSA |= VBAT_STATUSA_POR_DET_MASK; |
| 145 | +}; |
| 146 | + |
| 147 | +static int nxp_mcxw71_init(void) |
| 148 | +{ |
| 149 | + unsigned int oldLevel; /* old interrupt lock level */ |
| 150 | + |
| 151 | + /* disable interrupts */ |
| 152 | + oldLevel = irq_lock(); |
| 153 | + |
| 154 | + /* Initialize system clock to 40 MHz */ |
| 155 | + clock_init(); |
| 156 | + |
| 157 | + /* Smart power switch initialization */ |
| 158 | + vbat_init(); |
| 159 | + |
| 160 | + /* restore interrupt state */ |
| 161 | + irq_unlock(oldLevel); |
| 162 | + |
| 163 | + return 0; |
| 164 | +} |
| 165 | + |
| 166 | +SYS_INIT(nxp_mcxw71_init, PRE_KERNEL_1, 0); |
0 commit comments