cpu/kinetis: implement power modes for pm_layered

This commit is contained in:
Thomas Stilwell 2020-03-27 17:41:49 -05:00
parent 00b14cce27
commit ae7ec96731
4 changed files with 110 additions and 10 deletions

View File

@ -18,5 +18,6 @@ else ifneq (,$(filter periph_mcg,$(FEATURES_USED)))
endif
USEMODULE += periph_wdog
USEMODULE += pm_layered
include $(RIOTCPU)/cortexm_common/Makefile.dep

View File

@ -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

View File

@ -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

View File

@ -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);
}