/* * Copyright 2022-2023 NXP * SPDX-License-Identifier: Apache-2.0 */ #include #include "fsl_power.h" #include #include "board.h" #ifdef CONFIG_FLASH_MCUX_FLEXSPI_XIP #include "flash_clock_setup.h" #endif /* OTP fuse index. */ #define FRO_192MHZ_SC_TRIM 0x2C #define FRO_192MHZ_RD_TRIM 0x2B #define FRO_96MHZ_SC_TRIM 0x2E #define FRO_96MHZ_RD_TRIM 0x2D #define OTP_FUSE_READ_API ((void (*)(uint32_t addr, uint32_t *data))0x1300805D) #define PMIC_MODE_BOOT 0U #define PMIC_MODE_DEEP_SLEEP 1U #define PMIC_MODE_FRO192M_900MV 2U #define PMIC_MODE_FRO96M_800MV 3U #define PMIC_SETTLING_TIME 2000U /* in micro-seconds */ static uint32_t sc_trim_192, rd_trim_192, sc_trim_96, rd_trim_96; #if CONFIG_REGULATOR #include #define NODE_PCA9420 DT_NODELABEL(pca9420) #define NODE_SW1 DT_NODELABEL(pca9420_sw1) #define NODE_SW2 DT_NODELABEL(pca9420_sw2) #define NODE_LDO1 DT_NODELABEL(pca9420_ldo1) #define NODE_LDO2 DT_NODELABEL(pca9420_ldo2) static const struct device *pca9420 = DEVICE_DT_GET(NODE_PCA9420); static const struct device *sw1 = DEVICE_DT_GET(NODE_SW1); static const struct device *sw2 = DEVICE_DT_GET(NODE_SW2); static const struct device *ldo1 = DEVICE_DT_GET(NODE_LDO1); static const struct device *ldo2 = DEVICE_DT_GET(NODE_LDO2); static int current_power_profile; #define MEGA (1000000U) /* Core frequency levels number. */ #define POWER_FREQ_LEVELS_NUM (5U) /* Invalid voltage level. */ #define POWER_INVALID_VOLT_LEVEL (0xFFFFFFFFU) static const uint32_t power_freq_level[POWER_FREQ_LEVELS_NUM] = { 275U * MEGA, 230U * MEGA, 192U * MEGA, 100U * MEGA, 60U * MEGA }; /* System clock frequency. */ extern uint32_t SystemCoreClock; static const int32_t sw1_volt[] = {1100000, 1000000, 900000, 800000, 700000}; static int32_t board_calc_volt_level(void) { uint32_t i; uint32_t volt; for (i = 0U; i < POWER_FREQ_LEVELS_NUM; i++) { if (SystemCoreClock > power_freq_level[i]) { break; } } /* Frequency exceed max supported */ if (i == 0U) { volt = POWER_INVALID_VOLT_LEVEL; } else { volt = sw1_volt[i - 1U]; } return volt; } static int board_config_pmic(void) { uint32_t volt; int ret = 0; volt = board_calc_volt_level(); ret = regulator_set_voltage(sw1, volt, volt); if (ret != 0) { return ret; } ret = regulator_set_voltage(sw2, 1800000, 1800000); if (ret != 0) { return ret; } ret = regulator_set_voltage(ldo1, 1800000, 1800000); if (ret != 0) { return ret; } ret = regulator_set_voltage(ldo2, 3300000, 3300000); if (ret != 0) { return ret; } /* We can enter deep low power modes */ pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES); return ret; } static int board_pmic_change_mode(uint8_t pmic_mode) { int ret; if (pmic_mode >= 4) { return -ERANGE; } ret = regulator_parent_dvs_state_set(pca9420, pmic_mode); if (ret != -EPERM) { return ret; } POWER_SetPmicMode(pmic_mode, kCfg_Run); k_busy_wait(PMIC_SETTLING_TIME); return 0; } /* Changes power-related config to preset profiles, like clocks and VDDCORE voltage */ __ramfunc int32_t power_manager_set_profile(uint32_t power_profile) { bool voltage_changed = false; int32_t current_uv, future_uv; int ret; if (power_profile == current_power_profile) { return 0; } /* Confirm valid power_profile, and read the new VDDCORE voltage */ switch (power_profile) { case POWER_PROFILE_AFTER_BOOT: future_uv = DT_PROP(NODE_SW1, nxp_mode0_microvolt); break; case POWER_PROFILE_FRO192M_900MV: future_uv = DT_PROP(NODE_SW1, nxp_mode2_microvolt); break; case POWER_PROFILE_FRO96M_800MV: future_uv = DT_PROP(NODE_SW1, nxp_mode3_microvolt); break; default: return -EINVAL; } if (current_power_profile == POWER_PROFILE_AFTER_BOOT) { /* One-Time optimization after boot */ POWER_DisableLVD(); CLOCK_AttachClk(kFRO_DIV1_to_MAIN_CLK); /* Set SYSCPUAHBCLKDIV divider to value 1 */ CLOCK_SetClkDiv(kCLOCK_DivSysCpuAhbClk, 1U); /* Other clock optimizations */ #ifdef CONFIG_FLASH_MCUX_FLEXSPI_XIP flexspi_setup_clock(FLEXSPI0, 0U, 1U); /* main_clk div by 1 */ #endif /* Disable the PFDs of SYSPLL */ CLKCTL0->SYSPLL0PFD |= CLKCTL0_SYSPLL0PFD_PFD0_CLKGATE_MASK | CLKCTL0_SYSPLL0PFD_PFD1_CLKGATE_MASK | CLKCTL0_SYSPLL0PFD_PFD2_CLKGATE_MASK; POWER_EnablePD(kPDRUNCFG_PD_SYSPLL_LDO); POWER_EnablePD(kPDRUNCFG_PD_SYSPLL_ANA); POWER_EnablePD(kPDRUNCFG_PD_AUDPLL_LDO); POWER_EnablePD(kPDRUNCFG_PD_AUDPLL_ANA); POWER_EnablePD(kPDRUNCFG_PD_SYSXTAL); /* Configure MCU that PMIC supplies will be powered in all * PMIC modes */ PMC->PMICCFG = 0xFF; } /* Get current and future PMIC voltages to determine DVFS sequence */ ret = regulator_get_voltage(sw1, ¤t_uv); if (ret) { return ret; } if (power_profile == POWER_PROFILE_FRO192M_900MV) { /* check if voltage or frequency change is first */ if (future_uv > current_uv) { /* Increase voltage first before frequencies */ ret = board_pmic_change_mode(PMIC_MODE_FRO192M_900MV); if (ret) { return ret; } voltage_changed = true; } /* Trim FRO to 192MHz */ CLKCTL0->FRO_SCTRIM = sc_trim_192; CLKCTL0->FRO_RDTRIM = rd_trim_192; /* Reset the EXP_COUNT. */ CLKCTL0->FRO_CONTROL &= ~CLKCTL0_FRO_CONTROL_EXP_COUNT_MASK; CLOCK_AttachClk(kFRO_DIV1_to_MAIN_CLK); /* Set SYSCPUAHBCLKDIV divider to value 1 */ CLOCK_SetClkDiv(kCLOCK_DivSysCpuAhbClk, 1U); if (voltage_changed == false) { ret = board_pmic_change_mode(PMIC_MODE_FRO192M_900MV); if (ret) { return ret; } } } else if (power_profile == POWER_PROFILE_FRO96M_800MV) { /* This PMIC mode is the lowest voltage used for DVFS, * Reduce frequency first, and then reduce voltage */ /* Trim the FRO to 96MHz */ CLKCTL0->FRO_SCTRIM = sc_trim_96; CLKCTL0->FRO_RDTRIM = rd_trim_96; /* Reset the EXP_COUNT. */ CLKCTL0->FRO_CONTROL &= ~CLKCTL0_FRO_CONTROL_EXP_COUNT_MASK; CLOCK_AttachClk(kFRO_DIV1_to_MAIN_CLK); /* Set SYSCPUAHBCLKDIV divider to value 1 */ CLOCK_SetClkDiv(kCLOCK_DivSysCpuAhbClk, 1U); ret = board_pmic_change_mode(PMIC_MODE_FRO96M_800MV); if (ret) { return ret; } } current_power_profile = power_profile; return 0; } #endif /* CONFIG_REGULATOR */ static int mimxrt595_evk_init(void) { /* Set the correct voltage range according to the board. */ power_pad_vrange_t vrange = { .Vdde0Range = kPadVol_171_198, .Vdde1Range = kPadVol_171_198, .Vdde2Range = kPadVol_171_198, .Vdde3Range = kPadVol_300_360, .Vdde4Range = kPadVol_171_198 }; POWER_SetPadVolRange(&vrange); /* Do not enter deep low power modes until the PMIC modes have been initialized */ pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES); #ifdef CONFIG_I2S #ifdef CONFIG_I2S_TEST_SEPARATE_DEVICES /* Set shared signal set 0 SCK, WS from Transmit I2S - Flexcomm3 */ SYSCTL1->SHAREDCTRLSET[0] = SYSCTL1_SHAREDCTRLSET_SHAREDSCKSEL(3) | SYSCTL1_SHAREDCTRLSET_SHAREDWSSEL(3); /* Select Data in from Transmit I2S - Flexcomm 3 */ SYSCTL1->SHAREDCTRLSET[0] |= SYSCTL1_SHAREDCTRLSET_SHAREDDATASEL(3); /* Enable Transmit I2S - Flexcomm 3 for Shared Data Out */ SYSCTL1->SHAREDCTRLSET[0] |= SYSCTL1_SHAREDCTRLSET_FC3DATAOUTEN(1); #else /* Set shared signal set 0: SCK, WS from Flexcomm1 */ SYSCTL1->SHAREDCTRLSET[0] = SYSCTL1_SHAREDCTRLSET_SHAREDSCKSEL(1) | SYSCTL1_SHAREDCTRLSET_SHAREDWSSEL(1); #endif /* Set Receive I2S - Flexcomm 1 SCK, WS from shared signal set 0 */ SYSCTL1->FCCTRLSEL[1] = SYSCTL1_FCCTRLSEL_SCKINSEL(1) | SYSCTL1_FCCTRLSEL_WSINSEL(1); /* Set Transmit I2S - Flexcomm 3 SCK, WS from shared signal set 0 */ SYSCTL1->FCCTRLSEL[3] = SYSCTL1_FCCTRLSEL_SCKINSEL(1) | SYSCTL1_FCCTRLSEL_WSINSEL(1); #ifdef CONFIG_I2S_TEST_SEPARATE_DEVICES /* Select Receive I2S - Flexcomm 1 Data in from shared signal set 0 */ SYSCTL1->FCCTRLSEL[1] |= SYSCTL1_FCCTRLSEL_DATAINSEL(1); /* Select Transmit I2S - Flexcomm 3 Data out to shared signal set 0 */ SYSCTL1->FCCTRLSEL[3] |= SYSCTL1_FCCTRLSEL_DATAOUTSEL(1); #endif /* CONFIG_I2S_TEST_SEPARATE_DEVICES */ #endif /* CONFIG_I2S */ #ifdef CONFIG_REBOOT /* * The sys_reboot API calls NVIC_SystemReset. On the RT595, the warm * reset will not complete correctly unless the ROM toggles the * flash reset pin. We can control this behavior using the OTP shadow * register for OPT word BOOT_CFG1 * * Set FLEXSPI_RESET_PIN_ENABLE=1, FLEXSPI_RESET_PIN= PIO4_5 */ OCOTP0->OTP_SHADOW[97] = 0x164000; #endif /* CONFIG_REBOOT */ /* Read 192M FRO clock Trim settings from fuses. * NOTE: Reading OTP fuses requires a VDDCORE voltage of at least 1.0V */ OTP_FUSE_READ_API(FRO_192MHZ_SC_TRIM, &sc_trim_192); OTP_FUSE_READ_API(FRO_192MHZ_RD_TRIM, &rd_trim_192); /* Read 96M FRO clock Trim settings from fuses. */ OTP_FUSE_READ_API(FRO_96MHZ_SC_TRIM, &sc_trim_96); OTP_FUSE_READ_API(FRO_96MHZ_RD_TRIM, &rd_trim_96); /* Check if the 96MHz fuses have been programmed. * Production devices have 96M trim values programmed in OTP fuses. * However, older EVKs may have pre-production silicon. */ if ((rd_trim_96 == 0) && (sc_trim_96 == 0)) { /* If not programmed then use software to calculate the trim values */ CLOCK_FroTuneToFreq(96000000u); rd_trim_96 = CLKCTL0->FRO_RDTRIM; sc_trim_96 = sc_trim_192; } return 0; } #ifdef CONFIG_LV_Z_VBD_CUSTOM_SECTION extern char __flexspi2_start[]; extern char __flexspi2_end[]; static int init_psram_framebufs(void) { /* * Framebuffers will be stored in PSRAM, within FlexSPI2 linker * section. Zero out BSS section. */ memset(__flexspi2_start, 0, __flexspi2_end - __flexspi2_start); return 0; } #endif /* CONFIG_LV_Z_VBD_CUSTOM_SECTION */ #if CONFIG_REGULATOR /* PMIC setup is dependent on the regulator API */ SYS_INIT(board_config_pmic, POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY); #endif #ifdef CONFIG_LV_Z_VBD_CUSTOM_SECTION /* Framebuffers should be setup after PSRAM is initialized but before * Graphics framework init */ SYS_INIT(init_psram_framebufs, POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY); #endif SYS_INIT(mimxrt595_evk_init, PRE_KERNEL_1, CONFIG_BOARD_INIT_PRIORITY);