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_hw_rtc.c
2023-04-17 07:32:48 +02:00

212 lines
6.0 KiB
C

/*
* 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 <gunar@schorcht.net>
*
* @}
*/
/* RIOT headers have to be included before ESP-IDF headers! */
#include "cpu.h"
#include "esp/common_macros.h"
#include "esp_common.h"
#include "irq_arch.h"
#include "log.h"
#include "periph/rtt.h"
#include "rtt_arch.h"
#include "syscalls.h"
#include "timex.h"
/* ESP-IDF headers */
#include "esp_attr.h"
#include "esp_sleep.h"
#include "hal/interrupt_controller_types.h"
#include "hal/interrupt_controller_ll.h"
#include "rom/ets_sys.h"
#include "soc/periph_defs.h"
#include "soc/rtc_cntl_struct.h"
#if __xtensa__
#include "soc/dport_reg.h"
#include "xtensa/xtensa_api.h"
#endif
#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_RTT);
/* set interrupt handler and enable the CPU interrupt */
intr_cntrl_ll_set_int_handler(CPU_INUM_RTT, _rtc_isr, NULL);
intr_cntrl_ll_enable_interrupts(BIT(CPU_INUM_RTT));
}
static void _rtc_poweroff(void)
{
/* reset interrupt handler and disable the CPU interrupt */
intr_cntrl_ll_disable_interrupts(BIT(CPU_INUM_RTT));
intr_cntrl_ll_set_int_handler(CPU_INUM_RTT, NULL, NULL);
}
uint64_t _rtc_get_counter(void)
{
/* trigger timer register update */
RTCCNTL.time_update.update = 1;
#if defined(CPU_FAM_ESP32)
/* 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;
#elif defined(CPU_FAM_ESP32C3) || defined(CPU_FAM_ESP32S2) || defined(CPU_FAM_ESP32S3)
/* read the time from 48-bit counter and return */
return (((uint64_t)RTCCNTL.time_high0.val) << 32) + RTCCNTL.time_low0;
#else
#error "MCU implementation is missing"
#endif
}
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=%" PRIu32 " rtt_diff=%" PRIu32
" rtc_alarm=%" PRIu64 " @rtc=%" PRIu64 "\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__,
(unsigned)RTCCNTL.slp_timer1.slp_val_hi, (unsigned)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 %" PRIu32 "\n", __func__, counter);
if (_rtc_alarm.alarm_cb) {
DEBUG("%s alarm %" PRIu32 "\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,
};