diff --git a/components/esp_hw_support/include/esp_private/systimer.h b/components/esp_hw_support/include/esp_private/systimer.h index 068b3fa91c..38debe6d41 100644 --- a/components/esp_hw_support/include/esp_private/systimer.h +++ b/components/esp_hw_support/include/esp_private/systimer.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,6 +7,24 @@ #pragma once #include +#include "sdkconfig.h" + +#if CONFIG_ESP_TIMER_IMPL_TG0_LAC +/* Selects which Timer Group peripheral to use */ +#define LACT_MODULE 0 + +/* Desired number of timer ticks per microsecond. + * This value should be small enough so that all possible APB frequencies + * could be divided by it without remainder. + * On the other hand, the smaller this value is, the longer we need to wait + * after setting UPDATE_REG before the timer value can be read. + * If LACT_TICKS_PER_US == 1, then we need to wait up to 1 microsecond, which + * makes esp_timer_impl_get_time function take too much time. + * The value LACT_TICKS_PER_US == 2 allows for most of the APB frequencies, and + * allows reading the counter quickly enough. + */ +#define LACT_TICKS_PER_US 2 +#endif // we assign the systimer resources statically #define SYSTIMER_COUNTER_ESPTIMER 0 // Counter used by esptimer, to generate the system level wall clock diff --git a/components/esp_hw_support/port/esp32/rtc_clk.c b/components/esp_hw_support/port/esp32/rtc_clk.c index 79c458ee9c..bc07109686 100644 --- a/components/esp_hw_support/port/esp32/rtc_clk.c +++ b/components/esp_hw_support/port/esp32/rtc_clk.c @@ -8,6 +8,7 @@ #include #include #include +#include "esp_attr.h" #include "soc/rtc.h" #include "esp_private/rtc_clk.h" #include "soc/rtc_periph.h" @@ -26,6 +27,10 @@ #include "hal/clk_tree_ll.h" #include "soc/rtc_cntl_reg.h" #include "soc/io_mux_reg.h" +#ifndef BOOTLOADER_BUILD +#include "esp_private/systimer.h" +#include "hal/timer_ll.h" +#endif #define XTAL_32K_BOOTSTRAP_TIME_US 7 @@ -365,6 +370,9 @@ void rtc_clk_cpu_freq_to_xtal(int cpu_freq, int div) clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_XTAL, cpu_freq); /* switch clock source */ clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_XTAL); +#ifndef BOOTLOADER_BUILD + timer_ll_set_lact_clock_prescale(TIMER_LL_GET_HW(LACT_MODULE), cpu_freq / LACT_TICKS_PER_US); +#endif rtc_clk_apb_freq_update(cpu_freq * MHZ); /* lower the voltage */ int dbias = (cpu_freq <= 2) ? DIG_DBIAS_2M : DIG_DBIAS_XTAL; @@ -380,6 +388,9 @@ static void rtc_clk_cpu_freq_to_8m(void) clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_RC_FAST, 8); /* switch clock source */ clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_RC_FAST); +#ifndef BOOTLOADER_BUILD + timer_ll_set_lact_clock_prescale(TIMER_LL_GET_HW(LACT_MODULE), SOC_CLK_RC_FAST_FREQ_APPROX / MHZ / LACT_TICKS_PER_US); +#endif rtc_clk_apb_freq_update(SOC_CLK_RC_FAST_FREQ_APPROX); } @@ -391,8 +402,11 @@ static void rtc_clk_cpu_freq_to_8m(void) static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz) { int dbias = (cpu_freq_mhz == 240) ? DIG_DBIAS_240M : DIG_DBIAS_80M_160M; - clk_ll_cpu_set_freq_mhz_from_pll(cpu_freq_mhz); REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, dbias); +#ifndef BOOTLOADER_BUILD + timer_ll_set_lact_clock_prescale(TIMER_LL_GET_HW(LACT_MODULE), 80 / LACT_TICKS_PER_US); +#endif + clk_ll_cpu_set_freq_mhz_from_pll(cpu_freq_mhz); /* adjust ref_tick */ clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_PLL, cpu_freq_mhz); /* switch clock source */ diff --git a/components/esp_pm/pm_impl.c b/components/esp_pm/pm_impl.c index a352d176c8..53857a86d1 100644 --- a/components/esp_pm/pm_impl.c +++ b/components/esp_pm/pm_impl.c @@ -555,12 +555,14 @@ void IRAM_ATTR esp_pm_impl_switch_mode(pm_mode_t mode, */ static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_per_us) { +#if !CONFIG_IDF_TARGET_ESP32 uint32_t old_apb_ticks_per_us = MIN(old_ticks_per_us, 80); uint32_t apb_ticks_per_us = MIN(ticks_per_us, 80); /* Update APB frequency value used by the timer */ if (old_apb_ticks_per_us != apb_ticks_per_us) { esp_timer_private_update_apb_freq(apb_ticks_per_us); } +#endif #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT #ifdef XT_RTOS_TIMER_INT diff --git a/components/esp_timer/src/esp_timer_impl_lac.c b/components/esp_timer/src/esp_timer_impl_lac.c index 294c6b5662..772e39a34e 100644 --- a/components/esp_timer/src/esp_timer_impl_lac.c +++ b/components/esp_timer/src/esp_timer_impl_lac.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -15,6 +15,7 @@ #include "esp_log.h" #include "esp_private/esp_clk.h" #include "esp_private/periph_ctrl.h" +#include "esp_private/systimer.h" #include "soc/soc.h" #include "soc/timer_group_reg.h" #include "soc/rtc.h" @@ -31,9 +32,6 @@ * The timer can be configured to produce an edge or a level interrupt. */ -/* Selects which Timer Group peripheral to use */ -#define LACT_MODULE 0 - #if LACT_MODULE == 0 #define INTR_SOURCE_LACT ETS_TG0_LACT_LEVEL_INTR_SOURCE #define PERIPH_LACT PERIPH_TIMG0_MODULE @@ -44,18 +42,6 @@ #error "Incorrect the number of LACT module (only 0 or 1)" #endif -/* Desired number of timer ticks per microsecond. - * This value should be small enough so that all possible APB frequencies - * could be divided by it without remainder. - * On the other hand, the smaller this value is, the longer we need to wait - * after setting UPDATE_REG before the timer value can be read. - * If TICKS_PER_US == 1, then we need to wait up to 1 microsecond, which - * makes esp_timer_impl_get_time function take too much time. - * The value TICKS_PER_US == 2 allows for most of the APB frequencies, and - * allows reading the counter quickly enough. - */ -#define TICKS_PER_US 2 - /* Shorter register names, used in this file */ #define CONFIG_REG (TIMG_LACTCONFIG_REG(LACT_MODULE)) #define RTC_STEP_REG (TIMG_LACTRTC_REG(LACT_MODULE)) @@ -138,7 +124,7 @@ uint64_t IRAM_ATTR esp_timer_impl_get_counter_reg(void) int64_t IRAM_ATTR esp_timer_impl_get_time(void) { - return esp_timer_impl_get_counter_reg() / TICKS_PER_US; + return esp_timer_impl_get_counter_reg() / LACT_TICKS_PER_US; } int64_t esp_timer_get_time(void) __attribute__((alias("esp_timer_impl_get_time"))); @@ -150,9 +136,9 @@ void IRAM_ATTR esp_timer_impl_set_alarm_id(uint64_t timestamp, unsigned alarm_id timestamp_id[alarm_id] = timestamp; timestamp = MIN(timestamp_id[0], timestamp_id[1]); if (timestamp != UINT64_MAX) { - int64_t offset = TICKS_PER_US * 2; + int64_t offset = LACT_TICKS_PER_US * 2; uint64_t now_time = esp_timer_impl_get_counter_reg(); - timer_64b_reg_t alarm = { .val = MAX(timestamp * TICKS_PER_US, now_time + offset) }; + timer_64b_reg_t alarm = { .val = MAX(timestamp * LACT_TICKS_PER_US, now_time + offset) }; do { REG_CLR_BIT(CONFIG_REG, TIMG_LACT_ALARM_EN); REG_WRITE(ALARM_LO_REG, alarm.lo); @@ -162,7 +148,7 @@ void IRAM_ATTR esp_timer_impl_set_alarm_id(uint64_t timestamp, unsigned alarm_id int64_t delta = (int64_t)alarm.val - (int64_t)now_time; if (delta <= 0 && REG_GET_FIELD(INT_ST_REG, TIMG_LACT_INT_ST) == 0) { // new alarm is less than the counter and the interrupt flag is not set - offset += llabs(delta) + TICKS_PER_US * 2; + offset += llabs(delta) + LACT_TICKS_PER_US * 2; alarm.val = now_time + offset; } else { // finish if either (alarm > counter) or the interrupt flag is already set. @@ -219,19 +205,10 @@ static void IRAM_ATTR timer_alarm_isr(void *arg) #endif // ISR_HANDLERS != 1 } -void IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us) -{ - portENTER_CRITICAL(&s_time_update_lock); - assert(apb_ticks_per_us >= 3 && "divider value too low"); - assert(apb_ticks_per_us % TICKS_PER_US == 0 && "APB frequency (in MHz) should be divisible by TICK_PER_US"); - REG_SET_FIELD(CONFIG_REG, TIMG_LACT_DIVIDER, apb_ticks_per_us / TICKS_PER_US); - portEXIT_CRITICAL(&s_time_update_lock); -} - void esp_timer_impl_set(uint64_t new_us) { portENTER_CRITICAL(&s_time_update_lock); - timer_64b_reg_t dst = { .val = new_us * TICKS_PER_US }; + timer_64b_reg_t dst = { .val = new_us * LACT_TICKS_PER_US }; REG_WRITE(LOAD_LO_REG, dst.lo); REG_WRITE(LOAD_HI_REG, dst.hi); REG_WRITE(LOAD_REG, 1); @@ -260,7 +237,7 @@ esp_err_t esp_timer_impl_early_init(void) REG_WRITE(ALARM_HI_REG, UINT32_MAX); REG_WRITE(LOAD_REG, 1); REG_SET_BIT(INT_CLR_REG, TIMG_LACT_INT_CLR); - REG_SET_FIELD(CONFIG_REG, TIMG_LACT_DIVIDER, APB_CLK_FREQ / 1000000 / TICKS_PER_US); + REG_SET_FIELD(CONFIG_REG, TIMG_LACT_DIVIDER, APB_CLK_FREQ / 1000000 / LACT_TICKS_PER_US); REG_SET_BIT(CONFIG_REG, TIMG_LACT_INCREASE | TIMG_LACT_LEVEL_INT_EN | TIMG_LACT_EN); @@ -295,12 +272,10 @@ esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler) * will not cause issues in practice. */ REG_SET_BIT(INT_ENA_REG, TIMG_LACT_INT_ENA); - - esp_timer_impl_update_apb_freq(esp_clk_apb_freq() / 1000000); - + timer_ll_set_lact_clock_prescale(TIMER_LL_GET_HW(LACT_MODULE), esp_clk_apb_freq() / MHZ / LACT_TICKS_PER_US); // Set the step for the sleep mode when the timer will work // from a slow_clk frequency instead of the APB frequency. - uint32_t slowclk_ticks_per_us = esp_clk_slowclk_cal_get() * TICKS_PER_US; + uint32_t slowclk_ticks_per_us = esp_clk_slowclk_cal_get() * LACT_TICKS_PER_US; REG_SET_FIELD(RTC_STEP_REG, TIMG_LACT_RTC_STEP_LEN, slowclk_ticks_per_us); } @@ -343,6 +318,5 @@ uint64_t esp_timer_impl_get_alarm_reg(void) return alarm.val; } -void esp_timer_private_update_apb_freq(uint32_t apb_ticks_per_us) __attribute__((alias("esp_timer_impl_update_apb_freq"))); void esp_timer_private_set(uint64_t new_us) __attribute__((alias("esp_timer_impl_set"))); void esp_timer_private_advance(int64_t time_diff_us) __attribute__((alias("esp_timer_impl_advance"))); diff --git a/components/hal/esp32/include/hal/timer_ll.h b/components/hal/esp32/include/hal/timer_ll.h index 4a52141152..35c1a2a1f9 100644 --- a/components/hal/esp32/include/hal/timer_ll.h +++ b/components/hal/esp32/include/hal/timer_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,6 +9,7 @@ #pragma once #include +#include "esp_attr.h" #include "hal/assert.h" #include "hal/misc.h" #include "hal/timer_types.h" @@ -328,6 +329,19 @@ static inline volatile void *timer_ll_get_intr_status_reg(timg_dev_t *hw) return &hw->int_st_timers.val; } +/** + * @brief Set clock prescale for LACT 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) + */ +FORCE_INLINE_ATTR void timer_ll_set_lact_clock_prescale(timg_dev_t *hw, uint32_t divider) +{ + HAL_ASSERT(divider>=2); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->lactconfig, lact_divider, divider); +} + #ifdef __cplusplus } #endif