/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright 2016, NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include "fsl_common.h"
#include "fsl_power.h"
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.power"
#endif

/*! @name Driver version */
/*@{*/
/*! @brief LPC546XX power contorl version 1.0.0. */
#define FSL_LPC55SXX_POWER_CONTROL_VERSION (MAKE_VERSION(1UL, 0UL, 0UL))
/*@}*/

/*******************************************************************************
 * Variables
 ******************************************************************************/

/*******************************************************************************
 * Definitions
 ******************************************************************************/
/** @brief  Analog components power modes control during low power modes*/
typedef enum pd_bits_2
{
    kPDRUNCFG_PD_DCDC         = (1UL << 0),
    kPDRUNCFG_PD_BIAS         = (1UL << 1),
    kPDRUNCFG_PD_TEMPSENS     = (1UL << 14),
    kPDRUNCFG_PD_LDOMEM       = (1UL << 16),
    kPDRUNCFG_PD_LDODEEPSLEEP = (1UL << 17),
    kPDRUNCFG_PD_ROM          = (1UL << 24),
} pd_bit_t_2;

/**
 * @brief Sleep Postpone
 */
#define LOWPOWER_HWWAKE_FORCED (1UL << 0) /*!< Force peripheral clocking to stay on during deep-sleep mode. */
#define LOWPOWER_HWWAKE_ENABLE_FRO192M                                                                  \
    (1UL << 31) /*!< Need to be set if FRO192M is disable - via PDCTRL0 - in Deep Sleep mode and any of \
                   LOWPOWER_HWWAKE_PERIPHERALS, LOWPOWER_HWWAKE_SDMA0 or LOWPOWER_HWWAKE_SDMA1 is set */

/** @brief  Low Power main structure */
typedef enum
{
    VD_AON       = 0x0, /*!< Digital Always On power domain */
    VD_MEM       = 0x1, /*!< Memories (SRAM) power domain   */
    VD_DCDC      = 0x2, /*!< Core logic power domain        */
    VD_DEEPSLEEP = 0x3  /*!< Core logic power domain        */
} LPC_POWER_DOMAIN_T;

/**
 * @brief LDO_FLASH_NV & LDO_USB voltage settings
 */
typedef enum _v_flashnv
{
    V_LDOFLASHNV_1P650 = 0, /*!< 0.95  V */
    V_LDOFLASHNV_1P700 = 1, /*!< 0.975 V */
    V_LDOFLASHNV_1P750 = 2, /*!< 1     V */
    V_LDOFLASHNV_0P800 = 3, /*!< 1.025 V */
    V_LDOFLASHNV_1P850 = 4, /*!< 1.050 V */
    V_LDOFLASHNV_1P900 = 5, /*!< 1.075 V */
    V_LDOFLASHNV_1P950 = 6, /*!< 1.1   V */
    V_LDOFLASHNV_2P000 = 7  /*!< 1.125 V */
} v_flashnv_t;

/** @brief  Low Power main structure */
typedef struct
{                              /*     */
    __IO uint32_t CFG;         /*!< Low Power Mode Configuration, and miscallenous options  */
    __IO uint32_t PDCTRL0;     /*!< Power Down control : controls power of various modules
                                 in the different Low power modes, including ROM */
    __IO uint32_t SRAMRETCTRL; /*!< Power Down control : controls power SRAM instances
                                 in the different Low power modes */
    __IO uint32_t CPURETCTRL;  /*!< CPU0 retention control : controls CPU retention parameters in POWER DOWN modes */
    __IO uint64_t VOLTAGE;     /*!< Voltage control in Low Power Modes */
    __IO uint64_t WAKEUPSRC;   /*!< Wake up sources control for sleepcon */
    __IO uint64_t WAKEUPINT;   /*!< Wake up sources control for ARM */
    __IO uint32_t HWWAKE;      /*!< Interrupt that can postpone power down modes
                                 in case an interrupt is pending when the processor request deepsleep */
    __IO uint32_t WAKEUPIOSRC; /*!< Wake up I/O sources in DEEP POWER DOWN mode */
    __IO uint32_t TIMERCFG;    /*!< Wake up timers configuration */
    __IO uint32_t TIMERCOUNT;  /*!< Wake up Timer count*/
    __IO uint32_t POWERCYCLE;  /*!< Cancels entry in Low Power mode if set with 0xDEADABBA (might be used by some
                                 interrupt handlers)*/
} LPC_LOWPOWER_T;

/*   */
#define LOWPOWER_POWERCYCLE_CANCELLED 0xDEADABBAUL /*!<    */

/**
 * @brief SRAM Low Power Modes
 */
#define LOWPOWER_SRAM_LPMODE_MASK      (0xFUL)
#define LOWPOWER_SRAM_LPMODE_ACTIVE    (0x6UL) /*!< SRAM functional mode                                */
#define LOWPOWER_SRAM_LPMODE_SLEEP     (0xFUL) /*!< SRAM Sleep mode (Data retention, fast wake up)      */
#define LOWPOWER_SRAM_LPMODE_DEEPSLEEP (0x8UL) /*!< SRAM Deep Sleep mode (Data retention, slow wake up) */
#define LOWPOWER_SRAM_LPMODE_SHUTDOWN  (0x9UL) /*!< SRAM Shut Down mode (no data retention)             */
#define LOWPOWER_SRAM_LPMODE_POWERUP   (0xAUL) /*!< SRAM is powering up                                 */

/**
 * @brief Wake up timers configuration in Low Power Modes
 */
#define LOWPOWER_TIMERCFG_CTRL_INDEX   0
#define LOWPOWER_TIMERCFG_CTRL_MASK    (0x1UL << LOWPOWER_TIMERCFG_CTRL_INDEX)
#define LOWPOWER_TIMERCFG_TIMER_INDEX  1
#define LOWPOWER_TIMERCFG_TIMER_MASK   (0x7UL << LOWPOWER_TIMERCFG_TIMER_INDEX)
#define LOWPOWER_TIMERCFG_OSC32K_INDEX 4
#define LOWPOWER_TIMERCFG_OSC32K_MASK  (0x1UL << LOWPOWER_TIMERCFG_OSC32K_INDEX)

#define LOWPOWER_TIMERCFG_CTRL_DISABLE 0 /*!< Wake Timer Disable */
#define LOWPOWER_TIMERCFG_CTRL_ENABLE  1 /*!< Wake Timer Enable  */

/**
 * @brief Primary Wake up timers configuration in Low Power Modes
 */
#define LOWPOWER_TIMERCFG_TIMER_RTC1KHZ 0 /*!< 1 KHz Real Time Counter (RTC) used as wake up source */
#define LOWPOWER_TIMERCFG_TIMER_RTC1HZ  1 /*!< 1 Hz Real Time Counter (RTC) used as wake up source  */
#define LOWPOWER_TIMERCFG_TIMER_OSTIMER 2 /*!< OS Event Timer used as wake up source                */

#define LOWPOWER_TIMERCFG_OSC32K_FRO32KHZ  0 /*!< Wake up Timers uses FRO 32 KHz as clock source      */
#define LOWPOWER_TIMERCFG_OSC32K_XTAL32KHZ 1 /*!< Wake up Timers uses Chrystal 32 KHz as clock source */

//! @brief Interface for lowpower functions
typedef struct LowpowerDriverInterface
{
    void (*power_cycle_cpu_and_flash)(void);
    void (*set_lowpower_mode)(LPC_LOWPOWER_T *p_lowpower_cfg);
} lowpower_driver_interface_t;

/**<  DCDC Power Profiles */
typedef enum
{
    DCDC_POWER_PROFILE_LOW,    /**<  LOW (for CPU frequencies below DCDC_POWER_PROFILE_LOW_MAX_FREQ_HZ) */
    DCDC_POWER_PROFILE_MEDIUM, /**<  MEDIUM (for CPU frequencies between DCDC_POWER_PROFILE_LOW_MAX_FREQ_HZ and
                                  DCDC_POWER_PROFILE_MEDIUM_MAX_FREQ_HZ) */
    DCDC_POWER_PROFILE_HIGH,   /**<  HIGH (for CPU frequencies between DCDC_POWER_PROFILE_MEDIUM_MAX_FREQ_HZ and
                                  DCDC_POWER_PROFILE_HIGH_MAX_FREQ_HZ) */
} lowpower_dcdc_power_profile_enum;

/**<  Manufacturing Process Corners */
typedef enum
{
    PROCESS_CORNER_SSS,    /**< Slow Corner Process */
    PROCESS_CORNER_NNN,    /**< Nominal Corner Process */
    PROCESS_CORNER_FFF,    /**< Fast Corner Process */
    PROCESS_CORNER_OTHERS, /**< SFN, SNF, NFS, Poly Res ... Corner Process */
} lowpower_process_corner_enum;

/**
 * @brief DCDC voltage settings
 */
typedef enum _v_dcdc
{
    V_DCDC_0P950 = 0, /*!< 0.95  V */
    V_DCDC_0P975 = 1, /*!< 0.975 V */
    V_DCDC_1P000 = 2, /*!< 1     V */
    V_DCDC_1P025 = 3, /*!< 1.025 V */
    V_DCDC_1P050 = 4, /*!< 1.050 V */
    V_DCDC_1P075 = 5, /*!< 1.075 V */
    V_DCDC_1P100 = 6, /*!< 1.1   V */
    V_DCDC_1P125 = 7, /*!< 1.125 V */
    V_DCDC_1P150 = 8, /*!< 1.150 V */
    V_DCDC_1P175 = 9, /*!< 1.175 V */
    V_DCDC_1P200 = 10 /*!< 1.2   V */
} v_dcdc_t;

/**
 * @brief Deep Sleep LDO voltage settings
 */
typedef enum _v_deepsleep
{
    V_DEEPSLEEP_0P900 = 0, /*!< 0.9   V */
    V_DEEPSLEEP_0P925 = 1, /*!< 0.925 V */
    V_DEEPSLEEP_0P950 = 2, /*!< 0.95  V */
    V_DEEPSLEEP_0P975 = 3, /*!< 0.975 V */
    V_DEEPSLEEP_1P000 = 4, /*!< 1.000 V */
    V_DEEPSLEEP_1P025 = 5, /*!< 1.025 V */
    V_DEEPSLEEP_1P050 = 6, /*!< 1.050 V */
    V_DEEPSLEEP_1P075 = 7  /*!< 1.075 V */
} v_deepsleep_t;

/**
 * @brief Always On and Memories LDO voltage settings
 */
typedef enum _v_ao
{
    V_AO_0P700 = 1,  /*!< 0.7   V */
    V_AO_0P725 = 2,  /*!< 0.725 V */
    V_AO_0P750 = 3,  /*!< 0.75  V */
    V_AO_0P775 = 4,  /*!< 0.775 V */
    V_AO_0P800 = 5,  /*!< 0.8   V */
    V_AO_0P825 = 6,  /*!< 0.825 V */
    V_AO_0P850 = 7,  /*!< 0.85  V */
    V_AO_0P875 = 8,  /*!< 0.875 V */
    V_AO_0P900 = 9,  /*!< 0.9   V */
    V_AO_0P960 = 10, /*!< 0.96  V */
    V_AO_0P970 = 11, /*!< 0.97  V */
    V_AO_0P980 = 12, /*!< 0.98  V */
    V_AO_0P990 = 13, /*!< 0.99  V */
    V_AO_1P000 = 14, /*!< 1     V */
    V_AO_1P010 = 15, /*!< 1.01  V */
    V_AO_1P020 = 16, /*!< 1.02  V */
    V_AO_1P030 = 17, /*!< 1.03  V */
    V_AO_1P040 = 18, /*!< 1.04  V */
    V_AO_1P050 = 19, /*!< 1.05  V */
    V_AO_1P060 = 20, /*!< 1.06  V */
    V_AO_1P070 = 21, /*!< 1.07  V */
    V_AO_1P080 = 22, /*!< 1.08  V */
    V_AO_1P090 = 23, /*!< 1.09  V */
    V_AO_1P100 = 24, /*!< 1.1   V */
    V_AO_1P110 = 25, /*!< 1.11  V */
    V_AO_1P120 = 26, /*!< 1.12  V */
    V_AO_1P130 = 27, /*!< 1.13  V */
    V_AO_1P140 = 28, /*!< 1.14  V */
    V_AO_1P150 = 29, /*!< 1.15  V */
    V_AO_1P160 = 30, /*!< 1.16  V */
    V_AO_1P220 = 31  /*!< 1.22  V */
} v_ao_t;

/* Low Power modes  */
#define LOWPOWER_CFG_LPMODE_INDEX          0
#define LOWPOWER_CFG_LPMODE_MASK           (0x3UL << LOWPOWER_CFG_LPMODE_INDEX)
#define LOWPOWER_CFG_SELCLOCK_INDEX        2
#define LOWPOWER_CFG_SELCLOCK_MASK         (0x1UL << LOWPOWER_CFG_SELCLOCK_INDEX)
#define LOWPOWER_CFG_SELMEMSUPPLY_INDEX    3
#define LOWPOWER_CFG_SELMEMSUPPLY_MASK     (0x1UL << LOWPOWER_CFG_SELMEMSUPPLY_INDEX)
#define LOWPOWER_CFG_MEMLOWPOWERMODE_INDEX 4
#define LOWPOWER_CFG_MEMLOWPOWERMODE_MASK  (0x1UL << LOWPOWER_CFG_MEMLOWPOWERMODE_INDEX)
#define LOWPOWER_CFG_LDODEEPSLEEPREF_INDEX 5
#define LOWPOWER_CFG_LDODEEPSLEEPREF_MASK  (0x1UL << LOWPOWER_CFG_LDODEEPSLEEPREF_INDEX)

#define LOWPOWER_CFG_LPMODE_ACTIVE        0 /*!< ACTIVE mode          */
#define LOWPOWER_CFG_LPMODE_DEEPSLEEP     1 /*!< DEEP SLEEP mode      */
#define LOWPOWER_CFG_LPMODE_POWERDOWN     2 /*!< POWER DOWN mode      */
#define LOWPOWER_CFG_LPMODE_DEEPPOWERDOWN 3 /*!< DEEP POWER DOWN mode */
#define LOWPOWER_CFG_LPMODE_SLEEP         4 /*!< SLEEP mode */

#define LOWPOWER_CFG_SELCLOCK_1MHZ 0 /*!< The 1 MHz clock is used during the configuration of the PMC */
#define LOWPOWER_CFG_SELCLOCK_12MHZ \
    1 /*!< The 12 MHz clock is used during the configuration of the PMC (to speed up PMC configuration process)*/

#define LOWPOWER_CFG_SELMEMSUPPLY_LDOMEM 0 /*!< In DEEP SLEEP power mode, the Memories are supplied by the LDO_MEM */
#define LOWPOWER_CFG_SELMEMSUPPLY_LDODEEPSLEEP \
    1 /*!< In DEEP SLEEP power mode, the Memories are supplied by the LDO_DEEP_SLEEP (or DCDC) */

#define LOWPOWER_CFG_MEMLOWPOWERMODE_SOURCEBIASING                                                                     \
    0 /*!< All SRAM instances use "Source Biasing" as low power mode technic (it is recommended to set LDO_MEM as high \
         as possible -- 1.1V typical -- during low power mode) */
#define LOWPOWER_CFG_MEMLOWPOWERMODE_VOLTAGESCALING                                                                    \
    1 /*!< All SRAM instances use "Voltage Scaling" as low power mode technic (it is recommended to set LDO_MEM as low \
         as possible -- down to 0.7V -- during low power mode) */

/* CPU Retention Control*/
#define LOWPOWER_CPURETCTRL_ENA_INDEX           0
#define LOWPOWER_CPURETCTRL_ENA_MASK            (0x1UL << LOWPOWER_CPURETCTRL_ENA_INDEX)
#define LOWPOWER_CPURETCTRL_MEMBASE_INDEX       1
#define LOWPOWER_CPURETCTRL_MEMBASE_MASK        (0x1FFFUL << LOWPOWER_CPURETCTRL_MEMBASE_INDEX)
#define LOWPOWER_CPURETCTRL_RETDATALENGTH_INDEX 14
#define LOWPOWER_CPURETCTRL_RETDATALENGTH_MASK  (0x3FFUL << LOWPOWER_CPURETCTRL_RETDATALENGTH_INDEX)

/* Voltgae setting*/
#define DCDC_POWER_PROFILE_LOW_MAX_FREQ_HZ \
    (72000000U) /* Maximum System Frequency allowed with DCDC Power Profile LOW */
#define DCDC_POWER_PROFILE_MEDIUM_MAX_FREQ_HZ \
    (100000000U) /* Maximum System Frequency allowed with DCDC Power Profile MEDIUM */
#define DCDC_POWER_PROFILE_HIGH_MAX_FREQ_HZ \
    (150000000U)                       /* Maximum System Frequency allowed with DCDC Power Profile HIGH */
#define PROCESS_NNN_AVG_HZ (19300000U) /* Average Ring OScillator value for Nominal (NNN) Manufacturing Process */
#define PROCESS_NNN_STD_HZ \
    (400000U) /* Standard Deviation Ring OScillator value for Nominal (NNN) Manufacturing Process */
#define PROCESS_NNN_LIMITS \
    (6U) /* Nominal (NNN) Manufacturing Process Ring Oscillator values limit (with respect to the Average value) */
#define PROCESS_NNN_MIN_HZ \
    (PROCESS_NNN_AVG_HZ -  \
     (PROCESS_NNN_LIMITS * \
      PROCESS_NNN_STD_HZ)) /* Minimum Ring OScillator value for Nominal (NNN) Manufacturing Process */
#define PROCESS_NNN_MAX_HZ \
    (PROCESS_NNN_AVG_HZ +  \
     (PROCESS_NNN_LIMITS * \
      PROCESS_NNN_STD_HZ))         /* Maximum Ring OScillator value for Nominal (NNN) Manufacturing Process */
#define VOLTAGE_SSS_LOW_MV (1100U) /* Voltage Settings for : Process=SSS, DCDC Power Profile=LOW */
#define VOLTAGE_SSS_MED_MV (1150U) /* Voltage Settings for : Process=SSS, DCDC Power Profile=MEDIUM */
#define VOLTAGE_SSS_HIG_MV (1200U) /* Voltage Settings for : Process=SSS, DCDC Power Profile=HIGH */
#define VOLTAGE_NNN_LOW_MV (1050U) /* Voltage Settings for : Process=NNN, DCDC Power Profile=LOW */
#define VOLTAGE_NNN_MED_MV (1075U) /* Voltage Settings for : Process=NNN, DCDC Power Profile=MEDIUM */
#define VOLTAGE_NNN_HIG_MV (1150U) /* Voltage Settings for : Process=NNN, DCDC Power Profile=HIGH */
#define VOLTAGE_FFF_LOW_MV (1000U) /* Voltage Settings for : Process=FFF, DCDC Power Profile=LOW */
#define VOLTAGE_FFF_MED_MV (1025U) /* Voltage Settings for : Process=FFF, DCDC Power Profile=MEDIUM */
#define VOLTAGE_FFF_HIG_MV (1050U) /* Voltage Settings for : Process=FFF, DCDC Power Profile=HIGH */

/**
 * @brief LDO Voltage control in Low Power Modes
 */
#define LOWPOWER_VOLTAGE_LDO_PMU_INDEX        0
#define LOWPOWER_VOLTAGE_LDO_PMU_MASK         (0x1FULL << LOWPOWER_VOLTAGE_LDO_PMU_INDEX)
#define LOWPOWER_VOLTAGE_LDO_MEM_INDEX        5
#define LOWPOWER_VOLTAGE_LDO_MEM_MASK         (0x1FULL << LOWPOWER_VOLTAGE_LDO_MEM_INDEX)
#define LOWPOWER_VOLTAGE_LDO_DEEP_SLEEP_INDEX 10
#define LOWPOWER_VOLTAGE_LDO_DEEP_SLEEP_MASK  (0x7ULL << LOWPOWER_VOLTAGE_LDO_DEEP_SLEEP_INDEX)
#define LOWPOWER_VOLTAGE_LDO_PMU_BOOST_INDEX  19
#define LOWPOWER_VOLTAGE_LDO_PMU_BOOST_MASK   (0x1FULL << LOWPOWER_VOLTAGE_LDO_PMU_BOOST_INDEX)
#define LOWPOWER_VOLTAGE_LDO_MEM_BOOST_INDEX  24
#define LOWPOWER_VOLTAGE_LDO_MEM_BOOST_MASK   (0x1FULL << LOWPOWER_VOLTAGE_LDO_MEM_BOOST_INDEX)
#define LOWPOWER_VOLTAGE_DCDC_INDEX           29
#define LOWPOWER_VOLTAGE_DCDC_MASK            (0xFULL << LOWPOWER_VOLTAGE_DCDC_INDEX)
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.lpc_niobe4_power"
#endif
/*! @brief set and clear bit MACRO's. */
#define U32_SET_BITS(P, B) ((*(uint32_t *)P) |= (B))
#define U32_CLR_BITS(P, B) ((*(uint32_t *)P) &= ~(B))
/* Return values from Config (N-2) page of flash */
#define GET_16MXO_TRIM() (*(uint32_t *)(FLASH_NMPA_BASE + 0xC8U)) // (0x3FCC8)
#define GET_32KXO_TRIM() (*(uint32_t *)(FLASH_NMPA_BASE + 0xCCU)) // (0x3FCCC)

#define CPU_RETENTION_RAMX_STORAGE_START_ADDR (0x04002000)

#define XO_SLAVE_EN (1)
/*******************************************************************************
 * Codes
 ******************************************************************************/

/*******************************************************************************
 * LOCAL FUNCTIONS PROTOTYPES
 ******************************************************************************/
static uint32_t lf_set_ldo_ao_ldo_mem_voltage(uint32_t p_lp_mode, uint32_t p_dcdc_voltage);
static uint32_t lf_wakeup_io_ctrl(uint32_t p_wakeup_io_ctrl);
static uint8_t CLOCK_u8OscCapConvert(uint8_t u8OscCap, uint8_t u8CapBankDiscontinuity);

static void lowpower_set_dcdc_power_profile(lowpower_dcdc_power_profile_enum dcdc_power_profile);
static lowpower_process_corner_enum lowpower_get_part_process_corner(void);
static void lowpower_set_voltage_for_process(lowpower_dcdc_power_profile_enum dcdc_power_profile);
static lowpower_driver_interface_t *s_lowpowerDriver = (lowpower_driver_interface_t *)(0x130050e4);

/**
 * @brief   Configures and enters in low power mode
 * @param   p_lowpower_cfg: pointer to a structure that contains all low power mode parameters
 * @return  Nothing
 *
 *          !!! IMPORTANT NOTES :
 *           1 - CPU Interrupt Enable registers are updated with p_lowpower_cfg->WAKEUPINT. They are NOT restored by the
 * API.
 *           2 - The Non Maskable Interrupt (NMI) should be disable before calling this API (otherwise, there is a risk
 * of Dead Lock).
 *           3 - The HARD FAULT handler should execute from SRAM. (The Hard fault handler should initiate a full chip
 * reset)
 */
static void POWER_EnterLowPower(LPC_LOWPOWER_T *p_lowpower_cfg);

/**
 * @brief
 * @param
 * @return
 */
static void lf_set_dcdc_power_profile_low(void)
{
#define DCDC_POWER_PROFILE_LOW_0_ADDRS (FLASH_NMPA_BASE + 0xE0U) // N4M (0x3FCE0U) / N4S (0x9FCE0U)
#define DCDC_POWER_PROFILE_LOW_1_ADDRS (FLASH_NMPA_BASE + 0xE4U) // N4M (0x3FCE4U) / N4S (0x9FCE4U)

    uint32_t dcdcTrimValue0 = (*((volatile unsigned int *)(DCDC_POWER_PROFILE_LOW_0_ADDRS)));
    uint32_t dcdcTrimValue1 = (*((volatile unsigned int *)(DCDC_POWER_PROFILE_LOW_1_ADDRS)));

    if (0UL != (dcdcTrimValue0 & 0x1UL))
    {
        PMC->DCDC0 = dcdcTrimValue0 >> 1;
        PMC->DCDC1 = dcdcTrimValue1;
    }
}

/**
 * @brief   Configures and enters in low power mode
 * @param           : p_lowpower_cfg
 * @return  Nothing
 */
static void POWER_EnterLowPower(LPC_LOWPOWER_T *p_lowpower_cfg)
{
    /* PMC clk set to 12 MHZ */
    p_lowpower_cfg->CFG |= (uint32_t)LOWPOWER_CFG_SELCLOCK_12MHZ << LOWPOWER_CFG_SELCLOCK_INDEX;

    /* Enable Analog References fast wake-up in case of wake-up from a low power mode (DEEP SLEEP, POWER DOWN and DEEP
     * POWER DOWN) and Hardware Pin reset */
    PMC->REFFASTWKUP = (PMC->REFFASTWKUP & (~PMC_REFFASTWKUP_LPWKUP_MASK) & (~PMC_REFFASTWKUP_HWWKUP_MASK)) |
                       PMC_REFFASTWKUP_LPWKUP(1) | PMC_REFFASTWKUP_HWWKUP(1);

    /* SRAM uses Voltage Scaling in all Low Power modes */
    PMC->SRAMCTRL = (PMC->SRAMCTRL & (~PMC_SRAMCTRL_SMB_MASK)) | PMC_SRAMCTRL_SMB(3);

    /* CPU Retention configuration : preserve the value of FUNCRETENTIONCTRL.RET_LENTH which is a Hardware defined
     * parameter. */
    p_lowpower_cfg->CPURETCTRL = (SYSCON->FUNCRETENTIONCTRL & SYSCON_FUNCRETENTIONCTRL_RET_LENTH_MASK) |
                                 (p_lowpower_cfg->CPURETCTRL & (~SYSCON_FUNCRETENTIONCTRL_RET_LENTH_MASK));

    /* Switch System Clock to FRO12Mhz (the configuration before calling this function will not be restored back) */
    CLOCK_AttachClk(kFRO12M_to_MAIN_CLK);                      /* Switch main clock to FRO12MHz */
    CLOCK_SetClkDiv(kCLOCK_DivAhbClk, 1U, false);              /* Main clock divided by 1 */
    SYSCON->FMCCR = (SYSCON->FMCCR & 0xFFFF0000UL) | 0x201AUL; /* Adjust FMC waiting time cycles */
    lf_set_dcdc_power_profile_low(); /* Align DCDC Power profile with the 12 MHz clock (DCDC Power Profile LOW) */

    (*(s_lowpowerDriver->set_lowpower_mode))(p_lowpower_cfg);

    /* Restore the configuration of the MISCCTRL Register : LDOMEMBLEEDDSLP = 0, LDOMEMHIGHZMODE =
     * 0 */
    PMC->MISCCTRL &= (~PMC_MISCCTRL_DISABLE_BLEED_MASK) & (~PMC_MISCCTRL_LDOMEMHIGHZMODE_MASK);
}

/**
 * @brief   Shut off the Flash and execute the _WFI(), then power up the Flash after wake-up event
 * @param   None
 * @return  Nothing
 */
void POWER_CycleCpuAndFlash(void)
{
    (*(s_lowpowerDriver->power_cycle_cpu_and_flash))();
};

/**
 * brief    PMC Deep Sleep function call
 * return   nothing
 */
void POWER_EnterDeepSleep(uint32_t exclude_from_pd,
                          uint32_t sram_retention_ctrl,
                          uint64_t wakeup_interrupts,
                          uint32_t hardware_wake_ctrl)
{
    LPC_LOWPOWER_T lv_low_power_mode_cfg; /* Low Power Mode configuration structure */
    uint32_t cpu0_nmi_enable;
    uint32_t cpu0_int_enable_0;
    uint32_t cpu0_int_enable_1;
    uint32_t dcdc_voltage;
    uint32_t pmc_reset_ctrl;
    /* Clear Low Power Mode configuration variable */
    (void)memset(&lv_low_power_mode_cfg, 0x0, sizeof(LPC_LOWPOWER_T));

    /* Configure Low Power Mode configuration variable */
    lv_low_power_mode_cfg.CFG |= (uint32_t)LOWPOWER_CFG_LPMODE_DEEPSLEEP
                                 << LOWPOWER_CFG_LPMODE_INDEX; /* DEEPSLEEP mode */

    /* DCDC will be always used during Deep Sleep (instead of LDO Deep Sleep); Make sure LDO MEM & Analog references
     * will stay powered, Shut down ROM */
    lv_low_power_mode_cfg.PDCTRL0 = (~exclude_from_pd & ~(uint32_t)kPDRUNCFG_PD_DCDC & ~(uint32_t)kPDRUNCFG_PD_LDOMEM &
                                     ~(uint32_t)kPDRUNCFG_PD_BIAS) |
                                    (uint32_t)kPDRUNCFG_PD_LDODEEPSLEEP | (uint32_t)kPDRUNCFG_PD_ROM;

    /* Voltage control in DeepSleep Low Power Modes */
    /* The Memories Voltage settings below are for voltage scaling */
    dcdc_voltage                  = (uint32_t)V_DCDC_0P950;
    lv_low_power_mode_cfg.VOLTAGE = lf_set_ldo_ao_ldo_mem_voltage(LOWPOWER_CFG_LPMODE_POWERDOWN, dcdc_voltage);

    /* SRAM retention control during POWERDOWN */
    lv_low_power_mode_cfg.SRAMRETCTRL = sram_retention_ctrl;

    /* CPU Wake up & Interrupt sources control */
    lv_low_power_mode_cfg.WAKEUPINT = wakeup_interrupts;
    lv_low_power_mode_cfg.WAKEUPSRC = wakeup_interrupts;

    /* Interrupts that allow DMA transfers with Flexcomm without waking up the Processor */
    if (0UL != (hardware_wake_ctrl & (LOWPOWER_HWWAKE_PERIPHERALS | LOWPOWER_HWWAKE_SDMA0 | LOWPOWER_HWWAKE_SDMA1)))
    {
        lv_low_power_mode_cfg.HWWAKE = (hardware_wake_ctrl & ~LOWPOWER_HWWAKE_FORCED) | LOWPOWER_HWWAKE_ENABLE_FRO192M;
    }

    cpu0_nmi_enable = SYSCON->NMISRC & SYSCON_NMISRC_NMIENCPU0_MASK; /* Save the configuration of the NMI Register */
    SYSCON->NMISRC &= ~SYSCON_NMISRC_NMIENCPU0_MASK;                 /* Disable NMI of CPU0 */

    /* Save the configuration of the CPU interrupt enable Registers (because they are overwritten inside the low power
     * API */
    cpu0_int_enable_0 = NVIC->ISER[0];
    cpu0_int_enable_1 = NVIC->ISER[1];

    pmc_reset_ctrl = PMC->RESETCTRL;

    if ((pmc_reset_ctrl & (PMC_RESETCTRL_BODCORERESETENA_SECURE_MASK | PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_MASK)) ==
        ((0x1UL << PMC_RESETCTRL_BODCORERESETENA_SECURE_SHIFT) |
         (0x1UL << PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_SHIFT)))
    {
        /* BoD CORE reset is activated, so make sure BoD Core and Biasing won't be shutdown */
        lv_low_power_mode_cfg.PDCTRL0 &= ~(uint32_t)kPDRUNCFG_PD_BODCORE & ~(uint32_t)kPDRUNCFG_PD_BIAS;
    }
    if ((pmc_reset_ctrl & (PMC_RESETCTRL_BODVBATRESETENA_SECURE_MASK | PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_MASK)) ==
        ((0x1UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_SHIFT) |
         (0x1UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_SHIFT)))
    {
        /* BoD VBAT reset is activated, so make sure BoD VBAT and Biasing won't be shutdown */
        lv_low_power_mode_cfg.PDCTRL0 &= ~(uint32_t)kPDRUNCFG_PD_BODVBAT & ~(uint32_t)kPDRUNCFG_PD_BIAS;
    }

    /* Enter low power mode */
    POWER_EnterLowPower(&lv_low_power_mode_cfg);

    /* Restore the configuration of the NMI Register */
    SYSCON->NMISRC |= cpu0_nmi_enable;

    /* Restore the configuration of the CPU interrupt enable Registers (because they have been overwritten inside the
     * low power API */
    NVIC->ISER[0] = cpu0_int_enable_0;
    NVIC->ISER[1] = cpu0_int_enable_1;
}

/**
 * brief    PMC power Down function call
 * return   nothing
 */
void POWER_EnterPowerDown(uint32_t exclude_from_pd,
                          uint32_t sram_retention_ctrl,
                          uint64_t wakeup_interrupts,
                          uint32_t cpu_retention_ctrl)
{
    LPC_LOWPOWER_T lv_low_power_mode_cfg; /* Low Power Mode configuration structure */
    uint32_t cpu0_nmi_enable;
    uint32_t cpu0_int_enable_0;
    uint32_t cpu0_int_enable_1;
    uint64_t wakeup_src_int;
    uint32_t pmc_reset_ctrl;
    uint32_t rng_entropy_save[6];

    uint32_t analog_ctrl_regs[12]; /* To store Analog Controller Regristers */

    /* Clear Low Power Mode configuration variable */
    (void)memset(&lv_low_power_mode_cfg, 0x0, sizeof(LPC_LOWPOWER_T));

    /* Configure Low Power Mode configuration variable */
    lv_low_power_mode_cfg.CFG |= (uint32_t)LOWPOWER_CFG_LPMODE_POWERDOWN
                                 << LOWPOWER_CFG_LPMODE_INDEX; /* POWER DOWN mode */

    /* Only FRO32K, XTAL32K, COMP, BIAS and LDO_MEM can be stay powered during POWERDOWN (valid from application point
     * of view; Hardware allows BODVBAT, LDODEEPSLEEP and FRO1M to stay powered, that's why they are excluded below) */
    lv_low_power_mode_cfg.PDCTRL0 = (~exclude_from_pd) | (uint32_t)kPDRUNCFG_PD_BODVBAT | (uint32_t)kPDRUNCFG_PD_FRO1M |
                                    (uint32_t)kPDRUNCFG_PD_LDODEEPSLEEP;

    /* @TODO Guillaume: add save/restore entropy during PowerDown */
    /* Entropy for RNG need to saved */
    if ((exclude_from_pd & (uint32_t)kPDRUNCFG_PD_RNG) != 0UL)
    {
        CLOCK_EnableClock(kCLOCK_Rng);
        RESET_ClearPeripheralReset(kRNG_RST_SHIFT_RSTn);
        for (int i = 0; i < 6; i++)
        {
            rng_entropy_save[i] = RNG->RANDOM_NUMBER;
        }
    }

    /* CPU0 retention Ctrl.
     * For the time being, we do not allow customer to relocate the CPU retention area in SRAMX, meaning that the
     * retention area range is [0x0400_6000 - 0x0400_6600] (beginning of RAMX2 for N4S) If required by customer,
     * retention area range is [0x0400_2000 - 0x0400_2600] (beginning of RAMX2 for N4M) If required by customer,
     * cpu_retention_ctrl[13:1] will be used for that to modify the default retention area
     */
    lv_low_power_mode_cfg.CPURETCTRL =
        (cpu_retention_ctrl & LOWPOWER_CPURETCTRL_ENA_MASK) |
        ((((uint32_t)CPU_RETENTION_RAMX_STORAGE_START_ADDR >> 2UL) << LOWPOWER_CPURETCTRL_MEMBASE_INDEX) &
         LOWPOWER_CPURETCTRL_MEMBASE_MASK);
    if (0UL != (cpu_retention_ctrl & 0x1UL))
    {
        /* Add RAMX2 for retention */
        sram_retention_ctrl |= LOWPOWER_SRAMRETCTRL_RETEN_RAMX2;
        /* CPU retention is required: store Analog Controller Registers */
        analog_ctrl_regs[0] = ANACTRL->FRO192M_CTRL;
        analog_ctrl_regs[1] = ANACTRL->ANALOG_CTRL_CFG;
        analog_ctrl_regs[2] = ANACTRL->ADC_CTRL;
        analog_ctrl_regs[3] = ANACTRL->XO32M_CTRL;
        analog_ctrl_regs[4] = ANACTRL->BOD_DCDC_INT_CTRL;
        analog_ctrl_regs[5] = ANACTRL->RINGO0_CTRL;
        analog_ctrl_regs[6] = ANACTRL->RINGO1_CTRL;
        analog_ctrl_regs[7] = ANACTRL->RINGO2_CTRL;
        analog_ctrl_regs[8] = ANACTRL->LDO_XO32M;
        analog_ctrl_regs[9] = ANACTRL->AUX_BIAS;
    }

    /* SRAM retention control during POWERDOWN */
    lv_low_power_mode_cfg.SRAMRETCTRL = sram_retention_ctrl;

    /* Sanity check: If retention is required for any of SRAM instances, make sure LDO MEM will stay powered */
    if ((sram_retention_ctrl & 0x7FFFUL) != 0UL)
    {
        lv_low_power_mode_cfg.PDCTRL0 &= ~(uint32_t)kPDRUNCFG_PD_LDOMEM;
    }

    /* Voltage control in Low Power Modes */
    /* The Memories Voltage settings below are for voltage scaling */
    lv_low_power_mode_cfg.VOLTAGE = lf_set_ldo_ao_ldo_mem_voltage(LOWPOWER_CFG_LPMODE_POWERDOWN, 0);

    /* CPU Wake up & Interrupt sources control : only WAKEUP_GPIO_GLOBALINT0, WAKEUP_GPIO_GLOBALINT1, WAKEUP_FLEXCOMM3,
     * WAKEUP_ACMP_CAPT, WAKEUP_RTC_LITE_ALARM_WAKEUP, WAKEUP_OS_EVENT_TIMER, WAKEUP_ALLWAKEUPIOS */
    wakeup_src_int = (uint64_t)(WAKEUP_GPIO_GLOBALINT0 | WAKEUP_GPIO_GLOBALINT1 | WAKEUP_FLEXCOMM3 | WAKEUP_ACMP |
                                WAKEUP_RTC_LITE_ALARM_WAKEUP | WAKEUP_OS_EVENT_TIMER | WAKEUP_ALLWAKEUPIOS);
    lv_low_power_mode_cfg.WAKEUPINT = wakeup_interrupts & wakeup_src_int;
    lv_low_power_mode_cfg.WAKEUPSRC = wakeup_interrupts & wakeup_src_int;

    cpu0_nmi_enable = SYSCON->NMISRC & SYSCON_NMISRC_NMIENCPU0_MASK; /* Save the configuration of the NMI Register */
    SYSCON->NMISRC &= ~SYSCON_NMISRC_NMIENCPU0_MASK;                 /* Disable NMI of CPU0 */

    /* Save the configuration of the CPU interrupt enable Registers (because they are overwritten inside the low power
     * API */
    cpu0_int_enable_0 = NVIC->ISER[0];
    cpu0_int_enable_1 = NVIC->ISER[1];

    /* Disable BoD VBAT and BoD Core resets */
    /* BOD VBAT disable reset */
    pmc_reset_ctrl =
        PMC->RESETCTRL & (~(PMC_RESETCTRL_BODVBATRESETENA_SECURE_MASK | PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_MASK));
    pmc_reset_ctrl |= (0x2UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_SHIFT) |
                      (0x2UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_SHIFT);

    /* BOD CORE disable reset */
    pmc_reset_ctrl &= (~(PMC_RESETCTRL_BODCORERESETENA_SECURE_MASK | PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_MASK));
    pmc_reset_ctrl |= (0x2UL << PMC_RESETCTRL_BODCORERESETENA_SECURE_SHIFT) |
                      (0x2UL << PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_SHIFT);

    PMC->RESETCTRL = pmc_reset_ctrl;

    /* Enter low power mode */
    POWER_EnterLowPower(&lv_low_power_mode_cfg);

    /*** We'll reach this point in case of POWERDOWN with CPU retention or if the POWERDOWN has not been taken (for
      instance because an interrupt is pending). In case of CPU retention, assumption is that the SRAM containing the
      stack used to call this function shall be preserved during low power ***/

    /* Following code is to reset PUF to remove over consumption */
    /* Enable PUF register clock to access register */
    SYSCON->AHBCLKCTRLSET[2] = SYSCON_AHBCLKCTRL2_PUF_MASK;
    /* Release PUF reset */
    SYSCON->PRESETCTRLCLR[2] = SYSCON_PRESETCTRL2_PUF_RST_MASK;
    /* Enable PUF SRAM */
    PUF_SRAM_CTRL->CFG |= PUF_SRAM_CTRL_CFG_ENABLE_MASK | PUF_SRAM_CTRL_CFG_CKGATING_MASK;

    /* Disable PUF register clock. */
    // Delaying the line of code below until the PUF State Machine execution is completed:
    // Shuting down the clock to early will prevent the state machine from reaching the end.
    // => Wait for status bit in PUF Controller Registers before stop PUF clock.
    while (0UL == (PUF_SRAM_CTRL->INT_STATUS & PUF_SRAM_CTRL_INT_STATUS_READY_MASK))
    {
    }

    SYSCON->AHBCLKCTRLCLR[2] = SYSCON_AHBCLKCTRL2_PUF_MASK;

    /* Restore the configuration of the NMI Register */
    SYSCON->NMISRC |= cpu0_nmi_enable;

    /* Restore PMC RESETCTRL register */
    PMC->RESETCTRL = pmc_reset_ctrl;

    /* Restore the configuration of the CPU interrupt enable Registers (because they have been overwritten inside the
     * low power API */
    NVIC->ISER[0] = cpu0_int_enable_0;
    NVIC->ISER[1] = cpu0_int_enable_1;

    if ((cpu_retention_ctrl & 0x1UL) != 0UL)
    {
        /* Restore Analog Controller Registers */
        ANACTRL->FRO192M_CTRL      = analog_ctrl_regs[0] | ANACTRL_FRO192M_CTRL_WRTRIM_MASK;
        ANACTRL->ANALOG_CTRL_CFG   = analog_ctrl_regs[1];
        ANACTRL->ADC_CTRL          = analog_ctrl_regs[2];
        ANACTRL->XO32M_CTRL        = analog_ctrl_regs[3];
        ANACTRL->BOD_DCDC_INT_CTRL = analog_ctrl_regs[4];
        ANACTRL->RINGO0_CTRL       = analog_ctrl_regs[5];
        ANACTRL->RINGO1_CTRL       = analog_ctrl_regs[6];
        ANACTRL->RINGO2_CTRL       = analog_ctrl_regs[7];
        ANACTRL->LDO_XO32M         = analog_ctrl_regs[8];
        ANACTRL->AUX_BIAS          = analog_ctrl_regs[9];
    }

    /* @TODO Guillaume: add save/restore entropy during PowerDown */
    /* Restore Entropy for RNG */
    if ((exclude_from_pd & (uint32_t)kPDRUNCFG_PD_RNG) != 0UL)
    {
        RNG->POWERDOWN &= ~RNG_POWERDOWN_POWERDOWN_MASK;
        for (int i = 0; i < 6; i++)
        {
            RNG->ENTROPY_INJECT = rng_entropy_save[i];
        }
    }
}

/**
 * brief    PMC Deep Sleep Power Down function call
 * return   nothing
 */
void POWER_EnterDeepPowerDown(uint32_t exclude_from_pd,
                              uint32_t sram_retention_ctrl,
                              uint64_t wakeup_interrupts,
                              uint32_t wakeup_io_ctrl)
{
    LPC_LOWPOWER_T lv_low_power_mode_cfg; /* Low Power Mode configuration structure */
    uint32_t cpu0_nmi_enable;
    uint32_t cpu0_int_enable_0;
    uint32_t cpu0_int_enable_1;
    uint32_t pmc_reset_ctrl;

    /* Clear Low Power Mode configuration variable */
    (void)memset(&lv_low_power_mode_cfg, 0x0, sizeof(LPC_LOWPOWER_T));

    /* Configure Low Power Mode configuration variable */
    lv_low_power_mode_cfg.CFG |= (uint32_t)LOWPOWER_CFG_LPMODE_DEEPPOWERDOWN
                                 << LOWPOWER_CFG_LPMODE_INDEX; /* DEEP POWER DOWN mode */

    /* Only FRO32K, XTAL32K and LDO_MEM can be stay powered during DEEPPOWERDOWN (valid from application point of view;
     * Hardware allows BODVBAT, BIAS FRO1M and COMP to stay powered, that's why they are excluded below) */
    lv_low_power_mode_cfg.PDCTRL0 = (~exclude_from_pd) | (uint32_t)kPDRUNCFG_PD_BIAS | (uint32_t)kPDRUNCFG_PD_BODVBAT |
                                    (uint32_t)kPDRUNCFG_PD_FRO1M | (uint32_t)kPDRUNCFG_PD_COMP;

    /* SRAM retention control during DEEPPOWERDOWN */
    /* RAM00 used by ROM code to restart. */
    sram_retention_ctrl = sram_retention_ctrl & (~(LOWPOWER_SRAMRETCTRL_RETEN_RAM00));

    /* SRAM retention control during DEEPPOWERDOWN */
    lv_low_power_mode_cfg.SRAMRETCTRL = sram_retention_ctrl;

    /* Sanity check: If retention is required for any of SRAM instances, make sure LDO MEM will stay powered */
    if ((sram_retention_ctrl & 0x7FFFUL) != 0UL)
    {
        lv_low_power_mode_cfg.PDCTRL0 &= ~(uint32_t)kPDRUNCFG_PD_LDOMEM;
    }

    /* Voltage control in Low Power Modes */
    /* The Memories Voltage settings below are for voltage scaling */
    lv_low_power_mode_cfg.VOLTAGE = lf_set_ldo_ao_ldo_mem_voltage(LOWPOWER_CFG_LPMODE_DEEPPOWERDOWN, 0);

    lv_low_power_mode_cfg.WAKEUPINT =
        wakeup_interrupts & (WAKEUP_RTC_LITE_ALARM_WAKEUP |
                             WAKEUP_OS_EVENT_TIMER); /* CPU Wake up sources control : only WAKEUP_RTC_LITE_ALARM_WAKEUP,
                                                        WAKEUP_OS_EVENT_TIMER */
    lv_low_power_mode_cfg.WAKEUPSRC =
        wakeup_interrupts &
        (WAKEUP_RTC_LITE_ALARM_WAKEUP | WAKEUP_OS_EVENT_TIMER |
         WAKEUP_ALLWAKEUPIOS); /*!< Hardware Wake up sources control: : only WAKEUP_RTC_LITE_ALARM_WAKEUP,
                                  WAKEUP_OS_EVENT_TIMER and WAKEUP_ALLWAKEUPIOS */

    /* Wake up I/O sources */
    lv_low_power_mode_cfg.WAKEUPIOSRC = lf_wakeup_io_ctrl(wakeup_io_ctrl);

    cpu0_nmi_enable = SYSCON->NMISRC & SYSCON_NMISRC_NMIENCPU0_MASK; /* Save the configuration of the NMI Register */
    SYSCON->NMISRC &= ~SYSCON_NMISRC_NMIENCPU0_MASK;                 /* Disable NMI of CPU0 */

    /* Save the configuration of the CPU interrupt enable Registers (because they are overwritten inside the low power
     * API */
    cpu0_int_enable_0 = NVIC->ISER[0];
    cpu0_int_enable_1 = NVIC->ISER[1];

    /* Save the configuration of the PMC RESETCTRL register */
    /* BOD VBAT disable reset */
    pmc_reset_ctrl =
        PMC->RESETCTRL & (~(PMC_RESETCTRL_BODVBATRESETENA_SECURE_MASK | PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_MASK));
    pmc_reset_ctrl |= (0x2UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_SHIFT) |
                      (0x2UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_SHIFT);

    /* BOD CORE disable reset */
    pmc_reset_ctrl &= (~(PMC_RESETCTRL_BODCORERESETENA_SECURE_MASK | PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_MASK));
    pmc_reset_ctrl |= (0x2UL << PMC_RESETCTRL_BODCORERESETENA_SECURE_SHIFT) |
                      (0x2UL << PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_SHIFT);

    PMC->RESETCTRL = pmc_reset_ctrl;

    /* Enter low power mode */
    POWER_EnterLowPower(&lv_low_power_mode_cfg);

    /*** We'll reach this point ONLY and ONLY if the DEEPPOWERDOWN has not been taken (for instance because an RTC or
     * OSTIMER interrupt is pending) ***/

    /* Restore the configuration of the NMI Register */
    SYSCON->NMISRC |= cpu0_nmi_enable;

    /* Restore PMC RESETCTRL register */
    PMC->RESETCTRL = pmc_reset_ctrl;

    /* Restore the configuration of the CPU interrupt enable Registers (because they have been overwritten inside the
     * low power API */
    NVIC->ISER[0] = cpu0_int_enable_0;
    NVIC->ISER[1] = cpu0_int_enable_1;
}

/**
 * brief    PMC Sleep function call
 * return   nothing
 */
void POWER_EnterSleep(void)
{
    uint32_t pmsk;
    pmsk = __get_PRIMASK();
    __disable_irq();
    SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
    __WFI();
    __set_PRIMASK(pmsk);
}

/* Get power lib version */
/*!
 * brief Power Library API to return the library version.
 *
 * param none
 * return version number of the power library
 */
uint32_t POWER_GetLibVersion(void)
{
    return FSL_LPC55SXX_POWER_CONTROL_VERSION;
}

/**
 * @brief
 * @param
 * @return
 */
static uint32_t lf_set_ldo_ao_ldo_mem_voltage(uint32_t p_lp_mode, uint32_t p_dcdc_voltage)
{
#define FLASH_NMPA_LDO_AO_ADDRS                (FLASH_NMPA_BASE + 0x0F4U) // N4M (0x3FCF4U) / N4S (0x9FCF4U)
#define FLASH_NMPA_LDO_AO_DSLP_TRIM_VALID_MASK (0x100U)
#define FLASH_NMPA_LDO_AO_DSLP_TRIM_MASK       (0x3E00U)
#define FLASH_NMPA_LDO_AO_DSLP_TRIM_SHIFT      (9U)
#define FLASH_NMPA_LDO_AO_PDWN_TRIM_VALID_MASK (0x10000U)
#define FLASH_NMPA_LDO_AO_PDWN_TRIM_MASK       (0x3E0000U)
#define FLASH_NMPA_LDO_AO_PDWN_TRIM_SHIFT      (17U)
#define FLASH_NMPA_LDO_AO_DPDW_TRIM_VALID_MASK (0x1000000U)
#define FLASH_NMPA_LDO_AO_DPDW_TRIM_MASK       (0x3E000000U)
#define FLASH_NMPA_LDO_AO_DPDW_TRIM_SHIFT      (25U)

    uint32_t ldo_ao_trim, voltage;
    uint32_t lv_v_ldo_pmu, lv_v_ldo_pmu_boost;

    ldo_ao_trim = (*((volatile unsigned int *)(FLASH_NMPA_LDO_AO_ADDRS)));

    switch (p_lp_mode)
    {
        case LOWPOWER_CFG_LPMODE_DEEPSLEEP:
        {
            if ((ldo_ao_trim & FLASH_NMPA_LDO_AO_DSLP_TRIM_VALID_MASK) != 0UL)
            {
                /* Apply settings coming from Flash */
                lv_v_ldo_pmu = (ldo_ao_trim & FLASH_NMPA_LDO_AO_DSLP_TRIM_MASK) >> FLASH_NMPA_LDO_AO_DSLP_TRIM_SHIFT;
                lv_v_ldo_pmu_boost = lv_v_ldo_pmu - 2UL; /* - 50 mV */
            }
            else
            {
                /* Apply default settings */
                lv_v_ldo_pmu       = (uint32_t)V_AO_0P900;
                lv_v_ldo_pmu_boost = (uint32_t)V_AO_0P850;
            }
        }
        break;

        case LOWPOWER_CFG_LPMODE_POWERDOWN:
        {
            if ((ldo_ao_trim & FLASH_NMPA_LDO_AO_PDWN_TRIM_VALID_MASK) != 0UL)
            {
                /* Apply settings coming from Flash */
                lv_v_ldo_pmu = (ldo_ao_trim & FLASH_NMPA_LDO_AO_PDWN_TRIM_MASK) >> FLASH_NMPA_LDO_AO_PDWN_TRIM_SHIFT;
                lv_v_ldo_pmu_boost = lv_v_ldo_pmu - 2UL; /* - 50 mV */
            }
            else
            {
                /* Apply default settings */
                lv_v_ldo_pmu       = (uint32_t)V_AO_0P800;
                lv_v_ldo_pmu_boost = (uint32_t)V_AO_0P750;
            }
        }
        break;

        case LOWPOWER_CFG_LPMODE_DEEPPOWERDOWN:
        {
            if ((ldo_ao_trim & FLASH_NMPA_LDO_AO_DPDW_TRIM_VALID_MASK) != 0UL)
            {
                /* Apply settings coming from Flash */
                lv_v_ldo_pmu = (ldo_ao_trim & FLASH_NMPA_LDO_AO_DPDW_TRIM_MASK) >> FLASH_NMPA_LDO_AO_DPDW_TRIM_SHIFT;
                lv_v_ldo_pmu_boost = lv_v_ldo_pmu - 2UL; /* - 50 mV */
            }
            else
            {
                /* Apply default settings */
                lv_v_ldo_pmu       = (uint32_t)V_AO_0P800;
                lv_v_ldo_pmu_boost = (uint32_t)V_AO_0P750;
            }
        }
        break;

        default:
            /* We should never reach this point */
            lv_v_ldo_pmu       = (uint32_t)V_AO_1P100;
            lv_v_ldo_pmu_boost = (uint32_t)V_AO_1P050;
            break;
    }

    /* The Memories Voltage settings below are for voltage scaling */
    voltage =
        (lv_v_ldo_pmu << LOWPOWER_VOLTAGE_LDO_PMU_INDEX) |               /*  */
        (lv_v_ldo_pmu_boost << LOWPOWER_VOLTAGE_LDO_PMU_BOOST_INDEX) |   /*  */
        ((uint32_t)V_AO_0P750 << LOWPOWER_VOLTAGE_LDO_MEM_INDEX) |       /* Set to 0.75V (voltage Scaling) */
        ((uint32_t)V_AO_0P700 << LOWPOWER_VOLTAGE_LDO_MEM_BOOST_INDEX) | /* Set to 0.7V  (voltage Scaling) */
        ((uint32_t)V_DEEPSLEEP_0P900
         << LOWPOWER_VOLTAGE_LDO_DEEP_SLEEP_INDEX) |    /* Set to 0.90 V (Not used because LDO_DEEP_SLEEP is disabled)*/
        (p_dcdc_voltage << LOWPOWER_VOLTAGE_DCDC_INDEX) /*  */
        ;

    return (voltage);
}

/**
 * @brief
 * @param
 * @return
 */
static uint32_t lf_wakeup_io_ctrl(uint32_t p_wakeup_io_ctrl)
{
    uint32_t wake_up_type;
    uint32_t wakeup_io_ctrl_reg;
    uint8_t use_external_pullupdown = 0;

    /* Configure Pull up & Pull down based on the required wake-up edge */
    CLOCK_EnableClock(kCLOCK_Iocon);

    wakeup_io_ctrl_reg = 0UL;

    /* Wake-up I/O 0 */
    wake_up_type = (p_wakeup_io_ctrl & 0x3UL) >> LOWPOWER_WAKEUPIOSRC_PIO0_INDEX;
    wakeup_io_ctrl_reg |= (wake_up_type << LOWPOWER_WAKEUPIOSRC_PIO0_INDEX);
    use_external_pullupdown = (uint8_t)((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO0_USEEXTERNALPULLUPDOWN_MASK) >>
                                        LOWPOWER_WAKEUPIO_PIO0_USEEXTERNALPULLUPDOWN_INDEX);

    if (use_external_pullupdown == 0UL)
    {
        if ((wake_up_type == 1UL) || (wake_up_type == 3UL))
        {
            /* Rising edge and both rising and falling edges */
            IOCON->PIO[1][1] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(1); /* Pull down */
            wakeup_io_ctrl_reg |=
                ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO0MODE_INDEX);
        }
        else
        {
            if (wake_up_type == 2UL)
            {
                /* Falling edge only */
                IOCON->PIO[1][1] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(2); /* Pull up */
                wakeup_io_ctrl_reg |=
                    ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO0MODE_INDEX);
            }
            else
            {
                /* Wake-up I/O is disabled : set it as required by the user */
                if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO0_DISABLEPULLUPDOWN_MASK) != 0UL)
                {
                    /* Wake-up I/O is configured as Plain Input */
                    // @TODO not used               p_wakeup_io_ctrl &= ~LOWPOWER_WAKEUPIO_PIO0_PULLUPDOWN_MASK;
                    wakeup_io_ctrl_reg |=
                        ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO0MODE_INDEX);
                }
                else
                {
                    /* Wake-up I/O is configured as pull-up or pull-down */
                    // @TODO update for mask name
                    if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO0_PULLUPDOWN_MASK) != 0UL)
                    {
                        /* Wake-up I/O is configured as pull-up */
                        wakeup_io_ctrl_reg |=
                            ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO0MODE_INDEX);
                    }
                    else
                    {
                        /* Wake-up I/O is configured as pull-down */
                        wakeup_io_ctrl_reg |=
                            ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO0MODE_INDEX);
                    }
                }
            }
        }
    }
    else
    {
        wakeup_io_ctrl_reg |= ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO0MODE_INDEX);
    }

    /* Wake-up I/O 1 */
    wake_up_type = (p_wakeup_io_ctrl & 0xCUL) >> LOWPOWER_WAKEUPIOSRC_PIO1_INDEX;
    wakeup_io_ctrl_reg |= (wake_up_type << LOWPOWER_WAKEUPIOSRC_PIO1_INDEX);
    use_external_pullupdown = (uint8_t)((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO1_USEEXTERNALPULLUPDOWN_MASK) >>
                                        LOWPOWER_WAKEUPIO_PIO1_USEEXTERNALPULLUPDOWN_INDEX);

    if (use_external_pullupdown == 0UL)
    {
        if ((wake_up_type == 1UL) || (wake_up_type == 3UL))
        {
            /* Rising edge  and both rising and falling edges */
            IOCON->PIO[0][28] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(1); /* Pull down */
            wakeup_io_ctrl_reg |=
                ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO1MODE_INDEX);
        }
        else
        {
            if (wake_up_type == 2UL)
            {
                /* Falling edge only */
                IOCON->PIO[0][28] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(2); /* Pull up */
                wakeup_io_ctrl_reg |=
                    ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO1MODE_INDEX);
            }
            else
            {
                /* Wake-up I/O is disabled : set it as required by the user */
                if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO1_DISABLEPULLUPDOWN_MASK) != 0UL)
                {
                    /* Wake-up I/O is configured as Plain Input */
                    // @TODO not used                p_wakeup_io_ctrl &= ~LOWPOWER_WAKEUPIO_PIO1_PULLUPDOWN_MASK;
                    wakeup_io_ctrl_reg |=
                        ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO1MODE_INDEX);
                }
                else
                {
                    /* Wake-up I/O is configured as pull-up or pull-down */
                    // @TODO update for mask name
                    if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO1_PULLUPDOWN_MASK) != 0UL)
                    {
                        /* Wake-up I/O is configured as pull-up */
                        wakeup_io_ctrl_reg |=
                            ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO1MODE_INDEX);
                    }
                    else
                    {
                        /* Wake-up I/O is configured as pull-down */
                        wakeup_io_ctrl_reg |=
                            ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO1MODE_INDEX);
                    }
                }
            }
        }
    }
    else
    {
        wakeup_io_ctrl_reg |= ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO1MODE_INDEX);
    }

    /* Wake-up I/O 2 */
    wake_up_type = (p_wakeup_io_ctrl & 0x30UL) >> LOWPOWER_WAKEUPIOSRC_PIO2_INDEX;
    wakeup_io_ctrl_reg |= (wake_up_type << LOWPOWER_WAKEUPIOSRC_PIO2_INDEX);
    use_external_pullupdown = (uint8_t)((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO2_USEEXTERNALPULLUPDOWN_MASK) >>
                                        LOWPOWER_WAKEUPIO_PIO2_USEEXTERNALPULLUPDOWN_INDEX);

    if (use_external_pullupdown == 0UL)
    {
        if ((wake_up_type == 1UL) || (wake_up_type == 3UL))
        {
            /* Rising edge  and both rising and falling edges */
            IOCON->PIO[1][18] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(1); /* Pull down */
            wakeup_io_ctrl_reg |=
                ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO2MODE_INDEX);
        }
        else
        {
            if (wake_up_type == 2UL)
            {
                /* Falling edge only */
                IOCON->PIO[1][18] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(2); /* Pull up */
                wakeup_io_ctrl_reg |=
                    ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO2MODE_INDEX);
            }
            else
            {
                /* Wake-up I/O is disabled : set it as required by the user */
                if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO2_DISABLEPULLUPDOWN_MASK) != 0UL)
                {
                    /* Wake-up I/O is configured as Plain Input */
                    // @TODO not used                p_wakeup_io_ctrl &= ~LOWPOWER_WAKEUPIO_PIO2_PULLUPDOWN_MASK;
                    wakeup_io_ctrl_reg |=
                        ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO2MODE_INDEX);
                }
                else
                {
                    /* Wake-up I/O is configured as pull-up or pull-down */
                    // @TODO update for mask name
                    if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO2_PULLUPDOWN_MASK) != 0UL)
                    {
                        /* Wake-up I/O is configured as pull-up */
                        wakeup_io_ctrl_reg |=
                            ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO2MODE_INDEX);
                    }
                    else
                    {
                        /* Wake-up I/O is configured as pull-down */
                        wakeup_io_ctrl_reg |=
                            ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO2MODE_INDEX);
                    }
                }
            }
        }
    }
    else
    {
        wakeup_io_ctrl_reg |= ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO2MODE_INDEX);
    }

    /* Wake-up I/O 3 */
    wake_up_type = (p_wakeup_io_ctrl & 0xC0UL) >> LOWPOWER_WAKEUPIOSRC_PIO3_INDEX;
    wakeup_io_ctrl_reg |= (wake_up_type << LOWPOWER_WAKEUPIOSRC_PIO3_INDEX);
    use_external_pullupdown = (uint8_t)((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO3_USEEXTERNALPULLUPDOWN_MASK) >>
                                        LOWPOWER_WAKEUPIO_PIO3_USEEXTERNALPULLUPDOWN_INDEX);

    if (use_external_pullupdown == 0UL)
    {
        if ((wake_up_type == 1UL) || (wake_up_type == 3UL))
        {
            /* Rising edge  and both rising and falling edges */
            IOCON->PIO[1][30] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(1); /* Pull down */
            wakeup_io_ctrl_reg |=
                ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO3MODE_INDEX);
        }
        else
        {
            if (wake_up_type == 2UL)
            {
                /* Falling edge only */
                IOCON->PIO[1][30] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(2); /* Pull up */
                wakeup_io_ctrl_reg |=
                    ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO3MODE_INDEX);
            }
            else
            {
                /* Wake-up I/O is disabled : set it as required by the user */
                if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO3_DISABLEPULLUPDOWN_MASK) != 0UL)
                {
                    /* Wake-up I/O is configured as Plain Input */
                    // @TODO not used                p_wakeup_io_ctrl &= ~LOWPOWER_WAKEUPIO_PIO3_PULLUPDOWN_MASK;
                    wakeup_io_ctrl_reg |=
                        ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO3MODE_INDEX);
                }
                else
                {
                    /* Wake-up I/O is configured as pull-up or pull-down */
                    // @TODO update for mask name
                    if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO3_PULLUPDOWN_MASK) != 0UL)
                    {
                        /* Wake-up I/O is configured as pull-up */
                        wakeup_io_ctrl_reg |=
                            ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO3MODE_INDEX);
                    }
                    else
                    {
                        /* Wake-up I/O is configured as pull-down */
                        wakeup_io_ctrl_reg |=
                            ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO3MODE_INDEX);
                    }
                }
            }
        }
    }
    else
    {
        wakeup_io_ctrl_reg |= ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO3MODE_INDEX);
    }

    return (wakeup_io_ctrl_reg);
}

/**
 * @brief
 * @param
 * @return
 */
static uint8_t CLOCK_u8OscCapConvert(uint8_t u8OscCap, uint8_t u8CapBankDiscontinuity)
{
    /* Compensate for discontinuity in the capacitor banks */
    if (u8OscCap < 64U)
    {
        if (u8OscCap >= u8CapBankDiscontinuity)
        {
            u8OscCap -= u8CapBankDiscontinuity;
        }
        else
        {
            u8OscCap = 0U;
        }
    }
    else
    {
        if (u8OscCap <= (127U - u8CapBankDiscontinuity))
        {
            u8OscCap += u8CapBankDiscontinuity;
        }
        else
        {
            u8OscCap = 127U;
        }
    }
    return u8OscCap;
}

/**
 * @brief             Described in fsl_common.h
 * @param
 * @return
 */
static void lowpower_set_system_voltage(uint32_t system_voltage_mv)
{
    /*
     * Set system voltage
     */
    uint32_t lv_ldo_ao       = (uint32_t)V_AO_1P100;   /* <ldo_ao> */
    uint32_t lv_ldo_ao_boost = (uint32_t)V_AO_1P150;   /* <ldo_ao_boost> */
    uint32_t lv_dcdc         = (uint32_t)V_DCDC_1P100; /* <dcdc> */

    /*
     * Because DCDC has less code than LD_AO, we first determine the
     * optimum DCDC settings, then we find the closest possible settings
     * for LD_AO, knowing that we want both settings to be as close as possible
     * (ideally, they shall be equal).
     */

    if (system_voltage_mv <= 950UL)
    {
        lv_dcdc         = (uint32_t)V_DCDC_0P950;
        lv_ldo_ao       = (uint32_t)V_AO_0P960;
        lv_ldo_ao_boost = (uint32_t)V_AO_1P010;
    }
    else if (system_voltage_mv <= 975UL)
    {
        lv_dcdc         = (uint32_t)V_DCDC_0P975;
        lv_ldo_ao       = (uint32_t)V_AO_0P980;
        lv_ldo_ao_boost = (uint32_t)V_AO_1P030;
    }
    else if (system_voltage_mv <= 1000UL)
    {
        lv_dcdc         = (uint32_t)V_DCDC_1P000;
        lv_ldo_ao       = (uint32_t)V_AO_1P000;
        lv_ldo_ao_boost = (uint32_t)V_AO_1P050;
    }
    else if (system_voltage_mv <= 1025UL)
    {
        lv_dcdc         = (uint32_t)V_DCDC_1P025;
        lv_ldo_ao       = (uint32_t)V_AO_1P030;
        lv_ldo_ao_boost = (uint32_t)V_AO_1P080;
    }
    else if (system_voltage_mv <= 1050UL)
    {
        lv_dcdc         = (uint32_t)V_DCDC_1P050;
        lv_ldo_ao       = (uint32_t)V_AO_1P060;
        lv_ldo_ao_boost = (uint32_t)V_AO_1P110;
    }
    else if (system_voltage_mv <= 1075UL)
    {
        lv_dcdc         = (uint32_t)V_DCDC_1P075;
        lv_ldo_ao       = (uint32_t)V_AO_1P080;
        lv_ldo_ao_boost = (uint32_t)V_AO_1P130;
    }
    else if (system_voltage_mv <= 1100UL)
    {
        lv_dcdc         = (uint32_t)V_DCDC_1P100;
        lv_ldo_ao       = (uint32_t)V_AO_1P100;
        lv_ldo_ao_boost = (uint32_t)V_AO_1P150;
    }
    else if (system_voltage_mv <= 1125UL)
    {
        lv_dcdc         = (uint32_t)V_DCDC_1P125;
        lv_ldo_ao       = (uint32_t)V_AO_1P130;
        lv_ldo_ao_boost = (uint32_t)V_AO_1P160;
    }
    else if (system_voltage_mv <= 1150UL)
    {
        lv_dcdc         = (uint32_t)V_DCDC_1P150;
        lv_ldo_ao       = (uint32_t)V_AO_1P160;
        lv_ldo_ao_boost = (uint32_t)V_AO_1P220;
    }
    else if (system_voltage_mv <= 1175UL)
    {
        lv_dcdc         = (uint32_t)V_DCDC_1P175;
        lv_ldo_ao       = (uint32_t)V_AO_1P160;
        lv_ldo_ao_boost = (uint32_t)V_AO_1P220;
    }
    else
    {
        lv_dcdc         = (uint32_t)V_DCDC_1P200;
        lv_ldo_ao       = (uint32_t)V_AO_1P160;
        lv_ldo_ao_boost = (uint32_t)V_AO_1P220;
    }

    /* Set up LDO Always-On voltages */
    PMC->LDOPMU = (PMC->LDOPMU & (~PMC_LDOPMU_VADJ_MASK) & (~PMC_LDOPMU_VADJ_BOOST_MASK)) | PMC_LDOPMU_VADJ(lv_ldo_ao) |
                  PMC_LDOPMU_VADJ_BOOST(lv_ldo_ao_boost);

    /* Set up DCDC voltage */
    PMC->DCDC0 = (PMC->DCDC0 & (~PMC_DCDC0_VOUT_MASK)) | PMC_DCDC0_VOUT(lv_dcdc);
}

/**
 * @brief	Described in fsl_common.h
 * @param
 * @return
 */
static void lowpower_set_dcdc_power_profile(lowpower_dcdc_power_profile_enum dcdc_power_profile)
{
#define FLASH_NMPA_DCDC_POWER_PROFILE_LOW_0_ADDRS    (FLASH_NMPA_BASE + 0xE0U) // N4M (0x3FCE0U) / N4S (0x9FCE0U)
#define FLASH_NMPA_DCDC_POWER_PROFILE_LOW_1_ADDRS    (FLASH_NMPA_BASE + 0xE4U) // N4M (0x3FCE4U) / N4S (0x9FCE4U)
#define FLASH_NMPA_DCDC_POWER_PROFILE_MEDIUM_0_ADDRS (FLASH_NMPA_BASE + 0xE8U) // N4M (0x3FCE8U) / N4S (0x9FCE8U)
#define FLASH_NMPA_DCDC_POWER_PROFILE_MEDIUM_1_ADDRS (FLASH_NMPA_BASE + 0xECU) // N4M (0x3FCECU) / N4S (0x9FCECU)
#define FLASH_NMPA_DCDC_POWER_PROFILE_HIGH_0_ADDRS   (FLASH_NMPA_BASE + 0xD8U) // N4M (0x3FCD8U) / N4S (0x9FCD8U)
#define FLASH_NMPA_DCDC_POWER_PROFILE_HIGH_1_ADDRS   (FLASH_NMPA_BASE + 0xDCU) // N4M (0x3FCDCU) / N4S (0x9FCDCU)

    const uint32_t PMC_DCDC0_DEFAULT = 0x010C4E68;
    const uint32_t PMC_DCDC1_DEFAULT = 0x01803A98;

    uint32_t dcdcTrimValue0;
    uint32_t dcdcTrimValue1;

    switch (dcdc_power_profile)
    {
        case DCDC_POWER_PROFILE_LOW:
            /* Low */
            dcdcTrimValue0 = (*((volatile unsigned int *)(FLASH_NMPA_DCDC_POWER_PROFILE_LOW_0_ADDRS)));
            dcdcTrimValue1 = (*((volatile unsigned int *)(FLASH_NMPA_DCDC_POWER_PROFILE_LOW_1_ADDRS)));

            if (0UL != (dcdcTrimValue0 & 0x1UL))
            {
                dcdcTrimValue0 = dcdcTrimValue0 >> 1;

                PMC->DCDC0 = dcdcTrimValue0;
                PMC->DCDC1 = dcdcTrimValue1;
#if (defined(NIOBE_DEBUG_LEVEL) && (NIOBE_DEBUG_LEVEL >= 1))
                PRINTF(
                    "\nINFO : DCDC Power Profile set to "
                    "LOW"
                    "\n");
#endif
            }
            break;

        case DCDC_POWER_PROFILE_MEDIUM:
            /* Medium */
            dcdcTrimValue0 = (*((volatile unsigned int *)(FLASH_NMPA_DCDC_POWER_PROFILE_MEDIUM_0_ADDRS)));
            dcdcTrimValue1 = (*((volatile unsigned int *)(FLASH_NMPA_DCDC_POWER_PROFILE_MEDIUM_1_ADDRS)));

            if (0UL != (dcdcTrimValue0 & 0x1UL))
            {
                dcdcTrimValue0 = dcdcTrimValue0 >> 1;

                PMC->DCDC0 = dcdcTrimValue0;
                PMC->DCDC1 = dcdcTrimValue1;
#if (defined(NIOBE_DEBUG_LEVEL) && (NIOBE_DEBUG_LEVEL >= 1))
                PRINTF(
                    "\nINFO : DCDC Power Profile set to "
                    "MEDIUM"
                    "\n");
#endif
            }
            break;

        case DCDC_POWER_PROFILE_HIGH:
            /* High */
            dcdcTrimValue0 = (*((volatile unsigned int *)(FLASH_NMPA_DCDC_POWER_PROFILE_HIGH_0_ADDRS)));
            dcdcTrimValue1 = (*((volatile unsigned int *)(FLASH_NMPA_DCDC_POWER_PROFILE_HIGH_1_ADDRS)));

            if (0UL != (dcdcTrimValue0 & 0x1UL))
            {
                dcdcTrimValue0 = dcdcTrimValue0 >> 1;

                PMC->DCDC0 = dcdcTrimValue0;
                PMC->DCDC1 = dcdcTrimValue1;
#if (defined(NIOBE_DEBUG_LEVEL) && (NIOBE_DEBUG_LEVEL >= 1))
                PRINTF(
                    "\nINFO : DCDC Power Profile set to "
                    "HIGH"
                    "\n");
#endif
            }
            break;

        default:
            /* Low */
            PMC->DCDC0 = PMC_DCDC0_DEFAULT;
            PMC->DCDC1 = PMC_DCDC1_DEFAULT;
#if (defined(NIOBE_DEBUG_LEVEL) && (NIOBE_DEBUG_LEVEL >= 1))
            PRINTF(
                "\nINFO : DCDC Power Profile set to "
                "LOW"
                "\n");
#endif
            break;
    }
}

/**
 * @brief
 * @param
 * @return
 */
static lowpower_process_corner_enum lowpower_get_part_process_corner(void)
{
#define FLASH_NMPA_PVT_MONITOR_0_RINGO_ADDRS (FLASH_NMPA_BASE + 0x130U) // N4M=>0x3FD30U / N4S=>0x9FD30U
#define FLASH_NMPA_PVT_MONITOR_1_RINGO_ADDRS (FLASH_NMPA_BASE + 0x140U) // N4M=>0x3FD40U / N4S=>0x9FD40U

    lowpower_process_corner_enum part_process_corner;
    uint32_t pvt_ringo_hz;
    uint32_t pvt_ringo_0 = (*((volatile unsigned int *)(FLASH_NMPA_PVT_MONITOR_0_RINGO_ADDRS)));
    uint32_t pvt_ringo_1 = (*((volatile unsigned int *)(FLASH_NMPA_PVT_MONITOR_1_RINGO_ADDRS)));

    /*
     * Check that the PVT Monitors Trimmings in flash are valid.
     * Note : On Customer Samples, PVT Trimmings in flash will ALWAYS be valid,
     *      so that in the SDK, the check below could be skipped (but NOT the right shift operation)
     */
    if (0UL != (pvt_ringo_0 & 0x1UL))
    {
        /* PVT Trimmings in Flash are valid */
        pvt_ringo_0 = pvt_ringo_0 >> 1;
    }
    else
    {
        /* PVT Trimmings in Flash are NOT valid (average value assumed) */
        pvt_ringo_0 = PROCESS_NNN_AVG_HZ;
    }

    if (0UL != (pvt_ringo_1 & 0x1UL))
    {
        /* PVT Trimmings in Flash are valid */
        pvt_ringo_1 = pvt_ringo_1 >> 1;
    }
    else
    {
        /* PVT Trimmings in Flash are NOT valid (average value assumed) */
        pvt_ringo_1 = PROCESS_NNN_AVG_HZ;
    }

    /*
     * There are 2 Ring Oscillators in the System.
     * We consider the worst case scenario by choosing
     * the minimum of the 2 Ring Oscillators values
     * as the final value to determine the Process Corner.
     */
    if (pvt_ringo_1 <= pvt_ringo_0)
    {
        pvt_ringo_hz = pvt_ringo_1;
    }
    else
    {
        pvt_ringo_hz = pvt_ringo_0;
    }

    /*
     * Determine the process corner based on the value of the Ring Oscillator frequency
     */
    if (pvt_ringo_hz <= PROCESS_NNN_MIN_HZ)
    {
        /* SSS Process Corner */
        part_process_corner = PROCESS_CORNER_SSS;
#if (defined(NIOBE_DEBUG_LEVEL) && (NIOBE_DEBUG_LEVEL >= 1))
        PRINTF(
            "\nINFO : Process Corner : "
            "SSS"
            "\n");
#endif
    }
    else
    {
        if (pvt_ringo_hz <= PROCESS_NNN_MAX_HZ)
        {
            /* NNN Process Corner */
            part_process_corner = PROCESS_CORNER_NNN;
#if (defined(NIOBE_DEBUG_LEVEL) && (NIOBE_DEBUG_LEVEL >= 1))
            PRINTF(
                "\nINFO : Process Corner : "
                "NNN"
                "\n");
#endif
        }
        else
        {
            /* FFF Process Corner */
            part_process_corner = PROCESS_CORNER_FFF;
#if (defined(NIOBE_DEBUG_LEVEL) && (NIOBE_DEBUG_LEVEL >= 1))
            PRINTF(
                "\nINFO : Process Corner : "
                "FFF"
                "\n");
#endif
        }
    }

    return (part_process_corner);
}

/**
 * @brief	Described in fsl_common.h
 * @param
 * @return
 */
static void lowpower_set_voltage_for_process(lowpower_dcdc_power_profile_enum dcdc_power_profile)
{
    /* Get Sample Process Corner */
    lowpower_process_corner_enum part_process_corner = lowpower_get_part_process_corner();

    switch (part_process_corner)
    {
        case PROCESS_CORNER_SSS:
            /* Slow Corner */
            {
                switch (dcdc_power_profile)
                {
                    case DCDC_POWER_PROFILE_MEDIUM:
                        /* Medium */
                        lowpower_set_system_voltage(VOLTAGE_SSS_MED_MV);
                        break;

                    case DCDC_POWER_PROFILE_HIGH:
                        /* High */
                        lowpower_set_system_voltage(VOLTAGE_SSS_HIG_MV);
                        break;

                    default:
                        /* DCDC_POWER_PROFILE_LOW */
                        lowpower_set_system_voltage(VOLTAGE_SSS_LOW_MV);
                        break;
                } // switch(dcdc_power_profile)
            }
            break;

        case PROCESS_CORNER_FFF:
            /* Fast Corner */
            {
                switch (dcdc_power_profile)
                {
                    case DCDC_POWER_PROFILE_MEDIUM:
                        /* Medium */
                        lowpower_set_system_voltage(VOLTAGE_FFF_MED_MV);
                        break;

                    case DCDC_POWER_PROFILE_HIGH:
                        /* High */
                        lowpower_set_system_voltage(VOLTAGE_FFF_HIG_MV);
                        break;

                    default:
                        /* DCDC_POWER_PROFILE_LOW */
                        lowpower_set_system_voltage(VOLTAGE_FFF_LOW_MV);
                        break;
                } // switch(dcdc_power_profile)
            }
            break;

        default:
            /* Nominal (NNN) and all others Process Corners : assume Nominal Corner */
            {
                switch (dcdc_power_profile)
                {
                    case DCDC_POWER_PROFILE_MEDIUM:
                        /* Medium */
                        lowpower_set_system_voltage(VOLTAGE_NNN_MED_MV);
                        break;

                    case DCDC_POWER_PROFILE_HIGH:
                        /* High */
                        lowpower_set_system_voltage(VOLTAGE_NNN_HIG_MV);
                        break;

                    default:
                        /* DCDC_POWER_PROFILE_LOW */
                        lowpower_set_system_voltage(VOLTAGE_NNN_LOW_MV);
                        break;
                } // switch(dcdc_power_profile)
                break;
            }
    } // switch(part_process_corner)
}

/**
 * @brief             Described in fsl_common.h
 * @param
 * @return
 */
void POWER_SetVoltageForFreq(uint32_t system_freq_hz)
{
    if (system_freq_hz <= DCDC_POWER_PROFILE_LOW_MAX_FREQ_HZ)
    {
        /* [0 Hz - DCDC_POWER_PROFILE_LOW_MAX_FREQ_HZ Hz] */
        lowpower_set_dcdc_power_profile(DCDC_POWER_PROFILE_LOW); /* DCDC VOUT = 1.05 V by default */
        lowpower_set_voltage_for_process(DCDC_POWER_PROFILE_LOW);
    }
    else
    {
        if (system_freq_hz <= DCDC_POWER_PROFILE_MEDIUM_MAX_FREQ_HZ)
        {
            /* ]DCDC_POWER_PROFILE_LOW_MAX_FREQ_HZ Hz - DCDC_POWER_PROFILE_MEDIUM_MAX_FREQ_HZ Hz] */
            lowpower_set_dcdc_power_profile(DCDC_POWER_PROFILE_MEDIUM); /* DCDC VOUT = 1.15 V by default */
            lowpower_set_voltage_for_process(DCDC_POWER_PROFILE_MEDIUM);
        }
        else
        {
            /* > DCDC_POWER_PROFILE_MEDIUM_MAX_FREQ_HZ Hz */
            lowpower_set_dcdc_power_profile(DCDC_POWER_PROFILE_HIGH); /* DCDC VOUT = 1.2 V by default */
            lowpower_set_voltage_for_process(DCDC_POWER_PROFILE_HIGH);
        }
    }
}

void POWER_Xtal16mhzCapabankTrim(int32_t pi32_16MfXtalIecLoadpF_x100,
                                 int32_t pi32_16MfXtalPPcbParCappF_x100,
                                 int32_t pi32_16MfXtalNPcbParCappF_x100)
{
    uint32_t u32XOTrimValue;
    uint8_t u8IECXinCapCal6pF, u8IECXinCapCal8pF, u8IECXoutCapCal6pF, u8IECXoutCapCal8pF, u8XOSlave;
    int32_t iaXin_x4, ibXin, iaXout_x4, ibXout;
    int32_t iXOCapInpF_x100, iXOCapOutpF_x100;
    uint8_t u8XOCapInCtrl, u8XOCapOutCtrl;
    uint32_t u32RegVal;
    int32_t i32Tmp;

    /* Enable and set LDO, if not already done */
    POWER_SetXtal16mhzLdo();
    /* Get Cal values from Flash */
    u32XOTrimValue = GET_16MXO_TRIM();
    /* Check validity and apply */
    if ((0UL != (u32XOTrimValue & 1UL)) && (0UL != ((u32XOTrimValue >> 15UL) & 1UL)))
    {
        /* These fields are 7 bits, unsigned */
        u8IECXinCapCal6pF  = (uint8_t)((u32XOTrimValue >> 1UL) & 0x7fUL);
        u8IECXinCapCal8pF  = (uint8_t)((u32XOTrimValue >> 8UL) & 0x7fUL);
        u8IECXoutCapCal6pF = (uint8_t)((u32XOTrimValue >> 16UL) & 0x7fUL);
        u8IECXoutCapCal8pF = (uint8_t)((u32XOTrimValue >> 23UL) & 0x7fUL);
        /* This field is 1 bit */
        u8XOSlave = (uint8_t)((u32XOTrimValue >> 30UL) & 0x1UL);
        /* Linear fit coefficients calculation */
        iaXin_x4  = (int)u8IECXinCapCal8pF - (int)u8IECXinCapCal6pF;
        ibXin     = (int)u8IECXinCapCal6pF - iaXin_x4 * 3;
        iaXout_x4 = (int)u8IECXoutCapCal8pF - (int)u8IECXoutCapCal6pF;
        ibXout    = (int)u8IECXoutCapCal6pF - iaXout_x4 * 3;
    }
    else
    {
        iaXin_x4  = 20;  // gain in LSB/pF
        ibXin     = -9;  // offset in LSB
        iaXout_x4 = 20;  // gain in LSB/pF
        ibXout    = -13; // offset in LSB
        u8XOSlave = 0;
    }
    /* In & out load cap calculation with derating */
    iXOCapInpF_x100 = 2 * pi32_16MfXtalIecLoadpF_x100 - pi32_16MfXtalNPcbParCappF_x100 +
                      39 * ((int32_t)XO_SLAVE_EN - (int32_t)u8XOSlave) - 15;
    iXOCapOutpF_x100 = 2 * pi32_16MfXtalIecLoadpF_x100 - pi32_16MfXtalPPcbParCappF_x100 - 21;
    /* In & out XO_OSC_CAP_Code_CTRL calculation, with rounding */
    i32Tmp         = ((iXOCapInpF_x100 * iaXin_x4 + ibXin * 400) + 200) / 400;
    u8XOCapInCtrl  = (uint8_t)i32Tmp;
    i32Tmp         = ((iXOCapOutpF_x100 * iaXout_x4 + ibXout * 400) + 200) / 400;
    u8XOCapOutCtrl = (uint8_t)i32Tmp;
    /* Read register and clear fields to be written */
    u32RegVal = ANACTRL->XO32M_CTRL;
    u32RegVal &= ~(ANACTRL_XO32M_CTRL_OSC_CAP_IN_MASK | ANACTRL_XO32M_CTRL_OSC_CAP_OUT_MASK);
    /* Configuration of 32 MHz XO output buffers */
#if (XO_SLAVE_EN == 0)
    u32RegVal &= ~(ANACTRL_XO32M_CTRL_SLAVE_MASK | ANACTRL_XO32M_CTRL_ACBUF_PASS_ENABLE_MASK);
#else
    u32RegVal |= ANACTRL_XO32M_CTRL_SLAVE_MASK | ANACTRL_XO32M_CTRL_ACBUF_PASS_ENABLE_MASK;
#endif
    /* XO_OSC_CAP_Code_CTRL to XO_OSC_CAP_Code conversion */
    u32RegVal |= (uint32_t)CLOCK_u8OscCapConvert(u8XOCapInCtrl, 13) << ANACTRL_XO32M_CTRL_OSC_CAP_IN_SHIFT;
    u32RegVal |= (uint32_t)CLOCK_u8OscCapConvert(u8XOCapOutCtrl, 13) << ANACTRL_XO32M_CTRL_OSC_CAP_OUT_SHIFT;
    /* Write back to register */
    ANACTRL->XO32M_CTRL = u32RegVal;
}

void POWER_Xtal32khzCapabankTrim(int32_t pi32_32kfXtalIecLoadpF_x100,
                                 int32_t pi32_32kfXtalPPcbParCappF_x100,
                                 int32_t pi32_32kfXtalNPcbParCappF_x100)
{
    uint32_t u32XOTrimValue;
    uint8_t u8IECXinCapCal6pF, u8IECXinCapCal8pF, u8IECXoutCapCal6pF, u8IECXoutCapCal8pF;
    int32_t iaXin_x4, ibXin, iaXout_x4, ibXout;
    int32_t iXOCapInpF_x100, iXOCapOutpF_x100;
    uint8_t u8XOCapInCtrl, u8XOCapOutCtrl;
    uint32_t u32RegVal;
    int32_t i32Tmp;
    /* Get Cal values from Flash */
    u32XOTrimValue = GET_32KXO_TRIM();
    /* check validity and apply */
    if ((0UL != (u32XOTrimValue & 1UL)) && (0UL != ((u32XOTrimValue >> 15UL) & 1UL)))
    {
        /* These fields are 7 bits, unsigned */
        u8IECXinCapCal6pF  = (uint8_t)((u32XOTrimValue >> 1UL) & 0x7fUL);
        u8IECXinCapCal8pF  = (uint8_t)((u32XOTrimValue >> 8UL) & 0x7fUL);
        u8IECXoutCapCal6pF = (uint8_t)((u32XOTrimValue >> 16UL) & 0x7fUL);
        u8IECXoutCapCal8pF = (uint8_t)((u32XOTrimValue >> 23UL) & 0x7fUL);
        /* Linear fit coefficients calculation */
        iaXin_x4  = (int)u8IECXinCapCal8pF - (int)u8IECXinCapCal6pF;
        ibXin     = (int)u8IECXinCapCal6pF - iaXin_x4 * 3;
        iaXout_x4 = (int)u8IECXoutCapCal8pF - (int)u8IECXoutCapCal6pF;
        ibXout    = (int)u8IECXoutCapCal6pF - iaXout_x4 * 3;
    }
    else
    {
        iaXin_x4  = 16; // gain in LSB/pF
        ibXin     = 12; // offset in LSB
        iaXout_x4 = 16; // gain in LSB/pF
        ibXout    = 11; // offset in LSB
    }

    /* In & out load cap calculation with derating */
    iXOCapInpF_x100  = 2 * pi32_32kfXtalIecLoadpF_x100 - pi32_32kfXtalNPcbParCappF_x100 - 130;
    iXOCapOutpF_x100 = 2 * pi32_32kfXtalIecLoadpF_x100 - pi32_32kfXtalPPcbParCappF_x100 - 41;

    /* In & out XO_OSC_CAP_Code_CTRL calculation, with rounding */
    i32Tmp         = ((iXOCapInpF_x100 * iaXin_x4 + ibXin * 400) + 200) / 400;
    u8XOCapInCtrl  = (uint8_t)i32Tmp;
    i32Tmp         = ((iXOCapOutpF_x100 * iaXout_x4 + ibXout * 400) + 200) / 400;
    u8XOCapOutCtrl = (uint8_t)i32Tmp;

    /* Read register and clear fields to be written */
    u32RegVal = PMC->XTAL32K;
    u32RegVal &= ~(PMC_XTAL32K_CAPBANKIN_MASK | PMC_XTAL32K_CAPBANKOUT_MASK);

    /* XO_OSC_CAP_Code_CTRL to XO_OSC_CAP_Code conversion */
    u32RegVal |= (uint32_t)CLOCK_u8OscCapConvert(u8XOCapInCtrl, 23) << PMC_XTAL32K_CAPBANKIN_SHIFT;
    u32RegVal |= (uint32_t)CLOCK_u8OscCapConvert(u8XOCapOutCtrl, 23) << PMC_XTAL32K_CAPBANKOUT_SHIFT;

    /* Write back to register */
    PMC->XTAL32K = u32RegVal;
}

void POWER_SetXtal16mhzLdo(void)
{
    uint32_t temp;
    const uint32_t u32Mask =
        (ANACTRL_LDO_XO32M_VOUT_MASK | ANACTRL_LDO_XO32M_IBIAS_MASK | ANACTRL_LDO_XO32M_STABMODE_MASK);

    const uint32_t u32Value =
        (ANACTRL_LDO_XO32M_VOUT(0x5) | ANACTRL_LDO_XO32M_IBIAS(0x2) | ANACTRL_LDO_XO32M_STABMODE(0x1));

    /* Enable & set-up XTAL 32 MHz clock LDO */
    temp = ANACTRL->LDO_XO32M;

    if ((temp & u32Mask) != u32Value)
    {
        temp &= ~u32Mask;

        /*
         * Enable the XTAL32M LDO
         * Adjust the output voltage level, 0x5 for 1.1V
         * Adjust the biasing current, 0x2 value
         * Stability configuration, 0x1 default mode
         */
        temp |= u32Value;

        ANACTRL->LDO_XO32M = temp;

        /* Delay for LDO to be up */
        // CLOCK_uDelay(20);
    }

    /* Enable LDO XO32M */
    PMC->PDRUNCFGCLR0 = PMC_PDRUNCFG0_PDEN_LDOXO32M_MASK;
}

void POWER_SetXtal16mhzTrim(uint32_t amp, uint32_t gm)
{
    uint32_t temp;
    const uint32_t u32Mask  = (ANACTRL_XO32M_CTRL_AMP_MASK | ANACTRL_XO32M_CTRL_GM_MASK);
    const uint32_t u32Value = (ANACTRL_XO32M_CTRL_AMP(amp) | ANACTRL_XO32M_CTRL_GM(gm));

    /* Set-up XTAL 16-MHz Trimmings */
    temp = ANACTRL->XO32M_CTRL;
    temp &= ~u32Mask;
    temp |= u32Value;
    ANACTRL->XO32M_CTRL = temp;
}

/*!
 * @brief set BOD VBAT level.
 *
 * @param level BOD detect level
 * @param hyst BoD Hysteresis control
 * @param enBodVbatReset VBAT brown out detect reset
 */
void POWER_SetBodVbatLevel(power_bod_vbat_level_t level, power_bod_hyst_t hyst, bool enBodVbatReset)
{
    uint32_t pmc_reset_ctrl;
    /* BOD VBAT disable reset */
    pmc_reset_ctrl =
        PMC->RESETCTRL & (~(PMC_RESETCTRL_BODVBATRESETENA_SECURE_MASK | PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_MASK));
    pmc_reset_ctrl |= (0x2UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_SHIFT) |
                      (0x2UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_SHIFT);

    PMC->RESETCTRL = pmc_reset_ctrl;

    PMC->BODVBAT = (PMC->BODVBAT & (~(PMC_BODVBAT_TRIGLVL_MASK | PMC_BODVBAT_HYST_MASK))) | PMC_BODVBAT_TRIGLVL(level) |
                   PMC_BODVBAT_HYST(hyst);

    /* BOD VBAT enable reset */
    if ((uint32_t)enBodVbatReset == 1UL)
    {
        pmc_reset_ctrl &= (~(PMC_RESETCTRL_BODVBATRESETENA_SECURE_MASK | PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_MASK));
        pmc_reset_ctrl |= (0x1UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_SHIFT) |
                          (0x1UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_SHIFT);
        PMC->RESETCTRL = pmc_reset_ctrl;
    }
}

#if defined(PMC_BODCORE_TRIGLVL_MASK)
/*!
 * @brief set BOD core level.
 *
 * @param level BOD detect level
 * @param hyst BoD Hysteresis control
 * @param enBodCoreReset core brown out detect reset
 */
void POWER_SetBodCoreLevel(power_bod_core_level_t level, power_bod_hyst_t hyst, bool enBodCoreReset)
{
    uint32_t pmc_reset_ctrl;
    /* BOD CORE disable reset */
    pmc_reset_ctrl =
        PMC->RESETCTRL & (~(PMC_RESETCTRL_BODCORERESETENA_SECURE_MASK | PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_MASK));
    pmc_reset_ctrl |=
        (0x2 << PMC_RESETCTRL_BODCORERESETENA_SECURE_SHIFT) | (0x2 << PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_SHIFT);

    PMC->RESETCTRL = pmc_reset_ctrl;

    PMC->BODCORE = (PMC->BODCORE & (~(PMC_BODCORE_TRIGLVL_MASK | PMC_BODCORE_HYST_MASK))) | PMC_BODCORE_TRIGLVL(level) |
                   PMC_BODCORE_HYST(hyst);

    /* BOD CORE enable reset */
    if (enBodCoreReset == 1)
    {
        pmc_reset_ctrl &= (~(PMC_RESETCTRL_BODCORERESETENA_SECURE_MASK | PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_MASK));
        pmc_reset_ctrl |= (0x1 << PMC_RESETCTRL_BODCORERESETENA_SECURE_SHIFT) |
                          (0x1 << PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_SHIFT);
        PMC->RESETCTRL = pmc_reset_ctrl;
    }
}
#endif

/**
 * @brief   Return some key information related to the device reset causes / wake-up sources, for all power modes.
 * @param   p_reset_cause   : the device reset cause, according to the definition of power_device_reset_cause_t type.
 * @param   p_boot_mode     : the device boot mode, according to the definition of power_device_boot_mode_t type.
 * @param   p_wakeupio_cause: the wake-up pin sources, according to the definition of register PMC->WAKEIOCAUSE[3:0].

 * @return  Nothing
 *
 *         !!!  IMPORTANT ERRATA - IMPORTANT ERRATA - IMPORTANT ERRATA     !!!
 *         !!!   valid ONLY for LPC55S69 (not for LPC55S16 and LPC55S06)   !!!
 *         !!!   when FALLING EDGE DETECTION is enabled on wake-up pins:   !!!
 *         - 1. p_wakeupio_cause is NOT ACCURATE
 *         - 2. Spurious kRESET_CAUSE_DPDRESET_WAKEUPIO* event is reported when
 *              several wake-up sources are enabled during DEEP-POWER-DOWN
 *              (like enabling wake-up on RTC and Falling edge wake-up pins)
 *
 */
void POWER_GetWakeUpCause(power_device_reset_cause_t *p_reset_cause,
                          power_device_boot_mode_t *p_boot_mode,
                          uint32_t *p_wakeupio_cause)
{
    uint32_t reset_cause_reg;
    uint32_t boot_mode_reg;

#if (defined(LPC55S06_SERIES) || defined(LPC55S04_SERIES) || defined(LPC5506_SERIES) || defined(LPC5504_SERIES) || \
     defined(LPC5502_SERIES) || defined(LPC55S16_SERIES) || defined(LPC55S14_SERIES) || defined(LPC5516_SERIES) || \
     defined(LPC5514_SERIES) || defined(LPC5512_SERIES))
    reset_cause_reg = (PMC->AOREG1) & 0x3FF0UL;
#else /* LPC55S69/28 */
    reset_cause_reg = (PMC->AOREG1) & 0x1FF0UL;
#endif

    /*
     * Prioritize interrupts source with respect to their critical level
     */
#if (defined(LPC55S06_SERIES) || defined(LPC55S04_SERIES) || defined(LPC5506_SERIES) || defined(LPC5504_SERIES) || \
     defined(LPC5502_SERIES) || defined(LPC55S16_SERIES) || defined(LPC55S14_SERIES) || defined(LPC5516_SERIES) || \
     defined(LPC5514_SERIES) || defined(LPC5512_SERIES))
    if (0UL != (reset_cause_reg & PMC_AOREG1_CDOGRESET_MASK))
    { /* Code Watchdog Reset */
        *p_reset_cause    = kRESET_CAUSE_CDOGRESET;
        *p_boot_mode      = kBOOT_MODE_POWER_UP;
        *p_wakeupio_cause = 0; /* Device has not been waked-up by any wake-up pins */
    }
    else
#endif
    {
        if (0UL != (reset_cause_reg & PMC_AOREG1_WDTRESET_MASK))
        { /* Watchdog Timer Reset */
            *p_reset_cause    = kRESET_CAUSE_WDTRESET;
            *p_boot_mode      = kBOOT_MODE_POWER_UP;
            *p_wakeupio_cause = 0; /* Device has not been waked-up by any wake-up pins */
        }
        else
        {
            if (0UL != (reset_cause_reg & PMC_AOREG1_SYSTEMRESET_MASK))
            { /* ARM System Reset */
                *p_reset_cause    = kRESET_CAUSE_ARMSYSTEMRESET;
                *p_boot_mode      = kBOOT_MODE_POWER_UP;
                *p_wakeupio_cause = 0; /* Device has not been waked-up by any wake-up pins */
            }
            else
            {
                boot_mode_reg = (PMC->STATUS & PMC_STATUS_BOOTMODE_MASK) >> PMC_STATUS_BOOTMODE_SHIFT;

                if (boot_mode_reg == 0UL) /* POWER-UP: Power On Reset, Pin reset, Brown Out Detectors, Software Reset */
                {
                    *p_boot_mode      = kBOOT_MODE_POWER_UP; /* All non wake-up from a Low Power mode */
                    *p_wakeupio_cause = 0;                   /* Device has not been waked-up by any wake-up pins */

                    /*
                     * Prioritise Reset causes, starting from the strongest (Power On Reset)
                     */
                    if (0UL != (reset_cause_reg & PMC_AOREG1_POR_MASK))
                    { /* Power On Reset */
                        *p_reset_cause = kRESET_CAUSE_POR;
                    }
                    else
                    {
                        if (0UL != (reset_cause_reg & PMC_AOREG1_BODRESET_MASK))
                        { /* Brown-out Detector reset (either BODVBAT or BODCORE) */
                            *p_reset_cause = kRESET_CAUSE_BODRESET;
                        }
                        else
                        {
                            if (0UL != (reset_cause_reg & PMC_AOREG1_PADRESET_MASK))
                            { /* Hardware Pin Reset */
                                *p_reset_cause = kRESET_CAUSE_PADRESET;
                            }
                            else
                            {
                                if (0UL != (reset_cause_reg & PMC_AOREG1_SWRRESET_MASK))
                                { /* Software triggered Reset */
                                    *p_reset_cause = kRESET_CAUSE_SWRRESET;
                                }
                                else
                                { /* Unknown Reset Cause */
                                    *p_reset_cause = kRESET_CAUSE_NOT_DETERMINISTIC;
                                }
                            }
                        }
                    }

#if (defined(LPC55S06_SERIES) || defined(LPC55S04_SERIES) || defined(LPC5506_SERIES) || defined(LPC5504_SERIES) || \
     defined(LPC5502_SERIES) || defined(LPC55S16_SERIES) || defined(LPC55S14_SERIES) || defined(LPC5516_SERIES) || \
     defined(LPC5514_SERIES) || defined(LPC5512_SERIES))
                    /* Transfer the control of the 4 wake-up pins to IOCON (instead of the Power Management Controller
                     */
                    PMC->WAKEUPIOCTRL = PMC->WAKEUPIOCTRL & (~PMC_WAKEUPIOCTRL_WAKEUPIO_ENABLE_CTRL_MASK);
#endif
                }
                else /* DEEP-SLEEP, POWER-DOWN and DEEP-POWER-DOWN */
                {
                    /*
                     * 1- First, save wakeup_io_cause register ...
                     */
                    *p_wakeupio_cause = PMC->WAKEIOCAUSE;

                    if (boot_mode_reg == 3UL) /* DEEP-POWER-DOWN */
                    {
                        *p_boot_mode = kBOOT_MODE_LP_DEEP_POWER_DOWN;

                        switch (((reset_cause_reg >> PMC_AOREG1_DPDRESET_WAKEUPIO_SHIFT) & 0x7UL))
                        {
                            case 1:
                                *p_reset_cause = kRESET_CAUSE_DPDRESET_WAKEUPIO;
                                break;
                            case 2:
                                *p_reset_cause = kRESET_CAUSE_DPDRESET_RTC;
                                break;
                            case 3:
                                *p_reset_cause = kRESET_CAUSE_DPDRESET_WAKEUPIO_RTC;
                                break;
                            case 4:
                                *p_reset_cause = kRESET_CAUSE_DPDRESET_OSTIMER;
                                break;
                            case 5:
                                *p_reset_cause = kRESET_CAUSE_DPDRESET_WAKEUPIO_OSTIMER;
                                break;
                            case 6:
                                *p_reset_cause = kRESET_CAUSE_DPDRESET_RTC_OSTIMER;
                                break;
                            case 7:
                                *p_reset_cause = kRESET_CAUSE_DPDRESET_WAKEUPIO_RTC_OSTIMER;
                                break;
                            default:
                                /* Unknown Reset Cause */
                                *p_reset_cause = kRESET_CAUSE_NOT_DETERMINISTIC;
                                break;
                        }

#if (defined(LPC55S06_SERIES) || defined(LPC55S04_SERIES) || defined(LPC5506_SERIES) || defined(LPC5504_SERIES) || \
     defined(LPC5502_SERIES) || defined(LPC55S16_SERIES) || defined(LPC55S14_SERIES) || defined(LPC5516_SERIES) || \
     defined(LPC5514_SERIES) || defined(LPC5512_SERIES))
                        /*
                         * 2- Next, transfer the control of the 4 wake-up pins
                         * to IOCON (instead of the Power Management Controller)
                         */
                        PMC->WAKEUPIOCTRL = PMC->WAKEUPIOCTRL & (~PMC_WAKEUPIOCTRL_WAKEUPIO_ENABLE_CTRL_MASK);
#endif
                    }
                    else /* DEEP-SLEEP and POWER-DOWN */
                    {
                        *p_reset_cause = kRESET_CAUSE_NOT_RELEVANT;

                        /*
                         * The control of the 4 wake-up pins is already in IOCON,
                         * so there is nothing special to do.
                         */

                        if (boot_mode_reg == 1UL) /* DEEP-SLEEP */
                        {
                            *p_boot_mode = kBOOT_MODE_LP_DEEP_SLEEP;
                        }
                        else /* POWER-DOWN */
                        {
                            *p_boot_mode = kBOOT_MODE_LP_POWER_DOWN;

                        } /* if ( boot_mode_reg == 1 ) DEEP-SLEEP */

                    } /* if ( boot_mode == 3 )  DEEP-POWER-DOWN */

                } /* if ( boot_mode == 0 )  POWER-UP */

            } /* if ( reset_cause_reg & PMC_AOREG1_CDOGRESET_MASK ) */

        } /* if ( reset_cause_reg & PMC_AOREG1_WDTRESET_MASK ) */

    } /* if ( reset_cause_reg & PMC_AOREG1_SYSTEMRESET_MASK ) */
}
