diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index b166fcdc7b..1d25417fec 100644 --- a/components/esp32/Kconfig +++ b/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 diff --git a/components/esp32c3/Kconfig b/components/esp32c3/Kconfig index bb583c732e..dd6e5543bc 100644 --- a/components/esp32c3/Kconfig +++ b/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 diff --git a/components/esp32h2/Kconfig b/components/esp32h2/Kconfig index f8e7f6fc56..9144e0d96e 100644 --- a/components/esp32h2/Kconfig +++ b/components/esp32h2/Kconfig @@ -153,6 +153,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 diff --git a/components/esp32s2/Kconfig b/components/esp32s2/Kconfig index 9c8805dacf..997c553ddc 100644 --- a/components/esp32s2/Kconfig +++ b/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 diff --git a/components/esp32s3/Kconfig b/components/esp32s3/Kconfig index 044d7c593c..fc7d526174 100644 --- a/components/esp32s3/Kconfig +++ b/components/esp32s3/Kconfig @@ -433,6 +433,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 diff --git a/components/esp_hw_support/port/esp32c3/rtc_time.c b/components/esp_hw_support/port/esp32c3/rtc_time.c index 3378a84bda..59f8a68d38 100644 --- a/components/esp_hw_support/port/esp32c3/rtc_time.c +++ b/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) { diff --git a/components/esp_hw_support/port/esp32s2/rtc_time.c b/components/esp_hw_support/port/esp32s2/rtc_time.c index ee75ecd1a8..a18f36981b 100644 --- a/components/esp_hw_support/port/esp32s2/rtc_time.c +++ b/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) { diff --git a/components/esp_hw_support/port/esp32s3/rtc_time.c b/components/esp_hw_support/port/esp32s3/rtc_time.c index b54e4af3ca..3668a9eaa1 100644 --- a/components/esp_hw_support/port/esp32s3/rtc_time.c +++ b/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) { diff --git a/components/esp_system/CMakeLists.txt b/components/esp_system/CMakeLists.txt index a8a9d9e5c5..0aa60f72a7 100644 --- a/components/esp_system/CMakeLists.txt +++ b/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") diff --git a/components/esp_system/Kconfig b/components/esp_system/Kconfig index bb3988c495..7a03588903 100644 --- a/components/esp_system/Kconfig +++ b/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 diff --git a/components/esp_system/include/esp_xt_wdt.h b/components/esp_system/include/esp_xt_wdt.h new file mode 100644 index 0000000000..3b39d8056d --- /dev/null +++ b/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 + +#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 diff --git a/components/esp_system/startup.c b/components/esp_system/startup.c index 8afc43087f..ddf6c82b77 100644 --- a/components/esp_system/startup.c +++ b/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) diff --git a/components/esp_system/xt_wdt.c b/components/esp_system/xt_wdt.c new file mode 100644 index 0000000000..d3828df47c --- /dev/null +++ b/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 diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index ba6e51db8b..b5d8de8a9b 100644 --- a/components/hal/CMakeLists.txt +++ b/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" diff --git a/components/hal/component.mk b/components/hal/component.mk index 66d5054f62..d979fa78b8 100644 --- a/components/hal/component.mk +++ b/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 diff --git a/components/hal/esp32c3/include/hal/xt_wdt_ll.h b/components/hal/esp32c3/include/hal/xt_wdt_ll.h new file mode 100644 index 0000000000..65b36253e5 --- /dev/null +++ b/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 +#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 diff --git a/components/hal/esp32s2/include/hal/xt_wdt_ll.h b/components/hal/esp32s2/include/hal/xt_wdt_ll.h new file mode 100644 index 0000000000..65b36253e5 --- /dev/null +++ b/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 +#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 diff --git a/components/hal/esp32s3/include/hal/xt_wdt_ll.h b/components/hal/esp32s3/include/hal/xt_wdt_ll.h new file mode 100644 index 0000000000..65b36253e5 --- /dev/null +++ b/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 +#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 diff --git a/components/hal/include/hal/xt_wdt_hal.h b/components/hal/include/hal/xt_wdt_hal.h new file mode 100644 index 0000000000..bd08f2a989 --- /dev/null +++ b/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 +#include + +#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 diff --git a/components/hal/xt_wdt_hal.c b/components/hal/xt_wdt_hal.c new file mode 100644 index 0000000000..29ebb710a3 --- /dev/null +++ b/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 + +#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); +} diff --git a/components/soc/esp32c3/include/soc/rtc.h b/components/soc/esp32c3/include/soc/rtc.h index 3fda6cb8a7..40e6ec81d1 100644 --- a/components/soc/esp32c3/include/soc/rtc.h +++ b/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; /** diff --git a/components/soc/esp32c3/include/soc/soc_caps.h b/components/soc/esp32c3/include/soc/soc_caps.h index b30d095498..5ad734e6d0 100644 --- a/components/soc/esp32c3/include/soc/soc_caps.h +++ b/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 diff --git a/components/soc/esp32s2/include/soc/rtc.h b/components/soc/esp32s2/include/soc/rtc.h index 52322db983..1877bb1cfe 100644 --- a/components/soc/esp32s2/include/soc/rtc.h +++ b/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; /** diff --git a/components/soc/esp32s2/include/soc/soc_caps.h b/components/soc/esp32s2/include/soc/soc_caps.h index 29cefdee80..aa5705d86e 100644 --- a/components/soc/esp32s2/include/soc/soc_caps.h +++ b/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) diff --git a/components/soc/esp32s3/include/soc/rtc.h b/components/soc/esp32s3/include/soc/rtc.h index 2d4b0f6fee..d46211cc11 100644 --- a/components/soc/esp32s3/include/soc/rtc.h +++ b/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; /** diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h index 59ee736f69..0ad56c7791 100644 --- a/components/soc/esp32s3/include/soc/soc_caps.h +++ b/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) diff --git a/docs/en/api-reference/system/wdts.rst b/docs/en/api-reference/system/wdts.rst index 894c70d740..372a6bde4c 100644 --- a/docs/en/api-reference/system/wdts.rst +++ b/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 --------------------------------