cc26xx: implement CC2650_FAST_RADIO_STARTUP option, required for TSCH

This commit is contained in:
Atis Elsts 2016-04-25 18:08:34 +03:00
parent a47fb723e4
commit 3a99639294
3 changed files with 139 additions and 113 deletions

View file

@ -81,11 +81,13 @@ soc_rtc_init(void)
ti_lib_aon_rtc_event_clear(AON_RTC_CH0);
ti_lib_aon_rtc_event_clear(AON_RTC_CH1);
ti_lib_aon_rtc_event_clear(AON_RTC_CH2);
/* Setup the wakeup event */
ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU0, AON_EVENT_RTC_CH0);
ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU1, AON_EVENT_RTC_CH1);
ti_lib_aon_rtc_combined_event_config(AON_RTC_CH0 | AON_RTC_CH1);
ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU2, AON_EVENT_RTC_CH2);
ti_lib_aon_rtc_combined_event_config(AON_RTC_CH0 | AON_RTC_CH1 | AON_RTC_CH2);
HWREG(AON_RTC_BASE + AON_RTC_O_SEC) = SOC_RTC_START_TICK_COUNT;
@ -123,7 +125,7 @@ soc_rtc_get_next_trigger()
void
soc_rtc_schedule_one_shot(uint32_t channel, uint32_t ticks)
{
if((channel != AON_RTC_CH0) && (channel != AON_RTC_CH1)) {
if((channel != AON_RTC_CH0) && (channel != AON_RTC_CH1) && (channel != AON_RTC_CH2)) {
return;
}
@ -170,6 +172,12 @@ soc_rtc_isr(void)
rtimer_run_next();
}
if(ti_lib_aon_rtc_event_get(AON_RTC_CH2)) {
/* after sleep; since a rtimer is already scheduled, do nothing */
ti_lib_aon_rtc_channel_disable(AON_RTC_CH2);
HWREG(AON_RTC_BASE + AON_RTC_O_EVFLAGS) = AON_RTC_EVFLAGS_CH2;
}
ENERGEST_OFF(ENERGEST_TYPE_IRQ);
}
/*---------------------------------------------------------------------------*/

View file

@ -77,12 +77,13 @@ LIST(modules_list);
* Don't consider standby mode if the next AON RTC event is scheduled to fire
* in less than STANDBY_MIN_DURATION rtimer ticks
*/
#define STANDBY_MIN_DURATION (RTIMER_SECOND >> 11)
#define MINIMAL_SAFE_SCHEDUAL 8u
#define STANDBY_MIN_DURATION (RTIMER_SECOND / 100) /* 10.0 ms */
/* Wake up this much time earlier before the next rtimer */
#define SLEEP_GUARD_TIME (RTIMER_SECOND / 1000) /* 1.0 ms */
#define MAX_SLEEP_TIME RTIMER_SECOND
#define DEFAULT_SLEEP_TIME RTIMER_SECOND
/*---------------------------------------------------------------------------*/
#define CLK_TO_RT(c) ((c) * (RTIMER_SECOND / CLOCK_SECOND))
#define MINIMAL_SAFE_SCHEDULE 8u
/*---------------------------------------------------------------------------*/
/* Prototype of a function in clock.c. Called every time we come out of DS */
void clock_update(void);
@ -241,14 +242,124 @@ wake_up(void)
module->wakeup();
}
}
#if CC2650_FAST_RADIO_STARTUP
/*
* Trigger a switch to the XOSC, so that we can subsequently use the RF FS
*/
oscillators_request_hf_xosc();
#endif
}
/*---------------------------------------------------------------------------*/
static int
setup_sleep_mode(rtimer_clock_t *next_timer)
{
lpm_registered_module_t *module;
uint8_t max_pm = LPM_MODE_MAX_SUPPORTED;
rtimer_clock_t now = RTIMER_NOW();
const rtimer_clock_t max_sleep = now + MAX_SLEEP_TIME;
/* next_timer will hold the time of the next system wakeup due to a timer*/
*next_timer = max_sleep;
/* Check if any events fired before we turned interrupts off. If so, abort */
if(LPM_MODE_MAX_SUPPORTED == LPM_MODE_AWAKE || process_nevents()) {
return LPM_MODE_AWAKE;
}
if(ti_lib_aon_rtc_channel_active(AON_RTC_CH0)) {
rtimer_clock_t next_rtimer;
/* find out the timer of the next rtimer interrupt */
next_rtimer = ti_lib_aon_rtc_compare_value_get(AON_RTC_CH0);
if(RTIMER_CLOCK_LT(next_rtimer, now + 2)) {
return LPM_MODE_AWAKE;
}
if(RTIMER_CLOCK_LT(next_rtimer, now + STANDBY_MIN_DURATION)) {
return LPM_MODE_SLEEP;
}
*next_timer = next_rtimer;
}
/* also find out the timer of the next etimer */
if(etimer_pending()) {
int32_t until_next_etimer;
rtimer_clock_t next_etimer;
until_next_etimer = (int32_t)etimer_next_expiration_time() - (int32_t)clock_time();
if(until_next_etimer < 1) {
return LPM_MODE_AWAKE;
}
next_etimer = soc_rtc_last_isr_time() + (until_next_etimer * (RTIMER_SECOND / CLOCK_SECOND));
if(RTIMER_CLOCK_LT(next_etimer, now + STANDBY_MIN_DURATION)) {
/* ensure that we schedule sleep a minimal number of ticks into the
future */
soc_rtc_schedule_one_shot(AON_RTC_CH1, now + MINIMAL_SAFE_SCHEDULE);
return LPM_MODE_SLEEP;
}
if(RTIMER_CLOCK_LT(max_sleep, next_etimer)) {
/* if max_pm is LPM_MODE_SLEEP, we could trigger the watchdog if we slept
for too long. */
if(RTIMER_CLOCK_LT(max_sleep, *next_timer)) {
soc_rtc_schedule_one_shot(AON_RTC_CH1, max_sleep);
}
} else {
/* Reschedule AON RTC CH1 to fire just in time for the next etimer event */
soc_rtc_schedule_one_shot(AON_RTC_CH1, next_etimer);
}
if(RTIMER_CLOCK_LT(next_etimer, *next_timer)) {
/* set `next_timer` to the time the first etimer fires */
*next_timer = next_etimer;
}
}
/* Collect max allowed PM permission from interested modules */
for(module = list_head(modules_list); module != NULL;
module = module->next) {
if(module->request_max_pm) {
uint8_t module_pm = module->request_max_pm();
if(module_pm < max_pm) {
max_pm = module_pm;
}
}
}
return max_pm;
}
/*---------------------------------------------------------------------------*/
void
lpm_sleep(void)
{
ENERGEST_SWITCH(ENERGEST_TYPE_CPU, ENERGEST_TYPE_LPM);
/* We are only interested in IRQ energest while idle or in LPM */
ENERGEST_IRQ_RESTORE(irq_energest);
/* Just to be on the safe side, explicitly disable Deep Sleep */
HWREG(NVIC_SYS_CTRL) &= ~(NVIC_SYS_CTRL_SLEEPDEEP);
ti_lib_prcm_sleep();
/* Remember IRQ energest for next pass */
ENERGEST_IRQ_SAVE(irq_energest);
ENERGEST_SWITCH(ENERGEST_TYPE_LPM, ENERGEST_TYPE_CPU);
}
/*---------------------------------------------------------------------------*/
static void
deep_sleep(void)
deep_sleep(rtimer_clock_t next_timer)
{
uint32_t domains = LOCKABLE_DOMAINS;
lpm_registered_module_t *module;
#if CC2650_FAST_RADIO_STARTUP
/* schedule a wakeup briefly before the next etimer/rtimer to wake up the system */
soc_rtc_schedule_one_shot(AON_RTC_CH2, next_timer - SLEEP_GUARD_TIME);
#endif
/*
* Notify all registered modules that we are dropping to mode X. We do not
* need to do this for simple sleep.
@ -304,7 +415,8 @@ deep_sleep(void)
* 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.
* clock source should already be the HF RC, unless CC2650_FAST_RADIO_STARTUP
* is defined.
*
* Nevertheless, request the switch to the HF RC explicitly here.
*/
@ -370,131 +482,32 @@ deep_sleep(void)
* unpending events so the handlers can fire
*/
wake_up();
}
/*---------------------------------------------------------------------------*/
static void
safe_schedule_rtimer(rtimer_clock_t time, rtimer_clock_t now, int pm)
{
rtimer_clock_t min_sleep;
rtimer_clock_t max_sleep;
min_sleep = now + MINIMAL_SAFE_SCHEDUAL;
max_sleep = now + MAX_SLEEP_TIME;
if(RTIMER_CLOCK_LT(time, min_sleep)) {
/* ensure that we schedule sleep a minimal number of ticks into the
future */
soc_rtc_schedule_one_shot(AON_RTC_CH1, min_sleep);
} else if((pm == LPM_MODE_SLEEP) && RTIMER_CLOCK_LT(max_sleep, time)) {
/* if max_pm is LPM_MODE_SLEEP, we could trigger the watchdog if we slept
for too long. */
soc_rtc_schedule_one_shot(AON_RTC_CH1, max_sleep);
} else {
soc_rtc_schedule_one_shot(AON_RTC_CH1, time);
}
}
/*---------------------------------------------------------------------------*/
static int
setup_sleep_mode(void)
{
rtimer_clock_t et_distance = 0;
lpm_registered_module_t *module;
int max_pm;
int module_pm;
int etimer_is_pending;
rtimer_clock_t now;
rtimer_clock_t et_time;
rtimer_clock_t next_trig;
max_pm = LPM_MODE_MAX_SUPPORTED;
now = RTIMER_NOW();
if((LPM_MODE_MAX_SUPPORTED == LPM_MODE_AWAKE) || process_nevents()) {
return LPM_MODE_AWAKE;
}
etimer_is_pending = etimer_pending();
if(etimer_is_pending) {
et_distance = CLK_TO_RT(etimer_next_expiration_time() - clock_time());
if(RTIMER_CLOCK_LT(et_distance, 1)) {
/* there is an etimer which is already expired; we shouldn't go to
sleep at all */
return LPM_MODE_AWAKE;
}
}
next_trig = soc_rtc_get_next_trigger();
if(RTIMER_CLOCK_LT(next_trig, now + STANDBY_MIN_DURATION)) {
return LPM_MODE_SLEEP;
}
/* 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;
}
}
}
/* Reschedule AON RTC CH1 to fire just in time for the next etimer event */
if(etimer_is_pending) {
et_time = soc_rtc_last_isr_time() + et_distance;
safe_schedule_rtimer(et_time, now, max_pm);
} else {
/* set a maximal sleep period if no etimers are queued */
soc_rtc_schedule_one_shot(AON_RTC_CH1, now + DEFAULT_SLEEP_TIME);
}
return max_pm;
ti_lib_int_master_enable();
}
/*---------------------------------------------------------------------------*/
void
lpm_drop()
{
uint8_t max_pm;
rtimer_clock_t next_timer;
/* Critical. Don't get interrupted! */
ti_lib_int_master_disable();
max_pm = setup_sleep_mode();
max_pm = setup_sleep_mode(&next_timer);
/* Drop */
if(max_pm == LPM_MODE_SLEEP) {
lpm_sleep();
} else if(max_pm == LPM_MODE_DEEP_SLEEP) {
deep_sleep();
deep_sleep(next_timer);
}
ti_lib_int_master_enable();
}
/*---------------------------------------------------------------------------*/
void
lpm_sleep(void)
{
ENERGEST_SWITCH(ENERGEST_TYPE_CPU, ENERGEST_TYPE_LPM);
/* We are only interested in IRQ energest while idle or in LPM */
ENERGEST_IRQ_RESTORE(irq_energest);
/* Just to be on the safe side, explicitly disable Deep Sleep */
HWREG(NVIC_SYS_CTRL) &= ~(NVIC_SYS_CTRL_SLEEPDEEP);
ti_lib_prcm_sleep();
/* Remember IRQ energest for next pass */
ENERGEST_IRQ_SAVE(irq_energest);
ENERGEST_SWITCH(ENERGEST_TYPE_LPM, ENERGEST_TYPE_CPU);
}
/*---------------------------------------------------------------------------*/
void
lpm_register_module(lpm_registered_module_t *module)
{
list_add(modules_list, module);
@ -512,7 +525,7 @@ lpm_init()
list_init(modules_list);
/* Always wake up on any DIO edge detection */
ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU2, AON_EVENT_IO);
ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU3, AON_EVENT_IO);
}
/*---------------------------------------------------------------------------*/
void

View file

@ -149,6 +149,11 @@ main(void)
/* Set the LF XOSC as the LF system clock source */
oscillators_select_lf_xosc();
#if CC2650_FAST_RADIO_STARTUP
/* Also request HF XOSC to start up */
oscillators_request_hf_xosc();
#endif
lpm_init();
board_init();