Преглед на файлове

WDT: Add support for XTAL32K Watchdog timer

Marius Vikhammer преди 4 години
родител
ревизия
4869b3cd4a

+ 1 - 0
components/esp32/Kconfig

@@ -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

+ 1 - 0
components/esp32c3/Kconfig

@@ -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

+ 1 - 0
components/esp32h2/Kconfig

@@ -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

+ 1 - 0
components/esp32s2/Kconfig

@@ -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

+ 1 - 0
components/esp32s3/Kconfig

@@ -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

+ 4 - 0
components/esp_hw_support/port/esp32c3/rtc_time.c

@@ -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) {

+ 3 - 0
components/esp_hw_support/port/esp32s2/rtc_time.c

@@ -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) {

+ 3 - 0
components/esp_hw_support/port/esp32s3/rtc_time.c

@@ -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) {

+ 2 - 1
components/esp_system/CMakeLists.txt

@@ -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")

+ 31 - 0
components/esp_system/Kconfig

@@ -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

+ 63 - 0
components/esp_system/include/esp_xt_wdt.h

@@ -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

+ 10 - 0
components/esp_system/startup.c

@@ -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)

+ 95 - 0
components/esp_system/xt_wdt.c

@@ -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

+ 3 - 0
components/hal/CMakeLists.txt

@@ -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"

+ 1 - 1
components/hal/component.mk

@@ -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

+ 101 - 0
components/hal/esp32c3/include/hal/xt_wdt_ll.h

@@ -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

+ 101 - 0
components/hal/esp32s2/include/hal/xt_wdt_ll.h

@@ -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

+ 101 - 0
components/hal/esp32s3/include/hal/xt_wdt_ll.h

@@ -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

+ 61 - 0
components/hal/include/hal/xt_wdt_hal.h

@@ -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

+ 78 - 0
components/hal/xt_wdt_hal.c

@@ -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);
+}

+ 2 - 1
components/soc/esp32c3/include/soc/rtc.h

@@ -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;
 
 /**

+ 3 - 1
components/soc/esp32c3/include/soc/soc_caps.h

@@ -14,7 +14,9 @@
 #define SOC_ASYNC_MEMCPY_SUPPORTED      1
 #define SOC_USB_SERIAL_JTAG_SUPPORTED   1
 #define SOC_TEMP_SENSOR_SUPPORTED       1
-#define SOC_FLASH_ENCRYPTION_XTS_AES      1
+#define SOC_FLASH_ENCRYPTION_XTS_AES    1
+#define SOC_XT_WDT_SUPPORTED            1
+
 
 /*-------------------------- COMMON CAPS ---------------------------------------*/
 #define SOC_SUPPORTS_SECURE_DL_MODE     1

+ 2 - 1
components/soc/esp32s2/include/soc/rtc.h

@@ -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;
 
 /**

+ 1 - 0
components/soc/esp32s2/include/soc/soc_caps.h

@@ -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)

+ 2 - 1
components/soc/esp32s3/include/soc/rtc.h

@@ -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;
 
 /**

+ 2 - 0
components/soc/esp32s3/include/soc/soc_caps.h

@@ -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)

+ 23 - 3
docs/en/api-reference/system/wdts.rst

@@ -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
 --------------------------------