feat(gptimer): use RCC atomic block to enable/reset peripheral

This commit is contained in:
morris
2023-08-22 17:05:35 +08:00
parent a9c813ca3e
commit 71cf16ec01
27 changed files with 488 additions and 113 deletions
+35 -6
View File
@@ -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
@@ -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");
@@ -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];
@@ -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);
}
@@ -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);
}
@@ -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) {