cc26xx: implement CC2650_FAST_RADIO_STARTUP option, required for TSCH
This commit is contained in:
parent
a47fb723e4
commit
3a99639294
|
@ -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_CH0);
|
||||||
ti_lib_aon_rtc_event_clear(AON_RTC_CH1);
|
ti_lib_aon_rtc_event_clear(AON_RTC_CH1);
|
||||||
|
ti_lib_aon_rtc_event_clear(AON_RTC_CH2);
|
||||||
|
|
||||||
/* Setup the wakeup event */
|
/* 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_WU0, AON_EVENT_RTC_CH0);
|
||||||
ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU1, AON_EVENT_RTC_CH1);
|
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;
|
HWREG(AON_RTC_BASE + AON_RTC_O_SEC) = SOC_RTC_START_TICK_COUNT;
|
||||||
|
|
||||||
|
@ -123,7 +125,7 @@ soc_rtc_get_next_trigger()
|
||||||
void
|
void
|
||||||
soc_rtc_schedule_one_shot(uint32_t channel, uint32_t ticks)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +172,12 @@ soc_rtc_isr(void)
|
||||||
rtimer_run_next();
|
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);
|
ENERGEST_OFF(ENERGEST_TYPE_IRQ);
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
|
@ -77,12 +77,13 @@ LIST(modules_list);
|
||||||
* Don't consider standby mode if the next AON RTC event is scheduled to fire
|
* Don't consider standby mode if the next AON RTC event is scheduled to fire
|
||||||
* in less than STANDBY_MIN_DURATION rtimer ticks
|
* in less than STANDBY_MIN_DURATION rtimer ticks
|
||||||
*/
|
*/
|
||||||
#define STANDBY_MIN_DURATION (RTIMER_SECOND >> 11)
|
#define STANDBY_MIN_DURATION (RTIMER_SECOND / 100) /* 10.0 ms */
|
||||||
#define MINIMAL_SAFE_SCHEDUAL 8u
|
|
||||||
|
/* 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 MAX_SLEEP_TIME RTIMER_SECOND
|
||||||
#define DEFAULT_SLEEP_TIME RTIMER_SECOND
|
#define MINIMAL_SAFE_SCHEDULE 8u
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
#define CLK_TO_RT(c) ((c) * (RTIMER_SECOND / CLOCK_SECOND))
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
/* Prototype of a function in clock.c. Called every time we come out of DS */
|
/* Prototype of a function in clock.c. Called every time we come out of DS */
|
||||||
void clock_update(void);
|
void clock_update(void);
|
||||||
|
@ -241,14 +242,124 @@ wake_up(void)
|
||||||
module->wakeup();
|
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
|
static void
|
||||||
deep_sleep(void)
|
deep_sleep(rtimer_clock_t next_timer)
|
||||||
{
|
{
|
||||||
uint32_t domains = LOCKABLE_DOMAINS;
|
uint32_t domains = LOCKABLE_DOMAINS;
|
||||||
lpm_registered_module_t *module;
|
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
|
* Notify all registered modules that we are dropping to mode X. We do not
|
||||||
* need to do this for simple sleep.
|
* need to do this for simple sleep.
|
||||||
|
@ -304,7 +415,8 @@ deep_sleep(void)
|
||||||
* turn back off.
|
* turn back off.
|
||||||
*
|
*
|
||||||
* If the radio is on, we won't even reach here, and if it's off the HF
|
* 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.
|
* Nevertheless, request the switch to the HF RC explicitly here.
|
||||||
*/
|
*/
|
||||||
|
@ -370,131 +482,32 @@ deep_sleep(void)
|
||||||
* unpending events so the handlers can fire
|
* unpending events so the handlers can fire
|
||||||
*/
|
*/
|
||||||
wake_up();
|
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;
|
ti_lib_int_master_enable();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
void
|
void
|
||||||
lpm_drop()
|
lpm_drop()
|
||||||
{
|
{
|
||||||
uint8_t max_pm;
|
uint8_t max_pm;
|
||||||
|
rtimer_clock_t next_timer;
|
||||||
|
|
||||||
/* Critical. Don't get interrupted! */
|
/* Critical. Don't get interrupted! */
|
||||||
ti_lib_int_master_disable();
|
ti_lib_int_master_disable();
|
||||||
|
|
||||||
max_pm = setup_sleep_mode();
|
max_pm = setup_sleep_mode(&next_timer);
|
||||||
|
|
||||||
/* Drop */
|
/* Drop */
|
||||||
if(max_pm == LPM_MODE_SLEEP) {
|
if(max_pm == LPM_MODE_SLEEP) {
|
||||||
lpm_sleep();
|
lpm_sleep();
|
||||||
} else if(max_pm == LPM_MODE_DEEP_SLEEP) {
|
} else if(max_pm == LPM_MODE_DEEP_SLEEP) {
|
||||||
deep_sleep();
|
deep_sleep(next_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
ti_lib_int_master_enable();
|
ti_lib_int_master_enable();
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
void
|
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)
|
lpm_register_module(lpm_registered_module_t *module)
|
||||||
{
|
{
|
||||||
list_add(modules_list, module);
|
list_add(modules_list, module);
|
||||||
|
@ -512,7 +525,7 @@ lpm_init()
|
||||||
list_init(modules_list);
|
list_init(modules_list);
|
||||||
|
|
||||||
/* Always wake up on any DIO edge detection */
|
/* 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
|
void
|
||||||
|
|
|
@ -149,6 +149,11 @@ main(void)
|
||||||
/* Set the LF XOSC as the LF system clock source */
|
/* Set the LF XOSC as the LF system clock source */
|
||||||
oscillators_select_lf_xosc();
|
oscillators_select_lf_xosc();
|
||||||
|
|
||||||
|
#if CC2650_FAST_RADIO_STARTUP
|
||||||
|
/* Also request HF XOSC to start up */
|
||||||
|
oscillators_request_hf_xosc();
|
||||||
|
#endif
|
||||||
|
|
||||||
lpm_init();
|
lpm_init();
|
||||||
|
|
||||||
board_init();
|
board_init();
|
||||||
|
|
Loading…
Reference in a new issue