cc2538: lpm: Add registration mechanism for peripherals

Some peripherals have their clocks automatically gated in PM1+ modes, so they
cannot operate. This new mechanism gives peripherals a way to prohibit PM1+
modes so that they can properly complete their current operations before
entering PM1+.

This mechanism is implemented with peripheral functions registered to the LPM
module. These functions return whether the associated peripheral permits or not
PM1+ modes. They are called by the LPM module each time PM1+ might be possible.
If any of the peripherals wants to block PM1+, then the system is only dropped
to PM0.

Partly from: George Oikonomou
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
This commit is contained in:
Benoît Thébaudeau 2013-11-15 16:21:34 +01:00
parent a6227e1e3e
commit d35732505b
3 changed files with 71 additions and 6 deletions

View file

@ -43,8 +43,10 @@
#include "dev/rfcore-xreg.h"
#include "dev/usb-regs.h"
#include "rtimer-arch.h"
#include "lpm.h"
#include "reg.h"
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
/*---------------------------------------------------------------------------*/
@ -97,6 +99,30 @@ void clock_adjust(clock_time_t ticks);
/* Stores the currently specified MAX allowed PM */
static uint8_t max_pm;
/*---------------------------------------------------------------------------*/
/* Buffer to store peripheral PM1+ permission FPs */
#ifdef LPM_CONF_PERIPH_PERMIT_PM1_FUNCS_MAX
#define LPM_PERIPH_PERMIT_PM1_FUNCS_MAX LPM_CONF_PERIPH_PERMIT_PM1_FUNCS_MAX
#else
#define LPM_PERIPH_PERMIT_PM1_FUNCS_MAX 0
#endif
lpm_periph_permit_pm1_func_t
periph_permit_pm1_funcs[LPM_PERIPH_PERMIT_PM1_FUNCS_MAX];
/*---------------------------------------------------------------------------*/
static bool
periph_permit_pm1(void)
{
int i;
for(i = 0; i < LPM_PERIPH_PERMIT_PM1_FUNCS_MAX &&
periph_permit_pm1_funcs[i] != NULL; i++) {
if(!periph_permit_pm1_funcs[i]()) {
return false;
}
}
return true;
}
/*---------------------------------------------------------------------------*/
/*
* Routine to put is in PM0. We also need to do some housekeeping if the stats
* or the energest module is enabled
@ -191,15 +217,15 @@ lpm_enter()
rtimer_clock_t duration;
/*
* If either the RF or the USB is 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 either the RF, the USB 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.
*
* Note: USB Suspend/Resume/Remote Wake-Up are not supported. Once the PLL is
* on, it stays on.
*/
if((REG(RFCORE_XREG_FSMSTAT0) & RFCORE_XREG_FSMSTAT0_FSM_FFCTRL_STATE) != 0
|| REG(USB_CTRL) != 0 || max_pm == 0) {
|| REG(USB_CTRL) != 0 || !periph_permit_pm1() || max_pm == 0) {
enter_pm0();
/* We reach here when the interrupt context that woke us up has returned */
@ -207,7 +233,8 @@ lpm_enter()
}
/*
* USB PLL was off. Radio was off: Some Duty Cycling in place.
* Registered peripherals were off. USB PLL was 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
@ -224,7 +251,8 @@ lpm_enter()
}
/* If we reach here, we -may- (but may as well not) be dropping to PM1+. We
* know the USB and RF are off so we can switch to the 16MHz RCOSC. */
* know the registered peripherals, USB and RF are off so we can switch to the
* 16MHz RCOSC. */
select_16_mhz_rcosc();
/*
@ -299,6 +327,21 @@ lpm_set_max_pm(uint8_t pm)
}
/*---------------------------------------------------------------------------*/
void
lpm_register_peripheral(lpm_periph_permit_pm1_func_t permit_pm1_func)
{
int i;
for(i = 0; i < LPM_PERIPH_PERMIT_PM1_FUNCS_MAX; i++) {
if(periph_permit_pm1_funcs[i] == permit_pm1_func) {
break;
} else if(periph_permit_pm1_funcs[i] == NULL) {
periph_permit_pm1_funcs[i] = permit_pm1_func;
break;
}
}
}
/*---------------------------------------------------------------------------*/
void
lpm_init()
{
/*