cpu/sam0_common: GPIO: use tamper detection to wake from Deep Sleep

On samd5x only the RTC can wake the CPU from Deep Sleep (pm modes 0 & 1).
The external interrupt controller is disabled, but we can use the tamper
detection of the RTC.

If an gpio interrupt is configured on one of the five tamper detect pins,
those can be used to wake the CPU from Deep Sleep / Hibernate.
This commit is contained in:
Benjamin Valentin 2020-05-06 00:11:29 +02:00
parent 0499d016ad
commit 310eb4970c
8 changed files with 83 additions and 1 deletions

View File

@ -16,6 +16,15 @@
* @file gpio.c * @file gpio.c
* @brief Low-level GPIO driver implementation * @brief Low-level GPIO driver implementation
* *
* On processors that support Deep Sleep the External Interrupt Controller
* will be off during Deep Sleep.
* To wake the CPU up from Deep Sleep the RTC Tamper Detection will be
* used instead.
* Only a few pins (@ref rtc_tamper_pins) can be used for that purpose.
*
* Note that when configuring those pins as interrupt, the RTC/RTT will be
* stopped briefly as the RTC configuration is enable protected.
*
* @author Troels Hoffmeyer <troels.d.hoffmeyer@gmail.com> * @author Troels Hoffmeyer <troels.d.hoffmeyer@gmail.com>
* @author Thomas Eichinger <thomas.eichinger@fu-berlin.de> * @author Thomas Eichinger <thomas.eichinger@fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de> * @author Kaspar Schleiser <kaspar@schleiser.de>
@ -240,11 +249,41 @@ static int _exti(gpio_t pin)
return exti_config[port_num][_pin_pos(pin)]; return exti_config[port_num][_pin_pos(pin)];
} }
/* check if an RTC tamper pin was configured as interrupt */
__attribute__ ((unused))
static bool _rtc_irq_enabled(void)
{
#if MODULE_PERIPH_GPIO_TAMPER_WAKE
for (unsigned i = 0; i < ARRAY_SIZE(rtc_tamper_pins); ++i) {
int exti = _exti(rtc_tamper_pins[i]);
if (exti == -1) {
continue;
}
if (_EIC->INTENSET.reg & (1 << exti)) {
return true;
}
}
#endif
return false;
}
static void _init_rtc_pin(gpio_t pin, gpio_flank_t flank)
{
if (IS_ACTIVE(MODULE_PERIPH_GPIO_TAMPER_WAKE)) {
rtc_tamper_register(pin, flank);
}
}
int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank,
gpio_cb_t cb, void *arg) gpio_cb_t cb, void *arg)
{ {
int exti = _exti(pin); int exti = _exti(pin);
/* if it's a tamper pin configure wake from Deep Sleep */
_init_rtc_pin(pin, flank);
/* make sure EIC channel is valid */ /* make sure EIC channel is valid */
if (exti == -1) { if (exti == -1) {
return -1; return -1;
@ -330,11 +369,17 @@ void gpio_pm_cb_enter(int deep)
{ {
#if defined(PM_SLEEPCFG_SLEEPMODE_STANDBY) #if defined(PM_SLEEPCFG_SLEEPMODE_STANDBY)
(void) deep; (void) deep;
unsigned mode = PM->SLEEPCFG.bit.SLEEPMODE;
if (PM->SLEEPCFG.bit.SLEEPMODE == PM_SLEEPCFG_SLEEPMODE_STANDBY) { if (mode == PM_SLEEPCFG_SLEEPMODE_STANDBY) {
DEBUG_PUTS("gpio: switching EIC to slow clock"); DEBUG_PUTS("gpio: switching EIC to slow clock");
reenable_eic(_EIC_CLOCK_SLOW); reenable_eic(_EIC_CLOCK_SLOW);
} }
else if (IS_ACTIVE(MODULE_PERIPH_GPIO_TAMPER_WAKE)
&& mode > PM_SLEEPCFG_SLEEPMODE_STANDBY
&& _rtc_irq_enabled()) {
rtc_tamper_enable();
}
#else #else
if (deep) { if (deep) {
DEBUG_PUTS("gpio: switching EIC to slow clock"); DEBUG_PUTS("gpio: switching EIC to slow clock");

View File

@ -12,6 +12,7 @@ config CPU_COMMON_SAMD5X
select HAS_BACKUP_RAM select HAS_BACKUP_RAM
select HAS_CORTEXM_MPU select HAS_CORTEXM_MPU
select HAS_CPU_SAMD5X select HAS_CPU_SAMD5X
select HAS_PERIPH_GPIO_TAMPER_WAKE
select HAS_PERIPH_HWRNG select HAS_PERIPH_HWRNG
config CPU_FAM_SAMD51 config CPU_FAM_SAMD51

View File

@ -1 +1,5 @@
ifneq (,$(filter periph_gpio_tamper_wake,$(USEMODULE)))
USEMODULE += periph_rtc_rtt
endif
include $(RIOTCPU)/sam0_common/Makefile.dep include $(RIOTCPU)/sam0_common/Makefile.dep

View File

@ -3,5 +3,6 @@ CPU_CORE = cortex-m4f
FEATURES_PROVIDED += periph_hwrng FEATURES_PROVIDED += periph_hwrng
FEATURES_PROVIDED += backup_ram FEATURES_PROVIDED += backup_ram
FEATURES_PROVIDED += cortexm_mpu FEATURES_PROVIDED += cortexm_mpu
FEATURES_PROVIDED += periph_gpio_tamper_wake
include $(RIOTCPU)/sam0_common/Makefile.features include $(RIOTCPU)/sam0_common/Makefile.features

View File

@ -116,6 +116,15 @@ typedef enum {
#define RTT_MAX_FREQUENCY (RTT_CLOCK_FREQUENCY) /* in Hz */ #define RTT_MAX_FREQUENCY (RTT_CLOCK_FREQUENCY) /* in Hz */
/** @} */ /** @} */
/**
* @brief RTC input pins that can be used for tamper detection and
* wake from Deep Sleep
*/
static const gpio_t rtc_tamper_pins[RTC_NUM_OF_TAMPERS] = {
GPIO_PIN(PB, 0), GPIO_PIN(PB, 2), GPIO_PIN(PA, 2),
GPIO_PIN(PC, 0), GPIO_PIN(PC, 1)
};
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -82,6 +82,15 @@ typedef enum {
#define RTT_MAX_FREQUENCY (RTT_CLOCK_FREQUENCY) /* in Hz */ #define RTT_MAX_FREQUENCY (RTT_CLOCK_FREQUENCY) /* in Hz */
/** @} */ /** @} */
/**
* @brief RTC input pins that can be used for tamper detection and
* wake from Deep Sleep
*/
static const gpio_t rtc_tamper_pins[RTC_NUM_OF_TAMPERS] = {
GPIO_PIN(PA, 8), GPIO_PIN(PA, 9), GPIO_PIN(PA, 16),
GPIO_PIN(PA, 17)
};
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -22,6 +22,8 @@
#define USB_H_USER_IS_RIOT_INTERNAL #define USB_H_USER_IS_RIOT_INTERNAL
#include "periph_cpu.h"
#ifdef MODULE_PERIPH_INIT #ifdef MODULE_PERIPH_INIT
#ifdef MODULE_PERIPH_INIT_I2C #ifdef MODULE_PERIPH_INIT_I2C
#include "periph/i2c.h" #include "periph/i2c.h"
@ -73,6 +75,11 @@ void periph_init(void)
rtc_init(); rtc_init();
#endif #endif
/* Initialize Tamper Detection */
#ifdef MODULE_PERIPH_INIT_GPIO_TAMPER_WAKE
rtc_tamper_init();
#endif
#ifdef MODULE_PERIPH_INIT_HWRNG #ifdef MODULE_PERIPH_INIT_HWRNG
hwrng_init(); hwrng_init();
#endif #endif

View File

@ -150,6 +150,12 @@ config HAS_PERIPH_GPIO_FAST_READ
operations are faster, usually with a tradeoff against a different operations are faster, usually with a tradeoff against a different
property. property.
config HAS_PERIPH_GPIO_TAMPER_WAKE
bool
help
Indicates that Tamper Detection can be used to wake the CPU from
Deep Sleep.
config HAS_PERIPH_HWRNG config HAS_PERIPH_HWRNG
bool bool
help help