/** @file hal/micro/cortexm3/sleep.c
 * 
 * @brief STM32W108 micro specific sleep functions.
 *
 * <!--(C) COPYRIGHT 2010 STMicroelectronics. All rights reserved.        -->
 */

#include PLATFORM_HEADER
#include "hal/micro/micro-common.h"
#include "hal/micro/cortexm3/micro-common.h"





















































































//We don't have a real register to hold this composite information.
//Pretend we do so halGetWakeInfo can operate like halGetResetInfo.
//This "register" is only ever set by halInternalSleep.
// [31] = WakeInfoValid
// [30] = SleepSkipped
// [29] = CSYSPWRUPREQ
// [28] = CDBGPWRUPREQ
// [27] = WAKE_CORE
// [26] = TIMER_WAKE_WRAP
// [25] = TIMER_WAKE_COMPB
// [24] = TIMER_WAKE_COMPA
// [23:0] = corresponding GPIO activity
#define WAKEINFOVALID_INTERNAL_WAKE_EVENT_BIT 31
#define SLEEPSKIPPED_INTERNAL_WAKE_EVENT_BIT  30
#define CSYSPWRUPREQ_INTERNAL_WAKE_EVENT_BIT  29
#define CDBGPWRUPREQ_INTERNAL_WAKE_EVENT_BIT  28
#define WAKE_CORE_INTERNAL_WAKE_EVENT_BIT     27
#define WRAP_INTERNAL_WAKE_EVENT_BIT          26
#define CMPB_INTERNAL_WAKE_EVENT_BIT          25
#define CMPA_INTERNAL_WAKE_EVENT_BIT          24
//This define shifts events from the PWRUP_EVENT register into the proper
//place in the halInternalWakeEvent variable
#define INTERNAL_WAKE_EVENT_BIT_SHIFT         20

static int32u halInternalWakeEvent=0;

int32u halGetWakeInfo(void)
{
  return halInternalWakeEvent;
}
  
void halInternalSleep(SleepModes sleepMode)
{
  //Timer restoring always takes place during the wakeup sequence.  We save
  //the state here in case SLEEPMODE_NOTIMER is invoked, which would disable
  //the clocks.
  int32u SLEEPTMR_CLKEN_SAVED = SLEEPTMR_CLKEN;
  
  //This code assumes all wake source registers are properly configured.
  //As such, it should be called from halSleepWithOptions() or from
  // halSleepForQsWithOptions() which configues the wake sources.
  
  //The parameter gpioWakeSel is a bitfield composite of the GPIO wake
  //sources derived from the 3 ports, indicating which of the 24 GPIO
  //are configured as a wake source.
  int32u gpioWakeSel  = (GPIO_PAWAKE<<0);
         gpioWakeSel |= (GPIO_PBWAKE<<8);
         gpioWakeSel |= (GPIO_PCWAKE<<16);
  
  //PB2 is also WAKE_SC1.  Set this wake source if PB2's GPIO wake is set.
  if(GPIO_PBWAKE & PB2) {
    WAKE_SEL |= WAKE_SC1;
  }
  
  //PA2 is also WAKE_SC2.  Set this wake source if PA2's GPIO wake is set.
  if(GPIO_PAWAKE & PA2) {
    WAKE_SEL |= WAKE_SC2;
  }
  
  //The WAKE_IRQD source can come from any pin based on IRQD's sel register.
  if(gpioWakeSel & BIT(GPIO_IRQDSEL)) {
    WAKE_SEL |= WAKE_IRQD;
  }
  
  halInternalWakeEvent = 0; //clear old wake events
  
  switch(sleepMode)
  {
    case SLEEPMODE_NOTIMER:
      //The sleep timer clock sources (both RC and XTAL) are turned off.
      //Wakeup is possible from only GPIO.  System time is lost.
      //NOTE: Timer restoring always takes place during the wakeup sequence.
      SLEEPTMR_CLKEN = 0;
      goto deepSleepCore;
    
    case SLEEPMODE_WAKETIMER:
      //The sleep timer clock sources remain running.  The RC is always
      //running and the 32kHz XTAL depends on the board header.  Wakeup
      //is possible from both GPIO and the sleep timer.  System time
      //is maintained.  The sleep timer is assumed to be configured
      //properly for wake events.
      //NOTE: This mode assumes the caller has configured the *entire*
      //      sleep timer properly.
      
      if(INT_SLEEPTMRCFG&INT_SLEEPTMRWRAP) {
        WAKE_SEL |= WAKE_SLEEPTMRWRAP;
      }
      if(INT_SLEEPTMRCFG&INT_SLEEPTMRCMPB) {
        WAKE_SEL |= WAKE_SLEEPTMRCMPB;
      }
      if(INT_SLEEPTMRCFG&INT_SLEEPTMRCMPA) {
        WAKE_SEL |= WAKE_SLEEPTMRCMPA;
      }
      //fall into SLEEPMODE_MAINTAINTIMER's sleep code:
      
    case SLEEPMODE_MAINTAINTIMER:
      //The sleep timer clock sources remain running.  The RC is always
      //running and the 32kHz XTAL depends on the board header.  Wakeup
      //is possible from only GPIO.  System time is maintained.
      //NOTE: System time is maintained without any sleep timer interrupts
      //      because the hardware sleep timer counter is large enough
      //      to hold the entire count value and not need a RAM counter.
      
    ////////////////////////////////////////////////////////////////////////////
    // Core deep sleep code
    ////////////////////////////////////////////////////////////////////////////
deepSleepCore:
      // Interrupts *must* be/stay disabled for DEEP SLEEP operation
      // INTERRUPTS_OFF will use BASEPRI to disable all interrupts except
      // fault handlers and PendSV.
      INTERRUPTS_OFF();
      // This is the point of no return.  From here on out, only the interrupt
      // sources available in WAKE_SEL will be captured and propagated across
      // deep sleep.
      //stick all our saved info onto stack since it's only temporary
      {
        boolean restoreWatchdog = halInternalWatchDogEnabled();
        boolean skipSleep = FALSE;
        
        // Only three register blocks keep power across deep sleep:
        //  CM_HV, GPIO, SLOW_TIMERS
        //
        // All other register blocks lose their state across deep sleep:
        //  BASEBAND, MAC, SECURITY, SERIAL, TMR1, TMR2, EVENT, CM_LV, RAM_CTRL,
        //  AUX_ADC, CAL_ADC, FLASH_CONTROL, ITM, DWT, FPB, NVIC, TPIU
        //
        // The sleep code will only save and restore registers where it is
        // meaningful and necessary to do so.  In most cases, there must still
        // be a powerup function to restore proper state.
        //
        // NOTE: halPowerUp() and halPowerDown() will always be called before
        // and after this function.  halPowerDown and halPowerUp should leave
        // the modules in a safe state and then restart the modules.
        // (For example, shutting down and restarting Timer1)
        //
        //----BASEBAND
        //        reinitialized by stStackPowerUp()
        //----MAC
        //        reinitialized by stStackPowerUp()
        //----SECURITY
        //        reinitialized by stStackPowerUp()
        //----SERIAL
        //        reinitialized by halPowerUp() or similar
        //----TMR1
        //        reinitialized by halPowerUp() or similar
        //----TMR2
        //        reinitialized by halPowerUp() or similar
        //----EVENT
        //SRC or FLAG interrupts are not saved or restored
        //MISS interrupts are not saved or restored
        //MAC_RX_INT_MASK - reinitialized by stStackPowerUp()
        //MAC_TX_INT_MASK - reinitialized by stStackPowerUp()
        //MAC_TIMER_INT_MASK - reinitialized by stStackPowerUp()
        //BB_INT_MASK - reinitialized by stStackPowerUp()
        //SEC_INT_MASK - reinitialized by stStackPowerUp()
        int32u INT_SLEEPTMRCFG_SAVED = INT_SLEEPTMRCFG_REG;
        int32u INT_MGMTCFG_SAVED = INT_MGMTCFG_REG;
        //INT_TIM1CFG - reinitialized by halPowerUp() or similar
        //INT_TIM2CFG - reinitialized by halPowerUp() or similar
        //INT_SC1CFG - reinitialized by halPowerUp() or similar
        //INT_SC2CFG - reinitialized by halPowerUp() or similar
        //INT_ADCCFG - reinitialized by halPowerUp() or similar
        int32u GPIO_INTCFGA_SAVED = GPIO_INTCFGA_REG;
        int32u GPIO_INTCFGB_SAVED = GPIO_INTCFGB_REG;
        int32u GPIO_INTCFGC_SAVED = GPIO_INTCFGC_REG;
        int32u GPIO_INTCFGD_SAVED = GPIO_INTCFGD_REG;
        //SC1_INTMODE - reinitialized by halPowerUp() or similar
        //SC2_INTMODE - reinitialized by halPowerUp() or similar
        //----CM_LV
        int32u OSC24M_BIASTRIM_SAVED = OSC24M_BIASTRIM_REG;
        int32u OSCHF_TUNE_SAVED = OSCHF_TUNE_REG;
        int32u DITHER_DIS_SAVED = DITHER_DIS_REG;
        //OSC24M_CTRL - reinitialized by halPowerUp() or similar
        //CPU_CLKSEL  - reinitialized by halPowerUp() or similar
        //TMR1_CLK_SEL - reinitialized by halPowerUp() or similar
        //TMR2_CLK_SEL - reinitialized by halPowerUp() or similar
        int32u PCTRACE_SEL_SAVED = PCTRACE_SEL_REG;
        //----RAM_CTRL
        int32u MEM_PROT_0_SAVED = MEM_PROT_0_REG;
        int32u MEM_PROT_1_SAVED = MEM_PROT_1_REG;
        int32u MEM_PROT_2_SAVED = MEM_PROT_2_REG;
        int32u MEM_PROT_3_SAVED = MEM_PROT_3_REG;
        int32u MEM_PROT_4_SAVED = MEM_PROT_4_REG;
        int32u MEM_PROT_5_SAVED = MEM_PROT_5_REG;
        int32u MEM_PROT_6_SAVED = MEM_PROT_6_REG;
        int32u MEM_PROT_7_SAVED = MEM_PROT_7_REG;
        int32u MEM_PROT_EN_SAVED = MEM_PROT_EN_REG;
        //----AUX_ADC
        //        reinitialized by halPowerUp() or similar
        //----CAL_ADC
        //        reinitialized by stStackPowerUp()
        //----FLASH_CONTROL
        //        configured on the fly by the flash library
        //----ITM
        //        reinitialized by halPowerUp() or similar
        //----DWT
        //        not used by software on chip
        //----FPB
        //        not used by software on chip
        //----NVIC
        //ST_CSR - fixed, restored by cstartup when exiting deep sleep
        //ST_RVR - fixed, restored by cstartup when exiting deep sleep
        int32u INT_CFGSET_SAVED = INT_CFGSET_REG; //mask against wake sources
        //INT_PENDSET - used below when overlapping interrupts and wake sources
        //NVIC_IPR_3to0 - fixed, restored by cstartup when exiting deep sleep
        //NVIC_IPR_7to4 - fixed, restored by cstartup when exiting deep sleep
        //NVIC_IPR_11to8 - fixed, restored by cstartup when exiting deep sleep
        //NVIC_IPR_15to12 - fixed, restored by cstartup when exiting deep sleep
        //NVIC_IPR_19to16 - fixed, restored by cstartup when exiting deep sleep
        int32u SCS_VTOR_SAVED = SCS_VTOR_REG;
        //SCS_CCR - fixed, restored by cstartup when exiting deep sleep
        //SCS_SHPR_7to4 - fixed, restored by cstartup when exiting deep sleep
        //SCS_SHPR_11to8 - fixed, restored by cstartup when exiting deep sleep
        //SCS_SHPR_15to12 - fixed, restored by cstartup when exiting deep sleep
        //SCS_SHCSR - fixed, restored by cstartup when exiting deep sleep
        //----TPIU
        //        reinitialized by halPowerUp() or similar
        
        //stmDebugPowerDown() should have shutdown the DWT/ITM/TPIU already.
        
        //freeze input to the GPIO from LV (alternate output functions freeze)
        EVENT_CTRL = LV_FREEZE;
        //record GPIO state for wake monitoring purposes
        //By having a snapshot of GPIO state, we can figure out after waking
        //up exactly which GPIO could have woken us up.
        //Reading the three IN registers is done separately to avoid warnings
        //about undefined order of volatile access.
        int32u GPIO_IN_SAVED =   GPIO_PAIN;
               GPIO_IN_SAVED |= (GPIO_PBIN<<8);
               GPIO_IN_SAVED |= (GPIO_PCIN<<16);
        //reset the power up events by writing 1 to all bits.
        PWRUP_EVENT = 0xFFFFFFFF;



        //By clearing the events, the wake up event capturing is activated.
        //At this point we can safely check our interrupt flags since event
        //capturing is now overlapped.  Up to now, interrupts indicate
        //activity, after this point, powerup events indicate activity.
        //If any of the interrupt flags are set, that means we saw a wake event
        //sometime while entering sleep, so we need to skip over sleeping
        //
        //--possible interrupt sources for waking:
        //  IRQA, IRQB, IRQC, IRQD
        //  SleepTMR CMPA, CMPB, Wrap
        //  WAKE_CORE (DebugIsr)
        //
        //check for IRQA interrupt and if IRQA (PB0) is wake source
        if((INT_PENDSET&INT_IRQA) &&
           (GPIO_PBWAKE&PB0) &&
           (WAKE_SEL&GPIO_WAKE)) {
          skipSleep = TRUE;
          //log IRQA as a wake event
          halInternalWakeEvent |= BIT(PORTB_PIN(0));



        }
        //check for IRQB interrupt and if IRQB (PB6) is wake source
        if((INT_PENDSET&INT_IRQB) &&
           (GPIO_PBWAKE&PB6) &&
           (WAKE_SEL&GPIO_WAKE)) {
          skipSleep = TRUE;
          //log IRQB as a wake event
          halInternalWakeEvent |= BIT(PORTB_PIN(6));



        }
        //check for IRQC interrupt and if IRQC (GPIO_IRQCSEL) is wake source
        if((INT_PENDSET&INT_IRQC) &&
           (gpioWakeSel&BIT(GPIO_IRQCSEL)) &&
           (WAKE_SEL&GPIO_WAKE)) {
          skipSleep = TRUE;
          //log IRQC as a wake event
          halInternalWakeEvent |= BIT(GPIO_IRQCSEL);



        }
        //check for IRQD interrupt and if IRQD (GPIO_IRQDSEL) is wake source
        if((INT_PENDSET&INT_IRQD) &&
           (gpioWakeSel&BIT(GPIO_IRQDSEL)) &&
           ((WAKE_SEL&GPIO_WAKE) ||
            (WAKE_SEL&WAKE_IRQD))) {
          skipSleep = TRUE;
          //log IRQD as a wake event
          halInternalWakeEvent |= BIT(GPIO_IRQDSEL);



        }
        //check for SleepTMR CMPA interrupt and if SleepTMR CMPA is wake source
        if((INT_SLEEPTMR&INT_SLEEPTMRCMPA) && (WAKE_SEL&WAKE_SLEEPTMRCMPA)) {
          skipSleep = TRUE;
          //log SleepTMR CMPA as a wake event
          halInternalWakeEvent |= BIT32(CMPA_INTERNAL_WAKE_EVENT_BIT);



        }
        //check for SleepTMR CMPB interrupt and if SleepTMR CMPB is wake source
        if((INT_SLEEPTMR&INT_SLEEPTMRCMPB) && (WAKE_SEL&WAKE_SLEEPTMRCMPB)) {
          skipSleep = TRUE;
          //log SleepTMR CMPB as a wake event
          halInternalWakeEvent |= BIT32(CMPB_INTERNAL_WAKE_EVENT_BIT);



        }
        //check for SleepTMR WRAP interrupt and if SleepTMR WRAP is wake source
        if((INT_SLEEPTMR&INT_SLEEPTMRWRAP) && (WAKE_SEL&WAKE_SLEEPTMRWRAP)) {
          skipSleep = TRUE;
          //log SleepTMR WRAP as a wake event
          halInternalWakeEvent |= BIT32(WRAP_INTERNAL_WAKE_EVENT_BIT);



        }
        //check for Debug interrupt and if WAKE_CORE is wake source
        if((INT_PENDSET&INT_DEBUG) && (WAKE_SEL&WAKE_WAKE_CORE)) {
          skipSleep = TRUE;
          //log WAKE_CORE as a wake event
          halInternalWakeEvent |= BIT32(WAKE_CORE_INTERNAL_WAKE_EVENT_BIT);



        }

        //only propagate across deep sleep the interrupts that are both
        //enabled and possible wake sources
        {
          int32u wakeSourceInterruptMask = 0;
          
          if(GPIO_PBWAKE&PB0) {
            wakeSourceInterruptMask |= INT_IRQA;



          }
          if(GPIO_PBWAKE&PB6) {
            wakeSourceInterruptMask |= INT_IRQB;



          }
          if(gpioWakeSel&BIT(GPIO_IRQCSEL)) {
            wakeSourceInterruptMask |= INT_IRQC;



          }
          if(gpioWakeSel&BIT(GPIO_IRQDSEL)) {
            wakeSourceInterruptMask |= INT_IRQD;



          }
          if( (WAKE_SEL&WAKE_SLEEPTMRCMPA) ||
              (WAKE_SEL&WAKE_SLEEPTMRCMPB) ||
              (WAKE_SEL&WAKE_SLEEPTMRWRAP) ) {
            wakeSourceInterruptMask |= INT_SLEEPTMR;



          }
          if(WAKE_SEL&WAKE_WAKE_CORE) {
            wakeSourceInterruptMask |= INT_DEBUG;



          }
          
          INT_CFGSET_SAVED &= wakeSourceInterruptMask;
        }
        















        
        //disable watchdog while sleeping (since we can't reset it asleep)
        halInternalDisableWatchDog(MICRO_DISABLE_WATCH_DOG_KEY);
        
        //The chip is not allowed to enter a deep sleep mode (which could
        //cause a core reset cycle) while CSYSPWRUPREQ is set.  CSYSPWRUPREQ
        //indicates that the debugger is trying to access sections of the
        //chip that would get reset during deep sleep.  Therefore, a reset
        //cycle could very easily cause the debugger to error and we don't
        //want that.  While the power management state machine will stall
        //if CSYSPWRUPREQ is set (to avoid the situation just described),
        //in this stalled state the chip will not be responsive to wake
        //events.  To be sensitive to wake events, we must handle them in
        //software instead.  To accomplish this, we request that the
        //CSYSPWRUPACK be inhibited (which will indicate the debugger is not
        //connected).  But, we cannot induce deep sleep until CSYSPWRUPREQ/ACK
        //go low and these are under the debuggers control, so we must stall
        //and wait here.  If there is a wake event during this time, break
        //out and wake like normal.  If the ACK eventually clears,
        //we can proceed into deep sleep.  The CSYSPWRUPACK_INHIBIT
        //functionality will hold off the debugger (by holding off the ACK)
        //until we are safely past and out of deep sleep.  The power management
        //state machine then becomes responsible for clearing
        //CSYSPWRUPACK_INHIBIT and responding to a CSYSPWRUPREQ with a
        //CSYSPWRUPACK at the right/safe time.
        CSYSPWRUPACK_INHIBIT = CSYSPWRUPACK_INHIBIT_CSYSPWRUPACK_INHIBIT;
        {
          //Use a local copy of WAKE_SEL to avoid warnings from the compiler
          //about order of volatile accesses
          int32u wakeSel = WAKE_SEL;
          //stall until a wake event or CSYSPWRUPREQ/ACK clears
          while( (CSYSPWRUPACK_STATUS) && (!(PWRUP_EVENT&wakeSel)) ) {}
          //if there was a wake event, allow CSYSPWRUPACK and skip sleep
          if(PWRUP_EVENT&wakeSel) {
            CSYSPWRUPACK_INHIBIT = CSYSPWRUPACK_INHIBIT_RESET;
            skipSleep = TRUE;
          }
        }
        
        



        if(!skipSleep) {



          //FogBugz 7283 states that we must switch to the OSCHF when entering
          //deep sleep since using the 24MHz XTAL could result in RAM
          //corruption.  This switch must occur at least 2*24MHz cycles before
          //sleeping.
          //FogBugz 8858 states that we cannot go into deep-sleep when the
          //chip is clocked with the 24MHz XTAL with a duty cycle as low as
          //70/30 since this causes power_down generation timing to fail.
          OSC24M_CTRL &= ~OSC24M_CTRL_OSC24M_SEL;
          //If DS12 needs to be forced regardless of state, clear
          //REGEN_DSLEEP here.  This is hugely dangerous and
          //should only be done in very controlled chip tests.
          SCS_SCR |= SCS_SCR_SLEEPDEEP;      //enable deep sleep
          extern volatile boolean halPendSvSaveContext;
          halPendSvSaveContext = 1;          //1 means save context
          //The INTERRUPTS_OFF used at the beginning of this function set
          //BASEPRI such that the only interrupts that will fire are faults
          //and PendSV.  Trigger PendSV now to induce a context save.
          SCS_ICSR |= SCS_ICSR_PENDSVSET;    //pend the context save and Dsleep
          //Since the interrupt will not fire immediately it is possible to
          //execute a few lines of code.  To stay halted in this spot until the
          //WFI instruction, spin on the context flag (which will get cleared
          //during the startup sequence when restoring context).
          while(halPendSvSaveContext) {}
          //I AM ASLEEP.  WHEN EXECUTION RESUMES, CSTARTUP WILL RESTORE TO HERE
        } else {
          //Record the fact that we skipped sleep
          halInternalWakeEvent |= BIT32(SLEEPSKIPPED_INTERNAL_WAKE_EVENT_BIT);
          //If this was a true deep sleep, we would have executed cstartup and
          //PRIMASK would be set right now.  If we skipped sleep, PRIMASK is not
          //set so we explicitely set it to guarantee the powerup sequence
          //works cleanly and consistently with respect to interrupt
          //dispatching and enabling.
          _setPriMask();
        }



        



        
        //Clear the interrupt flags for all wake sources.  This
        //is necessary because if we don't execute an actual deep sleep cycle
        //the interrupt flags will never be cleared.  By clearing the flags,
        //we always mimick a real deep sleep as closely as possible and
        //guard against any accidental interrupt triggering coming out
        //of deep sleep.  (The interrupt dispatch code coming out of sleep
        //is responsible for translating wake events into interrupt events,
        //and if we don't clear interrupt flags here it's possible for an
        //interrupt to trigger even if it wasn't the true wake event.)
        INT_SLEEPTMRFLAG = (INT_SLEEPTMRCMPA |
                            INT_SLEEPTMRCMPB |
                            INT_SLEEPTMRWRAP);
        INT_GPIOFLAG = (INT_IRQAFLAG |
                        INT_IRQBFLAG |
                        INT_IRQCFLAG |
                        INT_IRQDFLAG);
        
        //immediately restore the registers we saved before sleeping
        //so IRQ and SleepTMR capture can be reenabled as quickly as possible
        //this is safe because our global interrupts are still disabled
        //other registers will be restored later







        SLEEPTMR_CLKEN_REG = SLEEPTMR_CLKEN_SAVED;
        INT_SLEEPTMRCFG_REG = INT_SLEEPTMRCFG_SAVED;
        INT_MGMTCFG_REG = INT_MGMTCFG_SAVED;
        GPIO_INTCFGA_REG = GPIO_INTCFGA_SAVED;
        GPIO_INTCFGB_REG = GPIO_INTCFGB_SAVED;
        GPIO_INTCFGC_REG = GPIO_INTCFGC_SAVED;
        GPIO_INTCFGD_REG = GPIO_INTCFGD_SAVED;
        OSC24M_BIASTRIM_REG = OSC24M_BIASTRIM_SAVED;
        OSCHF_TUNE_REG = OSCHF_TUNE_SAVED;
        DITHER_DIS_REG = DITHER_DIS_SAVED;
        PCTRACE_SEL_REG = PCTRACE_SEL_SAVED;
        MEM_PROT_0_REG = MEM_PROT_0_SAVED;
        MEM_PROT_1_REG = MEM_PROT_1_SAVED;
        MEM_PROT_2_REG = MEM_PROT_2_SAVED;
        MEM_PROT_3_REG = MEM_PROT_3_SAVED;
        MEM_PROT_4_REG = MEM_PROT_4_SAVED;
        MEM_PROT_5_REG = MEM_PROT_5_SAVED;
        MEM_PROT_6_REG = MEM_PROT_6_SAVED;
        MEM_PROT_7_REG = MEM_PROT_7_SAVED;
        MEM_PROT_EN_REG = MEM_PROT_EN_SAVED;
        INT_CFGSET_REG = INT_CFGSET_SAVED;
        SCS_VTOR_REG = SCS_VTOR_SAVED;
        
        //WAKE_CORE/INT_DEBUG and INT_IRQx is cleared by INT_PENDCLR below
        INT_PENDCLR = 0xFFFFFFFF;
        
        //Now that we're awake, normal interrupts are operational again
        //Take a snapshot of the new GPIO state and the EVENT register to
        //record our wake event
        int32u GPIO_IN_NEW =   GPIO_PAIN;
               GPIO_IN_NEW |= (GPIO_PBIN<<8);
               GPIO_IN_NEW |= (GPIO_PCIN<<16);
        //Only operate on power up events that are also wake events.  Power
        //up events will always trigger like an interrupt flag, so we have
        //to check them against events that are enabled for waking. (This is
        //a two step process because we're accessing two volatile values.)
        int32u powerUpEvents = PWRUP_EVENT;
               powerUpEvents &= WAKE_SEL;
        halInternalWakeEvent |= ((GPIO_IN_SAVED^GPIO_IN_NEW)&gpioWakeSel);
        //PWRUP_SC1 is PB2 which is bit 10
        halInternalWakeEvent |= (!!(powerUpEvents&PWRUP_SC1))<<((1*8)+2);
        //PWRUP_SC2 is PA2 which is bit 2
        halInternalWakeEvent |= (!!(powerUpEvents&PWRUP_SC2))<<((0*8)+2);
        //PWRUP_IRQD is chosen by GPIO_IRQDSEL
        halInternalWakeEvent |= (!!(powerUpEvents&PWRUP_IRQD))<<(GPIO_IRQDSEL);
        halInternalWakeEvent |= ((powerUpEvents & 
                                  (PWRUP_CSYSPWRUPREQ_MASK  |
                                   PWRUP_CDBGPWRUPREQ_MASK  |
                                   PWRUP_WAKECORE_MASK      |
                                   PWRUP_SLEEPTMRWRAP_MASK  |
                                   PWRUP_SLEEPTMRCOMPB_MASK |
                                   PWRUP_SLEEPTMRCOMPA_MASK ))
                                          <<INTERNAL_WAKE_EVENT_BIT_SHIFT);
        //at this point wake events are fully captured and interrupts have
        //taken over handling all new events
        




        
        //Bring limited interrupts back online.  INTERRUPTS_OFF will use
        //BASEPRI to disable all interrupts except fault handlers and PendSV.
        //PRIMASK is still set though (global interrupt disable) so we need
        //to clear that next.
        INTERRUPTS_OFF();
        



        
        //Now that BASEPRI has taken control of interrupt enable/disable,
        //we can clear PRIMASK to reenable global interrupt operation.
        _clearPriMask();
        



        
        //wake events are saved and interrupts are back on track,
        //disable gpio freeze
        EVENT_CTRL = EVENT_CTRL_RESET;
        
        //restart watchdog if it was running when we entered sleep
        //do this before dispatching interrupts while we still have tight
        //control of code execution
        if(restoreWatchdog) {
          halInternalEnableWatchDog();
        }
        



        
        //Pend any interrupts associated with deep sleep wake sources.  The
        //restoration of INT_CFGSET above and the changing of BASEPRI below
        //is responsible for proper dispatching of interrupts at the end of
        //halSleepWithOptions.
        //
        //
        //The WAKE_CORE wake source triggers a Debug Interrupt.  If INT_DEBUG
        //interrupt is enabled and WAKE_CORE is a wake event, then pend the
        //Debug interrupt (using the wake_core bit).
        if( (INT_CFGSET&INT_DEBUG) &&
            (halInternalWakeEvent&BIT(WAKE_CORE_INTERNAL_WAKE_EVENT_BIT)) ) {
          WAKE_CORE = WAKE_CORE_FIELD;



        }
        //
        //
        //The SleepTMR CMPA is linked to a real ISR.  If the SleepTMR CMPA
        //interrupt is enabled and CMPA is a wake event, then pend the CMPA
        //interrupt (force the second level interrupt).
        if( (INT_SLEEPTMRCFG&INT_SLEEPTMRCMPA) &&
            (halInternalWakeEvent&BIT(CMPA_INTERNAL_WAKE_EVENT_BIT)) ) {
          INT_SLEEPTMRFORCE = INT_SLEEPTMRCMPA;



        }
        //
        //The SleepTMR CMPB is linked to a real ISR.  If the SleepTMR CMPB
        //interrupt is enabled and CMPB is a wake event, then pend the CMPB
        //interrupt (force the second level interrupt).
        if( (INT_SLEEPTMRCFG&INT_SLEEPTMRCMPB) &&
            (halInternalWakeEvent&BIT(CMPB_INTERNAL_WAKE_EVENT_BIT)) ) {
          INT_SLEEPTMRFORCE = INT_SLEEPTMRCMPB;



        }
        //
        //The SleepTMR WRAP is linked to a real ISR.  If the SleepTMR WRAP
        //interrupt is enabled and WRAP is a wake event, then pend the WRAP
        //interrupt (force the second level interrupt).
        if( (INT_SLEEPTMRCFG&INT_SLEEPTMRWRAP) &&
            (halInternalWakeEvent&BIT(WRAP_INTERNAL_WAKE_EVENT_BIT)) ) {
          INT_SLEEPTMRFORCE = INT_SLEEPTMRWRAP;



        }
        //
        //
        //The four IRQs are linked to a real ISR.  If any of the four IRQs
        //triggered, then pend their ISR
        //
        //If the IRQA interrupt mode is enabled and IRQA (PB0) is wake
        //event, then pend the interrupt.
        if( ((GPIO_INTCFGA&GPIO_INTMOD)!=0) &&
            (halInternalWakeEvent&BIT(PORTB_PIN(0))) ) {
          INT_PENDSET = INT_IRQA;



        }
        //If the IRQB interrupt mode is enabled and IRQB (PB6) is wake
        //event, then pend the interrupt.
        if( ((GPIO_INTCFGB&GPIO_INTMOD)!=0) &&
            (halInternalWakeEvent&BIT(PORTB_PIN(6))) ) {
          INT_PENDSET = INT_IRQB;



        }
        //If the IRQC interrupt mode is enabled and IRQC (GPIO_IRQCSEL) is wake
        //event, then pend the interrupt.
        if( ((GPIO_INTCFGC&GPIO_INTMOD)!=0) &&
            (halInternalWakeEvent&BIT(GPIO_IRQCSEL)) ) {
          INT_PENDSET = INT_IRQC;



        }
        //If the IRQD interrupt mode is enabled and IRQD (GPIO_IRQDSEL) is wake
        //event, then pend the interrupt.
        if( ((GPIO_INTCFGD&GPIO_INTMOD)!=0) &&
            (halInternalWakeEvent&BIT(GPIO_IRQDSEL)) ) {
          INT_PENDSET = INT_IRQD;



        }
      }
      




      
      //Mark the wake events valid just before exiting
      halInternalWakeEvent |= BIT32(WAKEINFOVALID_INTERNAL_WAKE_EVENT_BIT);
      
      //We are now reconfigured, appropriate ISRs are pended, and ready to go,
      //so enable interrupts!
      INTERRUPTS_ON();
      



      
      break; //and deep sleeping is done!
    
    case SLEEPMODE_IDLE:
      //Only the CPU is idled.  The rest of the chip continues runing
      //normally.  The chip will wake from any interrupt.
      {
        boolean restoreWatchdog = halInternalWatchDogEnabled();
        //disable watchdog while sleeping (since we can't reset it asleep)
        halInternalDisableWatchDog(MICRO_DISABLE_WATCH_DOG_KEY);
        //Normal ATOMIC/INTERRUPTS_OFF/INTERRUPTS_ON uses the BASEPRI mask
        //to juggle priority levels so that the fault handlers can always
        //be serviced.  But, the WFI instruction is only capable of
        //working with the PRIMASK bit.  Therefore, we have to switch from
        //using BASEPRI to PRIMASK to keep interrupts disabled so that the
        //WFI can return on an interrupt
        //Globally disable interrupts with PRIMASK
        _setPriMask();
        //Bring the BASEPRI up to 0 to allow interrupts (but still disabled
        //with PRIMASK)
        INTERRUPTS_ON();
        //an internal function call is made here instead of injecting the
        //"WFI" assembly instruction because injecting assembly code will
        //cause the compiler's optimizer to reduce efficiency.
        halInternalIdleSleep();
        //The WFI instruction does not actually clear the PRIMASK bit, it
        //only allows the PRIMASK bit to be bypassed.  Therefore, we must
        //manually clear PRIMASK to reenable all interrupts.
        _clearPriMask();
        //restart watchdog if it was running when we entered sleep
        if(restoreWatchdog)
          halInternalEnableWatchDog();
      }
      break;
      
    default:
      //Oops!  Invalid sleepMode parameter.
      assert(0);
  }
}


void halSleepWithOptions(SleepModes sleepMode, int32u gpioWakeBitMask)
{
  //configure all GPIO wake sources
  GPIO_PAWAKE = (gpioWakeBitMask>>0)&0xFF;
  GPIO_PBWAKE = (gpioWakeBitMask>>8)&0xFF;
  GPIO_PCWAKE = (gpioWakeBitMask>>16)&0xFF;
  
  //use the defines found in the board file to choose our wakeup source(s)
  WAKE_SEL = 0;  //start with no wake sources
  
  //if any of the GPIO wakeup monitor bits are set, enable the top level
  //GPIO wakeup monitor
  if((GPIO_PAWAKE)||(GPIO_PBWAKE)||(GPIO_PCWAKE)) {
    WAKE_SEL |= GPIO_WAKE;
  }
  //always wakeup when the debugger is connected
  WAKE_SEL |= WAKE_CDBGPWRUPREQ;
  //always wakeup when the debugger attempts to access the chip
  WAKE_SEL |= WAKE_CSYSPWRUPREQ;
  //always wakeup when the debug channel attempts to access the chip
  WAKE_SEL |= WAKE_WAKE_CORE;
  //the timer wakeup sources are enabled below in POWERSAVE, if needed
  
  //wake sources are configured so do the actual sleeping
  halInternalSleep(sleepMode);
}