Skip to content

Commit

Permalink
Merge branch 'feature/gptimer_ll_enable_reset' into 'master'
Browse files Browse the repository at this point in the history
HW-Support: Add Atomic Code Block for Reset and Clock Control

See merge request espressif/esp-idf!25401
  • Loading branch information
suda-morris committed Aug 23, 2023
2 parents 7af7723 + 4e143ad commit 3b50c71
Show file tree
Hide file tree
Showing 39 changed files with 785 additions and 160 deletions.
32 changes: 28 additions & 4 deletions components/driver/deprecated/timer_legacy.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ static const char *TIMER_TAG = "timer_group";
#define TIMER_ENTER_CRITICAL(mux) portENTER_CRITICAL_SAFE(mux);
#define TIMER_EXIT_CRITICAL(mux) portEXIT_CRITICAL_SAFE(mux);

#if CONFIG_IDF_TARGET_ESP32P4
#define GPTIMER_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define GPTIMER_CLOCK_SRC_ATOMIC()
#endif

typedef struct {
timer_isr_t fn; /*!< isr function */
void *args; /*!< isr function args */
Expand Down Expand Up @@ -305,7 +311,12 @@ esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, const timer
}
timer_hal_context_t *hal = &p_timer_obj[group_num][timer_num]->hal;

periph_module_enable(timer_group_periph_signals.groups[group_num].module);
PERIPH_RCC_ACQUIRE_ATOMIC(timer_group_periph_signals.groups[group_num].module, ref_count) {
if (ref_count == 0) {
timer_ll_enable_bus_clock(group_num, true);
timer_ll_reset_register(group_num);
}
}

TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
timer_hal_init(hal, group_num, timer_num);
Expand All @@ -315,9 +326,12 @@ esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, const timer
if (config->clk_src) {
clk_src = config->clk_src;
}
// although `clk_src` is of `timer_src_clk_t` type, but it's binary compatible with `gptimer_clock_source_t`,
// as the underlying enum entries come from the same `soc_module_clk_t`
timer_ll_set_clock_source(p_timer_obj[group_num][timer_num]->hal.dev, timer_num, (gptimer_clock_source_t)clk_src);
GPTIMER_CLOCK_SRC_ATOMIC() {
// although `clk_src` is of `timer_src_clk_t` type, but it's binary compatible with `gptimer_clock_source_t`,
// as the underlying enum entries come from the same `soc_module_clk_t`
timer_ll_set_clock_source(p_timer_obj[group_num][timer_num]->hal.dev, timer_num, (gptimer_clock_source_t)clk_src);
timer_ll_enable_clock(hal->dev, timer_num, true);
}
timer_ll_set_clock_prescale(hal->dev, timer_num, config->divider);
timer_ll_set_count_direction(p_timer_obj[group_num][timer_num]->hal.dev, timer_num, config->counter_dir);
timer_ll_enable_intr(hal->dev, TIMER_LL_EVENT_ALARM(timer_num), false);
Expand All @@ -343,12 +357,22 @@ esp_err_t timer_deinit(timer_group_t group_num, timer_idx_t timer_num)
ESP_RETURN_ON_FALSE(p_timer_obj[group_num][timer_num] != NULL, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_NEVER_INIT_ERROR);
timer_hal_context_t *hal = &p_timer_obj[group_num][timer_num]->hal;

// disable the source clock
GPTIMER_CLOCK_SRC_ATOMIC() {
timer_ll_enable_clock(hal->dev, hal->timer_id, false);
}
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
timer_ll_enable_intr(hal->dev, TIMER_LL_EVENT_ALARM(timer_num), false);
timer_ll_clear_intr_status(hal->dev, TIMER_LL_EVENT_ALARM(timer_num));
timer_hal_deinit(hal);
TIMER_EXIT_CRITICAL(&timer_spinlock[group_num]);

PERIPH_RCC_RELEASE_ATOMIC(timer_group_periph_signals.groups[group_num].module, ref_count) {
if (ref_count == 0) {
timer_ll_enable_bus_clock(group_num, false);
}
}

free(p_timer_obj[group_num][timer_num]);
p_timer_obj[group_num][timer_num] = NULL;

Expand Down
41 changes: 35 additions & 6 deletions components/driver/gptimer/gptimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@

static const char *TAG = "gptimer";

#if CONFIG_IDF_TARGET_ESP32P4
#define GPTIMER_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define GPTIMER_CLOCK_SRC_ATOMIC()
#endif

typedef struct gptimer_platform_t {
_lock_t mutex; // platform level mutex lock
gptimer_group_t *groups[SOC_TIMER_GROUPS]; // timer group pool
Expand Down Expand Up @@ -163,8 +169,13 @@ esp_err_t gptimer_del_timer(gptimer_handle_t timer)
gptimer_clock_source_t clk_src = timer->clk_src;
int group_id = group->group_id;
int timer_id = timer->timer_id;
timer_hal_context_t *hal = &timer->hal;
ESP_LOGD(TAG, "del timer (%d,%d)", group_id, timer_id);
timer_hal_deinit(&timer->hal);
// disable the source clock
GPTIMER_CLOCK_SRC_ATOMIC() {
timer_ll_enable_clock(hal->dev, hal->timer_id, false);
}
timer_hal_deinit(hal);
// recycle memory resource
ESP_RETURN_ON_ERROR(gptimer_destroy(timer), TAG, "destroy gptimer failed");

Expand Down Expand Up @@ -382,8 +393,6 @@ static gptimer_group_t *gptimer_acquire_group_handle(int group_id)
// initialize timer group members
group->group_id = group_id;
group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
// enable APB access timer registers
periph_module_enable(timer_group_periph_signals.groups[group_id].module);
}
} else {
group = s_platform.groups[group_id];
Expand All @@ -395,6 +404,15 @@ static gptimer_group_t *gptimer_acquire_group_handle(int group_id)
_lock_release(&s_platform.mutex);

if (new_group) {
// !!! HARDWARE SHARED RESOURCE !!!
// the gptimer and watchdog reside in the same the timer group
// we need to increase/decrease the reference count before enable/disable/reset the peripheral
PERIPH_RCC_ACQUIRE_ATOMIC(timer_group_periph_signals.groups[group_id].module, ref_count) {
if (ref_count == 0) {
timer_ll_enable_bus_clock(group_id, true);
timer_ll_reset_register(group_id);
}
}
ESP_LOGD(TAG, "new group (%d) @%p", group_id, group);
}

Expand All @@ -412,11 +430,16 @@ static void gptimer_release_group_handle(gptimer_group_t *group)
assert(s_platform.groups[group_id]);
do_deinitialize = true;
s_platform.groups[group_id] = NULL;
periph_module_disable(timer_group_periph_signals.groups[group_id].module);
}
_lock_release(&s_platform.mutex);

if (do_deinitialize) {
// disable bus clock for the timer group
PERIPH_RCC_RELEASE_ATOMIC(timer_group_periph_signals.groups[group_id].module, ref_count) {
if (ref_count == 0) {
timer_ll_enable_bus_clock(group_id, false);
}
}
free(group);
ESP_LOGD(TAG, "del group (%d)", group_id);
}
Expand Down Expand Up @@ -476,9 +499,15 @@ static esp_err_t gptimer_select_periph_clock(gptimer_t *timer, gptimer_clock_sou
}
#endif // CONFIG_PM_ENABLE

timer_ll_set_clock_source(timer->hal.dev, timer_id, src_clk);
// !!! HARDWARE SHARED RESOURCE !!!
// on some ESP chip, different peripheral's clock source setting are mixed in the same register
// so we need to make this done in an atomic way
GPTIMER_CLOCK_SRC_ATOMIC() {
timer_ll_set_clock_source(timer->hal.dev, timer_id, src_clk);
timer_ll_enable_clock(timer->hal.dev, timer_id, true);
}
timer->clk_src = src_clk;
unsigned int prescale = counter_src_hz / resolution_hz; // potential resolution loss here
uint32_t prescale = counter_src_hz / resolution_hz; // potential resolution loss here
timer_ll_set_clock_prescale(timer->hal.dev, timer_id, prescale);
timer->resolution_hz = counter_src_hz / prescale; // this is the real resolution
if (timer->resolution_hz != resolution_hz) {
Expand Down
59 changes: 58 additions & 1 deletion components/esp_hw_support/include/esp_private/periph_ctrl.h
Original file line number Diff line number Diff line change
@@ -1,16 +1,73 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

#include <stdint.h>
#include "soc/periph_defs.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @defgroup Reset and Clock Control APIs
* @{
*/

/**
* @brief Acquire the RCC lock for a peripheral module
*
* @note User code protected by this macro should be as short as possible, because it's a critical section
* @note This macro will increase the reference lock of that peripheral.
* You can get the value before the increment from the `rc_name` local variable
*/
#define PERIPH_RCC_ACQUIRE_ATOMIC(periph, rc_name) \
for (uint8_t rc_name, i = 1, __DECLARE_RCC_RC_ATOMIC_ENV; \
i ? (rc_name = periph_rcc_acquire_enter(periph), 1) : 0; \
periph_rcc_acquire_exit(periph, rc_name), i--)

/**
* @brief Release the RCC lock for a peripheral module
*
* @note User code protected by this macro should be as short as possible, because it's a critical section
* @note This macro will decrease the reference lock of that peripheral.
* You can get the value before the increment from the `rc_name` local variable
*/
#define PERIPH_RCC_RELEASE_ATOMIC(periph, rc_name) \
for (uint8_t rc_name, i = 1, __DECLARE_RCC_RC_ATOMIC_ENV; \
i ? (rc_name = periph_rcc_release_enter(periph), 1) : 0; \
periph_rcc_release_exit(periph, rc_name), i--)

/**
* @brief A simplified version of `PERIPH_RCC_ACQUIRE/RELEASE_ATOMIC`, without a reference count
*
* @note User code protected by this macro should be as short as possible, because it's a critical section
*/
#define PERIPH_RCC_ATOMIC() \
for (int i = 1, __DECLARE_RCC_ATOMIC_ENV; \
i ? (periph_rcc_enter(), 1) : 0; \
periph_rcc_exit(), i--)

/** @cond */
// The following functions are not intended to be used directly by the developers
uint8_t periph_rcc_acquire_enter(periph_module_t periph);
void periph_rcc_acquire_exit(periph_module_t periph, uint8_t ref_count);
uint8_t periph_rcc_release_enter(periph_module_t periph);
void periph_rcc_release_exit(periph_module_t periph, uint8_t ref_count);
void periph_rcc_enter(void);
void periph_rcc_exit(void);
/** @endcond */

/**
* @}
*/

/*************************************************************************************************************
* @note The following APIs are no longer supported since ESP32P4, please use the RCC lock macros instead.
*************************************************************************************************************/
/**
* @brief Enable peripheral module by un-gating the clock and de-asserting the reset signal.
*
Expand Down
36 changes: 36 additions & 0 deletions components/esp_hw_support/periph_ctrl.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,46 @@
#include "esp_private/esp_modem_clock.h"
#endif

/// @brief For simplicity and backward compatible, we are using the same spin lock for both bus clock on/off and reset
/// @note We may want to split them into two spin locks in the future
static portMUX_TYPE periph_spinlock = portMUX_INITIALIZER_UNLOCKED;

static uint8_t ref_counts[PERIPH_MODULE_MAX] = {0};

void periph_rcc_enter(void)
{
portENTER_CRITICAL_SAFE(&periph_spinlock);
}

void periph_rcc_exit(void)
{
portEXIT_CRITICAL_SAFE(&periph_spinlock);
}

uint8_t periph_rcc_acquire_enter(periph_module_t periph)
{
periph_rcc_enter();
return ref_counts[periph];
}

void periph_rcc_acquire_exit(periph_module_t periph, uint8_t ref_count)
{
ref_counts[periph] = ++ref_count;
periph_rcc_exit();
}

uint8_t periph_rcc_release_enter(periph_module_t periph)
{
periph_rcc_enter();
return ref_counts[periph] - 1;
}

void periph_rcc_release_exit(periph_module_t periph, uint8_t ref_count)
{
ref_counts[periph] = ref_count;
periph_rcc_exit();
}

void periph_module_enable(periph_module_t periph)
{
assert(periph < PERIPH_MODULE_MAX);
Expand Down
29 changes: 25 additions & 4 deletions components/esp_hw_support/port/esp32/rtc_time.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -8,11 +8,13 @@
#include "esp_rom_sys.h"
#include "hal/clk_tree_ll.h"
#include "hal/rtc_cntl_ll.h"
#include "hal/timer_ll.h"
#include "soc/rtc.h"
#include "soc/timer_periph.h"
#include "esp_hw_log.h"
#include "esp_private/periph_ctrl.h"

static const char* TAG = "rtc_time";
static const char *TAG = "rtc_time";

/* Calibration of RTC_SLOW_CLK is performed using a special feature of TIMG0.
* This feature counts the number of XTAL clock cycles within a given number of
Expand Down Expand Up @@ -55,10 +57,10 @@ static uint32_t rtc_clk_cal_internal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cyc
uint32_t expected_freq;
soc_rtc_slow_clk_src_t slow_clk_src = rtc_clk_slow_src_get();
if (cal_clk == RTC_CAL_32K_XTAL ||
(cal_clk == RTC_CAL_RTC_MUX && slow_clk_src == SOC_RTC_SLOW_CLK_SRC_XTAL32K)) {
(cal_clk == RTC_CAL_RTC_MUX && slow_clk_src == SOC_RTC_SLOW_CLK_SRC_XTAL32K)) {
expected_freq = SOC_CLK_XTAL32K_FREQ_APPROX; /* standard 32k XTAL */
} else if (cal_clk == RTC_CAL_8MD256 ||
(cal_clk == RTC_CAL_RTC_MUX && slow_clk_src == SOC_RTC_SLOW_CLK_SRC_RC_FAST_D256)) {
(cal_clk == RTC_CAL_RTC_MUX && slow_clk_src == SOC_RTC_SLOW_CLK_SRC_RC_FAST_D256)) {
expected_freq = SOC_CLK_RC_FAST_D256_FREQ_APPROX;
} else {
expected_freq = SOC_CLK_RC_SLOW_FREQ_APPROX; /* 150k internal oscillator */
Expand Down Expand Up @@ -185,3 +187,22 @@ uint32_t rtc_clk_freq_cal(uint32_t cal_val)
}
return 1000000ULL * (1 << RTC_CLK_CAL_FRACT) / cal_val;
}

/// @brief if the calibration is used, we need to enable the timer group0 first
__attribute__((constructor))
static void enable_timer_group0_for_calibration(void)
{
#ifndef BOOTLOADER_BUILD
PERIPH_RCC_ACQUIRE_ATOMIC(PERIPH_TIMG0_MODULE, ref_count) {
if (ref_count == 0) {
timer_ll_enable_bus_clock(0, true);
timer_ll_reset_register(0);
}
}
#else
// no critical section is needed for bootloader
int __DECLARE_RCC_RC_ATOMIC_ENV;
timer_ll_enable_bus_clock(0, true);
timer_ll_reset_register(0);
#endif
}
16 changes: 15 additions & 1 deletion components/esp_hw_support/port/esp32c2/rtc_time.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -10,8 +10,10 @@
#include "soc/rtc_cntl_reg.h"
#include "hal/clk_tree_ll.h"
#include "hal/rtc_cntl_ll.h"
#include "hal/timer_ll.h"
#include "soc/timer_group_reg.h"
#include "esp_rom_sys.h"
#include "esp_private/periph_ctrl.h"

/* Calibration of RTC_SLOW_CLK is performed using a special feature of TIMG0.
* This feature counts the number of XTAL clock cycles within a given number of
Expand Down Expand Up @@ -185,3 +187,15 @@ uint32_t rtc_clk_freq_cal(uint32_t cal_val)
}
return 1000000ULL * (1 << RTC_CLK_CAL_FRACT) / cal_val;
}

/// @brief if the calibration is used, we need to enable the timer group0 first
__attribute__((constructor))
static void enable_timer_group0_for_calibration(void)
{
PERIPH_RCC_ACQUIRE_ATOMIC(PERIPH_TIMG0_MODULE, ref_count) {
if (ref_count == 0) {
timer_ll_enable_bus_clock(0, true);
timer_ll_reset_register(0);
}
}
}
Loading

0 comments on commit 3b50c71

Please sign in to comment.