xtensa/esp32s2: Add xtwdt and rwdt support

This commit is contained in:
Eren Terzioglu 2023-11-27 14:12:45 +03:00 committed by Xiang Xiao
parent 26b7de0f34
commit c15392d9b7
13 changed files with 741 additions and 126 deletions

View File

@ -330,3 +330,21 @@ the ``Device Drivers -> CAN Driver Support -> CAN loopback mode`` option and run
TSEG2: 4
SJW: 3
ID: 1 DLC: 1
watchdog
--------
This config test the watchdog timers. It includes the 2 MWDTs,
adds driver support, registers the WDTs as devices and includes the watchdog
example.
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

@ -394,3 +394,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

@ -420,6 +420,28 @@ config ESP32S2_RWDT
to have the RTC module reset, please, use the Timers' Module WDTs.
They will only reset Main System.
config ESP32S2_XTWDT
bool "XTAL32K Watchdog Timer"
depends on ESP32S2_RTC_CLK_EXT_OSC || ESP32S2_RTC_CLK_EXT_XTAL
default n
select ESP32S2_WDT
select ESP32S2_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 ESP32S2_XTWDT_BACKUP_CLK_ENABLE) and, then, generates
an interrupt that could be captured by WDIOC_CAPTURE.
config ESP32S2_XTWDT_BACKUP_CLK_ENABLE
bool "Automatically switch to BACKUP32K_CLK when timer expires"
depends on ESP32S2_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 ESP32S2_RTC
bool "Real Time Clock (RTC)"
default y

View File

@ -131,23 +131,32 @@
#define RTC_DISABLE_ROM_LOG ((1 << 0) | (1 << 16))
#define RTC_SLEEP_PD_DIG BIT(0) /* Deep sleep (power down digital domain) */
#define RTC_SLEEP_PD_DIG BIT(0) /* Deep sleep (power down
* digital domain) */
#define RTC_SLEEP_PD_RTC_PERIPH BIT(1) /* Power down RTC peripherals */
#define RTC_SLEEP_PD_RTC_SLOW_MEM BIT(2) /* Power down RTC SLOW memory */
#define RTC_SLEEP_PD_RTC_FAST_MEM BIT(3) /* Power down RTC FAST memory */
#define RTC_SLEEP_PD_RTC_MEM_FOLLOW_CPU BIT(4) /* RTC FAST and SLOW memories are automatically powered up and down along with the CPU */
#define RTC_SLEEP_PD_VDDSDIO BIT(5) /* Power down VDDSDIO regulator */
#define RTC_SLEEP_PD_RTC_MEM_FOLLOW_CPU BIT(4) /* RTC FAST and SLOW memories
* are automatically powered
* up and down along with the
* CPU */
#define RTC_SLEEP_PD_VDDSDIO BIT(5) /* Power down VDDSDIO
* regulator */
#define RTC_SLEEP_PD_WIFI BIT(6) /* Power down WIFI */
#define RTC_SLEEP_PD_INT_8M BIT(7) /* Power down Internal 8M oscillator */
#define RTC_SLEEP_PD_INT_8M BIT(7) /* Power down Internal 8M
* oscillator */
#define RTC_SLEEP_PD_XTAL BIT(8) /* Power down main XTAL */
/* These flags are not power domains, but will affect some sleep parameters */
#define RTC_SLEEP_DIG_USE_8M BIT(16)
#define RTC_SLEEP_USE_ADC_TESEN_MONITOR BIT(17)
#define RTC_SLEEP_NO_ULTRA_LOW BIT(18) /* Avoid using ultra low power in deep sleep,
* in which RTCIO cannot be used as input,
* and RTCMEM can't work under high temperature */
#define RTC_SLEEP_NO_ULTRA_LOW BIT(18) /* Avoid using ultra low
* power in deep sleep, in
* which RTCIO cannot be used
* as input, and RTCMEM can't
* work under high
* temperature */
#define is_dslp(pd_flags) ((pd_flags) & RTC_SLEEP_PD_DIG)
@ -173,14 +182,14 @@
* Valid if RTC_CNTL_DBG_ATTEN is 0.
*/
#define RTC_CNTL_DBIAS_0V90 0 /* sleep dig_dbias & rtc_dbias */
#define RTC_CNTL_DBIAS_0V95 1 /* digital voltage */
#define RTC_CNTL_DBIAS_0V90 0 /* Sleep dig_dbias & rtc_dbias */
#define RTC_CNTL_DBIAS_0V95 1 /* Digital voltage */
#define RTC_CNTL_DBIAS_1V00 2
#define RTC_CNTL_DBIAS_1V05 3
#define RTC_CNTL_DBIAS_1V10 4
#define RTC_CNTL_DBIAS_1V15 5
#define RTC_CNTL_DBIAS_1V20 6
#define RTC_CNTL_DBIAS_1V25 7 /* voltage is about 1.34v in fact */
#define RTC_CNTL_DBIAS_1V25 7 /* Voltage is about 1.34v in fact */
/* Default initializer for esp32s2_rtc_sleep_config_t
* This initializer sets all fields to "reasonable" values
@ -265,10 +274,12 @@
/* RTC Memory & Store Register usage */
#define RTC_SLOW_CLK_CAL_REG RTC_CNTL_STORE1_REG /* RTC_SLOW_CLK calibration value */
#define RTC_SLOW_CLK_CAL_REG RTC_CNTL_STORE1_REG /* RTC_SLOW_CLK
* calibration value */
#define RTC_BOOT_TIME_LOW_REG RTC_CNTL_STORE2_REG /* Boot time, low word */
#define RTC_BOOT_TIME_HIGH_REG RTC_CNTL_STORE3_REG /* Boot time, high word */
#define RTC_XTAL_FREQ_REG RTC_CNTL_STORE4_REG /* External XTAL frequency */
#define RTC_XTAL_FREQ_REG RTC_CNTL_STORE4_REG /* External XTAL
* frequency */
#define RTC_APB_FREQ_REG RTC_CNTL_STORE5_REG /* APB bus frequency */
#define RTC_ENTRY_ADDR_REG RTC_CNTL_STORE6_REG /* FAST_RTC_MEMORY_ENTRY */
#define RTC_RESET_CAUSE_REG RTC_CNTL_STORE6_REG
@ -282,45 +293,63 @@
struct esp32s2_rtc_priv_s
{
uint32_t ck8m_wait : 8; /* Number of rtc_fast_clk cycles to wait for 8M clock to be ready */
uint32_t xtal_wait : 8; /* Number of rtc_fast_clk cycles to wait for XTAL clock to be ready */
uint32_t pll_wait : 8; /* Number of rtc_fast_clk cycles to wait for PLL to be ready */
uint32_t clkctl_init : 1; /* Perform clock control related initialization */
uint32_t pwrctl_init : 1; /* Perform power control related initialization */
uint32_t ck8m_wait : 8; /* Number of rtc_fast_clk cycles to wait
* for 8M clock to be ready */
uint32_t xtal_wait : 8; /* Number of rtc_fast_clk cycles to wait
* for XTAL clock to be ready */
uint32_t pll_wait : 8; /* Number of rtc_fast_clk cycles to wait
* for PLL to be ready */
uint32_t clkctl_init : 1; /* Perform clock control related
* initialization */
uint32_t pwrctl_init : 1; /* Perform power control related
* initialization */
uint32_t rtc_dboost_fpd : 1; /* Force power down RTC_DBOOST */
uint32_t xtal_fpu : 1;
uint32_t bbpll_fpu : 1;
uint32_t cpu_waiti_clk_gate : 1;
uint32_t cali_ocode : 1; /* Calibrate Ocode to make bangap voltage more precise. */
uint32_t cali_ocode : 1; /* Calibrate Ocode to make bangap voltage
* more precise. */
};
/* sleep configuration for rtc_sleep_init function */
struct esp32s2_rtc_sleep_config_s
{
uint32_t lslp_mem_inf_fpu : 1; /* force normal voltage in sleep mode (digital domain memory) */
uint32_t rtc_mem_inf_follow_cpu : 1; /* keep low voltage in sleep mode (even if ULP/touch is used) */
uint32_t rtc_fastmem_pd_en : 1; /* power down RTC fast memory */
uint32_t rtc_slowmem_pd_en : 1; /* power down RTC slow memory */
uint32_t rtc_peri_pd_en : 1; /* power down RTC peripherals */
uint32_t wifi_pd_en : 1; /* power down WiFi */
uint32_t lslp_mem_inf_fpu : 1; /* Force normal voltage in sleep mode
* (digital domain memory) */
uint32_t rtc_mem_inf_follow_cpu : 1; /* Keep low voltage in sleep mode
* (even if ULP/touch is used) */
uint32_t rtc_fastmem_pd_en : 1; /* Power down RTC fast memory */
uint32_t rtc_slowmem_pd_en : 1; /* Power down RTC slow memory */
uint32_t rtc_peri_pd_en : 1; /* Power down RTC peripherals */
uint32_t wifi_pd_en : 1; /* Power down WiFi */
uint32_t int_8m_pd_en : 1; /* Power down Internal 8M oscillator */
uint32_t deep_slp : 1; /* power down digital domain */
uint32_t wdt_flashboot_mod_en : 1; /* enable WDT flashboot mode */
uint32_t dig_dbias_wak : 3; /* set bias for digital domain, in active mode */
uint32_t dig_dbias_slp : 3; /* set bias for digital domain, in sleep mode */
uint32_t rtc_dbias_wak : 3; /* set bias for RTC domain, in active mode */
uint32_t rtc_dbias_slp : 3; /* set bias for RTC domain, in sleep mode */
uint32_t bias_sleep_monitor : 1; /* circuit control parameter, in monitor mode */
uint32_t dbg_atten_slp : 4; /* voltage parameter, in sleep mode */
uint32_t bias_sleep_slp : 1; /* circuit control parameter, in sleep mode */
uint32_t pd_cur_monitor : 1; /* circuit control parameter, in monitor mode */
uint32_t pd_cur_slp : 1; /* circuit control parameter, in sleep mode */
uint32_t vddsdio_pd_en : 1; /* power down VDDSDIO regulator */
uint32_t xtal_fpu : 1; /* keep main XTAL powered up in sleep */
uint32_t rtc_regulator_fpu : 1; /* keep rtc regulator powered up in sleep */
uint32_t deep_slp_reject : 1; /* enable deep sleep reject */
uint32_t light_slp_reject : 1; /* enable light sleep reject */
uint32_t deep_slp : 1; /* Power down digital domain */
uint32_t wdt_flashboot_mod_en : 1; /* Enable WDT flashboot mode */
uint32_t dig_dbias_wak : 3; /* Set bias for digital domain,
* in active mode */
uint32_t dig_dbias_slp : 3; /* Set bias for digital domain,
* in sleep mode */
uint32_t rtc_dbias_wak : 3; /* Set bias for RTC domain,
* in active mode */
uint32_t rtc_dbias_slp : 3; /* Set bias for RTC domain,
* in sleep mode */
uint32_t bias_sleep_monitor : 1; /* Circuit control parameter,
* in monitor mode */
uint32_t dbg_atten_slp : 4; /* Voltage parameter, in sleep mode */
uint32_t bias_sleep_slp : 1; /* Circuit control parameter,
* in sleep mode */
uint32_t pd_cur_monitor : 1; /* Circuit control parameter,
* in monitor mode */
uint32_t pd_cur_slp : 1; /* Circuit control parameter,
* in sleep mode */
uint32_t vddsdio_pd_en : 1; /* Power down VDDSDIO regulator */
uint32_t xtal_fpu : 1; /* Keep main XTAL powered up in
* sleep */
uint32_t rtc_regulator_fpu : 1; /* Keep rtc regulator powered up
* in sleep */
uint32_t deep_slp_reject : 1; /* Enable deep sleep reject */
uint32_t light_slp_reject : 1; /* Enable light sleep reject */
};
/* Power down flags for rtc_sleep_pd function */
@ -329,7 +358,8 @@ struct esp32s2_rtc_sleep_pd_config_s
{
uint32_t dig_fpu : 1; /* Set to 1 to power down digital part in sleep */
uint32_t rtc_fpu : 1; /* Set to 1 to power down RTC memories in sleep */
uint32_t cpu_fpu : 1; /* Set to 1 to power down digital memories and CPU in sleep */
uint32_t cpu_fpu : 1; /* Set to 1 to power down digital memories
* and CPU in sleep */
uint32_t i2s_fpu : 1; /* Set to 1 to power down I2S in sleep */
uint32_t bb_fpu : 1; /* Set to 1 to power down Wi-Fi in sleep */
uint32_t nrx_fpu : 1; /* Set to 1 to power down Wi-Fi in sleep */
@ -341,7 +371,8 @@ struct alm_cbinfo_s
{
struct rt_timer_s *alarm_hdl; /* Timer id point to here */
volatile alm_callback_t ac_cb; /* Client callback function */
volatile void *ac_arg; /* Argument to pass with the callback function */
volatile void *ac_arg; /* Argument to pass with the
* callback function */
uint64_t deadline_us;
uint8_t index;
};
@ -600,7 +631,7 @@ static uint32_t IRAM_ATTR esp32s2_rtc_clk_cal_internal(
if (cal_clk == RTC_CAL_32K_XTAL || slow_freq == RTC_SLOW_FREQ_32K_XTAL)
{
expected_freq = 32768; /* standard 32KHz XTAL */
expected_freq = 32768; /* Standard 32KHz XTAL */
}
else if (cal_clk == RTC_CAL_8MD256 || slow_freq == RTC_SLOW_FREQ_8MD256)
{
@ -650,6 +681,20 @@ static uint32_t IRAM_ATTR esp32s2_rtc_clk_cal_internal(
return cal_val;
}
/****************************************************************************
* Name: esp32s2_wait_dig_dbias_valid
*
* Description:
* Wait digtial dbias valid
*
* Input Parameters:
* rtc_cycles - RTC count
*
* Returned Value:
* None
*
****************************************************************************/
static void esp32s2_wait_dig_dbias_valid(uint64_t rtc_cycles)
{
int slow_clk_freq = esp32s2_rtc_clk_slow_freq_get();
@ -792,7 +837,7 @@ static void IRAM_ATTR esp32s2_rtc_clk_8m_enable(bool clk_8m_en, bool d256_en)
{
modifyreg32(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M, 0);
/* no need to wait once enabled by software */
/* No need to wait once enabled by software */
REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, 1);
if (d256_en)
@ -1070,14 +1115,15 @@ static void esp32s2_rtc_calibrate_ocode(void)
uint64_t max_delay_time_us = 10000;
struct esp32s2_cpu_freq_config_s freq_config;
/* Bandgap output voltage is not precise when calibrate o-code by hardware
* sometimes, so need software o-code calibration (must turn off PLL).
/* Band gap output voltage is sometimes not precise when calibrating
* the o-code by hardware, so we need a software o-code calibration
* (must turn off PLL).
* Method:
* 1. read current cpu config, save in old_config
* 2. switch cpu to xtal because PLL will be closed when o-code calibration
* 3. begin o-code calibration
* 4. wait o-code calibration done flag or timeout
* 5. set cpu to old-config
* 1. Read current cpu config, save in old_config
* 2. Switch cpu to xtal because PLL will be closed when o-code calibration
* 3. Begin o-code calibration
* 4. Wait o-code calibration done flag or timeout
* 5. Set cpu to old-config
*/
enum esp32s2_rtc_slow_freq_e slow_clk_freq =
@ -1129,6 +1175,20 @@ static void esp32s2_rtc_calibrate_ocode(void)
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: esp32s2_rtc_clk_slow_freq_get_hz
*
* Description:
* Get the approximate frequency of RTC_SLOW_CLK, in Hz
*
* Input Parameters:
* None
*
* Returned Value:
* RTC_SLOW_CLK frequency, in Hz
*
****************************************************************************/
static int IRAM_ATTR esp32s2_rtc_clk_slow_freq_get(void)
{
return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL);
@ -1224,7 +1284,7 @@ enum esp32s2_rtc_slow_freq_e IRAM_ATTR esp32s2_rtc_get_slow_clk(void)
*
* Input Parameters:
* cal_clk - clock to be measured
* slowclk_cycles - number of slow clock cycles to average
* slowclk_cycles - number of slow clock cycles what is to be averaged
*
* Returned Value:
* Average slow clock period in microseconds, Q13.19 fixed point format
@ -1436,12 +1496,12 @@ void IRAM_ATTR esp32s2_rtc_init(void)
/* Moved from rtc sleep to rtc init to save sleep function running time */
/* set shortest possible sleep time limit */
/* Set shortest possible sleep time limit */
REG_SET_FIELD(RTC_CNTL_TIMER5_REG, RTC_CNTL_MIN_SLP_VAL,
RTC_CNTL_MIN_SLP_VAL_MIN);
/* set wifi timer */
/* Set wifi timer */
REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_WIFI_POWERUP_TIMER, 1);
REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_WIFI_WAIT_TIMER, 1);
@ -1455,27 +1515,27 @@ void IRAM_ATTR esp32s2_rtc_init(void)
if (cfg.clkctl_init)
{
/* clear CMMU clock force on */
/* Clear CMMU clock force on */
modifyreg32(EXTMEM_PRO_CACHE_MMU_POWER_CTRL_REG,
EXTMEM_PRO_CACHE_MMU_MEM_FORCE_ON, 0);
/* clear rom clock force on */
/* Clear rom clock force on */
REG_SET_FIELD(SYSTEM_ROM_CTRL_0_REG, SYSTEM_ROM_FO, 0);
/* clear tag clock force on */
/* Clear tag clock force on */
REG_SET_FIELD(SYSTEM_SRAM_CTRL_0_REG, SYSTEM_SRAM_FO, 0);
/* clear tag clock force on */
/* Clear tag clock force on */
modifyreg32(EXTMEM_PRO_DCACHE_TAG_POWER_CTRL_REG,
EXTMEM_PRO_DCACHE_TAG_MEM_FORCE_ON, 0);
modifyreg32(EXTMEM_PRO_ICACHE_TAG_POWER_CTRL_REG,
EXTMEM_PRO_ICACHE_TAG_MEM_FORCE_ON, 0);
/* clear register clock force on */
/* Clear register clock force on */
modifyreg32(SPI_MEM_CLOCK_GATE_REG(0), SPI_MEM_CLK_EN, 0);
modifyreg32(SPI_MEM_CLOCK_GATE_REG(1), SPI_MEM_CLK_EN, 0);
@ -1540,7 +1600,7 @@ void IRAM_ATTR esp32s2_rtc_init(void)
modifyreg32(RTC_CNTL_REG, RTC_CNTL_DBOOST_FORCE_PD, 0);
}
/* cancel digital pu force */
/* Cancel digital pu force */
modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_FORCE_PU |
RTC_CNTL_FASTMEM_FORCE_PU, 0);
@ -1572,7 +1632,7 @@ void IRAM_ATTR esp32s2_rtc_init(void)
modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_FORCE_NOISO, 0);
/* cancel digital PADS force no iso */
/* Cancel digital PADS force no iso */
if (cfg.cpu_waiti_clk_gate)
{
@ -1753,11 +1813,39 @@ void IRAM_ATTR esp32s2_rtc_wait_for_slow_cycle(void)
}
}
/****************************************************************************
* Name: esp32s2_rtc_clk_apb_freq_update
*
* Description:
* Store new APB frequency value into RTC_APB_FREQ_REG
*
* Input Parameters:
* apb_freq - New APB frequency, in Hz
*
* Returned Value:
* None
*
****************************************************************************/
void esp32s2_rtc_clk_apb_freq_update(uint32_t apb_freq)
{
g_apb_freq = apb_freq;
}
/****************************************************************************
* Name: esp32s2_rtc_clk_apb_freq_get
*
* Description:
* Get the current stored APB frequency
*
* Input Parameters:
* None
*
* Returned Value:
* The APB frequency value, in Hz.
*
****************************************************************************/
uint32_t esp32s2_rtc_clk_apb_freq_get(void)
{
return g_apb_freq;
@ -2014,7 +2102,7 @@ void IRAM_ATTR esp32s2_rtc_sleep_init(uint32_t flags)
REG_SET_BIT(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_FORCE_PU);
}
/* enable VDDSDIO control by state machine */
/* Enable VDDSDIO control by state machine */
modifyreg32(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_FORCE, 0);
REG_SET_FIELD(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_REG_PD_EN,
@ -2067,7 +2155,7 @@ int IRAM_ATTR esp32s2_rtc_sleep_start(uint32_t wakeup_opt,
modifyreg32(RTC_CNTL_INT_CLR_RTC_REG, 0,
RTC_CNTL_SLP_REJECT_INT_CLR | RTC_CNTL_SLP_WAKEUP_INT_CLR);
/* restore DBG_ATTEN to the default value */
/* Restore DBG_ATTEN to the default value */
REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN_DEEP_SLP,
RTC_CNTL_DBG_ATTEN_LIGHTSLEEP_DEFAULT);

View File

@ -96,7 +96,8 @@ enum esp32s2_rtc_slow_freq_e
{
RTC_SLOW_FREQ_RTC = 0, /* Internal 150 kHz RC oscillator */
RTC_SLOW_FREQ_32K_XTAL = 1, /* External 32 kHz XTAL */
RTC_SLOW_FREQ_8MD256 = 2, /* Internal 8 MHz RC oscillator, divided by 256 */
RTC_SLOW_FREQ_8MD256 = 2, /* Internal 8 MHz RC oscillator, divided
* by 256 */
};
/* RTC FAST_CLK frequency values */
@ -241,7 +242,7 @@ enum esp32s2_rtc_slow_freq_e esp32s2_rtc_get_slow_clk(void);
*
* Input Parameters:
* cal_clk - clock to be measured
* slowclk_cycles - number of slow clock cycles to average
* slowclk_cycles - number of slow clock cycles what is to be averaged
*
* Returned Value:
* Average slow clock period in microseconds, Q13.19 fixed point format

View File

@ -32,28 +32,44 @@
#include "xtensa.h"
#include "esp32s2_irq.h"
#include "esp32s2_rtc_gpio.h"
#include "esp32s2_rtc.h"
#include "esp32s2_wdt.h"
#include "hardware/esp32s2_efuse.h"
#include "hardware/esp32s2_rtccntl.h"
#include "hardware/esp32s2_tim.h"
#ifdef CONFIG_ESP32S2_RWDT
# error "RWDT not yet supported due to missing RTC driver!"
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Helpers for converting from Q13.19 fixed-point format to float */
#define N 19
#define Q_TO_FLOAT(x) ((float)x/(float)(1<<N))
/* 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)
#define IS_RWDT(dev) (((struct esp32s2_wdt_priv_s *)dev)->type == RTC)
/* Check whether the provided device is a Main Watchdog Timer */
#define IS_MWDT(dev) (((struct esp32s2_wdt_priv_s *)dev)->type == TIMER)
/* Check whether the provided device is a XTAL32K Watchdog Timer */
#define IS_XTWDT(dev) (((struct esp32s2_wdt_priv_s *)dev)->type == XTAL32K)
/****************************************************************************
* Private Types
****************************************************************************/
enum wdt_peripheral_e
{
RTC,
TIMER,
XTAL32K,
};
struct esp32s2_wdt_priv_s
{
struct esp32s2_wdt_ops_s *ops;
@ -62,8 +78,15 @@ struct esp32s2_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
****************************************************************************/
@ -96,6 +119,8 @@ static int32_t wdt_setisr(struct esp32s2_wdt_dev_s *dev,
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);
static uint16_t wdt_rtc_clk(struct esp32s2_wdt_dev_s *dev);
static void wdt_rstclk(struct esp32s2_wdt_dev_s *dev);
/****************************************************************************
* Private Data
@ -103,7 +128,7 @@ static void wdt_ackint(struct esp32s2_wdt_dev_s *dev);
/* ESP32-S2 WDT ops */
struct esp32s2_wdt_ops_s esp32s2_mwdt_ops =
struct esp32s2_wdt_ops_s esp32s2_wdt_ops =
{
.start = wdt_start,
.stop = wdt_stop,
@ -113,63 +138,63 @@ struct esp32s2_wdt_ops_s esp32s2_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 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,
.rtc_clk = wdt_rtc_clk,
.setisr = wdt_setisr,
.enableint = wdt_enableint,
.disableint = wdt_disableint,
.ackint = wdt_ackint,
.rstclk = wdt_rstclk,
};
#ifdef CONFIG_ESP32S2_MWDT0
struct esp32s2_wdt_priv_s g_esp32s2_mwdt0_priv =
{
.ops = &esp32s2_mwdt_ops,
.ops = &esp32s2_wdt_ops,
.base = TIMG_T0CONFIG_REG(0),
.periph = ESP32S2_PERIPH_TG_WDT_LEVEL,
.irq = ESP32S2_IRQ_TG_WDT_LEVEL,
.cpuint = -ENOMEM,
.inuse = false,
.type = TIMER,
};
#endif
#ifdef CONFIG_ESP32S2_MWDT1
struct esp32s2_wdt_priv_s g_esp32s2_mwdt1_priv =
{
.ops = &esp32s2_mwdt_ops,
.ops = &esp32s2_wdt_ops,
.base = TIMG_T0CONFIG_REG(1),
.periph = ESP32S2_PERIPH_TG1_WDT_LEVEL,
.irq = ESP32S2_IRQ_TG1_WDT_LEVEL,
.cpuint = -ENOMEM,
.inuse = false,
.type = TIMER,
};
#endif
#ifdef CONFIG_ESP32S2_RWDT
struct esp32s2_wdt_priv_s g_esp32s2_rwdt_priv =
{
.ops = &esp32s2_rwdt_ops,
.ops = &esp32s2_wdt_ops,
.base = RTC_CNTL_OPTIONS0_REG,
.periph = ESP32S2_PERIPH_RTC_CORE,
.irq = ESP32S2_IRQ_RTC_WDT,
.cpuint = -ENOMEM,
.inuse = false,
.type = RTC,
};
#endif
#ifdef CONFIG_ESP32S2_XTWDT
struct esp32s2_wdt_priv_s g_esp32s2_xtwdt_priv =
{
.ops = &esp32s2_wdt_ops,
.base = RTC_CNTL_OPTIONS0_REG,
.periph = ESP32S2_PERIPH_RTC_CORE,
.irq = ESP32S2_IRQ_RTC_XTAL32K_DEAD,
.cpuint = -ENOMEM,
.inuse = false,
.type = XTAL32K,
};
#endif
@ -262,10 +287,18 @@ static void wdt_start(struct esp32s2_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_ESP32S2_XTWDT_BACKUP_CLK_ENABLE
wdt_modifyreg32(dev, XTWDT_CONFIG0_OFFSET,
0, RTC_CNTL_XTAL32K_AUTO_BACKUP);
#endif
}
}
/****************************************************************************
@ -303,7 +336,7 @@ static int32_t wdt_config_stage(struct esp32s2_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);
@ -319,7 +352,7 @@ static int32_t wdt_config_stage(struct esp32s2_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);
@ -335,7 +368,7 @@ static int32_t wdt_config_stage(struct esp32s2_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);
@ -351,7 +384,7 @@ static int32_t wdt_config_stage(struct esp32s2_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);
@ -388,10 +421,14 @@ static void wdt_stop(struct esp32s2_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);
}
}
/****************************************************************************
@ -417,7 +454,7 @@ static void wdt_enablewp(struct esp32s2_wdt_dev_s *dev)
{
wdt_putreg(dev, RWDT_WP_REG, 0);
}
else
else if (IS_MWDT(dev))
{
wdt_putreg(dev, MWDT_WP_REG, 0);
}
@ -446,7 +483,7 @@ static void wdt_disablewp(struct esp32s2_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);
}
@ -469,12 +506,23 @@ static void wdt_disablewp(struct esp32s2_wdt_dev_s *dev)
static void wdt_pre(struct esp32s2_wdt_dev_s *dev, uint16_t pre)
{
uint32_t mask = (uint32_t)pre << TIMG_WDT_CLK_PRESCALER_S;
uint32_t mask = 0;
DEBUGASSERT(dev != NULL);
wdt_modifyreg32(dev, MWDT_CLK_PRESCALE_OFFSET, TIMG_WDT_CLK_PRESCALER_M,
mask);
if (IS_MWDT(dev))
{
mask = (uint32_t)pre << TIMG_WDT_CLK_PRESCALER_S;
wdt_modifyreg32(dev, MWDT_CLK_PRESCALE_OFFSET,
TIMG_WDT_CLK_PRESCALER_M, mask);
}
#ifdef CONFIG_ESP32S2_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
}
/****************************************************************************
@ -499,6 +547,14 @@ static int32_t wdt_settimeout(struct esp32s2_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 ESP32S2_WDT_STAGE0:
@ -515,7 +571,7 @@ static int32_t wdt_settimeout(struct esp32s2_wdt_dev_s *dev, uint32_t value,
value = value >> (delay + 1);
wdt_putreg(dev, RWDT_STAGE0_TIMEOUT_OFFSET, value);
}
else
else if (IS_MWDT(dev))
{
wdt_putreg(dev, MWDT_STAGE0_TIMEOUT_OFFSET, value);
}
@ -528,7 +584,7 @@ static int32_t wdt_settimeout(struct esp32s2_wdt_dev_s *dev, uint32_t value,
{
wdt_putreg(dev, RWDT_STAGE1_TIMEOUT_OFFSET, value);
}
else
else if (IS_MWDT(dev))
{
wdt_putreg(dev, MWDT_STAGE1_TIMEOUT_OFFSET, value);
}
@ -541,7 +597,7 @@ static int32_t wdt_settimeout(struct esp32s2_wdt_dev_s *dev, uint32_t value,
{
wdt_putreg(dev, RWDT_STAGE2_TIMEOUT_OFFSET, value);
}
else
else if (IS_MWDT(dev))
{
wdt_putreg(dev, MWDT_STAGE2_TIMEOUT_OFFSET, value);
}
@ -554,7 +610,7 @@ static int32_t wdt_settimeout(struct esp32s2_wdt_dev_s *dev, uint32_t value,
{
wdt_putreg(dev, RWDT_STAGE3_TIMEOUT_OFFSET, value);
}
else
else if (IS_MWDT(dev))
{
wdt_putreg(dev, MWDT_STAGE3_TIMEOUT_OFFSET, value);
}
@ -591,12 +647,76 @@ static void wdt_feed(struct esp32s2_wdt_dev_s *dev)
{
wdt_modifyreg32(dev, RWDT_FEED_OFFSET, 0, RTC_CNTL_WDT_FEED);
}
else
else if (IS_MWDT(dev))
{
wdt_putreg(dev, MWDT_FEED_OFFSET, TIMG_WDT_FEED);
}
}
/****************************************************************************
* Name: wdt_rtc_clk
*
* Description:
* Check the RTC clock source and return the necessary cycles to complete
* 1 ms.
*
* Parameters:
* dev - Pointer to the driver state structure.
*
* Returned Values:
* Number of cycles to complete 1 ms.
*
****************************************************************************/
static uint16_t wdt_rtc_clk(struct esp32s2_wdt_dev_s *dev)
{
enum esp32s2_rtc_slow_freq_e slow_clk_rtc;
uint32_t period_13q19;
float period;
float cycles_ms;
uint16_t cycles_ms_int;
/* Calibration map: Maps each RTC SLOW_CLK source to the number
* used to calibrate this source.
*/
static const enum esp32s2_rtc_cal_sel_e cal_map[] =
{
RTC_CAL_RTC_MUX,
RTC_CAL_32K_XTAL,
RTC_CAL_8MD256
};
DEBUGASSERT(dev);
/* Check which clock is sourcing the slow_clk_rtc */
slow_clk_rtc = esp32s2_rtc_get_slow_clk();
/* Get the slow_clk_rtc period in us in Q13.19 fixed point format */
period_13q19 = esp32s2_rtc_clk_cal(cal_map[slow_clk_rtc],
SLOW_CLK_CAL_CYCLES);
/* Assert no error happened during the calibration */
DEBUGASSERT(period_13q19 != 0);
/* Convert from Q13.19 format to float */
period = Q_TO_FLOAT(period_13q19);
/* Get the number of cycles necessary to count 1 ms */
cycles_ms = 1000.0 / period;
/* Get the integer number of cycles */
cycles_ms_int = (uint16_t)cycles_ms;
return cycles_ms_int;
}
/****************************************************************************
* Name: wdt_setisr
*
@ -635,8 +755,9 @@ static int32_t wdt_setisr(struct esp32s2_wdt_dev_s *dev, xcpt_t handler,
if (wdt->cpuint >= 0)
{
#ifdef CONFIG_ESP32S2_RWDT
if (wdt->irq == ESP32S2_IRQ_RTC_WDT)
#if defined(CONFIG_ESP32S2_RWDT) || defined(CONFIG_ESP32S2_XTWDT)
if (wdt->irq == ESP32S2_IRQ_RTC_WDT ||
wdt->irq == ESP32S2_IRQ_RTC_XTAL32K_DEAD)
{
esp32s2_rtcioirqdisable(wdt->irq);
irq_detach(wdt->irq);
@ -663,8 +784,9 @@ static int32_t wdt_setisr(struct esp32s2_wdt_dev_s *dev, xcpt_t handler,
{
/* Set up to receive peripheral interrupts on the current CPU */
#ifdef CONFIG_ESP32S2_RWDT
if (wdt->irq == ESP32S2_IRQ_RTC_WDT)
#if defined(CONFIG_ESP32S2_RWDT) || defined(CONFIG_ESP32S2_XTWDT)
if (wdt->irq == ESP32S2_IRQ_RTC_WDT ||
wdt->irq == ESP32S2_IRQ_RTC_XTAL32K_DEAD)
{
ret = irq_attach(wdt->irq, handler, arg);
@ -728,10 +850,15 @@ static void wdt_enableint(struct esp32s2_wdt_dev_s *dev)
{
wdt_modifyreg32(dev, RWDT_INT_ENA_REG_OFFSET, 0, RTC_CNTL_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_XTAL32K_DEAD_INT_ENA);
}
}
/****************************************************************************
@ -753,10 +880,15 @@ static void wdt_disableint(struct esp32s2_wdt_dev_s *dev)
{
wdt_modifyreg32(dev, RWDT_INT_ENA_REG_OFFSET, RTC_CNTL_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_XTAL32K_DEAD_INT_ENA, 0);
}
}
/****************************************************************************
@ -778,10 +910,49 @@ static void wdt_ackint(struct esp32s2_wdt_dev_s *dev)
{
wdt_putreg(dev, RWDT_INT_CLR_REG_OFFSET, RTC_CNTL_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_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 esp32s2_wdt_dev_s *dev)
{
DEBUGASSERT(dev != NULL);
struct esp32s2_wdt_priv_s *wdt = (struct esp32s2_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);
}
}
/****************************************************************************
@ -834,6 +1005,15 @@ struct esp32s2_wdt_dev_s *esp32s2_wdt_init(enum esp32s2_wdt_inst_e wdt_id)
break;
}
#endif
#ifdef CONFIG_ESP32S2_XTWDT
case ESP32S2_WDT_XTWDT:
{
wdt = &g_esp32s2_xtwdt_priv;
break;
}
#endif
default:
@ -928,7 +1108,7 @@ bool esp32s2_wdt_is_running(struct esp32s2_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)
@ -936,6 +1116,14 @@ bool esp32s2_wdt_is_running(struct esp32s2_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 ESP32S2_WDT_START(d) ((d)->ops->start(d))
@ -42,14 +59,17 @@
#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_XTWDT_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_XTWDT_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))
#define ESP32S2_XTWDT_RST_CLK(d) ((d)->ops->rstclk(d))
/****************************************************************************
* Public Types
@ -61,7 +81,8 @@ 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) */
ESP32S2_WDT_RWDT, /* RTC Watchdog Timer (RWDT) */
ESP32S2_WDT_XTWDT /* XTAL32K Watchdog Timer (XTWDT) */
};
/* Stages of a Watchdog Timer. A WDT has 4 stages. */
@ -134,6 +155,7 @@ struct esp32s2_wdt_ops_s
void (*enableint)(struct esp32s2_wdt_dev_s *dev);
void (*disableint)(struct esp32s2_wdt_dev_s *dev);
void (*ackint)(struct esp32s2_wdt_dev_s *dev);
void (*rstclk)(struct esp32s2_wdt_dev_s *dev);
};
/****************************************************************************

View File

@ -37,6 +37,7 @@
#include "xtensa.h"
#include "esp32s2_wdt.h"
#include "esp32s2_rtc.h"
#include "esp32s2_wdt_lowerhalf.h"
#include "hardware/esp32s2_soc.h"
@ -68,6 +69,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
****************************************************************************/
@ -75,7 +94,8 @@
enum wdt_peripheral_e
{
RTC,
TIMER
TIMER,
XTAL32K,
};
/* This structure provides the private representation of the "lower-half"
@ -114,6 +134,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_ESP32S2_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
@ -129,7 +155,7 @@ static const struct watchdog_ops_s g_esp32s2_wdg_ops =
.getstatus = wdt_lh_getstatus,
.settimeout = wdt_lh_settimeout,
.capture = wdt_lh_capture,
.ioctl = NULL
.ioctl = wdt_lh_ioctl,
};
#ifdef CONFIG_ESP32S2_MWDT0
@ -159,6 +185,15 @@ static struct esp32s2_wdt_lowerhalf_s g_esp32s2_rwdt_lowerhalf =
};
#endif
#ifdef CONFIG_ESP32S2_XTWDT
/* XTWDT lower-half */
static struct esp32s2_wdt_lowerhalf_s g_esp32s2_xtwdt_lowerhalf =
{
.ops = &g_esp32s2_wdg_ops,
};
#endif
/****************************************************************************
* Name: wdt_lh_start
*
@ -216,7 +251,7 @@ static int wdt_lh_start(struct watchdog_lowerhalf_s *lower)
ESP32S2_WDT_STG_CONF(priv->wdt, ESP32S2_WDT_STAGE0,
ESP32S2_WDT_STAGE_ACTION_RESET_SYSTEM);
}
else
else if (priv->peripheral == RTC)
{
ESP32S2_WDT_STG_CONF(priv->wdt, ESP32S2_WDT_STAGE0,
ESP32S2_WDT_STAGE_ACTION_RESET_RTC);
@ -433,6 +468,8 @@ static int wdt_lh_settimeout(struct watchdog_lowerhalf_s *lower,
(struct esp32s2_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);
@ -466,7 +503,7 @@ static int wdt_lh_settimeout(struct watchdog_lowerhalf_s *lower,
/* Watchdog from RTC Module */
else
else if (priv->peripheral == RTC)
{
rtc_cycles = ESP32S2_RWDT_CLK(priv->wdt);
rtc_ms_max = (RWDT_FULL_STAGE / (uint32_t)rtc_cycles);
@ -486,6 +523,28 @@ 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 / 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;
ESP32S2_WDT_STO(priv->wdt, timeout, ESP32S2_WDT_STAGE0);
}
}
/* Reset the wdt */
ESP32S2_WDT_FEED(priv->wdt);
@ -588,7 +647,7 @@ static xcpt_t wdt_lh_capture(struct watchdog_lowerhalf_s *lower,
ESP32S2_WDT_STG_CONF(priv->wdt, ESP32S2_WDT_STAGE0,
ESP32S2_WDT_STAGE_ACTION_RESET_SYSTEM);
}
else
else if (priv->peripheral == RTC)
{
ESP32S2_WDT_STG_CONF(priv->wdt, ESP32S2_WDT_STAGE0,
ESP32S2_WDT_STAGE_ACTION_RESET_RTC);
@ -600,6 +659,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 esp32s2_wdt_lowerhalf_s *priv =
(struct esp32s2_wdt_lowerhalf_s *)lower;
wdinfo("ioctl Call: cmd=0x%x arg=0x%lx", cmd, arg);
/* Process the IOCTL command */
switch (cmd)
{
case WDIOC_RSTCLK:
ESP32S2_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 +722,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_ESP32S2_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 +855,15 @@ int esp32s2_wdt_initialize(const char *devpath, enum esp32s2_wdt_inst_e wdt)
}
#endif
#ifdef CONFIG_ESP32S2_XTWDT
case ESP32S2_WDT_XTWDT:
{
lower = &g_esp32s2_xtwdt_lowerhalf;
lower->peripheral = XTAL32K;
break;
}
#endif
default:
{
ret = -ENODEV;
@ -712,6 +896,26 @@ int esp32s2_wdt_initialize(const char *devpath, enum esp32s2_wdt_inst_e wdt)
ESP32S2_MWDT_PRE(lower->wdt, MWDT_CLK_PRESCALER_VALUE);
}
/* Configure auto backup clock when XTAL32K fails */
#ifdef CONFIG_ESP32S2_XTWDT_BACKUP_CLK_ENABLE
if (lower->peripheral == XTAL32K)
{
/* Estimate frequency of internal RTC oscillator */
uint32_t rtc_clk_period_us =
esp32s2_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);
ESP32S2_XTWDT_PRE(lower->wdt, xtal32k_clk_factor);
}
#endif
ESP32S2_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 0x0094
#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 0x00f4
#define XTWDT_CLK_PRESCALE_OFFSET 0x00f0
#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

@ -215,6 +215,7 @@ PROVIDE ( dummy_len_plus = 0x3ffffd54 );
PROVIDE ( Enable_QMode = 0x40016690 );
PROVIDE ( esp_crc8 = 0x40011a78 );
PROVIDE ( esp_rom_config_pad_power_select = 0x40016e58 );
PROVIDE ( esp_rom_delay_us = ets_delay_us );
PROVIDE ( esp_rom_opiflash_cache_mode_config = 0x40016754 );
PROVIDE ( esp_rom_opiflash_exec_cmd = 0x40017e30 );
PROVIDE ( esp_rom_opiflash_exit_continuous_read_mode = 0x40017ee8 );

View File

@ -31,8 +31,6 @@
#include "esp32s2_wdt_lowerhalf.h"
#include "esp32s2_wdt.h"
#include "esp32s2-saola-1.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@ -84,6 +82,15 @@ int board_wdt_init(void)
}
#endif /* CONFIG_ESP32S2_RWDT */
#ifdef CONFIG_ESP32S2_XTWDT
ret = esp32s2_wdt_initialize("/dev/watchdog3", ESP32S2_WDT_XTWDT);
if (ret < 0)
{
syslog(LOG_ERR, "Failed to initialize XTWDT: %d\n", ret);
return ret;
}
#endif /* CONFIG_ESP32S2_XTWDT */
return ret;
}

View File

@ -0,0 +1,51 @@
#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_ARCH_LEDS is not set
# CONFIG_NSH_ARGCAT is not set
# CONFIG_NSH_CMDOPT_HEXDUMP is not set
CONFIG_ARCH="xtensa"
CONFIG_ARCH_BOARD="esp32s2-kaluga-1"
CONFIG_ARCH_BOARD_COMMON=y
CONFIG_ARCH_BOARD_ESP32S2_KALUGA_1=y
CONFIG_ARCH_CHIP="esp32s2"
CONFIG_ARCH_CHIP_ESP32S2=y
CONFIG_ARCH_CHIP_ESP32S2WROVER=y
CONFIG_ARCH_STACKDUMP=y
CONFIG_ARCH_XTENSA=y
CONFIG_BOARD_LOOPSPERMSEC=16717
CONFIG_BUILTIN=y
CONFIG_DEBUG_FULLOPT=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_ESP32S2_MWDT0=y
CONFIG_ESP32S2_MWDT1=y
CONFIG_ESP32S2_RWDT=y
CONFIG_ESP32S2_UART0=y
CONFIG_EXAMPLES_WATCHDOG=y
CONFIG_FS_PROCFS=y
CONFIG_HAVE_CXX=y
CONFIG_HAVE_CXXINITIALIZE=y
CONFIG_IDLETHREAD_STACKSIZE=3072
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INTELHEX_BINARY=y
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_LINELEN=64
CONFIG_NSH_READLINE=y
CONFIG_PREALLOC_TIMERS=4
CONFIG_RAM_SIZE=114688
CONFIG_RAM_START=0x20000000
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_WAITPID=y
CONFIG_START_DAY=6
CONFIG_START_MONTH=12
CONFIG_START_YEAR=2011
CONFIG_SYSLOG_BUFFER=y
CONFIG_SYSTEM_NSH=y
CONFIG_UART0_SERIAL_CONSOLE=y
CONFIG_WATCHDOG=y

View File

@ -23,6 +23,7 @@ CONFIG_DEBUG_FULLOPT=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_ESP32S2_MWDT0=y
CONFIG_ESP32S2_MWDT1=y
CONFIG_ESP32S2_RWDT=y
CONFIG_ESP32S2_UART0=y
CONFIG_EXAMPLES_WATCHDOG=y
CONFIG_FS_PROCFS=y