378 lines
9.7 KiB
C
378 lines
9.7 KiB
C
/*
|
|
* 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
|
|
|
|
/* Set shared signal set 0 SCK, WS from Transmit I2S - Flexcomm3 */
|
|
SYSCTL1->SHAREDCTRLSET[0] = SYSCTL1_SHAREDCTRLSET_SHAREDSCKSEL(3) |
|
|
SYSCTL1_SHAREDCTRLSET_SHAREDWSSEL(3);
|
|
|
|
#ifdef CONFIG_I2S_TEST_SEPARATE_DEVICES
|
|
/* 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);
|
|
#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
|
|
|
|
#endif
|
|
|
|
|
|
#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);
|