WDT: Add support for XTAL32K Watchdog timer

This commit is contained in:
Marius Vikhammer 2021-08-30 11:30:12 +08:00
parent 375145ecdb
commit 4869b3cd4a
27 changed files with 697 additions and 9 deletions

View File

@ -646,6 +646,7 @@ menu "ESP32-specific"
select ESP_SYSTEM_RTC_EXT_XTAL
config ESP32_RTC_CLK_SRC_EXT_OSC
bool "External 32kHz oscillator at 32K_XN pin"
select ESP_SYSTEM_RTC_EXT_OSC
config ESP32_RTC_CLK_SRC_INT_8MD256
bool "Internal 8.5MHz oscillator, divided by 256 (~33kHz)"
endchoice

View File

@ -157,6 +157,7 @@ menu "ESP32C3-Specific"
select ESP_SYSTEM_RTC_EXT_XTAL
config ESP32C3_RTC_CLK_SRC_EXT_OSC
bool "External 32kHz oscillator at 32K_XP pin"
select ESP_SYSTEM_RTC_EXT_OSC
config ESP32C3_RTC_CLK_SRC_INT_8MD256
bool "Internal 8MHz oscillator, divided by 256 (~32kHz)"
endchoice

View File

@ -150,6 +150,7 @@ menu "ESP32H2-Specific"
select ESP_SYSTEM_RTC_EXT_XTAL
config ESP32H2_RTC_CLK_SRC_EXT_OSC
bool "External 32kHz oscillator at 32K_XP pin"
select ESP_SYSTEM_RTC_EXT_OSC
config ESP32H2_RTC_CLK_SRC_INT_8MD256
bool "Internal 8MHz oscillator, divided by 256 (~32kHz)"
endchoice

View File

@ -382,6 +382,7 @@ menu "ESP32S2-specific"
select ESP_SYSTEM_RTC_EXT_XTAL
config ESP32S2_RTC_CLK_SRC_EXT_OSC
bool "External 32kHz oscillator at 32K_XN pin"
select ESP_SYSTEM_RTC_EXT_OSC
config ESP32S2_RTC_CLK_SRC_INT_8MD256
bool "Internal 8MHz oscillator, divided by 256 (~32kHz)"
endchoice

View File

@ -430,6 +430,7 @@ menu "ESP32S3-Specific"
select ESP_SYSTEM_RTC_EXT_XTAL
config ESP32S3_RTC_CLK_SRC_EXT_OSC
bool "External 32kHz oscillator at 32K_XP pin"
select ESP_SYSTEM_RTC_EXT_OSC
config ESP32S3_RTC_CLK_SRC_INT_8MD256
bool "Internal 8MHz oscillator, divided by 256 (~32kHz)"
endchoice

View File

@ -43,7 +43,11 @@ uint32_t rtc_clk_cal_internal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
} else if (slow_freq == RTC_SLOW_FREQ_8MD256) {
cal_clk = RTC_CAL_8MD256;
}
} else if (cal_clk == RTC_CAL_INTERNAL_OSC) {
cal_clk = RTC_CAL_RTC_MUX;
}
/* Enable requested clock (150k clock is always on) */
int dig_32k_xtal_state = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN);
if (cal_clk == RTC_CAL_32K_XTAL && !dig_32k_xtal_state) {

View File

@ -141,7 +141,10 @@ uint32_t rtc_clk_cal_internal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles, ui
} else if (slow_freq == RTC_SLOW_FREQ_8MD256) {
cal_clk = RTC_CAL_8MD256;
}
} else if (cal_clk == RTC_CAL_INTERNAL_OSC) {
cal_clk = RTC_CAL_RTC_MUX;
}
/* Enable requested clock (90k clock is always on) */
int dig_32k_xtal_state = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN);
if (cal_clk == RTC_CAL_32K_XTAL && !dig_32k_xtal_state) {

View File

@ -42,7 +42,10 @@ uint32_t rtc_clk_cal_internal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
} else if (slow_freq == RTC_SLOW_FREQ_8MD256) {
cal_clk = RTC_CAL_8MD256;
}
} else if (cal_clk == RTC_CAL_INTERNAL_OSC) {
cal_clk = RTC_CAL_RTC_MUX;
}
/* Enable requested clock (150k clock is always on) */
int dig_32k_xtal_state = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN);
if (cal_clk == RTC_CAL_32K_XTAL && !dig_32k_xtal_state) {

View File

@ -21,7 +21,8 @@ else()
"system_time.c"
"stack_check.c"
"task_wdt.c"
"ubsan.c")
"ubsan.c"
"xt_wdt.c")
if(NOT (${target} STREQUAL "esp32c3") AND NOT (${target} STREQUAL "esp32h2"))
list(APPEND srcs "dbg_stubs.c")

View File

@ -53,6 +53,12 @@ menu "ESP System Settings"
bool
default n
config ESP_SYSTEM_RTC_EXT_OSC
# This is a High Layer Kconfig option, invisible, can be selected by other Kconfig option
# e.g. It will be selected on when ESPX_RTC_CLK_SRC_EXT_OSC is on
bool
default n
config ESP_SYSTEM_RTC_EXT_XTAL_BOOTSTRAP_CYCLES
int "Bootstrap cycles for external 32kHz crystal"
depends on ESP_SYSTEM_RTC_EXT_XTAL
@ -389,6 +395,31 @@ menu "ESP System Settings"
If this option is enabled, the Task Wtachdog Timer will wach the CPU1
Idle Task.
config ESP_XT_WDT
bool "Initialize XTAL32K watchdog timer on startup"
depends on !IDF_TARGET_ESP32 && (ESP_SYSTEM_RTC_EXT_OSC || ESP_SYSTEM_RTC_EXT_XTAL)
default n
help
This watchdog timer can detect oscillation failure of the XTAL32K_CLK. When such a failure
is detected the hardware can be set up to automatically switch to BACKUP32K_CLK and generate
an interrupt.
config ESP_XT_WDT_TIMEOUT
int "XTAL32K watchdog timeout period"
depends on ESP_XT_WDT
range 1 255
default 200
help
Timeout period configuration for the XTAL32K watchdog timer based on RTC_CLK.
config ESP_XT_WDT_BACKUP_CLK_ENABLE
bool "Automatically switch to BACKUP32K_CLK when timer expires"
depends on ESP_XT_WDT
default y
help
Enable this to automatically switch to BACKUP32K_CLK as the source of RTC_SLOW_CLK when
the watchdog timer expires.
config ESP_PANIC_HANDLER_IRAM
bool "Place panic handler code in IRAM"
default n

View File

@ -0,0 +1,63 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "esp_intr_alloc.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief esp_xt_wdt configuration struct
*
*/
typedef struct {
uint8_t timeout; /*!< Watchdog timeout */
bool auto_backup_clk_enable; /*!< Enable automatic switch to backup clock at timeout */
} esp_xt_wdt_config_t;
/* Callback function for WDT interrupt*/
typedef void (*esp_xt_callback_t)(void *arg);
/**
* @brief Initializes the xtal32k watchdog timer
*
* @param cfg Pointer to configuration struct
* @return esp_err_t
* - ESP_OK: XTWDT was successfully enabled
* - ESP_ERR_NO_MEM: Failed to allocate ISR
*/
esp_err_t esp_xt_wdt_init(const esp_xt_wdt_config_t *cfg);
/**
* @brief Register a callback function that will be called when the watchdog
* times out.
*
* @note This function will be called from an interrupt context where the cache might be disabled.
* Thus the function should be placed in IRAM and must not perform any blocking operations.
*
* Only one callback function can be registered, any call to esp_xt_wdt_register_callback
* will override the previous callback function.
*
* @param func The callback function to register
* @param arg Pointer to argument that will be passed to the callback function
*/
void esp_xt_wdt_register_callback(esp_xt_callback_t func, void *arg);
/**
* @brief Restores the xtal32k clock and re-enables the WDT
*
*/
void esp_xt_wdt_restore_clk(void);
#ifdef __cplusplus
}
#endif

View File

@ -41,6 +41,7 @@
#include "esp_flash_encrypt.h"
#include "esp_secure_boot.h"
#include "esp_sleep.h"
#include "esp_xt_wdt.h"
/***********************************************/
// Headers for other components init functions
@ -344,6 +345,15 @@ static void do_core_init(void)
// Note: in some configs this may read flash, so placed after flash init
esp_secure_boot_init_checks();
#endif
#if CONFIG_ESP_XT_WDT
esp_xt_wdt_config_t cfg = {
.timeout = CONFIG_ESP_XT_WDT_TIMEOUT,
.auto_backup_clk_enable = CONFIG_ESP_XT_WDT_BACKUP_CLK_ENABLE,
};
err = esp_xt_wdt_init(&cfg);
assert(err == ESP_OK && "Failed to init xtwdt");
#endif
}
static void do_secondary_init(void)

View File

@ -0,0 +1,95 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_xt_wdt.h"
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_attr.h"
#include "esp_intr_alloc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#if SOC_XT_WDT_SUPPORTED
#include "driver/rtc_cntl.h"
#include "hal/xt_wdt_hal.h"
#include "hal/xt_wdt_ll.h"
#include "soc/rtc.h"
#define RTC_CLK_CAL_CYCLES 500
const static char *TAG = "esp_xt_wdt";
static xt_wdt_hal_context_t s_hal_ctx;
static esp_xt_callback_t s_callback_func;
static void *s_callback_arg;
portMUX_TYPE s_xt_wdt_lock = portMUX_INITIALIZER_UNLOCKED;
static IRAM_ATTR void rtc_xt_wdt_default_isr_handler(void *arg)
{
ESP_EARLY_LOGE(TAG, "XTAL32K watchdog timer got triggered");
portENTER_CRITICAL_ISR(&s_xt_wdt_lock);
if (s_callback_func) {
(*s_callback_func)(s_callback_arg);
}
portEXIT_CRITICAL_ISR(&s_xt_wdt_lock);
}
esp_err_t esp_xt_wdt_init(const esp_xt_wdt_config_t *cfg)
{
esp_err_t ret = ESP_OK;
xt_wdt_hal_config_t hal_config = {
.timeout = cfg->timeout,
};
xt_wdt_hal_init(&s_hal_ctx, &hal_config);
if (cfg->auto_backup_clk_enable) {
/* Estimate frequency of internal RTC oscillator */
uint32_t rtc_clk_frequency_khz = rtc_clk_freq_cal(rtc_clk_cal(RTC_CAL_INTERNAL_OSC, RTC_CLK_CAL_CYCLES)) / 1000;
ESP_LOGD(TAG, "Calibrating backup clock from rtc clock with frequency %d", rtc_clk_frequency_khz);
xt_wdt_hal_enable_backup_clk(&s_hal_ctx, rtc_clk_frequency_khz);
}
ESP_GOTO_ON_ERROR(rtc_isr_register(rtc_xt_wdt_default_isr_handler, NULL, XT_WDT_LL_XTAL32_DEAD_INTR_MASK), err, TAG, "Failed to register isr");
xt_wdt_hal_enable(&s_hal_ctx, 1);
return ESP_OK;
err:
return ret;
}
void esp_xt_wdt_restore_clk(void)
{
xt_wdt_hal_enable(&s_hal_ctx, false);
REG_CLR_BIT(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_XPD_XTAL_32K);
REG_SET_BIT(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_XPD_XTAL_32K);
/* Needs some time after switching to 32khz XTAL before turning on WDT again */
esp_rom_delay_us(300);
xt_wdt_hal_enable(&s_hal_ctx, true);
}
void esp_xt_wdt_register_callback(esp_xt_callback_t func, void *arg)
{
portENTER_CRITICAL(&s_xt_wdt_lock);
s_callback_func = func;
s_callback_arg = arg;
portEXIT_CRITICAL(&s_xt_wdt_lock);
}
#endif //SOC_XT_WDT_SUPPORTED

View File

@ -61,6 +61,7 @@ if(NOT BOOTLOADER_BUILD)
"systimer_hal.c"
"touch_sensor_hal.c"
"usb_hal.c"
"xt_wdt_hal.c"
"esp32s2/adc_hal.c"
"esp32s2/brownout_hal.c"
"esp32s2/cp_dma_hal.c"
@ -81,6 +82,7 @@ if(NOT BOOTLOADER_BUILD)
"systimer_hal.c"
"touch_sensor_hal.c"
"usb_hal.c"
"xt_wdt_hal.c"
"esp32s3/brownout_hal.c"
"esp32s3/hmac_hal.c"
"esp32s3/interrupt_descriptor_table.c"
@ -95,6 +97,7 @@ if(NOT BOOTLOADER_BUILD)
"spi_flash_hal_gpspi.c"
"spi_slave_hd_hal.c"
"systimer_hal.c"
"xt_wdt_hal.c"
"esp32c3/adc_hal.c"
"esp32c3/brownout_hal.c"
"esp32c3/hmac_hal.c"

View File

@ -2,7 +2,7 @@ COMPONENT_SRCDIRS := . esp32
COMPONENT_ADD_INCLUDEDIRS := esp32/include include platform_port/include
COMPONENT_ADD_LDFRAGMENTS += linker.lf
COMPONENT_OBJEXCLUDE += ./spi_slave_hd_hal.o ./spi_flash_hal_gpspi.o ./spi_slave_hd_hal.o ./ds_hal.o ./gdma_hal.o ./lcd_hal.o ./systimer_hal.o ./usb_hal.o ./usbh_hal.o
COMPONENT_OBJEXCLUDE += ./spi_slave_hd_hal.o ./spi_flash_hal_gpspi.o ./spi_slave_hd_hal.o ./ds_hal.o ./gdma_hal.o ./lcd_hal.o ./systimer_hal.o ./usb_hal.o ./usbh_hal.o ./xt_wdt_hal.o
ifndef CONFIG_ETH_USE_ESP32_EMAC
COMPONENT_OBJEXCLUDE += ./emac_hal.o

View File

@ -0,0 +1,101 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// The LL layer for xtal32k WDT register operations.
// Note that most of the register operations in this layer are non-atomic operations.
#pragma once
#include <stdbool.h>
#include "soc/rtc_cntl_periph.h"
#ifdef __cplusplus
extern "C" {
#endif
#define XT_WDT_LL_XTAL32_DEAD_INTR_MASK RTC_CNTL_XTAL32K_DEAD_INT_ST_M
/**
* @brief Enable the XT_WDT
*
* @param hw Start address of the peripheral registers.
*/
inline void xt_wdt_ll_enable(rtc_cntl_dev_t *hw, bool enable)
{
hw->ext_xtl_conf.xtal32k_wdt_en = enable;
}
/**
* @brief Check if the XT_WDT is enabled
*
* @param hw Start address of the peripheral registers.
* @return True if XT WDT is enabled
*/
inline bool xt_wdt_ll_check_if_enabled(rtc_cntl_dev_t *hw)
{
return (hw->ext_xtl_conf.xtal32k_wdt_en) ? true : false;
}
/**
* @brief Set the watchdog timeout value
*
* @param hw Start address of the peripheral registers.
* @param timeout timeout value in RTC_CLK cycles
*/
inline void xt_wdt_ll_set_timeout(rtc_cntl_dev_t *hw, uint8_t timeout)
{
hw->xtal32k_conf.xtal32k_wdt_timeout = timeout;
}
/**
* @brief Reset the XT_WDT
*
* @param hw Start address of the peripheral registers.
*/
inline void xt_wdt_ll_reset(rtc_cntl_dev_t *hw)
{
hw->ext_xtl_conf.xtal32k_wdt_reset = 1;
hw->ext_xtl_conf.xtal32k_wdt_reset = 0;
}
/**
* @brief Set the backup clock value
*
* @param hw Start address of the peripheral registers.
* @param backup_clk_val Backup clock value, see TRM for definition
*/
inline void xt_wdt_ll_set_backup_clk_factor(rtc_cntl_dev_t *hw, uint32_t backup_clk_val)
{
hw->xtal32k_clk_factor = backup_clk_val;
}
/**
* @brief Enable the auto-backup clock feature
*
* @param hw Start address of the peripheral registers.
* @param enable True - enable, False - disable
*/
inline void xt_wdt_ll_auto_backup_enable(rtc_cntl_dev_t *hw, bool enable)
{
hw->ext_xtl_conf.xtal32k_auto_backup = enable;
}
/**
* @brief Enable the timeout interrupt
*
* @param hw Start address of the peripheral registers.
* @param enable True - enable, False - disable
*/
inline void xt_wdt_ll_intr_enable(rtc_cntl_dev_t *hw, bool enable)
{
hw->int_ena.rtc_xtal32k_dead = enable;
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,101 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// The LL layer for xtal32k WDT register operations.
// Note that most of the register operations in this layer are non-atomic operations.
#pragma once
#include <stdbool.h>
#include "soc/rtc_cntl_periph.h"
#ifdef __cplusplus
extern "C" {
#endif
#define XT_WDT_LL_XTAL32_DEAD_INTR_MASK RTC_CNTL_XTAL32K_DEAD_INT_ST_M
/**
* @brief Enable the XT_WDT
*
* @param hw Start address of the peripheral registers.
*/
inline void xt_wdt_ll_enable(rtc_cntl_dev_t *hw, bool enable)
{
hw->ext_xtl_conf.xtal32k_wdt_en = enable;
}
/**
* @brief Check if the XT_WDT is enabled
*
* @param hw Start address of the peripheral registers.
* @return True if XT WDT is enabled
*/
inline bool xt_wdt_ll_check_if_enabled(rtc_cntl_dev_t *hw)
{
return (hw->ext_xtl_conf.xtal32k_wdt_en) ? true : false;
}
/**
* @brief Set the watchdog timeout value
*
* @param hw Start address of the peripheral registers.
* @param timeout timeout value in RTC_CLK cycles
*/
inline void xt_wdt_ll_set_timeout(rtc_cntl_dev_t *hw, uint8_t timeout)
{
hw->xtal32k_conf.xtal32k_wdt_timeout = timeout;
}
/**
* @brief Reset the XT_WDT
*
* @param hw Start address of the peripheral registers.
*/
inline void xt_wdt_ll_reset(rtc_cntl_dev_t *hw)
{
hw->ext_xtl_conf.xtal32k_wdt_reset = 1;
hw->ext_xtl_conf.xtal32k_wdt_reset = 0;
}
/**
* @brief Set the backup clock value
*
* @param hw Start address of the peripheral registers.
* @param backup_clk_val Backup clock value, see TRM for definition
*/
inline void xt_wdt_ll_set_backup_clk_factor(rtc_cntl_dev_t *hw, uint32_t backup_clk_val)
{
hw->xtal32k_clk_factor = backup_clk_val;
}
/**
* @brief Enable the auto-backup clock feature
*
* @param hw Start address of the peripheral registers.
* @param enable True - enable, False - disable
*/
inline void xt_wdt_ll_auto_backup_enable(rtc_cntl_dev_t *hw, bool enable)
{
hw->ext_xtl_conf.xtal32k_auto_backup = enable;
}
/**
* @brief Enable the timeout interrupt
*
* @param hw Start address of the peripheral registers.
* @param enable True - enable, False - disable
*/
inline void xt_wdt_ll_intr_enable(rtc_cntl_dev_t *hw, bool enable)
{
hw->int_ena.rtc_xtal32k_dead = enable;
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,101 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// The LL layer for xtal32k WDT register operations.
// Note that most of the register operations in this layer are non-atomic operations.
#pragma once
#include <stdbool.h>
#include "soc/rtc_cntl_periph.h"
#ifdef __cplusplus
extern "C" {
#endif
#define XT_WDT_LL_XTAL32_DEAD_INTR_MASK RTC_CNTL_XTAL32K_DEAD_INT_ST_M
/**
* @brief Enable the XT_WDT
*
* @param hw Start address of the peripheral registers.
*/
inline void xt_wdt_ll_enable(rtc_cntl_dev_t *hw, bool enable)
{
hw->ext_xtl_conf.xtal32k_wdt_en = enable;
}
/**
* @brief Check if the XT_WDT is enabled
*
* @param hw Start address of the peripheral registers.
* @return True if XT WDT is enabled
*/
inline bool xt_wdt_ll_check_if_enabled(rtc_cntl_dev_t *hw)
{
return (hw->ext_xtl_conf.xtal32k_wdt_en) ? true : false;
}
/**
* @brief Set the watchdog timeout value
*
* @param hw Start address of the peripheral registers.
* @param timeout timeout value in RTC_CLK cycles
*/
inline void xt_wdt_ll_set_timeout(rtc_cntl_dev_t *hw, uint8_t timeout)
{
hw->xtal32k_conf.xtal32k_wdt_timeout = timeout;
}
/**
* @brief Reset the XT_WDT
*
* @param hw Start address of the peripheral registers.
*/
inline void xt_wdt_ll_reset(rtc_cntl_dev_t *hw)
{
hw->ext_xtl_conf.xtal32k_wdt_reset = 1;
hw->ext_xtl_conf.xtal32k_wdt_reset = 0;
}
/**
* @brief Set the backup clock value
*
* @param hw Start address of the peripheral registers.
* @param backup_clk_val Backup clock value, see TRM for definition
*/
inline void xt_wdt_ll_set_backup_clk_factor(rtc_cntl_dev_t *hw, uint32_t backup_clk_val)
{
hw->xtal32k_clk_factor = backup_clk_val;
}
/**
* @brief Enable the auto-backup clock feature
*
* @param hw Start address of the peripheral registers.
* @param enable True - enable, False - disable
*/
inline void xt_wdt_ll_auto_backup_enable(rtc_cntl_dev_t *hw, bool enable)
{
hw->ext_xtl_conf.xtal32k_auto_backup = enable;
}
/**
* @brief Enable the timeout interrupt
*
* @param hw Start address of the peripheral registers.
* @param enable True - enable, False - disable
*/
inline void xt_wdt_ll_intr_enable(rtc_cntl_dev_t *hw, bool enable)
{
hw->int_ena.rtc_xtal32k_dead = enable;
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,61 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "hal/xt_wdt_ll.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
rtc_cntl_dev_t *dev; /* Pointer to the RTC register struct */
} xt_wdt_hal_context_t; /* HAL context struct */
typedef struct {
uint32_t timeout; /* Watchdog timer timeout in RTC_CLK cycles*/
} xt_wdt_hal_config_t; /* HAL config parameter struct */
/* ---------------------------- Init and Config ----------------------------- */
/**
* @brief Initialize the WDTs associated HAL context
*
* Prepares the register for enabling the WDT and sets the timeout value
*
* @param hal Pointer to the HAL layer context
* @param config Pointer to config struct
*/
void xt_wdt_hal_init(xt_wdt_hal_context_t *hal, const xt_wdt_hal_config_t *config);
/**
* @brief Enable or disable the WDT
*
* @param hal Pointer to the HAL layer context
* @param enable true for enable WDT, false for disable
*/
void xt_wdt_hal_enable(xt_wdt_hal_context_t *hal, bool enable);
/**
* @brief Enable the automatic RTC backup clock with the given frequency
*
* Calculates and sets the necessary hardware parameters to meet the desired
* backup clock frequency
*
* @param hal Pointer to the HAL layer context
* @param rtc_clk_frequency_khz desired frequency for the backup clock
* @return uint32_t the calculated clock factor value
*/
uint32_t xt_wdt_hal_enable_backup_clk(xt_wdt_hal_context_t *hal, uint32_t rtc_clk_frequency_khz);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,78 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "soc/soc_caps.h"
#include "hal/xt_wdt_hal.h"
#include "hal/xt_wdt_ll.h"
#include "hal/assert.h"
#define DIV_COMP_N_MAX 8
static uint32_t xt_wdt_hal_calculate(uint32_t rtc_clk_frequency_khz)
{
uint32_t xtal32k_clk_factor = 0;
uint8_t divisor_comps[DIV_COMP_N_MAX];
/* From the TRM:
Define the frequency of RTC_CLK as f_rtc_clk (unit: kHz), and the eight divisor components as
x0, x1, x2, x3, x4, x5, x6, and x7, respectively. S = x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7.
The following conditions should be fulfilled:
S = f_rtc_clk * (4/32)
M + 1 >= xn >= M(0 <= n <= 7)
M = f_rtc_clk/32/2
xn should be an integer. M and S are rounded up or down. Each divisor component (x0 ~x7) is 4-bit long, and
corresponds to the value of RTC_CNTL_XTAL32K_CLK_FACTOR (32-bit) in order.
*/
uint8_t M = ((rtc_clk_frequency_khz / 32) / 2);
uint32_t S = ((4 * rtc_clk_frequency_khz) / 32);
memset(divisor_comps, M, DIV_COMP_N_MAX);
/* Calculate how far we are away from satisfying S = SUM(x_n) */
uint8_t off = S - DIV_COMP_N_MAX * M;
/* Offset should never be this big */
HAL_ASSERT(off <= DIV_COMP_N_MAX);
for (int i = 0; i < DIV_COMP_N_MAX; i++) {
if (off) {
divisor_comps[i]++;
off--;
}
/* Sum up all divisors */
xtal32k_clk_factor |= (divisor_comps[i] << 4 * i);
}
return xtal32k_clk_factor;
}
void xt_wdt_hal_init(xt_wdt_hal_context_t *hal, const xt_wdt_hal_config_t *config)
{
hal->dev = &RTCCNTL;
xt_wdt_ll_enable(hal->dev, false);
xt_wdt_ll_set_timeout(hal->dev, config->timeout);
}
uint32_t xt_wdt_hal_enable_backup_clk(xt_wdt_hal_context_t *hal, uint32_t rtc_clk_frequency_khz)
{
uint32_t xtal32k_clk_factor = xt_wdt_hal_calculate(rtc_clk_frequency_khz);
xt_wdt_ll_set_backup_clk_factor(hal->dev, xtal32k_clk_factor);
xt_wdt_ll_auto_backup_enable(hal->dev, true);
return xtal32k_clk_factor;
}
void xt_wdt_hal_enable(xt_wdt_hal_context_t *hal, bool enable)
{
xt_wdt_ll_enable(hal->dev, enable);
xt_wdt_ll_intr_enable(hal->dev, enable);
}

View File

@ -206,7 +206,8 @@ typedef enum {
typedef enum {
RTC_CAL_RTC_MUX = 0, //!< Currently selected RTC SLOW_CLK
RTC_CAL_8MD256 = 1, //!< Internal 8 MHz RC oscillator, divided by 256
RTC_CAL_32K_XTAL = 2 //!< External 32 kHz XTAL
RTC_CAL_32K_XTAL = 2, //!< External 32 kHz XTAL
RTC_CAL_INTERNAL_OSC = 3 //!< Internal 150 kHz oscillator
} rtc_cal_sel_t;
/**

View File

@ -15,6 +15,8 @@
#define SOC_USB_SERIAL_JTAG_SUPPORTED 1
#define SOC_TEMP_SENSOR_SUPPORTED 1
#define SOC_FLASH_ENCRYPTION_XTS_AES 1
#define SOC_XT_WDT_SUPPORTED 1
/*-------------------------- COMMON CAPS ---------------------------------------*/
#define SOC_SUPPORTS_SECURE_DL_MODE 1

View File

@ -215,7 +215,8 @@ typedef enum {
typedef enum {
RTC_CAL_RTC_MUX = 0, //!< Currently selected RTC SLOW_CLK
RTC_CAL_8MD256 = 1, //!< Internal 8 MHz RC oscillator, divided by 256
RTC_CAL_32K_XTAL = 2 //!< External 32 kHz XTAL
RTC_CAL_32K_XTAL = 2, //!< External 32 kHz XTAL
RTC_CAL_INTERNAL_OSC = 3 //!< Internal 150 kHz oscillator
} rtc_cal_sel_t;
/**

View File

@ -59,6 +59,7 @@
#define SOC_FLASH_ENCRYPTION_XTS_AES 1
#define SOC_FLASH_ENCRYPTION_XTS_AES_256 1
#define SOC_PSRAM_DMA_CAPABLE 1
#define SOC_XT_WDT_SUPPORTED 1
/*-------------------------- ADC CAPS ----------------------------------------*/
#define SOC_ADC_PERIPH_NUM (2)

View File

@ -202,7 +202,8 @@ typedef enum {
typedef enum {
RTC_CAL_RTC_MUX = 0, //!< Currently selected RTC SLOW_CLK
RTC_CAL_8MD256 = 1, //!< Internal 8 MHz RC oscillator, divided by 256
RTC_CAL_32K_XTAL = 2 //!< External 32 kHz XTAL
RTC_CAL_32K_XTAL = 2, //!< External 32 kHz XTAL
RTC_CAL_INTERNAL_OSC = 3 //!< Internal 150 kHz oscillator
} rtc_cal_sel_t;
/**

View File

@ -28,6 +28,8 @@
#define SOC_FLASH_ENCRYPTION_XTS_AES 1
#define SOC_FLASH_ENCRYPTION_XTS_AES_256 1
#define SOC_PSRAM_DMA_CAPABLE 1
#define SOC_XT_WDT_SUPPORTED 1
/*-------------------------- SOC CAPS ----------------------------------------*/
#define SOC_APPCPU_HAS_CLOCK_GATING_BUG (1)

View File

@ -4,7 +4,7 @@ Watchdogs
Overview
--------
The ESP-IDF has support for two types of watchdogs: The Interrupt Watchdog Timer
The ESP-IDF has support for multiple types of watchdogs, with the two main ones being: The Interrupt Watchdog Timer
and the Task Watchdog Timer (TWDT). The Interrupt Watchdog Timer and the TWDT
can both be enabled using :ref:`project-configuration-menu`, however the TWDT can also be
enabled during runtime. The Interrupt Watchdog is responsible for detecting
@ -101,13 +101,13 @@ timeout at runtime by calling :cpp:func:`esp_task_wdt_init`.
The following config options control TWDT configuration at startup. They are all enabled by default:
{IDF_TARGET_IDLE_TASK:default="Idle task", esp32="CPU0 Idle task"}
{IDF_TARGET_IDLE_TASK:default="Idle task", esp32="CPU0 Idle task", esp32s3="CPU0 Idle task"}
.. list::
- :ref:`CONFIG_ESP_TASK_WDT` - the TWDT is initialized automatically during startup. If this option is disabled, it is still possible to initialize the Task WDT at runtime by calling :cpp:func:`esp_task_wdt_init`.
- :ref:`CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0` - {IDF_TARGET_IDLE_TASK} is subscribed to the TWDT during startup. If this option is disabled, it is still possible to subscribe the idle task by calling :cpp:func:`esp_task_wdt_add` at any time.
:esp32: - :ref:`CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1` - CPU1 Idle task is subscribed to the TWDT during startup.
:not CONFIG_FREERTOS_UNICORE: - :ref:`CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1` - CPU1 Idle task is subscribed to the TWDT during startup.
JTAG and watchdogs
@ -124,6 +124,26 @@ panics from either watchdogs will be generated when the {IDF_TARGET_NAME} is con
OpenOCD via JTAG.
.. only:: SOC_XT_WDT_SUPPORTED
XTAL32K Watchdog Timer (XTWDT)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The XTAL32K watchdog makes sure the (optional) external 32 KHz crystal or oscillator is functioning correctly.
When `XTAL32K_CLK` works as the clock source of `RTC_SLOW_CLK` and stops oscillating, the XTAL32K watchdog timer will detect this and generate an interrupt.
It also provides functionality for automatically switching over to the internal, but less accurate oscillator as the `RTC_SLOW_CLK` source.
Since the switch to the backup clock is done in hardware it can also happen during deep sleep. This means that even if `XTAL32K_CLK` stops functioning while the chip in deep sleep, waiting for a timer to expire, it will still be able to wake-up as planned.
If the `XTAL32K_CLK` starts functioning normally again, you can call `esp_xt_wdt_restore_clk` to switch back to this clock source and re-enable the watchdog timer.
Configuration
@@@@@@@@@@@@@
When the external 32KHz crystal or oscillator is selected (:ref:`CONFIG_{IDF_TARGET_CFG_PREFIX}_RTC_CLK_SRC`) the XTAL32K watchdog can be enabled via the :ref:`CONFIG_ESP_XT_WDT` configuration
flag. The timeout is configured by setting :ref:`CONFIG_ESP_XT_WDT_TIMEOUT`. The automatic backup clock functionality is enabled via the ref:`CONFIG_ESP_XT_WDT_BACKUP_CLK_ENABLE` configuration.
Interrupt Watchdog API Reference
--------------------------------