diff --git a/cpu/cc2538/dev/gpio.c b/cpu/cc2538/dev/gpio.c index 111946001..2961a2eb0 100644 --- a/cpu/cc2538/dev/gpio.c +++ b/cpu/cc2538/dev/gpio.c @@ -83,10 +83,10 @@ notify(uint8_t mask, uint8_t port) void gpio_port_a_isr() { - ENERGEST_ON(ENERGEST_TYPE_IRQ); - lpm_exit(); + ENERGEST_ON(ENERGEST_TYPE_IRQ); + notify(REG(GPIO_A_BASE | GPIO_MIS), GPIO_A_NUM); GPIO_CLEAR_INTERRUPT(GPIO_A_BASE, 0xFF); @@ -99,10 +99,10 @@ gpio_port_a_isr() void gpio_port_b_isr() { - ENERGEST_ON(ENERGEST_TYPE_IRQ); - lpm_exit(); + ENERGEST_ON(ENERGEST_TYPE_IRQ); + notify(REG(GPIO_B_BASE | GPIO_MIS), GPIO_B_NUM); GPIO_CLEAR_INTERRUPT(GPIO_B_BASE, 0xFF); @@ -115,10 +115,10 @@ gpio_port_b_isr() void gpio_port_c_isr() { - ENERGEST_ON(ENERGEST_TYPE_IRQ); - lpm_exit(); + ENERGEST_ON(ENERGEST_TYPE_IRQ); + notify(REG(GPIO_C_BASE | GPIO_MIS), GPIO_C_NUM); GPIO_CLEAR_INTERRUPT(GPIO_C_BASE, 0xFF); @@ -131,10 +131,10 @@ gpio_port_c_isr() void gpio_port_d_isr() { - ENERGEST_ON(ENERGEST_TYPE_IRQ); - lpm_exit(); + ENERGEST_ON(ENERGEST_TYPE_IRQ); + notify(REG(GPIO_D_BASE | GPIO_MIS), GPIO_D_NUM); GPIO_CLEAR_INTERRUPT(GPIO_D_BASE, 0xFF); diff --git a/cpu/cc2538/lpm.c b/cpu/cc2538/lpm.c index bbc035219..80b536998 100644 --- a/cpu/cc2538/lpm.c +++ b/cpu/cc2538/lpm.c @@ -158,16 +158,28 @@ enter_pm0(void) static void select_32_mhz_xosc(void) { + /*First, make sure there is no ongoing clock source change */ + while((REG(SYS_CTRL_CLOCK_STA) & SYS_CTRL_CLOCK_STA_SOURCE_CHANGE) != 0); + /* Turn on the 32 MHz XOSC and source the system clock on it. */ REG(SYS_CTRL_CLOCK_CTRL) &= ~SYS_CTRL_CLOCK_CTRL_OSC; /* Wait for the switch to take place */ while((REG(SYS_CTRL_CLOCK_STA) & SYS_CTRL_CLOCK_STA_OSC) != 0); + + /* Power down the unused oscillator. */ + REG(SYS_CTRL_CLOCK_CTRL) |= SYS_CTRL_CLOCK_CTRL_OSC_PD; } /*---------------------------------------------------------------------------*/ static void select_16_mhz_rcosc(void) { + /* + * Power up both oscillators in order to speed up the transition to the 32-MHz + * XOSC after wake up. + */ + REG(SYS_CTRL_CLOCK_CTRL) &= ~SYS_CTRL_CLOCK_CTRL_OSC_PD; + /*First, make sure there is no ongoing clock source change */ while((REG(SYS_CTRL_CLOCK_STA) & SYS_CTRL_CLOCK_STA_SOURCE_CHANGE) != 0); @@ -187,6 +199,16 @@ lpm_exit() 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, RTIMER_NOW() - sleep_enter_time); @@ -301,6 +323,11 @@ lpm_enter() select_32_mhz_xosc(); REG(SYS_CTRL_PMCTL) = SYS_CTRL_PMCTL_PM0; + + /* Remember IRQ energest for next pass */ + ENERGEST_IRQ_SAVE(irq_energest); + ENERGEST_ON(ENERGEST_TYPE_CPU); + ENERGEST_OFF(ENERGEST_TYPE_LPM); } else { /* All clear. Assert WFI and drop to PM1/2. This is now un-interruptible */ assert_wfi(); diff --git a/cpu/cc2538/lpm.h b/cpu/cc2538/lpm.h index f4ca3366f..0c497b5be 100644 --- a/cpu/cc2538/lpm.h +++ b/cpu/cc2538/lpm.h @@ -150,6 +150,12 @@ void lpm_enter(void); * 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. * + * 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() */ void lpm_exit(void); diff --git a/cpu/cc2538/rtimer-arch.c b/cpu/cc2538/rtimer-arch.c index 2407b64b6..ffb221024 100644 --- a/cpu/cc2538/rtimer-arch.c +++ b/cpu/cc2538/rtimer-arch.c @@ -136,13 +136,6 @@ rtimer_arch_now() void 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 * that the 32MHz OSC is selected as the clock source. We need to do this @@ -150,6 +143,13 @@ rtimer_isr() */ 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(); ENERGEST_OFF(ENERGEST_TYPE_IRQ);