blob: 5e3d97a83223d2bfe99fea2b697c9d68ffcf2389 [file] [log] [blame]
/*
* Copyright (c) 2016, Freescale Semiconductor, Inc.
* All rights reserved.
*
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_common.h"
#include "fsl_power.h"
#include "fsl_debug_console.h"
#include "fsl_iocon.h"
#include "rom_mpu.h"
#include "rom_pmc.h"
#include "rom_api.h"
#include "rom_lowpower.h"
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.power_no_lib"
#endif
#define POWER_LIB_VERSION 6042018
/* Do not disable the DC bus when going to power modes */
//#define POWER_DCBUS_NOT_DISABLED
//#define DUMP_CONFIG
//#define TRACE_ERR
//#define TRACE_VRB
//#define DISPLAY_ACTIVE_VOLTAGE
/* Force BODMEM enable in power down mode for test : 1uA power consumption increase */
//#define POWER_FORCE_BODMEM_IN_PD
/* On ES2, use directly the safe voltage in deep sleep provided by Patrick */
/* on ES2 powerdown1/2/3/4 , only BANK7 is maintained in retention */
#define POWER_DOWN_WARM_4K
#if defined(DUMP_CONFIG) || defined(TRACE_ERR) || defined(TRACE_VRB)
#define POWER_CONSOLE_DEINIT
#endif
/*******************************************************************************
* Macros/Constants
*******************************************************************************/
#if 1 /* Cope with LDO CORE @ 1.0v in Active and LDO MEM @ 0.9 in power down */
#define POWER_BODMEM_TRIG 0x4 /* 0.80V Considering LDOMEM = 0.9v */
#define POWER_BODMEM_HYST 0x1 /* 0.050V */
#define POWER_BODCORE_TRIG 0x6 /* 0.90V Considering LDOCORE = 1.0v */
#define POWER_BODCORE_HYST 0x1 /* 0.050V */
#else // safest setting for LDO CORE @ 1.1v and LDO MEM @ 1.0v
#define POWER_BODMEM_TRIG 0x6 /* 0.90V - 0.925V +/- 3% Considering LDOMEM = 1.9v */
#define POWER_BODMEM_HYST 0x0 /* 0.025V */
#define POWER_BODCORE_TRIG 0x7 /* 0.95V - 0.975V +/- 3% Considering LDOCORE = 1.1v */
#define POWER_BODCORE_HYST 0x0 /* 0.025V */
#endif
#define BODVBAT_LVL_DEFAULT POWER_BOD_LVL_1_75V
#define BODVBAT_HYST_DEFAULT POWER_BOD_HYST_100MV
#define POWER_LDO_TRIM_UNDEFINED 0x7F
/* ********************** END OF POWER MODE DEFINITIONS **************************************************/
#define VOLTAGE(Vpmu, Vpmuboost, Vmem, Vmemboost, Vcore, Vpmuboostenable, Vflashcore) \
(((Vpmu << LOWPOWER_VOLTAGE_LDO_PMU_INDEX) & LOWPOWER_VOLTAGE_LDO_PMU_MASK) | \
((Vpmuboost << LOWPOWER_VOLTAGE_LDO_PMU_BOOST_INDEX) & LOWPOWER_VOLTAGE_LDO_PMU_BOOST_MASK) | \
((Vpmuboostenable << LOWPOWER_VOLTAGE_LDO_PMU_BOOST_ENABLE_INDEX) & LOWPOWER_VOLTAGE_LDO_PMU_BOOST_ENABLE_MASK) | \
((Vmem << LOWPOWER_VOLTAGE_LDO_MEM_INDEX) & LOWPOWER_VOLTAGE_LDO_MEM_MASK) | \
((Vmemboost << LOWPOWER_VOLTAGE_LDO_MEM_BOOST_INDEX) & LOWPOWER_VOLTAGE_LDO_MEM_BOOST_MASK) | \
((Vcore << LOWPOWER_VOLTAGE_LDO_CORE_INDEX) & LOWPOWER_VOLTAGE_LDO_CORE_MASK) | \
((Vflashcore << LOWPOWER_VOLTAGE_LDO_FLASH_CORE_INDEX) & LOWPOWER_VOLTAGE_LDO_FLASH_CORE_MASK))
/*
* Recommended Voltage settings from
* https://www.collabnet.nxp.com/svn/lprfprojects/JN5189/Documents/17_Architecture/17_n_Digital/Block_Specification/jn5189_settings.xlsx
*/
#define VOLTAGE_PMU_DOWN 0x5 /* 0.8V */
#define VOLTAGE_PMUBOOST_DOWN 0x3 /* 0.75V */
#define VOLTAGE_MEM_DOWN_0_9V 0x9 /* 0.9V */
#define VOLTAGE_MEM_DOWN_1_0V 0xE /* 1V */
#define VOLTAGE_MEMBOOST_DOWN_0_85V 0x7 /* 0.85V */
#define VOLTAGE_MEMBOOST_DOWN_0_96V 0xA /* 0.96V */
#define VOLTAGE_PMU_DEEP_SLEEP 0xA /* 0.96V */
#define VOLTAGE_PMUBOOST_DEEP_SLEEP 0x9 /* 0.9V */
#define VOLTAGE_MEM_DEEP_SLEEP 0x18 /* 1.1V */
#define VOLTAGE_MEMBOOST_DEEP_SLEEP 0x13 /* 1.05V */
#define VOLTAGE_CORE_DEEP_SLEEP 0x2 /* 0.95V */
#define VOLTAGE_FLASH_CORE_DEEP_SLEEP 2 /* 0.95V */
#define VOLTAGE_PMU_DEEP_DOWN 0x5 /* 0.8V */
#define VOLTAGE_PMUBOOST_DEEP_DOWN 0x3 /* 0.75V */
#define VOLTAGE_LDO_PMU_BOOST 0
#define POWER_ULPGB_TRIM_FLASH_ADDR (uint32_t *)0x9FCD4
#define POWER_GET_ACTIVE_TRIM_VALUE(__reg) ((int8_t)((__reg & (1 << 4)) ? -(__reg & 0xF) : (__reg & 0xF)))
#define POWER_GET_PWD_TRIM_VALUE(__reg) \
((int8_t)(((__reg >> 5) & (1 << 4)) ? -((__reg >> 5) & 0xF) : ((__reg >> 5) & 0xF)))
#define POWER_APPLY_ACTIVE_TRIM(__voltage) \
((active_trim_val != POWER_LDO_TRIM_UNDEFINED) ? (MIN(0x1E, MAX(((int8_t)__voltage + active_trim_val), 0xA))) : \
(__voltage))
#define POWER_APPLY_PWD_TRIM(__voltage) \
((active_trim_val != POWER_LDO_TRIM_UNDEFINED) ? (MIN(0x9, MAX(((int8_t)__voltage + pwd_trim_val), 0x1))) : \
(__voltage))
#define POWER_APPLY_TRIM(__voltage) \
((__voltage < 0xA) ? POWER_APPLY_PWD_TRIM(__voltage) : POWER_APPLY_ACTIVE_TRIM(__voltage))
/*******************************************************************************
* Types
******************************************************************************/
static const LPC_LOWPOWER_LDOVOLTAGE_T lowpower_ldovoltage_reset = {
.LDOPMU = 0x18, // 1.1V
.LDOPMUBOOST = 0x13, // 1.05V
.LDOMEM = 0x18, // 1.1V
.LDOMEMBOOST = 0x13, // 1.05V
.LDOCORE = 0x5, // 1.1V
.LDOFLASHNV = 0x5, // 1.9V
.LDOFLASHCORE = 0x6, // 1.15V
.LDOADC = 0x5, // 1.1V
.LDOPMUBOOST_ENABLE = 1, // Force Boost activation on LDOPMU
};
static const LPC_LOWPOWER_LDOVOLTAGE_T lowpower_ldovoltage_min = {
.LDOPMU = 0xE, // 1V
.LDOPMUBOOST = 0xA, // 0.96V
.LDOMEM = 0xE, // 1V
.LDOMEMBOOST = 0xA, // 0.96V
.LDOCORE = 0x3, // 1V
.LDOFLASHNV = 0x5, // 1.9V
.LDOFLASHCORE = 0x6, // 1.15V
.LDOADC = 0x5, // 1.1V
.LDOPMUBOOST_ENABLE = 1, // Force Boost activation on LDOPMU
};
/* trimming value to apply to active/pwd voltage - Initialized from flash in POWER_Init() */
static int8_t active_trim_val = POWER_LDO_TRIM_UNDEFINED;
static int8_t pwd_trim_val = POWER_LDO_TRIM_UNDEFINED;
/*******************************************************************************
* Local prototypes
******************************************************************************/
#ifdef DUMP_CONFIG
static void LF_DumpConfig(LPC_LOWPOWER_T *LV_LowPowerMode);
#endif
/*******************************************************************************
* Code
******************************************************************************/
static void POWER_FlexcomClocksDisable(void)
{
CLOCK_AttachClk(kNONE_to_USART_CLK);
CLOCK_AttachClk(kNONE_to_FRG_CLK);
CLOCK_AttachClk(kNONE_to_I2C_CLK);
CLOCK_AttachClk(kNONE_to_SPI_CLK);
CLOCK_DisableClock(kCLOCK_Usart0);
CLOCK_DisableClock(kCLOCK_I2c0);
CLOCK_DisableClock(kCLOCK_Spi0);
}
#ifdef FOR_BOD_DEBUG
/****** BODMEM ********/
static void POWER_BodMemDisable(void)
{
PMC->PDRUNCFG &= ~PMC_PDRUNCFG_ENA_BOD_MEM_MASK;
PMC->BODMEM &= ~PMC_BODMEM_RESETENABLE_MASK;
}
static void POWER_BodMemSetup(void)
{
// TODO : really needed?
POWER_BodMemDisable();
/* Configure BODMEM so it can be enabled before going to power down */
PMC->BODMEM = (PMC->BODMEM & ~(PMC_BODMEM_TRIGLVL_MASK | PMC_BODMEM_HYST_MASK)) |
(PMC_BODMEM_TRIGLVL(POWER_BODMEM_TRIG) | PMC_BODMEM_HYST(POWER_BODMEM_HYST));
/* Clear BODMEM interrupt */
SYSCON->ANACTRL_INTENCLR = SYSCON_ANACTRL_INTENSET_BODMEM_MASK;
/* Enable the BODMEM */
PMC->PDRUNCFG |= PMC_PDRUNCFG_ENA_BOD_MEM_MASK;
}
static void POWER_BodMemEnableInt(void)
{
/* Warning, we should wait for the LDO to set up (27us) before clearing the status and enabling the interrupts
* (RFT1852) However, we expect this function to be called more than 27us after POWER_BodCoreConfigure() so we can
* discard the 27us delay here */
// CLOCK_uDelay(27);
/* clear initial status (RFT1891) and enable interrupt */
SYSCON->ANACTRL_STAT = SYSCON_ANACTRL_STAT_BODMEM_MASK;
SYSCON->ANACTRL_INTENSET = SYSCON_ANACTRL_INTENSET_BODMEM_MASK;
/* BODMEM Reset enable */
PMC->BODMEM |= PMC_BODMEM_RESETENABLE_MASK;
}
/****** BODCORE ********/
static void POWER_BodCoreDisable(void)
{
PMC->PDRUNCFG &= ~PMC_PDRUNCFG_ENA_BOD_CORE_MASK;
PMC->BODCORE &= ~PMC_BODCORE_RESETENABLE_MASK;
}
static void POWER_BodCoreSetup(void)
{
/* TODO : shall we disable it first? */
POWER_BodCoreDisable();
/* Configure BODMEM so it can be enabled before going to power down */
PMC->BODCORE = (PMC->BODCORE & ~(PMC_BODCORE_TRIGLVL_MASK | PMC_BODCORE_HYST_MASK)) |
(PMC_BODCORE_TRIGLVL(POWER_BODCORE_TRIG) | PMC_BODCORE_HYST(POWER_BODCORE_TRIG));
/* Clear BODCORE interrupt */
SYSCON->ANACTRL_INTENCLR = SYSCON_ANACTRL_INTENSET_BODCORE_MASK;
/* Enable the BODCORE */
PMC->PDRUNCFG |= PMC_PDRUNCFG_ENA_BOD_CORE_MASK;
}
static void POWER_BodCoreEnableInt(void)
{
/* Warning, we should wait for the LDO to set up (27us) before clearing the status and enabling the interrupts
* (RFT1852) However, we expect this function to be called more than 27us after POWER_BodCoreConfigure() so we can
* discard the 27us delay here */
// CLOCK_uDelay(27);
/* clear initial status (RFT1891) and enable interrupt */
SYSCON->ANACTRL_STAT = SYSCON_ANACTRL_STAT_BODCORE_MASK;
SYSCON->ANACTRL_INTENSET = SYSCON_ANACTRL_INTENSET_BODCORE_MASK;
/* BOD IC Reset enable */
PMC->BODCORE |= PMC_BODCORE_RESETENABLE_MASK;
}
#endif
static void POWER_UpdateTrimmingVoltageValue(void)
{
uint32_t ulpbg_trim_flash_val;
/* Save a bit of time is the trimming values are already retrieved */
if ((active_trim_val == POWER_LDO_TRIM_UNDEFINED) || (pwd_trim_val == POWER_LDO_TRIM_UNDEFINED))
{
/* set the trimming values for active and pwd from N-2 page Flash */
ulpbg_trim_flash_val = *POWER_ULPGB_TRIM_FLASH_ADDR;
active_trim_val = POWER_GET_ACTIVE_TRIM_VALUE(ulpbg_trim_flash_val);
pwd_trim_val = POWER_GET_PWD_TRIM_VALUE(ulpbg_trim_flash_val);
}
#ifdef DUMP_CONFIG
PRINTF("reg=0x%x active_trim=0x%X pwd_trim=0x%X\r\n", ulpbg_trim_flash_val, active_trim_val, pwd_trim_val);
#endif
}
static uint32_t POWER_GetIoClampConfig(void)
{
uint32_t io_clamp = 0;
for (int i = 0; i < 22; i++)
{
if ((i == 10) | (i == 11))
{
io_clamp |= (((IOCON->PIO[0][i] & IOCON_IO_CLAMPING_COMBO_MFIO_I2C) >> 12) << i);
}
else
{
io_clamp |= (((IOCON->PIO[0][i] & IOCON_IO_CLAMPING_NORMAL_MFIO) >> 11) << i);
}
}
return io_clamp;
}
/*!
* brief Power Library API to return the library version.
*
* param none
* return version number of the power library
*/
uint32_t POWER_GetLibVersion(void)
{
return POWER_LIB_VERSION;
}
/*!
* brief determine cause of reset
* return reset_cause
*/
reset_cause_t POWER_GetResetCause(void)
{
reset_cause_t reset_cause = RESET_UNDEFINED;
uint32_t pmc_reset = pmc_reset_get_cause();
if (pmc_reset & PMC_RESETCAUSE_POR_MASK)
{
reset_cause |= RESET_POR;
}
if (pmc_reset & PMC_RESETCAUSE_PADRESET_MASK)
{
reset_cause |= RESET_EXT_PIN;
}
if (pmc_reset & PMC_RESETCAUSE_BODRESET_MASK)
{
reset_cause |= RESET_BOR;
}
if (pmc_reset & PMC_RESETCAUSE_SYSTEMRESET_MASK)
{
reset_cause |= RESET_SYS_REQ;
}
if (pmc_reset & PMC_RESETCAUSE_WDTRESET_MASK)
{
reset_cause |= RESET_WDT;
}
if (pmc_reset & PMC_RESETCAUSE_WAKEUPIORESET_MASK)
{
reset_cause |= RESET_WAKE_DEEP_PD;
}
if (pmc_reset & PMC_RESETCAUSE_WAKEUPPWDNRESET_MASK)
{
reset_cause |= RESET_WAKE_PD;
}
if (pmc_reset & PMC_RESETCAUSE_SWRRESET_MASK)
{
reset_cause |= RESET_SW_REQ;
}
return reset_cause;
}
/*!
* brief Clear cause of reset
*/
void POWER_ClearResetCause(void)
{
pmc_reset_clear_cause(0xFFFFFFFF);
}
void POWER_DisplayActiveVoltage(void)
{
LPC_LOWPOWER_LDOVOLTAGE_T ldo_voltage;
Chip_LOWPOWER_GetSystemVoltages(&ldo_voltage);
PRINTF("LDOPMU : %d\n", (int)(ldo_voltage.LDOPMU & 0x1fUL));
PRINTF("LDOPMUBOOST : %d\n", (int)(ldo_voltage.LDOPMUBOOST & 0x1fUL));
PRINTF("LDOMEM : %d\n", (int)(ldo_voltage.LDOMEM & 0x1fUL));
PRINTF("LDOMEMBOOST : %d\n", (int)(ldo_voltage.LDOMEMBOOST & 0x1fUL));
PRINTF("LDOCORE : %d\n", (int)(ldo_voltage.LDOCORE & 0x07UL));
PRINTF("LDOFLASHCORE : %d\n", (int)(ldo_voltage.LDOFLASHCORE & 0x07UL));
PRINTF("LDOFLASHNV : %d\n", (int)(ldo_voltage.LDOFLASHNV & 0x07UL));
PRINTF("LDOADC : %d\n", (int)(ldo_voltage.LDOADC & 0x07UL));
PRINTF("LDOPMUBOOST_ENABLE : %d\n", ldo_voltage.LDOPMUBOOST_ENABLE);
PRINTF("\n");
}
void POWER_ApplyActiveVoltage(const LPC_LOWPOWER_LDOVOLTAGE_T *ldo_voltage)
{
LPC_LOWPOWER_LDOVOLTAGE_T ldo_voltage_l;
memcpy(&ldo_voltage_l, ldo_voltage, sizeof(LPC_LOWPOWER_LDOVOLTAGE_T));
/* Apply some trimming on LDOPMU and LDOMEM to avoid extra consumption */
ldo_voltage_l.LDOPMU = POWER_APPLY_TRIM(ldo_voltage->LDOPMU);
ldo_voltage_l.LDOPMUBOOST = POWER_APPLY_TRIM(ldo_voltage->LDOPMUBOOST);
ldo_voltage_l.LDOMEM = POWER_APPLY_TRIM(ldo_voltage->LDOMEM);
ldo_voltage_l.LDOMEMBOOST = POWER_APPLY_TRIM(ldo_voltage->LDOMEMBOOST);
Chip_LOWPOWER_SetSystemVoltages(&ldo_voltage_l);
}
void POWER_ApplyLdoActiveVoltage(pm_ldo_volt_t ldoVolt)
{
switch (ldoVolt)
{
case PM_LDO_VOLT_1_0V:
POWER_ApplyActiveVoltage(&lowpower_ldovoltage_min);
break;
case PM_LDO_VOLT_1_1V_DEFAULT:
default:
POWER_ApplyActiveVoltage(&lowpower_ldovoltage_reset);
}
}
/*!
* brief Initialize the sdk power drivers
*
* Optimize the LDO voltage for power saving
* Initialize the power domains
* Activate the BOD
*
* return none
*/
void POWER_Init(void)
{
static bool warm_start = false;
#ifdef FOR_BOD_DEBUG
/* Enable the clock for the analog interrupt control module - required for the BOD
* and set up BOD core and mem*/
POWER_BodSetUp();
#endif
if (warm_start == false)
{
POWER_SetTrimDefaultActiveVoltage();
warm_start = true;
}
#ifdef DISPLAY_ACTIVE_VOLTAGE
POWER_DisplayActiveVoltage();
#endif
/* This time, need to wait for LDO to be set up (27us) */
CLOCK_uDelay(27);
#ifdef FOR_BOD_DEBUG
/* enable interrupt and SW reset for the BODCORE */
POWER_BodActivate();
#endif
}
void POWER_SetTrimDefaultActiveVoltage(void)
{
POWER_UpdateTrimmingVoltageValue();
/* Always startup at 1.1V to cope with higher current load when enabling clocks */
POWER_ApplyLdoActiveVoltage(PM_LDO_VOLT_1_1V_DEFAULT);
#ifdef DISPLAY_ACTIVE_VOLTAGE
POWER_DisplayActiveVoltage();
#endif
}
#ifdef FOR_BOD_DEBUG
void POWER_BodSetUp(void)
{
/* Enable the clock for the analog interrupt control module - required for the BOD */
CLOCK_EnableClock(kCLOCK_AnaInt);
POWER_BodCoreSetup();
POWER_BodMemSetup();
}
void POWER_BodActivate(void)
{
/* enable interrupt and SW reset for the BODCORE */
POWER_BodCoreEnableInt();
CLOCK_DisableClock(kCLOCK_AnaInt);
}
#endif
/*!
* brief Get default Vbat BOD config parameters, level 1.75V, Hysteresis 100mV
*
* param bod_cfg_p
* return none
*/
void POWER_BodVbatGetDefaultConfig(pm_bod_cfg_t *bod_cfg_p)
{
bod_cfg_p->bod_level = BODVBAT_LVL_DEFAULT;
bod_cfg_p->bod_hyst = BODVBAT_HYST_DEFAULT;
bod_cfg_p->bod_cfg = POWER_BOD_ENABLE | POWER_BOD_INT_ENABLE;
}
/*!
* brief Configure the VBAT BOD
*
* param bod_cfg_p
* return false if configuration parameters are incorrect
*/
bool POWER_BodVbatConfig(pm_bod_cfg_t *bod_cfg_p)
{
uint32_t comparator_interrupt;
if (bod_cfg_p->bod_cfg & POWER_BOD_ENABLE)
{
if ((bod_cfg_p->bod_level >= POWER_BOD_LVL_1_75V) && (bod_cfg_p->bod_level <= POWER_BOD_LVL_3_3V) &&
(bod_cfg_p->bod_hyst <= POWER_BOD_HYST_100MV))
{
uint32_t bodvbat = PMC->BODVBAT;
bodvbat &= ~(PMC_BODVBAT_TRIGLVL_MASK | PMC_BODVBAT_HYST_MASK);
bodvbat |= PMC_BODVBAT_TRIGLVL(bod_cfg_p->bod_level);
bodvbat |= PMC_BODVBAT_HYST(bod_cfg_p->bod_hyst);
PMC->BODVBAT = bodvbat;
}
else
{
return false;
}
}
/* enable the clock for the analog interrupt control module */
CLOCK_EnableClock(kCLOCK_AnaInt);
if (bod_cfg_p->bod_cfg & POWER_BOD_HIGH)
{
/* Disable Interrupt on BODVBAT High */
SYSCON->ANACTRL_INTENCLR = SYSCON_ANACTRL_INTENCLR_BODVBATHIGH_MASK;
/* clear initial status of interrupt */
SYSCON->ANACTRL_STAT = SYSCON_ANACTRL_STAT_BODVBATHIGH_MASK;
/* enable comparator interrupt */
comparator_interrupt = SYSCON_ANACTRL_INTENSET_BODVBATHIGH_MASK;
}
else
{
/* BOD IC Interrupt enable */
SYSCON->ANACTRL_INTENCLR = SYSCON_ANACTRL_INTENCLR_BODVBAT_MASK;
/* clear initial status of interrupt */
SYSCON->ANACTRL_STAT = SYSCON_ANACTRL_STAT_BODVBAT_MASK;
/* enable comparator interrupt */
comparator_interrupt = SYSCON_ANACTRL_INTENSET_BODVBAT_MASK;
}
if (bod_cfg_p->bod_cfg & POWER_BOD_ENABLE)
{
if (bod_cfg_p->bod_cfg & POWER_BOD_INT_ENABLE)
{
SYSCON->ANACTRL_INTENSET = comparator_interrupt;
NVIC_EnableIRQ(WDT_BOD_IRQn);
}
else
{
NVIC_DisableIRQ(WDT_BOD_IRQn);
}
#ifdef FOR_BOD_DEBUG
if (bod_cfg_p->bod_cfg & POWER_BOD_RST_ENABLE)
{
PMC->BODVBAT |= PMC_BODVBAT_RESETENABLE_MASK;
}
else
{
PMC->BODVBAT &= ~PMC_BODVBAT_RESETENABLE_MASK;
}
#endif
}
CLOCK_DisableClock(kCLOCK_AnaInt);
// PRINTF( "BODVBAT=0x%X ANACTRL_CTRL=0x%X _STAT=0x%X _VAL=0x%X _INTENSET=0x%X\n", PMC->BODVBAT,
// SYSCON->ANACTRL_CTRL, SYSCON->ANACTRL_STAT, SYSCON->ANACTRL_VAL, SYSCON->ANACTRL_INTENSET);
return true;
}
bool POWER_EnterDeepSleepMode(pm_power_config_t *pm_power_config)
{
/* [RFT1911] Disable the DC bus to prevent extra consumption */
#ifndef POWER_DCBUS_NOT_DISABLED
ASYNC_SYSCON->DCBUSCTRL =
(ASYNC_SYSCON->DCBUSCTRL & ~ASYNC_SYSCON_DCBUSCTRL_ADDR_MASK) | (1 << ASYNC_SYSCON_DCBUSCTRL_ADDR_SHIFT);
#endif
/* [artf555998] Enable new ES2 feature for fast wakeup */
PMC->CTRLNORST = PMC_CTRLNORST_FASTLDOENABLE_MASK;
return false;
}
bool POWER_EnterPowerDownMode(pm_power_config_t *pm_power_config)
{
int radio_retention;
int autostart_32mhz_xtal;
int keep_ao_voltage;
int sram_cfg;
int wakeup_src0;
int wakeup_src1;
uint8_t voltage_mem_down;
uint8_t voltage_membootst_down;
LPC_LOWPOWER_T lp_config;
memset(&lp_config, 0, sizeof(lp_config));
sram_cfg = pm_power_config->pm_config & PM_CFG_SRAM_ALL_RETENTION;
radio_retention = pm_power_config->pm_config & PM_CFG_RADIO_RET;
autostart_32mhz_xtal = pm_power_config->pm_config & PM_CFG_XTAL32M_AUTOSTART;
keep_ao_voltage = pm_power_config->pm_config & PM_CFG_KEEP_AO_VOLTAGE;
wakeup_src0 = (int)pm_power_config->pm_wakeup_src & 0xFFFFFFFF;
wakeup_src1 = (int)(pm_power_config->pm_wakeup_src >> 32) & 0xFFFFFFFF;
#ifdef TRACE_VRB
PRINTF("POWER_EnterPowerDownMode:\n");
PRINTF(" wakeup_src0 : 0x%x\n", wakeup_src0);
PRINTF(" wakeup_src1 : 0x%x\n", wakeup_src1);
PRINTF(" wakeup_io : 0x%x\n", pm_power_config->pm_wakeup_io);
PRINTF(" pm_config : 0x%x\n", pm_power_config->pm_config);
#endif
lp_config.CFG = LOWPOWER_CFG_MODE_POWERDOWN;
/* PDRUNCFG : on ES2, flag discard to keep the same configuration than active */
lp_config.CFG |= LOWPOWER_CFG_PDRUNCFG_DISCARD_MASK;
/* PDSLEEPCFG (note: LDOMEM will be enabled by lowpower API if one memory bank in retention*/
lp_config.PMUPWDN |= LOWPOWER_PMUPWDN_DCDC | LOWPOWER_PMUPWDN_BIAS | LOWPOWER_PMUPWDN_BODVBAT;
/* Disable All banks except those given in sram_cfg */
lp_config.DIGPWDN |= ((LOWPOWER_DIGPWDN_SRAM_ALL_MASK) & ~(sram_cfg << LOWPOWER_DIGPWDN_SRAM0_INDEX));
// TODO : if COMM0 is disabled, need to switch off the clocks also for safe wake up
lp_config.DIGPWDN |= LOWPOWER_DIGPWDN_COMM0; // PDSLEEP DISABLE COM0
lp_config.DIGPWDN |=
LOWPOWER_DIGPWDN_MCU_RET; // PDSLEEP DISABLE retention : on ES1, CPU retention, on ES2 Zigbee retention
// lp_config.DIGPWDN |= LOWPOWER_DIGPWDN_NTAG_FD; // DPDWKSRC DISABLE NTAG - not used in lowpower API in
// power down
lp_config.SLEEPPOSTPONE = 0;
lp_config.GPIOLATCH = 0;
#if gPWR_LDOMEM_0_9V_PD /* Warning : do not apply this flag , for experimental use only */
voltage_mem_down = VOLTAGE_MEM_DOWN_0_9V;
voltage_membootst_down = VOLTAGE_MEMBOOST_DOWN_0_85V;
#else
/* A bit in the flash is now set (bit 31 at address 0x9FCD4).
* If this bit is set, RAM retention in sleep should use voltage of 0.9v.
* If it is not set, RAM retention in sleep should use voltage of 1.0v. */
uint32_t *ate_setting = (uint32_t *)0x9FCD4;
if ((*ate_setting & 0x80000000) == 0x80000000)
{
voltage_mem_down = VOLTAGE_MEM_DOWN_0_9V;
voltage_membootst_down = VOLTAGE_MEMBOOST_DOWN_0_85V;
}
else
{
voltage_mem_down = VOLTAGE_MEM_DOWN_1_0V;
voltage_membootst_down = VOLTAGE_MEMBOOST_DOWN_0_96V;
}
#endif
if (keep_ao_voltage)
{
LPC_LOWPOWER_LDOVOLTAGE_T ldo_voltage;
Chip_LOWPOWER_GetSystemVoltages(&ldo_voltage);
/* keep the same voltage than in active for the Always ON powerdomain */
lp_config.VOLTAGE = VOLTAGE(POWER_APPLY_TRIM(ldo_voltage.LDOPMU), POWER_APPLY_TRIM(ldo_voltage.LDOPMUBOOST),
POWER_APPLY_TRIM(voltage_mem_down), POWER_APPLY_TRIM(voltage_membootst_down), 0,
VOLTAGE_LDO_PMU_BOOST, 0);
}
else
{
lp_config.VOLTAGE = VOLTAGE(POWER_APPLY_TRIM(VOLTAGE_PMU_DOWN), POWER_APPLY_TRIM(VOLTAGE_PMUBOOST_DOWN),
POWER_APPLY_TRIM(voltage_mem_down), POWER_APPLY_TRIM(voltage_membootst_down), 0,
VOLTAGE_LDO_PMU_BOOST, 0);
}
lp_config.WAKEUPSRCINT0 = wakeup_src0;
lp_config.WAKEUPSRCINT1 = wakeup_src1;
/* Variation from reference */
if (radio_retention)
{
/* Enable Zigbee retention */
lp_config.DIGPWDN &= ~LOWPOWER_DIGPWDN_MCU_RET;
}
/* Configure IO wakeup source */
if (lp_config.WAKEUPSRCINT1 & LOWPOWER_WAKEUPSRCINT1_IO_IRQ)
{
lp_config.WAKEUPIOSRC = pm_power_config->pm_wakeup_io;
}
if (lp_config.WAKEUPSRCINT0 & LOWPOWER_WAKEUPSRCINT0_SYSTEM_IRQ)
{
/* Need to enable the BIAS for VBAT BOD */
lp_config.PMUPWDN &= ~(LOWPOWER_PMUPWDN_BIAS | LOWPOWER_PMUPWDN_BODVBAT);
}
if (lp_config.WAKEUPSRCINT0 &
(LOWPOWER_WAKEUPSRCINT0_USART0_IRQ | LOWPOWER_WAKEUPSRCINT0_I2C0_IRQ | LOWPOWER_WAKEUPSRCINT0_SPI0_IRQ))
{
/* Keep Flexcom0 in power down mode */
lp_config.DIGPWDN &= ~LOWPOWER_DIGPWDN_COMM0;
}
/* On ES2 , Analog comparator is already enabled in PDRUNCFG + RFT1877 : No need to keep the bias */
if (sram_cfg)
{
/* Configure the SRAM to SMB1 (low leakage biasing) */
SYSCON->SRAMCTRL =
(SYSCON->SRAMCTRL & (~SYSCON_SRAMCTRL_SMB_MASK)) | (SYSCON_SRAMCTRL_SMB(1) << SYSCON_SRAMCTRL_SMB_SHIFT);
/*
* BODMEM requires the bandgap enable in power down, this induces a power consumption increase of 1uA
* so Enable BODMEM only if bandgap is already enabled for BODVBAT (see code above)
*/
#ifndef POWER_FORCE_BODMEM_IN_PD
if ((lp_config.PMUPWDN & LOWPOWER_PMUPWDN_BIAS) == 0)
#endif
{
#ifdef FOR_BOD_DEBUG
CLOCK_EnableClock(kCLOCK_AnaInt);
/* Note: BODMEM should be already enabled in the POWER_Init() function but do it again if not */
if (!(PMC->PDRUNCFG & PMC_PDRUNCFG_ENA_BOD_MEM_MASK))
{
POWER_BodMemSetup();
/* This time, need to wait for LDO to be set up (27us) */
CLOCK_uDelay(27);
}
POWER_BodMemEnableInt();
#endif
}
#ifndef POWER_FORCE_BODMEM_IN_PD
else
{
#ifdef FOR_BOD_DEBUG
/* Disable the BODMEM otherwise */
POWER_BodMemDisable();
#endif
}
#endif
}
else
{
#ifdef FOR_BOD_DEBUG
/* Disable the BODMEM otherwise */
POWER_BodMemDisable();
#endif
}
#ifdef FOR_BOD_DEBUG
/* Disable BodCore , no longer used in power down */
POWER_BodCoreDisable();
#endif
if (wakeup_src0 & LOWPOWER_WAKEUPSRCINT0_NFCTAG_IRQ)
{
lp_config.WAKEUPSRCINT1 |= LOWPOWER_WAKEUPSRCINT1_IO_IRQ;
lp_config.WAKEUPIOSRC |= LOWPOWER_WAKEUPIOSRC_NTAG_FD;
}
/* On Power down, NTAG field detect is enabled by IO so don t need to set the LOWPOWER_DIGPWDN_NTAG_FD */
// lp_config.DIGPWDN &= ~LOWPOWER_DIGPWDN_NTAG_FD; // used for deep down only
if (autostart_32mhz_xtal)
{
lp_config.CFG |= LOWPOWER_CFG_XTAL32MSTARTENA_MASK;
}
/* get IO clamping state already set by the application and give it to lowpower API
* Lowpower API overrides the IO configuration with GPIOLATCH setting
*/
lp_config.GPIOLATCH = POWER_GetIoClampConfig();
/* [RFT1911] Disable the DC bus to prevent extra consumption */
#ifndef POWER_DCBUS_NOT_DISABLED
ASYNC_SYSCON->DCBUSCTRL =
(ASYNC_SYSCON->DCBUSCTRL & ~ASYNC_SYSCON_DCBUSCTRL_ADDR_MASK) | (1 << ASYNC_SYSCON_DCBUSCTRL_ADDR_SHIFT);
#endif
/* [artf555998] Enable new ES2 feature for fast wakeup */
PMC->CTRLNORST = PMC_CTRLNORST_FASTLDOENABLE_MASK;
#ifdef DUMP_CONFIG
LF_DumpConfig(&lp_config);
#endif
/* If flexcom is maintained, do not disable the console and the clocks - let the application do it if needed */
if (lp_config.DIGPWDN & LOWPOWER_DIGPWDN_COMM0)
{
/* remove console if not done */
DbgConsole_Deinit();
/* Disable clocks to FLEXCOM power domain. This power domain is not reseted on wakeup by HW */
POWER_FlexcomClocksDisable();
}
/* Apply default LDO voltage */
POWER_ApplyLdoActiveVoltage(PM_LDO_VOLT_1_1V_DEFAULT);
Chip_LOWPOWER_SetLowPowerMode(&lp_config);
/* If we go here, the power mode has been aborted - this can happen only if WFI is executed in lowpower API*/
return false;
}
bool POWER_EnterDeepDownMode(pm_power_config_t *pm_power_config)
{
int autostart_32mhz_xtal;
int wakeup_src0;
int wakeup_src1;
LPC_LOWPOWER_T lp_config;
memset(&lp_config, 0, sizeof(lp_config));
autostart_32mhz_xtal = pm_power_config->pm_config & PM_CFG_XTAL32M_AUTOSTART;
wakeup_src0 = (int)pm_power_config->pm_wakeup_src & 0xFFFFFFFF;
wakeup_src1 = (int)(pm_power_config->pm_wakeup_src >> 32) & 0xFFFFFFFF;
#ifdef TRACE_VRB
PRINTF("POWER_EnterDeepDownMode:\n");
PRINTF(" wakeup_src0 : 0x%x\n", wakeup_src0);
PRINTF(" wakeup_src1 : 0x%x\n", wakeup_src1);
#else
(void)wakeup_src0;
#endif
lp_config.CFG = LOWPOWER_CFG_MODE_DEEPPOWERDOWN;
lp_config.PMUPWDN = LOWPOWER_PMUPWDN_DCDC | LOWPOWER_PMUPWDN_BIAS | LOWPOWER_PMUPWDN_BODVBAT |
LOWPOWER_PMUPWDN_FRO192M | LOWPOWER_PMUPWDN_FRO1M;
lp_config.DIGPWDN = LOWPOWER_DIGPWDN_IO;
if (wakeup_src1 & LOWPOWER_WAKEUPSRCINT1_IO_IRQ)
{
lp_config.DIGPWDN &= ~LOWPOWER_DIGPWDN_IO;
lp_config.WAKEUPIOSRC = pm_power_config->pm_wakeup_io;
}
if (wakeup_src0 & LOWPOWER_WAKEUPSRCINT0_NFCTAG_IRQ)
{
lp_config.DIGPWDN &= ~LOWPOWER_DIGPWDN_NTAG_FD;
}
else
{
lp_config.DIGPWDN |= LOWPOWER_DIGPWDN_NTAG_FD;
}
lp_config.VOLTAGE = VOLTAGE(POWER_APPLY_TRIM(VOLTAGE_PMU_DEEP_DOWN), POWER_APPLY_TRIM(VOLTAGE_PMUBOOST_DEEP_DOWN),
0, 0, 0, VOLTAGE_LDO_PMU_BOOST, 0);
if (autostart_32mhz_xtal)
{
lp_config.CFG |= LOWPOWER_CFG_XTAL32MSTARTENA_MASK;
}
/* [RFT1911] Disable the DC bus to prevent extra consumption */
#ifndef POWER_DCBUS_NOT_DISABLED
ASYNC_SYSCON->DCBUSCTRL =
(ASYNC_SYSCON->DCBUSCTRL & ~ASYNC_SYSCON_DCBUSCTRL_ADDR_MASK) | (1 << ASYNC_SYSCON_DCBUSCTRL_ADDR_SHIFT);
#endif
/* [artf555998] Enable new ES2 feature for fast wakeup */
PMC->CTRLNORST = PMC_CTRLNORST_FASTLDOENABLE_MASK;
#ifdef DUMP_CONFIG
LF_DumpConfig(&lp_config);
#endif
/* remove console if not done */
DbgConsole_Deinit();
/* Disable clocks to FLEXCOM power domain. This power domain is not reseted on wakeup by HW */
POWER_FlexcomClocksDisable();
Chip_LOWPOWER_SetLowPowerMode(&lp_config);
/* If we go here, the power mode has been aborted - this can happen only if WFI is executed in lowpower API*/
return false;
}
/*!
* brief Power Library API to enter different power mode.
*
* If requested mode is PM_POWER_DOWN, the API will perform the clamping of the DIOs
* if the PIO register has the bit IO_CLAMPING set: SYSCON->RETENTIONCTRL.IOCLAMP
* will be set
*
* return false if chip could not go to sleep. Configuration structure is incorrect
*/
bool POWER_EnterPowerMode(pm_power_mode_t pm_power_mode, pm_power_config_t *pm_power_config)
{
bool ret;
switch (pm_power_mode)
{
/* case PM_DEEP_SLEEP:
ret = POWER_EnterDeepSleepMode(pm_power_config);
break; */
case PM_POWER_DOWN:
ret = POWER_EnterPowerDownMode(pm_power_config);
break;
case PM_DEEP_DOWN:
ret = POWER_EnterDeepDownMode(pm_power_config);
break;
default:
ret = false;
}
return ret;
}
#ifdef DUMP_CONFIG
static void LF_DumpConfig(LPC_LOWPOWER_T *LV_LowPowerMode)
{
PRINTF("Powerdown configuration\n");
PRINTF("CFG: 0x%x\n", LV_LowPowerMode->CFG);
PRINTF("PMUPWDN: 0x%x\n", LV_LowPowerMode->PMUPWDN);
PRINTF("DIGPWDN: 0x%x\n", LV_LowPowerMode->DIGPWDN);
PRINTF("VOLTAGE: 0x%x\n", LV_LowPowerMode->VOLTAGE);
PRINTF("WAKEUPSRCINT0: 0x%x\n", LV_LowPowerMode->WAKEUPSRCINT0);
PRINTF("WAKEUPSRCINT1: 0x%x\n", LV_LowPowerMode->WAKEUPSRCINT1);
PRINTF("SLEEPPOSTPONE: 0x%x\n", LV_LowPowerMode->SLEEPPOSTPONE);
PRINTF("WAKEUPIOSRC 0x%x\n", LV_LowPowerMode->WAKEUPIOSRC);
PRINTF("GPIOLATCH 0x%x\n", LV_LowPowerMode->GPIOLATCH);
PRINTF("TIMERCFG 0x%x\n", LV_LowPowerMode->TIMERCFG);
PRINTF("TIMERBLECFG 0x%x\n", LV_LowPowerMode->TIMERBLECFG);
PRINTF("TIMERCOUNTLSB 0x%x\n", LV_LowPowerMode->TIMERCOUNTLSB);
PRINTF("TIMERCOUNTMSB 0x%x\n", LV_LowPowerMode->TIMERCOUNTMSB);
PRINTF("TIMER2NDCOUNTLSB 0x%x\n", LV_LowPowerMode->TIMER2NDCOUNTLSB);
PRINTF("TIMER2NDCOUNTMSB 0x%x\n", LV_LowPowerMode->TIMER2NDCOUNTMSB);
}
#endif