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

Reply via email to