xtensa/esp32s3: 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 <gustavo.nihei@espressif.com>
This commit is contained in:
Gustavo Henrique Nihei 2022-02-22 10:31:36 -03:00 committed by Xiang Xiao
parent 9603b8f67c
commit b49ee3d4ed
8 changed files with 5109 additions and 2 deletions

View File

@ -306,6 +306,10 @@ config ESP32S3_TIMER
bool
default n
config ESP32S3_WDT
bool
default n
config ESP32S3_UART0
bool "UART 0"
default n
@ -355,6 +359,33 @@ config ESP32S3_TIMER3
---help---
Enables Timer 3
config ESP32S3_MWDT0
bool "Main System Watchdog Timer (Group 0)"
default n
select ESP32S3_WDT
---help---
Includes MWDT0. This watchdog timer is part of the Group 0
timer submodule.
config ESP32S3_MWDT1
bool "Main System Watchdog Timer (Group 1)"
default n
select ESP32S3_WDT
---help---
Includes MWDT1. This watchdog timer is part of the Group 0
timer submodule.
config ESP32S3_RWDT
bool "RTC Watchdog Timer"
default n
select ESP32S3_WDT
---help---
Includes RWDT. This watchdog timer is from the RTC module.
When it is selected, if the developer sets it to reset on expiration
it will reset Main System and the RTC module. If you don't want
to have the RTC module reset, please, use the Timers' Module WDTs.
They will only reset Main System.
endmenu # ESP32-S3 Peripheral Selection
menu "UART configuration"

View File

@ -79,3 +79,7 @@ ifeq ($(CONFIG_TIMER),y)
CHIP_CSRCS += esp32s3_tim_lowerhalf.c
endif
endif
ifeq ($(CONFIG_WATCHDOG),y)
CHIP_CSRCS += esp32s3_wdt_lowerhalf.c
endif

View File

@ -22,15 +22,830 @@
* Included Files
****************************************************************************/
#include "xtensa.h"
#include <nuttx/config.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <stdbool.h>
#include <assert.h>
#include <debug.h>
#include "esp32s3_wdt.h"
#include "xtensa.h"
#include "hardware/esp32s3_rtccntl.h"
#include "hardware/esp32s3_tim.h"
#include "hardware/esp32s3_efuse.h"
#include "esp32s3_irq.h"
#include "esp32s3_wdt.h"
#ifdef CONFIG_ESP32S3_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 esp32s3_wdt_priv_s *)dev)->base == \
RTC_CNTL_RTC_OPTIONS0_REG)
/****************************************************************************
* Private Types
****************************************************************************/
struct esp32s3_wdt_priv_s
{
struct esp32s3_wdt_ops_s *ops;
uint32_t base; /* WDT register base address */
uint8_t cpu; /* CPU ID */
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 esp32s3_wdt_dev_s *dev, uint32_t offset,
uint32_t value);
static void wdt_modifyreg32(struct esp32s3_wdt_dev_s *dev, uint32_t offset,
uint32_t clearbits, uint32_t setbits);
static uint32_t wdt_getreg(struct esp32s3_wdt_dev_s *dev, uint32_t offset);
/* WDT operations ***********************************************************/
static void wdt_start(struct esp32s3_wdt_dev_s *dev);
static void wdt_stop(struct esp32s3_wdt_dev_s *dev);
static void wdt_enablewp(struct esp32s3_wdt_dev_s *dev);
static void wdt_disablewp(struct esp32s3_wdt_dev_s *dev);
static void wdt_pre(struct esp32s3_wdt_dev_s *dev,
uint16_t value);
static int32_t wdt_settimeout(struct esp32s3_wdt_dev_s *dev,
uint32_t value,
enum esp32s3_wdt_stage_e stage);
static void wdt_feed(struct esp32s3_wdt_dev_s *dev);
static int32_t wdt_config_stage(struct esp32s3_wdt_dev_s *dev,
enum esp32s3_wdt_stage_e stage,
enum esp32s3_wdt_stage_action_e cfg);
static int32_t wdt_setisr(struct esp32s3_wdt_dev_s *dev,
xcpt_t handler, void *arg);
static void wdt_enableint(struct esp32s3_wdt_dev_s *dev);
static void wdt_disableint(struct esp32s3_wdt_dev_s *dev);
static void wdt_ackint(struct esp32s3_wdt_dev_s *dev);
/****************************************************************************
* Private Data
****************************************************************************/
/* ESP32-S3 WDT ops */
struct esp32s3_wdt_ops_s esp32s3_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 esp32s3_wdt_ops_s esp32s3_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_ESP32S3_MWDT0
struct esp32s3_wdt_priv_s g_esp32s3_mwdt0_priv =
{
.ops = &esp32s3_mwdt_ops,
.base = TIMG_T0CONFIG_REG(0),
.periph = ESP32S3_PERIPH_TG_WDT_LEVEL,
.irq = ESP32S3_IRQ_TG_WDT_LEVEL,
.cpuint = -ENOMEM,
.inuse = false,
};
#endif
#ifdef CONFIG_ESP32S3_MWDT1
struct esp32s3_wdt_priv_s g_esp32s3_mwdt1_priv =
{
.ops = &esp32s3_mwdt_ops,
.base = TIMG_T0CONFIG_REG(1),
.periph = ESP32S3_PERIPH_TG1_WDT_LEVEL,
.irq = ESP32S3_IRQ_TG1_WDT_LEVEL,
.cpuint = -ENOMEM,
.inuse = false,
};
#endif
#ifdef CONFIG_ESP32S3_RWDT
struct esp32s3_wdt_priv_s g_esp32s3_rwdt_priv =
{
.ops = &esp32s3_rwdt_ops,
.base = RTC_CNTL_RTC_OPTIONS0_REG,
.periph = ESP32S3_PERIPH_RTC_CORE,
.irq = ESP32S3_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 esp32s3_wdt_dev_s *dev, uint32_t offset,
uint32_t value)
{
DEBUGASSERT(dev != NULL);
putreg32(value, ((struct esp32s3_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 esp32s3_wdt_dev_s *dev, uint32_t offset,
uint32_t clearbits, uint32_t setbits)
{
DEBUGASSERT(dev != NULL);
modifyreg32(((struct esp32s3_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 esp32s3_wdt_dev_s *dev, uint32_t offset)
{
DEBUGASSERT(dev != NULL);
return getreg32(((struct esp32s3_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 esp32s3_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 esp32s3_wdt_dev_s *dev,
enum esp32s3_wdt_stage_e stage,
enum esp32s3_wdt_stage_action_e cfg)
{
int32_t ret = OK;
uint32_t mask;
DEBUGASSERT(dev != NULL);
switch (stage)
{
case ESP32S3_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 ESP32S3_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 ESP32S3_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 ESP32S3_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 esp32s3_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 esp32s3_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 esp32s3_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 esp32s3_wdt_dev_s *dev, uint16_t pre)
{
uint32_t mask = (uint32_t)pre << TIMG_WDT_CLK_PRESCALE_S;
DEBUGASSERT(dev != NULL);
wdt_modifyreg32(dev, MWDT_CLK_PRESCALE_OFFSET, TIMG_WDT_CLK_PRESCALE_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 esp32s3_wdt_dev_s *dev, uint32_t value,
enum esp32s3_wdt_stage_e stage)
{
int32_t ret = OK;
DEBUGASSERT(dev != NULL);
switch (stage)
{
case ESP32S3_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 ESP32S3_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 ESP32S3_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 ESP32S3_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 esp32s3_wdt_dev_s *dev)
{
DEBUGASSERT(dev != NULL);
if (IS_RWDT(dev))
{
wdt_modifyreg32(dev, RWDT_FEED_OFFSET, 0, RTC_CNTL_RTC_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 esp32s3_wdt_dev_s *dev, xcpt_t handler,
void *arg)
{
struct esp32s3_wdt_priv_s *wdt = NULL;
int32_t ret = OK;
DEBUGASSERT(dev != NULL);
wdt = (struct esp32s3_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);
esp32s3_teardown_irq(wdt->cpu, 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->cpu = up_cpu_index();
wdt->cpuint = esp32s3_setup_irq(wdt->cpu, wdt->periph,
1, ESP32S3_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)
{
esp32s3_teardown_irq(wdt->cpu, 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 esp32s3_wdt_dev_s *dev)
{
DEBUGASSERT(dev != NULL);
if (IS_RWDT(dev))
{
wdt_modifyreg32(dev, RWDT_INT_ENA_REG_OFFSET, 0,
RTC_CNTL_RTC_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 esp32s3_wdt_dev_s *dev)
{
DEBUGASSERT(dev != NULL);
if (IS_RWDT(dev))
{
wdt_modifyreg32(dev, RWDT_INT_ENA_REG_OFFSET, RTC_CNTL_RTC_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 esp32s3_wdt_dev_s *dev)
{
DEBUGASSERT(dev != NULL);
if (IS_RWDT(dev))
{
wdt_putreg(dev, RWDT_INT_CLR_REG_OFFSET, RTC_CNTL_RTC_WDT_INT_CLR);
}
else
{
wdt_putreg(dev, MWDT_INT_CLR_REG_OFFSET, TIMG_WDT_INT_CLR);
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: esp32s3_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 esp32s3_wdt_dev_s *esp32s3_wdt_init(enum esp32s3_wdt_inst_e wdt_id)
{
struct esp32s3_wdt_priv_s *wdt = NULL;
/* Get WDT instance */
switch (wdt_id)
{
#ifdef CONFIG_ESP32S3_MWDT0
case ESP32S3_WDT_MWDT0:
{
wdt = &g_esp32s3_mwdt0_priv;
break;
}
#endif
#ifdef CONFIG_ESP32S3_MWDT1
case ESP32S3_WDT_MWDT1:
{
wdt = &g_esp32s3_mwdt1_priv;
break;
}
#endif
#ifdef CONFIG_ESP32S3_RWDT
case ESP32S3_WDT_RWDT:
{
wdt = &g_esp32s3_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 == true)
{
wderr("ERROR: WDT %d is already in use\n", wdt_id);
wdt = NULL;
}
else
{
wdt->inuse = true;
}
errout:
return (struct esp32s3_wdt_dev_s *)wdt;
}
/****************************************************************************
* Name: esp32s3_wdt_early_deinit
*
@ -48,3 +863,65 @@ void esp32s3_wdt_early_deinit(void)
putreg32(regval, RTC_CNTL_RTC_WDTCONFIG0_REG);
putreg32(0, RTC_CNTL_RTC_WDTWPROTECT_REG);
}
/****************************************************************************
* Name: esp32s3_wdt_deinit
*
* Description:
* Deinitialize a WDT device.
*
* Parameters:
* dev - Pointer to the driver state structure.
*
****************************************************************************/
void esp32s3_wdt_deinit(struct esp32s3_wdt_dev_s *dev)
{
struct esp32s3_wdt_priv_s *wdt = NULL;
DEBUGASSERT(dev != NULL);
wdt = (struct esp32s3_wdt_priv_s *)dev;
wdt->inuse = false;
}
/****************************************************************************
* Name: esp32s3_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 esp32s3_wdt_is_running(struct esp32s3_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;
}

View File

@ -25,10 +25,125 @@
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <nuttx/irq.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Helpers ******************************************************************/
#define ESP32S3_WDT_START(d) ((d)->ops->start(d))
#define ESP32S3_WDT_STOP(d) ((d)->ops->stop(d))
#define ESP32S3_WDT_LOCK(d) ((d)->ops->enablewp(d))
#define ESP32S3_WDT_UNLOCK(d) ((d)->ops->disablewp(d))
#define ESP32S3_MWDT_PRE(d, v) ((d)->ops->pre(d, v))
#define ESP32S3_WDT_STO(d, v, s) ((d)->ops->settimeout(d, v, s))
#define ESP32S3_WDT_FEED(d) ((d)->ops->feed(d))
#define ESP32S3_WDT_STG_CONF(d, s, c) ((d)->ops->stg_conf(d, s, c))
#define ESP32S3_RWDT_CLK(d) ((d)->ops->rtc_clk(d))
#define ESP32S3_WDT_SETISR(d, hnd, arg) ((d)->ops->setisr(d, hnd, arg))
#define ESP32S3_WDT_ENABLEINT(d) ((d)->ops->enableint(d))
#define ESP32S3_WDT_DISABLEINT(d) ((d)->ops->disableint(d))
#define ESP32S3_WDT_ACKINT(d) ((d)->ops->ackint(d))
/****************************************************************************
* Public Types
****************************************************************************/
/* Instances of Watchdog Timer */
enum esp32s3_wdt_inst_e
{
ESP32S3_WDT_MWDT0 = 0, /* Main System Watchdog Timer (MWDT) of Timer Group 0 */
ESP32S3_WDT_MWDT1, /* Main System Watchdog Timer (MWDT) of Timer Group 1 */
ESP32S3_WDT_RWDT /* RTC Watchdog Timer (RWDT) */
};
/* Stages of a Watchdog Timer. A WDT has 4 stages. */
enum esp32s3_wdt_stage_e
{
ESP32S3_WDT_STAGE0 = 0, /* Stage 0 */
ESP32S3_WDT_STAGE1 = 1, /* Stage 1 */
ESP32S3_WDT_STAGE2 = 2, /* Stage 2 */
ESP32S3_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 esp32s3_wdt_stage_action_e
{
ESP32S3_WDT_STAGE_ACTION_OFF = 0, /* Disabled. This stage will have no effects on the system. */
ESP32S3_WDT_STAGE_ACTION_INT = 1, /* Trigger an interrupt when the stage expires. */
ESP32S3_WDT_STAGE_ACTION_RESET_CPU = 2, /* Reset a CPU core when the stage expires. */
ESP32S3_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.
*/
ESP32S3_WDT_STAGE_ACTION_RESET_RTC = 4 /* Reset the main system and the RTC when the stage expires.
* ONLY AVAILABLE FOR RWDT.
*/
};
/* ESP32-S3 WDT device */
struct esp32s3_wdt_dev_s
{
struct esp32s3_wdt_ops_s *ops;
};
/* ESP32-S3 WDT operations
*
* This is a struct containing the pointers to the WDT operations.
*/
struct esp32s3_wdt_ops_s
{
/* WDT tasks */
void (*start)(struct esp32s3_wdt_dev_s *dev);
void (*stop)(struct esp32s3_wdt_dev_s *dev);
/* WDT configuration */
void (*enablewp)(struct esp32s3_wdt_dev_s *dev);
void (*disablewp)(struct esp32s3_wdt_dev_s *dev);
void (*pre)(struct esp32s3_wdt_dev_s *dev, uint16_t value);
int32_t (*settimeout)(struct esp32s3_wdt_dev_s *dev,
uint32_t value,
enum esp32s3_wdt_stage_e stage);
void (*feed)(struct esp32s3_wdt_dev_s *dev);
int32_t (*stg_conf)(struct esp32s3_wdt_dev_s *dev,
enum esp32s3_wdt_stage_e stage,
enum esp32s3_wdt_stage_action_e conf);
uint16_t (*rtc_clk)(struct esp32s3_wdt_dev_s *dev);
/* WDT interrupts */
int32_t (*setisr)(struct esp32s3_wdt_dev_s *dev, xcpt_t handler,
void *arg);
void (*enableint)(struct esp32s3_wdt_dev_s *dev);
void (*disableint)(struct esp32s3_wdt_dev_s *dev);
void (*ackint)(struct esp32s3_wdt_dev_s *dev);
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
struct esp32s3_wdt_dev_s *esp32s3_wdt_init(enum esp32s3_wdt_inst_e wdt_id);
void esp32s3_wdt_early_deinit(void);
void esp32s3_wdt_deinit(struct esp32s3_wdt_dev_s *dev);
bool esp32s3_wdt_is_running(struct esp32s3_wdt_dev_s *dev);
#endif /* __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_WDT_H */

View File

@ -0,0 +1,741 @@
/****************************************************************************
* arch/xtensa/src/esp32s3/esp32s3_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 <sys/types.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/clock.h>
#include <nuttx/timers/watchdog.h>
#include "xtensa.h"
#include "hardware/esp32s3_soc.h"
#include "esp32s3_wdt.h"
#include "esp32s3_wdt_lowerhalf.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 esp32s3_wdt_lowerhalf_s
{
const struct watchdog_ops_s *ops; /* Lower half operations */
struct esp32s3_wdt_dev_s *wdt; /* ESP32-S3 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_esp32s3_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_ESP32S3_MWDT0
/* MWDT0 lower-half */
static struct esp32s3_wdt_lowerhalf_s g_esp32s3_mwdt0_lowerhalf =
{
.ops = &g_esp32s3_wdg_ops,
};
#endif
#ifdef CONFIG_ESP32S3_MWDT1
/* MWDT1 lower-half */
static struct esp32s3_wdt_lowerhalf_s g_esp32s3_mwdt1_lowerhalf =
{
.ops = &g_esp32s3_wdg_ops,
};
#endif
#ifdef CONFIG_ESP32S3_RWDT
/* RWDT lower-half */
static struct esp32s3_wdt_lowerhalf_s g_esp32s3_rwdt_lowerhalf =
{
.ops = &g_esp32s3_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 esp32s3_wdt_lowerhalf_s *priv =
(struct esp32s3_wdt_lowerhalf_s *)lower;
int ret = OK;
wdinfo("Entry: wdt_lh_start\n");
DEBUGASSERT(priv);
if (priv->started == true)
{
/* Return EBUSY to indicate that the timer was already running */
ret = -EBUSY;
goto errout;
}
/* If WDT was not started yet */
else
{
irqstate_t flags;
priv->started = true;
/* Unlock WDT */
ESP32S3_WDT_UNLOCK(priv->wdt);
/* No User Handler */
if (priv->handler == NULL)
{
/* Then configure it to reset on wdt expiration */
if (priv->peripheral == TIMER)
{
ESP32S3_WDT_STG_CONF(priv->wdt, ESP32S3_WDT_STAGE0,
ESP32S3_WDT_STAGE_ACTION_RESET_SYSTEM);
}
else
{
ESP32S3_WDT_STG_CONF(priv->wdt, ESP32S3_WDT_STAGE0,
ESP32S3_WDT_STAGE_ACTION_RESET_RTC);
}
}
/* User handler was already provided */
else
{
/* Then configure it to call the user handler on wdt expiration */
ESP32S3_WDT_STG_CONF(priv->wdt, ESP32S3_WDT_STAGE0,
ESP32S3_WDT_STAGE_ACTION_INT);
/* Set the lower-half handler and enable interrupt */
flags = enter_critical_section();
ESP32S3_WDT_SETISR(priv->wdt, wdt_handler, priv);
leave_critical_section(flags);
ESP32S3_WDT_ENABLEINT(priv->wdt);
}
flags = enter_critical_section();
priv->lastreset = clock_systime_ticks();
ESP32S3_WDT_START(priv->wdt);
leave_critical_section(flags);
/* Lock it again */
ESP32S3_WDT_LOCK(priv->wdt);
}
errout:
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 esp32s3_wdt_lowerhalf_s *priv =
(struct esp32s3_wdt_lowerhalf_s *)lower;
/* Unlock WDT */
ESP32S3_WDT_UNLOCK(priv->wdt);
/* Disable the WDT */
ESP32S3_WDT_STOP(priv->wdt);
/* In case there is some callback registered, disable and deallocate */
if (priv->handler != NULL)
{
irqstate_t flags;
ESP32S3_WDT_DISABLEINT(priv->wdt);
flags = enter_critical_section();
ESP32S3_WDT_SETISR(priv->wdt, NULL, NULL);
leave_critical_section(flags);
}
/* Lock it again */
ESP32S3_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 esp32s3_wdt_lowerhalf_s *priv =
(struct esp32s3_wdt_lowerhalf_s *)lower;
irqstate_t flags;
wdinfo("Entry\n");
/* Unlock */
ESP32S3_WDT_UNLOCK(priv->wdt);
/* Feed the dog and updates the lastreset variable */
flags = enter_critical_section();
priv->lastreset = clock_systime_ticks();
ESP32S3_WDT_FEED(priv->wdt);
leave_critical_section(flags);
/* Lock */
ESP32S3_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 esp32s3_wdt_lowerhalf_s *priv =
(struct esp32s3_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 esp32s3_wdt_lowerhalf_s *priv =
(struct esp32s3_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 */
ESP32S3_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
{
ESP32S3_WDT_STO(priv->wdt, MWDT_TIMEOUT_MS(timeout),
ESP32S3_WDT_STAGE0);
}
}
/* Watchdog from RTC Module */
else
{
rtc_cycles = ESP32S3_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;
ESP32S3_WDT_STO(priv->wdt, timeout, ESP32S3_WDT_STAGE0);
}
}
/* Reset the wdt */
ESP32S3_WDT_FEED(priv->wdt);
/* Lock it again */
ESP32S3_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 esp32s3_wdt_lowerhalf_s *priv =
(struct esp32s3_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;
ESP32S3_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 == true)
{
/* Deallocate the previous allocated interrupt
* If there is a previous allocated interrupt.
*/
if (oldhandler != NULL)
{
ESP32S3_WDT_SETISR(priv->wdt, NULL, NULL);
}
else
{
/* If it was previous configured to reset on timeout
* then change to interrupt.
*/
ESP32S3_WDT_STG_CONF(priv->wdt, ESP32S3_WDT_STAGE0,
ESP32S3_WDT_STAGE_ACTION_INT);
}
/* Set the lower-half handler and enable interrupt */
ESP32S3_WDT_SETISR(priv->wdt, wdt_handler, priv);
ESP32S3_WDT_ENABLEINT(priv->wdt);
}
/* In case the user wants to disable the callback */
else
{
ESP32S3_WDT_DISABLEINT(priv->wdt);
ESP32S3_WDT_SETISR(priv->wdt, NULL, NULL);
/* Then configure it to reset on WDT expiration */
if (priv->peripheral == TIMER)
{
ESP32S3_WDT_STG_CONF(priv->wdt, ESP32S3_WDT_STAGE0,
ESP32S3_WDT_STAGE_ACTION_RESET_SYSTEM);
}
else
{
ESP32S3_WDT_STG_CONF(priv->wdt, ESP32S3_WDT_STAGE0,
ESP32S3_WDT_STAGE_ACTION_RESET_RTC);
}
}
leave_critical_section(flags);
ESP32S3_WDT_LOCK(priv->wdt);
return oldhandler;
}
/* Interrupt handling *******************************************************/
static int wdt_handler(int irq, void *context, void *arg)
{
struct esp32s3_wdt_lowerhalf_s *priv = arg;
/* Run the user callback */
priv->handler(irq, context, priv->upper);
/* Clear the Interrupt */
ESP32S3_WDT_UNLOCK(priv->wdt);
ESP32S3_WDT_ACKINT(priv->wdt);
ESP32S3_WDT_LOCK(priv->wdt);
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: esp32s3_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 esp32s3_wdt_initialize(const char *devpath, enum esp32s3_wdt_inst_e wdt)
{
struct esp32s3_wdt_lowerhalf_s *lower = NULL;
int ret = OK;
DEBUGASSERT(devpath);
switch (wdt)
{
#ifdef CONFIG_ESP32S3_MWDT0
case ESP32S3_WDT_MWDT0:
{
lower = &g_esp32s3_mwdt0_lowerhalf;
lower->peripheral = TIMER;
break;
}
#endif
#ifdef CONFIG_ESP32S3_MWDT1
case ESP32S3_WDT_MWDT1:
{
lower = &g_esp32s3_mwdt1_lowerhalf;
lower->peripheral = TIMER;
break;
}
#endif
#ifdef CONFIG_ESP32S3_RWDT
case ESP32S3_WDT_RWDT:
{
lower = &g_esp32s3_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 = esp32s3_wdt_init(wdt);
if (lower->wdt == NULL)
{
ret = -EINVAL;
goto errout;
}
lower->started = esp32s3_wdt_is_running(lower->wdt);
ESP32S3_WDT_UNLOCK(lower->wdt);
/* If it is a Main System Watchdog Timer configure the Prescale to
* have a 500us period.
*/
if (lower->peripheral == TIMER)
{
ESP32S3_MWDT_PRE(lower->wdt, MWDT_CLK_PRESCALER_VALUE);
}
ESP32S3_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;
goto errout;
}
errout:
return ret;
}

View File

@ -0,0 +1,57 @@
/****************************************************************************
* arch/xtensa/src/esp32s3/esp32s3_wdt_lowerhalf.h
*
* 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.
*
****************************************************************************/
#ifndef __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_WDT_LOWERHALF_H
#define __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_WDT_LOWERHALF_H
/****************************************************************************
* Included Files
****************************************************************************/
#include "esp32s3_wdt.h"
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: esp32s3_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.
* wdt - WDT instance to be initialized.
*
* Returned Values:
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
****************************************************************************/
int esp32s3_wdt_initialize(const char *devpath, enum esp32s3_wdt_inst_e wdt);
#endif /* __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_WDT_LOWERHALF_H */

File diff suppressed because it is too large Load Diff

View File

@ -31,12 +31,49 @@
* Pre-processor Definitions
****************************************************************************/
/* Offset relative to each watchdog timer instance memory base */
#define RWDT_CONFIG0_OFFSET 0x0090
/* RWDT */
#define RWDT_STAGE0_TIMEOUT_OFFSET 0x0094
#define RWDT_STAGE1_TIMEOUT_OFFSET 0x0098
#define RWDT_STAGE2_TIMEOUT_OFFSET 0x009c
#define RWDT_STAGE3_TIMEOUT_OFFSET 0x00a0
#define RWDT_FEED_OFFSET 0x00a4
#define RWDT_WP_REG 0x00a8
#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
/* 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
/* Possible values for RTC_CNTL_WDT_CPU_RESET_LENGTH
* and RTC_CNTL_WDT_SYS_RESET_LENGTH
*/
#define RTC_WDT_RESET_LENGTH_100_NS 0
#define RTC_WDT_RESET_LENGTH_200_NS 1
#define RTC_WDT_RESET_LENGTH_300_NS 2
#define RTC_WDT_RESET_LENGTH_400_NS 3
#define RTC_WDT_RESET_LENGTH_500_NS 4
#define RTC_WDT_RESET_LENGTH_800_NS 5
#define RTC_WDT_RESET_LENGTH_1600_NS 6
#define RTC_WDT_RESET_LENGTH_3200_NS 7
#define RTC_CNTL_TIME0_REG RTC_CNTL_TIME_LOW0_REG
#define RTC_CNTL_TIME1_REG RTC_CNTL_TIME_HIGH0_REG
/* RTC_CNTL_RTC_OPTIONS0_REG register
* RTC common configure register
*/