From 94eeb84814e1ba37f330097ff2ef36dc839c5186 Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Wed, 31 Dec 2025 16:02:42 +0800 Subject: [PATCH] feat(gptimer): add gptimer support on esp32s31 --- .../test_apps/gptimer/README.md | 4 +- .../test_apps/gptimer/main/CMakeLists.txt | 2 +- .../esp32h4/include/hal/timer_ll.h | 2 +- .../esp32s31/include/hal/timer_ll.h | 379 +++++++++++++++++- .../esp32s31/include/hal/timg_ll.h | 21 +- .../esp_hal_timg/esp32s31/timer_periph.c | 223 ++++++++++- .../port/esp32s31/esp_clk_tree.c | 14 +- .../esp32s31/include/soc/Kconfig.soc_caps.in | 16 + .../soc/esp32s31/include/soc/soc_caps.h | 8 +- .../peripherals/timer_group/gptimer/README.md | 4 +- .../timer_group/wiegand_interface/README.md | 4 +- examples/system/sysview_tracing/README.md | 4 +- .../system/sysview_tracing_heap_log/README.md | 4 +- 13 files changed, 652 insertions(+), 33 deletions(-) diff --git a/components/esp_driver_gptimer/test_apps/gptimer/README.md b/components/esp_driver_gptimer/test_apps/gptimer/README.md index 44f3780f1d..d76348d52c 100644 --- a/components/esp_driver_gptimer/test_apps/gptimer/README.md +++ b/components/esp_driver_gptimer/test_apps/gptimer/README.md @@ -1,2 +1,2 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | diff --git a/components/esp_driver_gptimer/test_apps/gptimer/main/CMakeLists.txt b/components/esp_driver_gptimer/test_apps/gptimer/main/CMakeLists.txt index ea5f81bd44..79038681c1 100644 --- a/components/esp_driver_gptimer/test_apps/gptimer/main/CMakeLists.txt +++ b/components/esp_driver_gptimer/test_apps/gptimer/main/CMakeLists.txt @@ -5,7 +5,7 @@ if(CONFIG_GPTIMER_ISR_CACHE_SAFE) list(APPEND srcs "test_gptimer_iram.c") endif() -if(CONFIG_SOC_TIMER_SUPPORT_ETM) +if(CONFIG_SOC_TIMER_SUPPORT_ETM AND CONFIG_SOC_ETM_SUPPORTED) list(APPEND srcs "test_gptimer_etm.c") endif() diff --git a/components/esp_hal_timg/esp32h4/include/hal/timer_ll.h b/components/esp_hal_timg/esp32h4/include/hal/timer_ll.h index f4401e51f8..c03c19992f 100644 --- a/components/esp_hal_timg/esp32h4/include/hal/timer_ll.h +++ b/components/esp_hal_timg/esp32h4/include/hal/timer_ll.h @@ -15,10 +15,10 @@ #include "soc/pcr_struct.h" #include "soc/soc_etm_source.h" -// Get timer group register base address with giving group number // Total number of general purpose timers #define TIMER_LL_GPTIMERS_TOTAL (TIMG_LL_INST_NUM * TIMG_LL_GPTIMERS_PER_INST) +// Get timer group register base address with giving group number #define TIMER_LL_GET_HW(group_id) ((group_id == 0) ? (&TIMERG0) : (&TIMERG1)) // Bit width of GPTIMER counter diff --git a/components/esp_hal_timg/esp32s31/include/hal/timer_ll.h b/components/esp_hal_timg/esp32s31/include/hal/timer_ll.h index cc69b7df3e..54b0725413 100644 --- a/components/esp_hal_timg/esp32s31/include/hal/timer_ll.h +++ b/components/esp_hal_timg/esp32s31/include/hal/timer_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,9 +12,13 @@ #include "hal/timer_types.h" #include "hal/timg_ll.h" #include "soc/timer_group_struct.h" +#include "soc/soc_etm_source.h" +#include "soc/hp_sys_clkrst_struct.h" +// Total number of general purpose timers #define TIMER_LL_GPTIMERS_TOTAL (TIMG_LL_INST_NUM * TIMG_LL_GPTIMERS_PER_INST) +// Get timer group register base address with giving group number #define TIMER_LL_GET_HW(group_id) ((group_id == 0) ? (&TIMERG0) : (&TIMERG1)) // Bit width of GPTIMER counter @@ -25,3 +29,376 @@ // Support RC_FAST as function clock #define TIMER_LL_FUNC_CLOCK_SUPPORT_RC_FAST 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#define TIMER_LL_ETM_TASK_TABLE(group, timer, task) \ + (uint32_t[2][2][GPTIMER_ETM_TASK_MAX]){ \ + { \ + { \ + [GPTIMER_ETM_TASK_START_COUNT] = TG0_TASK_CNT_START_TIMER0, \ + [GPTIMER_ETM_TASK_STOP_COUNT] = TG0_TASK_CNT_STOP_TIMER0, \ + [GPTIMER_ETM_TASK_EN_ALARM] = TG0_TASK_ALARM_START_TIMER0, \ + [GPTIMER_ETM_TASK_RELOAD] = TG0_TASK_CNT_RELOAD_TIMER0, \ + [GPTIMER_ETM_TASK_CAPTURE] = TG0_TASK_CNT_CAP_TIMER0, \ + }, \ + { \ + [GPTIMER_ETM_TASK_START_COUNT] = TG0_TASK_CNT_START_TIMER1, \ + [GPTIMER_ETM_TASK_STOP_COUNT] = TG0_TASK_CNT_STOP_TIMER1, \ + [GPTIMER_ETM_TASK_EN_ALARM] = TG0_TASK_ALARM_START_TIMER1, \ + [GPTIMER_ETM_TASK_RELOAD] = TG0_TASK_CNT_RELOAD_TIMER1, \ + [GPTIMER_ETM_TASK_CAPTURE] = TG0_TASK_CNT_CAP_TIMER1, \ + }, \ + }, \ + { \ + { \ + [GPTIMER_ETM_TASK_START_COUNT] = TG1_TASK_CNT_START_TIMER0, \ + [GPTIMER_ETM_TASK_STOP_COUNT] = TG1_TASK_CNT_STOP_TIMER0, \ + [GPTIMER_ETM_TASK_EN_ALARM] = TG1_TASK_ALARM_START_TIMER0, \ + [GPTIMER_ETM_TASK_RELOAD] = TG1_TASK_CNT_RELOAD_TIMER0, \ + [GPTIMER_ETM_TASK_CAPTURE] = TG1_TASK_CNT_CAP_TIMER0, \ + }, \ + { \ + [GPTIMER_ETM_TASK_START_COUNT] = TG1_TASK_CNT_START_TIMER1, \ + [GPTIMER_ETM_TASK_STOP_COUNT] = TG1_TASK_CNT_STOP_TIMER1, \ + [GPTIMER_ETM_TASK_EN_ALARM] = TG1_TASK_ALARM_START_TIMER1, \ + [GPTIMER_ETM_TASK_RELOAD] = TG1_TASK_CNT_RELOAD_TIMER1, \ + [GPTIMER_ETM_TASK_CAPTURE] = TG1_TASK_CNT_CAP_TIMER1, \ + }, \ + }, \ + }[group][timer][task] + +#define TIMER_LL_ETM_EVENT_TABLE(group, timer, event) \ + (uint32_t[2][2][GPTIMER_ETM_EVENT_MAX]){ \ + { \ + { \ + [GPTIMER_ETM_EVENT_ALARM_MATCH] = TG0_EVT_CNT_CMP_TIMER0, \ + }, \ + { \ + [GPTIMER_ETM_EVENT_ALARM_MATCH] = TG0_EVT_CNT_CMP_TIMER1, \ + }, \ + }, \ + { \ + { \ + [GPTIMER_ETM_EVENT_ALARM_MATCH] = TG1_EVT_CNT_CMP_TIMER0, \ + }, \ + { \ + [GPTIMER_ETM_EVENT_ALARM_MATCH] = TG1_EVT_CNT_CMP_TIMER1, \ + }, \ + }, \ + }[group][timer][event] + +/** + * @brief Set clock source for timer + * + * @param group_id Group ID + * @param timer_num Timer number in the group + * @param clk_src Clock source + */ +static inline void _timer_ll_set_clock_source(int group_id, uint32_t timer_num, gptimer_clock_source_t clk_src) +{ + uint8_t clk_id = 0; + switch (clk_src) { + case GPTIMER_CLK_SRC_XTAL: + clk_id = 0; + break; + case GPTIMER_CLK_SRC_PLL_F80M: + clk_id = 2; + break; + case GPTIMER_CLK_SRC_RC_FAST: + clk_id = 1; + break; + default: + HAL_ASSERT(false); + break; + } + if (group_id == 0) { + if (timer_num == 0) { + HP_SYS_CLKRST.timergrp0_ctrl0.reg_timergrp0_t0_src_sel = clk_id; + } else { + HP_SYS_CLKRST.timergrp0_ctrl0.reg_timergrp0_t1_src_sel = clk_id; + } + } else { + if (timer_num == 0) { + HP_SYS_CLKRST.timergrp1_ctrl0.reg_timergrp1_t0_src_sel = clk_id; + } else { + HP_SYS_CLKRST.timergrp1_ctrl0.reg_timergrp1_t1_src_sel = clk_id; + } + } +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define timer_ll_set_clock_source(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + _timer_ll_set_clock_source(__VA_ARGS__); \ +} while(0) + +/** + * @brief Enable Timer Group (GPTimer) module clock + * + * @param group_id Group ID + * @param timer_num Timer index in the group + * @param en true to enable, false to disable + */ +static inline void _timer_ll_enable_clock(int group_id, uint32_t timer_num, bool en) +{ + if (group_id == 0) { + if (timer_num == 0) { + HP_SYS_CLKRST.timergrp0_ctrl0.reg_timergrp0_t0_clk_en = en; + } else { + HP_SYS_CLKRST.timergrp0_ctrl0.reg_timergrp0_t1_clk_en = en; + } + } else { + if (timer_num == 0) { + HP_SYS_CLKRST.timergrp1_ctrl0.reg_timergrp1_t0_clk_en = en; + } else { + HP_SYS_CLKRST.timergrp1_ctrl0.reg_timergrp1_t1_clk_en = en; + } + } +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define timer_ll_enable_clock(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + _timer_ll_enable_clock(__VA_ARGS__); \ +} while(0) + +/** + * @brief Enable alarm event + * + * @param hw Timer Group register base address + * @param timer_num Timer number in the group + * @param en True: enable alarm + * False: disable alarm + */ +__attribute__((always_inline)) +static inline void timer_ll_enable_alarm(timg_dev_t *hw, uint32_t timer_num, bool en) +{ + hw->hw_timer[timer_num].config.tx_alarm_en = en; +} + +/** + * @brief Set clock prescale for timer + * + * @param hw Timer Group register base address + * @param timer_num Timer number in the group + * @param divider Prescale value (0 and 1 are not valid) + */ +static inline void timer_ll_set_clock_prescale(timg_dev_t *hw, uint32_t timer_num, uint32_t divider) +{ + HAL_ASSERT(divider >= 2 && divider <= 65536); + if (divider >= 65536) { + divider = 0; + } + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->hw_timer[timer_num].config, tx_divider, divider); + hw->hw_timer[timer_num].config.tx_divcnt_rst = 1; +} + +/** + * @brief Enable auto-reload mode + * + * @param hw Timer Group register base address + * @param timer_num Timer number in the group + * @param en True: enable auto reload mode + * False: disable auto reload mode + */ +__attribute__((always_inline)) +static inline void timer_ll_enable_auto_reload(timg_dev_t *hw, uint32_t timer_num, bool en) +{ + hw->hw_timer[timer_num].config.tx_autoreload = en; +} + +/** + * @brief Set count direction + * + * @param hw Timer peripheral register base address + * @param timer_num Timer number in the group + * @param direction Count direction + */ +static inline void timer_ll_set_count_direction(timg_dev_t *hw, uint32_t timer_num, gptimer_count_direction_t direction) +{ + hw->hw_timer[timer_num].config.tx_increase = (direction == GPTIMER_COUNT_UP); +} + +/** + * @brief Enable timer, start counting + * + * @param hw Timer Group register base address + * @param timer_num Timer number in the group + * @param en True: enable the counter + * False: disable the counter + */ +__attribute__((always_inline)) +static inline void timer_ll_enable_counter(timg_dev_t *hw, uint32_t timer_num, bool en) +{ + hw->hw_timer[timer_num].config.tx_en = en; +} + +/** + * @brief Trigger software capture event + * + * @param hw Timer Group register base address + * @param timer_num Timer number in the group + */ +__attribute__((always_inline)) +static inline void timer_ll_trigger_soft_capture(timg_dev_t *hw, uint32_t timer_num) +{ + hw->hw_timer[timer_num].update.tx_update = 1; + // Timer register is in a different clock domain from Timer hardware logic + // We need to wait for the update to take effect before fetching the count value + while (hw->hw_timer[timer_num].update.tx_update) { + } +} + +/** + * @brief Get counter value + * + * @param hw Timer Group register base address + * @param timer_num Timer number in the group + * + * @return counter value + */ +__attribute__((always_inline)) +static inline uint64_t timer_ll_get_counter_value(timg_dev_t *hw, uint32_t timer_num) +{ + return ((uint64_t)hw->hw_timer[timer_num].hi.tx_hi << 32) | (hw->hw_timer[timer_num].lo.tx_lo); +} + +/** + * @brief Set alarm value + * + * @param hw Timer Group register base address + * @param timer_num Timer number in the group + * @param alarm_value When counter reaches alarm value, alarm event will be triggered + */ +__attribute__((always_inline)) +static inline void timer_ll_set_alarm_value(timg_dev_t *hw, uint32_t timer_num, uint64_t alarm_value) +{ + hw->hw_timer[timer_num].alarmhi.tx_alarm_hi = (uint32_t)(alarm_value >> 32); + hw->hw_timer[timer_num].alarmlo.tx_alarm_lo = (uint32_t)alarm_value; +} + +/** + * @brief Set reload value + * + * @param hw Timer Group register base address + * @param timer_num Timer number in the group + * @param reload_val Reload counter value + */ +__attribute__((always_inline)) +static inline void timer_ll_set_reload_value(timg_dev_t *hw, uint32_t timer_num, uint64_t reload_val) +{ + hw->hw_timer[timer_num].loadhi.tx_load_hi = (uint32_t)(reload_val >> 32); + hw->hw_timer[timer_num].loadlo.tx_load_lo = (uint32_t)reload_val; +} + +/** + * @brief Get reload value + * + * @param hw Timer Group register base address + * @param timer_num Timer number in the group + * @return reload count value + */ +__attribute__((always_inline)) +static inline uint64_t timer_ll_get_reload_value(timg_dev_t *hw, uint32_t timer_num) +{ + return ((uint64_t)hw->hw_timer[timer_num].loadhi.tx_load_hi << 32) | (hw->hw_timer[timer_num].loadlo.tx_load_lo); +} + +/** + * @brief Trigger software reload, value set by `timer_ll_set_reload_value()` will be reflected into counter immediately + * + * @param hw Timer Group register base address + * @param timer_num Timer number in the group + */ +__attribute__((always_inline)) +static inline void timer_ll_trigger_soft_reload(timg_dev_t *hw, uint32_t timer_num) +{ + hw->hw_timer[timer_num].load.tx_load = 1; +} + +/** + * @brief Enable ETM module + * + * @param hw Timer Group register base address + * @param en True: enable ETM module, False: disable ETM module + */ +static inline void timer_ll_enable_etm(timg_dev_t *hw, bool en) +{ + hw->regclk.etm_en = en; +} + +/** + * @brief Enable timer interrupt by mask + * + * @param hw Timer Group register base address + * @param mask Mask of interrupt events + * @param en True: enable interrupt + * False: disable interrupt + */ +__attribute__((always_inline)) +static inline void timer_ll_enable_intr(timg_dev_t *hw, uint32_t mask, bool en) +{ + if (en) { + hw->int_ena_timers.val |= mask; + } else { + hw->int_ena_timers.val &= ~mask; + } +} + +/** + * @brief Get interrupt status + * + * @param hw Timer Group register base address + * + * @return Interrupt status + */ +__attribute__((always_inline)) +static inline uint32_t timer_ll_get_intr_status(timg_dev_t *hw) +{ + return hw->int_st_timers.val & 0x03; +} + +/** + * @brief Clear interrupt status by mask + * + * @param hw Timer Group register base address + * @param mask Interrupt events mask + */ +__attribute__((always_inline)) +static inline void timer_ll_clear_intr_status(timg_dev_t *hw, uint32_t mask) +{ + hw->int_clr_timers.val = mask; +} + +/** + * @brief Enable the register clock forever + * + * @param hw Timer Group register base address + * @param en True: Enable the register clock forever + * False: Register clock is enabled only when register operation happens + */ +static inline void timer_ll_enable_register_clock_always_on(timg_dev_t *hw, bool en) +{ + hw->regclk.clk_en = en; +} + +/** + * @brief Get interrupt status register address + * + * @param hw Timer Group register base address + * + * @return Interrupt status register address + */ +static inline volatile void *timer_ll_get_intr_status_reg(timg_dev_t *hw) +{ + return &hw->int_st_timers; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hal_timg/esp32s31/include/hal/timg_ll.h b/components/esp_hal_timg/esp32s31/include/hal/timg_ll.h index a446535ddd..b86687dccf 100644 --- a/components/esp_hal_timg/esp32s31/include/hal/timg_ll.h +++ b/components/esp_hal_timg/esp32s31/include/hal/timg_ll.h @@ -12,6 +12,7 @@ #include "hal/assert.h" #include "hal/misc.h" #include "soc/timer_group_struct.h" +#include "soc/hp_sys_clkrst_struct.h" #define TIMG_LL_GET(_attr) TIMG_LL_ ## _attr @@ -19,14 +20,12 @@ #define TIMG_LL_INST_NUM 2 // Number of general purpose timers in each Timer Group -#define TIMG_LL_GPTIMERS_PER_INST 1 +#define TIMG_LL_GPTIMERS_PER_INST 2 #ifdef __cplusplus extern "C" { #endif -// TODO: ["ESP32S31"] IDF-14745 - /** * @brief Enable the bus clock for timer group module * @@ -35,7 +34,11 @@ extern "C" { */ static inline void _timg_ll_enable_bus_clock(int group_id, bool enable) { - // TODO: ["ESP32S31"] IDF-14745 + if (group_id == 0) { + HP_SYS_CLKRST.timergrp0_ctrl0.reg_timergrp0_apb_clk_en = enable; + } else { + HP_SYS_CLKRST.timergrp1_ctrl0.reg_timergrp1_apb_clk_en = enable; + } } /// use a macro to wrap the function, force the caller to use it in a critical section @@ -56,7 +59,15 @@ static inline void _timg_ll_enable_bus_clock(int group_id, bool enable) */ static inline void _timg_ll_reset_register(int group_id) { - // TODO: ["ESP32S31"] IDF-14745 + if (group_id == 0) { + HP_SYS_CLKRST.timergrp0_ctrl0.reg_timergrp0_rst_en = 1; + HP_SYS_CLKRST.timergrp0_ctrl0.reg_timergrp0_rst_en = 0; + TIMERG0.wdtconfig0.wdt_flashboot_mod_en = 0; + } else { + HP_SYS_CLKRST.timergrp1_ctrl0.reg_timergrp1_rst_en = 1; + HP_SYS_CLKRST.timergrp1_ctrl0.reg_timergrp1_rst_en = 0; + TIMERG1.wdtconfig0.wdt_flashboot_mod_en = 0; + } } /// use a macro to wrap the function, force the caller to use it in a critical section diff --git a/components/esp_hal_timg/esp32s31/timer_periph.c b/components/esp_hal_timg/esp32s31/timer_periph.c index 55580b79ae..0024dcda9e 100644 --- a/components/esp_hal_timg/esp32s31/timer_periph.c +++ b/components/esp_hal_timg/esp32s31/timer_periph.c @@ -1,36 +1,243 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#include "soc/timer_periph.h" - -// TODO: ["ESP32S31"] IDF-14693 +#include "hal/timer_periph.h" const soc_timg_gptimer_signal_desc_t soc_timg_gptimer_signals[2][2] = { [0] = { [0] = { .module_name = "TIMG0T0", .parent_module = PERIPH_TIMG0_MODULE, - .irq_id = ETS_TG0_T0_LEVEL_INTR_SOURCE, + .irq_id = ETS_TIMERGRP0_T0_INTR_SOURCE, }, [1] = { .module_name = "TIMG0T1", .parent_module = PERIPH_TIMG0_MODULE, - .irq_id = ETS_TG0_T1_LEVEL_INTR_SOURCE, + .irq_id = ETS_TIMERGRP0_T1_INTR_SOURCE, }, }, [1] = { [0] = { .module_name = "TIMG1T0", .parent_module = PERIPH_TIMG1_MODULE, - .irq_id = ETS_TG1_T0_LEVEL_INTR_SOURCE, + .irq_id = ETS_TIMERGRP1_T0_INTR_SOURCE, }, [1] = { .module_name = "TIMG1T1", .parent_module = PERIPH_TIMG1_MODULE, - .irq_id = ETS_TG1_T1_LEVEL_INTR_SOURCE, + .irq_id = ETS_TIMERGRP1_T1_INTR_SOURCE, }, } }; + +#if SOC_PAU_SUPPORTED +/* Registers in retention context: + * TIMG_T0CONFIG_REG + * TIMG_T0ALARMLO_REG + * TIMG_T0ALARMHI_REG + * TIMG_T0LOADLO_REG + * TIMG_T0LOADHI_REG + * TIMG_INT_ENA_TIMERS_REG + * TIMG_REGCLK_REG + */ +#define TG0_TIMER0_RETENTION_REGS_BASE (REG_TIMG_BASE(0)) +#define TG1_TIMER0_RETENTION_REGS_BASE (REG_TIMG_BASE(1)) +#define TG_TIMER0_RETENTION_REGS_CNT 7 +static const uint32_t tg_timer0_regs_map[4] = {0x100000f1, 0x80000000, 0x0, 0x0}; + +/* Registers in retention context: + * TIMG_T1CONFIG_REG + * TIMG_T1ALARMLO_REG + * TIMG_T1ALARMHI_REG + * TIMG_T1LOADLO_REG + * TIMG_T1LOADHI_REG + * TIMG_INT_ENA_TIMERS_REG + * TIMG_REGCLK_REG + */ +#define TG0_TIMER1_RETENTION_REGS_BASE (REG_TIMG_BASE(0) + 0x24) +#define TG1_TIMER1_RETENTION_REGS_BASE (REG_TIMG_BASE(1) + 0x24) +#define TG_TIMER1_RETENTION_REGS_CNT 7 +static const uint32_t tg_timer1_regs_map[4] = {0x800f1, 0x400000, 0x0, 0x0}; + +const regdma_entries_config_t tg0_timer0_regdma_entries[] = { + // backup stage: trigger a soft capture + [0] = { + .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG0_TIMER_LINK(0x00), + TIMG_T0UPDATE_REG(0), TIMG_T0_UPDATE, TIMG_T0_UPDATE_M, 0, 1), + .owner = ENTRY(0) + }, + // backup stage: wait for the capture done + [1] = { + .config = REGDMA_LINK_WAIT_INIT(REGDMA_TG0_TIMER_LINK(0x01), + TIMG_T0UPDATE_REG(0), 0x0, TIMG_T0_UPDATE_M, 0, 1), + .owner = ENTRY(0) + }, + // backup stage: save the captured counter value + // restore stage: store the captured counter value to the loader register + [2] = { + .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_TG0_TIMER_LINK(0x02), + TIMG_T0LO_REG(0), TIMG_T0LOADLO_REG(0), 2, 0, 0), + .owner = ENTRY(0) + }, + // restore stage: trigger a soft reload, so the timer can continue from where it was backed up + [3] = { + .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG0_TIMER_LINK(0x03), + TIMG_T0LOAD_REG(0), 0x1, TIMG_T0_LOAD_M, 1, 0), + .owner = ENTRY(0) + }, + // backup stage: save other configuration and status registers + // restore stage: restore the configuration and status registers + [4] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_TG0_TIMER_LINK(0x04), + TG0_TIMER0_RETENTION_REGS_BASE, TG0_TIMER0_RETENTION_REGS_BASE, + TG_TIMER0_RETENTION_REGS_CNT, 0, 0, + tg_timer0_regs_map[0], tg_timer0_regs_map[1], + tg_timer0_regs_map[2], tg_timer0_regs_map[3]), + .owner = ENTRY(0) + }, +}; + +const regdma_entries_config_t tg0_timer1_regdma_entries[] = { + // backup stage: trigger a soft capture + [0] = { + .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG0_TIMER_LINK(0x05), + TIMG_T1UPDATE_REG(0), TIMG_T1_UPDATE, TIMG_T1_UPDATE_M, 0, 1), + .owner = ENTRY(0) + }, + // backup stage: wait for the capture done + [1] = { + .config = REGDMA_LINK_WAIT_INIT(REGDMA_TG0_TIMER_LINK(0x06), + TIMG_T1UPDATE_REG(0), 0x0, TIMG_T1_UPDATE_M, 0, 1), + .owner = ENTRY(0) + }, + // backup stage: save the captured counter value + // restore stage: store the captured counter value to the loader register + [2] = { + .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_TG0_TIMER_LINK(0x07), + TIMG_T1LO_REG(0), TIMG_T1LOADLO_REG(0), 2, 0, 0), + .owner = ENTRY(0) + }, + // restore stage: trigger a soft reload, so the timer can continue from where it was backed up + [3] = { + .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG0_TIMER_LINK(0x08), + TIMG_T1LOAD_REG(0), 0x1, TIMG_T1_LOAD_M, 1, 0), + .owner = ENTRY(0) + }, + // backup stage: save other configuration and status registers + // restore stage: restore the configuration and status registers + [4] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_TG0_TIMER_LINK(0x09), + TG0_TIMER1_RETENTION_REGS_BASE, TG0_TIMER1_RETENTION_REGS_BASE, + TG_TIMER1_RETENTION_REGS_CNT, 0, 0, + tg_timer1_regs_map[0], tg_timer1_regs_map[1], + tg_timer1_regs_map[2], tg_timer1_regs_map[3]), + .owner = ENTRY(0) + }, +}; + +const regdma_entries_config_t tg1_timer0_regdma_entries[] = { + // backup stage: trigger a soft capture + [0] = { + .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG1_TIMER_LINK(0x00), + TIMG_T0UPDATE_REG(1), TIMG_T0_UPDATE, TIMG_T0_UPDATE_M, 0, 1), + .owner = ENTRY(0) + }, + // backup stage: wait for the capture done + [1] = { + .config = REGDMA_LINK_WAIT_INIT(REGDMA_TG1_TIMER_LINK(0x01), + TIMG_T0UPDATE_REG(1), 0x0, TIMG_T0_UPDATE_M, 0, 1), + .owner = ENTRY(0) + }, + // backup stage: save the captured counter value + // restore stage: store the captured counter value to the loader register + [2] = { + .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_TG1_TIMER_LINK(0x02), + TIMG_T0LO_REG(1), TIMG_T0LOADLO_REG(1), 2, 0, 0), + .owner = ENTRY(0) + }, + // restore stage: trigger a soft reload, so the timer can continue from where it was backed up + [3] = { + .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG1_TIMER_LINK(0x03), + TIMG_T0LOAD_REG(1), 0x1, TIMG_T0_LOAD_M, 1, 0), + .owner = ENTRY(0) + }, + // backup stage: save other configuration and status registers + // restore stage: restore the configuration and status registers + [4] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_TG1_TIMER_LINK(0x04), + TG1_TIMER0_RETENTION_REGS_BASE, TG1_TIMER0_RETENTION_REGS_BASE, + TG_TIMER0_RETENTION_REGS_CNT, 0, 0, + tg_timer0_regs_map[0], tg_timer0_regs_map[1], + tg_timer0_regs_map[2], tg_timer0_regs_map[3]), + .owner = ENTRY(0) + }, +}; + +const regdma_entries_config_t tg1_timer1_regdma_entries[] = { + // backup stage: trigger a soft capture + [0] = { + .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG1_TIMER_LINK(0x05), + TIMG_T1UPDATE_REG(1), TIMG_T1_UPDATE, TIMG_T1_UPDATE_M, 0, 1), + .owner = ENTRY(0) + }, + // backup stage: wait for the capture done + [1] = { + .config = REGDMA_LINK_WAIT_INIT(REGDMA_TG1_TIMER_LINK(0x06), + TIMG_T1UPDATE_REG(1), 0x0, TIMG_T1_UPDATE_M, 0, 1), + .owner = ENTRY(0) + }, + // backup stage: save the captured counter value + // restore stage: store the captured counter value to the loader register + [2] = { + .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_TG1_TIMER_LINK(0x07), + TIMG_T1LO_REG(1), TIMG_T1LOADLO_REG(1), 2, 0, 0), + .owner = ENTRY(0) + }, + // restore stage: trigger a soft reload, so the timer can continue from where it was backed up + [3] = { + .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG1_TIMER_LINK(0x08), + TIMG_T1LOAD_REG(1), 0x1, TIMG_T1_LOAD_M, 1, 0), + .owner = ENTRY(0) + }, + // backup stage: save other configuration and status registers + // restore stage: restore the configuration and status registers + [4] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_TG1_TIMER_LINK(0x09), + TG1_TIMER1_RETENTION_REGS_BASE, TG1_TIMER1_RETENTION_REGS_BASE, + TG_TIMER1_RETENTION_REGS_CNT, 0, 0, + tg_timer1_regs_map[0], tg_timer1_regs_map[1], + tg_timer1_regs_map[2], tg_timer1_regs_map[3]), + .owner = ENTRY(0) + }, +}; + +const soc_timg_gptimer_retention_desc_t soc_timg_gptimer_retention_infos[2][2] = { + [0] = { + [0] = { + .module = SLEEP_RETENTION_MODULE_TG0_TIMER0, + .regdma_entry_array = tg0_timer0_regdma_entries, + .array_size = ARRAY_SIZE(tg0_timer0_regdma_entries) + }, + [1] = { + .module = SLEEP_RETENTION_MODULE_TG0_TIMER1, + .regdma_entry_array = tg0_timer1_regdma_entries, + .array_size = ARRAY_SIZE(tg0_timer1_regdma_entries) + }, + }, + [1] = { + [0] = { + .module = SLEEP_RETENTION_MODULE_TG1_TIMER0, + .regdma_entry_array = tg1_timer0_regdma_entries, + .array_size = ARRAY_SIZE(tg1_timer0_regdma_entries) + }, + [1] = { + .module = SLEEP_RETENTION_MODULE_TG1_TIMER1, + .regdma_entry_array = tg1_timer1_regdma_entries, + .array_size = ARRAY_SIZE(tg1_timer1_regdma_entries) + }, + }, +}; +#endif // SOC_PAU_SUPPORTED diff --git a/components/esp_hw_support/port/esp32s31/esp_clk_tree.c b/components/esp_hw_support/port/esp32s31/esp_clk_tree.c index 068e3e2d93..f2aa1711c4 100644 --- a/components/esp_hw_support/port/esp32s31/esp_clk_tree.c +++ b/components/esp_hw_support/port/esp32s31/esp_clk_tree.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,7 +14,7 @@ #include "esp_private/esp_clk_tree_common.h" #include "esp_private/periph_ctrl.h" -static const char *TAG = "esp_clk_tree"; +ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree"); /* TODO: [ESP32S31] IDF-14733 */ @@ -30,15 +30,15 @@ esp_err_t esp_clk_tree_src_get_freq_hz(soc_module_clk_t clk_src, esp_clk_tree_sr ESP_RETURN_ON_FALSE(precision < ESP_CLK_TREE_SRC_FREQ_PRECISION_INVALID, ESP_ERR_INVALID_ARG, TAG, "unknown precision"); ESP_RETURN_ON_FALSE(freq_value, ESP_ERR_INVALID_ARG, TAG, "null pointer"); -#if SOC_CLK_TREE_SUPPORTED - uint32_t clk_src_freq = 0; switch (clk_src) { +#if SOC_CLK_TREE_SUPPORTED case SOC_MOD_CLK_CPU: clk_src_freq = clk_hal_cpu_get_freq_hz(); break; +#endif // SOC_CLK_TREE_SUPPORTED case SOC_MOD_CLK_XTAL: - clk_src_freq = clk_hal_xtal_get_freq_mhz() * MHZ; + clk_src_freq = SOC_XTAL_FREQ_40M * MHZ; break; case SOC_MOD_CLK_PLL_F20M: clk_src_freq = CLK_LL_PLL_480M_FREQ_MHZ / clk_ll_pll_f20m_get_divider() * MHZ; @@ -52,6 +52,7 @@ esp_err_t esp_clk_tree_src_get_freq_hz(soc_module_clk_t clk_src, esp_clk_tree_sr case SOC_MOD_CLK_PLL_F240M: clk_src_freq = CLK_LL_PLL_240M_FREQ_MHZ * MHZ; break; +#if SOC_CLK_TREE_SUPPORTED case SOC_MOD_CLK_CPLL: clk_src_freq = clk_ll_cpll_get_freq_mhz(clk_hal_xtal_get_freq_mhz()) * MHZ; break; @@ -84,6 +85,7 @@ esp_err_t esp_clk_tree_src_get_freq_hz(soc_module_clk_t clk_src, esp_clk_tree_sr case SOC_MOD_CLK_LP_PLL: clk_src_freq = clk_ll_lp_pll_get_freq_mhz() * MHZ; break; +#endif // SOC_CLK_TREE_SUPPORTED default: break; } @@ -91,7 +93,7 @@ esp_err_t esp_clk_tree_src_get_freq_hz(soc_module_clk_t clk_src, esp_clk_tree_sr ESP_RETURN_ON_FALSE(clk_src_freq, ESP_FAIL, TAG, "freq shouldn't be 0, calibration failed"); *freq_value = clk_src_freq; -#endif // SOC_CLK_TREE_SUPPORTED + return ESP_OK; } diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index da34000e54..543475a83a 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -7,6 +7,10 @@ config SOC_UART_SUPPORTED bool default y +config SOC_GPTIMER_SUPPORTED + bool + default y + config SOC_EFUSE_KEY_PURPOSE_FIELD bool default y @@ -271,6 +275,14 @@ config SOC_SYSTIMER_ALARM_MISS_COMPENSATE bool default y +config SOC_TIMER_SUPPORT_ETM + bool + default y + +config SOC_TIMER_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_MWDT_SUPPORT_XTAL bool default y @@ -382,3 +394,7 @@ config SOC_CLK_LP_FAST_SUPPORT_XTAL config SOC_CLK_ANA_I2C_MST_HAS_ROOT_GATE bool default y + +config SOC_PERIPH_CLK_CTRL_SHARED + bool + default y diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index 15c61fa92b..32a1bcbde2 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -33,7 +33,7 @@ // #define SOC_AXI_GDMA_SUPPORTED 1 // TODO: [ESP32S31] IDF-14758 // #define SOC_DW_GDMA_SUPPORTED 1 // TODO: [ESP32S31] IDF-14758 // #define SOC_DMA2D_SUPPORTED 1 // TODO: [ESP32S31] IDF-14762 -// #define SOC_GPTIMER_SUPPORTED 1 // TODO: [ESP32S31] IDF-14745 +#define SOC_GPTIMER_SUPPORTED 1 // #define SOC_PCNT_SUPPORTED 1 // TODO: [ESP32S31] IDF-14699 // #define SOC_LCDCAM_SUPPORTED 1 // TODO: [ESP32S31] IDF-14722 // #define SOC_LCDCAM_CAM_SUPPORTED 1 // TODO: [ESP32S31] IDF-14722 @@ -216,6 +216,10 @@ #define SOC_SYSTIMER_INT_LEVEL 1 // Systimer peripheral uses level interrupt #define SOC_SYSTIMER_ALARM_MISS_COMPENSATE 1 // Systimer peripheral can generate interrupt immediately if t(target) > t(current) +/*--------------------------- TIMER GROUP CAPS ---------------------------------------*/ +#define SOC_TIMER_SUPPORT_ETM (1) +#define SOC_TIMER_SUPPORT_SLEEP_RETENTION (1) + /*--------------------------- WATCHDOG CAPS ---------------------------------------*/ // TODO: [ESP32S31] IDF-14656 #define SOC_MWDT_SUPPORT_XTAL (1) @@ -271,3 +275,5 @@ #define SOC_CLK_LP_FAST_SUPPORT_XTAL (1) /*!< Support XTAL clock as the LP_FAST clock source */ #define SOC_CLK_ANA_I2C_MST_HAS_ROOT_GATE (1) /*!< Any regi2c operation needs enable the analog i2c master clock first */ + +#define SOC_PERIPH_CLK_CTRL_SHARED (1) /*!< Peripheral clock control (e.g. set clock source) is shared between various peripherals */ diff --git a/examples/peripherals/timer_group/gptimer/README.md b/examples/peripherals/timer_group/gptimer/README.md index 5c43d9a496..80fe76e4ca 100644 --- a/examples/peripherals/timer_group/gptimer/README.md +++ b/examples/peripherals/timer_group/gptimer/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | # Example: General Purpose Timer diff --git a/examples/peripherals/timer_group/wiegand_interface/README.md b/examples/peripherals/timer_group/wiegand_interface/README.md index 0c6f3efa26..77fb5251d3 100644 --- a/examples/peripherals/timer_group/wiegand_interface/README.md +++ b/examples/peripherals/timer_group/wiegand_interface/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | # Wiegand Interface Example diff --git a/examples/system/sysview_tracing/README.md b/examples/system/sysview_tracing/README.md index 22a208cab6..78ac4942b6 100644 --- a/examples/system/sysview_tracing/README.md +++ b/examples/system/sysview_tracing/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | # Example: Application Level Tracing - SystemView Tracing (sysview_tracing) This test code shows how to perform system-wide behavioral analysis of the program using [SEGGER SystemView tool](https://www.segger.com/products/development-tools/systemview/). diff --git a/examples/system/sysview_tracing_heap_log/README.md b/examples/system/sysview_tracing_heap_log/README.md index 050a5f5fbb..d0cce54afc 100644 --- a/examples/system/sysview_tracing_heap_log/README.md +++ b/examples/system/sysview_tracing_heap_log/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | # SystemView Heap and Log Tracing Example