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 <eren.terzio...@espressif.com> 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