text: /******************************************************************************
  *  Filename:       sys_ctrl.c
  *  Revised:        2020-12-10 13:53:19 +0100 (Thu, 10 Dec 2020)
  *  Revision:       59832
  *
  *  Description:    Driver for the System Control.
  *
  *  Copyright (c) 2015 - 2020, Texas Instruments Incorporated
  *  All rights reserved.
  *
  *  Redistribution and use in source and binary forms, with or without
  *  modification, are permitted provided that the following conditions are met:
  *
  *  1) Redistributions of source code must retain the above copyright notice,
  *     this list of conditions and the following disclaimer.
  *
  *  2) Redistributions in binary form must reproduce the above copyright notice,
  *     this list of conditions and the following disclaimer in the documentation
  *     and/or other materials provided with the distribution.
  *
  *  3) Neither the name of the ORGANIZATION nor the names of its contributors may
  *     be used to endorse or promote products derived from this software without
  *     specific prior written permission.
  *
  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  *  POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************/
  
  // Hardware headers
  #include "../inc/hw_types.h"
  #include "../inc/hw_ccfg.h"
  #include "../inc/hw_ioc.h"
  // Driverlib headers
  #include "aon_batmon.h"
  #include "flash.h"
  #include "gpio.h"
  #include "setup_rom.h"
  #include "sys_ctrl.h"
  
  
  //*****************************************************************************
  //
  // Handle support for DriverLib in ROM:
  // This section will undo prototype renaming made in the header file
  //
  //*****************************************************************************
  #if !defined(DOXYGEN)
      #undef  SysCtrlIdle
      #define SysCtrlIdle                     NOROM_SysCtrlIdle
      #undef  SysCtrlShutdownWithAbort
      #define SysCtrlShutdownWithAbort        NOROM_SysCtrlShutdownWithAbort
      #undef  SysCtrlShutdown
      #define SysCtrlShutdown                 NOROM_SysCtrlShutdown
      #undef  SysCtrlStandby
      #define SysCtrlStandby                  NOROM_SysCtrlStandby
      #undef  SysCtrlSetRechargeBeforePowerDown
      #define SysCtrlSetRechargeBeforePowerDown NOROM_SysCtrlSetRechargeBeforePowerDown
      #undef  SysCtrlAdjustRechargeAfterPowerDown
      #define SysCtrlAdjustRechargeAfterPowerDown NOROM_SysCtrlAdjustRechargeAfterPowerDown
      #undef  SysCtrl_DCDC_VoltageConditionalControl
      #define SysCtrl_DCDC_VoltageConditionalControl NOROM_SysCtrl_DCDC_VoltageConditionalControl
      #undef  SysCtrlResetSourceGet
      #define SysCtrlResetSourceGet           NOROM_SysCtrlResetSourceGet
  #endif
  
  
  
  //*****************************************************************************
  //
  // Force the system in to idle mode
  //
  //*****************************************************************************
  void SysCtrlIdle(uint32_t vimsPdMode)
  {
      // Configure the VIMS mode
      HWREG(PRCM_BASE + PRCM_O_PDCTL1VIMS) = vimsPdMode;
  
      // Always keep cache retention ON in IDLE
      PRCMCacheRetentionEnable();
  
      // Turn off the CPU power domain, will take effect when PRCMDeepSleep() executes
      PRCMPowerDomainOff(PRCM_DOMAIN_CPU);
  
      // Ensure any possible outstanding AON writes complete
      SysCtrlAonSync();
  
      // Invoke deep sleep to go to IDLE
      PRCMDeepSleep();
  }
  
  //*****************************************************************************
  //
  // Try to enter shutdown but abort if wakeup event happened before shutdown
  //
  //*****************************************************************************
  void SysCtrlShutdownWithAbort(void)
  {
      uint32_t wu_detect_vector = 0;
      uint32_t io_num = 0;
  
      // For all IO CFG registers check if wakeup detect is enabled
      for(io_num = 0; io_num < 32; io_num++)
      {
          // Read MSB from WU_CFG bit field
          if( HWREG(IOC_BASE + IOC_O_IOCFG0 + (io_num * 4) ) & (1 << (IOC_IOCFG0_WU_CFG_S + IOC_IOCFG0_WU_CFG_W - 1)) )
          {
              wu_detect_vector |= (1 << io_num);
          }
      }
  
      // Wakeup events are detected when pads are in sleep mode
      PowerCtrlPadSleepEnable();
  
      // Make sure all potential events have propagated before checking event flags
      SysCtrlAonUpdate();
      SysCtrlAonUpdate();
  
      // If no edge detect flags for wakeup enabled IOs are set then shut down the device
      if( GPIO_getEventMultiDio(wu_detect_vector) == 0 )
      {
          SysCtrlShutdown();
      }
      else
      {
          PowerCtrlPadSleepDisable();
      }
  }
  
  //*****************************************************************************
  //
  // Force the system into shutdown mode
  //
  //*****************************************************************************
  void SysCtrlShutdown(void)
  {
      // Request shutdown mode
      HWREG(AON_PMCTL_BASE + AON_PMCTL_O_SHUTDOWN) = AON_PMCTL_SHUTDOWN_EN;
  
      // Make sure System CPU does not continue beyond this point.
      // Shutdown happens when all shutdown conditions are met.
      while(1);
  }
  
  //*****************************************************************************
  //
  // Force the system in to standby mode
  //
  //*****************************************************************************
  void SysCtrlStandby(bool retainCache, uint32_t vimsPdMode, uint32_t rechargeMode)
  {
      uint32_t modeVIMS;
  
      // In external regulator mode:
      // Set static recharge timing to approximately 500 milliseconds
      // Else:
      // Handle compensation for improving RCOSC_LF stability at low temperatures
      // as configured in CCFG
      SysCtrlSetRechargeBeforePowerDown(XOSC_IN_HIGH_POWER_MODE);
  
      // Freeze the IOs on the boundary between MCU and AON
      AONIOCFreezeEnable();
  
      // Ensure any possible outstanding AON writes complete before turning off the power domains
      SysCtrlAonSync();
  
      // Request power off of domains in the MCU voltage domain
      PRCMPowerDomainOff(PRCM_DOMAIN_RFCORE | PRCM_DOMAIN_SERIAL | PRCM_DOMAIN_PERIPH | PRCM_DOMAIN_CPU);
  
      // Ensure that no clocks are forced on in any modes for Crypto, DMA and I2S
      HWREG(PRCM_BASE + PRCM_O_SECDMACLKGR) &= (~PRCM_SECDMACLKGR_CRYPTO_AM_CLK_EN & ~PRCM_SECDMACLKGR_DMA_AM_CLK_EN);
      HWREG(PRCM_BASE + PRCM_O_I2SCLKGR)    &= ~PRCM_I2SCLKGR_AM_CLK_EN;
  
      // Gate running deep sleep clocks for Crypto, DMA and I2S
      PRCMPeripheralDeepSleepDisable(PRCM_PERIPH_CRYPTO);
      PRCMPeripheralDeepSleepDisable(PRCM_PERIPH_UDMA);
      PRCMPeripheralDeepSleepDisable(PRCM_PERIPH_I2S);
  
      // Load the new clock settings
      PRCMLoadSet();
  
      // Configure the VIMS power domain mode
      HWREG(PRCM_BASE + PRCM_O_PDCTL1VIMS) = vimsPdMode;
  
      // Request uLDO during standby
      PRCMMcuUldoConfigure(1);
  
      // In external regulator mode:
      // - Setting the AON_PMCTL_O_RECHARGECFG register is already handled above.
      //   (in function SysCtrlSetRechargeBeforePowerDown() )
      // Else:
      // - Set the AON_PMCTL_O_RECHARGECFG register as described below.
      if ((HWREG(AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL) & AON_PMCTL_PWRCTL_EXT_REG_MODE)==0)
      {
          // In internal regulator mode the recharge functionality is set up with
          // adaptive recharge mode and fixed parameter values
          if(rechargeMode == SYSCTRL_PREFERRED_RECHARGE_MODE)
          {
              // Enable the Recharge Comparator
              HWREG(AON_PMCTL_BASE + AON_PMCTL_O_RECHARGECFG) = AON_PMCTL_RECHARGECFG_MODE_COMPARATOR;
          }
          else
          {
              // Set requested recharge mode
              HWREG(AON_PMCTL_BASE + AON_PMCTL_O_RECHARGECFG) = rechargeMode;
          }
      }
  
      // Ensure all writes have taken effect
      SysCtrlAonSync();
  
      // Ensure UDMA, Crypto and I2C clocks are turned off
      while (!PRCMLoadGet()) {;}
  
      // Ensure power domains have been turned off.
      // CPU power domain will power down when PRCMDeepSleep() executes.
      while (PRCMPowerDomainsAllOff(PRCM_DOMAIN_RFCORE | PRCM_DOMAIN_SERIAL | PRCM_DOMAIN_PERIPH) != PRCM_DOMAIN_POWER_OFF) {;}
  
      // Turn off cache retention if requested
      if (retainCache == false) {
  
          // Get the current VIMS mode
          do {
              modeVIMS = VIMSModeGet(VIMS_BASE);
          } while (modeVIMS == VIMS_MODE_CHANGING);
  
          // If in a cache mode, turn VIMS off
          if (modeVIMS == VIMS_MODE_ENABLED) {
             VIMSModeSet(VIMS_BASE, VIMS_MODE_OFF);
          }
  
          // Disable retention of cache RAM
          PRCMCacheRetentionDisable();
      }
  
      // Invoke deep sleep to go to STANDBY
      PRCMDeepSleep();
  }
  
  //*****************************************************************************
  //
  // SysCtrlSetRechargeBeforePowerDown( xoscPowerMode )
  //
  //*****************************************************************************
  void
  SysCtrlSetRechargeBeforePowerDown( uint32_t xoscPowerMode )
  {
     uint32_t          ccfg_ModeConfReg        ;
  
     // If external regulator mode we shall:
     // - Set static recharge timing in AON_PMCTL_RECHARGECFG (MODE_STATIC)
     // - Set recharge period to approximately 500 mS (perM=31, perE=5 => 0xFD)
     if ( HWREG( AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL ) & AON_PMCTL_PWRCTL_EXT_REG_MODE ) {
        HWREG( AON_PMCTL_BASE + AON_PMCTL_O_RECHARGECFG ) = ( AON_PMCTL_RECHARGECFG_MODE_STATIC | 0x000000FD );
        return;
     }
  
     // read the MODE_CONF register in CCFG
     ccfg_ModeConfReg = HWREG( CCFG_BASE + CCFG_O_MODE_CONF );
     // Do temperature compensation if enabled
     if (( ccfg_ModeConfReg & CCFG_MODE_CONF_VDDR_TRIM_SLEEP_TC ) == 0 ) {
        int32_t vddrSleepDelta  ;
        int32_t curTemp         ;
        int32_t tcDelta         ;
        int32_t vddrSleepTrim   ;
  
        // Get VDDR_TRIM_SLEEP_DELTA + 1 (sign extended) ==> vddrSleepDelta = -7..+8
        vddrSleepDelta = (((int32_t)( ccfg_ModeConfReg << ( 32 - CCFG_MODE_CONF_VDDR_TRIM_SLEEP_DELTA_W - CCFG_MODE_CONF_VDDR_TRIM_SLEEP_DELTA_S )))
                                                       >> ( 32 - CCFG_MODE_CONF_VDDR_TRIM_SLEEP_DELTA_W )) + 1 ;
        curTemp = AONBatMonTemperatureGetDegC();
        tcDelta = ( 62 - curTemp ) >> 3;
        if ( tcDelta > 7 ) {
           tcDelta = 7 ;
        }
        if ( tcDelta > vddrSleepDelta ) {
           vddrSleepDelta = tcDelta ;
        }
        vddrSleepTrim = (( HWREG( FCFG1_BASE + FCFG1_O_MISC_TRIM ) & FCFG1_MISC_TRIM_TRIM_RECHARGE_COMP_REFLEVEL_M ) >>
                                                                     FCFG1_MISC_TRIM_TRIM_RECHARGE_COMP_REFLEVEL_S ) ;
  
        vddrSleepTrim -= vddrSleepDelta ;
        if ( vddrSleepTrim >  15 ) vddrSleepTrim =  15 ;
        if ( vddrSleepTrim <   1 ) vddrSleepTrim =   1 ;
        // Write adjusted value using MASKED write (MASK8)
        HWREGB( ADI3_BASE + ADI_O_MASK4B + ( ADI_3_REFSYS_O_CTL_RECHARGE_CMP0 * 2 )) = (( ADI_3_REFSYS_CTL_RECHARGE_CMP0_TRIM_RECHARGE_COMP_REFLEVEL_M << 4 ) |
          (( vddrSleepTrim << ADI_3_REFSYS_CTL_RECHARGE_CMP0_TRIM_RECHARGE_COMP_REFLEVEL_S ) & ADI_3_REFSYS_CTL_RECHARGE_CMP0_TRIM_RECHARGE_COMP_REFLEVEL_M )   );
        // Make a dummy read in order to make sure the write above is done before going into standby
        HWREGB( ADI3_BASE + ADI_3_REFSYS_O_CTL_RECHARGE_CMP0 );
     }
  }
  
  
  //*****************************************************************************
  //
  // SysCtrlAdjustRechargeAfterPowerDown()
  //
  //*****************************************************************************
  void
  SysCtrlAdjustRechargeAfterPowerDown( uint32_t vddrRechargeMargin )
  {
     // Nothing to be done but keeping this function for platform compatibility.
  }
  
  
  //*****************************************************************************
  //
  // SysCtrl_DCDC_VoltageConditionalControl()
  //
  //*****************************************************************************
  void
  SysCtrl_DCDC_VoltageConditionalControl( void )
  {
     uint32_t batThreshold     ;  // Fractional format with 8 fractional bits.
     uint32_t aonBatmonBat     ;  // Fractional format with 8 fractional bits.
     uint32_t ccfg_ModeConfReg ;  // Holds a copy of the CCFG_O_MODE_CONF register.
     uint32_t aonPmctlPwrctl   ;  // Reflect whats read/written to the AON_PMCTL_O_PWRCTL register.
  
     // We could potentially call this function before any battery voltage measurement
     // is made/available. In that case we must make sure that we do not turn off the DCDC.
     // This can be done by doing nothing as long as the battery voltage is 0 (Since the
     // reset value of the battery voltage register is 0).
     aonBatmonBat = HWREG( AON_BATMON_BASE + AON_BATMON_O_BAT );
     if ( aonBatmonBat != 0 ) {
        // Check if Voltage Conditional Control is enabled
        // It is enabled if all the following are true:
        // - DCDC in use (either in active or recharge mode), (in use if one of the corresponding CCFG bits are zero).
        // - Alternative DCDC settings are enabled ( DIS_ALT_DCDC_SETTING == 0 )
        // - Not in external regulator mode ( EXT_REG_MODE == 0 )
        ccfg_ModeConfReg = HWREG( CCFG_BASE + CCFG_O_MODE_CONF );
  
        if (((( ccfg_ModeConfReg & CCFG_MODE_CONF_DCDC_RECHARGE_M ) == 0                                            ) ||
             (( ccfg_ModeConfReg & CCFG_MODE_CONF_DCDC_ACTIVE_M   ) == 0                                            )    ) &&
            (( HWREG( AON_PMCTL_BASE  + AON_PMCTL_O_PWRCTL  ) & AON_PMCTL_PWRCTL_EXT_REG_MODE  )               == 0      ) &&
            (( HWREG( CCFG_BASE + CCFG_O_SIZE_AND_DIS_FLAGS ) & CCFG_SIZE_AND_DIS_FLAGS_DIS_ALT_DCDC_SETTING ) == 0      )    )
        {
           aonPmctlPwrctl = HWREG( AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL );
           batThreshold   = (((( HWREG( CCFG_BASE + CCFG_O_MODE_CONF_1 ) &
              CCFG_MODE_CONF_1_ALT_DCDC_VMIN_M ) >>
              CCFG_MODE_CONF_1_ALT_DCDC_VMIN_S ) + 28 ) << 4 );
  
           if ( aonPmctlPwrctl & ( AON_PMCTL_PWRCTL_DCDC_EN_M | AON_PMCTL_PWRCTL_DCDC_ACTIVE_M )) {
              // DCDC is ON, check if it should be switched off
              if ( aonBatmonBat < batThreshold ) {
                 aonPmctlPwrctl &= ~( AON_PMCTL_PWRCTL_DCDC_EN_M | AON_PMCTL_PWRCTL_DCDC_ACTIVE_M );
  
                 HWREG( AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL ) = aonPmctlPwrctl;
              }
           } else {
              // DCDC is OFF, check if it should be switched on
              if ( aonBatmonBat > batThreshold ) {
                 if (( ccfg_ModeConfReg & CCFG_MODE_CONF_DCDC_RECHARGE_M ) == 0 ) aonPmctlPwrctl |= AON_PMCTL_PWRCTL_DCDC_EN_M     ;
                 if (( ccfg_ModeConfReg & CCFG_MODE_CONF_DCDC_ACTIVE_M   ) == 0 ) aonPmctlPwrctl |= AON_PMCTL_PWRCTL_DCDC_ACTIVE_M ;
  
                 HWREG( AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL ) = aonPmctlPwrctl;
              }
           }
        }
     }
  }
  
  
  //*****************************************************************************
  //
  // SysCtrlResetSourceGet()
  //
  //*****************************************************************************
  uint32_t
  SysCtrlResetSourceGet( void )
  {
     uint32_t aonPmctlResetCtl = HWREG( AON_PMCTL_BASE + AON_PMCTL_O_RESETCTL );
  
     if ( aonPmctlResetCtl & AON_PMCTL_RESETCTL_WU_FROM_SD_M ) {
        if ( aonPmctlResetCtl & AON_PMCTL_RESETCTL_GPIO_WU_FROM_SD_M ) {
           return ( RSTSRC_WAKEUP_FROM_SHUTDOWN );
        } else {
           return ( RSTSRC_WAKEUP_FROM_TCK_NOISE );
        }
     } else {
        return (( aonPmctlResetCtl & AON_PMCTL_RESETCTL_RESET_SRC_M ) >> AON_PMCTL_RESETCTL_RESET_SRC_S );
     }
  }
  