From 0ee0c5e40e561f766032e33d48a028bfa7993649 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Thu, 19 Aug 2021 18:42:12 +0200 Subject: [PATCH 1/4] drivers/periph: define periph_rtc_mem --- drivers/include/periph/rtc_mem.h | 70 ++++++++++++++++++++++++++++++++ kconfigs/Kconfig.features | 5 +++ 2 files changed, 75 insertions(+) create mode 100644 drivers/include/periph/rtc_mem.h diff --git a/drivers/include/periph/rtc_mem.h b/drivers/include/periph/rtc_mem.h new file mode 100644 index 000000000000..e8c837d63917 --- /dev/null +++ b/drivers/include/periph/rtc_mem.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2021 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @defgroup drivers_periph_rtc_mem Low-Power RTC Memory + * @ingroup drivers_periph_rtc + * @brief Low-level RTC Memory peripheral driver + * + * This API provides an interface to access low-power memory present on some RTCs. + * This memory is retained even when the rest of the system is powered off. + * + * @{ + * @file + * @brief Low-level RTC memory peripheral driver interface definitions + * + * @author Benjamin Valentin + */ + +#ifndef PERIPH_RTC_MEM_H +#define PERIPH_RTC_MEM_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get the amount of RTC memory. + * + * @return The usable amount of RTC memory in bytes + */ +size_t rtc_mem_size(void); + +/** + * @brief Read from RTC memory + * + * @note Reading beyond @ref rtc_mem_size are illegal and trigger an + * assertion / be discarded. + * + * @param[in] offset Offset to the start of RTC memory in bytes + * @param[out] data Destination buffer + * @param[in] len Amount of bytes to read + */ +void rtc_mem_read(unsigned offset, void *data, size_t len); + +/** + * @brief Write to RTC memory + * + * @note Writing beyond @ref rtc_mem_size are illegal and trigger an + * assertion / be discarded. + * + * @param[in] offset Offset to the start of RTC memory in bytes + * @param[in] data Source buffer + * @param[in] len Amount of bytes to write + */ +void rtc_mem_write(unsigned offset, const void *data, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* PERIPH_RTC_MEM_H */ +/** @} */ diff --git a/kconfigs/Kconfig.features b/kconfigs/Kconfig.features index 0d9b400c561b..537b63b2fc79 100644 --- a/kconfigs/Kconfig.features +++ b/kconfigs/Kconfig.features @@ -257,6 +257,11 @@ config HAS_PERIPH_RTC help Indicates that an RTC peripheral is present. +config HAS_PERIPH_RTC_MEM + bool + help + Indicates that the RTC peripheral provides storage memory for deep sleep. + config HAS_PERIPH_RTC_MS bool help From 3fbf473a070f305c35d667e7ef90991be5fd6cfb Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Thu, 19 Aug 2021 18:38:26 +0200 Subject: [PATCH 2/4] cpu/sam0_common: implement periph_rtc_mem --- cpu/sam0_common/periph/rtc_rtt.c | 82 ++++++++++++++++++++++++++++++++ cpu/samd5x/Kconfig | 1 + cpu/samd5x/Makefile.features | 1 + cpu/saml21/Kconfig | 1 + cpu/saml21/Makefile.features | 1 + 5 files changed, 86 insertions(+) diff --git a/cpu/sam0_common/periph/rtc_rtt.c b/cpu/sam0_common/periph/rtc_rtt.c index bb0c2d670579..3d3d1f1b5273 100644 --- a/cpu/sam0_common/periph/rtc_rtt.c +++ b/cpu/sam0_common/periph/rtc_rtt.c @@ -25,6 +25,7 @@ */ #include +#include #include "periph/rtc.h" #include "periph/rtt.h" #include "periph_conf.h" @@ -215,10 +216,70 @@ static void _rtt_clock_setup(void) #endif /* MODULE_PERIPH_RTT */ #endif /* !CPU_COMMON_SAMD21 - Clock Setup */ +#ifdef MODULE_PERIPH_RTC_MEM +/* first two GP registers are shared with COMP[0] / ALARM[0] */ +#ifdef RTC_MODE2_CTRLB_GP2EN +#define RTC_GPR_START (2) +#else +#define RTC_GPR_START (0) +#endif + +#define RTC_GPR_NUM_AVAIL (RTC_GPR_NUM - RTC_GPR_START) +#define RTC_MEM_SIZE (RTC_GPR_NUM_AVAIL * sizeof(uint32_t)) + +size_t rtc_mem_size(void) +{ + return RTC_MEM_SIZE; +} + +static void _read_gp(uint32_t *dst) +{ + for (unsigned i = RTC_GPR_START; i < RTC_GPR_NUM; ++i) { + dst[i - RTC_GPR_START] = RTC->MODE0.GP[i].reg; + } +} + +static void _write_gp(const uint32_t *src) +{ + for (unsigned i = RTC_GPR_START; i < RTC_GPR_NUM; ++i) { + _wait_syncbusy(); + RTC->MODE0.GP[i].reg = src[i - RTC_GPR_START]; + } +} + +void rtc_mem_read(unsigned offset, void *data, size_t len) +{ + uint32_t tmp[RTC_GPR_NUM_AVAIL]; + + if (offset + len > RTC_MEM_SIZE) { + assert(0); + return; + } + + _read_gp(tmp); + memcpy(data, ((uint8_t *)tmp) + offset, len); +} + +void rtc_mem_write(unsigned offset, void *data, size_t len) +{ + uint32_t tmp[RTC_GPR_NUM_AVAIL]; + + if (offset + len > RTC_MEM_SIZE) { + assert(0); + return; + } + + _read_gp(tmp); + memcpy(((uint8_t *)tmp) + offset, data, len); + _write_gp(tmp); +} +#endif /* MODULE_PERIPH_RTC_MEM */ + #ifdef MODULE_PERIPH_RTC static void _rtc_init(void) { #ifdef REG_RTC_MODE2_CTRLA + /* skip reset if already in RTC mode */ if (RTC->MODE2.CTRLA.bit.MODE == RTC_MODE2_CTRLA_MODE_CLOCK_Val) { return; } @@ -229,6 +290,11 @@ static void _rtc_init(void) RTC->MODE2.CTRLA.reg = RTC_MODE2_CTRLA_PRESCALER_DIV1024 /* CLK_RTC_CNT = 1KHz / 1024 -> 1Hz */ | RTC_MODE2_CTRLA_CLOCKSYNC /* Clock Read Synchronization Enable */ | RTC_MODE2_CTRLA_MODE_CLOCK; +#ifdef RTC_MODE2_CTRLB_GP2EN + /* RTC driver does not use COMP[1] or ALARM[1] */ + /* Use second set of Compare registers as general purpose register */ + RTC->MODE2.CTRLB.reg = RTC_MODE2_CTRLB_GP2EN; +#endif #else if (RTC->MODE2.CTRL.bit.MODE == RTC_MODE2_CTRL_MODE_CLOCK_Val) { return; @@ -268,10 +334,26 @@ void rtc_init(void) #ifdef MODULE_PERIPH_RTT void rtt_init(void) { + _rtt_clock_setup(); _poweron(); + +#ifdef MODULE_PERIPH_RTC_MEM + uint32_t backup[RTC_GPR_NUM_AVAIL]; + _read_gp(backup); +#endif + _rtt_reset(); +#ifdef MODULE_PERIPH_RTC_MEM +#ifdef RTC_MODE2_CTRLB_GP2EN + /* RTC driver does not use COMP[1] or ALARM[1] */ + /* Use second set of Compare registers as general purpose register */ + RTC->MODE2.CTRLB.reg = RTC_MODE2_CTRLB_GP2EN; +#endif + _write_gp(backup); +#endif /* MODULE_PERIPH_RTC_MEM */ + /* set 32bit counting mode & enable the RTC */ #ifdef REG_RTC_MODE0_CTRLA RTC->MODE0.CTRLA.reg = RTC_MODE0_CTRLA_MODE(0) diff --git a/cpu/samd5x/Kconfig b/cpu/samd5x/Kconfig index 07f6a9d1509b..0531548781a1 100644 --- a/cpu/samd5x/Kconfig +++ b/cpu/samd5x/Kconfig @@ -15,6 +15,7 @@ config CPU_COMMON_SAMD5X select HAS_PERIPH_DMA select HAS_PERIPH_GPIO_TAMPER_WAKE select HAS_PERIPH_HWRNG + select HAS_PERIPH_RTC_MEM select HAS_PERIPH_SPI_ON_QSPI config CPU_FAM_SAMD51 diff --git a/cpu/samd5x/Makefile.features b/cpu/samd5x/Makefile.features index ff059dc6771e..e024c5e1bc96 100644 --- a/cpu/samd5x/Makefile.features +++ b/cpu/samd5x/Makefile.features @@ -4,6 +4,7 @@ FEATURES_PROVIDED += periph_hwrng FEATURES_PROVIDED += backup_ram FEATURES_PROVIDED += cortexm_mpu FEATURES_PROVIDED += periph_gpio_tamper_wake +FEATURES_PROVIDED += periph_rtc_mem FEATURES_PROVIDED += periph_spi_on_qspi include $(RIOTCPU)/sam0_common/Makefile.features diff --git a/cpu/saml21/Kconfig b/cpu/saml21/Kconfig index b9091cc836d8..97c99c25403d 100644 --- a/cpu/saml21/Kconfig +++ b/cpu/saml21/Kconfig @@ -13,6 +13,7 @@ config CPU_COMMON_SAML21 select HAS_CPU_SAML21 select HAS_PERIPH_DMA select HAS_PERIPH_GPIO_FAST_READ + select HAS_PERIPH_RTC_MEM config CPU_FAM_SAML21 bool diff --git a/cpu/saml21/Makefile.features b/cpu/saml21/Makefile.features index cdd972d64c5b..85685fb27f79 100644 --- a/cpu/saml21/Makefile.features +++ b/cpu/saml21/Makefile.features @@ -10,6 +10,7 @@ FEATURES_PROVIDED += periph_gpio_fast_read # It can still be used in normal and standby mode, but code that relies on it # being availiable during deep sleep / backup mode will not be portable here. FEATURES_PROVIDED += backup_ram +FEATURES_PROVIDED += periph_rtc_mem ifeq (,$(filter $(CPU_MODELS_WITHOUT_HWRNG),$(CPU_MODEL))) FEATURES_PROVIDED += periph_hwrng From c467f05317e519537cf0adcfbe64dae82bc54648 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Thu, 19 Aug 2021 18:39:36 +0200 Subject: [PATCH 3/4] tests/periph_rtt: test for periph_rtc_mem --- tests/periph_rtt/Makefile | 1 + tests/periph_rtt/main.c | 59 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/tests/periph_rtt/Makefile b/tests/periph_rtt/Makefile index 5c70de468301..75b7c29ac377 100644 --- a/tests/periph_rtt/Makefile +++ b/tests/periph_rtt/Makefile @@ -3,6 +3,7 @@ include ../Makefile.tests_common FEATURES_REQUIRED = periph_rtt FEATURES_OPTIONAL += periph_rtt_set_counter +FEATURES_OPTIONAL += periph_rtc_mem DISABLE_MODULE += periph_init_rtt diff --git a/tests/periph_rtt/main.c b/tests/periph_rtt/main.c index 59b74442c92a..7095a9f7f035 100644 --- a/tests/periph_rtt/main.c +++ b/tests/periph_rtt/main.c @@ -23,11 +23,13 @@ #include #include +#include #include #include "cpu.h" #include "periph_conf.h" #include "periph/rtt.h" +#include "periph/rtc_mem.h" #define TICKS_TO_WAIT (5 * RTT_FREQUENCY) @@ -46,6 +48,58 @@ void cb(void *arg) puts("Hello"); } +#ifdef MODULE_PERIPH_RTC_MEM +static const uint8_t riot_msg_offset = 1; +static const char riot_msg[] = "RIOT"; +static void _set_rtc_mem(void) +{ + /* first fill the whole memory */ + uint8_t size = rtc_mem_size(); + while (size--) { + rtc_mem_write(size, &size, sizeof(size)); + } + + /* write test data */ + rtc_mem_write(riot_msg_offset, riot_msg, sizeof(riot_msg) - 1); +} + +static void _get_rtc_mem(void) +{ + char buf[4]; + rtc_mem_read(riot_msg_offset, buf, sizeof(buf)); + + if (memcmp(buf, riot_msg, sizeof(buf))) { + puts("RTC mem content does not match"); + for (unsigned i = 0; i < sizeof(buf); ++i) { + printf("%02x - %02x\n", riot_msg[i], buf[i]); + } + return; + } + + uint8_t size = rtc_mem_size(); + while (size--) { + uint8_t data; + + if (size >= riot_msg_offset && + size < riot_msg_offset + sizeof(riot_msg)) { + continue; + } + + rtc_mem_read(size, &data, 1); + if (data != size) { + puts("RTC mem content does not match"); + printf("%02x: %02x\n", size, data); + } + } + + + puts("RTC mem OK"); +} +#else +static inline void _set_rtc_mem(void) {} +static inline void _get_rtc_mem(void) {} +#endif + int main(void) { puts("\nRIOT RTT low-level driver test"); @@ -71,6 +125,9 @@ int main(void) puts("Initializing the RTT driver"); rtt_init(); + _set_rtc_mem(); + _get_rtc_mem(); + puts("This test will now display 'Hello' every 5 seconds\n"); uint32_t now = rtt_get_counter(); printf("RTT now: %" PRIu32 "\n", now); @@ -103,6 +160,8 @@ int main(void) puts("rtt_get_alarm() PASSED"); } + _get_rtc_mem(); + puts("Done setting up the RTT, wait for many Hellos"); return 0; } From c8d94e89c92c43185a6544ce04b38a88303d5ba0 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Thu, 19 Aug 2021 18:40:44 +0200 Subject: [PATCH 4/4] tests/periph_rtc: test for periph_rtc_mem --- tests/periph_rtc/Makefile | 1 + tests/periph_rtc/main.c | 73 ++++++++++++++++++++++++++++++++++----- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/tests/periph_rtc/Makefile b/tests/periph_rtc/Makefile index 3d33287f1969..c717733c395c 100644 --- a/tests/periph_rtc/Makefile +++ b/tests/periph_rtc/Makefile @@ -2,6 +2,7 @@ include ../Makefile.tests_common FEATURES_REQUIRED += periph_rtc FEATURES_OPTIONAL += periph_rtc_ms +FEATURES_OPTIONAL += periph_rtc_mem DISABLE_MODULE += periph_init_rtc diff --git a/tests/periph_rtc/main.c b/tests/periph_rtc/main.c index db798a6fd0e9..229f3deb6357 100644 --- a/tests/periph_rtc/main.c +++ b/tests/periph_rtc/main.c @@ -23,10 +23,12 @@ #include #include +#include #include "mutex.h" #include "periph_conf.h" #include "periph/rtc.h" +#include "periph/rtc_mem.h" #include "xtimer.h" #define PERIOD (2U) @@ -69,6 +71,58 @@ static void cb(void *arg) mutex_unlock(arg); } +#ifdef MODULE_PERIPH_RTC_MEM +static const uint8_t riot_msg_offset = 1; +static const char riot_msg[] = "RIOT"; +static void _set_rtc_mem(void) +{ + /* first fill the whole memory */ + uint8_t size = rtc_mem_size(); + while (size--) { + rtc_mem_write(size, &size, sizeof(size)); + } + + /* write test data */ + rtc_mem_write(riot_msg_offset, riot_msg, sizeof(riot_msg) - 1); +} + +static void _get_rtc_mem(void) +{ + char buf[4]; + rtc_mem_read(riot_msg_offset, buf, sizeof(buf)); + + if (memcmp(buf, riot_msg, sizeof(buf))) { + puts("RTC mem content does not match"); + for (unsigned i = 0; i < sizeof(buf); ++i) { + printf("%02x - %02x\n", riot_msg[i], buf[i]); + } + return; + } + + uint8_t size = rtc_mem_size(); + while (size--) { + uint8_t data; + + if (size >= riot_msg_offset && + size < riot_msg_offset + sizeof(riot_msg)) { + continue; + } + + rtc_mem_read(size, &data, 1); + if (data != size) { + puts("RTC mem content does not match"); + printf("%02x: %02x\n", size, data); + } + } + + + puts("RTC mem OK"); +} +#else +static inline void _set_rtc_mem(void) {} +static inline void _get_rtc_mem(void) {} +#endif + int main(void) { struct tm time = { @@ -88,6 +142,9 @@ int main(void) rtc_init(); + _set_rtc_mem(); + _get_rtc_mem(); + /* set RTC */ print_time(" Setting clock to ", &time); rtc_set_time(&time); @@ -149,17 +206,17 @@ int main(void) puts(""); /* loop over a few alarm cycles */ - while (1) { + do { mutex_lock(&rtc_mtx); puts("Alarm!"); - if (++cnt < REPEAT) { - struct tm time; - rtc_get_alarm(&time); - inc_secs(&time, PERIOD); - rtc_set_alarm(&time, cb, &rtc_mtx); - } - } + struct tm time; + rtc_get_alarm(&time); + inc_secs(&time, PERIOD); + rtc_set_alarm(&time, cb, &rtc_mtx); + } while (++cnt < REPEAT); + + _get_rtc_mem(); return 0; }