This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit 140dc248dbdffd63e78334377a2d7c8f0f6226d7 Author: Gustavo Henrique Nihei <[email protected]> AuthorDate: Mon Mar 7 10:33:57 2022 -0300 xtensa/esp32s2: Add support for Main System Watchdog Timers Support for RTC Watchdog Timer is currently in place, but not yet functional due to not yet implemented RTC driver. Signed-off-by: Gustavo Henrique Nihei <[email protected]> --- arch/xtensa/src/esp32s2/Kconfig | 4 - arch/xtensa/src/esp32s2/Make.defs | 6 +- arch/xtensa/src/esp32s2/esp32s2_wdt.c | 872 ++++++++++++++++++++- arch/xtensa/src/esp32s2/esp32s2_wdt.h | 115 +++ arch/xtensa/src/esp32s2/esp32s2_wdt_lowerhalf.c | 738 +++++++++++++++++ .../{esp32s2_wdt.c => esp32s2_wdt_lowerhalf.h} | 39 +- arch/xtensa/src/esp32s2/hardware/esp32s2_rtccntl.h | 23 +- arch/xtensa/src/esp32s2/hardware/esp32s2_tim.h | 24 +- 8 files changed, 1797 insertions(+), 24 deletions(-) diff --git a/arch/xtensa/src/esp32s2/Kconfig b/arch/xtensa/src/esp32s2/Kconfig index deb20a3..9ccfb65 100644 --- a/arch/xtensa/src/esp32s2/Kconfig +++ b/arch/xtensa/src/esp32s2/Kconfig @@ -199,10 +199,6 @@ config ESP32S2_UART bool default n -config ESP32S2_UART - bool - default n - config ESP32S2_TIMER bool default n diff --git a/arch/xtensa/src/esp32s2/Make.defs b/arch/xtensa/src/esp32s2/Make.defs index 62859a6..7f38961 100644 --- a/arch/xtensa/src/esp32s2/Make.defs +++ b/arch/xtensa/src/esp32s2/Make.defs @@ -23,7 +23,7 @@ include chip/Bootloader.mk # The start-up, "head", file. May be either a .S or a .c file. HEAD_ASRC = xtensa_vectors.S xtensa_window_vector.S xtensa_windowspill.S -HEAD_ASRC += xtensa_int_handlers.S xtensa_user_handler.S +HEAD_ASRC += xtensa_int_handlers.S xtensa_user_handler.S HEAD_CSRC = esp32s2_start.c esp32s2_wdt.c # Common XTENSA files (arch/xtensa/src/common) @@ -88,6 +88,10 @@ CHIP_CSRCS += esp32s2_tim_lowerhalf.c endif endif +ifeq ($(CONFIG_WATCHDOG),y) +CHIP_CSRCS += esp32s2_wdt_lowerhalf.c +endif + ifeq ($(CONFIG_ESP32S2_FREERUN),y) CHIP_CSRCS += esp32s2_freerun.c endif diff --git a/arch/xtensa/src/esp32s2/esp32s2_wdt.c b/arch/xtensa/src/esp32s2/esp32s2_wdt.c index 60addbd..b5d21fa 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_wdt.c +++ b/arch/xtensa/src/esp32s2/esp32s2_wdt.c @@ -22,16 +22,825 @@ * Included Files ****************************************************************************/ +#include <nuttx/config.h> +#include <nuttx/arch.h> +#include <nuttx/irq.h> +#include <stdbool.h> +#include <assert.h> +#include <debug.h> + #include "xtensa.h" +#include "esp32s2_irq.h" +#include "esp32s2_wdt.h" +#include "hardware/esp32s2_efuse.h" #include "hardware/esp32s2_rtccntl.h" +#include "hardware/esp32s2_tim.h" -#include "esp32s2_wdt.h" +#ifdef CONFIG_ESP32S2_RWDT +# error "RWDT not yet supported due to missing RTC driver!" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Check whether the provided device is a RTC Watchdog Timer */ + +#define IS_RWDT(dev) (((struct esp32s2_wdt_priv_s *)(dev))->base == \ + RTC_CNTL_OPTIONS0_REG) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct esp32s2_wdt_priv_s +{ + struct esp32s2_wdt_ops_s *ops; + uint32_t base; /* WDT register base address */ + uint8_t periph; /* Peripheral ID */ + uint8_t irq; /* Interrupt ID */ + int32_t cpuint; /* CPU interrupt assigned to this WDT */ + bool inuse; /* Flag indicating if this WDT is in use */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* WDT registers access *****************************************************/ + +static void wdt_putreg(struct esp32s2_wdt_dev_s *dev, uint32_t offset, + uint32_t value); +static void wdt_modifyreg32(struct esp32s2_wdt_dev_s *dev, uint32_t offset, + uint32_t clearbits, uint32_t setbits); +static uint32_t wdt_getreg(struct esp32s2_wdt_dev_s *dev, uint32_t offset); + +/* WDT operations ***********************************************************/ + +static void wdt_start(struct esp32s2_wdt_dev_s *dev); +static void wdt_stop(struct esp32s2_wdt_dev_s *dev); +static void wdt_enablewp(struct esp32s2_wdt_dev_s *dev); +static void wdt_disablewp(struct esp32s2_wdt_dev_s *dev); +static void wdt_pre(struct esp32s2_wdt_dev_s *dev, + uint16_t value); +static int32_t wdt_settimeout(struct esp32s2_wdt_dev_s *dev, + uint32_t value, + enum esp32s2_wdt_stage_e stage); +static void wdt_feed(struct esp32s2_wdt_dev_s *dev); +static int32_t wdt_config_stage(struct esp32s2_wdt_dev_s *dev, + enum esp32s2_wdt_stage_e stage, + enum esp32s2_wdt_stage_action_e cfg); +static int32_t wdt_setisr(struct esp32s2_wdt_dev_s *dev, + xcpt_t handler, void *arg); +static void wdt_enableint(struct esp32s2_wdt_dev_s *dev); +static void wdt_disableint(struct esp32s2_wdt_dev_s *dev); +static void wdt_ackint(struct esp32s2_wdt_dev_s *dev); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* ESP32-S2 WDT ops */ + +struct esp32s2_wdt_ops_s esp32s2_mwdt_ops = +{ + .start = wdt_start, + .stop = wdt_stop, + .enablewp = wdt_enablewp, + .disablewp = wdt_disablewp, + .pre = wdt_pre, + .settimeout = wdt_settimeout, + .feed = wdt_feed, + .stg_conf = wdt_config_stage, + .rtc_clk = NULL, + .setisr = wdt_setisr, + .enableint = wdt_enableint, + .disableint = wdt_disableint, + .ackint = wdt_ackint, +}; + +struct esp32s2_wdt_ops_s esp32s2_rwdt_ops = +{ + .start = wdt_start, + .stop = wdt_stop, + .enablewp = wdt_enablewp, + .disablewp = wdt_disablewp, + .pre = NULL, + .settimeout = wdt_settimeout, + .feed = wdt_feed, + .stg_conf = wdt_config_stage, + .rtc_clk = NULL, + .setisr = wdt_setisr, + .enableint = wdt_enableint, + .disableint = wdt_disableint, + .ackint = wdt_ackint, +}; + +#ifdef CONFIG_ESP32S2_MWDT0 +struct esp32s2_wdt_priv_s g_esp32s2_mwdt0_priv = +{ + .ops = &esp32s2_mwdt_ops, + .base = TIMG_T0CONFIG_REG(0), + .periph = ESP32S2_PERIPH_TG_WDT_LEVEL, + .irq = ESP32S2_IRQ_TG_WDT_LEVEL, + .cpuint = -ENOMEM, + .inuse = false, +}; +#endif + +#ifdef CONFIG_ESP32S2_MWDT1 +struct esp32s2_wdt_priv_s g_esp32s2_mwdt1_priv = +{ + .ops = &esp32s2_mwdt_ops, + .base = TIMG_T0CONFIG_REG(1), + .periph = ESP32S2_PERIPH_TG1_WDT_LEVEL, + .irq = ESP32S2_IRQ_TG1_WDT_LEVEL, + .cpuint = -ENOMEM, + .inuse = false, +}; +#endif + +#ifdef CONFIG_ESP32S2_RWDT +struct esp32s2_wdt_priv_s g_esp32s2_rwdt_priv = +{ + .ops = &esp32s2_rwdt_ops, + .base = RTC_CNTL_OPTIONS0_REG, + .periph = ESP32S2_PERIPH_RTC_CORE, + .irq = ESP32S2_IRQ_RTC_CORE, + .cpuint = -ENOMEM, + .inuse = false, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: wdt_putreg + * + * Description: + * Write a 32-bit register value by offset. + * + * Parameters: + * dev - Pointer to the driver state structure. + * offset - Offset value to the base address of the WDT device. + * value - Value to written to the specified memory region. + * + ****************************************************************************/ + +static void wdt_putreg(struct esp32s2_wdt_dev_s *dev, uint32_t offset, + uint32_t value) +{ + DEBUGASSERT(dev != NULL); + + putreg32(value, ((struct esp32s2_wdt_priv_s *)dev)->base + offset); +} + +/**************************************************************************** + * Name: wdt_modifyreg32 + * + * Description: + * Atomically modify a 32-bit register value by offset. + * + * Parameters: + * dev - Pointer to the driver state structure. + * offset - Offset value to the base address of the WDT device. + * clearbits - Bits to be cleared on the specified memory region. + * setbits - Bits to be set on the specified memory region. + * + ****************************************************************************/ + +static void wdt_modifyreg32(struct esp32s2_wdt_dev_s *dev, uint32_t offset, + uint32_t clearbits, uint32_t setbits) +{ + DEBUGASSERT(dev != NULL); + + modifyreg32(((struct esp32s2_wdt_priv_s *)dev)->base + offset, + clearbits, setbits); +} + +/**************************************************************************** + * Name: wdt_getreg + * + * Description: + * Read a 32-bit register value by offset. + * + * Parameters: + * dev - Pointer to the driver state structure. + * offset - Offset value to the base address of the WDT device. + * + * Returned Values: + * A 32-bit value from the provided memory region of the WDT device. + * + ****************************************************************************/ + +static uint32_t wdt_getreg(struct esp32s2_wdt_dev_s *dev, uint32_t offset) +{ + DEBUGASSERT(dev != NULL); + + return getreg32(((struct esp32s2_wdt_priv_s *)dev)->base + offset); +} + +/**************************************************************************** + * Name: wdt_start + * + * Description: + * Release the counter. + * + * Parameters: + * dev - Pointer to the driver state structure. + * + ****************************************************************************/ + +static void wdt_start(struct esp32s2_wdt_dev_s *dev) +{ + DEBUGASSERT(dev != NULL); + + if (IS_RWDT(dev)) + { + wdt_modifyreg32(dev, RWDT_CONFIG0_OFFSET, 0, RTC_CNTL_WDT_EN); + } + else + { + wdt_modifyreg32(dev, MWDT_CONFIG0_OFFSET, 0, TIMG_WDT_EN); + } +} + +/**************************************************************************** + * Name: wdt_config_stage + * + * Description: + * Configure the action to be triggered by a stage on expiration. + * + * Parameters: + * dev - Pointer to the driver state structure. + * stage - WDT stage to be configured. + * cfg - Action to be executed on stage expiration. + * + * Returned Values: + * Zero (OK) is returned on success; A negated errno value is returned + * to indicate the nature of any failure. + * + ****************************************************************************/ + +static int32_t wdt_config_stage(struct esp32s2_wdt_dev_s *dev, + enum esp32s2_wdt_stage_e stage, + enum esp32s2_wdt_stage_action_e cfg) +{ + int32_t ret = OK; + uint32_t mask; + + DEBUGASSERT(dev != NULL); + + switch (stage) + { + case ESP32S2_WDT_STAGE0: + { + if (IS_RWDT(dev)) + { + mask = (uint32_t)cfg << RTC_CNTL_WDT_STG0_S; + wdt_modifyreg32(dev, RWDT_CONFIG0_OFFSET, RTC_CNTL_WDT_STG0_M, + mask); + } + else + { + mask = (uint32_t)cfg << TIMG_WDT_STG0_S; + wdt_modifyreg32(dev, MWDT_CONFIG0_OFFSET, TIMG_WDT_STG0_M, mask); + } + break; + } + + case ESP32S2_WDT_STAGE1: + { + if (IS_RWDT(dev)) + { + mask = (uint32_t)cfg << RTC_CNTL_WDT_STG1_S; + wdt_modifyreg32(dev, RWDT_CONFIG0_OFFSET, RTC_CNTL_WDT_STG1_M, + mask); + } + else + { + mask = (uint32_t)cfg << TIMG_WDT_STG1_S; + wdt_modifyreg32(dev, MWDT_CONFIG0_OFFSET, TIMG_WDT_STG1_M, mask); + } + break; + } + + case ESP32S2_WDT_STAGE2: + { + if (IS_RWDT(dev)) + { + mask = (uint32_t)cfg << RTC_CNTL_WDT_STG2_S; + wdt_modifyreg32(dev, RWDT_CONFIG0_OFFSET, RTC_CNTL_WDT_STG2_M, + mask); + } + else + { + mask = (uint32_t)cfg << TIMG_WDT_STG2_S; + wdt_modifyreg32(dev, MWDT_CONFIG0_OFFSET, TIMG_WDT_STG2_M, mask); + } + break; + } + + case ESP32S2_WDT_STAGE3: + { + if (IS_RWDT(dev)) + { + mask = (uint32_t)cfg << RTC_CNTL_WDT_STG3_S; + wdt_modifyreg32(dev, RWDT_CONFIG0_OFFSET, RTC_CNTL_WDT_STG3_M, + mask); + } + else + { + mask = (uint32_t)cfg << TIMG_WDT_STG3_S; + wdt_modifyreg32(dev, MWDT_CONFIG0_OFFSET, TIMG_WDT_STG3_M, mask); + } + break; + } + + default: + { + wderr("ERROR: unsupported stage %d\n", stage); + ret = -EINVAL; + goto errout; + } + } + + errout: + return ret; +} + +/**************************************************************************** + * Name: wdt_stop + * + * Description: + * Disable the watchdog. + * + * Parameters: + * dev - Pointer to the driver state structure. + * + ****************************************************************************/ + +static void wdt_stop(struct esp32s2_wdt_dev_s *dev) +{ + DEBUGASSERT(dev != NULL); + + if (IS_RWDT(dev)) + { + wdt_modifyreg32(dev, RWDT_CONFIG0_OFFSET, RTC_CNTL_WDT_EN, 0); + } + else + { + wdt_modifyreg32(dev, MWDT_CONFIG0_OFFSET, TIMG_WDT_EN, 0); + } +} + +/**************************************************************************** + * Name: wdt_enablewp + * + * Description: + * Enable write protection (WP) on registers against accidental writing. + * TRM recommends to change any WDT register through this sequence: + * - Disable WP + * - Do the op + * - Re-enable WP + * + * Parameters: + * dev - Pointer to the driver state structure. + * + ****************************************************************************/ + +static void wdt_enablewp(struct esp32s2_wdt_dev_s *dev) +{ + DEBUGASSERT(dev != NULL); + + if (IS_RWDT(dev)) + { + wdt_putreg(dev, RWDT_WP_REG, 0); + } + else + { + wdt_putreg(dev, MWDT_WP_REG, 0); + } +} + +/**************************************************************************** + * Name: wdt_disablewp + * + * Description: + * Disable write protection (WP) on registers against accidental writing. + * TRM recommends to change any WDT register through this sequence: + * - Disable WP + * - Do the op + * - Re-enable WP + * + * Parameters: + * dev - Pointer to the driver state structure. + * + ****************************************************************************/ + +static void wdt_disablewp(struct esp32s2_wdt_dev_s *dev) +{ + DEBUGASSERT(dev != NULL); + + if (IS_RWDT(dev)) + { + wdt_putreg(dev, RWDT_WP_REG, RTC_CNTL_WDT_WKEY_VALUE); + } + else + { + wdt_putreg(dev, MWDT_WP_REG, TIMG_WDT_WKEY_VALUE); + } +} + +/**************************************************************************** + * Name: wdt_pre + * + * Description: + * Set a prescale value. + * The MWDT clock period is 12.5 ns * value (pre). + * NOTE: There's no prescaler register for RWDT and its source clock is + * clocked from the RTC slow clock. + * + * Parameters: + * dev - Pointer to the driver state structure. + * pre - Prescaler value to be configured. + * + ****************************************************************************/ + +static void wdt_pre(struct esp32s2_wdt_dev_s *dev, uint16_t pre) +{ + uint32_t mask = (uint32_t)pre << TIMG_WDT_CLK_PRESCALER_S; + + DEBUGASSERT(dev != NULL); + + wdt_modifyreg32(dev, MWDT_CLK_PRESCALE_OFFSET, TIMG_WDT_CLK_PRESCALER_M, + mask); +} + +/**************************************************************************** + * Name: wdt_settimeout + * + * Description: + * Set the WDT timeout. + * + * Parameters: + * dev - Pointer to the driver state structure. + * value - Timeout value in number of WDT cycles. + * stage - Stage whose timeout value needs to be configured. + * + * Returned Values: + * Zero (OK) is returned on success; A negated errno value is returned + * to indicate the nature of any failure. + * + ****************************************************************************/ + +static int32_t wdt_settimeout(struct esp32s2_wdt_dev_s *dev, uint32_t value, + enum esp32s2_wdt_stage_e stage) +{ + int32_t ret = OK; + + DEBUGASSERT(dev != NULL); + + switch (stage) + { + case ESP32S2_WDT_STAGE0: + { + if (IS_RWDT(dev)) + { + /* The timeout of only stage 0 happens at: + * Thold0 = RTC_CNTL_WDT_STG0_HOLD << (EFUSE_WDT_DELAY_SEL + 1) + */ + + uint32_t delay; + delay = REG_GET_FIELD(EFUSE_RD_REPEAT_DATA1_REG, + EFUSE_WDT_DELAY_SEL); + value = value >> (delay + 1); + wdt_putreg(dev, RWDT_STAGE0_TIMEOUT_OFFSET, value); + } + else + { + wdt_putreg(dev, MWDT_STAGE0_TIMEOUT_OFFSET, value); + } + break; + } + + case ESP32S2_WDT_STAGE1: + { + if (IS_RWDT(dev)) + { + wdt_putreg(dev, RWDT_STAGE1_TIMEOUT_OFFSET, value); + } + else + { + wdt_putreg(dev, MWDT_STAGE1_TIMEOUT_OFFSET, value); + } + break; + } + + case ESP32S2_WDT_STAGE2: + { + if (IS_RWDT(dev)) + { + wdt_putreg(dev, RWDT_STAGE2_TIMEOUT_OFFSET, value); + } + else + { + wdt_putreg(dev, MWDT_STAGE2_TIMEOUT_OFFSET, value); + } + break; + } + + case ESP32S2_WDT_STAGE3: + { + if (IS_RWDT(dev)) + { + wdt_putreg(dev, RWDT_STAGE3_TIMEOUT_OFFSET, value); + } + else + { + wdt_putreg(dev, MWDT_STAGE3_TIMEOUT_OFFSET, value); + } + break; + } + + default: + { + wderr("ERROR: unsupported stage %d\n", stage); + ret = -EINVAL; + goto errout; + } + } + + errout: + return ret; +} + +/**************************************************************************** + * Name: wdt_feed + * + * Description: + * Feed the watchdog. + * The watchdog timer returns to stage 0 and its counter restarts from 0. + * + * Parameters: + * dev - Pointer to the driver state structure. + * + ****************************************************************************/ + +static void wdt_feed(struct esp32s2_wdt_dev_s *dev) +{ + DEBUGASSERT(dev != NULL); + + if (IS_RWDT(dev)) + { + wdt_modifyreg32(dev, RWDT_FEED_OFFSET, 0, RTC_CNTL_WDT_FEED); + } + else + { + wdt_putreg(dev, MWDT_FEED_OFFSET, TIMG_WDT_FEED); + } +} + +/**************************************************************************** + * Name: wdt_setisr + * + * Description: + * Allocate a Level CPU Interrupt, connect the peripheral source to this + * Interrupt, register the callback and enable the interrupt. + * In case a NULL handler is provided, deallocate the interrupt and + * unregister the previously provided handler. + * + * Parameters: + * dev - Pointer to the driver state structure. + * handler - Callback to be invoked on watchdog timer interrupt. + * arg - Argument to be passed to the handler callback. + * + * Returned Values: + * Zero (OK) is returned on success; A negated errno value is returned + * to indicate the nature of any failure. + * + ****************************************************************************/ + +static int32_t wdt_setisr(struct esp32s2_wdt_dev_s *dev, xcpt_t handler, + void *arg) +{ + struct esp32s2_wdt_priv_s *wdt; + int32_t ret = OK; + + DEBUGASSERT(dev != NULL); + + wdt = (struct esp32s2_wdt_priv_s *)dev; + + /* Disable interrupt when callback is removed. */ + + if (handler == NULL) + { + /* If a CPU Interrupt was previously allocated, then deallocate it */ + + if (wdt->cpuint >= 0) + { + /* Disable CPU Interrupt, free a previously allocated + * CPU Interrupt + */ + + up_disable_irq(wdt->irq); + esp32s2_teardown_irq(wdt->periph, wdt->cpuint); + irq_detach(wdt->irq); + } + + ret = OK; + goto errout; + } + + /* Otherwise set callback and enable interrupt. */ + + else + { + /* Set up to receive peripheral interrupts on the current CPU */ + + wdt->cpuint = esp32s2_setup_irq(wdt->periph, 1, ESP32S2_CPUINT_LEVEL); + if (wdt->cpuint < 0) + { + wderr("ERROR: No CPU Interrupt available"); + ret = wdt->cpuint; + goto errout; + } + + /* Associate an IRQ Number (from the WDT) to an ISR */ + + ret = irq_attach(wdt->irq, handler, arg); + + if (ret != OK) + { + esp32s2_teardown_irq(wdt->periph, wdt->cpuint); + wderr("ERROR: Failed to associate an IRQ Number"); + goto errout; + } + + /* Enable the CPU Interrupt that is linked to the WDT */ + + up_enable_irq(wdt->irq); + } + +errout: + return ret; +} + +/**************************************************************************** + * Name: wdt_enableint + * + * Description: + * Enable a Level Interrupt at timeout. + * + * Parameters: + * dev - Pointer to the driver state structure. + * + ****************************************************************************/ + +static void wdt_enableint(struct esp32s2_wdt_dev_s *dev) +{ + DEBUGASSERT(dev != NULL); + + if (IS_RWDT(dev)) + { + wdt_modifyreg32(dev, RWDT_INT_ENA_REG_OFFSET, 0, RTC_CNTL_WDT_INT_ENA); + } + else + { + wdt_modifyreg32(dev, MWDT_INT_ENA_REG_OFFSET, 0, TIMG_WDT_INT_ENA); + } +} + +/**************************************************************************** + * Name: wdt_disableint + * + * Description: + * Disable a Level Interrupt at timeout. + * + * Parameters: + * dev - Pointer to the driver state structure. + * + ****************************************************************************/ + +static void wdt_disableint(struct esp32s2_wdt_dev_s *dev) +{ + DEBUGASSERT(dev != NULL); + + if (IS_RWDT(dev)) + { + wdt_modifyreg32(dev, RWDT_INT_ENA_REG_OFFSET, RTC_CNTL_WDT_INT_ENA, 0); + } + else + { + wdt_modifyreg32(dev, MWDT_INT_ENA_REG_OFFSET, TIMG_WDT_INT_ENA, 0); + } +} + +/**************************************************************************** + * Name: wdt_ackint + * + * Description: + * Acknowledge an interrupt. + * + * Parameters: + * dev - Pointer to the driver state structure. + * + ****************************************************************************/ + +static void wdt_ackint(struct esp32s2_wdt_dev_s *dev) +{ + DEBUGASSERT(dev != NULL); + + if (IS_RWDT(dev)) + { + wdt_putreg(dev, RWDT_INT_CLR_REG_OFFSET, RTC_CNTL_WDT_INT_CLR); + } + else + { + wdt_putreg(dev, MWDT_INT_CLR_REG_OFFSET, TIMG_WDT_INT_CLR); + } +} /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** + * Name: esp32s2_wdt_init + * + * Description: + * Initialize WDT device. + * + * Parameters: + * wdt_id - A Watchdog Timer instance to be initialized. + * + * Return Values: + * Pointer to the driver state structure. + * + ****************************************************************************/ + +struct esp32s2_wdt_dev_s *esp32s2_wdt_init(enum esp32s2_wdt_inst_e wdt_id) +{ + struct esp32s2_wdt_priv_s *wdt = NULL; + + /* Get WDT instance */ + + switch (wdt_id) + { +#ifdef CONFIG_ESP32S2_MWDT0 + case ESP32S2_WDT_MWDT0: + { + wdt = &g_esp32s2_mwdt0_priv; + break; + } + +#endif + +#ifdef CONFIG_ESP32S2_MWDT1 + case ESP32S2_WDT_MWDT1: + { + wdt = &g_esp32s2_mwdt1_priv; + break; + } +#endif + +#ifdef CONFIG_ESP32S2_RWDT + case ESP32S2_WDT_RWDT: + { + wdt = &g_esp32s2_rwdt_priv; + break; + } + +#endif + + default: + { + wderr("ERROR: unsupported WDT %d\n", wdt_id); + goto errout; + } + } + + /* If some code is using it then sends an error message, + * Otherwise, inform it has been used. + */ + + if (wdt->inuse) + { + wderr("ERROR: WDT %d is already in use\n", wdt_id); + wdt = NULL; + } + else + { + wdt->inuse = true; + } + +errout: + return (struct esp32s2_wdt_dev_s *)wdt; +} + +/**************************************************************************** * Name: esp32s2_wdt_early_deinit * * Description: @@ -48,3 +857,64 @@ void esp32s2_wdt_early_deinit(void) putreg32(regval, RTC_CNTL_WDTCONFIG0_REG); putreg32(0, RTC_CNTL_WDTWPROTECT_REG); } + +/**************************************************************************** + * Name: esp32s2_wdt_deinit + * + * Description: + * Deinitialize a WDT device. + * + * Parameters: + * dev - Pointer to the driver state structure. + * + ****************************************************************************/ + +void esp32s2_wdt_deinit(struct esp32s2_wdt_dev_s *dev) +{ + struct esp32s2_wdt_priv_s *wdt; + + DEBUGASSERT(dev != NULL); + + wdt = (struct esp32s2_wdt_priv_s *)dev; + wdt->inuse = false; +} + +/**************************************************************************** + * Name: esp32s2_wdt_is_running + * + * Description: + * Check whether the WDT is already started. + * + * Parameters: + * dev - Pointer to the driver state structure. + * + * Returned Values: + * true if the Watchdog Timer is already started, false otherwise. + * + ****************************************************************************/ + +bool esp32s2_wdt_is_running(struct esp32s2_wdt_dev_s *dev) +{ + uint32_t status = 0; + + DEBUGASSERT(dev != NULL); + + if (IS_RWDT(dev)) + { + status = wdt_getreg(dev, RWDT_CONFIG0_OFFSET); + if ((status & RTC_CNTL_WDT_EN) == RTC_CNTL_WDT_EN) + { + return true; + } + } + else + { + status = wdt_getreg(dev, MWDT_CONFIG0_OFFSET); + if ((status & TIMG_WDT_EN) == TIMG_WDT_EN) + { + return true; + } + } + + return false; +} diff --git a/arch/xtensa/src/esp32s2/esp32s2_wdt.h b/arch/xtensa/src/esp32s2/esp32s2_wdt.h index bd33075..08b48e7 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_wdt.h +++ b/arch/xtensa/src/esp32s2/esp32s2_wdt.h @@ -25,10 +25,125 @@ * Included Files ****************************************************************************/ +#include <nuttx/config.h> + +#include <stdint.h> + +#include <nuttx/irq.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Helpers ******************************************************************/ + +#define ESP32S2_WDT_START(d) ((d)->ops->start(d)) +#define ESP32S2_WDT_STOP(d) ((d)->ops->stop(d)) +#define ESP32S2_WDT_LOCK(d) ((d)->ops->enablewp(d)) +#define ESP32S2_WDT_UNLOCK(d) ((d)->ops->disablewp(d)) +#define ESP32S2_MWDT_PRE(d, v) ((d)->ops->pre(d, v)) +#define ESP32S2_WDT_STO(d, v, s) ((d)->ops->settimeout(d, v, s)) +#define ESP32S2_WDT_FEED(d) ((d)->ops->feed(d)) +#define ESP32S2_WDT_STG_CONF(d, s, c) ((d)->ops->stg_conf(d, s, c)) +#define ESP32S2_RWDT_CLK(d) ((d)->ops->rtc_clk(d)) +#define ESP32S2_WDT_SETISR(d, hnd, arg) ((d)->ops->setisr(d, hnd, arg)) +#define ESP32S2_WDT_ENABLEINT(d) ((d)->ops->enableint(d)) +#define ESP32S2_WDT_DISABLEINT(d) ((d)->ops->disableint(d)) +#define ESP32S2_WDT_ACKINT(d) ((d)->ops->ackint(d)) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Instances of Watchdog Timer */ + +enum esp32s2_wdt_inst_e +{ + ESP32S2_WDT_MWDT0 = 0, /* Main System Watchdog Timer (MWDT) of Timer Group 0 */ + ESP32S2_WDT_MWDT1, /* Main System Watchdog Timer (MWDT) of Timer Group 1 */ + ESP32S2_WDT_RWDT /* RTC Watchdog Timer (RWDT) */ +}; + +/* Stages of a Watchdog Timer. A WDT has 4 stages. */ + +enum esp32s2_wdt_stage_e +{ + ESP32S2_WDT_STAGE0 = 0, /* Stage 0 */ + ESP32S2_WDT_STAGE1 = 1, /* Stage 1 */ + ESP32S2_WDT_STAGE2 = 2, /* Stage 2 */ + ESP32S2_WDT_STAGE3 = 3 /* Stage 3 */ +}; + +/** + * Behavior of the WDT stage if it times out. + * + * @note These enum values should be compatible with the + * corresponding register field values. + */ + +enum esp32s2_wdt_stage_action_e +{ + ESP32S2_WDT_STAGE_ACTION_OFF = 0, /* Disabled. This stage will have no effects on the system. */ + ESP32S2_WDT_STAGE_ACTION_INT = 1, /* Trigger an interrupt when the stage expires. */ + ESP32S2_WDT_STAGE_ACTION_RESET_CPU = 2, /* Reset a CPU core when the stage expires. */ + ESP32S2_WDT_STAGE_ACTION_RESET_SYSTEM = 3, /* Reset the main system when the stage expires. + * This includes the CPU and all peripherals. + * The RTC is an exception and will not be reset. + */ + ESP32S2_WDT_STAGE_ACTION_RESET_RTC = 4 /* Reset the main system and the RTC when the stage expires. + * ONLY AVAILABLE FOR RWDT. + */ +}; + +/* ESP32-S2 WDT device */ + +struct esp32s2_wdt_dev_s +{ + struct esp32s2_wdt_ops_s *ops; +}; + +/* ESP32-S2 WDT operations + * + * This is a struct containing the pointers to the WDT operations. + */ + +struct esp32s2_wdt_ops_s +{ + /* WDT tasks */ + + void (*start)(struct esp32s2_wdt_dev_s *dev); + void (*stop)(struct esp32s2_wdt_dev_s *dev); + + /* WDT configuration */ + + void (*enablewp)(struct esp32s2_wdt_dev_s *dev); + void (*disablewp)(struct esp32s2_wdt_dev_s *dev); + void (*pre)(struct esp32s2_wdt_dev_s *dev, uint16_t value); + int32_t (*settimeout)(struct esp32s2_wdt_dev_s *dev, + uint32_t value, + enum esp32s2_wdt_stage_e stage); + void (*feed)(struct esp32s2_wdt_dev_s *dev); + int32_t (*stg_conf)(struct esp32s2_wdt_dev_s *dev, + enum esp32s2_wdt_stage_e stage, + enum esp32s2_wdt_stage_action_e conf); + uint16_t (*rtc_clk)(struct esp32s2_wdt_dev_s *dev); + + /* WDT interrupts */ + + int32_t (*setisr)(struct esp32s2_wdt_dev_s *dev, xcpt_t handler, + void *arg); + void (*enableint)(struct esp32s2_wdt_dev_s *dev); + void (*disableint)(struct esp32s2_wdt_dev_s *dev); + void (*ackint)(struct esp32s2_wdt_dev_s *dev); +}; + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ +struct esp32s2_wdt_dev_s *esp32s2_wdt_init(enum esp32s2_wdt_inst_e wdt_id); void esp32s2_wdt_early_deinit(void); +void esp32s2_wdt_deinit(struct esp32s2_wdt_dev_s *dev); +bool esp32s2_wdt_is_running(struct esp32s2_wdt_dev_s *dev); #endif /* __ARCH_XTENSA_SRC_ESP32S2_ESP32S2_WDT_H */ diff --git a/arch/xtensa/src/esp32s2/esp32s2_wdt_lowerhalf.c b/arch/xtensa/src/esp32s2/esp32s2_wdt_lowerhalf.c new file mode 100644 index 0000000..6289b39 --- /dev/null +++ b/arch/xtensa/src/esp32s2/esp32s2_wdt_lowerhalf.c @@ -0,0 +1,738 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s2/esp32s2_wdt_lowerhalf.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <assert.h> +#include <debug.h> +#include <errno.h> +#include <stdbool.h> +#include <string.h> +#include <sys/types.h> + +#include <nuttx/arch.h> +#include <nuttx/clock.h> +#include <nuttx/timers/watchdog.h> + +#include "xtensa.h" +#include "esp32s2_wdt.h" +#include "esp32s2_wdt_lowerhalf.h" +#include "hardware/esp32s2_soc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* MWDT clock period in microseconds */ + +#define MWDT_CLK_PERIOD_US (500) + +/* Number of MWDT cycles per microseconds */ + +#define MWDT_CYCLES_PER_MS (USEC_PER_MSEC / MWDT_CLK_PERIOD_US) + +/* Convert MWDT timeout cycles to milliseconds */ + +#define MWDT_TIMEOUT_MS(t) ((t) * MWDT_CYCLES_PER_MS) + +/* Maximum number of MWDT cycles supported for timeout */ + +#define MWDT_MAX_TIMEOUT_MS (UINT32_MAX / MWDT_CYCLES_PER_MS) + +/* MWDT clock prescaler value */ + +#define MWDT_CLK_PRESCALER_VALUE (MWDT_CLK_PERIOD_US * NSEC_PER_USEC / 12.5) + +/* Maximum number of cycles supported for a RWDT stage timeout */ + +#define RWDT_FULL_STAGE (UINT32_MAX) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum wdt_peripheral_e +{ + RTC, + TIMER +}; + +/* This structure provides the private representation of the "lower-half" + * driver state structure. This structure must be cast-compatible with the + * well-known watchdog_lowerhalf_s structure. + */ + +struct esp32s2_wdt_lowerhalf_s +{ + const struct watchdog_ops_s *ops; /* Lower-half operations */ + struct esp32s2_wdt_dev_s *wdt; /* ESP32-S2 watchdog driver */ + uint32_t timeout; /* The current timeout */ + enum wdt_peripheral_e peripheral; /* Indicates if it is from RTC or Timer Module */ + uint32_t lastreset; /* The last reset time */ + bool started; /* True: Timer has been started */ + xcpt_t handler; /* User Handler */ + void *upper; /* Pointer to watchdog_upperhalf_s */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Interrupt handling *******************************************************/ + +static int wdt_handler(int irq, void *context, void *arg); + +/* "Lower-half" driver methods **********************************************/ + +static int wdt_lh_start(struct watchdog_lowerhalf_s *lower); +static int wdt_lh_stop(struct watchdog_lowerhalf_s *lower); +static int wdt_lh_keepalive(struct watchdog_lowerhalf_s *lower); +static int wdt_lh_getstatus(struct watchdog_lowerhalf_s *lower, + struct watchdog_status_s *status); +static int wdt_lh_settimeout(struct watchdog_lowerhalf_s *lower, + uint32_t timeout); +static xcpt_t wdt_lh_capture(struct watchdog_lowerhalf_s *lower, + xcpt_t handler); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* "Lower-half" driver methods */ + +static const struct watchdog_ops_s g_esp32s2_wdg_ops = +{ + .start = wdt_lh_start, + .stop = wdt_lh_stop, + .keepalive = wdt_lh_keepalive, + .getstatus = wdt_lh_getstatus, + .settimeout = wdt_lh_settimeout, + .capture = wdt_lh_capture, + .ioctl = NULL +}; + +#ifdef CONFIG_ESP32S2_MWDT0 +/* MWDT0 lower-half */ + +static struct esp32s2_wdt_lowerhalf_s g_esp32s2_mwdt0_lowerhalf = +{ + .ops = &g_esp32s2_wdg_ops +}; +#endif + +#ifdef CONFIG_ESP32S2_MWDT1 +/* MWDT1 lower-half */ + +static struct esp32s2_wdt_lowerhalf_s g_esp32s2_mwdt1_lowerhalf = +{ + .ops = &g_esp32s2_wdg_ops +}; +#endif + +#ifdef CONFIG_ESP32S2_RWDT +/* RWDT lower-half */ + +static struct esp32s2_wdt_lowerhalf_s g_esp32s2_rwdt_lowerhalf = +{ + .ops = &g_esp32s2_wdg_ops +}; +#endif + +/**************************************************************************** + * Name: wdt_lh_start + * + * Description: + * Start the watchdog timer, register a callback if there is one and + * enables interrupt, otherwise, configure it to reset system on + * expiration. + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the + * "lower-half" driver state structure. + * + * Returned Values: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int wdt_lh_start(struct watchdog_lowerhalf_s *lower) +{ + struct esp32s2_wdt_lowerhalf_s *priv = + (struct esp32s2_wdt_lowerhalf_s *)lower; + int ret = OK; + + wdinfo("Entry: wdt_lh_start\n"); + + DEBUGASSERT(priv); + + if (priv->started) + { + /* Return EBUSY to indicate that the timer was already running */ + + ret = -EBUSY; + } + + /* If WDT was not started yet */ + + else + { + irqstate_t flags; + + priv->started = true; + + /* Unlock WDT */ + + ESP32S2_WDT_UNLOCK(priv->wdt); + + /* No User Handler */ + + if (priv->handler == NULL) + { + /* Then configure it to reset on wdt expiration */ + + if (priv->peripheral == TIMER) + { + ESP32S2_WDT_STG_CONF(priv->wdt, ESP32S2_WDT_STAGE0, + ESP32S2_WDT_STAGE_ACTION_RESET_SYSTEM); + } + else + { + ESP32S2_WDT_STG_CONF(priv->wdt, ESP32S2_WDT_STAGE0, + ESP32S2_WDT_STAGE_ACTION_RESET_RTC); + } + } + + /* User handler was already provided */ + + else + { + /* Then configure it to call the user handler on wdt expiration */ + + ESP32S2_WDT_STG_CONF(priv->wdt, ESP32S2_WDT_STAGE0, + ESP32S2_WDT_STAGE_ACTION_INT); + + /* Set the lower-half handler and enable interrupt */ + + flags = enter_critical_section(); + ESP32S2_WDT_SETISR(priv->wdt, wdt_handler, priv); + leave_critical_section(flags); + ESP32S2_WDT_ENABLEINT(priv->wdt); + } + + flags = enter_critical_section(); + priv->lastreset = clock_systime_ticks(); + ESP32S2_WDT_START(priv->wdt); + leave_critical_section(flags); + + /* Lock it again */ + + ESP32S2_WDT_LOCK(priv->wdt); + } + + return ret; +} + +/**************************************************************************** + * Name: wdt_lh_stop + * + * Description: + * Stop the watchdog timer. In case a callback was previously configured, + * unregister and deallocate it. + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the + * "lower-half" driver state structure. + * + ****************************************************************************/ + +static int wdt_lh_stop(struct watchdog_lowerhalf_s *lower) +{ + struct esp32s2_wdt_lowerhalf_s *priv = + (struct esp32s2_wdt_lowerhalf_s *)lower; + + /* Unlock WDT */ + + ESP32S2_WDT_UNLOCK(priv->wdt); + + /* Disable the WDT */ + + ESP32S2_WDT_STOP(priv->wdt); + + /* In case there is some callback registered, disable and deallocate */ + + if (priv->handler != NULL) + { + irqstate_t flags; + + ESP32S2_WDT_DISABLEINT(priv->wdt); + + flags = enter_critical_section(); + ESP32S2_WDT_SETISR(priv->wdt, NULL, NULL); + leave_critical_section(flags); + } + + /* Lock it again */ + + ESP32S2_WDT_LOCK(priv->wdt); + + priv->started = false; + + return OK; +} + +/**************************************************************************** + * Name: wdt_lh_keepalive + * + * Description: + * Reset the watchdog timer, prevent any + * imminent watchdog timeouts. This is sometimes referred as "pinging" + * the watchdog timer or "petting the dog". + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the + * "lower-half" driver state structure. + * + * + ****************************************************************************/ + +static int wdt_lh_keepalive(struct watchdog_lowerhalf_s *lower) +{ + struct esp32s2_wdt_lowerhalf_s *priv = + (struct esp32s2_wdt_lowerhalf_s *)lower; + irqstate_t flags; + + wdinfo("Entry\n"); + + /* Unlock */ + + ESP32S2_WDT_UNLOCK(priv->wdt); + + /* Feed the dog and updates the lastreset variable */ + + flags = enter_critical_section(); + priv->lastreset = clock_systime_ticks(); + ESP32S2_WDT_FEED(priv->wdt); + leave_critical_section(flags); + + /* Lock */ + + ESP32S2_WDT_LOCK(priv->wdt); + + return OK; +} + +/**************************************************************************** + * Name: wdt_lh_getstatus + * + * Description: + * Get the current watchdog timer status + * + * Input Parameters: + * lower - A pointer the publicly visible representation of + * the "lower-half" driver state structure. + * status - The location to return the watchdog status information. + * + ****************************************************************************/ + +static int wdt_lh_getstatus(struct watchdog_lowerhalf_s *lower, + struct watchdog_status_s *status) +{ + struct esp32s2_wdt_lowerhalf_s *priv = + (struct esp32s2_wdt_lowerhalf_s *)lower; + uint32_t ticks; + uint32_t elapsed; + + DEBUGASSERT(priv); + + /* Flags */ + + status->flags = 0; + + /* If no handler was settled, then RESET on expiration. + * Otherwise, call the user handler. + */ + + if (priv->handler == NULL) + { + status->flags |= WDFLAGS_RESET; + } + else + { + status->flags |= WDFLAGS_CAPTURE; + } + + if (priv->started) + { + status->flags |= WDFLAGS_ACTIVE; + } + + /* Return the current timeout in milliseconds */ + + status->timeout = priv->timeout; + + /* Get the elapsed time since the last ping */ + + ticks = clock_systime_ticks() - priv->lastreset; + elapsed = (uint32_t)TICK2MSEC(ticks); + + if (elapsed < priv->timeout) + { + /* Return the approximate time until the watchdog timer expiration */ + + status->timeleft = priv->timeout - elapsed; + } + else + { + status->timeleft = 0; + } + + return OK; +} + +/**************************************************************************** + * Name: wdt_lh_settimeout + * + * Description: + * Set a new timeout value (and reset the watchdog timer) + * + * Input Parameters: + * lower - A pointer the publicly visible representation of + * the "lower-half" driver state structure. + * timeout - The new timeout value in milliseconds. + * + * Returned Values: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int wdt_lh_settimeout(struct watchdog_lowerhalf_s *lower, + uint32_t timeout) +{ + struct esp32s2_wdt_lowerhalf_s *priv = + (struct esp32s2_wdt_lowerhalf_s *)lower; + uint16_t rtc_cycles = 0; + uint32_t rtc_ms_max = 0; + + wdinfo("Entry: timeout=%" PRIu32 "\n", timeout); + DEBUGASSERT(priv); + + /* Unlock WDT */ + + ESP32S2_WDT_UNLOCK(priv->wdt); + + /* Write the timeout value */ + + priv->timeout = timeout; + + /* Watchdog from Timer Module */ + + if (priv->peripheral == TIMER) + { + /* Is this timeout a valid value for Timer's WDT? */ + + if (timeout == 0 || timeout > MWDT_MAX_TIMEOUT_MS) + { + wderr("Cannot represent timeout=%" PRIu32 " > %" PRIu32 "\n", + timeout, MWDT_MAX_TIMEOUT_MS); + return -ERANGE; + } + else + { + ESP32S2_WDT_STO(priv->wdt, MWDT_TIMEOUT_MS(timeout), + ESP32S2_WDT_STAGE0); + } + } + + /* Watchdog from RTC Module */ + + else + { + rtc_cycles = ESP32S2_RWDT_CLK(priv->wdt); + rtc_ms_max = (RWDT_FULL_STAGE / (uint32_t)rtc_cycles); + + /* Is this timeout a valid value for RTC WDT? */ + + if (timeout == 0 || timeout > rtc_ms_max) + { + wderr("Cannot represent timeout=%" PRIu32 " > %" PRIu32 "\n", + timeout, rtc_ms_max); + return -ERANGE; + } + else + { + timeout = timeout * rtc_cycles; + ESP32S2_WDT_STO(priv->wdt, timeout, ESP32S2_WDT_STAGE0); + } + } + + /* Reset the wdt */ + + ESP32S2_WDT_FEED(priv->wdt); + + /* Lock it again */ + + ESP32S2_WDT_LOCK(priv->wdt); + + return OK; +} + +/**************************************************************************** + * Name: wdt_lh_capture + * + * Description: + * Don't reset on watchdog timer timeout; instead, call this user provider + * timeout handler. NOTE: Providing handler==NULL will restore the reset + * behavior. + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the + * "lower-half" driver state structure. + * newhandler - The new watchdog expiration function pointer. If this + * function pointer is NULL, then the reset-on-expiration + * behavior is restored, + * + * Returned Value: + * The previous watchdog expiration function pointer or NULL if there was + * no previous function pointer, i.e., if the previous behavior was + * reset-on-expiration (NULL is also returned if an error occurs). + * + ****************************************************************************/ + +static xcpt_t wdt_lh_capture(struct watchdog_lowerhalf_s *lower, + xcpt_t handler) +{ + struct esp32s2_wdt_lowerhalf_s *priv = + (struct esp32s2_wdt_lowerhalf_s *)lower; + irqstate_t flags; + xcpt_t oldhandler; + + DEBUGASSERT(priv); + + wdinfo("Entry: handler=0x%" PRIxPTR "\n", (uintptr_t) handler); + + /* Get the old handler to return it */ + + oldhandler = priv->handler; + + ESP32S2_WDT_UNLOCK(priv->wdt); + + flags = enter_critical_section(); + + /* Save the new user handler */ + + priv->handler = handler; + + /* There is a user callback and the timer has already been started. + * The user wants to set a callback after starting the wdt or wants to + * change the callback function once a callback has already been settled. + */ + + if (priv->handler != NULL && priv->started) + { + /* Deallocate the previous allocated interrupt + * If there is a previous allocated interrupt. + */ + + if (oldhandler != NULL) + { + ESP32S2_WDT_SETISR(priv->wdt, NULL, NULL); + } + else + { + /* If it was previous configured to reset on timeout + * then change to interrupt. + */ + + ESP32S2_WDT_STG_CONF(priv->wdt, ESP32S2_WDT_STAGE0, + ESP32S2_WDT_STAGE_ACTION_INT); + } + + /* Set the lower-half handler and enable interrupt */ + + ESP32S2_WDT_SETISR(priv->wdt, wdt_handler, priv); + ESP32S2_WDT_ENABLEINT(priv->wdt); + } + + /* In case the user wants to disable the callback */ + + else + { + ESP32S2_WDT_DISABLEINT(priv->wdt); + ESP32S2_WDT_SETISR(priv->wdt, NULL, NULL); + + /* Then configure it to reset on WDT expiration */ + + if (priv->peripheral == TIMER) + { + ESP32S2_WDT_STG_CONF(priv->wdt, ESP32S2_WDT_STAGE0, + ESP32S2_WDT_STAGE_ACTION_RESET_SYSTEM); + } + else + { + ESP32S2_WDT_STG_CONF(priv->wdt, ESP32S2_WDT_STAGE0, + ESP32S2_WDT_STAGE_ACTION_RESET_RTC); + } + } + + leave_critical_section(flags); + ESP32S2_WDT_LOCK(priv->wdt); + return oldhandler; +} + +/* Interrupt handling *******************************************************/ + +static int wdt_handler(int irq, void *context, void *arg) +{ + struct esp32s2_wdt_lowerhalf_s *priv = + (struct esp32s2_wdt_lowerhalf_s *)arg; + + /* Run the user callback */ + + priv->handler(irq, context, priv->upper); + + /* Clear the Interrupt */ + + ESP32S2_WDT_UNLOCK(priv->wdt); + + ESP32S2_WDT_ACKINT(priv->wdt); + + ESP32S2_WDT_LOCK(priv->wdt); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s2_wdt_initialize + * + * Description: + * Initialize the watchdog timer. The watchdog timer is initialized + * and registered as 'devpath'. + * + * Input Parameters: + * devpath - The full path to the watchdog. This should + * be of the form /dev/watchdogX + * wdt - WDT instance to be initialized. + * + * Returned Values: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int esp32s2_wdt_initialize(const char *devpath, enum esp32s2_wdt_inst_e wdt) +{ + struct esp32s2_wdt_lowerhalf_s *lower = NULL; + int ret = OK; + + DEBUGASSERT(devpath); + + switch (wdt) + { +#ifdef CONFIG_ESP32S2_MWDT0 + case ESP32S2_WDT_MWDT0: + { + lower = &g_esp32s2_mwdt0_lowerhalf; + lower->peripheral = TIMER; + break; + } +#endif + +#ifdef CONFIG_ESP32S2_MWDT1 + case ESP32S2_WDT_MWDT1: + { + lower = &g_esp32s2_mwdt1_lowerhalf; + lower->peripheral = TIMER; + break; + } +#endif + +#ifdef CONFIG_ESP32S2_RWDT + case ESP32S2_WDT_RWDT: + { + lower = &g_esp32s2_rwdt_lowerhalf; + lower->peripheral = RTC; + break; + } +#endif + + default: + { + ret = -ENODEV; + goto errout; + } + } + + /* Initialize the elements of lower-half state structure */ + + lower->handler = NULL; + lower->timeout = 0; + lower->wdt = esp32s2_wdt_init(wdt); + + if (lower->wdt == NULL) + { + ret = -EINVAL; + goto errout; + } + + lower->started = esp32s2_wdt_is_running(lower->wdt); + + ESP32S2_WDT_UNLOCK(lower->wdt); + + /* If it is a Main System Watchdog Timer configure the Prescale to + * have a 500us period. + */ + + if (lower->peripheral == TIMER) + { + ESP32S2_MWDT_PRE(lower->wdt, MWDT_CLK_PRESCALER_VALUE); + } + + ESP32S2_WDT_LOCK(lower->wdt); + + /* Register the watchdog driver as /dev/watchdogX. If the registration goes + * right the returned value from watchdog_register is a pointer to + * watchdog_upperhalf_s that can be either used with watchdog_unregister() + * or with the handler's arg. + */ + + lower->upper = watchdog_register(devpath, + (struct watchdog_lowerhalf_s *)lower); + if (lower->upper == NULL) + { + /* The actual cause of the failure may have been a failure to allocate + * perhaps a failure to register the watchdog driver (such as if the + * 'devpath' were not unique). We know here but we return EEXIST to + * indicate the failure (implying the non-unique devpath). + */ + + ret = -EEXIST; + } + +errout: + return ret; +} diff --git a/arch/xtensa/src/esp32s2/esp32s2_wdt.c b/arch/xtensa/src/esp32s2/esp32s2_wdt_lowerhalf.h similarity index 62% copy from arch/xtensa/src/esp32s2/esp32s2_wdt.c copy to arch/xtensa/src/esp32s2/esp32s2_wdt_lowerhalf.h index 60addbd..a3bbd25 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_wdt.c +++ b/arch/xtensa/src/esp32s2/esp32s2_wdt_lowerhalf.h @@ -1,5 +1,5 @@ /**************************************************************************** - * arch/xtensa/src/esp32s2/esp32s2_wdt.c + * arch/xtensa/src/esp32s2/esp32s2_wdt_lowerhalf.h * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -18,33 +18,40 @@ * ****************************************************************************/ +#ifndef __ARCH_XTENSA_SRC_ESP32S2_ESP32S2_WDT_LOWERHALF_H +#define __ARCH_XTENSA_SRC_ESP32S2_ESP32S2_WDT_LOWERHALF_H + /**************************************************************************** * Included Files ****************************************************************************/ -#include "xtensa.h" -#include "hardware/esp32s2_rtccntl.h" - #include "esp32s2_wdt.h" /**************************************************************************** - * Public Functions + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes ****************************************************************************/ /**************************************************************************** - * Name: esp32s2_wdt_early_deinit + * Name: esp32s2_wdt_initialize * * Description: - * Disable the WDT(s) that was/were enabled by the bootloader. + * Initialize the watchdog timer. The watchdog timer is initialized + * and registered as 'devpath'. + * + * Input Parameters: + * devpath - The full path to the watchdog. + * wdt - WDT instance to be initialized. + * + * Returned Values: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. * ****************************************************************************/ -void esp32s2_wdt_early_deinit(void) -{ - uint32_t regval; - putreg32(RTC_CNTL_WDT_WKEY_VALUE, RTC_CNTL_WDTWPROTECT_REG); - regval = getreg32(RTC_CNTL_WDTCONFIG0_REG); - regval &= ~RTC_CNTL_WDT_EN; - putreg32(regval, RTC_CNTL_WDTCONFIG0_REG); - putreg32(0, RTC_CNTL_WDTWPROTECT_REG); -} +int esp32s2_wdt_initialize(const char *devpath, enum esp32s2_wdt_inst_e wdt); + +#endif /* __ARCH_XTENSA_SRC_ESP32S2_ESP32S2_WDT_LOWERHALF_H */ diff --git a/arch/xtensa/src/esp32s2/hardware/esp32s2_rtccntl.h b/arch/xtensa/src/esp32s2/hardware/esp32s2_rtccntl.h index 4120517..45f93ce 100644 --- a/arch/xtensa/src/esp32s2/hardware/esp32s2_rtccntl.h +++ b/arch/xtensa/src/esp32s2/hardware/esp32s2_rtccntl.h @@ -31,11 +31,32 @@ * Pre-processor Definitions ****************************************************************************/ +/* Offset relative to each watchdog timer instance memory base */ + +#define RWDT_CONFIG0_OFFSET 0x0094 + +/* RWDT */ + +#define RWDT_STAGE0_TIMEOUT_OFFSET 0x0098 +#define RWDT_STAGE1_TIMEOUT_OFFSET 0x009c +#define RWDT_STAGE2_TIMEOUT_OFFSET 0x00a0 +#define RWDT_STAGE3_TIMEOUT_OFFSET 0x00a4 +#define RWDT_FEED_OFFSET 0x00a8 +#define RWDT_WP_REG 0x00ac +#define RWDT_INT_ENA_REG_OFFSET 0x0040 +#define RWDT_INT_CLR_REG_OFFSET 0x004c + /* The value that needs to be written to RTC_CNTL_WDT_WKEY to * write-enable the wdt registers */ -#define RTC_CNTL_WDT_WKEY_VALUE 0x50d83aa1 +#define RTC_CNTL_WDT_WKEY_VALUE 0x50d83aa1 + +/* The value that needs to be written to RTC_CNTL_SWD_WPROTECT_REG + * to write-enable the wdt registers + */ + +#define RTC_CNTL_SWD_WKEY_VALUE 0x8f1d312a #define DPORT_CPUPERIOD_SEL_80 0 #define DPORT_CPUPERIOD_SEL_160 1 diff --git a/arch/xtensa/src/esp32s2/hardware/esp32s2_tim.h b/arch/xtensa/src/esp32s2/hardware/esp32s2_tim.h index 03479c4..8b8a75b 100644 --- a/arch/xtensa/src/esp32s2/hardware/esp32s2_tim.h +++ b/arch/xtensa/src/esp32s2/hardware/esp32s2_tim.h @@ -31,7 +31,29 @@ * Pre-processor Definitions ****************************************************************************/ -#define SHIFT_32 32 +/* Offset relative to each watchdog timer instance memory base */ + +#define MWDT_CONFIG0_OFFSET 0x0048 + +/* MWDT */ + +#define MWDT_CLK_PRESCALE_OFFSET 0x004c +#define MWDT_STAGE0_TIMEOUT_OFFSET 0x0050 +#define MWDT_STAGE1_TIMEOUT_OFFSET 0x0054 +#define MWDT_STAGE2_TIMEOUT_OFFSET 0x0058 +#define MWDT_STAGE3_TIMEOUT_OFFSET 0x005c +#define MWDT_FEED_OFFSET 0x0060 +#define MWDT_WP_REG 0x0064 +#define MWDT_INT_ENA_REG_OFFSET 0x0098 +#define MWDT_INT_CLR_REG_OFFSET 0x00a0 + +/* The value that needs to be written to TIMG_WDT_WKEY to + * write-enable the WDT registers. + */ + +#define TIMG_WDT_WKEY_VALUE 0x50d83aa1 + +#define SHIFT_32 32 /* TIMG_T0CONFIG_REG(i) register * Timer 0 configuration register
