diff --git a/boards/arm/mimxrt1064_evk/mimxrt1064_evk.dts b/boards/arm/mimxrt1064_evk/mimxrt1064_evk.dts index 0d77530885e..beff9339beb 100644 --- a/boards/arm/mimxrt1064_evk/mimxrt1064_evk.dts +++ b/boards/arm/mimxrt1064_evk/mimxrt1064_evk.dts @@ -100,6 +100,25 @@ }; }; }; + + power-states { + idle: idle { + compatible = "zephyr,power-state"; + power-state-name = "runtime-idle"; + exit-latency-us = <4000>; + min-residency-us = <5000>; + }; + suspend: suspend { + compatible = "zephyr,power-state"; + power-state-name = "suspend-to-idle"; + exit-latency-us = <5000>; + min-residency-us = <10000>; + }; + }; +}; + +&cpu0 { + cpu-power-states = <&idle &suspend>; }; arduino_i2c: &lpi2c1 {}; diff --git a/soc/arm/nxp_imx/rt/CMakeLists.txt b/soc/arm/nxp_imx/rt/CMakeLists.txt index 5d8573e9b38..4991aca8762 100644 --- a/soc/arm/nxp_imx/rt/CMakeLists.txt +++ b/soc/arm/nxp_imx/rt/CMakeLists.txt @@ -28,8 +28,16 @@ endif() if (CONFIG_PM AND CONFIG_SOC_SERIES_IMX_RT10XX) zephyr_sources(power_rt10xx.c) zephyr_code_relocate(power_rt10xx.c ITCM_TEXT) + if (CONFIG_SOC_MIMXRT1064) + zephyr_sources(lpm_rt1064.c) + zephyr_code_relocate(lpm_rt1064.c ITCM_TEXT) + endif() endif() +zephyr_compile_definitions( + XIP_EXTERNAL_FLASH +) + zephyr_linker_section_configure( SECTION .rom_start INPUT ".boot_hdr.ivt" diff --git a/soc/arm/nxp_imx/rt/lpm_rt1064.c b/soc/arm/nxp_imx/rt/lpm_rt1064.c new file mode 100644 index 00000000000..206d0f51994 --- /dev/null +++ b/soc/arm/nxp_imx/rt/lpm_rt1064.c @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2021, NXP + * + * SPDX-License-Identifier: Apache-2.0 + * + * Note: this file is linked to RAM. Any functions called while changing clocks + * to the flexspi modules must be linked to RAM, or within this file + */ + +#include +#include +#include +#include "clock_config.h" + + +/* + * Clock configuration structures populated at boot time. These structures are + * used to reinitialize the PLLs after exiting low power mode. + */ +#ifdef CONFIG_INIT_ARM_PLL +static clock_arm_pll_config_t arm_pll_config; +#endif +#ifdef CONFIG_INIT_VIDEO_PLL +static clock_video_pll_config_t video_pll_config; +#endif +#ifdef CONFIG_INIT_ENET_PLL +static clock_enet_pll_config_t enet_pll_config; +#endif +static clock_sys_pll_config_t sys_pll_config; +static clock_usb_pll_config_t usb1_pll_config; + +#define IMX_RT_SYS_PFD_FRAC(reg, pfd_num) \ + (((reg) >> (8U * pfd_num)) &\ + CCM_ANALOG_PFD_528_PFD0_FRAC_MASK) +#define IMX_RT_USB1_PFD_FRAC(reg, pfd_num) \ + (((reg) >> (8U * pfd_num)) &\ + CCM_ANALOG_PFD_480_PFD0_FRAC_MASK) + +uint8_t sys_pll_pfd0_frac; +uint8_t sys_pll_pfd1_frac; +uint8_t sys_pll_pfd2_frac; +uint8_t sys_pll_pfd3_frac; + +uint8_t usb1_pll_pfd1_frac; +uint8_t usb1_pll_pfd2_frac; +uint8_t usb1_pll_pfd3_frac; + +uint32_t flexspi_div; + +/* + * Duplicate implementation of CLOCK_SetMux() provided by SDK. This function + * must be linked to ITCM, as it will be used to change the clocks of the + * FLEXSPI and SEMC peripherals. + * Any function called from this function must also reside in ITCM + */ +static void clock_set_mux(clock_mux_t mux, uint32_t value) +{ + uint32_t busy_shift; + + busy_shift = (uint32_t)CCM_TUPLE_BUSY_SHIFT(mux); + CCM_TUPLE_REG(CCM, mux) = (CCM_TUPLE_REG(CCM, mux) & (~CCM_TUPLE_MASK(mux))) | + (((uint32_t)((value) << CCM_TUPLE_SHIFT(mux))) & CCM_TUPLE_MASK(mux)); + + /* Clock switch need Handshake? */ + if (busy_shift != CCM_NO_BUSY_WAIT) { + /* Wait until CCM internal handshake finish. */ + while ((CCM->CDHIPR & ((1UL << busy_shift))) != 0UL) { + } + } +} + +/* + * Duplicate implementation of CLOCK_SetDiv() provided by SDK. This function + * must be linked to ITCM, as it will be used to change the clocks of the + * FLEXSPI and SEMC peripherals. + * Any function called from this function must also reside in ITCM + */ +static void clock_set_div(clock_div_t divider, uint32_t value) +{ + uint32_t busy_shift; + + busy_shift = CCM_TUPLE_BUSY_SHIFT(divider); + CCM_TUPLE_REG(CCM, divider) = (CCM_TUPLE_REG(CCM, divider) & (~CCM_TUPLE_MASK(divider))) | + (((uint32_t)((value) << CCM_TUPLE_SHIFT(divider))) & CCM_TUPLE_MASK(divider)); + + /* Clock switch need Handshake? */ + if (busy_shift != CCM_NO_BUSY_WAIT) { + /* Wait until CCM internal handshake finish. */ + while ((CCM->CDHIPR & ((uint32_t)(1UL << busy_shift))) != 0UL) { + } + } +} + +/* + * Duplicate implementation of CLOCK_InitUsb1Pll() provided by SDK. This function + * must be linked to ITCM, as it will be used to change the clocks of the + * FLEXSPI and SEMC peripherals. + * Any function called from this function must also reside in ITCM + */ +static void clock_init_usb1_pll(const clock_usb_pll_config_t *config) +{ + /* Bypass PLL first */ + CCM_ANALOG->PLL_USB1 = (CCM_ANALOG->PLL_USB1 & (~CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_MASK)) | + CCM_ANALOG_PLL_USB1_BYPASS_MASK | CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC(config->src); + + CCM_ANALOG->PLL_USB1 = (CCM_ANALOG->PLL_USB1 & (~CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK)) | + CCM_ANALOG_PLL_USB1_ENABLE_MASK | CCM_ANALOG_PLL_USB1_POWER_MASK | + CCM_ANALOG_PLL_USB1_EN_USB_CLKS_MASK | + CCM_ANALOG_PLL_USB1_DIV_SELECT(config->loopDivider); + + while ((CCM_ANALOG->PLL_USB1 & CCM_ANALOG_PLL_USB1_LOCK_MASK) == 0UL) { + ; + } + + /* Disable Bypass */ + CCM_ANALOG->PLL_USB1 &= ~CCM_ANALOG_PLL_USB1_BYPASS_MASK; +} + +static void flexspi_enter_critical(void) +{ +#if CONFIG_CODE_FLEXSPI2 + /* Wait for flexspi to be inactive, and gate the clock */ + while (!((FLEXSPI2->STS0 & FLEXSPI_STS0_ARBIDLE_MASK) && + (FLEXSPI2->STS0 & FLEXSPI_STS0_SEQIDLE_MASK))) { + } + FLEXSPI2->MCR0 |= FLEXSPI_MCR0_MDIS_MASK; + + /* Disable clock gate of flexspi2. */ + CCM->CCGR7 &= (~CCM_CCGR7_CG1_MASK); +#elif CONFIG_CODE_FLEXSPI + /* Wait for flexspi to be inactive, and gate the clock */ + while (!((FLEXSPI->STS0 & FLEXSPI_STS0_ARBIDLE_MASK) && + (FLEXSPI->STS0 & FLEXSPI_STS0_SEQIDLE_MASK))) { + } + FLEXSPI->MCR0 |= FLEXSPI_MCR0_MDIS_MASK; + + /* Disable clock of flexspi. */ + CCM->CCGR6 &= (~CCM_CCGR6_CG5_MASK); +#endif +} + +static void flexspi_exit_critical(void) +{ +#if CONFIG_CODE_FLEXSPI2 + /* Enable clock gate of flexspi2. */ + CCM->CCGR7 |= (CCM_CCGR7_CG1_MASK); + + FLEXSPI2->MCR0 &= ~FLEXSPI_MCR0_MDIS_MASK; + FLEXSPI2->MCR0 |= FLEXSPI_MCR0_SWRESET_MASK; + while (FLEXSPI2->MCR0 & FLEXSPI_MCR0_SWRESET_MASK) { + } + while (!((FLEXSPI2->STS0 & FLEXSPI_STS0_ARBIDLE_MASK) && + (FLEXSPI2->STS0 & FLEXSPI_STS0_SEQIDLE_MASK))) { + } +#elif CONFIG_CODE_FLEXSPI + /* Enable clock of flexspi. */ + CCM->CCGR6 |= CCM_CCGR6_CG5_MASK; + + FLEXSPI->MCR0 &= ~FLEXSPI_MCR0_MDIS_MASK; + FLEXSPI->MCR0 |= FLEXSPI_MCR0_SWRESET_MASK; + while (FLEXSPI->MCR0 & FLEXSPI_MCR0_SWRESET_MASK) { + } + while (!((FLEXSPI->STS0 & FLEXSPI_STS0_ARBIDLE_MASK) && + (FLEXSPI->STS0 & FLEXSPI_STS0_SEQIDLE_MASK))) { + } +#endif + /* Invalidate I-cache after flexspi clock changed. */ + if (SCB_CCR_IC_Msk == (SCB_CCR_IC_Msk & SCB->CCR)) { + SCB_InvalidateICache(); + } +} + + +void clock_full_power(void) +{ + /* Power up PLLS */ + /* Set arm PLL div to divide by 2*/ + clock_set_div(kCLOCK_ArmDiv, 1); +#ifdef CONFIG_INIT_ARM_PLL + /* Reinit arm pll based on saved configuration */ + CLOCK_InitArmPll(&arm_pll_config); +#endif + +#ifdef CONFIG_INIT_VIDEO_PLL + /* Reinit video pll */ + CLOCK_InitVideoPll(&video_pll_config); +#endif + +#ifdef CONFIG_INIT_ENET_PLL + /* Reinit enet pll */ + CLOCK_InitEnetPll(&enet_pll_config); +#endif + /* Init SYS PLL */ + CLOCK_InitSysPll(&sys_pll_config); + + /* Enable USB PLL PFD 1 2 3 */ + CLOCK_InitUsb1Pfd(kCLOCK_Pfd1, usb1_pll_pfd1_frac); + CLOCK_InitUsb1Pfd(kCLOCK_Pfd2, usb1_pll_pfd2_frac); + CLOCK_InitUsb1Pfd(kCLOCK_Pfd3, usb1_pll_pfd3_frac); + + /* Enable SYS PLL PFD0 1 2 3 */ + CLOCK_InitSysPfd(kCLOCK_Pfd0, sys_pll_pfd0_frac); + CLOCK_InitSysPfd(kCLOCK_Pfd1, sys_pll_pfd1_frac); + CLOCK_InitSysPfd(kCLOCK_Pfd2, sys_pll_pfd2_frac); + CLOCK_InitSysPfd(kCLOCK_Pfd3, sys_pll_pfd3_frac); + + /* Switch to full speed clocks */ +#if (defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1)) + flexspi_enter_critical(); +#endif + + /* Set Flexspi divider before increasing frequency of PLL3 PDF0. */ +#if CONFIG_CODE_FLEXSPI + clock_set_div(kCLOCK_FlexspiDiv, flexspi_div); + clock_set_mux(kCLOCK_FlexspiMux, 3); +#elif CONFIG_CODE_FLEXSPI2 + clock_set_div(kCLOCK_Flexspi2Div, flexspi_div); + clock_set_mux(kCLOCK_Flexspi2Mux, 1); +#endif + /* Init USB1 PLL. This will disable the PLL3 bypass. */ + clock_init_usb1_pll(&usb1_pll_config); + + /* Switch SEMC clock to PLL2_PFD2 clock */ + clock_set_mux(kCLOCK_SemcMux, 1); + + /* CORE CLK to 600MHz, AHB, IPG to 150MHz, PERCLK to 75MHz */ + clock_set_div(kCLOCK_PerclkDiv, 1); + clock_set_div(kCLOCK_IpgDiv, 3); + clock_set_div(kCLOCK_AhbDiv, 0); + /* PERCLK mux to IPG CLK */ + clock_set_mux(kCLOCK_PerclkMux, 0); + /* MUX to ENET_500M (RT1010-1024) / ARM_PODF (RT1050-1064) */ + clock_set_mux(kCLOCK_PrePeriphMux, 3); + /* PERIPH mux to periph clock 2 output */ + clock_set_mux(kCLOCK_PeriphMux, 0); + +#if (defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1)) + flexspi_exit_critical(); +#endif + +} + +void clock_low_power(void) +{ +#if (defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1)) + flexspi_enter_critical(); +#endif + /* Switch to 24MHz core clock, so ARM PLL can power down */ + clock_set_div(kCLOCK_PeriphClk2Div, 0); + /* Switch to OSC clock */ + clock_set_mux(kCLOCK_PeriphClk2Mux, 1); + /* Switch peripheral mux to 24MHz source */ + clock_set_mux(kCLOCK_PeriphMux, 1); + /* Set PLL3 to bypass mode, output 24M clock */ + CCM_ANALOG->PLL_USB1_SET = CCM_ANALOG_PLL_USB1_BYPASS_MASK; + CCM_ANALOG->PLL_USB1_SET = CCM_ANALOG_PLL_USB1_ENABLE_MASK; + CCM_ANALOG->PFD_480_CLR = CCM_ANALOG_PFD_480_PFD0_CLKGATE_MASK; + /* Change flexspi to use PLL3 PFD0 with no divisor (24M flexspi clock) */ +#if CONFIG_CODE_FLEXSPI + clock_set_div(kCLOCK_FlexspiDiv, 0); + /* FLEXSPI1 mux to PLL3 PFD0 BYPASS */ + clock_set_mux(kCLOCK_FlexspiMux, 3); +#elif CONFIG_CODE_FLEXSPI2 + clock_set_div(kCLOCK_Flexspi2Div, 0); + /* FLEXSPI2 mux to PLL3 PFD0 BYPASS */ + clock_set_mux(kCLOCK_Flexspi2Mux, 1); +#endif + /* CORE CLK to 24MHz and AHB, IPG, PERCLK to 12MHz */ + clock_set_div(kCLOCK_PerclkDiv, 0); + clock_set_div(kCLOCK_IpgDiv, 1); + clock_set_div(kCLOCK_AhbDiv, 0); + /* PERCLK mux to IPG CLK */ + clock_set_mux(kCLOCK_PerclkMux, 0); + /* Switch SEMC clock to peripheral clock */ + clock_set_mux(kCLOCK_SemcMux, 0); +#if (defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1)) + flexspi_exit_critical(); +#endif + /* After switching clocks, it is safe to power down the PLLs */ +#ifdef CONFIG_INIT_ARM_PLL + /* Deinit ARM PLL */ + CLOCK_DeinitArmPll(); +#endif + /* Deinit SYS PLL */ + CLOCK_DeinitSysPll(); + + /* Deinit SYS PLL PFD 0 1 2 3 */ + CLOCK_DeinitSysPfd(kCLOCK_Pfd0); + CLOCK_DeinitSysPfd(kCLOCK_Pfd1); + CLOCK_DeinitSysPfd(kCLOCK_Pfd2); + CLOCK_DeinitSysPfd(kCLOCK_Pfd3); + + + /* Deinit USB1 PLL PFD 1 2 3 */ + CLOCK_DeinitUsb1Pfd(kCLOCK_Pfd1); + CLOCK_DeinitUsb1Pfd(kCLOCK_Pfd2); + CLOCK_DeinitUsb1Pfd(kCLOCK_Pfd3); + + /* Deinit VIDEO PLL */ + CLOCK_DeinitVideoPll(); + + /* Deinit ENET PLL */ + CLOCK_DeinitEnetPll(); +} + +void clock_lpm_init(void) +{ + uint32_t tmp_reg = 0; + + CLOCK_SetMode(kCLOCK_ModeRun); + /* Enable RC OSC. It needs at least 4ms to be stable, so self tuning need to be enabled. */ + XTALOSC24M->LOWPWR_CTRL |= XTALOSC24M_LOWPWR_CTRL_RC_OSC_EN_MASK; + /* Configure RC OSC */ + XTALOSC24M->OSC_CONFIG0 = XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_CUR(0x4) | + XTALOSC24M_OSC_CONFIG0_SET_HYST_MINUS(0x2) | + XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG(0xA7) | + XTALOSC24M_OSC_CONFIG0_START_MASK | + XTALOSC24M_OSC_CONFIG0_ENABLE_MASK; + XTALOSC24M->OSC_CONFIG1 = XTALOSC24M_OSC_CONFIG1_COUNT_RC_CUR(0x40) | + XTALOSC24M_OSC_CONFIG1_COUNT_RC_TRG(0x2DC); + /* Take some delay */ + SDK_DelayAtLeastUs(4000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); + /* Add some hysteresis */ + tmp_reg = XTALOSC24M->OSC_CONFIG0; + tmp_reg &= ~(XTALOSC24M_OSC_CONFIG0_HYST_PLUS_MASK | + XTALOSC24M_OSC_CONFIG0_HYST_MINUS_MASK); + tmp_reg |= XTALOSC24M_OSC_CONFIG0_HYST_PLUS(3) | XTALOSC24M_OSC_CONFIG0_HYST_MINUS(3); + XTALOSC24M->OSC_CONFIG0 = tmp_reg; + /* Set COUNT_1M_TRG */ + tmp_reg = XTALOSC24M->OSC_CONFIG2; + tmp_reg &= ~XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_MASK; + tmp_reg |= XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG(0x2d7); + XTALOSC24M->OSC_CONFIG2 = tmp_reg; + /* Hardware requires to read OSC_CONFIG0 or OSC_CONFIG1 to make OSC_CONFIG2 write work */ + tmp_reg = XTALOSC24M->OSC_CONFIG1; + XTALOSC24M->OSC_CONFIG1 = tmp_reg; +} + +static int imxrt_lpm_init(const struct device *dev) +{ + ARG_UNUSED(dev); + + struct clock_callbacks callbacks; + uint32_t usb1_pll_pfd0_frac; + + callbacks.clock_set_run = clock_full_power; + callbacks.clock_set_low_power = clock_low_power; + callbacks.clock_lpm_init = clock_lpm_init; + + /* + * Read the boot time configuration of all PLLs. + * This is required because not all PLLs preserve register state + * when powered down. Additionally, populating these configuration + * structures enables the rest of the code to use the fsl_clock HAL api. + */ +#ifdef CONFIG_INIT_ARM_PLL + /* Read configuration values for arm pll */ + arm_pll_config.src = ((CCM_ANALOG->PLL_ARM & + CCM_ANALOG_PLL_ARM_BYPASS_CLK_SRC_MASK) >> + CCM_ANALOG_PLL_ARM_BYPASS_CLK_SRC_SHIFT); + arm_pll_config.loopDivider = ((CCM_ANALOG->PLL_ARM & + CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK) >> + CCM_ANALOG_PLL_ARM_DIV_SELECT_SHIFT); +#endif + /* Read configuration values for sys pll */ + sys_pll_config.src = ((CCM_ANALOG->PLL_SYS & + CCM_ANALOG_PLL_SYS_BYPASS_CLK_SRC_MASK) >> + CCM_ANALOG_PLL_SYS_BYPASS_CLK_SRC_SHIFT); + sys_pll_config.loopDivider = ((CCM_ANALOG->PLL_SYS & + CCM_ANALOG_PLL_SYS_DIV_SELECT_MASK) >> + CCM_ANALOG_PLL_SYS_DIV_SELECT_SHIFT); + sys_pll_config.numerator = CCM_ANALOG->PLL_SYS_NUM; + sys_pll_config.denominator = CCM_ANALOG->PLL_SYS_DENOM; + sys_pll_config.ss_step = ((CCM_ANALOG->PLL_SYS_SS & + CCM_ANALOG_PLL_SYS_SS_STEP_MASK) >> + CCM_ANALOG_PLL_SYS_SS_STEP_SHIFT); + sys_pll_config.ss_enable = ((CCM_ANALOG->PLL_SYS_SS & + CCM_ANALOG_PLL_SYS_SS_ENABLE_MASK) >> + CCM_ANALOG_PLL_SYS_SS_ENABLE_SHIFT); + sys_pll_config.ss_stop = ((CCM_ANALOG->PLL_SYS_SS & + CCM_ANALOG_PLL_SYS_SS_STOP_MASK) >> + CCM_ANALOG_PLL_SYS_SS_STOP_SHIFT); + + /* Read configuration values for usb1 pll */ + usb1_pll_config.src = ((CCM_ANALOG->PLL_USB1 & + CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_MASK) >> + CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_SHIFT); + usb1_pll_config.loopDivider = ((CCM_ANALOG->PLL_USB1 & + CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK) >> + CCM_ANALOG_PLL_USB1_DIV_SELECT_SHIFT); +#ifdef CONFIG_INIT_VIDEO_PLL + /* Read configuration values for video pll */ + video_pll_config.src = ((CCM_ANALOG->PLL_VIDEO & + CCM_ANALOG_PLL_VIDEO_BYPASS_CLK_SRC_MASK) >> + CCM_ANALOG_PLL_VIDEO_BYPASS_CLK_SRC_SHIFT); + video_pll_config.loopDivider = ((CCM_ANALOG->PLL_VIDEO & + CCM_ANALOG_PLL_VIDEO_DIV_SELECT_MASK) >> + CCM_ANALOG_PLL_VIDEO_DIV_SELECT_SHIFT); + video_pll_config.numerator = CCM_ANALOG->PLL_VIDEO_NUM + video_pll_config.denominator = CCM_ANALOG->PLL_VIDEO_DENOM; + switch ((CCM_ANALOG->PLL_VIDEO & + CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_MASK) >> + CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_SHIFT) { + case 0: + video_pll_config.postDivider = 16; + break; + case 1: + if (CCM_ANALOG->MISC2 & CCM_ANALOG_MISC2_VIDEO_DIV(3)) { + video_pll_config.postDivider = 8; + } else { + video_pll_config.postDivider = 2; + } + break; + case 2: + if (CCM_ANALOG->MISC2 & CCM_ANALOG_MISC2_VIDEO_DIV(3)) { + video_pll_config.postDivider = 4 + } else { + video_pll_config.postDivider = 1; + } + break; + default: + video_pll_config.postDivider = 1; + } +#endif +#if CONFIG_INIT_ENET_PLL + enet_pll_config.src = ((CCM_ANALOG->PLL_ENET & + CCM_ANALOG_PLL_ENET_BYPASS_CLK_SRC_MASK) >> + CCM_ANALOG_PLL_ENET_BYPASS_CLK_SRC_SHIFT); + enet_pll_config.loopDivider = ((CCM_ANALOG->PLL_ENET & + CCM_ANALOG_PLL_ENET_DIV_SELECT_MASK) >> + CCM_ANALOG_PLL_ENET_DIV_SELECT_SHIFT); + enet_pll_config.loopDivider1 = ((CCM_ANALOG->PLL_ENET & + CCM_ANALOG_PLL_ENET_ENET2_DIV_SELECT_MASK) >> + CCM_ANALOG_PLL_ENET_ENET2_DIV_SELECT_SHIFT); + enet_pll_config.enableClkOutput = (CCM_ANALOG->PLL_ENET & + CCM_ANALOG_PLL_ENET_ENABLE_MASK); + enet_pll_config.enableClkOutput1 = (CCM_ANALOG->PLL_ENET & + CCM_ANALOG_PLL_ENET_ENET2_REF_EN_MASK); + enet_pll_config.enableClkOutput25M = (CCM_ANALOG->PLL_ENET & + CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN_MASK); +#endif + + /* Record all pll PFD values that we intend to disable in low power mode */ + sys_pll_pfd0_frac = IMX_RT_SYS_PFD_FRAC(CCM_ANALOG->PFD_528, kCLOCK_Pfd0); + sys_pll_pfd1_frac = IMX_RT_SYS_PFD_FRAC(CCM_ANALOG->PFD_528, kCLOCK_Pfd1); + sys_pll_pfd2_frac = IMX_RT_SYS_PFD_FRAC(CCM_ANALOG->PFD_528, kCLOCK_Pfd2); + sys_pll_pfd3_frac = IMX_RT_SYS_PFD_FRAC(CCM_ANALOG->PFD_528, kCLOCK_Pfd3); + + usb1_pll_pfd0_frac = IMX_RT_USB1_PFD_FRAC(CCM_ANALOG->PFD_480, kCLOCK_Pfd0); + /* The target full power frequency for the flexspi clock is ~100MHz. + * Use the PFD0 value currently set to calculate the div we should use for + * the full power flexspi div + * PFD output frequency formula = (480 * 18) / pfd0_frac + * flexspi div formula = FLOOR((480*18) / (pfd0_frac * target_full_power_freq)) + */ + flexspi_div = (480 * 18) / (usb1_pll_pfd0_frac * 100); + + + usb1_pll_pfd1_frac = IMX_RT_USB1_PFD_FRAC(CCM_ANALOG->PFD_480, kCLOCK_Pfd1); + usb1_pll_pfd2_frac = IMX_RT_USB1_PFD_FRAC(CCM_ANALOG->PFD_480, kCLOCK_Pfd2); + usb1_pll_pfd3_frac = IMX_RT_USB1_PFD_FRAC(CCM_ANALOG->PFD_480, kCLOCK_Pfd3); + + /* Install LPM callbacks */ + imxrt_clock_pm_callbacks_register(&callbacks); + return 0; +} + + +SYS_INIT(imxrt_lpm_init, PRE_KERNEL_1, 0);