cc2538: lpm: Fix RTIMER_NOW() upon wake-up
When returning from PM1/2, the sleep timer value (used by RTIMER_NOW()) is not up-to-date until a positive edge on the 32-kHz clock has been detected after the system clock restarted. To ensure an updated value is read, wait for a positive transition on the 32-kHz clock by polling the SYS_CTRL_CLOCK_STA.SYNC_32K bit, before reading the sleep timer value. Because of this RTIMER_NOW() fixup, lpm_exit() has to be called at the very beginning of ISRs waking up the SoC. This also ensures that all clocks and timers are enabled at the correct frequency and updated before using them following wake-up. Without this fix, etimers could sometimes (randomly, depending on timings) become ultra slow (observed from 10x to 40x slower than normal) if the system exited PM1/2 very often. This issue occurred more often with PM1. Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
This commit is contained in:
parent
f149197aa8
commit
5261bb861d
|
@ -83,10 +83,10 @@ notify(uint8_t mask, uint8_t port)
|
||||||
void
|
void
|
||||||
gpio_port_a_isr()
|
gpio_port_a_isr()
|
||||||
{
|
{
|
||||||
ENERGEST_ON(ENERGEST_TYPE_IRQ);
|
|
||||||
|
|
||||||
lpm_exit();
|
lpm_exit();
|
||||||
|
|
||||||
|
ENERGEST_ON(ENERGEST_TYPE_IRQ);
|
||||||
|
|
||||||
notify(REG(GPIO_A_BASE | GPIO_MIS), GPIO_A_NUM);
|
notify(REG(GPIO_A_BASE | GPIO_MIS), GPIO_A_NUM);
|
||||||
|
|
||||||
GPIO_CLEAR_INTERRUPT(GPIO_A_BASE, 0xFF);
|
GPIO_CLEAR_INTERRUPT(GPIO_A_BASE, 0xFF);
|
||||||
|
@ -99,10 +99,10 @@ gpio_port_a_isr()
|
||||||
void
|
void
|
||||||
gpio_port_b_isr()
|
gpio_port_b_isr()
|
||||||
{
|
{
|
||||||
ENERGEST_ON(ENERGEST_TYPE_IRQ);
|
|
||||||
|
|
||||||
lpm_exit();
|
lpm_exit();
|
||||||
|
|
||||||
|
ENERGEST_ON(ENERGEST_TYPE_IRQ);
|
||||||
|
|
||||||
notify(REG(GPIO_B_BASE | GPIO_MIS), GPIO_B_NUM);
|
notify(REG(GPIO_B_BASE | GPIO_MIS), GPIO_B_NUM);
|
||||||
|
|
||||||
GPIO_CLEAR_INTERRUPT(GPIO_B_BASE, 0xFF);
|
GPIO_CLEAR_INTERRUPT(GPIO_B_BASE, 0xFF);
|
||||||
|
@ -115,10 +115,10 @@ gpio_port_b_isr()
|
||||||
void
|
void
|
||||||
gpio_port_c_isr()
|
gpio_port_c_isr()
|
||||||
{
|
{
|
||||||
ENERGEST_ON(ENERGEST_TYPE_IRQ);
|
|
||||||
|
|
||||||
lpm_exit();
|
lpm_exit();
|
||||||
|
|
||||||
|
ENERGEST_ON(ENERGEST_TYPE_IRQ);
|
||||||
|
|
||||||
notify(REG(GPIO_C_BASE | GPIO_MIS), GPIO_C_NUM);
|
notify(REG(GPIO_C_BASE | GPIO_MIS), GPIO_C_NUM);
|
||||||
|
|
||||||
GPIO_CLEAR_INTERRUPT(GPIO_C_BASE, 0xFF);
|
GPIO_CLEAR_INTERRUPT(GPIO_C_BASE, 0xFF);
|
||||||
|
@ -131,10 +131,10 @@ gpio_port_c_isr()
|
||||||
void
|
void
|
||||||
gpio_port_d_isr()
|
gpio_port_d_isr()
|
||||||
{
|
{
|
||||||
ENERGEST_ON(ENERGEST_TYPE_IRQ);
|
|
||||||
|
|
||||||
lpm_exit();
|
lpm_exit();
|
||||||
|
|
||||||
|
ENERGEST_ON(ENERGEST_TYPE_IRQ);
|
||||||
|
|
||||||
notify(REG(GPIO_D_BASE | GPIO_MIS), GPIO_D_NUM);
|
notify(REG(GPIO_D_BASE | GPIO_MIS), GPIO_D_NUM);
|
||||||
|
|
||||||
GPIO_CLEAR_INTERRUPT(GPIO_D_BASE, 0xFF);
|
GPIO_CLEAR_INTERRUPT(GPIO_D_BASE, 0xFF);
|
||||||
|
|
|
@ -199,6 +199,16 @@ lpm_exit()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When returning from PM1/2, the sleep timer value (used by RTIMER_NOW()) is
|
||||||
|
* not up-to-date until a positive edge on the 32-kHz clock has been detected
|
||||||
|
* after the system clock restarted. To ensure an updated value is read, wait
|
||||||
|
* for a positive transition on the 32-kHz clock by polling the
|
||||||
|
* SYS_CTRL_CLOCK_STA.SYNC_32K bit, before reading the sleep timer value.
|
||||||
|
*/
|
||||||
|
while(REG(SYS_CTRL_CLOCK_STA) & SYS_CTRL_CLOCK_STA_SYNC_32K);
|
||||||
|
while(!(REG(SYS_CTRL_CLOCK_STA) & SYS_CTRL_CLOCK_STA_SYNC_32K));
|
||||||
|
|
||||||
LPM_STATS_ADD(REG(SYS_CTRL_PMCTL) & SYS_CTRL_PMCTL_PM3,
|
LPM_STATS_ADD(REG(SYS_CTRL_PMCTL) & SYS_CTRL_PMCTL_PM3,
|
||||||
RTIMER_NOW() - sleep_enter_time);
|
RTIMER_NOW() - sleep_enter_time);
|
||||||
|
|
||||||
|
|
|
@ -150,6 +150,12 @@ void lpm_enter(void);
|
||||||
* interrupt. This may lead to other parts of the code trying to use the RF,
|
* interrupt. This may lead to other parts of the code trying to use the RF,
|
||||||
* so we need to switch the clock source \e before said code gets executed.
|
* so we need to switch the clock source \e before said code gets executed.
|
||||||
*
|
*
|
||||||
|
* This function also makes sure that the sleep timer value is up-to-date
|
||||||
|
* following wake-up from PM1/2 so that RTIMER_NOW() works.
|
||||||
|
*
|
||||||
|
* \note This function should be called at the very beginning of ISRs waking up
|
||||||
|
* the SoC in order to restore all clocks and timers.
|
||||||
|
*
|
||||||
* \sa lpm_enter(), rtimer_isr()
|
* \sa lpm_enter(), rtimer_isr()
|
||||||
*/
|
*/
|
||||||
void lpm_exit(void);
|
void lpm_exit(void);
|
||||||
|
|
|
@ -136,13 +136,6 @@ rtimer_arch_now()
|
||||||
void
|
void
|
||||||
rtimer_isr()
|
rtimer_isr()
|
||||||
{
|
{
|
||||||
ENERGEST_ON(ENERGEST_TYPE_IRQ);
|
|
||||||
|
|
||||||
next_trigger = 0;
|
|
||||||
|
|
||||||
nvic_interrupt_unpend(NVIC_INT_SM_TIMER);
|
|
||||||
nvic_interrupt_disable(NVIC_INT_SM_TIMER);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we were in PM1+, call the wake-up sequence first. This will make sure
|
* If we were in PM1+, call the wake-up sequence first. This will make sure
|
||||||
* that the 32MHz OSC is selected as the clock source. We need to do this
|
* that the 32MHz OSC is selected as the clock source. We need to do this
|
||||||
|
@ -150,6 +143,13 @@ rtimer_isr()
|
||||||
*/
|
*/
|
||||||
lpm_exit();
|
lpm_exit();
|
||||||
|
|
||||||
|
ENERGEST_ON(ENERGEST_TYPE_IRQ);
|
||||||
|
|
||||||
|
next_trigger = 0;
|
||||||
|
|
||||||
|
nvic_interrupt_unpend(NVIC_INT_SM_TIMER);
|
||||||
|
nvic_interrupt_disable(NVIC_INT_SM_TIMER);
|
||||||
|
|
||||||
rtimer_run_next();
|
rtimer_run_next();
|
||||||
|
|
||||||
ENERGEST_OFF(ENERGEST_TYPE_IRQ);
|
ENERGEST_OFF(ENERGEST_TYPE_IRQ);
|
||||||
|
|
Loading…
Reference in a new issue