From 8514a91ea98dd5015aba083af2d3f16a54b93e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 3 Dec 2013 18:44:01 +0100 Subject: [PATCH 1/4] cc2538: lpm: Fix energest context when aborting lpm_enter() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In one of the abort cases in lpm_enter(), the energest context has previously been set to LPM, so the abort code needs to set it back to CPU. Signed-off-by: Benoît Thébaudeau --- cpu/cc2538/lpm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cpu/cc2538/lpm.c b/cpu/cc2538/lpm.c index bbc035219..74d00df91 100644 --- a/cpu/cc2538/lpm.c +++ b/cpu/cc2538/lpm.c @@ -301,6 +301,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(); From 13006e1c737bf557f83ef9da6ccd9037eacb5a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 3 Dec 2013 18:51:39 +0100 Subject: [PATCH 2/4] cc2538: lpm: Let system clock transitions complete before further changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As a matter of precaution, always make sure that pending system clock transitions are complete before requesting a new change of the system clock source. Signed-off-by: Benoît Thébaudeau --- cpu/cc2538/lpm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpu/cc2538/lpm.c b/cpu/cc2538/lpm.c index 74d00df91..da0349495 100644 --- a/cpu/cc2538/lpm.c +++ b/cpu/cc2538/lpm.c @@ -158,6 +158,9 @@ 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; From f149197aa8b755754edfd7717b73ba666498a0de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 3 Dec 2013 19:03:00 +0100 Subject: [PATCH 3/4] cc2538: lpm: Speed up the transition to the 32-MHz XOSC after wake-up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As recommended by the CC2538 User's Guide, set SYS_CTRL_CLOCK_CTRL.OSC_PD to 0 before asserting WFI, and set it to 1 after the system clock is sourced from the 32-MHz XOSC following wake-up. This allows to automatically start both oscillators upon wake-up in order to partially hide the 32-MHz XOSC startup time by the 16-MHz RCOSC startup time. Signed-off-by: Benoît Thébaudeau --- cpu/cc2538/lpm.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cpu/cc2538/lpm.c b/cpu/cc2538/lpm.c index da0349495..f1605771b 100644 --- a/cpu/cc2538/lpm.c +++ b/cpu/cc2538/lpm.c @@ -166,11 +166,20 @@ select_32_mhz_xosc(void) /* 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); From 5261bb861d21b0001a664e2e2028ea7af966caab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 3 Dec 2013 19:16:24 +0100 Subject: [PATCH 4/4] cc2538: lpm: Fix RTIMER_NOW() upon wake-up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- cpu/cc2538/dev/gpio.c | 16 ++++++++-------- cpu/cc2538/lpm.c | 10 ++++++++++ cpu/cc2538/lpm.h | 6 ++++++ cpu/cc2538/rtimer-arch.c | 14 +++++++------- 4 files changed, 31 insertions(+), 15 deletions(-) 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 f1605771b..80b536998 100644 --- a/cpu/cc2538/lpm.c +++ b/cpu/cc2538/lpm.c @@ -199,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); 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);