/* * 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_esp8266 * @ingroup drivers_periph_rtt * @{ * * @file * @brief Low-level RTT driver implementation for ESP8266 * * @author Gunar Schorcht * * @} */ #define ENABLE_DEBUG 0 #include "debug.h" #include "cpu.h" #include "log.h" #include "periph/rtt.h" #include "esp_common.h" #include "esp/common_macros.h" #include "esp/dport_regs.h" #include "esp/rtc_regs.h" #include "sdk/sdk.h" #define FRC2_CLK_DIV_256 2 /* divider for the 80 MHz AHB clock */ /** * FRC2 is a 32-bit countup timer, triggers interrupt when reaches alarm value. */ typedef struct { uint32_t load; uint32_t count; union { struct { uint32_t intr_hold : 1; uint32_t reserved1 : 1; uint32_t clk_div : 2; uint32_t reserved2 : 2; uint32_t reload : 1; uint32_t enable : 1; uint32_t intr_sta : 1; uint32_t reserved3 : 23; }; uint32_t val; } ctrl; union { struct { uint32_t clear : 1; uint32_t reserved1: 31; }; uint32_t val; } intr; uint32_t alarm; } frc2_struct_t; /* * linker script esp8266.peripherals.ld will make sure this points to the * hardware register address */ extern volatile frc2_struct_t frc2; typedef struct { uint32_t alarm; /**< alarm */ 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 */ } rtt_config_t; static rtt_config_t rtt_config; static uint32_t RTC_BSS_ATTR _rtt_counter_saved; static uint32_t RTC_BSS_ATTR _rtc_counter_saved; extern uint32_t pm_rtc_clock_cali_proc(void); extern uint32_t pm_rtc2usec(uint32_t rtc_cycles, uint32_t period); void rtt_restore_counter(void); void IRAM rtt_cb(void *arg) { /* triggered alarm */ uint32_t alarm = frc2.alarm; if (alarm == rtt_config.alarm) { rtt_cb_t alarm_cb = rtt_config.alarm_cb; void * alarm_arg = rtt_config.alarm_arg; /* clear the alarm first (includes setting next alarm to overflow) */ rtt_clear_alarm(); /* call the alarm handler afterwards if callback was defined*/ if (alarm_cb) { alarm_cb(alarm_arg); } } if (alarm == 0) { /* set next alarm which is either an alarm if configured or overflow */ frc2.alarm = rtt_config.alarm; /* call the overflow handler if configured */ if (rtt_config.overflow_cb) { rtt_config.overflow_cb(rtt_config.overflow_arg); } } } void rtt_init(void) { DEBUG("%s saved rtt=%u rtc=%u\n", __func__, _rtt_counter_saved, _rtc_counter_saved); frc2.ctrl.clk_div = FRC2_CLK_DIV_256; frc2.ctrl.reload = 0; frc2.ctrl.intr_hold = 0; frc2.ctrl.enable = 1; /* initialize rtt_config structure after reboot or deep sleep */ rtt_clear_alarm(); rtt_clear_overflow_cb(); if (_rtt_counter_saved || _rtc_counter_saved) { /* if not in init after power on, restore the RTT counter value */ rtt_restore_counter(); } else { frc2.load = 0; DEBUG("%s after power on\n", __func__); } /* emulate overflow interrupt */ frc2.alarm = 0; ets_isr_attach (ETS_FRC2_INUM, rtt_cb, NULL); ets_isr_unmask (BIT(ETS_FRC2_INUM)); DPORT.INT_ENABLE |= DPORT_INT_ENABLE_FRC2; } void rtt_poweron(void) { /* power on simply reactivates the FRC2 counter */ frc2.ctrl.enable = 1; } void rtt_poweroff(void) { /* power off simply deactivates the FRC2 counter */ frc2.ctrl.enable = 0; } void rtt_set_overflow_cb(rtt_cb_t cb, void *arg) { /* there is no overflow interrupt, we emulate */ rtt_config.overflow_cb = cb; rtt_config.overflow_arg = arg; } void rtt_clear_overflow_cb(void) { /* there is no overflow interrupt, we emulate */ rtt_config.overflow_cb = NULL; rtt_config.overflow_arg = NULL; } uint32_t rtt_get_counter(void) { return frc2.count; } void rtt_set_counter(uint32_t counter) { frc2.load = counter; if (counter > frc2.alarm) { /* overflow is the next interrupt event */ frc2.alarm = 0; } } void rtt_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg) { rtt_config.alarm = alarm; rtt_config.alarm_cb = cb; rtt_config.alarm_arg = arg; if (frc2.count < alarm) { frc2.alarm = alarm; } } uint32_t rtt_get_alarm(void) { return rtt_config.alarm; } void rtt_clear_alarm(void) { frc2.alarm = 0; rtt_config.alarm = 0; rtt_config.alarm_cb = NULL; rtt_config.alarm_arg = NULL; } void rtt_save_counter(void) { /* save counters before going to sleep or reboot */ _rtt_counter_saved = frc2.count; _rtc_counter_saved = RTC.COUNTER; DEBUG("%s saved rtt=%u rtc=%u\n", __func__, _rtt_counter_saved, _rtc_counter_saved); } void rtt_restore_counter(void) { uint32_t rtc_diff = RTC.COUNTER - _rtc_counter_saved; uint32_t rtc_diff_us = pm_rtc2usec(rtc_diff, pm_rtc_clock_cali_proc()); uint32_t rtt_diff = RTT_US_TO_TICKS(rtc_diff_us); frc2.load = _rtt_counter_saved + rtt_diff; DEBUG("%s rtc_diff=%u rtt_diff=%u load=%u\n", __func__, rtc_diff, rtt_diff, _rtt_counter_saved + rtt_diff); }