From ae7ec96731093aea49b0c8b44cb2910d9696d9af Mon Sep 17 00:00:00 2001 From: Thomas Stilwell Date: Fri, 27 Mar 2020 17:41:49 -0500 Subject: [PATCH] cpu/kinetis: implement power modes for pm_layered --- cpu/kinetis/Makefile.dep | 1 + cpu/kinetis/Makefile.features | 1 + cpu/kinetis/include/periph_cpu.h | 27 +++++++++- cpu/kinetis/periph/pm.c | 91 +++++++++++++++++++++++++++++--- 4 files changed, 110 insertions(+), 10 deletions(-) diff --git a/cpu/kinetis/Makefile.dep b/cpu/kinetis/Makefile.dep index 4f4c133353..45e3557e38 100644 --- a/cpu/kinetis/Makefile.dep +++ b/cpu/kinetis/Makefile.dep @@ -18,5 +18,6 @@ else ifneq (,$(filter periph_mcg,$(FEATURES_USED))) endif USEMODULE += periph_wdog +USEMODULE += pm_layered include $(RIOTCPU)/cortexm_common/Makefile.dep diff --git a/cpu/kinetis/Makefile.features b/cpu/kinetis/Makefile.features index 9fc9cdecbe..0cc03f5e15 100644 --- a/cpu/kinetis/Makefile.features +++ b/cpu/kinetis/Makefile.features @@ -1,4 +1,5 @@ FEATURES_PROVIDED += periph_cpuid +FEATURES_PROVIDED += periph_pm # TRNG driver is not implemented for mkw41z models _KINETIS_CPU_MODELS_WITHOUT_HWRNG += mkw41z256vht4 mkw41z512vht4 diff --git a/cpu/kinetis/include/periph_cpu.h b/cpu/kinetis/include/periph_cpu.h index 087ee3580e..12f461456e 100644 --- a/cpu/kinetis/include/periph_cpu.h +++ b/cpu/kinetis/include/periph_cpu.h @@ -130,9 +130,32 @@ typedef uint16_t gpio_t; #define PERIPH_TIMER_PROVIDES_SET /** - * @brief number of usable power modes + * @name Kinetis power mode configuration + * @{ */ -#define PM_NUM_MODES (1U) +#define PM_NUM_MODES (3U) +enum { + KINETIS_PM_LLS = 0, + KINETIS_PM_VLPS = 1, + KINETIS_PM_STOP = 2, + KINETIS_PM_WAIT = 3, +}; +#if MODULE_PM_LAYERED +#include "pm_layered.h" +/** + * @brief pm_block iff pm_layered is used + */ +#define PM_BLOCK(x) pm_block(x) +/** + * @brief pm_unblock iff pm_layered is used + */ +#define PM_UNBLOCK(x) pm_unblock(x) +#else +/* ignore these calls when not using pm_layered */ +#define PM_BLOCK(x) +#define PM_UNBLOCK(x) +#endif +/** @} */ #ifdef RTC /* All Kinetis CPUs have exactly one RTC hardware module, except for the KL02 diff --git a/cpu/kinetis/periph/pm.c b/cpu/kinetis/periph/pm.c index 4a9961ba7a..3cb1ec0a54 100644 --- a/cpu/kinetis/periph/pm.c +++ b/cpu/kinetis/periph/pm.c @@ -26,17 +26,92 @@ #define ENABLE_DEBUG (0) #include "debug.h" -/** - * @note The current PM implementation is very much simplified down to only - * using the 'WAIT' mode. This implementation must be further expanded - * to make use of the available and more efficient (deep) sleep modes - * of the Kinetis CPUs. - */ +/* Set to 1 to use the LEDx macros to show which sleep mode is entered */ +#define ENABLE_LEDS (0) + +#if ENABLE_LEDS +#include "board.h" +#define PM_LED(x, state) LED ## x ## _ ## state +#else +#define PM_LED(x, state) +#endif + +/* SMC_PMCTRL_STOPM masks */ +enum { + SMC_PMCTRL_STOPM_STOP = 0, + /* 1 is reserved */ + SMC_PMCTRL_STOPM_VLPS = 2, + SMC_PMCTRL_STOPM_LLS = 3, + /* VLLS is not supported */ +}; + +/** Configure which stop mode will be entered when cortexm_sleep(1) is called */ +static inline void pm_stopm(uint8_t stopm) +{ + SMC->PMCTRL = (SMC->PMCTRL & ~(SMC_PMCTRL_STOPM_MASK)) | SMC_PMCTRL_STOPM(stopm); +} + void pm_set(unsigned mode) { + unsigned deep = 1; switch (mode) { - case 0: - cortexm_sleep(0); + /* Only the CPU stops in this mode. No clocks or peripherals are powered + * off. This state should always be safe to use, as any interrupt will + * wake it. Power consumption in this state is reduced by about 50%. */ + case KINETIS_PM_WAIT: /* 3 - ARM Sleep */ + /* WAIT */ + deep = 0; + PM_LED(0, ON); + break; + + /* Some system clocks are stopped in this mode. Usage, means of waking, + * and power consumption depend heavily on board-level clock and + * peripheral configuration.*/ + /* If the debug hardware has been used since the last POR, the debug + * hardware as well as the clock needed to support it will stay on in + * this mode. Thus, after flashing, issue a POR before testing it. */ + case KINETIS_PM_STOP: /* 2 - ARM Deep Sleep */ + /* STOP */ + pm_stopm(SMC_PMCTRL_STOPM_STOP); + PM_LED(1, ON); + break; + + /* Some system clocks are stopped in this mode. Usage, means of waking, + * and power consumption depend heavily on board-level clock and + * peripheral configuration.*/ + /* If the debug hardware has been used since the last POR, the debug + * hardware as well as the clock needed to support it will stay on in + * this mode. Thus, after flashing, issue a POR before testing it. */ + case KINETIS_PM_VLPS: /* 1 - ARM Deep Sleep */ + /* VLPS */ + pm_stopm(SMC_PMCTRL_STOPM_VLPS); + PM_LED(2, ON); + break; + + /* All clocks except 32kHz are stopped. Wakeup can occur from + * interrupts, several (LLWU-enabled) GPIOs, RTC, or (if the board + * configuration uses the 32kHz clock for xtimer) xtimer. Debug hardware + * is powered off. Power consumption in this state should be on the + * order of 5 uA for the MCU alone, with most boards having additional + * parts that will raise this amount. */ + case KINETIS_PM_LLS: /* 0 - ARM Deep Sleep */ + /* LLSx */ + pm_stopm(SMC_PMCTRL_STOPM_LLS); + PM_LED(0, ON); + PM_LED(2, ON); + /* Enable LLWU interrupt, or else we can never resume from LLS */ + /* Clear pending flag first, the LLWU has no purpose in RUN mode, so + * if the flag is set then it must be a remainder from handling the + * previous wakeup. */ + LLWU->F1 = 0xffu; + LLWU->F2 = 0xffu; + NVIC_ClearPendingIRQ(LLWU_IRQn); + NVIC_EnableIRQ(LLWU_IRQn); break; } + DEBUG("pm_set(%u)\n", mode); + cortexm_sleep(deep); + PM_LED(0, OFF); + PM_LED(1, OFF); + PM_LED(2, OFF); }