This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new c15392d9b7 xtensa/esp32s2: Add xtwdt and rwdt support
c15392d9b7 is described below
commit c15392d9b723f7d8add299c75c0fc95a146f0950
Author: Eren Terzioglu <[email protected]>
AuthorDate: Mon Nov 27 14:12:45 2023 +0300
xtensa/esp32s2: Add xtwdt and rwdt support
---
.../esp32s2/boards/esp32s2-kaluga-1/index.rst | 18 ++
.../esp32s2/boards/esp32s2-saola-1/index.rst | 5 +
arch/xtensa/src/esp32s2/Kconfig | 22 ++
arch/xtensa/src/esp32s2/esp32s2_rtc.c | 214 ++++++++++-----
arch/xtensa/src/esp32s2/esp32s2_rtc.h | 5 +-
arch/xtensa/src/esp32s2/esp32s2_wdt.c | 294 +++++++++++++++++----
arch/xtensa/src/esp32s2/esp32s2_wdt.h | 24 +-
arch/xtensa/src/esp32s2/esp32s2_wdt_lowerhalf.c | 214 ++++++++++++++-
arch/xtensa/src/esp32s2/hardware/esp32s2_rtccntl.h | 7 +
.../xtensa/esp32s2/common/scripts/esp32s2_rom.ld | 1 +
.../xtensa/esp32s2/common/src/esp32s2_board_wdt.c | 11 +-
.../configs/watchdog/defconfig | 6 +-
.../esp32s2-saola-1/configs/watchdog/defconfig | 1 +
13 files changed, 693 insertions(+), 129 deletions(-)
diff --git
a/Documentation/platforms/xtensa/esp32s2/boards/esp32s2-kaluga-1/index.rst
b/Documentation/platforms/xtensa/esp32s2/boards/esp32s2-kaluga-1/index.rst
index 00f373e7ec..5e1abc2d5f 100644
--- a/Documentation/platforms/xtensa/esp32s2/boards/esp32s2-kaluga-1/index.rst
+++ b/Documentation/platforms/xtensa/esp32s2/boards/esp32s2-kaluga-1/index.rst
@@ -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.
diff --git
a/Documentation/platforms/xtensa/esp32s2/boards/esp32s2-saola-1/index.rst
b/Documentation/platforms/xtensa/esp32s2/boards/esp32s2-saola-1/index.rst
index aa6404f671..36d622b138 100644
--- a/Documentation/platforms/xtensa/esp32s2/boards/esp32s2-saola-1/index.rst
+++ b/Documentation/platforms/xtensa/esp32s2/boards/esp32s2-saola-1/index.rst
@@ -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.
diff --git a/arch/xtensa/src/esp32s2/Kconfig b/arch/xtensa/src/esp32s2/Kconfig
index 5f40582572..0cf493e409 100644
--- a/arch/xtensa/src/esp32s2/Kconfig
+++ b/arch/xtensa/src/esp32s2/Kconfig
@@ -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
diff --git a/arch/xtensa/src/esp32s2/esp32s2_rtc.c
b/arch/xtensa/src/esp32s2/esp32s2_rtc.c
index 35851ea357..9f61ad1912 100644
--- a/arch/xtensa/src/esp32s2/esp32s2_rtc.c
+++ b/arch/xtensa/src/esp32s2/esp32s2_rtc.c
@@ -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);
diff --git a/arch/xtensa/src/esp32s2/esp32s2_rtc.h
b/arch/xtensa/src/esp32s2/esp32s2_rtc.h
index aa26335944..74b64fc05f 100644
--- a/arch/xtensa/src/esp32s2/esp32s2_rtc.h
+++ b/arch/xtensa/src/esp32s2/esp32s2_rtc.h
@@ -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
diff --git a/arch/xtensa/src/esp32s2/esp32s2_wdt.c
b/arch/xtensa/src/esp32s2/esp32s2_wdt.c
index 1fd5f04937..06ac7c13e9 100644
--- a/arch/xtensa/src/esp32s2/esp32s2_wdt.c
+++ b/arch/xtensa/src/esp32s2/esp32s2_wdt.c
@@ -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;
}
diff --git a/arch/xtensa/src/esp32s2/esp32s2_wdt.h
b/arch/xtensa/src/esp32s2/esp32s2_wdt.h
index 385e8460ec..c9d5b1da2b 100644
--- a/arch/xtensa/src/esp32s2/esp32s2_wdt.h
+++ b/arch/xtensa/src/esp32s2/esp32s2_wdt.h
@@ -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);
};
/****************************************************************************
diff --git a/arch/xtensa/src/esp32s2/esp32s2_wdt_lowerhalf.c
b/arch/xtensa/src/esp32s2/esp32s2_wdt_lowerhalf.c
index 6289b39232..308d92e018 100644
--- a/arch/xtensa/src/esp32s2/esp32s2_wdt_lowerhalf.c
+++ b/arch/xtensa/src/esp32s2/esp32s2_wdt_lowerhalf.c
@@ -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
diff --git a/arch/xtensa/src/esp32s2/hardware/esp32s2_rtccntl.h
b/arch/xtensa/src/esp32s2/hardware/esp32s2_rtccntl.h
index a3500c4074..bd1b16ef42 100644
--- a/arch/xtensa/src/esp32s2/hardware/esp32s2_rtccntl.h
+++ b/arch/xtensa/src/esp32s2/hardware/esp32s2_rtccntl.h
@@ -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
*/
diff --git a/boards/xtensa/esp32s2/common/scripts/esp32s2_rom.ld
b/boards/xtensa/esp32s2/common/scripts/esp32s2_rom.ld
index 492c081b50..2e0471fd48 100644
--- a/boards/xtensa/esp32s2/common/scripts/esp32s2_rom.ld
+++ b/boards/xtensa/esp32s2/common/scripts/esp32s2_rom.ld
@@ -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 );
diff --git a/boards/xtensa/esp32s2/common/src/esp32s2_board_wdt.c
b/boards/xtensa/esp32s2/common/src/esp32s2_board_wdt.c
index 57c9fd7e33..2cab40397a 100644
--- a/boards/xtensa/esp32s2/common/src/esp32s2_board_wdt.c
+++ b/boards/xtensa/esp32s2/common/src/esp32s2_board_wdt.c
@@ -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;
}
diff --git a/boards/xtensa/esp32s2/esp32s2-saola-1/configs/watchdog/defconfig
b/boards/xtensa/esp32s2/esp32s2-kaluga-1/configs/watchdog/defconfig
similarity index 92%
copy from boards/xtensa/esp32s2/esp32s2-saola-1/configs/watchdog/defconfig
copy to boards/xtensa/esp32s2/esp32s2-kaluga-1/configs/watchdog/defconfig
index dcaa9ba9eb..d875caa480 100644
--- a/boards/xtensa/esp32s2/esp32s2-saola-1/configs/watchdog/defconfig
+++ b/boards/xtensa/esp32s2/esp32s2-kaluga-1/configs/watchdog/defconfig
@@ -9,9 +9,9 @@
# CONFIG_NSH_ARGCAT is not set
# CONFIG_NSH_CMDOPT_HEXDUMP is not set
CONFIG_ARCH="xtensa"
-CONFIG_ARCH_BOARD="esp32s2-saola-1"
+CONFIG_ARCH_BOARD="esp32s2-kaluga-1"
CONFIG_ARCH_BOARD_COMMON=y
-CONFIG_ARCH_BOARD_ESP32S2_SAOLA_1=y
+CONFIG_ARCH_BOARD_ESP32S2_KALUGA_1=y
CONFIG_ARCH_CHIP="esp32s2"
CONFIG_ARCH_CHIP_ESP32S2=y
CONFIG_ARCH_CHIP_ESP32S2WROVER=y
@@ -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
@@ -30,7 +31,6 @@ CONFIG_HAVE_CXX=y
CONFIG_HAVE_CXXINITIALIZE=y
CONFIG_IDLETHREAD_STACKSIZE=3072
CONFIG_INIT_ENTRYPOINT="nsh_main"
-CONFIG_INIT_STACKSIZE=3072
CONFIG_INTELHEX_BINARY=y
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
diff --git a/boards/xtensa/esp32s2/esp32s2-saola-1/configs/watchdog/defconfig
b/boards/xtensa/esp32s2/esp32s2-saola-1/configs/watchdog/defconfig
index dcaa9ba9eb..bce5fa3d55 100644
--- a/boards/xtensa/esp32s2/esp32s2-saola-1/configs/watchdog/defconfig
+++ b/boards/xtensa/esp32s2/esp32s2-saola-1/configs/watchdog/defconfig
@@ -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