diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index 68ef1d202b..96e77e3d02 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -1069,6 +1069,32 @@ void dma_wait(dma_t dma); void dma_cancel(dma_t dma); /** @} */ +/** + * @name sam0 RTC Tamper Detection + * @{ + */ + +/** + * @brief Power on the RTC (if the RTC/RTT is not otherwise used) + */ +void rtc_tamper_init(void); + +/** + * @brief Enable Tamper Detection IRQs + * + * @param pin The GPIO pin to be used for tamper detection + * @param flank The Flank to trigger the even + * + * @return 0 on success, -1 if pin is not RTC pin + */ +int rtc_tamper_register(gpio_t pin, gpio_flank_t flank); + +/** + * @brief Enable Tamper Detection IRQs + */ +void rtc_tamper_enable(void); +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/sam0_common/periph/rtc_rtt.c b/cpu/sam0_common/periph/rtc_rtt.c index 4d88cc4b41..70237f08fe 100644 --- a/cpu/sam0_common/periph/rtc_rtt.c +++ b/cpu/sam0_common/periph/rtc_rtt.c @@ -25,6 +25,7 @@ */ #include +#include "mutex.h" #include "periph/rtc.h" #include "periph/rtt.h" #include "periph_conf.h" @@ -101,6 +102,16 @@ static void _poweron(void) #endif } +__attribute__((unused)) +static bool _power_is_on(void) +{ +#ifdef MCLK + return MCLK->APBAMASK.reg & MCLK_APBAMASK_RTC; +#else + return PM->APBAMASK.reg & PM_APBAMASK_RTC; +#endif +} + static void _poweroff(void) { #ifdef MCLK @@ -268,6 +279,93 @@ void rtt_init(void) NVIC_EnableIRQ(RTC_IRQn); } +#if RTC_NUM_OF_TAMPERS + +static rtc_state_t tamper_cb; + +/* check if pin is a RTC tamper pin */ +static int _rtc_pin(gpio_t pin) +{ + for (unsigned i = 0; i < ARRAY_SIZE(rtc_tamper_pins); ++i) { + if (rtc_tamper_pins[i] == pin) { + return i; + } + } + + return -1; +} + +void rtc_tamper_init(void) +{ + if (IS_ACTIVE(MODULE_PERIPH_RTC) || + IS_ACTIVE(MODULE_PERIPH_RTT) || + _power_is_on()) { + return; + } + + _rtt_clock_setup(); + _poweron(); + + /* disable all interrupt sources */ + RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_MASK; + + NVIC_EnableIRQ(RTC_IRQn); +} + +int rtc_tamper_register(gpio_t pin, gpio_flank_t flank) +{ + int in = _rtc_pin(pin); + + if (in < 0) { + return -1; + } + + /* TAMPCTRL is enable-protected */ + _rtc_set_enabled(0); + + RTC->MODE0.TAMPCTRL.reg |= RTC_TAMPCTRL_IN0ACT_WAKE << (2 * in); + + if (flank == GPIO_RISING) { + RTC->MODE0.TAMPCTRL.reg |= RTC_TAMPCTRL_TAMLVL0 << in; + } else if (flank == GPIO_FALLING) { + RTC->MODE0.TAMPCTRL.reg &= ~(RTC_TAMPCTRL_TAMLVL0 << in); + } + + /* enable the RTC again */ + _rtc_set_enabled(1); + + return 0; +} + +static void _unlock(void *m) +{ + mutex_unlock(m); +} + +void rtc_tamper_enable(void) +{ + mutex_t m = MUTEX_INIT; + + /* clear tamper id */ + RTC->MODE0.TAMPID.reg = 0xF; + + /* work around errata 2.17.4: + * ignore the first tamper event on the rising edge */ + if (RTC->MODE0.TAMPCTRL.reg & RTC_TAMPCTRL_TAMLVL_Msk) { + mutex_lock(&m); + tamper_cb.cb = _unlock; + tamper_cb.arg = &m; + } + + /* enable tamper detect as wake-up source */ + RTC->MODE0.INTENSET.bit.TAMPER = 1; + + /* wait for first tamper event */ + mutex_lock(&m); +} + +#endif /* RTC_NUM_OF_TAMPERS */ + void rtt_set_overflow_cb(rtt_cb_t cb, void *arg) { /* clear overflow cb to avoid race while assigning */ @@ -503,9 +601,23 @@ static void _isr_rtt(void) } } +static void _isr_tamper(void) +{ +#ifdef RTC_MODE0_INTFLAG_TAMPER + if (RTC->MODE0.INTFLAG.bit.TAMPER) { + RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_TAMPER; + if (tamper_cb.cb) { + tamper_cb.cb(tamper_cb.arg); + } + } +#endif +} + void isr_rtc(void) { _isr_rtc(); _isr_rtt(); + _isr_tamper(); + cortexm_isr_end(); }