xtensa/esp32s3: Add XTWDT support

This commit is contained in:
Eren Terzioglu 2023-10-24 10:38:06 +03:00 committed by Alan Carvalho de Assis
parent d63034994e
commit 4033018a72
10 changed files with 506 additions and 55 deletions

View File

@ -372,6 +372,22 @@ To test it, just run ``rand`` to get 32 randomly generated bytes::
0000 98 b9 66 a2 a2 c0 a2 ae 09 70 93 d1 b5 91 86 c8 ..f......p......
0010 8f 0e 0b 04 29 64 21 72 01 92 7c a2 27 60 6f 90 ....)d!r..|.'`o.
rtc
---
This configuration demonstrates the use of the RTC driver through alarms.
You can set an alarm, check its progress and receive a notification after it expires::
nsh> alarm 10
alarm_daemon started
alarm_daemon: Running
Opening /dev/rtc0
Alarm 0 set in 10 seconds
nsh> alarm -r
Opening /dev/rtc0
Alarm 0 is active with 10 seconds to expiration
nsh> alarm_daemon: alarm 0 received
smp
---
@ -534,3 +550,8 @@ To test it, just run the following::
nsh> wdog -i /dev/watchdogx
Where x is the watchdog instance.
To test the XTWDT(/dev/watchdog3) an interrupt handler needs to be
implemented because XTWDT does not have system reset feature. To implement
an interrupt handler `WDIOC_CAPTURE` command can be used. When interrupt
rises, XTAL32K clock can be restored with `WDIOC_RSTCLK` command.

View File

@ -1153,7 +1153,7 @@ void esp32_rtc_clk_set(void)
enum esp32_rtc_fast_freq_e fast_freq = RTC_FAST_FREQ_8M;
enum esp32_slow_clk_sel_e slow_clk = SLOW_CLK_150K;
#if defined(CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS)
#if defined(CONFIG_ESP32_RTC_CLK_SRC_EXT_XTAL)
slow_clk = SLOW_CLK_32K_XTAL;
#elif defined(CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC)
slow_clk = SLOW_CLK_32K_EXT_OSC;

View File

@ -703,6 +703,28 @@ config ESP32S3_RWDT
to have the RTC module reset, please, use the Timers' Module WDTs.
They will only reset Main System.
config ESP32S3_XTWDT
bool "XTAL32K Watchdog Timer"
depends on ESP32S3_RTC_CLK_EXT_OSC || ESP32S3_RTC_CLK_EXT_XTAL
default n
select ESP32S3_WDT
select ESP32S3_RTCIO_IRQ
---help---
Includes XTWDT. This watchdog timer monitors the status of the
external 32 kHz crystal oscillator (XTAL32K). When XTAL32K_CLK works
as the clock source of RTC_SLOW_CLK and it stops oscillating, the
XTAL32K watchdog timer first switches to BACKUP32K_CLK derived from
RC_SLOW_CLK (if ESP32S3_XTWDT_BACKUP_CLK_ENABLE) and, then, generates
an interrupt that could be captured by WDIOC_CAPTURE.
config ESP32S3_XTWDT_BACKUP_CLK_ENABLE
bool "Automatically switch to BACKUP32K_CLK when timer expires"
depends on ESP32S3_XTWDT
default y
help
Enable this to automatically switch to BACKUP32K_CLK as the source of
RTC_SLOW_CLK when the watchdog timer expires.
config ESP32S3_RT_TIMER
bool "Real-Time Timer"
default n
@ -1523,6 +1545,50 @@ config ESP32S3_FREERUN
endmenu # Timer/Counter Configuration
menu "RTC Configuration"
depends on ESP32S3_RTC
choice ESP32S3_RTC_CLK_SRC
prompt "RTC clock source"
default ESP32S3_RTC_CLK_INT_RC
---help---
Choose which clock is used as RTC clock source.
- "Internal 150kHz oscillator" option provides lowest deep sleep current
consumption, and does not require extra external components. However
frequency stability with respect to temperature is poor, so time may
drift in deep/light sleep modes.
- "External 32kHz crystal" provides better frequency stability, at the
expense of slightly higher (1uA) deep sleep current consumption.
- "External 32kHz oscillator" allows using 32kHz clock generated by an
external circuit. In this case, external clock signal must be connected
to 32K_XN pin. Amplitude should be <1.2V in case of sine wave signal,
and <1V in case of square wave signal. Common mode voltage should be
0.1 < Vcm < 0.5Vamp, where Vamp is the signal amplitude.
Additionally, 1nF capacitor must be connected between 32K_XP pin and
ground. 32K_XP pin can not be used as a GPIO in this case.
- "Internal 8.5MHz oscillator divided by 256" option results in higher
deep sleep current (by 5uA) but has better frequency stability than
the internal 150kHz oscillator. It does not require external components.
config ESP32S3_RTC_CLK_INT_RC
bool "Internal 150kHz RC oscillator"
config ESP32S3_RTC_CLK_EXT_XTAL
bool "External 32kHz crystal"
select ESP_SYSTEM_RTC_EXT_XTAL
config ESP32S3_RTC_CLK_EXT_OSC
bool "External 32kHz oscillator at 32K_XN pin"
select ESP_SYSTEM_RTC_EXT_XTAL
config ESP32S3_RTC_CLK_INT_8MD256
bool "Internal 8.5MHz oscillator, divided by 256 (~33kHz)"
endchoice
endmenu # "RTC Configuration"
menu "Real-Time Timer Configuration"
depends on ESP32S3_RT_TIMER

View File

@ -1465,11 +1465,11 @@ void esp32s3_rtc_clk_set(void)
enum esp32s3_rtc_fast_freq_e fast_freq = RTC_FAST_FREQ_8M;
enum esp32s3_slow_clk_sel_e slow_clk = SLOW_CLK_150K;
#if defined(CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS)
#if defined(CONFIG_ESP32S3_RTC_CLK_EXT_XTAL)
slow_clk = SLOW_CLK_32K_XTAL;
#elif defined(CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC)
#elif defined(CONFIG_ESP32S3_RTC_CLK_EXT_OSC)
slow_clk = SLOW_CLK_32K_EXT_OSC;
#elif defined(CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256)
#elif defined(CONFIG_ESP32S3_RTC_CLK_INT_8MD256)
slow_clk = SLOW_CLK_8MD256;
#endif

View File

@ -34,7 +34,7 @@
****************************************************************************/
/****************************************************************************
* Name: esp32_rtc_driverinit
* Name: esp32s3_rtc_driverinit
*
* Description:
* Bind the configuration timer to a timer lower half instance and register
@ -49,7 +49,7 @@
*
****************************************************************************/
int esp32_rtc_driverinit(void);
int esp32s3_rtc_driverinit(void);
#endif /* CONFIG_RTC_DRIVER */

View File

@ -50,13 +50,27 @@
/* 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)
#define IS_RWDT(dev) (((struct esp32s3_wdt_priv_s *)dev)->type == RTC)
/* Check whether the provided device is a Main Watchdog Timer */
#define IS_MWDT(dev) (((struct esp32s3_wdt_priv_s *)dev)->type == TIMER)
/* Check whether the provided device is a XTAL32K Watchdog Timer */
#define IS_XTWDT(dev) (((struct esp32s3_wdt_priv_s *)dev)->type == XTAL32K)
/****************************************************************************
* Private Types
****************************************************************************/
enum wdt_peripheral_e
{
RTC,
TIMER,
XTAL32K,
};
struct esp32s3_wdt_priv_s
{
struct esp32s3_wdt_ops_s *ops;
@ -66,8 +80,15 @@ struct esp32s3_wdt_priv_s
uint8_t irq; /* Interrupt ID */
int32_t cpuint; /* CPU interrupt assigned to this WDT */
bool inuse; /* Flag indicating if this WDT is in use */
enum wdt_peripheral_e type; /* Type of the WDT Peripheral */
};
/****************************************************************************
* External Functions
****************************************************************************/
extern void esp_rom_delay_us(uint32_t us);
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
@ -101,6 +122,7 @@ 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);
static uint16_t wdt_rtc_clk(struct esp32s3_wdt_dev_s *dev);
static void wdt_rstclk(struct esp32s3_wdt_dev_s *dev);
/****************************************************************************
* Private Data
@ -108,7 +130,7 @@ static uint16_t wdt_rtc_clk(struct esp32s3_wdt_dev_s *dev);
/* ESP32-S3 WDT ops */
struct esp32s3_wdt_ops_s esp32s3_mwdt_ops =
struct esp32s3_wdt_ops_s esp32s3_wdt_ops =
{
.start = wdt_start,
.stop = wdt_stop,
@ -118,63 +140,63 @@ struct esp32s3_wdt_ops_s esp32s3_mwdt_ops =
.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 = wdt_rtc_clk,
.setisr = wdt_setisr,
.enableint = wdt_enableint,
.disableint = wdt_disableint,
.ackint = wdt_ackint,
.rstclk = wdt_rstclk,
};
#ifdef CONFIG_ESP32S3_MWDT0
struct esp32s3_wdt_priv_s g_esp32s3_mwdt0_priv =
{
.ops = &esp32s3_mwdt_ops,
.ops = &esp32s3_wdt_ops,
.base = TIMG_T0CONFIG_REG(0),
.periph = ESP32S3_PERIPH_TG_WDT_LEVEL,
.irq = ESP32S3_IRQ_TG_WDT_LEVEL,
.cpuint = -ENOMEM,
.inuse = false,
.type = TIMER,
};
#endif
#ifdef CONFIG_ESP32S3_MWDT1
struct esp32s3_wdt_priv_s g_esp32s3_mwdt1_priv =
{
.ops = &esp32s3_mwdt_ops,
.ops = &esp32s3_wdt_ops,
.base = TIMG_T0CONFIG_REG(1),
.periph = ESP32S3_PERIPH_TG1_WDT_LEVEL,
.irq = ESP32S3_IRQ_TG1_WDT_LEVEL,
.cpuint = -ENOMEM,
.inuse = false,
.type = TIMER,
};
#endif
#ifdef CONFIG_ESP32S3_RWDT
struct esp32s3_wdt_priv_s g_esp32s3_rwdt_priv =
{
.ops = &esp32s3_rwdt_ops,
.ops = &esp32s3_wdt_ops,
.base = RTC_CNTL_RTC_OPTIONS0_REG,
.periph = ESP32S3_PERIPH_RTC_CORE,
.irq = ESP32S3_IRQ_RTC_WDT,
.cpuint = -ENOMEM,
.inuse = false,
.type = RTC,
};
#endif
#ifdef CONFIG_ESP32S3_XTWDT
struct esp32s3_wdt_priv_s g_esp32s3_xtwdt_priv =
{
.ops = &esp32s3_wdt_ops,
.base = RTC_CNTL_RTC_OPTIONS0_REG,
.periph = ESP32S3_PERIPH_RTC_CORE,
.irq = ESP32S3_IRQ_RTC_XTAL32K_DEAD,
.cpuint = -ENOMEM,
.inuse = false,
.type = XTAL32K,
};
#endif
@ -267,10 +289,18 @@ static void wdt_start(struct esp32s3_wdt_dev_s *dev)
{
wdt_modifyreg32(dev, RWDT_CONFIG0_OFFSET, 0, RTC_CNTL_WDT_EN);
}
else
else if (IS_MWDT(dev))
{
wdt_modifyreg32(dev, MWDT_CONFIG0_OFFSET, 0, TIMG_WDT_EN);
}
else
{
wdt_modifyreg32(dev, XTWDT_CONFIG0_OFFSET, 0, RTC_CNTL_XTAL32K_WDT_EN);
#ifdef CONFIG_ESP32S3_XTWDT_BACKUP_CLK_ENABLE
wdt_modifyreg32(dev, XTWDT_CONFIG0_OFFSET,
0, RTC_CNTL_XTAL32K_AUTO_BACKUP);
#endif
}
}
/****************************************************************************
@ -308,7 +338,7 @@ static int32_t wdt_config_stage(struct esp32s3_wdt_dev_s *dev,
wdt_modifyreg32(dev, RWDT_CONFIG0_OFFSET, RTC_CNTL_WDT_STG0_M,
mask);
}
else
else if (IS_MWDT(dev))
{
mask = (uint32_t)cfg << TIMG_WDT_STG0_S;
wdt_modifyreg32(dev, MWDT_CONFIG0_OFFSET, TIMG_WDT_STG0_M, mask);
@ -324,7 +354,7 @@ static int32_t wdt_config_stage(struct esp32s3_wdt_dev_s *dev,
wdt_modifyreg32(dev, RWDT_CONFIG0_OFFSET, RTC_CNTL_WDT_STG1_M,
mask);
}
else
else if (IS_MWDT(dev))
{
mask = (uint32_t)cfg << TIMG_WDT_STG1_S;
wdt_modifyreg32(dev, MWDT_CONFIG0_OFFSET, TIMG_WDT_STG1_M, mask);
@ -340,7 +370,7 @@ static int32_t wdt_config_stage(struct esp32s3_wdt_dev_s *dev,
wdt_modifyreg32(dev, RWDT_CONFIG0_OFFSET, RTC_CNTL_WDT_STG2_M,
mask);
}
else
else if (IS_MWDT(dev))
{
mask = (uint32_t)cfg << TIMG_WDT_STG2_S;
wdt_modifyreg32(dev, MWDT_CONFIG0_OFFSET, TIMG_WDT_STG2_M, mask);
@ -356,7 +386,7 @@ static int32_t wdt_config_stage(struct esp32s3_wdt_dev_s *dev,
wdt_modifyreg32(dev, RWDT_CONFIG0_OFFSET, RTC_CNTL_WDT_STG3_M,
mask);
}
else
else if (IS_MWDT(dev))
{
mask = (uint32_t)cfg << TIMG_WDT_STG3_S;
wdt_modifyreg32(dev, MWDT_CONFIG0_OFFSET, TIMG_WDT_STG3_M, mask);
@ -393,10 +423,14 @@ static void wdt_stop(struct esp32s3_wdt_dev_s *dev)
{
wdt_modifyreg32(dev, RWDT_CONFIG0_OFFSET, RTC_CNTL_WDT_EN, 0);
}
else
else if (IS_MWDT(dev))
{
wdt_modifyreg32(dev, MWDT_CONFIG0_OFFSET, TIMG_WDT_EN, 0);
}
else
{
wdt_modifyreg32(dev, XTWDT_CONFIG0_OFFSET, RTC_CNTL_XTAL32K_WDT_EN, 0);
}
}
/****************************************************************************
@ -422,7 +456,7 @@ static void wdt_enablewp(struct esp32s3_wdt_dev_s *dev)
{
wdt_putreg(dev, RWDT_WP_REG, 0);
}
else
else if (IS_MWDT(dev))
{
wdt_putreg(dev, MWDT_WP_REG, 0);
}
@ -451,7 +485,7 @@ static void wdt_disablewp(struct esp32s3_wdt_dev_s *dev)
{
wdt_putreg(dev, RWDT_WP_REG, RTC_CNTL_WDT_WKEY_VALUE);
}
else
else if (IS_MWDT(dev))
{
wdt_putreg(dev, MWDT_WP_REG, TIMG_WDT_WKEY_VALUE);
}
@ -474,12 +508,23 @@ static void wdt_disablewp(struct esp32s3_wdt_dev_s *dev)
static void wdt_pre(struct esp32s3_wdt_dev_s *dev, uint16_t pre)
{
uint32_t mask = (uint32_t)pre << TIMG_WDT_CLK_PRESCALE_S;
uint32_t mask = 0;
DEBUGASSERT(dev != NULL);
wdt_modifyreg32(dev, MWDT_CLK_PRESCALE_OFFSET, TIMG_WDT_CLK_PRESCALE_M,
mask);
if (IS_MWDT(dev))
{
mask = (uint32_t)pre << TIMG_WDT_CLK_PRESCALE_S;
wdt_modifyreg32(dev, MWDT_CLK_PRESCALE_OFFSET, TIMG_WDT_CLK_PRESCALE_M,
mask);
}
#ifdef CONFIG_ESP32S3_XTWDT_BACKUP_CLK_ENABLE
else if (IS_XTWDT(dev))
{
mask = (uint32_t)pre;
wdt_modifyreg32(dev, XTWDT_CLK_PRESCALE_OFFSET,
RTC_CNTL_XTAL32K_CLK_FACTOR_M, mask);
}
#endif
}
/****************************************************************************
@ -504,6 +549,14 @@ static int32_t wdt_settimeout(struct esp32s3_wdt_dev_s *dev, uint32_t value,
{
DEBUGASSERT(dev != NULL);
if (IS_XTWDT(dev))
{
value = value << RTC_CNTL_XTAL32K_WDT_TIMEOUT_S;
wdt_modifyreg32(dev, XTWDT_TIMEOUT_OFFSET,
RTC_CNTL_XTAL32K_WDT_TIMEOUT_M, value);
return OK;
}
switch (stage)
{
case ESP32S3_WDT_STAGE0:
@ -596,7 +649,7 @@ static void wdt_feed(struct esp32s3_wdt_dev_s *dev)
{
wdt_modifyreg32(dev, RWDT_FEED_OFFSET, 0, RTC_CNTL_RTC_WDT_FEED);
}
else
else if (IS_MWDT(dev))
{
wdt_putreg(dev, MWDT_FEED_OFFSET, TIMG_WDT_FEED);
}
@ -704,8 +757,9 @@ static int32_t wdt_setisr(struct esp32s3_wdt_dev_s *dev, xcpt_t handler,
if (wdt->cpuint >= 0)
{
#ifdef CONFIG_ESP32S3_RWDT
if (wdt->irq == ESP32S3_IRQ_RTC_WDT)
#if defined(CONFIG_ESP32S3_RWDT) || defined(CONFIG_ESP32S3_XTWDT)
if (wdt->irq == ESP32S3_IRQ_RTC_WDT ||
wdt->irq == ESP32S3_IRQ_RTC_XTAL32K_DEAD)
{
esp32s3_rtcioirqdisable(wdt->irq);
irq_detach(wdt->irq);
@ -732,8 +786,9 @@ static int32_t wdt_setisr(struct esp32s3_wdt_dev_s *dev, xcpt_t handler,
{
/* Set up to receive peripheral interrupts on the current CPU */
#ifdef CONFIG_ESP32S3_RWDT
if (wdt->irq == ESP32S3_IRQ_RTC_WDT)
#if defined(CONFIG_ESP32S3_RWDT) || defined(CONFIG_ESP32S3_XTWDT)
if (wdt->irq == ESP32S3_IRQ_RTC_WDT ||
wdt->irq == ESP32S3_IRQ_RTC_XTAL32K_DEAD)
{
ret = irq_attach(wdt->irq, handler, arg);
@ -799,10 +854,15 @@ static void wdt_enableint(struct esp32s3_wdt_dev_s *dev)
wdt_modifyreg32(dev, RWDT_INT_ENA_REG_OFFSET, 0,
RTC_CNTL_RTC_WDT_INT_ENA);
}
else
else if (IS_MWDT(dev))
{
wdt_modifyreg32(dev, MWDT_INT_ENA_REG_OFFSET, 0, TIMG_WDT_INT_ENA);
}
else
{
wdt_modifyreg32(dev, XTWDT_INT_ENA_REG_OFFSET, 0,
RTC_CNTL_RTC_XTAL32K_DEAD_INT_ENA);
}
}
/****************************************************************************
@ -825,10 +885,15 @@ static void wdt_disableint(struct esp32s3_wdt_dev_s *dev)
wdt_modifyreg32(dev, RWDT_INT_ENA_REG_OFFSET, RTC_CNTL_RTC_WDT_INT_ENA,
0);
}
else
else if (IS_MWDT(dev))
{
wdt_modifyreg32(dev, MWDT_INT_ENA_REG_OFFSET, TIMG_WDT_INT_ENA, 0);
}
else
{
wdt_modifyreg32(dev, XTWDT_INT_ENA_REG_OFFSET,
RTC_CNTL_RTC_XTAL32K_DEAD_INT_ENA, 0);
}
}
/****************************************************************************
@ -850,10 +915,49 @@ static void wdt_ackint(struct esp32s3_wdt_dev_s *dev)
{
wdt_putreg(dev, RWDT_INT_CLR_REG_OFFSET, RTC_CNTL_RTC_WDT_INT_CLR);
}
else
else if (IS_MWDT(dev))
{
wdt_putreg(dev, MWDT_INT_CLR_REG_OFFSET, TIMG_WDT_INT_CLR);
}
else
{
wdt_putreg(dev, MWDT_INT_CLR_REG_OFFSET,
RTC_CNTL_RTC_XTAL32K_DEAD_INT_CLR);
}
}
/****************************************************************************
* Name: wdt_rstclk
*
* Description:
* Restores the xtal32k clock.
*
* Parameters:
* dev - Pointer to the driver state structure.
*
****************************************************************************/
static void wdt_rstclk(struct esp32s3_wdt_dev_s *dev)
{
DEBUGASSERT(dev != NULL);
struct esp32s3_wdt_priv_s *wdt = (struct esp32s3_wdt_priv_s *)dev;
if (IS_XTWDT(dev))
{
wdt->ops->stop(dev);
wdt_modifyreg32(dev, XTWDT_CONFIG0_OFFSET, RTC_CNTL_XPD_XTAL_32K, 0);
wdt_modifyreg32(dev, XTWDT_CONFIG0_OFFSET, 0, RTC_CNTL_XPD_XTAL_32K);
/* Needs some time after switching to 32khz XTAL
* before turning on WDT again
*/
esp_rom_delay_us(300);
wdt->ops->start(dev);
}
}
/****************************************************************************
@ -906,6 +1010,15 @@ struct esp32s3_wdt_dev_s *esp32s3_wdt_init(enum esp32s3_wdt_inst_e wdt_id)
break;
}
#endif
#ifdef CONFIG_ESP32S3_XTWDT
case ESP32S3_WDT_XTWDT:
{
wdt = &g_esp32s3_xtwdt_priv;
break;
}
#endif
default:
@ -1000,7 +1113,7 @@ bool esp32s3_wdt_is_running(struct esp32s3_wdt_dev_s *dev)
return true;
}
}
else
else if (IS_MWDT(dev))
{
status = wdt_getreg(dev, MWDT_CONFIG0_OFFSET);
if ((status & TIMG_WDT_EN) == TIMG_WDT_EN)
@ -1008,6 +1121,14 @@ bool esp32s3_wdt_is_running(struct esp32s3_wdt_dev_s *dev)
return true;
}
}
else
{
status = wdt_getreg(dev, XTWDT_CONFIG0_OFFSET);
if ((status & RTC_CNTL_XTAL32K_WDT_EN) == RTC_CNTL_XTAL32K_WDT_EN)
{
return true;
}
}
return false;
}

View File

@ -35,6 +35,23 @@
* Pre-processor Definitions
****************************************************************************/
/* IOCTL Commands ***********************************************************/
/* The watchdog driver uses a standard character driver framework. However,
* since the watchdog driver is a device control interface and not a data
* transfer interface, the majority of the functionality is implemented in
* driver ioctl calls.
*
* See nuttx/timers/watchdog.h for the IOCTLs handled by the upper half.
*
* These are detected and handled by the "lower half" watchdog timer driver.
*
* WDIOC_RSTCLK - Restores the xtal32k clock
* Argument: Ignored
*/
#define WDIOC_RSTCLK _WDIOC(0x032)
/* Helpers ******************************************************************/
#define ESP32S3_WDT_START(d) ((d)->ops->start(d))
@ -42,14 +59,17 @@
#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_XTWDT_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_XTWDT_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))
#define ESP32S3_XTWDT_RST_CLK(d) ((d)->ops->rstclk(d))
/****************************************************************************
* Public Types
@ -61,7 +81,8 @@ 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) */
ESP32S3_WDT_RWDT, /* RTC Watchdog Timer (RWDT) */
ESP32S3_WDT_XTWDT /* XTAL32K Watchdog Timer (XTWDT) */
};
/* Stages of a Watchdog Timer. A WDT has 4 stages. */
@ -135,6 +156,7 @@ struct esp32s3_wdt_ops_s
void (*enableint)(struct esp32s3_wdt_dev_s *dev);
void (*disableint)(struct esp32s3_wdt_dev_s *dev);
void (*ackint)(struct esp32s3_wdt_dev_s *dev);
void (*rstclk)(struct esp32s3_wdt_dev_s *dev);
};
/****************************************************************************

View File

@ -38,6 +38,7 @@
#include "xtensa.h"
#include "esp32s3_wdt.h"
#include "esp32s3_rtc.h"
#include "esp32s3_wdt_lowerhalf.h"
#include "hardware/esp32s3_soc.h"
@ -69,6 +70,24 @@
#define RWDT_FULL_STAGE (UINT32_MAX)
/* XTWDT clock period in nanoseconds */
#define XTWDT_CLK_PERIOD_NS (30)
/* Maximum number of cycles supported for a XTWDT stage timeout */
#define XTWDT_FULL_STAGE (UINT8_MAX)
/* Number of cycles for RTC_SLOW_CLK calibration */
#define XT_WDT_CLK_CAL_CYCLES (500)
/* Maximum number of divisor components
* according to the frequency of RC_SLOW_CLK
*/
#define XT_WDT_DIV_COMP_N_MAX 8
/****************************************************************************
* Private Types
****************************************************************************/
@ -77,6 +96,7 @@ enum wdt_peripheral_e
{
RTC,
TIMER,
XTAL32K,
};
/* This structure provides the private representation of the "lower-half"
@ -116,6 +136,12 @@ 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);
static int wdt_lh_ioctl(struct watchdog_lowerhalf_s *lower, int cmd,
unsigned long arg);
#ifdef CONFIG_ESP32S3_XTWDT_BACKUP_CLK_ENABLE
static uint32_t wdt_lh_xt_calculate(uint32_t rtc_clk_frequency_khz);
static uint32_t wdt_lh_clk_freq_cal(uint32_t rtc_clk_period_us);
#endif
/****************************************************************************
* Private Data
@ -131,7 +157,7 @@ static const struct watchdog_ops_s g_esp32s3_wdg_ops =
.getstatus = wdt_lh_getstatus,
.settimeout = wdt_lh_settimeout,
.capture = wdt_lh_capture,
.ioctl = NULL,
.ioctl = wdt_lh_ioctl,
};
#ifdef CONFIG_ESP32S3_MWDT0
@ -161,6 +187,15 @@ static struct esp32s3_wdt_lowerhalf_s g_esp32s3_rwdt_lowerhalf =
};
#endif
#ifdef CONFIG_ESP32S3_XTWDT
/* XTWDT lower-half */
static struct esp32s3_wdt_lowerhalf_s g_esp32s3_xtwdt_lowerhalf =
{
.ops = &g_esp32s3_wdg_ops,
};
#endif
/****************************************************************************
* Name: wdt_lh_start
*
@ -217,7 +252,7 @@ static int wdt_lh_start(struct watchdog_lowerhalf_s *lower)
ESP32S3_WDT_STG_CONF(priv->wdt, ESP32S3_WDT_STAGE0,
ESP32S3_WDT_STAGE_ACTION_RESET_SYSTEM);
}
else
else if (priv->peripheral == RTC)
{
ESP32S3_WDT_STG_CONF(priv->wdt, ESP32S3_WDT_STAGE0,
ESP32S3_WDT_STAGE_ACTION_RESET_RTC);
@ -434,6 +469,8 @@ static int wdt_lh_settimeout(struct watchdog_lowerhalf_s *lower,
(struct esp32s3_wdt_lowerhalf_s *)lower;
uint16_t rtc_cycles = 0;
uint32_t rtc_ms_max = 0;
uint16_t xtal32k_cycles = 0;
uint32_t xtal32k_ms_max = 0;
wdinfo("Entry: timeout=%" PRIu32 "\n", timeout);
DEBUGASSERT(priv);
@ -467,7 +504,7 @@ static int wdt_lh_settimeout(struct watchdog_lowerhalf_s *lower,
/* Watchdog from RTC Module */
else
else if (priv->peripheral == RTC)
{
rtc_cycles = ESP32S3_RWDT_CLK(priv->wdt);
rtc_ms_max = (RWDT_FULL_STAGE / (uint32_t)rtc_cycles);
@ -487,6 +524,29 @@ static int wdt_lh_settimeout(struct watchdog_lowerhalf_s *lower,
}
}
/* Watchdog from XTAL32K Module */
else
{
xtal32k_cycles = XTWDT_CLK_PERIOD_NS;
xtal32k_ms_max = (XTWDT_FULL_STAGE * NSEC_PER_USEC
/ (uint32_t)xtal32k_cycles);
/* Is this timeout a valid value for RTC WDT? */
if (timeout == 0 || timeout > xtal32k_ms_max)
{
wderr("Cannot represent timeout=%" PRIu32 " > %" PRIu32 "\n",
timeout, rtc_ms_max);
return -ERANGE;
}
else
{
timeout = timeout * xtal32k_cycles;
ESP32S3_WDT_STO(priv->wdt, timeout, ESP32S3_WDT_STAGE0);
}
}
/* Reset the wdt */
ESP32S3_WDT_FEED(priv->wdt);
@ -589,7 +649,7 @@ static xcpt_t wdt_lh_capture(struct watchdog_lowerhalf_s *lower,
ESP32S3_WDT_STG_CONF(priv->wdt, ESP32S3_WDT_STAGE0,
ESP32S3_WDT_STAGE_ACTION_RESET_SYSTEM);
}
else
else if (priv->peripheral == RTC)
{
ESP32S3_WDT_STG_CONF(priv->wdt, ESP32S3_WDT_STAGE0,
ESP32S3_WDT_STAGE_ACTION_RESET_RTC);
@ -601,6 +661,47 @@ static xcpt_t wdt_lh_capture(struct watchdog_lowerhalf_s *lower,
return oldhandler;
}
/****************************************************************************
* Name: wdt_lh_ioctl
*
* Description:
* Any ioctl commands that are not recognized by the "upper-half" driver
* are forwarded to the lower half driver through this method.
*
* Input Parameters:
* lower - A pointer the publicly visible representation of the
* "lower-half" driver state structure.
* cmd - Command number to process.
* arg - Argument that sent to the command.
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
static int wdt_lh_ioctl(struct watchdog_lowerhalf_s *lower, int cmd,
unsigned long arg)
{
struct esp32s3_wdt_lowerhalf_s *priv =
(struct esp32s3_wdt_lowerhalf_s *)lower;
wdinfo("ioctl Call: cmd=0x%x arg=0x%lx", cmd, arg);
/* Process the IOCTL command */
switch (cmd)
{
case WDIOC_RSTCLK:
ESP32S3_XTWDT_RST_CLK(priv->wdt);
break;
default:
return -ENOTTY;
}
return OK;
}
/* Interrupt handling *******************************************************/
static int wdt_handler(int irq, void *context, void *arg)
@ -622,6 +723,81 @@ static int wdt_handler(int irq, void *context, void *arg)
return OK;
}
/****************************************************************************
* Name: wdt_lh_xt_calculate
*
* Description:
* Calculate the actual frequency of RC_SLOW_CLK to calibrate the backup
* RTC_SLOW_CLK. This is necessary to compensate for clock deviations.
*
* Input Parameters:
* rtc_clk_frequency_khz - Frequency of RTC SLOW CLK in khz
*
* Returned Values:
* The divisor of BACKUP32K_CLK used to calibrate the RTC_SLOW_CLK
* according to the actual frequency of the RC_SLOW_CLK.
*
****************************************************************************/
#ifdef CONFIG_ESP32S3_XTWDT_BACKUP_CLK_ENABLE
static uint32_t wdt_lh_xt_calculate(uint32_t rtc_clk_frequency_khz)
{
uint32_t xtal32k_clk_factor = 0;
uint8_t divisor_comps[XT_WDT_DIV_COMP_N_MAX];
uint8_t M = ((rtc_clk_frequency_khz / 32) / 2);
uint32_t S = ((4 * rtc_clk_frequency_khz) / 32);
memset(divisor_comps, M, XT_WDT_DIV_COMP_N_MAX);
/* Calculate how far we are away from satisfying S = SUM(x_n) */
uint8_t off = S - XT_WDT_DIV_COMP_N_MAX * M;
/* Offset should never be this big */
ASSERT(off <= XT_WDT_DIV_COMP_N_MAX);
for (int i = 0; i < XT_WDT_DIV_COMP_N_MAX; i++)
{
if (off)
{
divisor_comps[i]++;
off--;
}
/* Sum up all divisors */
xtal32k_clk_factor |= (divisor_comps[i] << 4 * i);
}
return xtal32k_clk_factor;
}
/****************************************************************************
* Name: wdt_lh_clk_freq_cal
*
* Description:
* Calculate the clock frequency from period in microseconds.
*
* Input Parameters:
* rtc_clk_period_us - Average slow clock period in microseconds.
*
* Returned Values:
* Frequency of the clock in Hz.
*
****************************************************************************/
static uint32_t wdt_lh_clk_freq_cal(uint32_t rtc_clk_period_us)
{
if (rtc_clk_period_us == 0)
{
return 0;
}
return 1000000ULL * (1 << RTC_CLK_CAL_FRACT) / rtc_clk_period_us;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
@ -680,6 +856,15 @@ int esp32s3_wdt_initialize(const char *devpath, enum esp32s3_wdt_inst_e wdt)
}
#endif
#ifdef CONFIG_ESP32S3_XTWDT
case ESP32S3_WDT_XTWDT:
{
lower = &g_esp32s3_xtwdt_lowerhalf;
lower->peripheral = XTAL32K;
break;
}
#endif
default:
{
ret = -ENODEV;
@ -712,6 +897,26 @@ int esp32s3_wdt_initialize(const char *devpath, enum esp32s3_wdt_inst_e wdt)
ESP32S3_MWDT_PRE(lower->wdt, MWDT_CLK_PRESCALER_VALUE);
}
/* Configure auto backup clock when XTAL32K fails */
#ifdef CONFIG_ESP32S3_XTWDT_BACKUP_CLK_ENABLE
if (lower->peripheral == XTAL32K)
{
/* Estimate frequency of internal RTC oscillator */
uint32_t rtc_clk_period_us =
esp32s3_rtc_clk_cal(RTC_CAL_INTERNAL_OSC, XT_WDT_CLK_CAL_CYCLES);
uint32_t rtc_clk_frequency_khz = wdt_lh_clk_freq_cal(rtc_clk_period_us)
/ 1000;
uint32_t xtal32k_clk_factor =
wdt_lh_xt_calculate(rtc_clk_frequency_khz);
ESP32S3_XTWDT_PRE(lower->wdt, xtal32k_clk_factor);
}
#endif
ESP32S3_WDT_LOCK(lower->wdt);
/* Register the watchdog driver as /dev/watchdogX. If the registration goes

View File

@ -34,6 +34,7 @@
/* Offset relative to each watchdog timer instance memory base */
#define RWDT_CONFIG0_OFFSET 0x0098
#define XTWDT_CONFIG0_OFFSET 0x0060
/* RWDT */
@ -46,6 +47,12 @@
#define RWDT_INT_ENA_REG_OFFSET 0x0040
#define RWDT_INT_CLR_REG_OFFSET 0x004c
/* XTWDT */
#define XTWDT_TIMEOUT_OFFSET 0x00f8
#define XTWDT_CLK_PRESCALE_OFFSET 0x00f4
#define XTWDT_INT_ENA_REG_OFFSET 0x0040
/* The value that needs to be written to RTC_CNTL_WDT_WKEY to
* write-enable the wdt registers
*/

View File

@ -82,6 +82,15 @@ int board_wdt_init(void)
}
#endif /* CONFIG_ESP32S3_RWDT */
#ifdef CONFIG_ESP32S3_XTWDT
ret = esp32s3_wdt_initialize("/dev/watchdog3", ESP32S3_WDT_XTWDT);
if (ret < 0)
{
syslog(LOG_ERR, "Failed to initialize XTWDT: %d\n", ret);
return ret;
}
#endif /* CONFIG_ESP32S3_XTWDT */
return ret;
}