| /* |
| * Copyright 2022-2023 NXP |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/init.h> |
| #include "fsl_power.h" |
| #include <zephyr/pm/policy.h> |
| #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 <zephyr/drivers/regulator.h> |
| |
| #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); |