/* * Copyright 2017 NXP * All rights reserved. * * * SPDX-License-Identifier: BSD-3-Clause */ #ifdef FSL_RTOS_FREE_RTOS #include "FreeRTOS.h" #include "task.h" #include "semphr.h" #include "fsl_tickless_generic.h" #endif #include "lpm.h" #include "fsl_gpc.h" #include "fsl_dcdc.h" #include "fsl_gpt.h" #include "fsl_iomuxc.h" #include "fsl_lpuart.h" #include "clock_config.h" #include "board.h" #include "FreeRTOS.h" #include "semphr.h" /******************************************************************************* * Definitions ******************************************************************************/ #define LPM_GPC_IMR_NUM (sizeof(GPC->IMR) / sizeof(GPC->IMR[0])) #ifdef FSL_RTOS_FREE_RTOS /* Define the counter clock of the systick (GPT). For accuracy purpose, * please make LPM_SYSTICK_COUNTER_FREQ divisible by 32768, and times of * configTICK_RATE_HZ. */ #define LPM_SYSTICK_COUNTER_FREQ (32768) #define LPM_COUNT_PER_TICK (LPM_SYSTICK_COUNTER_FREQ / configTICK_RATE_HZ) struct _lpm_power_mode_listener { lpm_power_mode_callback_t callback; void *data; struct _lpm_power_mode_listener *next; }; typedef struct _lpm_power_mode_listener lpm_power_mode_listener_t; #endif typedef struct _lpm_clock_context { uint32_t armDiv; uint32_t ahbDiv; uint32_t ipgDiv; uint32_t perDiv; uint32_t perSel; uint32_t periphSel; uint32_t preperiphSel; uint32_t pfd480; uint32_t pfd528; uint32_t pllArm_loopdiv; uint32_t pllArm; uint32_t pllSys; uint32_t pllUsb1; uint32_t pllUsb2; uint32_t pllAudio; uint32_t pllVideo; uint32_t pllEnet; uint32_t is_valid; } lpm_clock_context_t; typedef void (*lpm_system_func_t)(uint32_t context); typedef void (*freertos_tick_func_t)(void); /******************************************************************************* * Variables ******************************************************************************/ static lpm_power_mode_t s_curMode; static lpm_clock_context_t s_clockContext; static uint32_t s_DllBackupValue = 0; static int32_t s_SystemIdleFlag = 0; static int32_t s_SkipRestorePLLs = 0; #ifdef FSL_RTOS_FREE_RTOS static SemaphoreHandle_t s_mutex; static lpm_power_mode_listener_t *s_listenerHead; static lpm_power_mode_listener_t *s_listenerTail; #if (configUSE_TICKLESS_IDLE == 1) GPT_Type *vPortGetGptBase(void); IRQn_Type vPortGetGptIrqn(void); #endif #endif static lpm_power_mode_t s_targetPowerMode; void APP_PowerPreSwitchHook(lpm_power_mode_t targetMode); void APP_PowerPostSwitchHook(lpm_power_mode_t targetMode); lpm_power_mode_t APP_GetLPMPowerMode(void); /******************************************************************************* * Code ******************************************************************************/ static void BOARD_SetLPClockGate(void) { CCM->CCGR0 = 0x014030C5U; CCM->CCGR1 = 0x541C0000U; CCM->CCGR2 = 0x00150010U; CCM->CCGR3 = 0x50040130U; CCM->CCGR4 = 0x00005514U; CCM->CCGR5 = 0x51001105U; /* We can enable DCDC when need to config it and close it after configuration */ CCM->CCGR6 = 0x00540540U; } static void LPM_SetClockMode(clock_mode_t mode, uint32_t clpcr) { switch (mode) { case kCLOCK_ModeRun: CCM->CLPCR = clpcr; break; default: /* * ERR007265: CCM: When improper low-power sequence is used, * the SoC enters low power mode before the ARM core executes WFI. * * Software workaround: * 1) Software should trigger IRQ #41 (GPR_IRQ) to be always pending * by setting IOMUXC_GPR_GPR1_GINT. * 2) Software should then unmask IRQ #41 in GPC before setting CCM * Low-Power mode. * 3) Software should mask IRQ #41 right after CCM Low-Power mode * is set (set bits 0-1 of CCM_CLPCR). * */ LPM_EnableWakeupSource(GPR_IRQ_IRQn); CCM->CLPCR = clpcr; LPM_DisableWakeupSource(GPR_IRQ_IRQn); break; } } void LPM_SwitchToXtalOSC(void) { /* Restore XTAL-OSC and enable detector */ CCM_ANALOG->MISC0_CLR = CCM_ANALOG_MISC0_XTAL_24M_PWD_MASK; /* Power up */ while ((XTALOSC24M->LOWPWR_CTRL & XTALOSC24M_LOWPWR_CTRL_XTALOSC_PWRUP_STAT_MASK) == 0) {} CCM_ANALOG->MISC0_SET = CCM_ANALOG_MISC0_OSC_XTALOK_EN_MASK; /* detect freq */ while ((CCM_ANALOG->MISC0 & CCM_ANALOG_MISC0_OSC_XTALOK_MASK) == 0) {} CCM_ANALOG->MISC0_CLR = CCM_ANALOG_MISC0_OSC_XTALOK_EN_MASK; /* Switch to XTAL-OSC */ XTALOSC24M->LOWPWR_CTRL_CLR = XTALOSC24M_LOWPWR_CTRL_CLR_OSC_SEL_MASK; /* Turn off XTAL-OSC detector */ CCM_ANALOG->MISC0_CLR = CCM_ANALOG_MISC0_OSC_XTALOK_EN_MASK; /* Wait CCM operation finishes */ CLOCK_CCM_HANDSHAKE_WAIT(); /* Take some delay */ LPM_DELAY(40); } void LPM_SwitchToRcOSC(void) { /* Switch to RC-OSC */ XTALOSC24M->LOWPWR_CTRL_SET = XTALOSC24M_LOWPWR_CTRL_SET_OSC_SEL_MASK; /* Turn off XTAL-OSC */ CCM_ANALOG->MISC0_SET = CCM_ANALOG_MISC0_XTAL_24M_PWD_MASK; /* Power down */ /* Wait CCM operation finishes */ CLOCK_CCM_HANDSHAKE_WAIT(); /* Take some delay */ LPM_DELAY(40); } void LPM_SwitchFlexspiClock(lpm_power_mode_t power_mode) { while (!((FLEXSPI->STS0 & FLEXSPI_STS0_ARBIDLE_MASK) && (FLEXSPI->STS0 & FLEXSPI_STS0_SEQIDLE_MASK))) { ; } FLEXSPI->MCR0 |= FLEXSPI_MCR0_MDIS_MASK; /* Disable clock gate of flexspi. */ CCM->CCGR6 &= (~CCM_CCGR6_CG5_MASK); /* Periph_clk output will be used as SEMC clock root */ CLOCK_SET_MUX((uint32_t)kCLOCK_SemcMux, 0x0); /* Set post divider for SEMC clock as 0. */ CLOCK_SET_DIV(kCLOCK_SemcDiv, 0x0); /* Semc_clk_root_pre will be used as flexspi clock. */ CLOCK_SET_MUX((uint32_t)kCLOCK_FlexspiMux, 0x0); /* Set divider for flexspi clock root 0. */ CLOCK_SET_DIV((uint32_t)kCLOCK_FlexspiDiv, 0x0); /* Enable clock gate of flexspi. */ CCM->CCGR6 |= (CCM_CCGR6_CG5_MASK); if ((LPM_PowerModeLPIdle == power_mode) || (LPM_PowerModeLowPowerRun == power_mode)) { FLEXSPI->DLLCR[0] = FLEXSPI_DLLCR_OVRDEN(1) | FLEXSPI_DLLCR_OVRDVAL(19); } else { FLEXSPI->DLLCR[0] = 0x79; } 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))) { ; } /* Take some delay */ LPM_DELAY(40); } void LPM_RestoreFlexspiClock(void) { while (!((FLEXSPI->STS0 & FLEXSPI_STS0_ARBIDLE_MASK) && (FLEXSPI->STS0 & FLEXSPI_STS0_SEQIDLE_MASK))) { ; } FLEXSPI->MCR0 |= FLEXSPI_MCR0_MDIS_MASK; /* Disable clock gate of flexspi. */ CCM->CCGR6 &= (~CCM_CCGR6_CG5_MASK); /* PLL3 PFD0 will be used as flexspi clock. */ CLOCK_SET_MUX((uint32_t)kCLOCK_FlexspiMux, 0x3); /* Set divider for flexspi clock root 0. */ CLOCK_SET_DIV((uint32_t)kCLOCK_FlexspiDiv, 0x0); /* Enable clock gate of flexspi. */ CCM->CCGR6 |= (CCM_CCGR6_CG5_MASK); FLEXSPI->DLLCR[0] = s_DllBackupValue; 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))) { ; } /* Take some delay */ LPM_DELAY(40); } void LPM_SwitchBandgap(void) { /* Switch bandgap */ PMU->MISC0_SET = 0x00000004; XTALOSC24M->LOWPWR_CTRL_SET = XTALOSC24M_LOWPWR_CTRL_LPBG_SEL_MASK; PMU->MISC0_SET = CCM_ANALOG_MISC0_REFTOP_PWD_MASK; /* Wait CCM operation finishes */ CLOCK_CCM_HANDSHAKE_WAIT(); /* Take some delay */ LPM_DELAY(40); } void LPM_RestoreBandgap(void) { /* Restore bandgap */ /* Turn on regular bandgap and wait for stable */ CCM_ANALOG->MISC0_CLR = CCM_ANALOG_MISC0_REFTOP_PWD_MASK; while ((CCM_ANALOG->MISC0 & CCM_ANALOG_MISC0_REFTOP_VBGUP_MASK) == 0) {} /* Low power band gap disable */ XTALOSC24M->LOWPWR_CTRL_CLR = XTALOSC24M_LOWPWR_CTRL_LPBG_SEL_MASK; PMU->MISC0_CLR = 0x00000004; /* Wait CCM operation finishes */ CLOCK_CCM_HANDSHAKE_WAIT(); /* Take some delay */ LPM_DELAY(40); } void LPM_DisablePLLs(lpm_power_mode_t power_mode) { /* Application shouldn't rely on PLL in low power mode, * gate all PLL and PFD now */ if (LPM_PowerModeSuspend == power_mode) { return; } s_clockContext.pfd480 = CCM_ANALOG->PFD_480; s_clockContext.pfd528 = CCM_ANALOG->PFD_528; s_clockContext.pllSys = CCM_ANALOG->PLL_SYS; s_clockContext.pllUsb1 = CCM_ANALOG->PLL_USB1; s_clockContext.pllUsb2 = CCM_ANALOG->PLL_USB2; s_clockContext.pllAudio = CCM_ANALOG->PLL_AUDIO; s_clockContext.pllVideo = CCM_ANALOG->PLL_VIDEO; s_clockContext.pllEnet = CCM_ANALOG->PLL_ENET; s_clockContext.pllArm_loopdiv = (CCM_ANALOG->PLL_ARM & CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK) >> CCM_ANALOG_PLL_ARM_DIV_SELECT_SHIFT; s_clockContext.pllArm = CCM_ANALOG->PLL_ARM; s_clockContext.periphSel = CLOCK_GetMux(kCLOCK_PeriphMux); s_clockContext.ipgDiv = CLOCK_GetDiv(kCLOCK_IpgDiv); s_clockContext.ahbDiv = CLOCK_GetDiv(kCLOCK_AhbDiv); s_clockContext.perSel = CLOCK_GetMux(kCLOCK_PerclkMux); s_clockContext.perDiv = CLOCK_GetDiv(kCLOCK_PerclkDiv); s_clockContext.preperiphSel = CLOCK_GetMux(kCLOCK_PrePeriphMux); s_clockContext.armDiv = CLOCK_GetDiv(kCLOCK_ArmDiv); s_clockContext.is_valid = 1; /* When need to close PLL, we need to use bypass clock first and then power it down. */ /* Power off SYS PLL */ CCM_ANALOG->PLL_SYS_SET = CCM_ANALOG_PLL_SYS_BYPASS_MASK; CCM_ANALOG->PLL_SYS_SET = CCM_ANALOG_PLL_SYS_POWERDOWN_MASK; // CCM_ANALOG->PFD_528_SET = CCM_ANALOG_PFD_528_PFD2_CLKGATE_MASK; #if !(defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1)) /* If XIP in hyper flash, should switch to ARM PLL before disble USB1 PLL */ /* Power off USB1 PLL */ CCM_ANALOG->PLL_USB1_SET = CCM_ANALOG_PLL_USB1_BYPASS_MASK; CCM_ANALOG->PLL_USB1_CLR = CCM_ANALOG_PLL_USB1_POWER_MASK; CCM_ANALOG->PFD_480_SET = CCM_ANALOG_PFD_480_PFD2_CLKGATE_MASK; #endif /* Power off USB2 PLL */ CCM_ANALOG->PLL_USB2_SET = CCM_ANALOG_PLL_USB2_BYPASS_MASK; CCM_ANALOG->PLL_USB2_CLR = CCM_ANALOG_PLL_USB2_POWER_MASK; /* Power off AUDIO PLL */ CCM_ANALOG->PLL_AUDIO_SET = CCM_ANALOG_PLL_AUDIO_BYPASS_MASK; CCM_ANALOG->PLL_AUDIO_SET = CCM_ANALOG_PLL_AUDIO_POWERDOWN_MASK; /* Power off VIDEO PLL */ CCM_ANALOG->PLL_VIDEO_SET = CCM_ANALOG_PLL_VIDEO_BYPASS_MASK; CCM_ANALOG->PLL_VIDEO_SET = CCM_ANALOG_PLL_VIDEO_POWERDOWN_MASK; /* Power off ENET PLL */ CCM_ANALOG->PLL_ENET_SET = CCM_ANALOG_PLL_ENET_BYPASS_MASK; CCM_ANALOG->PLL_ENET_SET = CCM_ANALOG_PLL_ENET_POWERDOWN_MASK; if ((LPM_PowerModeSysIdle == power_mode) || (LPM_PowerModeLowSpeedRun == power_mode)) { CLOCK_SetMux(kCLOCK_PeriphClk2Mux, 1); CLOCK_SetDiv(kCLOCK_PeriphClk2Div, 0); CLOCK_SetMux(kCLOCK_PeriphMux, 1); CLOCK_SetDiv(kCLOCK_IpgDiv, 0); CLOCK_SetDiv(kCLOCK_AhbDiv, 0); /*ARM PLL as clksource*/ /* 24 * 88 / 2 / 8 = 132MHz */ CCM_ANALOG->PLL_ARM_CLR = CCM_ANALOG_PLL_ARM_POWERDOWN_MASK; CCM_ANALOG->PLL_ARM_SET = CCM_ANALOG_PLL_ARM_ENABLE_MASK | CCM_ANALOG_PLL_ARM_BYPASS_MASK; CCM_ANALOG->PLL_ARM_CLR = CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK; CCM_ANALOG->PLL_ARM_SET = CCM_ANALOG_PLL_ARM_DIV_SELECT(88); CLOCK_SetDiv(kCLOCK_ArmDiv, 7); CCM_ANALOG->PLL_ARM_CLR = CCM_ANALOG_PLL_ARM_BYPASS_MASK; /*Select ARM_PLL for pre_periph_clock */ CLOCK_SetMux(kCLOCK_PrePeriphMux, 3); CLOCK_SetMux(kCLOCK_PeriphMux, 0); if (LPM_PowerModeLowSpeedRun == power_mode) { /* SET AHB to 132MHz, IPG to 33MHz */ CLOCK_SetDiv(kCLOCK_IpgDiv, 3); CLOCK_SetDiv(kCLOCK_AhbDiv, 0); } else { /* SET AHB, IPG to 33MHz */ CLOCK_SetDiv(kCLOCK_IpgDiv, 0); CLOCK_SetDiv(kCLOCK_AhbDiv, 3); } /*Set PERCLK to 33MHz*/ CLOCK_SetMux(kCLOCK_PerclkMux, 0); CLOCK_SetDiv(kCLOCK_PerclkDiv, 0); } else if ((LPM_PowerModeLPIdle == power_mode) || (LPM_PowerModeLowPowerRun == power_mode)) { CLOCK_SetMux(kCLOCK_PeriphClk2Mux, 1); CLOCK_SetDiv(kCLOCK_PeriphClk2Div, 0); CLOCK_SetMux(kCLOCK_PeriphMux, 1); CLOCK_SetDiv(kCLOCK_IpgDiv, 0); CLOCK_SetDiv(kCLOCK_AhbDiv, 0); /*ARM PLL as clksource*/ CCM_ANALOG->PLL_ARM |= CCM_ANALOG_PLL_ARM_ENABLE_MASK | CCM_ANALOG_PLL_ARM_BYPASS_MASK; /* Power off ARM PLL */ CCM_ANALOG->PLL_ARM_SET = CCM_ANALOG_PLL_ARM_POWERDOWN_MASK; /*Select ARM_PLL for pre_periph_clock */ CLOCK_SetDiv(kCLOCK_ArmDiv, 0x0); CLOCK_SetMux(kCLOCK_PrePeriphMux, 0x3); CLOCK_SetMux(kCLOCK_PeriphMux, 0x0); if (LPM_PowerModeLowPowerRun == power_mode) { /* SET AHB to 24MHz, IPG to 12MHz */ CLOCK_SetDiv(kCLOCK_IpgDiv, 1); CLOCK_SetDiv(kCLOCK_AhbDiv, 0); } else { /* SET AHB, IPG to 12MHz */ CLOCK_SetDiv(kCLOCK_IpgDiv, 0); CLOCK_SetDiv(kCLOCK_AhbDiv, 1); } /*Set PERCLK to 12Mhz*/ CLOCK_SetMux(kCLOCK_PerclkMux, 0x0); CLOCK_SetDiv(kCLOCK_PerclkDiv, 0x0); } else { /* Direct return from RUN and Suspend */ return; } #if defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1) LPM_EnterCritical(); LPM_SwitchFlexspiClock(power_mode); LPM_ExitCritical(); /* Power off USB1 PLL */ CCM_ANALOG->PLL_USB1_SET = CCM_ANALOG_PLL_USB1_BYPASS_MASK; CCM_ANALOG->PLL_USB1_CLR = CCM_ANALOG_PLL_USB1_POWER_MASK; CCM_ANALOG->PFD_480_SET = CCM_ANALOG_PFD_480_PFD2_CLKGATE_MASK; #endif CLOCK_DeinitUsb1Pfd(kCLOCK_Pfd0); CLOCK_DeinitUsb1Pfd(kCLOCK_Pfd1); CLOCK_DeinitUsb1Pfd(kCLOCK_Pfd2); CLOCK_DeinitUsb1Pfd(kCLOCK_Pfd3); CLOCK_DeinitSysPfd(kCLOCK_Pfd0); CLOCK_DeinitSysPfd(kCLOCK_Pfd1); // CLOCK_DeinitSysPfd(kCLOCK_Pfd2); CLOCK_DeinitSysPfd(kCLOCK_Pfd3); } void LPM_RestorePLLs(lpm_power_mode_t power_mode) { if (s_clockContext.is_valid) { /* Restore USB1 PLL */ CCM_ANALOG->PLL_USB1_SET = CCM_ANALOG_PLL_USB1_BYPASS_MASK; CCM_ANALOG->PLL_USB1 = (s_clockContext.pllUsb1 & (~(CCM_ANALOG_PLL_USB1_POWER_MASK))) | CCM_ANALOG_PLL_USB1_BYPASS_MASK; CCM_ANALOG->PLL_USB1_CLR = CCM_ANALOG_PLL_USB1_BYPASS_MASK; CCM_ANALOG->PLL_USB1_SET = CCM_ANALOG_PLL_USB1_POWER_MASK; /* Restore USB1 PLL PFD */ CCM_ANALOG->PFD_480 = s_clockContext.pfd480; CCM_ANALOG->PFD_480_CLR = CCM_ANALOG_PFD_480_PFD0_CLKGATE_MASK; if ((CCM_ANALOG->PLL_USB1 & CCM_ANALOG_PLL_USB1_POWER_MASK) != 0) { while ((CCM_ANALOG->PLL_USB1 & CCM_ANALOG_PLL_USB1_LOCK_MASK) == 0) {} } /* Wait CCM operation finishes */ while (CCM->CDHIPR != 0) {} #if defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1) LPM_EnterCritical(); LPM_RestoreFlexspiClock(); LPM_ExitCritical(); #endif } #if defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1) /* If reset from suspend, need to switch back to USB1 PLL as the clock source */ else if (is_suspend_reset) { const clock_usb_pll_config_t g_ccmConfigUsbPll = {.loopDivider = 0U}; s_DllBackupValue = 0x79; is_suspend_reset = 0; CLOCK_InitUsb1Pll(&g_ccmConfigUsbPll); /* Configure USB1 PLL to 480M */ CLOCK_InitUsb1Pfd(kCLOCK_Pfd0, 26); LPM_EnterCritical(); LPM_RestoreFlexspiClock(); LPM_ExitCritical(); return; } #endif else { return; } CLOCK_SetMux(kCLOCK_PeriphClk2Mux, 1); CLOCK_SetDiv(kCLOCK_PeriphClk2Div, 0); CLOCK_SetMux(kCLOCK_PeriphMux, 1); CLOCK_SetDiv(kCLOCK_IpgDiv, 0x1); CLOCK_SetDiv(kCLOCK_AhbDiv, 0x0); /* ARM PLL as clksource*/ CCM_ANALOG->PLL_ARM_CLR = CCM_ANALOG_PLL_ARM_POWERDOWN_MASK; CCM_ANALOG->PLL_ARM_SET = CCM_ANALOG_PLL_ARM_ENABLE_MASK | CCM_ANALOG_PLL_ARM_BYPASS_MASK; CLOCK_SetDiv(kCLOCK_ArmDiv, s_clockContext.armDiv); CCM_ANALOG->PLL_ARM_CLR = CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK; CCM_ANALOG->PLL_ARM_SET = CCM_ANALOG_PLL_ARM_DIV_SELECT(s_clockContext.pllArm_loopdiv); if ((s_clockContext.pllArm & CCM_ANALOG_PLL_ARM_BYPASS_MASK) == 0) { while ((CCM_ANALOG->PLL_ARM & CCM_ANALOG_PLL_ARM_LOCK_MASK) == 0) {} } if ((s_clockContext.pllArm & CCM_ANALOG_PLL_ARM_BYPASS_MASK) == 0) { CCM_ANALOG->PLL_ARM_CLR = CCM_ANALOG_PLL_ARM_BYPASS_MASK; } /* Restore AHB and IPG div */ CCM->CBCDR = (CCM->CBCDR & ~(CCM_CBCDR_AHB_PODF_MASK | CCM_CBCDR_IPG_PODF_MASK | CCM_CBCDR_PERIPH_CLK_SEL_MASK)) | CCM_CBCDR_AHB_PODF(s_clockContext.ahbDiv) | CCM_CBCDR_IPG_PODF(s_clockContext.ipgDiv) | CCM_CBCDR_PERIPH_CLK_SEL(s_clockContext.periphSel); /* Restore Periphral clock */ CCM->CSCMR1 = (CCM->CSCMR1 & ~CCM_CSCMR1_PERCLK_PODF_MASK) | CCM_CSCMR1_PERCLK_PODF(s_clockContext.perDiv) | CCM_CSCMR1_PERCLK_CLK_SEL(s_clockContext.perSel); /* Switch clocks back */ CCM->CBCMR = (CCM->CBCMR & ~CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK) | CCM_CBCMR_PRE_PERIPH_CLK_SEL(s_clockContext.preperiphSel); /* Wait CCM operation finishes */ while (CCM->CDHIPR != 0) {} /* When need to enable PLL, we need to use bypass clock first and then switch pll back. */ /* Power on SYS PLL and wait for locked */ CCM_ANALOG->PLL_SYS_SET = CCM_ANALOG_PLL_SYS_BYPASS_MASK; CCM_ANALOG->PLL_SYS_CLR = CCM_ANALOG_PLL_SYS_POWERDOWN_MASK; CCM_ANALOG->PLL_SYS = s_clockContext.pllSys; if ((CCM_ANALOG->PLL_SYS & CCM_ANALOG_PLL_SYS_POWERDOWN_MASK) == 0) { while ((CCM_ANALOG->PLL_SYS & CCM_ANALOG_PLL_SYS_LOCK_MASK) == 0) {} } // CCM_ANALOG->PFD_528_CLR = CCM_ANALOG_PFD_528_PFD2_CLKGATE_MASK; /* Restore USB2 PLL */ CCM_ANALOG->PLL_USB2_SET = CCM_ANALOG_PLL_USB2_BYPASS_MASK; CCM_ANALOG->PLL_USB2_SET = CCM_ANALOG_PLL_USB2_POWER_MASK; CCM_ANALOG->PLL_USB2 = s_clockContext.pllUsb2; if ((CCM_ANALOG->PLL_USB2 & CCM_ANALOG_PLL_USB2_POWER_MASK) != 0) { while ((CCM_ANALOG->PLL_USB2 & CCM_ANALOG_PLL_USB2_LOCK_MASK) == 0) {} } /* Restore AUDIO PLL */ CCM_ANALOG->PLL_AUDIO_SET = CCM_ANALOG_PLL_AUDIO_BYPASS_MASK; CCM_ANALOG->PLL_AUDIO_CLR = CCM_ANALOG_PLL_AUDIO_POWERDOWN_MASK; CCM_ANALOG->PLL_AUDIO = s_clockContext.pllAudio; if ((CCM_ANALOG->PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_POWERDOWN_MASK) == 0) { while ((CCM_ANALOG->PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK_MASK) == 0) {} } /* Restore VIDEO PLL */ CCM_ANALOG->PLL_VIDEO_SET = CCM_ANALOG_PLL_VIDEO_BYPASS_MASK; CCM_ANALOG->PLL_VIDEO_CLR = CCM_ANALOG_PLL_VIDEO_POWERDOWN_MASK; CCM_ANALOG->PLL_VIDEO = s_clockContext.pllVideo; if ((CCM_ANALOG->PLL_VIDEO & CCM_ANALOG_PLL_VIDEO_POWERDOWN_MASK) == 0) { while ((CCM_ANALOG->PLL_VIDEO & CCM_ANALOG_PLL_VIDEO_LOCK_MASK) == 0) {} } /* Restore ENET PLL */ CCM_ANALOG->PLL_ENET_SET = CCM_ANALOG_PLL_ENET_BYPASS_MASK; CCM_ANALOG->PLL_ENET_SET = CCM_ANALOG_PLL_ENET_POWERDOWN_MASK; CCM_ANALOG->PLL_ENET = s_clockContext.pllEnet; if ((CCM_ANALOG->PLL_ENET & CCM_ANALOG_PLL_ENET_POWERDOWN_MASK) == 0) { while ((CCM_ANALOG->PLL_ENET & CCM_ANALOG_PLL_ENET_LOCK_MASK) == 0) {} } /* Restore SYS PLL PFD */ CCM_ANALOG->PFD_528 = s_clockContext.pfd528; s_clockContext.is_valid = 0; } static void LPM_DisableRbcBypass(void) { uint32_t gpcIMR[LPM_GPC_IMR_NUM]; uint32_t i; /* Mask all GPC interrupts before disabling the RBC counters */ for (i = 0; i < LPM_GPC_IMR_NUM; i++) { gpcIMR[i] = GPC->IMR[i]; GPC->IMR[i] = 0xFFFFFFFFU; } /* Disable the RBC bypass counter */ CCM->CCR &= ~CCM_CCR_RBC_EN_MASK; CCM->CCR &= ~CCM_CCR_REG_BYPASS_COUNT_MASK; /* Now delay for 2 CKIL cycles (61usec). ARM is at 528MHz at this point * so a short loop should be enough. */ for (i = 0; i < 528 * 22; i++) { __NOP(); } /* Recover all the GPC interrupts. */ for (i = 0; i < LPM_GPC_IMR_NUM; i++) { GPC->IMR[i] = gpcIMR[i]; } } static void LPM_SystemWait(void) { LPM_DisablePLLs(APP_GetLPMPowerMode()); /* Adjust voltage to 1.15V */ DCDC_AdjustTargetVoltage(DCDC, 0xE, 0x1); } static void LPM_SystemRestoreWait(void) { /* Restore voltage to 1.25V */ DCDC_AdjustTargetVoltage(DCDC, 0x12, 0x1); LPM_RestorePLLs(LPM_PowerModeSysIdle); } static void LPM_SystemIdle(void) { /* Switch DCDC to use DCDC internal OSC */ DCDC_SetClockSource(DCDC, kDCDC_ClockInternalOsc); /* Power down USBPHY */ USBPHY1->CTRL = 0xFFFFFFFF; USBPHY2->CTRL = 0xFFFFFFFF; LPM_DisablePLLs(APP_GetLPMPowerMode()); /* Enable weak 2P5 and turn off regular 2P5 */ PMU->REG_2P5 |= PMU_REG_2P5_ENABLE_WEAK_LINREG_MASK; PMU->REG_2P5 &= ~PMU_REG_2P5_ENABLE_LINREG_MASK; /* Enable weak 1P1 and turn off regular 1P1 */ PMU->REG_1P1 |= PMU_REG_1P1_ENABLE_WEAK_LINREG_MASK; PMU->REG_1P1 &= ~PMU_REG_1P1_ENABLE_LINREG_MASK; LPM_EnterCritical(); LPM_SwitchToRcOSC(); LPM_ExitCritical(); /* Lower OSC current by 37.5% */ CCM_ANALOG->MISC0_SET = CCM_ANALOG_MISC0_OSC_I_MASK; /* Enable FET ODRIVE */ PMU->REG_CORE_SET = PMU_REG_CORE_FET_ODRIVE_MASK; /* Disconnect vdd_high_in and connect vdd_snvs_in */ CCM_ANALOG->MISC0_SET = CCM_ANALOG_MISC0_DISCON_HIGH_SNVS_MASK; DCDC_AdjustTargetVoltage(DCDC, 0x6, 0x1); LPM_EnterCritical(); LPM_SwitchBandgap(); LPM_ExitCritical(); /* RBC = 0; Enable COSC, OSC COUNT = 0xAF */ CCM->CCR = (CCM_CCR_COSC_EN_MASK | CCM_CCR_OSCNT(0xAF)); s_SystemIdleFlag = 1; } void LPM_SystemRestoreIdle(void) { DCDC_AdjustTargetVoltage(DCDC, 0x12, 0x1); /* Switch DCDC to use DCDC internal OSC */ DCDC_SetClockSource(DCDC, kDCDC_ClockExternalOsc); /* Disconnect vdd_snvs_in and connect vdd_high_in */ CCM_ANALOG->MISC0_CLR = CCM_ANALOG_MISC0_DISCON_HIGH_SNVS_MASK; /* Increase OSC current to normal */ CCM_ANALOG->MISC0_CLR = CCM_ANALOG_MISC0_OSC_I_MASK; /* Clear FET ODRIVE */ PMU->REG_CORE_CLR = PMU_REG_CORE_FET_ODRIVE_MASK; LPM_EnterCritical(); LPM_SwitchToXtalOSC(); LPM_RestoreBandgap(); LPM_ExitCritical(); /* Enable regular 2P5 and wait for stable */ PMU->REG_2P5_SET = PMU_REG_2P5_ENABLE_LINREG_MASK; while ((PMU->REG_2P5 & PMU_REG_2P5_OK_VDD2P5_MASK) == 0) {} /* Turn off weak 2P5 */ PMU->REG_2P5_CLR = PMU_REG_2P5_ENABLE_WEAK_LINREG_MASK; /* Enable regular 1P1 and wait for stable */ PMU->REG_1P1_SET = PMU_REG_1P1_ENABLE_LINREG_MASK; while ((PMU->REG_1P1 & PMU_REG_1P1_OK_VDD1P1_MASK) == 0) {} /* Turn off weak 1P1 */ PMU->REG_1P1_CLR = PMU_REG_1P1_ENABLE_WEAK_LINREG_MASK; /* If s_SkipRestorePLLs is set, skip the process to restore the PLLs. */ if (!s_SkipRestorePLLs) { LPM_RestorePLLs(LPM_PowerModeLPIdle); } s_SkipRestorePLLs = 0; s_SystemIdleFlag = 0; } static void LPM_SystemDsm() { uint32_t i; uint32_t gpcIMR[LPM_GPC_IMR_NUM]; BOARD_SetLPClockGate(); /* Turn off FlexRAM1 */ PGC->MEGA_CTRL |= PGC_MEGA_CTRL_PCR_MASK; /* Turn off FlexRAM0 */ GPC->CNTR |= GPC_CNTR_PDRAM0_PGE_MASK; /* Clean and disable data cache to make sure context is saved into DDR */ SCB_CleanDCache(); SCB_DisableDCache(); /* Adjust LP voltage to 0.925V */ DCDC_AdjustTargetVoltage(DCDC, 0x12, 0x1); /* Switch DCDC to use DCDC internal OSC */ DCDC_SetClockSource(DCDC, kDCDC_ClockInternalOsc); /* Power down USBPHY */ USBPHY1->CTRL = 0xFFFFFFFF; USBPHY2->CTRL = 0xFFFFFFFF; /* Power down CPU when requested */ PGC->CPU_CTRL = PGC_CPU_CTRL_PCR_MASK; PMU->REG_CORE_SET = PMU_REG_CORE_FET_ODRIVE_MASK; /* Disconnect vdd_high_in and connect vdd_snvs_in */ CCM_ANALOG->MISC0_SET = CCM_ANALOG_MISC0_DISCON_HIGH_SNVS_MASK; /* STOP_MODE config, turn off all analog except RTC in stop mode */ CCM_ANALOG->MISC0_CLR = CCM_ANALOG_MISC0_STOP_MODE_CONFIG_MASK; /* Mask all GPC interrupts before enabling the RBC counters to avoid the counter starting too early if an interupt is already pending. */ for (i = 0; i < LPM_GPC_IMR_NUM; i++) { gpcIMR[i] = GPC->IMR[i]; GPC->IMR[i] = 0xFFFFFFFFU; } /* * ERR006223: CCM: Failure to resuem from wait/stop mode with power gating * Configure REG_BYPASS_COUNTER to 2 * Enable the RBC bypass counter here to hold off the interrupts. RBC counter * needs to be no less than 2. */ CCM->CCR = (CCM->CCR & ~CCM_CCR_REG_BYPASS_COUNT_MASK) | CCM_CCR_REG_BYPASS_COUNT(2); CCM->CCR |= (CCM_CCR_RBC_EN_MASK | CCM_CCR_COSC_EN_MASK | CCM_CCR_OSCNT(0xAF)); /* Recover all the GPC interrupts. */ for (i = 0; i < LPM_GPC_IMR_NUM; i++) { GPC->IMR[i] = gpcIMR[i]; } /* Now delay for a short while (3usec) ARM is at 528MHz at this point * so a short loop should be enough. This delay is required to ensure that * the RBC counter can start counting in case an interrupt is already pending * or in case an interrupt arrives just as ARM is about to assert DSM_request. */ for (i = 0; i < 22 * 24; i++) { __NOP(); } } void LPM_SystemRestoreDsm(void) { /* Enable clock of ARM platform memories when entering LPM */ CCM->CGPR |= CCM_CGPR_INT_MEM_CLK_LPM_MASK; /* Clear ARM power gate setting */ PGC->CPU_CTRL &= ~PGC_CPU_CTRL_PCR_MASK; /* Keep megamix power on when STOP */ PGC->MEGA_CTRL &= ~PGC_MEGA_CTRL_PCR_MASK; /* Clear FET ODRIVE */ PMU->REG_CORE_CLR = PMU_REG_CORE_FET_ODRIVE_MASK; /* Disconnect vdd_snvs_in and connect vdd_high_in */ CCM_ANALOG->MISC0_CLR = CCM_ANALOG_MISC0_DISCON_HIGH_SNVS_MASK; } void LPM_SystemResumeDsm(void) { uint32_t clpcr; clpcr = CCM->CLPCR & (~(CCM_CLPCR_LPM_MASK | CCM_CLPCR_ARM_CLK_DIS_ON_LPM_MASK)); GPC->CNTR &= ~GPC_CNTR_PDRAM0_PGE_MASK; /* Clear resume entry */ SRC->GPR[0] = 0U; SRC->GPR[1] = 0U; LPM_SystemRestoreDsm(); /* RBC bypass enabled in LPM_SystemDsm */ LPM_DisableRbcBypass(); LPM_SetClockMode(kCLOCK_ModeRun, clpcr); // LPM_DisableWakeupSource(vPortGetGptIrqn()); } void LPM_SystemOverRunRecovery(void) { /* Switch DCDC to use DCDC internal OSC */ DCDC_SetClockSource(DCDC, kDCDC_ClockExternalOsc); /* Disconnect vdd_snvs_in and connect vdd_high_in */ CCM_ANALOG->MISC0_CLR = CCM_ANALOG_MISC0_DISCON_HIGH_SNVS_MASK; /* Increase OSC current to normal */ CCM_ANALOG->MISC0_CLR = CCM_ANALOG_MISC0_OSC_I_MASK; /* Restore XTAL-OSC and enable detector */ CLOCK_InitExternalClk(false); /* Switch to XTAL-OSC */ LPM_EnterCritical(); LPM_SwitchToXtalOSC(); LPM_RestoreBandgap(); LPM_ExitCritical(); /* Enable regular 2P5 and wait for stable */ PMU->REG_2P5_SET = PMU_REG_2P5_ENABLE_LINREG_MASK; while ((PMU->REG_2P5 & PMU_REG_2P5_OK_VDD2P5_MASK) == 0) {} /* Turn off weak 2P5 */ PMU->REG_2P5_CLR = PMU_REG_2P5_ENABLE_WEAK_LINREG_MASK; /* Enable regular 1P1 and wait for stable */ PMU->REG_1P1_SET = PMU_REG_1P1_ENABLE_LINREG_MASK; while ((PMU->REG_1P1 & PMU_REG_1P1_OK_VDD1P1_MASK) == 0) {} /* Turn off weak 1P1 */ PMU->REG_1P1_CLR = PMU_REG_1P1_ENABLE_WEAK_LINREG_MASK; LPM_RestorePLLs(LPM_PowerModeOverRun); s_SystemIdleFlag = 0; } void LPM_SystemOverRun(void) { // change the DCDC_LP to 1.25V first DCDC_AdjustTargetVoltage(DCDC, 0x12, 0x1); LPM_SystemOverRunRecovery(); CLOCK_SetMux(kCLOCK_PeriphClk2Mux, 1); CLOCK_SetDiv(kCLOCK_PeriphClk2Div, 0); CLOCK_SetMux(kCLOCK_PeriphMux, 1); CLOCK_SetDiv(kCLOCK_IpgDiv, 0); CLOCK_SetDiv(kCLOCK_AhbDiv, 0); /*ARM PLL as clksource*/ /* 24 * 100 / 2 / 2 = 600MHz */ CCM_ANALOG->PLL_ARM_CLR = CCM_ANALOG_PLL_ARM_POWERDOWN_MASK; CCM_ANALOG->PLL_ARM_SET = CCM_ANALOG_PLL_ARM_ENABLE_MASK | CCM_ANALOG_PLL_ARM_BYPASS_MASK; CLOCK_SetDiv(kCLOCK_ArmDiv, 0x1); CCM_ANALOG->PLL_ARM_CLR = CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK; CCM_ANALOG->PLL_ARM_SET = CCM_ANALOG_PLL_ARM_DIV_SELECT(100); CCM_ANALOG->PLL_ARM_CLR = CCM_ANALOG_PLL_ARM_BYPASS_MASK; CLOCK_SetMux(kCLOCK_PrePeriphMux, 0x3); CLOCK_SetDiv(kCLOCK_IpgDiv, 0x3); CLOCK_SetDiv(kCLOCK_AhbDiv, 0); CLOCK_SetMux(kCLOCK_PeriphMux, 0x0); CLOCK_SetMux(kCLOCK_PerclkMux, 1); CLOCK_SetDiv(kCLOCK_PerclkDiv, 0); } void LPM_SystemFullRun(void) { /* Back to Over Run first */ LPM_SystemOverRun(); CLOCK_SetMux(kCLOCK_PeriphClk2Mux, 1); CLOCK_SetDiv(kCLOCK_PeriphClk2Div, 0); CLOCK_SetMux(kCLOCK_PeriphMux, 1); CLOCK_SetDiv(kCLOCK_IpgDiv, 0x1); CLOCK_SetDiv(kCLOCK_AhbDiv, 0x0); /* ARM PLL as clksource*/ /* 24 * 86 / 2 / 2 = 516MHz */ CCM_ANALOG->PLL_ARM_CLR = CCM_ANALOG_PLL_ARM_POWERDOWN_MASK; CCM_ANALOG->PLL_ARM_SET = CCM_ANALOG_PLL_ARM_ENABLE_MASK | CCM_ANALOG_PLL_ARM_BYPASS_MASK; CLOCK_SetDiv(kCLOCK_ArmDiv, 0x1); CCM_ANALOG->PLL_ARM_CLR = CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK; CCM_ANALOG->PLL_ARM_SET = CCM_ANALOG_PLL_ARM_DIV_SELECT(86); CCM_ANALOG->PLL_ARM_CLR = CCM_ANALOG_PLL_ARM_BYPASS_MASK; /* Select ARM_PLL for pre_periph_clock */ CLOCK_SetMux(kCLOCK_PrePeriphMux, 3); CLOCK_SetDiv(kCLOCK_IpgDiv, 0x3); CLOCK_SetDiv(kCLOCK_AhbDiv, 0); CLOCK_SetMux(kCLOCK_PeriphMux, 0x0); CLOCK_SetMux(kCLOCK_PerclkMux, 1); CLOCK_SetDiv(kCLOCK_PerclkDiv, 0); /* change the DCDC_LP to 1.15V */ DCDC_AdjustTargetVoltage(DCDC, 0xE, 0x1); } void LPM_SystemLowSpeedRun(void) { /* Back to Over Run first */ LPM_SystemOverRun(); LPM_SystemWait(); } void LPM_SystemLowPowerRun(void) { /* Back to Over Run first */ LPM_SystemOverRun(); LPM_SystemIdle(); } #ifdef FSL_RTOS_FREE_RTOS #if (configUSE_TICKLESS_IDLE == 1) void LPM_InitTicklessTimer(void) { gpt_config_t gptConfig; /* Init GPT for wakeup as FreeRTOS tell us */ GPT_GetDefaultConfig(&gptConfig); gptConfig.clockSource = kGPT_ClockSource_LowFreq; /* 32K RTC OSC */ // gptConfig.enableMode = false; /* Keep counter when stop */ gptConfig.enableMode = true; /* Don't keep counter when stop */ gptConfig.enableRunInDoze = true; /* Initialize GPT module */ GPT_Init(vPortGetGptBase(), &gptConfig); GPT_SetClockDivider(vPortGetGptBase(), 1); /* Enable GPT Output Compare1 interrupt */ GPT_EnableInterrupts(vPortGetGptBase(), kGPT_OutputCompare1InterruptEnable); NVIC_SetPriority(vPortGetGptIrqn(), configMAX_SYSCALL_INTERRUPT_PRIORITY + 2); EnableIRQ(vPortGetGptIrqn()); } #endif #endif bool LPM_Init(lpm_power_mode_t run_mode) { uint32_t i; uint32_t tmp_reg = 0; /* Boot ROM did initialize the XTAL, here we only sets external XTAL OSC freq */ CLOCK_SetXtalFreq(BOARD_XTAL0_CLK_HZ); CLOCK_SetRtcXtalFreq(BOARD_XTAL32K_CLK_HZ); /* Recover handshaking */ IOMUXC_GPR->GPR4 = 0x00000000; IOMUXC_GPR->GPR7 = 0x00000000; IOMUXC_GPR->GPR8 = 0x00000000; IOMUXC_GPR->GPR12 = 0x00000000; CCM->CCR &= ~CCM_CCR_REG_BYPASS_COUNT_MASK; s_targetPowerMode = run_mode; #ifdef FSL_RTOS_FREE_RTOS s_mutex = xSemaphoreCreateMutex(); if (s_mutex == NULL) { return false; } s_listenerHead = s_listenerTail = NULL; #if (configUSE_TICKLESS_IDLE == 1) LPM_InitTicklessTimer(); #endif #endif if (run_mode > LPM_PowerModeRunEnd) { return false; } s_curMode = run_mode; CLOCK_SetMode(kCLOCK_ModeRun); CCM->CGPR |= CCM_CGPR_INT_MEM_CLK_LPM_MASK; /* 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 */ LPM_DELAY(40); /* 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; s_DllBackupValue = FLEXSPI->DLLCR[0]; /* ERR007265 */ IOMUXC_GPR->GPR1 |= IOMUXC_GPR_GPR1_GINT_MASK; /* Initialize GPC to mask all IRQs */ for (i = 0; i < LPM_GPC_IMR_NUM; i++) { GPC->IMR[i] = 0xFFFFFFFFU; } return true; } void LPM_Deinit(void) { #ifdef FSL_RTOS_FREE_RTOS if (s_mutex != NULL) { vSemaphoreDelete(s_mutex); s_mutex = NULL; } #endif /* ERR007265 */ IOMUXC_GPR->GPR1 &= ~IOMUXC_GPR_GPR1_GINT_MASK; } void LPM_EnableWakeupSource(uint32_t irq) { GPC_EnableIRQ(GPC, irq); } void LPM_DisableWakeupSource(uint32_t irq) { GPC_DisableIRQ(GPC, irq); } bool LPM_SetPowerMode(lpm_power_mode_t mode) { #ifdef FSL_RTOS_FREE_RTOS /* Only FreeRTOS supports listener notification */ lpm_power_mode_listener_t *l1, *l2; #endif bool ret = true; if (mode == s_curMode) { return ret; } #ifdef FSL_RTOS_FREE_RTOS /* Only FreeRTOS supports listener notification */ /* Need to make sure the list of listeners is not changed * when traversing the list. */ xSemaphoreTake(s_mutex, portMAX_DELAY); for (l1 = s_listenerHead; l1 != NULL; l1 = l1->next) { if (l1->callback == NULL) { continue; } if (!l1->callback(s_curMode, mode, l1->data)) { /* One stakeholder doesn't allow new mode */ ret = false; break; } } if (ret) { s_curMode = mode; } else { /* roll back the state change of previous listeners */ for (l2 = s_listenerHead; l2 != l1; l2 = l2->next) { if (l2->callback == NULL) { continue; } l2->callback(mode, s_curMode, l2->data); } } xSemaphoreGive(s_mutex); #else s_curMode = mode; #endif /* FSL_RTOS_FREE_RTOS */ return ret; } #ifdef FSL_RTOS_FREE_RTOS /* Only FreeRTOS supports listener notification */ void LPM_RegisterPowerListener(lpm_power_mode_callback_t callback, void *data) { lpm_power_mode_listener_t *l = (lpm_power_mode_listener_t *)pvPortMalloc(sizeof(lpm_power_mode_listener_t)); assert(l); l->callback = callback; l->data = data; l->next = NULL; xSemaphoreTake(s_mutex, portMAX_DELAY); if (s_listenerHead) { s_listenerTail->next = l; s_listenerTail = l; } else { s_listenerHead = s_listenerTail = l; } xSemaphoreGive(s_mutex); } void LPM_UnregisterPowerListener(lpm_power_mode_callback_t callback, void *data) { lpm_power_mode_listener_t *l, *p = NULL; xSemaphoreTake(s_mutex, portMAX_DELAY); for (l = s_listenerHead; l != NULL; l = l->next) { if (l->callback == callback && l->data == data) { if (p) { p->next = l->next; } else { s_listenerHead = l->next; } if (l->next == NULL) { s_listenerTail = p; } vPortFree(l); break; } p = l; } xSemaphoreGive(s_mutex); } #endif /************ Internal public API start **************/ #ifdef FSL_RTOS_FREE_RTOS #if (configUSE_TICKLESS_IDLE == 1) GPT_Type *vPortGetGptBase(void) { return GPT1; } IRQn_Type vPortGetGptIrqn(void) { return GPT1_IRQn; } void vPortPRE_SLEEP_PROCESSING(TickType_t timeoutMilliSec) { uint32_t clpcr; APP_PowerPreSwitchHook(APP_GetLPMPowerMode()); LPM_EnableWakeupSource(vPortGetGptIrqn()); clpcr = CCM->CLPCR & (~(CCM_CLPCR_LPM_MASK | CCM_CLPCR_ARM_CLK_DIS_ON_LPM_MASK)); switch (APP_GetLPMPowerMode()) { case LPM_PowerModeOverRun: case LPM_PowerModeFullRun: case LPM_PowerModeLowSpeedRun: case LPM_PowerModeLowPowerRun: break; case LPM_PowerModeSysIdle: LPM_SetClockMode(kCLOCK_ModeWait, clpcr | CCM_CLPCR_LPM(kCLOCK_ModeWait) | CCM_CLPCR_ARM_CLK_DIS_ON_LPM_MASK | CCM_CLPCR_STBY_COUNT_MASK | 0x1C | 0x08280000); BOARD_SetLPClockGate(); /* If last mode is idle, need to restore idle states. */ if (s_SystemIdleFlag) { s_SkipRestorePLLs = 1; LPM_SystemRestoreIdle(); } LPM_SystemWait(); IOMUXC_GPR->GPR8 = 0xaaaaaaaa; IOMUXC_GPR->GPR12 = 0x0000000a; break; case LPM_PowerModeLPIdle: LPM_SetClockMode(kCLOCK_ModeWait, clpcr | CCM_CLPCR_LPM(kCLOCK_ModeWait) | CCM_CLPCR_ARM_CLK_DIS_ON_LPM_MASK | CCM_CLPCR_STBY_COUNT_MASK | 0x1C | 0x08280000); BOARD_SetLPClockGate(); LPM_SystemIdle(); IOMUXC_GPR->GPR8 = 0xaaaaaaaa; IOMUXC_GPR->GPR12 = 0x0000000a; /* put SDRAM to self-refresh mode */ IOMUXC_GPR->GPR4 = (1 << 9U); while ((IOMUXC_GPR->GPR4 & (1 << 25U)) != (1 << 25U)) {}; break; case LPM_PowerModeSuspend: LPM_SetClockMode(kCLOCK_ModeStop, clpcr | CCM_CLPCR_LPM(kCLOCK_ModeStop) | CCM_CLPCR_VSTBY_MASK | CCM_CLPCR_STBY_COUNT_MASK | CCM_CLPCR_SBYOS_MASK | CCM_CLPCR_ARM_CLK_DIS_ON_LPM_MASK | 0x08280000); LPM_SystemDsm(); // LPM_CoreStateSave(); break; default: assert(false); break; } } void vPortPOST_SLEEP_PROCESSING(TickType_t timeoutMilliSec) { uint32_t clpcr; clpcr = CCM->CLPCR & (~(CCM_CLPCR_LPM_MASK | CCM_CLPCR_ARM_CLK_DIS_ON_LPM_MASK)); switch (APP_GetLPMPowerMode()) { case LPM_PowerModeOverRun: case LPM_PowerModeFullRun: case LPM_PowerModeLowSpeedRun: case LPM_PowerModeLowPowerRun: break; case LPM_PowerModeSysIdle: IOMUXC_GPR->GPR8 = 0x00000000; IOMUXC_GPR->GPR12 = 0x00000000; LPM_SystemRestoreWait(); LPM_SetClockMode(kCLOCK_ModeRun, clpcr); break; case LPM_PowerModeLPIdle: __NOP(); __NOP(); __NOP(); __NOP(); /* restore SDRAM from self-refresh mode */ IOMUXC_GPR->GPR4 = (0 << 9U); while ((IOMUXC_GPR->GPR4 & (1 << 25U)) != (1 << 25U)) {}; IOMUXC_GPR->GPR8 = 0x00000000; IOMUXC_GPR->GPR12 = 0x00000000; /* Interrupt occurs before system idle */ LPM_SystemRestoreIdle(); LPM_SetClockMode(kCLOCK_ModeRun, clpcr); break; case LPM_PowerModeSuspend: /* Restore when wakeup from suspend reset */ LPM_SystemResumeDsm(); /* recover handshaking */ IOMUXC_GPR->GPR4 = 0x00000000; IOMUXC_GPR->GPR7 = 0x00000000; IOMUXC_GPR->GPR8 = 0x00000000; IOMUXC_GPR->GPR12 = 0x00000000; CCM->CCR &= ~CCM_CCR_REG_BYPASS_COUNT_MASK; break; default: assert(false); break; } LPM_DisableWakeupSource(vPortGetGptIrqn()); APP_PowerPostSwitchHook(APP_GetLPMPowerMode()); } #endif /* configUSE_TICKLESS_IDLE */ #endif void APP_PowerPreSwitchHook(lpm_power_mode_t targetMode) { if (targetMode == LPM_PowerModeSuspend || targetMode == LPM_PowerModeSNVS) { /* Wait for debug console output finished. */ while (!(kLPUART_TransmissionCompleteFlag & LPUART_GetStatusFlags((LPUART_Type *)BOARD_DEBUG_UART_BASEADDR))) {} // DbgConsole_Deinit(); /* * Set pin for current leakage. * Debug console RX pin: Set to pinmux to GPIO input. * Debug console TX pin: Don't need to change. */ IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_13_GPIO1_IO13, 0); IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_13_GPIO1_IO13, IOMUXC_SW_PAD_CTL_PAD_PKE_MASK | IOMUXC_SW_PAD_CTL_PAD_PUS(2) | IOMUXC_SW_PAD_CTL_PAD_PUE_MASK); } } void APP_PowerPostSwitchHook(lpm_power_mode_t targetMode) { if (targetMode == LPM_PowerModeSuspend) { /* * Debug console RX pin is set to GPIO input, nee to re-configure pinmux. * Debug console TX pin: Don't need to change. */ IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_13_LPUART1_RX, 0); IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_13_LPUART1_RX, IOMUXC_SW_PAD_CTL_PAD_SPEED(2)); // BOARD_InitDebugConsole(); } } lpm_power_mode_t APP_GetLPMPowerMode(void) { return s_targetPowerMode; } void APP_SetLPMPowerMode(lpm_power_mode_t mode) { s_targetPowerMode = mode; }