1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/esp32/periph/rtt.c
2023-04-17 07:32:48 +02:00

288 lines
7.6 KiB
C

/*
* Copyright (C) 2022 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 <gunar@schorcht.net>
*
* @}
*/
/* RIOT headers have to be included before ESP-IDF headers! */
#include "cpu.h"
#include "irq_arch.h"
#include "log.h"
#include "periph/rtt.h"
#include "rtt_arch.h"
#include "syscalls.h"
/* ESP-IDF headers */
#include "esp_attr.h"
#include "esp_sleep.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 _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=%" PRIu32 " @rtc=%" PRIu64
" rtc_active=%d @sys_time=%" PRIi64 "\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=%" PRIu32 " @sys_time=%" PRIu32" \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=%" PRIu32 " rtt_offset=%" PRIu32 " @rtt=%" PRIu32 "\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=%" PRIu32 " @rtt=%" PRIu32 "\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=%" PRIu32 "\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=%" PRIu32 " @rtt=%" PRIu32 " 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=%" PRIu32 " rtt_alarm=%" PRIu32 " @rtt=%" PRIu32 "\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=%" PRIu32 "\n", __func__, rtt_counter.alarm_active);
}
uint32_t _rtt_hw_to_rtt_counter(uint32_t hw_counter)
{
return hw_counter + _rtt_offset;
}