2022-01-02 03:14:17 -05:00
/*
* SPDX - FileCopyrightText : 2022 Espressif Systems ( Shanghai ) CO LTD
*
* SPDX - License - Identifier : Apache - 2.0
*/
# include <stdlib.h>
# include <sys/lock.h>
2022-01-17 01:42:09 -05:00
# include "sdkconfig.h"
# if CONFIG_GPTIMER_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
# define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
# endif
2022-01-02 03:14:17 -05:00
# include "freertos/FreeRTOS.h"
# include "esp_attr.h"
# include "esp_err.h"
# include "esp_heap_caps.h"
# include "esp_intr_alloc.h"
# include "esp_log.h"
# include "esp_check.h"
# include "esp_pm.h"
# include "driver/gptimer.h"
# include "hal/timer_types.h"
# include "hal/timer_hal.h"
# include "hal/timer_ll.h"
# include "soc/timer_periph.h"
2022-03-31 03:07:51 -04:00
# include "esp_memory_utils.h"
2022-01-02 03:14:17 -05:00
# include "esp_private/periph_ctrl.h"
# include "esp_private/esp_clk.h"
// If ISR handler is allowed to run whilst cache is disabled,
// Make sure all the code and related variables used by the handler are in the SRAM
2022-03-07 05:04:02 -05:00
# if CONFIG_GPTIMER_ISR_IRAM_SAFE || CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
# define GPTIMER_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
# else
# define GPTIMER_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
# endif
2022-01-02 03:14:17 -05:00
# if CONFIG_GPTIMER_ISR_IRAM_SAFE
# define GPTIMER_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED)
# else
# define GPTIMER_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED
2022-03-07 05:04:02 -05:00
# endif
2022-01-02 03:14:17 -05:00
# define GPTIMER_PM_LOCK_NAME_LEN_MAX 16
static const char * TAG = " gptimer " ;
typedef struct gptimer_platform_t gptimer_platform_t ;
typedef struct gptimer_group_t gptimer_group_t ;
typedef struct gptimer_t gptimer_t ;
struct gptimer_platform_t {
_lock_t mutex ; // platform level mutex lock
gptimer_group_t * groups [ SOC_TIMER_GROUPS ] ; // timer group pool
int group_ref_counts [ SOC_TIMER_GROUPS ] ; // reference count used to protect group install/uninstall
} ;
struct gptimer_group_t {
int group_id ;
portMUX_TYPE spinlock ; // to protect per-group register level concurrent access
gptimer_t * timers [ SOC_TIMER_GROUP_TIMERS_PER_GROUP ] ;
} ;
typedef enum {
2022-04-23 06:59:38 -04:00
GPTIMER_FSM_INIT ,
GPTIMER_FSM_ENABLE ,
} gptimer_fsm_t ;
2022-01-02 03:14:17 -05:00
struct gptimer_t {
gptimer_group_t * group ;
int timer_id ;
2022-08-04 01:08:48 -04:00
uint32_t resolution_hz ;
uint64_t reload_count ;
uint64_t alarm_count ;
2022-01-02 03:14:17 -05:00
gptimer_count_direction_t direction ;
timer_hal_context_t hal ;
2022-04-23 06:59:38 -04:00
gptimer_fsm_t fsm ;
2022-01-02 03:14:17 -05:00
intr_handle_t intr ;
portMUX_TYPE spinlock ; // to protect per-timer resources concurent accessed by task and ISR handler
gptimer_alarm_cb_t on_alarm ;
void * user_ctx ;
esp_pm_lock_handle_t pm_lock ; // power management lock
# if CONFIG_PM_ENABLE
char pm_lock_name [ GPTIMER_PM_LOCK_NAME_LEN_MAX ] ; // pm lock name
# endif
struct {
uint32_t intr_shared : 1 ;
uint32_t auto_reload_on_alarm : 1 ;
uint32_t alarm_en : 1 ;
} flags ;
} ;
// gptimer driver platform, it's always a singleton
static gptimer_platform_t s_platform ;
static gptimer_group_t * gptimer_acquire_group_handle ( int group_id ) ;
static void gptimer_release_group_handle ( gptimer_group_t * group ) ;
static esp_err_t gptimer_select_periph_clock ( gptimer_t * timer , gptimer_clock_source_t src_clk , uint32_t resolution_hz ) ;
2022-01-17 01:42:09 -05:00
static void gptimer_default_isr ( void * args ) ;
2022-01-02 03:14:17 -05:00
2022-04-08 01:31:29 -04:00
static esp_err_t gptimer_register_to_group ( gptimer_t * timer )
2022-01-02 03:14:17 -05:00
{
gptimer_group_t * group = NULL ;
int timer_id = - 1 ;
2022-04-08 01:31:29 -04:00
for ( int i = 0 ; i < SOC_TIMER_GROUPS ; i + + ) {
2022-01-02 03:14:17 -05:00
group = gptimer_acquire_group_handle ( i ) ;
2022-04-08 01:31:29 -04:00
ESP_RETURN_ON_FALSE ( group , ESP_ERR_NO_MEM , TAG , " no mem for group (%d) " , i ) ;
2022-01-02 03:14:17 -05:00
// loop to search free timer in the group
portENTER_CRITICAL ( & group - > spinlock ) ;
for ( int j = 0 ; j < SOC_TIMER_GROUP_TIMERS_PER_GROUP ; j + + ) {
if ( ! group - > timers [ j ] ) {
timer_id = j ;
group - > timers [ j ] = timer ;
break ;
}
}
portEXIT_CRITICAL ( & group - > spinlock ) ;
if ( timer_id < 0 ) {
gptimer_release_group_handle ( group ) ;
group = NULL ;
2022-04-08 01:31:29 -04:00
} else {
timer - > timer_id = timer_id ;
timer - > group = group ;
break ; ;
2022-01-02 03:14:17 -05:00
}
}
2022-04-08 01:31:29 -04:00
ESP_RETURN_ON_FALSE ( timer_id ! = - 1 , ESP_ERR_NOT_FOUND , TAG , " no free timer " ) ;
return ESP_OK ;
}
static void gptimer_unregister_from_group ( gptimer_t * timer )
{
gptimer_group_t * group = timer - > group ;
int timer_id = timer - > timer_id ;
portENTER_CRITICAL ( & group - > spinlock ) ;
group - > timers [ timer_id ] = NULL ;
portEXIT_CRITICAL ( & group - > spinlock ) ;
// timer has a reference on group, release it now
gptimer_release_group_handle ( group ) ;
}
static esp_err_t gptimer_destory ( gptimer_t * timer )
{
if ( timer - > pm_lock ) {
ESP_RETURN_ON_ERROR ( esp_pm_lock_delete ( timer - > pm_lock ) , TAG , " delete pm_lock failed " ) ;
}
if ( timer - > intr ) {
ESP_RETURN_ON_ERROR ( esp_intr_free ( timer - > intr ) , TAG , " delete interrupt service failed " ) ;
}
if ( timer - > group ) {
gptimer_unregister_from_group ( timer ) ;
}
free ( timer ) ;
return ESP_OK ;
}
esp_err_t gptimer_new_timer ( const gptimer_config_t * config , gptimer_handle_t * ret_timer )
{
# if CONFIG_GPTIMER_ENABLE_DEBUG_LOG
esp_log_level_set ( TAG , ESP_LOG_DEBUG ) ;
# endif
esp_err_t ret = ESP_OK ;
gptimer_t * timer = NULL ;
ESP_GOTO_ON_FALSE ( config & & ret_timer , ESP_ERR_INVALID_ARG , err , TAG , " invalid argument " ) ;
2022-08-04 01:08:48 -04:00
ESP_GOTO_ON_FALSE ( config - > resolution_hz , ESP_ERR_INVALID_ARG , err , TAG , " invalid timer resolution:% " PRIu32 , config - > resolution_hz ) ;
2022-04-08 01:31:29 -04:00
timer = heap_caps_calloc ( 1 , sizeof ( gptimer_t ) , GPTIMER_MEM_ALLOC_CAPS ) ;
ESP_GOTO_ON_FALSE ( timer , ESP_ERR_NO_MEM , err , TAG , " no mem for gptimer " ) ;
// register timer to the group (because one group can have several timers)
ESP_GOTO_ON_ERROR ( gptimer_register_to_group ( timer ) , err , TAG , " register timer failed " ) ;
gptimer_group_t * group = timer - > group ;
int group_id = group - > group_id ;
int timer_id = timer - > timer_id ;
2022-01-02 03:14:17 -05:00
// initialize HAL layer
timer_hal_init ( & timer - > hal , group_id , timer_id ) ;
// stop counter, alarm, auto-reload
timer_ll_enable_counter ( timer - > hal . dev , timer_id , false ) ;
timer_ll_enable_auto_reload ( timer - > hal . dev , timer_id , false ) ;
timer_ll_enable_alarm ( timer - > hal . dev , timer_id , false ) ;
// select clock source, set clock resolution
ESP_GOTO_ON_ERROR ( gptimer_select_periph_clock ( timer , config - > clk_src , config - > resolution_hz ) , err , TAG , " set periph clock failed " ) ;
// initialize counter value to zero
timer_hal_set_counter_value ( & timer - > hal , 0 ) ;
// set counting direction
timer_ll_set_count_direction ( timer - > hal . dev , timer_id , config - > direction ) ;
// interrupt register is shared by all timers in the same group
portENTER_CRITICAL ( & group - > spinlock ) ;
timer_ll_enable_intr ( timer - > hal . dev , TIMER_LL_EVENT_ALARM ( timer_id ) , false ) ; // disable interrupt
timer_ll_clear_intr_status ( timer - > hal . dev , TIMER_LL_EVENT_ALARM ( timer_id ) ) ; // clear pending interrupt event
portEXIT_CRITICAL ( & group - > spinlock ) ;
// initialize other members of timer
timer - > spinlock = ( portMUX_TYPE ) portMUX_INITIALIZER_UNLOCKED ;
2022-04-23 06:59:38 -04:00
timer - > fsm = GPTIMER_FSM_INIT ; // put the timer into init state
2022-01-02 03:14:17 -05:00
timer - > direction = config - > direction ;
timer - > flags . intr_shared = config - > flags . intr_shared ;
2022-08-04 01:08:48 -04:00
ESP_LOGD ( TAG , " new gptimer (%d,%d) at %p, resolution=% " PRIu32 " Hz " , group_id , timer_id , timer , timer - > resolution_hz ) ;
2022-01-02 03:14:17 -05:00
* ret_timer = timer ;
return ESP_OK ;
err :
if ( timer ) {
2022-04-08 01:31:29 -04:00
gptimer_destory ( timer ) ;
2022-01-02 03:14:17 -05:00
}
return ret ;
}
esp_err_t gptimer_del_timer ( gptimer_handle_t timer )
{
ESP_RETURN_ON_FALSE ( timer , ESP_ERR_INVALID_ARG , TAG , " invalid argument " ) ;
2022-04-23 06:59:38 -04:00
ESP_RETURN_ON_FALSE ( timer - > fsm = = GPTIMER_FSM_INIT , ESP_ERR_INVALID_STATE , TAG , " timer not in init state " ) ;
2022-04-08 01:31:29 -04:00
gptimer_group_t * group = timer - > group ;
2022-01-02 03:14:17 -05:00
int group_id = group - > group_id ;
int timer_id = timer - > timer_id ;
2022-04-08 01:31:29 -04:00
ESP_LOGD ( TAG , " del timer (%d,%d) " , group_id , timer_id ) ;
// recycle memory resource
ESP_RETURN_ON_ERROR ( gptimer_destory ( timer ) , TAG , " destory gptimer failed " ) ;
2022-01-02 03:14:17 -05:00
return ESP_OK ;
}
esp_err_t gptimer_set_raw_count ( gptimer_handle_t timer , unsigned long long value )
{
2022-01-17 01:42:09 -05:00
ESP_RETURN_ON_FALSE_ISR ( timer , ESP_ERR_INVALID_ARG , TAG , " invalid argument " ) ;
2022-01-02 03:14:17 -05:00
portENTER_CRITICAL_SAFE ( & timer - > spinlock ) ;
timer_hal_set_counter_value ( & timer - > hal , value ) ;
portEXIT_CRITICAL_SAFE ( & timer - > spinlock ) ;
return ESP_OK ;
}
esp_err_t gptimer_get_raw_count ( gptimer_handle_t timer , unsigned long long * value )
{
2022-01-17 01:42:09 -05:00
ESP_RETURN_ON_FALSE_ISR ( timer & & value , ESP_ERR_INVALID_ARG , TAG , " invalid argument " ) ;
2022-01-02 03:14:17 -05:00
portENTER_CRITICAL_SAFE ( & timer - > spinlock ) ;
* value = timer_ll_get_counter_value ( timer - > hal . dev , timer - > timer_id ) ;
portEXIT_CRITICAL_SAFE ( & timer - > spinlock ) ;
return ESP_OK ;
}
esp_err_t gptimer_register_event_callbacks ( gptimer_handle_t timer , const gptimer_event_callbacks_t * cbs , void * user_data )
{
gptimer_group_t * group = NULL ;
ESP_RETURN_ON_FALSE ( timer & & cbs , ESP_ERR_INVALID_ARG , TAG , " invalid argument " ) ;
group = timer - > group ;
2022-01-17 02:49:52 -05:00
int group_id = group - > group_id ;
int timer_id = timer - > timer_id ;
2022-01-02 03:14:17 -05:00
# if CONFIG_GPTIMER_ISR_IRAM_SAFE
if ( cbs - > on_alarm ) {
ESP_RETURN_ON_FALSE ( esp_ptr_in_iram ( cbs - > on_alarm ) , ESP_ERR_INVALID_ARG , TAG , " on_alarm callback not in IRAM " ) ;
}
if ( user_data ) {
2022-03-07 05:04:02 -05:00
ESP_RETURN_ON_FALSE ( esp_ptr_internal ( user_data ) , ESP_ERR_INVALID_ARG , TAG , " user context not in internal RAM " ) ;
2022-01-02 03:14:17 -05:00
}
# endif
// lazy install interrupt service
2022-01-17 02:49:52 -05:00
if ( ! timer - > intr ) {
2022-08-08 03:47:30 -04:00
ESP_RETURN_ON_FALSE ( timer - > fsm = = GPTIMER_FSM_INIT , ESP_ERR_INVALID_STATE , TAG , " timer not in init state " ) ;
2022-01-17 02:49:52 -05:00
// if user wants to control the interrupt allocation more precisely, we can expose more flags in `gptimer_config_t`
int isr_flags = timer - > flags . intr_shared ? ESP_INTR_FLAG_SHARED | GPTIMER_INTR_ALLOC_FLAGS : GPTIMER_INTR_ALLOC_FLAGS ;
ESP_RETURN_ON_ERROR ( esp_intr_alloc_intrstatus ( timer_group_periph_signals . groups [ group_id ] . timer_irq_id [ timer_id ] , isr_flags ,
( uint32_t ) timer_ll_get_intr_status_reg ( timer - > hal . dev ) , TIMER_LL_EVENT_ALARM ( timer_id ) ,
gptimer_default_isr , timer , & timer - > intr ) , TAG , " install interrupt service failed " ) ;
}
2022-01-02 03:14:17 -05:00
// enable/disable GPTimer interrupt events
2022-01-17 01:42:09 -05:00
portENTER_CRITICAL ( & group - > spinlock ) ;
2022-01-17 02:49:52 -05:00
timer_ll_enable_intr ( timer - > hal . dev , TIMER_LL_EVENT_ALARM ( timer - > timer_id ) , cbs - > on_alarm ! = NULL ) ; // enable timer interrupt
2022-01-17 01:42:09 -05:00
portEXIT_CRITICAL ( & group - > spinlock ) ;
2022-01-02 03:14:17 -05:00
timer - > on_alarm = cbs - > on_alarm ;
timer - > user_ctx = user_data ;
return ESP_OK ;
}
esp_err_t gptimer_set_alarm_action ( gptimer_handle_t timer , const gptimer_alarm_config_t * config )
{
2022-01-17 01:42:09 -05:00
ESP_RETURN_ON_FALSE_ISR ( timer , ESP_ERR_INVALID_ARG , TAG , " invalid argument " ) ;
2022-01-02 03:14:17 -05:00
if ( config ) {
// When auto_reload is enabled, alarm_count should not be equal to reload_count
bool valid_auto_reload = ! config - > flags . auto_reload_on_alarm | | config - > alarm_count ! = config - > reload_count ;
2022-01-17 01:42:09 -05:00
ESP_RETURN_ON_FALSE_ISR ( valid_auto_reload , ESP_ERR_INVALID_ARG , TAG , " reload count can't equal to alarm count " ) ;
2022-01-02 03:14:17 -05:00
2022-04-08 01:31:29 -04:00
portENTER_CRITICAL_SAFE ( & timer - > spinlock ) ;
2022-01-02 03:14:17 -05:00
timer - > reload_count = config - > reload_count ;
timer - > alarm_count = config - > alarm_count ;
timer - > flags . auto_reload_on_alarm = config - > flags . auto_reload_on_alarm ;
timer - > flags . alarm_en = true ;
timer_ll_set_reload_value ( timer - > hal . dev , timer - > timer_id , config - > reload_count ) ;
timer_ll_set_alarm_value ( timer - > hal . dev , timer - > timer_id , config - > alarm_count ) ;
portEXIT_CRITICAL_SAFE ( & timer - > spinlock ) ;
} else {
2022-04-08 01:31:29 -04:00
portENTER_CRITICAL_SAFE ( & timer - > spinlock ) ;
2022-01-02 03:14:17 -05:00
timer - > flags . auto_reload_on_alarm = false ;
timer - > flags . alarm_en = false ;
2022-04-08 01:31:29 -04:00
portEXIT_CRITICAL_SAFE ( & timer - > spinlock ) ;
2022-01-02 03:14:17 -05:00
}
portENTER_CRITICAL_SAFE ( & timer - > spinlock ) ;
timer_ll_enable_auto_reload ( timer - > hal . dev , timer - > timer_id , timer - > flags . auto_reload_on_alarm ) ;
timer_ll_enable_alarm ( timer - > hal . dev , timer - > timer_id , timer - > flags . alarm_en ) ;
portEXIT_CRITICAL_SAFE ( & timer - > spinlock ) ;
return ESP_OK ;
}
2022-04-23 06:59:38 -04:00
esp_err_t gptimer_enable ( gptimer_handle_t timer )
2022-01-02 03:14:17 -05:00
{
2022-04-23 06:59:38 -04:00
ESP_RETURN_ON_FALSE ( timer , ESP_ERR_INVALID_ARG , TAG , " invalid argument " ) ;
ESP_RETURN_ON_FALSE ( timer - > fsm = = GPTIMER_FSM_INIT , ESP_ERR_INVALID_STATE , TAG , " timer not in init state " ) ;
2022-01-02 03:14:17 -05:00
// acquire power manager lock
if ( timer - > pm_lock ) {
2022-04-23 06:59:38 -04:00
ESP_RETURN_ON_ERROR ( esp_pm_lock_acquire ( timer - > pm_lock ) , TAG , " acquire pm_lock failed " ) ;
}
2022-08-09 01:51:56 -04:00
// enable interrupt service
2022-04-23 06:59:38 -04:00
if ( timer - > intr ) {
ESP_RETURN_ON_ERROR ( esp_intr_enable ( timer - > intr ) , TAG , " enable interrupt service failed " ) ;
2022-01-02 03:14:17 -05:00
}
2022-04-23 06:59:38 -04:00
timer - > fsm = GPTIMER_FSM_ENABLE ;
return ESP_OK ;
}
esp_err_t gptimer_disable ( gptimer_handle_t timer )
{
ESP_RETURN_ON_FALSE ( timer , ESP_ERR_INVALID_ARG , TAG , " invalid argument " ) ;
ESP_RETURN_ON_FALSE ( timer - > fsm = = GPTIMER_FSM_ENABLE , ESP_ERR_INVALID_STATE , TAG , " timer not in enable state " ) ;
// disable interrupt service
2022-01-02 03:14:17 -05:00
if ( timer - > intr ) {
2022-04-23 06:59:38 -04:00
ESP_RETURN_ON_ERROR ( esp_intr_disable ( timer - > intr ) , TAG , " disable interrupt service failed " ) ;
}
// release power manager lock
if ( timer - > pm_lock ) {
ESP_RETURN_ON_ERROR ( esp_pm_lock_release ( timer - > pm_lock ) , TAG , " release pm_lock failed " ) ;
2022-01-02 03:14:17 -05:00
}
2022-04-23 06:59:38 -04:00
timer - > fsm = GPTIMER_FSM_INIT ;
return ESP_OK ;
}
esp_err_t gptimer_start ( gptimer_handle_t timer )
{
ESP_RETURN_ON_FALSE_ISR ( timer , ESP_ERR_INVALID_ARG , TAG , " invalid argument " ) ;
ESP_RETURN_ON_FALSE_ISR ( timer - > fsm = = GPTIMER_FSM_ENABLE , ESP_ERR_INVALID_STATE , TAG , " timer not enabled yet " ) ;
2022-01-02 03:14:17 -05:00
portENTER_CRITICAL_SAFE ( & timer - > spinlock ) ;
timer_ll_enable_counter ( timer - > hal . dev , timer - > timer_id , true ) ;
timer_ll_enable_alarm ( timer - > hal . dev , timer - > timer_id , timer - > flags . alarm_en ) ;
portEXIT_CRITICAL_SAFE ( & timer - > spinlock ) ;
return ESP_OK ;
}
esp_err_t gptimer_stop ( gptimer_handle_t timer )
{
2022-01-17 01:42:09 -05:00
ESP_RETURN_ON_FALSE_ISR ( timer , ESP_ERR_INVALID_ARG , TAG , " invalid argument " ) ;
2022-04-23 06:59:38 -04:00
ESP_RETURN_ON_FALSE_ISR ( timer - > fsm = = GPTIMER_FSM_ENABLE , ESP_ERR_INVALID_STATE , TAG , " timer not enabled yet " ) ;
2022-01-02 03:14:17 -05:00
2022-04-23 06:59:38 -04:00
// disable counter, alarm, auto-reload
2022-01-02 03:14:17 -05:00
portENTER_CRITICAL_SAFE ( & timer - > spinlock ) ;
timer_ll_enable_counter ( timer - > hal . dev , timer - > timer_id , false ) ;
timer_ll_enable_alarm ( timer - > hal . dev , timer - > timer_id , false ) ;
portEXIT_CRITICAL_SAFE ( & timer - > spinlock ) ;
return ESP_OK ;
}
static gptimer_group_t * gptimer_acquire_group_handle ( int group_id )
{
bool new_group = false ;
gptimer_group_t * group = NULL ;
// prevent install timer group concurrently
_lock_acquire ( & s_platform . mutex ) ;
if ( ! s_platform . groups [ group_id ] ) {
group = heap_caps_calloc ( 1 , sizeof ( gptimer_group_t ) , GPTIMER_MEM_ALLOC_CAPS ) ;
if ( group ) {
new_group = true ;
s_platform . groups [ group_id ] = group ;
// 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 ] ;
}
2022-01-17 02:49:52 -05:00
if ( group ) {
// someone acquired the group handle means we have a new object that refer to this group
s_platform . group_ref_counts [ group_id ] + + ;
}
2022-01-02 03:14:17 -05:00
_lock_release ( & s_platform . mutex ) ;
if ( new_group ) {
ESP_LOGD ( TAG , " new group (%d) @%p " , group_id , group ) ;
}
return group ;
}
static void gptimer_release_group_handle ( gptimer_group_t * group )
{
int group_id = group - > group_id ;
bool do_deinitialize = false ;
_lock_acquire ( & s_platform . mutex ) ;
s_platform . group_ref_counts [ group_id ] - - ;
if ( s_platform . group_ref_counts [ group_id ] = = 0 ) {
assert ( s_platform . groups [ group_id ] ) ;
do_deinitialize = true ;
s_platform . groups [ group_id ] = NULL ;
// Theoretically we need to disable the peripheral clock for the timer group
// However, next time when we enable the peripheral again, the registers will be reset to default value, including the watchdog registers inside the group
// Then the watchdog will go into reset state, e.g. the flash boot watchdog is enabled again and reset the system very soon
// periph_module_disable(timer_group_periph_signals.groups[group_id].module);
}
_lock_release ( & s_platform . mutex ) ;
if ( do_deinitialize ) {
free ( group ) ;
ESP_LOGD ( TAG , " del group (%d) " , group_id ) ;
}
}
static esp_err_t gptimer_select_periph_clock ( gptimer_t * timer , gptimer_clock_source_t src_clk , uint32_t resolution_hz )
{
unsigned int counter_src_hz = 0 ;
esp_err_t ret = ESP_OK ;
int timer_id = timer - > timer_id ;
2022-04-13 01:12:30 -04:00
// [clk_tree] TODO: replace the following switch table by clk_tree API
2022-01-02 03:14:17 -05:00
switch ( src_clk ) {
2022-04-13 01:12:30 -04:00
# if SOC_TIMER_GROUP_SUPPORT_APB
2022-01-02 03:14:17 -05:00
case GPTIMER_CLK_SRC_APB :
counter_src_hz = esp_clk_apb_freq ( ) ;
# if CONFIG_PM_ENABLE
sprintf ( timer - > pm_lock_name , " gptimer_%d_%d " , timer - > group - > group_id , timer_id ) ; // e.g. gptimer_0_0
ret = esp_pm_lock_create ( ESP_PM_APB_FREQ_MAX , 0 , timer - > pm_lock_name , & timer - > pm_lock ) ;
ESP_RETURN_ON_ERROR ( ret , TAG , " create APB_FREQ_MAX lock failed " ) ;
ESP_LOGD ( TAG , " install APB_FREQ_MAX lock for timer (%d,%d) " , timer - > group - > group_id , timer_id ) ;
# endif
break ;
2022-04-13 01:12:30 -04:00
# endif // SOC_TIMER_GROUP_SUPPORT_APB
# if SOC_TIMER_GROUP_SUPPORT_PLL_F40M
case GPTIMER_CLK_SRC_PLL_F40M :
// TODO: decide which kind of PM lock we should use for such clock
counter_src_hz = 40 * 1000 * 1000 ;
break ;
# endif // SOC_TIMER_GROUP_SUPPORT_PLL_F40M
# if SOC_TIMER_GROUP_SUPPORT_AHB
case GPTIMER_CLK_SRC_AHB :
// TODO: decide which kind of PM lock we should use for such clock
counter_src_hz = 48 * 1000 * 1000 ;
break ;
# endif // SOC_TIMER_GROUP_SUPPORT_AHB
2022-01-02 03:14:17 -05:00
# if SOC_TIMER_GROUP_SUPPORT_XTAL
case GPTIMER_CLK_SRC_XTAL :
counter_src_hz = esp_clk_xtal_freq ( ) ;
break ;
2022-04-13 01:12:30 -04:00
# endif // SOC_TIMER_GROUP_SUPPORT_XTAL
2022-01-02 03:14:17 -05:00
default :
ESP_RETURN_ON_FALSE ( false , ESP_ERR_NOT_SUPPORTED , TAG , " clock source %d is not support " , src_clk ) ;
break ;
}
timer_ll_set_clock_source ( timer - > hal . dev , timer_id , src_clk ) ;
unsigned int 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 ) {
2022-08-04 01:08:48 -04:00
ESP_LOGW ( TAG , " resolution lost, expect % " PRIu32 " , real % " PRIu32 , resolution_hz , timer - > resolution_hz ) ;
2022-01-02 03:14:17 -05:00
}
return ret ;
}
// Put the default ISR handler in the IRAM for better performance
IRAM_ATTR static void gptimer_default_isr ( void * args )
{
bool need_yield = false ;
gptimer_t * timer = ( gptimer_t * ) args ;
gptimer_group_t * group = timer - > group ;
gptimer_alarm_cb_t on_alarm_cb = timer - > on_alarm ;
uint32_t intr_status = timer_ll_get_intr_status ( timer - > hal . dev ) ;
if ( intr_status & TIMER_LL_EVENT_ALARM ( timer - > timer_id ) ) {
2022-06-06 23:00:57 -04:00
// Note: when alarm event happens, the alarm will be disabled automatically by hardware
2022-01-02 03:14:17 -05:00
gptimer_alarm_event_data_t edata = {
. count_value = timer_ll_get_counter_value ( timer - > hal . dev , timer - > timer_id ) ,
. alarm_value = timer - > alarm_count ,
} ;
portENTER_CRITICAL_ISR ( & group - > spinlock ) ;
timer_ll_clear_intr_status ( timer - > hal . dev , TIMER_LL_EVENT_ALARM ( timer - > timer_id ) ) ;
// for auto-reload, we need to re-enable the alarm manually
if ( timer - > flags . auto_reload_on_alarm ) {
timer_ll_enable_alarm ( timer - > hal . dev , timer - > timer_id , true ) ;
}
portEXIT_CRITICAL_ISR ( & group - > spinlock ) ;
if ( on_alarm_cb ) {
if ( on_alarm_cb ( timer , & edata , timer - > user_ctx ) ) {
need_yield = true ;
}
}
}
if ( need_yield ) {
portYIELD_FROM_ISR ( ) ;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///// The Following APIs are for internal use only (e.g. unit test) /////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
esp_err_t gptimer_get_intr_handle ( gptimer_handle_t timer , intr_handle_t * ret_intr_handle )
{
ESP_RETURN_ON_FALSE ( timer & & ret_intr_handle , ESP_ERR_INVALID_ARG , TAG , " invalid argument " ) ;
* ret_intr_handle = timer - > intr ;
return ESP_OK ;
}
esp_err_t gptimer_get_pm_lock ( gptimer_handle_t timer , esp_pm_lock_handle_t * ret_pm_lock )
{
ESP_RETURN_ON_FALSE ( timer & & ret_pm_lock , ESP_ERR_INVALID_ARG , TAG , " invalid argument " ) ;
* ret_pm_lock = timer - > pm_lock ;
return ESP_OK ;
}