mirror of
https://github.com/espressif/esp-idf.git
synced 2025-07-30 10:47:19 +02:00
fix(esp_hw_support): update LACT clock prescale immediately when APB changes on esp32
This commit is contained in:
@ -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
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -7,6 +7,24 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#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
|
// we assign the systimer resources statically
|
||||||
#define SYSTIMER_COUNTER_ESPTIMER 0 // Counter used by esptimer, to generate the system level wall clock
|
#define SYSTIMER_COUNTER_ESPTIMER 0 // Counter used by esptimer, to generate the system level wall clock
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include "esp_attr.h"
|
||||||
#include "soc/rtc.h"
|
#include "soc/rtc.h"
|
||||||
#include "esp_private/rtc_clk.h"
|
#include "esp_private/rtc_clk.h"
|
||||||
#include "soc/rtc_periph.h"
|
#include "soc/rtc_periph.h"
|
||||||
@ -26,6 +27,10 @@
|
|||||||
#include "hal/clk_tree_ll.h"
|
#include "hal/clk_tree_ll.h"
|
||||||
#include "soc/rtc_cntl_reg.h"
|
#include "soc/rtc_cntl_reg.h"
|
||||||
#include "soc/io_mux_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
|
#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);
|
clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_XTAL, cpu_freq);
|
||||||
/* switch clock source */
|
/* switch clock source */
|
||||||
clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_XTAL);
|
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);
|
rtc_clk_apb_freq_update(cpu_freq * MHZ);
|
||||||
/* lower the voltage */
|
/* lower the voltage */
|
||||||
int dbias = (cpu_freq <= 2) ? DIG_DBIAS_2M : DIG_DBIAS_XTAL;
|
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);
|
clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_RC_FAST, 8);
|
||||||
/* switch clock source */
|
/* switch clock source */
|
||||||
clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_RC_FAST);
|
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);
|
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)
|
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;
|
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);
|
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 */
|
/* adjust ref_tick */
|
||||||
clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_PLL, cpu_freq_mhz);
|
clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_PLL, cpu_freq_mhz);
|
||||||
/* switch clock source */
|
/* switch clock source */
|
||||||
|
@ -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)
|
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 old_apb_ticks_per_us = MIN(old_ticks_per_us, 80);
|
||||||
uint32_t apb_ticks_per_us = MIN(ticks_per_us, 80);
|
uint32_t apb_ticks_per_us = MIN(ticks_per_us, 80);
|
||||||
/* Update APB frequency value used by the timer */
|
/* Update APB frequency value used by the timer */
|
||||||
if (old_apb_ticks_per_us != apb_ticks_per_us) {
|
if (old_apb_ticks_per_us != apb_ticks_per_us) {
|
||||||
esp_timer_private_update_apb_freq(apb_ticks_per_us);
|
esp_timer_private_update_apb_freq(apb_ticks_per_us);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
|
#ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
|
||||||
#ifdef XT_RTOS_TIMER_INT
|
#ifdef XT_RTOS_TIMER_INT
|
||||||
|
@ -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
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -15,6 +15,7 @@
|
|||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_private/esp_clk.h"
|
#include "esp_private/esp_clk.h"
|
||||||
#include "esp_private/periph_ctrl.h"
|
#include "esp_private/periph_ctrl.h"
|
||||||
|
#include "esp_private/systimer.h"
|
||||||
#include "soc/soc.h"
|
#include "soc/soc.h"
|
||||||
#include "soc/timer_group_reg.h"
|
#include "soc/timer_group_reg.h"
|
||||||
#include "soc/rtc.h"
|
#include "soc/rtc.h"
|
||||||
@ -31,9 +32,6 @@
|
|||||||
* The timer can be configured to produce an edge or a level interrupt.
|
* 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
|
#if LACT_MODULE == 0
|
||||||
#define INTR_SOURCE_LACT ETS_TG0_LACT_LEVEL_INTR_SOURCE
|
#define INTR_SOURCE_LACT ETS_TG0_LACT_LEVEL_INTR_SOURCE
|
||||||
#define PERIPH_LACT PERIPH_TIMG0_MODULE
|
#define PERIPH_LACT PERIPH_TIMG0_MODULE
|
||||||
@ -44,18 +42,6 @@
|
|||||||
#error "Incorrect the number of LACT module (only 0 or 1)"
|
#error "Incorrect the number of LACT module (only 0 or 1)"
|
||||||
#endif
|
#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 */
|
/* Shorter register names, used in this file */
|
||||||
#define CONFIG_REG (TIMG_LACTCONFIG_REG(LACT_MODULE))
|
#define CONFIG_REG (TIMG_LACTCONFIG_REG(LACT_MODULE))
|
||||||
#define RTC_STEP_REG (TIMG_LACTRTC_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)
|
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")));
|
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_id[alarm_id] = timestamp;
|
||||||
timestamp = MIN(timestamp_id[0], timestamp_id[1]);
|
timestamp = MIN(timestamp_id[0], timestamp_id[1]);
|
||||||
if (timestamp != UINT64_MAX) {
|
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();
|
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 {
|
do {
|
||||||
REG_CLR_BIT(CONFIG_REG, TIMG_LACT_ALARM_EN);
|
REG_CLR_BIT(CONFIG_REG, TIMG_LACT_ALARM_EN);
|
||||||
REG_WRITE(ALARM_LO_REG, alarm.lo);
|
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;
|
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) {
|
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
|
// 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;
|
alarm.val = now_time + offset;
|
||||||
} else {
|
} else {
|
||||||
// finish if either (alarm > counter) or the interrupt flag is already set.
|
// 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
|
#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)
|
void esp_timer_impl_set(uint64_t new_us)
|
||||||
{
|
{
|
||||||
portENTER_CRITICAL(&s_time_update_lock);
|
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_LO_REG, dst.lo);
|
||||||
REG_WRITE(LOAD_HI_REG, dst.hi);
|
REG_WRITE(LOAD_HI_REG, dst.hi);
|
||||||
REG_WRITE(LOAD_REG, 1);
|
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(ALARM_HI_REG, UINT32_MAX);
|
||||||
REG_WRITE(LOAD_REG, 1);
|
REG_WRITE(LOAD_REG, 1);
|
||||||
REG_SET_BIT(INT_CLR_REG, TIMG_LACT_INT_CLR);
|
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 |
|
REG_SET_BIT(CONFIG_REG, TIMG_LACT_INCREASE |
|
||||||
TIMG_LACT_LEVEL_INT_EN |
|
TIMG_LACT_LEVEL_INT_EN |
|
||||||
TIMG_LACT_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.
|
* will not cause issues in practice.
|
||||||
*/
|
*/
|
||||||
REG_SET_BIT(INT_ENA_REG, TIMG_LACT_INT_ENA);
|
REG_SET_BIT(INT_ENA_REG, TIMG_LACT_INT_ENA);
|
||||||
|
timer_ll_set_lact_clock_prescale(TIMER_LL_GET_HW(LACT_MODULE), esp_clk_apb_freq() / MHZ / LACT_TICKS_PER_US);
|
||||||
esp_timer_impl_update_apb_freq(esp_clk_apb_freq() / 1000000);
|
|
||||||
|
|
||||||
// Set the step for the sleep mode when the timer will work
|
// Set the step for the sleep mode when the timer will work
|
||||||
// from a slow_clk frequency instead of the APB frequency.
|
// 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);
|
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;
|
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_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")));
|
void esp_timer_private_advance(int64_t time_diff_us) __attribute__((alias("esp_timer_impl_advance")));
|
||||||
|
@ -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
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -9,6 +9,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include "esp_attr.h"
|
||||||
#include "hal/assert.h"
|
#include "hal/assert.h"
|
||||||
#include "hal/misc.h"
|
#include "hal/misc.h"
|
||||||
#include "hal/timer_types.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;
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user