/*---------------------------------------------------------------------------*/ void lpm_drop() { lpm_registered_module_t *module; uint8_t max_pm = LPM_MODE_MAX_SUPPORTED; uint8_t module_pm; clock_time_t next_event; uint32_t domains = LOCKABLE_DOMAINS; if(RTIMER_CLOCK_LT(soc_rtc_get_next_trigger(), RTIMER_NOW() + STANDBY_MIN_DURATION)) { lpm_sleep(); return; } /* Collect max allowed PM permission from interested modules */ for(module = list_head(modules_list); module != NULL; module = module->next) { if(module->request_max_pm) { module_pm = module->request_max_pm(); if(module_pm < max_pm) { max_pm = module_pm; } } } /* Check if any events fired during this process. Last chance to abort */ if(process_nevents()) { return; } /* Drop */ if(max_pm == LPM_MODE_SLEEP) { lpm_sleep(); } else { /* Critical. Don't get interrupted! */ ti_lib_int_master_disable(); /* * Reschedule AON RTC CH1 to fire an event N ticks before the next etimer * event */ next_event = etimer_next_expiration_time(); if(next_event) { next_event = next_event - clock_time(); soc_rtc_schedule_one_shot(AON_RTC_CH1, RTIMER_NOW() + (next_event * (RTIMER_SECOND / CLOCK_SECOND))); } /* * Notify all registered modules that we are dropping to mode X. We do not * need to do this for simple sleep. * * This is a chance for modules to delay us a little bit until an ongoing * operation has finished (e.g. uart TX) or to configure themselves for * deep sleep. * * At this stage, we also collect power domain locks, if any. * The argument to PRCMPowerDomainOff() is a bitwise OR, so every time * we encounter a lock we just clear the respective bits in the 'domains' * variable as required by the lock. In the end the domains variable will * just hold whatever has not been cleared */ for(module = list_head(modules_list); module != NULL; module = module->next) { if(module->shutdown) { module->shutdown(max_pm); } /* Clear the bits specified in the lock */ domains &= ~module->domain_lock; } /* Pat the dog: We don't want it to shout right after we wake up */ watchdog_periodic(); /* Clear unacceptable bits, just in case a lock provided a bad value */ domains &= LOCKABLE_DOMAINS; /* * Freeze the IOs on the boundary between MCU and AON. We only do this if * PERIPH is not needed */ if(domains & PRCM_DOMAIN_PERIPH) { ti_lib_aon_ioc_freeze_enable(); } /* * Among LOCKABLE_DOMAINS, turn off those that are not locked * * If domains is != 0, pass it as-is */ if(domains) { ti_lib_prcm_power_domain_off(domains); } /* * Before entering Deep Sleep, we must switch off the HF XOSC. The HF XOSC * is predominantly controlled by the RF driver. In a build with radio * cycling (e.g. ContikiMAC), the RF driver will request the XOSC before * using the Freq. Synth, and switch back to the RC when it is about to * turn back off. * * If the radio is on, we won't even reach here, and if it's off the HF * clock source should already be the HF RC. * * Nevertheless, request the switch to the HF RC explicitly here. */ oscillators_switch_to_hf_rc(); /* Configure clock sources for MCU and AUX: No clock */ ti_lib_aon_wuc_mcu_power_down_config(AONWUC_NO_CLOCK); ti_lib_aon_wuc_aux_power_down_config(AONWUC_NO_CLOCK); /* Full RAM retention. */ ti_lib_aon_wuc_mcu_sram_config(MCU_RAM0_RETENTION | MCU_RAM1_RETENTION | MCU_RAM2_RETENTION | MCU_RAM3_RETENTION); /* Disable retention of AUX RAM */ ti_lib_aon_wuc_aux_sram_config(false); /* * Always turn off RFCORE, CPU, SYSBUS and VIMS. RFCORE should be off * already */ ti_lib_prcm_power_domain_off(PRCM_DOMAIN_RFCORE | PRCM_DOMAIN_CPU | PRCM_DOMAIN_VIMS | PRCM_DOMAIN_SYSBUS); /* Request JTAG domain power off */ ti_lib_aon_wuc_jtag_power_off(); /* Turn off AUX */ ti_lib_aux_wuc_power_ctrl(AUX_WUC_POWER_OFF); ti_lib_aon_wuc_domain_power_down_enable(); while(ti_lib_aon_wuc_power_status_get() & AONWUC_AUX_POWER_ON); /* Configure the recharge controller */ ti_lib_sys_ctrl_set_recharge_before_power_down(XOSC_IN_HIGH_POWER_MODE); /* * If both PERIPH and SERIAL PDs are off, request the uLDO as the power * source while in deep sleep. */ if(domains == LOCKABLE_DOMAINS) { ti_lib_pwr_ctrl_source_set(PWRCTRL_PWRSRC_ULDO); } /* We are only interested in IRQ energest while idle or in LPM */ ENERGEST_IRQ_RESTORE(irq_energest); ENERGEST_SWITCH(ENERGEST_TYPE_CPU, ENERGEST_TYPE_LPM); /* Sync the AON interface to ensure all writes have gone through. */ ti_lib_sys_ctrl_aon_sync(); /* * Explicitly turn off VIMS cache, CRAM and TRAM. Needed because of * retention mismatch between VIMS logic and cache. We wait to do this * until right before deep sleep to be able to use the cache for as long * as possible. */ ti_lib_prcm_cache_retention_disable(); ti_lib_vims_mode_set(VIMS_BASE, VIMS_MODE_OFF); /* Deep Sleep */ ti_lib_prcm_deep_sleep(); /* * When we reach here, some interrupt woke us up. The global interrupt * flag is off, hence we have a chance to run things here. We will wake up * the chip properly, and then we will enable the global interrupt without * unpending events so the handlers can fire */ wake_up(); ti_lib_int_master_enable(); } }
/** * \brief Schedules an rtimer task to be triggered at time t * \param t The time when the task will need executed. * * \e t is an absolute time, in other words the task will be executed AT * time \e t, not IN \e t rtimer ticks. * * This function schedules a one-shot event with the AON RTC. * * This functions converts \e to a value suitable for the AON RTC. */ void rtimer_arch_schedule(rtimer_clock_t t) { /* Convert the rtimer tick value to a value suitable for the AON RTC */ soc_rtc_schedule_one_shot(AON_RTC_CH0, t); }