CC26xx: fix a regression in and refactor LPM code
This commit is contained in:
parent
ef8c97f46d
commit
9caaf26012
|
@ -83,7 +83,7 @@ LIST(modules_list);
|
||||||
#define SLEEP_GUARD_TIME (RTIMER_SECOND / 1000) /* 1.0 ms */
|
#define SLEEP_GUARD_TIME (RTIMER_SECOND / 1000) /* 1.0 ms */
|
||||||
|
|
||||||
#define MAX_SLEEP_TIME RTIMER_SECOND
|
#define MAX_SLEEP_TIME RTIMER_SECOND
|
||||||
#define MINIMAL_SAFE_SCHEDULE 8u
|
#define MIN_SAFE_SCHEDULE 8u
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
/* 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);
|
||||||
|
@ -251,71 +251,71 @@ wake_up(void)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
static int
|
static uint8_t
|
||||||
setup_sleep_mode(rtimer_clock_t *next_timer)
|
check_next_rtimer(rtimer_clock_t now, rtimer_clock_t *next_rtimer, bool *next_rtimer_set)
|
||||||
|
{
|
||||||
|
uint8_t max_pm = LPM_MODE_MAX_SUPPORTED;
|
||||||
|
|
||||||
|
if(ti_lib_aon_rtc_channel_active(AON_RTC_CH0)) {
|
||||||
|
*next_rtimer_set = true;
|
||||||
|
|
||||||
|
/* 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)) {
|
||||||
|
max_pm = MIN(max_pm, LPM_MODE_AWAKE);
|
||||||
|
} else if(RTIMER_CLOCK_LT(*next_rtimer, now + STANDBY_MIN_DURATION)) {
|
||||||
|
max_pm = MIN(max_pm, LPM_MODE_SLEEP);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*next_rtimer_set = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return max_pm;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static uint8_t
|
||||||
|
check_next_etimer(rtimer_clock_t now, rtimer_clock_t *next_etimer, bool *next_etimer_set)
|
||||||
|
{
|
||||||
|
uint8_t max_pm = LPM_MODE_MAX_SUPPORTED;
|
||||||
|
|
||||||
|
*next_etimer_set = false;
|
||||||
|
|
||||||
|
/* Find out the time of the next etimer */
|
||||||
|
if(etimer_pending()) {
|
||||||
|
int32_t until_next_etimer = (int32_t)etimer_next_expiration_time() - (int32_t)clock_time();
|
||||||
|
if(until_next_etimer < 1) {
|
||||||
|
max_pm = MIN(max_pm, LPM_MODE_AWAKE);
|
||||||
|
} else {
|
||||||
|
*next_etimer_set = true;
|
||||||
|
*next_etimer = soc_rtc_last_isr_time() + (until_next_etimer * (RTIMER_SECOND / CLOCK_SECOND));
|
||||||
|
if(RTIMER_CLOCK_LT(*next_etimer, now + STANDBY_MIN_DURATION)) {
|
||||||
|
max_pm = MIN(max_pm, LPM_MODE_SLEEP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return max_pm;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static uint8_t
|
||||||
|
setup_sleep_mode(void)
|
||||||
{
|
{
|
||||||
lpm_registered_module_t *module;
|
lpm_registered_module_t *module;
|
||||||
uint8_t max_pm = LPM_MODE_MAX_SUPPORTED;
|
uint8_t max_pm = LPM_MODE_MAX_SUPPORTED;
|
||||||
|
uint8_t pm;
|
||||||
|
|
||||||
rtimer_clock_t now = RTIMER_NOW();
|
rtimer_clock_t now;
|
||||||
const rtimer_clock_t max_sleep = now + MAX_SLEEP_TIME;
|
rtimer_clock_t next_rtimer = 0;
|
||||||
|
rtimer_clock_t next_etimer = 0;
|
||||||
/* next_timer will hold the time of the next system wakeup due to a timer*/
|
bool next_rtimer_set = false;
|
||||||
*next_timer = max_sleep;
|
bool next_etimer_set = false;
|
||||||
|
|
||||||
/* Check if any events fired before we turned interrupts off. If so, abort */
|
/* Check if any events fired before we turned interrupts off. If so, abort */
|
||||||
if(LPM_MODE_MAX_SUPPORTED == LPM_MODE_AWAKE || process_nevents()) {
|
if(LPM_MODE_MAX_SUPPORTED == LPM_MODE_AWAKE || process_nevents()) {
|
||||||
return LPM_MODE_AWAKE;
|
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 */
|
/* Collect max allowed PM permission from interested modules */
|
||||||
for(module = list_head(modules_list); module != NULL;
|
for(module = list_head(modules_list); module != NULL;
|
||||||
module = module->next) {
|
module = module->next) {
|
||||||
|
@ -327,6 +327,60 @@ setup_sleep_mode(rtimer_clock_t *next_timer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
now = RTIMER_NOW();
|
||||||
|
|
||||||
|
pm = check_next_rtimer(now, &next_rtimer, &next_rtimer_set);
|
||||||
|
if(pm < max_pm) {
|
||||||
|
max_pm = pm;
|
||||||
|
}
|
||||||
|
pm = check_next_etimer(now, &next_etimer, &next_etimer_set);
|
||||||
|
if(pm < max_pm) {
|
||||||
|
max_pm = pm;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(max_pm == LPM_MODE_SLEEP) {
|
||||||
|
if(next_etimer_set) {
|
||||||
|
/* Schedule the next system wakeup due to etimer */
|
||||||
|
if(RTIMER_CLOCK_LT(next_etimer, now + MIN_SAFE_SCHEDULE)) {
|
||||||
|
/* Too soon in future, use this minimal interval instead */
|
||||||
|
next_etimer = now + MIN_SAFE_SCHEDULE;
|
||||||
|
} else if(RTIMER_CLOCK_LT(now + MAX_SLEEP_TIME, next_etimer)) {
|
||||||
|
/* Too far in future, use MAX_SLEEP_TIME instead */
|
||||||
|
next_etimer = now + MAX_SLEEP_TIME;
|
||||||
|
}
|
||||||
|
soc_rtc_schedule_one_shot(AON_RTC_CH1, next_etimer);
|
||||||
|
} else {
|
||||||
|
/* No etimers set. Since by default the CH1 RTC fires once every clock tick,
|
||||||
|
* need to explicitly schedule a wakeup in the future to save energy.
|
||||||
|
* But do not stay in this mode for too long, otherwise watchdog will be trigerred. */
|
||||||
|
soc_rtc_schedule_one_shot(AON_RTC_CH1, now + MAX_SLEEP_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(max_pm == LPM_MODE_DEEP_SLEEP) {
|
||||||
|
/* Watchdog is not enabled, so deep sleep can continue an arbitrary long time.
|
||||||
|
* On the other hand, if `CC2650_FAST_RADIO_STARTUP` is defined,
|
||||||
|
* early wakeup before the next rtimer should be scheduled. */
|
||||||
|
|
||||||
|
#if CC2650_FAST_RADIO_STARTUP
|
||||||
|
if(next_rtimer_set) {
|
||||||
|
if(!next_etimer_set || RTIMER_CLOCK_LT(next_rtimer - SLEEP_GUARD_TIME, next_etimer)) {
|
||||||
|
/* schedule a wakeup briefly before the next rtimer to wake up the system */
|
||||||
|
soc_rtc_schedule_one_shot(AON_RTC_CH2, next_rtimer - SLEEP_GUARD_TIME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(next_etimer_set) {
|
||||||
|
/* Schedule the next system wakeup due to etimer.
|
||||||
|
* No need to compare the `next_etimer` to `now` here as this branch
|
||||||
|
* is only entered when there's sufficient time for deep sleeping. */
|
||||||
|
soc_rtc_schedule_one_shot(AON_RTC_CH1, next_etimer);
|
||||||
|
} else {
|
||||||
|
/* Use the farthest possible wakeup time */
|
||||||
|
soc_rtc_schedule_one_shot(AON_RTC_CH1, now - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return max_pm;
|
return max_pm;
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
@ -350,16 +404,11 @@ lpm_sleep(void)
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
static void
|
static void
|
||||||
deep_sleep(rtimer_clock_t next_timer)
|
deep_sleep(void)
|
||||||
{
|
{
|
||||||
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.
|
||||||
|
@ -490,18 +539,17 @@ 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(&next_timer);
|
max_pm = setup_sleep_mode();
|
||||||
|
|
||||||
/* 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(next_timer);
|
deep_sleep();
|
||||||
}
|
}
|
||||||
|
|
||||||
ti_lib_int_master_enable();
|
ti_lib_int_master_enable();
|
||||||
|
|
Loading…
Reference in a new issue