/*---------------------------------------------------------------------------*/ void lpm_exit() { if((REG(SYS_CTRL_PMCTL) & SYS_CTRL_PMCTL_PM3) == SYS_CTRL_PMCTL_PM0) { /* We either just exited PM0 or we were not sleeping in the first place. * We don't need to do anything clever */ return; } LPM_STATS_ADD(REG(SYS_CTRL_PMCTL) & SYS_CTRL_PMCTL_PM3, RTIMER_NOW() - sleep_enter_time); /* Adjust the system clock, since it was not counting while we were sleeping * We need to convert sleep duration from rtimer ticks to clock ticks and * this will cost us some accuracy */ clock_adjust((clock_time_t) ((RTIMER_NOW() - sleep_enter_time) / RTIMER_CLOCK_TICK_RATIO)); /* Restore system clock to the 32 MHz XOSC */ select_32_mhz_xosc(); /* Restore PMCTL to PM0 for next pass */ REG(SYS_CTRL_PMCTL) = SYS_CTRL_PMCTL_PM0; /* Remember IRQ energest for next pass */ ENERGEST_IRQ_SAVE(irq_energest); ENERGEST_ON(ENERGEST_TYPE_CPU); ENERGEST_OFF(ENERGEST_TYPE_LPM); }
/*---------------------------------------------------------------------------*/ void lpm_exit() { if((REG(SYS_CTRL_PMCTL) & SYS_CTRL_PMCTL_PM3) == SYS_CTRL_PMCTL_PM0) { /* We either just exited PM0 or we were not sleeping in the first place. * We don't need to do anything clever */ return; } /* * When returning from PM1/2, the sleep timer value (used by RTIMER_NOW()) is * not up-to-date until a positive edge on the 32-kHz clock has been detected * after the system clock restarted. To ensure an updated value is read, wait * for a positive transition on the 32-kHz clock by polling the * SYS_CTRL_CLOCK_STA.SYNC_32K bit, before reading the sleep timer value. */ while(REG(SYS_CTRL_CLOCK_STA) & SYS_CTRL_CLOCK_STA_SYNC_32K); while(!(REG(SYS_CTRL_CLOCK_STA) & SYS_CTRL_CLOCK_STA_SYNC_32K)); LPM_STATS_ADD(REG(SYS_CTRL_PMCTL) & SYS_CTRL_PMCTL_PM3, RTIMER_NOW() - sleep_enter_time); /* Adjust the system clock, since it was not counting while we were sleeping * We need to convert sleep duration from rtimer ticks to clock ticks and * this will cost us some accuracy */ clock_adjust((clock_time_t) ((RTIMER_NOW() - sleep_enter_time) / RTIMER_CLOCK_TICK_RATIO)); /* Restore system clock to the 32 MHz XOSC */ select_32_mhz_xosc(); /* Restore PMCTL to PM0 for next pass */ REG(SYS_CTRL_PMCTL) = SYS_CTRL_PMCTL_PM0; /* Remember IRQ energest for next pass */ ENERGEST_IRQ_SAVE(irq_energest); ENERGEST_ON(ENERGEST_TYPE_CPU); ENERGEST_OFF(ENERGEST_TYPE_LPM); }
/*---------------------------------------------------------------------------*/ void lpm_enter() { rtimer_clock_t lpm_exit_time; rtimer_clock_t duration; /* * If either the RF or the registered peripherals are on, dropping to PM1/2 * would equal pulling the rug (32MHz XOSC) from under their feet. Thus, we * only drop to PM0. PM0 is also used if max_pm==0. */ if((REG(RFCORE_XREG_FSMSTAT0) & RFCORE_XREG_FSMSTAT0_FSM_FFCTRL_STATE) != 0 || !periph_permit_pm1() || max_pm == 0) { enter_pm0(); /* We reach here when the interrupt context that woke us up has returned */ return; } /* * Registered peripherals were off. Radio was off: Some Duty Cycling in place. * rtimers run on the Sleep Timer. Thus, if we have a scheduled rtimer * task, a Sleep Timer interrupt will fire and will wake us up. * Choose the most suitable PM based on anticipated deep sleep duration */ lpm_exit_time = rtimer_arch_next_trigger(); duration = lpm_exit_time - RTIMER_NOW(); if(duration < DEEP_SLEEP_PM1_THRESHOLD || lpm_exit_time == 0) { /* Anticipated duration too short or no scheduled rtimer task. Use PM0 */ enter_pm0(); /* We reach here when the interrupt context that woke us up has returned */ return; } /* If we reach here, we -may- (but may as well not) be dropping to PM1+. We * know the registered peripherals and RF are off so we can switch to the * 16MHz RCOSC. */ select_16_mhz_rcosc(); /* * Switching the System Clock from the 32MHz XOSC to the 16MHz RC OSC may * have taken a while. Re-estimate sleep duration. */ duration = lpm_exit_time - RTIMER_NOW(); if(duration < DEEP_SLEEP_PM1_THRESHOLD) { /* * oops... The clock switch took some time and now the remaining sleep * duration is too short. Restore the clock source to the 32MHz XOSC and * abort the LPM attempt altogether. We can't drop to PM0, * we need to yield to main() since we may have events to service now. */ select_32_mhz_xosc(); return; } else if(duration >= DEEP_SLEEP_PM2_THRESHOLD && max_pm == 2) { /* Long sleep duration and PM2 is allowed. Use it */ REG(SYS_CTRL_PMCTL) = SYS_CTRL_PMCTL_PM2; } else { /* * Anticipated duration too short for PM2 but long enough for PM1 and we * are allowed to use PM1 */ REG(SYS_CTRL_PMCTL) = SYS_CTRL_PMCTL_PM1; } /* We are only interested in IRQ energest while idle or in LPM */ ENERGEST_IRQ_RESTORE(irq_energest); ENERGEST_OFF(ENERGEST_TYPE_CPU); ENERGEST_ON(ENERGEST_TYPE_LPM); /* Remember the current time so we can adjust the clock when we wake up */ sleep_enter_time = RTIMER_NOW(); /* * Last chance to abort entering Deep Sleep. * * - There is the slight off-chance that a SysTick interrupt fired while we * were trying to make up our mind. This may have raised an event. * - The Sleep Timer may have fired * * Check if there is still a scheduled rtimer task and check for pending * events before going to Deep Sleep */ if(process_nevents() || rtimer_arch_next_trigger() == 0) { /* Event flag raised or rtimer inactive. * Turn on the 32MHz XOSC, restore PMCTL and abort */ select_32_mhz_xosc(); REG(SYS_CTRL_PMCTL) = SYS_CTRL_PMCTL_PM0; } else { /* All clear. Assert WFI and drop to PM1/2. This is now un-interruptible */ assert_wfi(); } /* * We reach here after coming back from PM1/2. The interrupt context that * woke us up has returned. lpm_exit() has run, it has switched the system * clock source back to the 32MHz XOSC, it has adjusted the system clock, * it has restored PMCTL and it has done energest housekeeping */ return; }