diff --git a/cpu/esp32/Makefile.dep b/cpu/esp32/Makefile.dep
index a85d61af3f..078200bee3 100644
--- a/cpu/esp32/Makefile.dep
+++ b/cpu/esp32/Makefile.dep
@@ -95,6 +95,11 @@ ifneq (,$(filter pm_layered,$(USEMODULE)))
USEMODULE += periph_rtc
endif
+ifneq (,$(filter periph_rtt,$(USEMODULE)))
+ USEMODULE += periph_rtt_hw_sys
+ USEMODULE += periph_rtt_hw_rtc
+endif
+
ifneq (,$(filter shell,$(USEMODULE)))
USEMODULE += ps
endif
diff --git a/cpu/esp32/Makefile.features b/cpu/esp32/Makefile.features
index 50faf56698..c7100987a5 100644
--- a/cpu/esp32/Makefile.features
+++ b/cpu/esp32/Makefile.features
@@ -6,3 +6,4 @@ FEATURES_PROVIDED += arch_esp32
FEATURES_PROVIDED += esp_wifi_enterprise
FEATURES_PROVIDED += periph_adc_ctrl
FEATURES_PROVIDED += periph_rtc
+FEATURES_PROVIDED += periph_rtt
diff --git a/cpu/esp32/doc.txt b/cpu/esp32/doc.txt
index e56d63f2bd..db1204eaa7 100644
--- a/cpu/esp32/doc.txt
+++ b/cpu/esp32/doc.txt
@@ -38,7 +38,7 @@
5. [PWM Channels](#esp32_pwm_channels)
6. [SPI Interfaces](#esp32_spi_interfaces)
7. [Timers](#esp32_timers)
- 8. [RTC Timer](#esp32_rtc_timer)
+ 8. [RTT Implementation](#esp32_rtt_counter)
9. [UART Interfaces](#esp32_uart_interfaces)
10. [CAN Interfaces](#esp32_can_interfaces)
11. [Power Management](#esp32_power_management)
@@ -120,8 +120,7 @@ Module | Default | Short description
[esp_log_startup](#esp32_esp_log_module) | not used | enable additional startup information
[esp_log_tagged](#esp32_esp_log_module) | not used | add additional information to the log output
[esp_now](#esp32_esp_now_network_interface) | not used | enable the ESP-NOW network device
-[esp_rtc_timer](#esp32_rtc_timer) | not used | enable RTC hardware timer with internal 150 kHz RC oscillator
-[esp_rtc_timer_32k](#esp32_rtc_timer) | not used | enable RTC hardware timer with external 32.768 kHz crystal.
+[esp_rtc_timer_32k](#esp32_rtt_counter) | not used | use RTC timer with external 32.768 kHz crystal as RTT
[esp_spi_ram](#esp32_spi_ram) | not used | enable SPI RAM
[esp_spiffs](#esp32_spiffs_device) | not used | enable SPIFFS for on-board flash memory
[esp_wifi](#esp32_wifi_network_interface) | not used | enable the Wifi network device in WPA2 personal mode
@@ -422,7 +421,6 @@ esp_log_colored | Enable colored log output, see section [Log output](#esp32_esp
esp_log_startup | Enable additional startup information, see section [Log output](#esp32_esp_log_module).
esp_log_tagged | Add additional information to the log output, see section [Log output](#esp32_esp_log_module).
esp_now | Enable the built-in WiFi module with the ESP-NOW protocol as `netdev` network device, see section [ESP-NOW Network Interface](#esp32_esp_now_network_interface).
-esp_rtc_timer | Enable RTC hardware timer with internal 150 kHz RC oscillator.
esp_rtc_timer_32k | Enable RTC hardware timer with external 32.768 kHz crystal.
esp_spiffs | Enable the optional SPIFFS drive in on-board flash memory, see section [SPIFFS Device](#esp32_spiffs_device).
esp_spi_ram | Enable the optional SPI RAM, see section [SPI RAM Modules](#esp32_spi_ram).
@@ -839,37 +837,31 @@ to application's makefile.
Timers are MCU built-in features and not board-specific. There is nothing to be configured.
-\anchor esp32_rtc_timer
-## RTC Timer [[TOC](#esp32_toc)]
+\anchor esp32_rtt_counter
+## RTT implementation [[TOC](#esp32_toc)]
-The RTC hardware timer of the ESP32 can be clocked with either an external
-32.768 kHz crystal or the internal adjustable 150 kHz RC oscillator. If the
-the external 32.768 kHz crystal is not available, the internal 150 kHz RC
-oscillator is used automatically. However, since this internal 150 kHz RC
-oscillator is not very accurate, the RTC low-level driver uses by default
-the PLL-controlled 64-bit microsecond system timer to emulate the RTC timer.
+The RTT peripheral low-level driver provides a RTT (Real Time Timer) with
+a frequency of 32.768 kHz. It either uses the RTC hardware timer if an
+external 32.768 kHz crystal is connected to the ESP32 or the PLL-controlled
+64-bit microsecond system timer to emulate the RTC timer.
-To allow the use of the RTC hardware timer for boards with an external
-32 kHz crystal, the pseudomodules `esp_rtc_timer` and `esp_rtc_timer_32k`
-can be used to control which timer is used by the RTC low-level driver
-as following:
+Whether an external 32.768 kHz crystal is connected to the ESP32 is
+specified as a feature by the board definition using the pseudomodule
+`esp_rtc_timer_32k`. If the feature `esp_rtc_timer_32k` is defined but the
+external 32.768 kHz crystal is not recognized during startup, the
+PLL controlled 64 bit microsecond system timer is used to emulate the
+RTC timer.
-- **esp_rtc_timer**:
- Use always the RTC hardware timer with the **internal 150 kHz RC** oscillator.
+The RTT is retained during light and deep sleep as well as during a restart.
+The RTC hardware timer is used for this purpose, regardless of whether an
+external 32.768 kHz crystal is connected to the ESP32 or the internal 150 kHz
+RC oscillator is used. All current timer values are saved in the RTC memory
+before entering a sleep mode or restart and are restored after when waking up
+or restarting.
-- **esp_rtc_timer_32k**:
- Use the RTC hardware timer with the **external 32.768 kHz crystal**. If the
- external 32.768 kHz crystal is not available, the RTC hardware timer
- is used with the internal 150 kHz RC oscillator.
-
-If none of the modules above is enabled, the PLL-driven **emulated RTC timer**
-is used. In this case, the RTC hardware timer with the internal RC 150 kHz
-oscillator is only used in deep sleep mode and during a reset.
-
-@note The accuracy of the emulated RTC timer is better than the accuracy of the
-RTC hardware timer with the internal 150 kHz RC oscillator. If you have not
-connected an external 32.768 kHz crystal, you should use the default
-configuration.
+@note The RTT implementation is also used to implement a RTC (Real Time Clock)
+peripheral. For this purpose the module `rt_rtc` is automatically enabled
+when the feature `periph_rtc` is used.
\anchor esp32_uart_interfaces
## UART Interfaces [[TOC](#esp32_toc)]
diff --git a/cpu/esp32/include/periph_cpu.h b/cpu/esp32/include/periph_cpu.h
index 9cda64b2f1..b70c2ace93 100644
--- a/cpu/esp32/include/periph_cpu.h
+++ b/cpu/esp32/include/periph_cpu.h
@@ -409,6 +409,27 @@ typedef struct {
#define RNG_DATA_REG_ADDR (0x3ff75144)
/** @} */
+/**
+ * @name RTT and RTC configuration
+ * @{
+ */
+
+/**
+ * @brief RTT frequency definition
+ *
+ * The RTT frequency is always 32.768 kHz even if no external crystal is
+ * connected. In this case the RTT value counted with the internal 150 kHz
+ * RC oscillator is converted to a value for an RTT with 32.768 kHz.
+ */
+#define RTT_FREQUENCY (32768UL)
+
+/**
+ * @brief RTT is a 32-bit counter
+ */
+#define RTT_MAX_VALUE (0xFFFFFFFFUL)
+
+/** @} */
+
/**
* @name SPI configuration
*
diff --git a/cpu/esp32/include/rtt_arch.h b/cpu/esp32/include/rtt_arch.h
new file mode 100644
index 0000000000..4aee0b4f5e
--- /dev/null
+++ b/cpu/esp32/include/rtt_arch.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 Gunar Schorcht
+ *
+ * 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.
+ */
+
+/**
+ * @ingroup cpu_esp32
+ * @{
+ *
+ * @file
+ * @brief Architecture specific RTT functions for ESP32
+ *
+ * The RTT peripheral driver implements a 32-bit RTT counter with a frequency
+ * of 32.768 kHz. It uses either
+ *
+ * - the 48-bit RTC counter if an external 32.678 kHz crystal is connected or
+ * - the 64-bit microsecond system timer.
+ *
+ * For this purpose, a hardware abstraction layer is defined by a driver
+ * interface of the type rtt_hw_driver_t, which generally provides a
+ * 48-bit RTC counter with a frequency of 32.678 kHz. This way the RTT
+ * implementation always sees a 48-bit counter with a frequency of 32.768 kHz
+ * regardless of which hardware implementation is actually used.
+ *
+ * If pseudomodule `esp_rtc_timer_32` is enabled by the board definition and
+ * the 32.768 kHz crystal is actually connected, the 48-bit RTC counter
+ * is used. Otherwise, the 64 bit microsecond system timer is used.
+ *
+ * Since the 64-bit microsecond system timer does not work during light/deep
+ * or during a reboot, the status of the 64-bit microsecond system timer is
+ * saved in RTC memory before entering a sleep mode or reboot. When leaving
+ * the sleep mode or after a reboot, it will be updated from the RTC counter.
+ *
+ * @author Gunar Schorcht
+ */
+
+#ifndef RTT_ARCH_H
+#define RTT_ARCH_H
+
+#include "periph/rtt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Maximum value of the 48-bit RTT hardware counter
+ */
+#define RTT_HW_COUNTER_MAX ((1ULL << 48) - 1)
+
+/**
+ * @brief RTT hardware abstraction layer driver
+ */
+typedef struct {
+ /**
+ * @brief Init the current hardware counter
+ */
+ void (*init)(void);
+
+ /**
+ * @brief Get the current hardware counter value
+ * @return 48-bit counter value with a frequency of 32.768 kHz
+ */
+ uint64_t (*get_counter)(void);
+
+ /**
+ * @brief Set the hardware alarm
+ * @param[in] alarm alarm time as 32.768 kHz ticks
+ * @param[in] cb function called on alarm interrupt
+ * @param[in] arg argument used as parameter for the @p cb function
+ */
+ void (*set_alarm)(uint32_t alarm, rtt_cb_t cb, void *arg);
+
+ /**
+ * @brief Clear the hardware alarm
+ */
+ void (*clear_alarm)(void);
+
+ /**
+ * @brief Save the counter value before sleep or reboot if necessary
+ */
+ void (*save_counter)(void);
+
+ /**
+ * @brief Restore the counter value before sleep or reboot
+ * @param[in] in_init true if function is called after deep sleep or
+ * reboot, false otherwise
+ */
+ void (*restore_counter)(bool in_init);
+
+ /**
+ * @brief Enable the RTT hardware counter
+ */
+ void (*poweron)(void);
+
+ /**
+ * @brief Disable the RTT hardware counter
+ */
+ void (*poweroff)(void);
+
+} rtt_hw_driver_t;
+
+/**
+ * @brief Called before the power management enters a light or deep sleep mode
+ * @param mode sleep mode that is entered
+ * @return time to sleep in us
+ */
+uint64_t rtt_pm_sleep_enter(unsigned mode);
+
+/**
+ * @brief Called after the power management left light sleep mode
+ * @param cause wake-up cause
+ */
+void rtt_pm_sleep_exit(uint32_t cause);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RTT_ARCH_H */
+/** @} */
diff --git a/cpu/esp32/periph/rtt.c b/cpu/esp32/periph/rtt.c
new file mode 100644
index 0000000000..9324b60782
--- /dev/null
+++ b/cpu/esp32/periph/rtt.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2020 Gunar Schorcht
+ *
+ * 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.
+ */
+
+/**
+ * @ingroup cpu_esp32
+ * @ingroup drivers_periph_rtt
+ * @{
+ *
+ * @file
+ * @brief Low-level RTT driver implementation for ESP32
+ *
+ * @author Gunar Schorcht
+ *
+ * @}
+ */
+
+#include "cpu.h"
+#include "esp_attr.h"
+#include "esp_sleep.h"
+#include "irq_arch.h"
+#include "log.h"
+#include "periph/rtt.h"
+#include "rtt_arch.h"
+#include "syscalls.h"
+
+#define ENABLE_DEBUG (0)
+#include "debug.h"
+
+#define RTC_CLK_CAL_FRACT 19 /* fractional bits of calibration value */
+
+/* contains the values as given at the interface */
+typedef struct {
+ uint32_t alarm; /**< alarm value as set at the interface */
+ rtt_cb_t alarm_cb; /**< alarm callback */
+ rtt_cb_t overflow_cb; /**< overflow callback */
+ void *alarm_arg; /**< argument for alarm callback */
+ void *overflow_arg; /**< argument for overflow callback */
+ uint32_t alarm_active; /**< alarm that is currently active */
+ bool alarm_set; /**< indicates whether an alarm is active */
+ bool wakeup; /**< indicates whether next alarm is a wake-up */
+} rtt_counter_t;
+
+static rtt_counter_t rtt_counter;
+
+static uint32_t RTC_BSS_ATTR _rtt_offset;
+
+/* we can't include soc/rtc.h because of rtc_init declaration conflicts */
+extern uint32_t rtc_clk_slow_freq_get_hz(void);
+
+/* forward declaration of functions */
+void rtt_restore_counter(bool sys_time);
+static void _rtt_update_hw_alarm(void);
+static void IRAM_ATTR _rtt_isr(void *arg);
+
+/* forward declarations of driver functions */
+uint64_t _rtc_get_counter(void);
+
+/* declaration of hardware counters */
+extern const rtt_hw_driver_t _rtt_hw_sys_driver;
+extern const rtt_hw_driver_t _rtt_hw_rtc_driver;
+
+/* used hardware driver (default _rtt_hw_sys_driver) */
+static const rtt_hw_driver_t *_rtt_hw = &_rtt_hw_sys_driver;
+
+void rtt_init(void)
+{
+ if (IS_USED(MODULE_ESP_RTC_TIMER_32K)) {
+ /* check whether the 32.678 kHz crystal is working */
+ if (rtc_clk_slow_freq_get_hz() == 32768) {
+ _rtt_hw = &_rtt_hw_rtc_driver;
+ }
+ else {
+ LOG_ERROR("[rtt] 32.768 kHz crystal not used!\n");
+ }
+ }
+
+ DEBUG("%s rtt_offset=%u @rtc=%llu rtc_active=%d @sys_time=%llu\n", __func__,
+ _rtt_offset, _rtc_get_counter(),
+ (_rtt_hw == &_rtt_hw_sys_driver) ? 1 : 0, system_get_time_64());
+
+ /* init the hardware counter if necessary */
+ _rtt_hw->init();
+
+ /* restore counter from RTC after deep sleep or reboot */
+ rtt_restore_counter(true);
+
+ /* clear alarm settings */
+ rtt_clear_alarm();
+ rtt_clear_overflow_cb();
+
+ /* power on the module and enable interrupts */
+ rtt_poweron();
+}
+
+void rtt_poweron(void)
+{
+ _rtt_hw->poweron();
+}
+
+void rtt_poweroff(void)
+{
+ _rtt_hw->poweroff();
+}
+
+void rtt_set_overflow_cb(rtt_cb_t cb, void *arg)
+{
+ /* there is no overflow interrupt, we emulate */
+ rtt_counter.overflow_cb = cb;
+ rtt_counter.overflow_arg = arg;
+
+ _rtt_update_hw_alarm();
+}
+
+void rtt_clear_overflow_cb(void)
+{
+ /* there is no overflow interrupt, we emulate */
+ rtt_counter.overflow_cb = NULL;
+ rtt_counter.overflow_arg = NULL;
+
+ _rtt_update_hw_alarm();
+}
+
+uint32_t rtt_get_counter(void)
+{
+ /* we use only the lower 32 bit of the 48-bit RTC counter */
+ uint32_t counter = _rtt_hw->get_counter() + _rtt_offset;
+ DEBUG("%s counter=%u @sys_time=%u\n", __func__, counter, system_get_time());
+ return counter;
+}
+
+void rtt_set_counter(uint32_t counter)
+{
+ uint32_t _rtt_current = _rtt_hw->get_counter();
+ _rtt_offset = counter - _rtt_current;
+
+ DEBUG("%s set=%u rtt_offset=%u @rtt=%u\n",
+ __func__, counter, _rtt_offset, _rtt_current);
+
+ _rtt_update_hw_alarm();
+}
+
+void rtt_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg)
+{
+ uint32_t counter = rtt_get_counter();
+ rtt_counter.alarm = alarm;
+ rtt_counter.alarm_cb = cb;
+ rtt_counter.alarm_arg = arg;
+
+ DEBUG("%s alarm=%u @rtt=%u\n", __func__, alarm, counter);
+
+ _rtt_update_hw_alarm();
+}
+
+void rtt_clear_alarm(void)
+{
+ /* clear the alarm */
+ rtt_counter.alarm = 0;
+ rtt_counter.alarm_cb = NULL;
+ rtt_counter.alarm_arg = NULL;
+
+ DEBUG("%s @rtt=%u\n", __func__, (uint32_t)_rtt_hw->get_counter());
+
+ _rtt_update_hw_alarm();
+}
+
+uint32_t rtt_get_alarm(void)
+{
+ return rtt_counter.alarm;
+}
+
+void rtt_save_counter(void)
+{
+ _rtt_hw->save_counter();
+}
+
+void rtt_restore_counter(bool in_init)
+{
+ _rtt_hw->restore_counter(in_init);
+}
+
+uint64_t rtt_pm_sleep_enter(unsigned mode)
+{
+ rtt_save_counter();
+
+ if (!rtt_counter.alarm_set) {
+ return 0;
+ }
+
+ uint32_t counter = rtt_get_counter();
+ uint64_t t_diff = RTT_TICKS_TO_US(rtt_counter.alarm_active - counter);
+
+ DEBUG("%s rtt_alarm=%u @rtt=%u t_diff=%llu\n", __func__,
+ rtt_counter.alarm_active, counter, t_diff);
+
+ if (t_diff) {
+ rtt_counter.wakeup = true;
+ esp_sleep_enable_timer_wakeup(t_diff);
+ }
+ else {
+ rtt_counter.wakeup = false;
+ }
+
+ return t_diff;
+}
+
+void rtt_pm_sleep_exit(uint32_t cause)
+{
+ rtt_restore_counter(false);
+
+ if (cause == ESP_SLEEP_WAKEUP_TIMER) {
+ _rtt_isr(NULL);
+ }
+}
+
+static void _rtt_update_hw_alarm(void)
+{
+ if (rtt_counter.alarm_cb && ((rtt_counter.alarm > rtt_get_counter()) ||
+ (rtt_counter.overflow_cb == NULL))) {
+ /* alarm is the next event if either the alarm is greater than the
+ current counter value or the overflow callback is not set. */
+ rtt_counter.alarm_active = rtt_counter.alarm;
+ rtt_counter.alarm_set = true;
+ _rtt_hw->set_alarm(rtt_counter.alarm - _rtt_offset, _rtt_isr, NULL);
+ }
+ else if (rtt_counter.overflow_cb) {
+ /* otherwise the overflow is the next event if its callback is set */
+ rtt_counter.alarm_active = 0;
+ rtt_counter.alarm_set = true;
+ _rtt_hw->set_alarm(0 - _rtt_offset, _rtt_isr, NULL);
+ }
+ else {
+ rtt_counter.alarm_set = false;
+ _rtt_hw->clear_alarm();
+ }
+}
+
+static void IRAM_ATTR _rtt_isr(void *arg)
+{
+ DEBUG("%s\n", __func__);
+
+ uint32_t alarm = rtt_counter.alarm_active;
+
+ if (rtt_counter.wakeup) {
+ rtt_counter.wakeup = false;
+ DEBUG("%s wakeup alarm alarm=%u rtt_alarm=%u @rtt=%u\n",
+ __func__, alarm, rtt_counter.alarm_active, rtt_get_counter());
+ }
+
+ if ((alarm == rtt_counter.alarm) && rtt_counter.alarm_cb) {
+ DEBUG("%s alarm\n", __func__);
+ rtt_cb_t alarm_cb = rtt_counter.alarm_cb;
+ void * alarm_arg = rtt_counter.alarm_arg;
+ /* clear the alarm first, includes setting next alarm to overflow */
+ rtt_clear_alarm();
+ /* call the alarm handler afterwards if a callback is set */
+ if (alarm_cb) {
+ alarm_cb(alarm_arg);
+ }
+ }
+
+ if (alarm == 0) {
+ DEBUG("%s overflow\n", __func__);
+ /* set next alarm which is either an alarm if configured or overflow */
+ _rtt_update_hw_alarm();
+ /* call the overflow handler if set */
+ if (rtt_counter.overflow_cb) {
+ rtt_counter.overflow_cb(rtt_counter.overflow_arg);
+ }
+ }
+
+ DEBUG("%s next rtt=%u\n", __func__, rtt_counter.alarm_active);
+}
+
+uint32_t _rtt_hw_to_rtt_counter(uint32_t hw_counter)
+{
+ return hw_counter + _rtt_offset;
+}
diff --git a/cpu/esp32/periph/rtt_hw_rtc.c b/cpu/esp32/periph/rtt_hw_rtc.c
new file mode 100644
index 0000000000..162bb883e9
--- /dev/null
+++ b/cpu/esp32/periph/rtt_hw_rtc.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2020 Gunar Schorcht
+ *
+ * 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.
+ */
+
+/**
+ * @ingroup cpu_esp32
+ * @ingroup drivers_periph_rtt
+ * @{
+ *
+ * @file
+ * @brief Low-level RTT driver implementation for ESP32
+ *
+ * @author Gunar Schorcht
+ *
+ * @}
+ */
+
+#include "cpu.h"
+#include "esp_attr.h"
+#include "esp/common_macros.h"
+#include "esp_common.h"
+#include "esp_sleep.h"
+#include "irq_arch.h"
+#include "log.h"
+#include "periph/rtt.h"
+#include "rtt_arch.h"
+#include "soc/dport_reg.h"
+#include "soc/rtc_cntl_struct.h"
+#include "syscalls.h"
+#include "timex.h"
+
+#define ENABLE_DEBUG (0)
+#include "debug.h"
+
+#define RTC_CLK_CAL_FRACT 19 /* fractional bits of calibration value */
+
+typedef struct {
+ uint32_t alarm_set; /**< alarm set at interface */
+ rtt_cb_t alarm_cb; /**< alarm callback */
+ void *alarm_arg; /**< argument for alarm callback */
+} _rtc_alarm_t;
+
+static _rtc_alarm_t _rtc_alarm;
+
+/* we can't include soc/rtc.h because of rtc_init declaration conflicts */
+extern uint32_t esp_clk_slowclk_cal_get(void);
+
+/* convert hardware counter to 32-bit RTT counter */
+uint32_t _rtt_hw_to_rtt_counter(uint32_t hw_counter);
+
+static void IRAM _rtc_isr(void *arg);
+
+/* converts a 48-bit RTC counter value to microseconds */
+uint64_t _rtc_counter_to_us(uint64_t raw)
+{
+ const uint32_t cal = esp_clk_slowclk_cal_get();
+ return ((((raw >> 32) * cal) << (32 - RTC_CLK_CAL_FRACT)) + /* high part */
+ (((raw & 0xffffffff) * cal) >> RTC_CLK_CAL_FRACT)); /* low part */
+}
+
+static void _rtc_init(void)
+{
+}
+
+static void _rtc_poweron(void)
+{
+ /* route all interrupt sources to the same RTT level type interrupt */
+ intr_matrix_set(PRO_CPU_NUM, ETS_RTC_CORE_INTR_SOURCE, CPU_INUM_RTC);
+
+ /* set interrupt handler and enable the CPU interrupt */
+ xt_set_interrupt_handler(CPU_INUM_RTC, _rtc_isr, NULL);
+ xt_ints_on(BIT(CPU_INUM_RTC));
+}
+
+static void _rtc_poweroff(void)
+{
+ /* reset interrupt handler and disable the CPU interrupt */
+ xt_ints_off(BIT(CPU_INUM_RTC));
+ xt_set_interrupt_handler(CPU_INUM_RTC, NULL, NULL);
+}
+
+uint64_t _rtc_get_counter(void)
+{
+ /* trigger timer register update */
+ RTCCNTL.time_update.update = 1;
+ /* wait until values in registers are valid */
+ while (!RTCCNTL.time_update.valid) {
+ ets_delay_us(1);
+ }
+ /* read the time from 48-bit counter and return */
+ return (((uint64_t)RTCCNTL.time1.val) << 32) + RTCCNTL.time0;
+}
+
+static void _rtc_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg)
+{
+ /* compute the time difference for 32.768 kHz as 32-bit value */
+ uint64_t rtc_counter = _rtc_get_counter();
+ uint32_t rtt_diff = alarm - rtc_counter;
+
+ /* use computed time difference directly to set the RTC counter alarm */
+ uint64_t rtc_alarm = (rtc_counter + rtt_diff) & RTT_HW_COUNTER_MAX;
+
+ DEBUG("%s alarm=%u rtt_diff=%u rtc_alarm=%llu @rtc=%llu\n",
+ __func__, alarm, rtt_diff, rtc_alarm, rtc_counter);
+
+ /* save the alarm configuration for interrupt handling */
+ _rtc_alarm.alarm_set = alarm;
+ _rtc_alarm.alarm_cb = cb;
+ _rtc_alarm.alarm_arg = arg;
+
+ /* set the timer value */
+ RTCCNTL.slp_timer0 = rtc_alarm & 0xffffffff;
+ RTCCNTL.slp_timer1.slp_val_hi = rtc_alarm >> 32;
+
+ DEBUG("%s %08x%08x \n", __func__,
+ RTCCNTL.slp_timer1.slp_val_hi, RTCCNTL.slp_timer0);
+
+ /* enable RTC timer alarm */
+ RTCCNTL.slp_timer1.main_timer_alarm_en = 1;
+
+ /* clear and enable RTC timer interrupt */
+ RTCCNTL.int_clr.rtc_main_timer = 1;
+ RTCCNTL.int_ena.rtc_main_timer = 1;
+}
+
+static void _rtc_clear_alarm(void)
+{
+ /* disable alarms first */
+ RTCCNTL.slp_timer1.main_timer_alarm_en = 0;
+
+ /* clear the bit in interrupt enable and status register */
+ RTCCNTL.int_clr.rtc_main_timer = 0;
+ RTCCNTL.int_ena.rtc_main_timer = 0;
+
+ /* reset the alarm configuration for interrupt handling */
+ _rtc_alarm.alarm_set = 0;
+ _rtc_alarm.alarm_cb = NULL;
+ _rtc_alarm.alarm_arg = NULL;
+}
+
+static void _rtc_save_counter(void)
+{
+}
+
+static void _rtc_restore_counter(bool in_init)
+{
+ (void)in_init;
+}
+
+static void IRAM _rtc_isr(void *arg)
+{
+ /* disable alarms first */
+ RTCCNTL.slp_timer1.main_timer_alarm_en = 0;
+
+ /* clear the bit in interrupt enable and status register */
+ RTCCNTL.int_clr.rtc_main_timer = 0;
+ RTCCNTL.int_ena.rtc_main_timer = 0;
+
+ /* save the lower 32 bit of the current counter value */
+ uint32_t counter = _rtc_get_counter();
+
+ DEBUG("%s %u\n", __func__, counter);
+
+ if (_rtc_alarm.alarm_cb) {
+ DEBUG("%s alarm %u\n", __func__, counter);
+
+ rtt_cb_t alarm_cb = _rtc_alarm.alarm_cb;
+ void *alarm_arg = _rtc_alarm.alarm_arg;
+
+ /* clear the alarm first */
+ _rtc_alarm.alarm_cb = NULL;
+ _rtc_alarm.alarm_arg = NULL;
+
+ /* call the alarm handler afterwards if callback was defined */
+ alarm_cb(alarm_arg);
+ }
+}
+
+const rtt_hw_driver_t _rtt_hw_rtc_driver = {
+ .init = _rtc_init,
+ .get_counter = _rtc_get_counter,
+ .set_alarm = _rtc_set_alarm,
+ .clear_alarm = _rtc_clear_alarm,
+ .poweron = _rtc_poweron,
+ .poweroff = _rtc_poweroff,
+ .save_counter = _rtc_save_counter,
+ .restore_counter = _rtc_restore_counter,
+};
diff --git a/cpu/esp32/periph/rtt_hw_sys.c b/cpu/esp32/periph/rtt_hw_sys.c
new file mode 100644
index 0000000000..4a003f8efb
--- /dev/null
+++ b/cpu/esp32/periph/rtt_hw_sys.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2020 Gunar Schorcht
+ *
+ * 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.
+ */
+
+/**
+ * @ingroup cpu_esp32
+ * @ingroup drivers_periph_rtt
+ * @{
+ *
+ * @file
+ * @brief Low-level RTT driver implementation for ESP32
+ *
+ * @author Gunar Schorcht
+ *
+ * @}
+ */
+
+#include "cpu.h"
+#include "esp_attr.h"
+#include "esp/common_macros.h"
+#include "esp_sleep.h"
+#include "irq_arch.h"
+#include "log.h"
+#include "periph/rtt.h"
+#include "rtt_arch.h"
+#include "soc/timer_group_struct.h"
+#include "syscalls.h"
+#include "timex.h"
+
+#define ENABLE_DEBUG (0)
+#include "debug.h"
+
+#define TIMER_SYSTEM_GROUP TIMERG0
+#define TIMER_SYSTEM_INT_MASK BIT(0)
+#define TIMER_SYSTEM_INT_SRC ETS_TG0_T0_LEVEL_INTR_SOURCE
+
+#define SYS_US_TO_TICKS(us) ((((uint64_t)us) << 15) / US_PER_SEC)
+#define SYS_TICKS_TO_US(cnt) (((uint64_t)cnt * US_PER_SEC) >> 15)
+
+typedef struct {
+ uint32_t alarm_set; /**< alarm set at interface */
+ rtt_cb_t alarm_cb; /**< alarm callback */
+ void *alarm_arg; /**< argument for alarm callback */
+} _sys_alarm_t;
+
+static _sys_alarm_t _sys_alarm;
+
+/* variables used to save counters during sleep or reboot */
+static uint64_t RTC_BSS_ATTR _rtc_counter_saved;
+static uint64_t RTC_BSS_ATTR _sys_counter_saved;
+
+/* the offset of the system time to the RTC time in microseconds */
+static uint64_t _sys_counter_offset;
+
+/* forward declarations of functions required from RTC counter */
+extern uint64_t _rtc_get_counter(void);
+extern uint64_t _rtc_counter_to_us(uint64_t raw);
+
+static void IRAM _sys_isr(void *arg);
+
+static void _sys_init(void)
+{
+}
+
+static void _sys_poweron(void)
+{
+ /* route all interrupt sources to the same RTT level type interrupt */
+ intr_matrix_set(PRO_CPU_NUM, TIMER_SYSTEM_INT_SRC, CPU_INUM_RTC);
+
+ /* set interrupt handler and enable the CPU interrupt */
+ xt_set_interrupt_handler(CPU_INUM_RTC, _sys_isr, NULL);
+ xt_ints_on(BIT(CPU_INUM_RTC));
+}
+
+static void _sys_poweroff(void)
+{
+ /* reset interrupt handler and disable the CPU interrupt */
+ xt_ints_off(BIT(CPU_INUM_RTC));
+ xt_set_interrupt_handler(CPU_INUM_RTC, NULL, NULL);
+}
+
+static uint64_t _sys_get_counter(void)
+{
+ /* convert the 64-bit microsecond system time to 48-bit 32.768 kHz time */
+ return SYS_US_TO_TICKS(system_get_time_64() + _sys_counter_offset) & RTT_HW_COUNTER_MAX;
+}
+
+static void _sys_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg)
+{
+ /* compute the time difference for 32.768 kHz as 32-bit value */
+ uint32_t rtt_diff = alarm - _sys_get_counter();
+
+ /*
+ * convert the computed time difference for 32.768 kHz to a 64-bit
+ * microsecond value and determine the alarm time for the 64-bit
+ * microsecond system timer
+ */
+ uint64_t _sys_diff = SYS_TICKS_TO_US(rtt_diff);
+ uint64_t _sys_time = system_get_time_64();
+ uint64_t _sys_alarm_time = _sys_time + _sys_diff;
+
+ DEBUG("%s alarm=%u rtt_diff=%u "
+ "sys_diff=%llu sys_alarm=%llu @sys_time=%llu\n", __func__,
+ alarm, rtt_diff, _sys_diff, _sys_alarm_time, _sys_time);
+
+ /* save the alarm configuration for interrupt handling */
+ _sys_alarm.alarm_set = alarm;
+ _sys_alarm.alarm_cb = cb;
+ _sys_alarm.alarm_arg = arg;
+
+ /* set the timer value */
+ TIMER_SYSTEM.alarm_high = (uint32_t)(_sys_alarm_time >> 32);
+ TIMER_SYSTEM.alarm_low = (uint32_t)(_sys_alarm_time & 0xffffffff);
+
+ /* clear the bit in status and set the bit in interrupt enable */
+ TIMER_SYSTEM_GROUP.int_clr_timers.val |= TIMER_SYSTEM_INT_MASK;
+ TIMER_SYSTEM_GROUP.int_ena.val |= TIMER_SYSTEM_INT_MASK;
+
+ /* enable the timer alarm */
+ TIMER_SYSTEM.config.level_int_en = 1;
+ TIMER_SYSTEM.config.alarm_en = 1;
+}
+
+static void _sys_clear_alarm(void)
+{
+ /* disable alarms first */
+ TIMER_SYSTEM.config.level_int_en = 0;
+ TIMER_SYSTEM.config.alarm_en = 0;
+
+ /* clear the bit in interrupt enable and status register */
+ TIMER_SYSTEM_GROUP.int_ena.val &= ~TIMER_SYSTEM_INT_MASK;
+ TIMER_SYSTEM_GROUP.int_clr_timers.val |= TIMER_SYSTEM_INT_MASK;
+
+ /* reset the alarm configuration for interrupt handling */
+ _sys_alarm.alarm_set = 0;
+ _sys_alarm.alarm_cb = NULL;
+ _sys_alarm.alarm_arg = NULL;
+}
+
+static void _sys_save_counter(void)
+{
+ critical_enter();
+
+ /* save counters for synchronization after wakeup or reboot */
+ _rtc_counter_saved = _rtc_get_counter();
+ _sys_counter_saved = system_get_time_64() + _sys_counter_offset;
+
+ critical_exit();
+
+ DEBUG("%s rtc_time_saved=%llu sys_time_saved=%llu\n", __func__,
+ _rtc_counter_saved, _sys_counter_saved);
+}
+
+static void _sys_restore_counter(bool in_init)
+{
+ critical_enter();
+
+ /* synchronize RTC counter and the 64-bit microsecond system timer */
+ uint64_t _rtc_time_diff = _rtc_get_counter() - _rtc_counter_saved;
+ _sys_counter_offset += _rtc_counter_to_us(_rtc_time_diff & RTT_HW_COUNTER_MAX);
+ _sys_counter_offset += (in_init) ? _sys_counter_saved : 0;
+
+ critical_exit();
+
+ DEBUG("%s rtc_time_saved=%llu rtc_time_diff=%llu "
+ "sys_time_saved=%llu sys_time_offset=%llu\n", __func__,
+ _rtc_counter_saved, _rtc_time_diff,
+ _sys_counter_saved, _sys_counter_offset);
+}
+
+static void IRAM _sys_isr(void *arg)
+{
+ if (!(TIMER_SYSTEM_GROUP.int_st_timers.val & TIMER_SYSTEM_INT_MASK)) {
+ /* return in case of another timer interrupt */
+ return;
+ }
+
+ /* disable alarms first */
+ TIMER_SYSTEM.config.level_int_en = 0;
+ TIMER_SYSTEM.config.alarm_en = 0;
+
+ /* clear the bit in interrupt enable and status register */
+ TIMER_SYSTEM_GROUP.int_ena.val &= ~TIMER_SYSTEM_INT_MASK;
+ TIMER_SYSTEM_GROUP.int_clr_timers.val |= TIMER_SYSTEM_INT_MASK;
+
+ /* save the lower 32 bit of the current counter value */
+ uint32_t counter = _sys_get_counter();
+
+ DEBUG("%s %u\n", __func__, counter);
+
+ if (_sys_alarm.alarm_cb) {
+ DEBUG("%s alarm %u\n", __func__, counter);
+
+ rtt_cb_t alarm_cb = _sys_alarm.alarm_cb;
+ void *alarm_arg = _sys_alarm.alarm_arg;
+
+ /* clear the alarm first */
+ _sys_alarm.alarm_cb = NULL;
+ _sys_alarm.alarm_arg = NULL;
+
+ /* call the alarm handler afterwards if callback was defined */
+ alarm_cb(alarm_arg);
+ }
+}
+
+const rtt_hw_driver_t _rtt_hw_sys_driver = {
+ .init = _sys_init,
+ .get_counter = _sys_get_counter,
+ .set_alarm = _sys_set_alarm,
+ .clear_alarm = _sys_clear_alarm,
+ .poweron = _sys_poweron,
+ .poweroff = _sys_poweroff,
+ .save_counter = _sys_save_counter,
+ .restore_counter = _sys_restore_counter,
+};