1
6
7
8
9
10
11
14
15
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
51
52
53
54
55
56
57
78
79
80
81
96
97
98
99
100
101
102
103
104
105
106
107
108
109
117
118
119
120
121
122
123
124
127
130
133
134
135
136
137
138
139
140
141
142
143
144
145
146
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
216
217
218
219
220
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
285
286
287
288
289
290
291
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
354
355
356
359
360
361
362
363
364
365
366
367
368
369
370
371
374
375
376
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
438
439
440
441
442
445
446
447
452
453
454
457
/* ... */
#include <stdlib.h>
#include <sys/lock.h>
#include "sdkconfig.h"
#if CONFIG_GPTIMER_ENABLE_DEBUG_LOG
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG/* ... */
#endif
#include "freertos/FreeRTOS.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "driver/gptimer.h"
#include "esp_memory_utils.h"
#include "gptimer_priv.h"8 includes
static const char *TAG = "gptimer";
static void gptimer_default_isr(void *args);
#if GPTIMER_USE_RETENTION_LINK
static esp_err_t gptimer_create_sleep_retention_link_cb(void *timer)
{
int group_id = ((gptimer_t *)timer)->group->group_id;
int timer_id = ((gptimer_t *)timer)->timer_id;
esp_err_t err = sleep_retention_entries_create(tg_timer_reg_retention_info[group_id][timer_id].regdma_entry_array,
tg_timer_reg_retention_info[group_id][timer_id].array_size,
REGDMA_LINK_PRI_GPTIMER, tg_timer_reg_retention_info[group_id][timer_id].module);
return err;
}{...}
static void gptimer_create_retention_module(gptimer_t *timer)
{
int group_id = timer->group->group_id;
int timer_id = timer->timer_id;
sleep_retention_module_t module = tg_timer_reg_retention_info[group_id][timer_id].module;
if (sleep_retention_is_module_inited(module) && !sleep_retention_is_module_created(module)) {
if (sleep_retention_module_allocate(module) != ESP_OK) {
ESP_LOGW(TAG, "create retention link failed on TimerGroup%d Timer%d, power domain won't be turned off during sleep", group_id, timer_id);
}{...}
}{...}
}{...}
/* ... */#endif
static esp_err_t gptimer_register_to_group(gptimer_t *timer)
{
gptimer_group_t *group = NULL;
int timer_id = -1;
for (int i = 0; i < SOC_TIMER_GROUPS; i++) {
group = gptimer_acquire_group_handle(i);
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", i);
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);
}{...} else {
timer->timer_id = timer_id;
timer->group = group;
break;
}{...}
}{...}
ESP_RETURN_ON_FALSE(timer_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free timer");
#if GPTIMER_USE_RETENTION_LINK
sleep_retention_module_t module = tg_timer_reg_retention_info[group->group_id][timer_id].module;
sleep_retention_module_init_param_t init_param = {
.cbs = {
.create = {
.handle = gptimer_create_sleep_retention_link_cb,
.arg = (void *)timer
}{...},
}{...},
.depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM)
}{...};
if (sleep_retention_module_init(module, &init_param) != ESP_OK) {
ESP_LOGW(TAG, "init sleep retention failed on TimerGroup%d Timer%d, power domain may be turned off during sleep", group->group_id, timer_id);
}{...}
#endif/* ... */
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);
#if GPTIMER_USE_RETENTION_LINK
sleep_retention_module_t module = tg_timer_reg_retention_info[group->group_id][timer_id].module;
if (sleep_retention_is_module_created(module)) {
sleep_retention_module_free(module);
}{...}
if (sleep_retention_is_module_inited(module)) {
sleep_retention_module_deinit(module);
}{...}
#endif/* ... */
gptimer_release_group_handle(group);
}{ ... }
static esp_err_t gptimer_destroy(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_RETURN_ON_FALSE(config && ret_timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(config->resolution_hz, ESP_ERR_INVALID_ARG, TAG, "invalid timer resolution:%"PRIu32, config->resolution_hz);
if (config->intr_priority) {
ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & GPTIMER_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG,
TAG, "invalid interrupt priority:%d", config->intr_priority);
}{...}
bool allow_pd = (config->flags.allow_pd == 1) || (config->flags.backup_before_sleep == 1);
#if !SOC_TIMER_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(allow_pd == false, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep");
#endif
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");
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;
if (allow_pd) {
#if GPTIMER_USE_RETENTION_LINK
gptimer_create_retention_module(timer);
#endif
}{...}
timer_hal_init(&timer->hal, group_id, timer_id);
ESP_GOTO_ON_ERROR(gptimer_select_periph_clock(timer, config->clk_src, config->resolution_hz), err, TAG, "set periph clock failed");
timer_hal_set_counter_value(&timer->hal, 0);
timer_ll_set_count_direction(timer->hal.dev, timer_id, config->direction);
portENTER_CRITICAL(&group->spinlock);
timer_ll_enable_intr(timer->hal.dev, TIMER_LL_EVENT_ALARM(timer_id), false);
timer_ll_clear_intr_status(timer->hal.dev, TIMER_LL_EVENT_ALARM(timer_id));
portEXIT_CRITICAL(&group->spinlock);
timer->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
atomic_init(&timer->fsm, GPTIMER_FSM_INIT);
timer->direction = config->direction;
timer->intr_priority = config->intr_priority;
timer->flags.intr_shared = config->flags.intr_shared;
ESP_LOGD(TAG, "new gptimer (%d,%d) at %p, resolution=%"PRIu32"Hz", group_id, timer_id, timer, timer->resolution_hz);
*ret_timer = timer;
return ESP_OK;
err:
if (timer) {
gptimer_destroy(timer);
}{...}
return ret;
}{ ... }
esp_err_t gptimer_del_timer(gptimer_handle_t timer)
{
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(atomic_load(&timer->fsm) == GPTIMER_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "timer not in init state");
gptimer_group_t *group = timer->group;
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);
GPTIMER_CLOCK_SRC_ATOMIC() {
timer_ll_enable_clock(hal->dev, hal->timer_id, false);
}{...}
timer_hal_deinit(hal);
ESP_RETURN_ON_ERROR(gptimer_destroy(timer), TAG, "destroy gptimer failed");
switch (clk_src) {
#if SOC_TIMER_GROUP_SUPPORT_RC_FAST
case GPTIMER_CLK_SRC_RC_FAST:
periph_rtc_dig_clk8m_disable();
break;/* ... */
#endif
default:
break;...
}{...}
return ESP_OK;
}{ ... }
esp_err_t gptimer_set_raw_count(gptimer_handle_t timer, unsigned long long value)
{
ESP_RETURN_ON_FALSE_ISR(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
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)
{
ESP_RETURN_ON_FALSE_ISR(timer && value, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
portENTER_CRITICAL_SAFE(&timer->spinlock);
*value = timer_hal_capture_and_get_counter_value(&timer->hal);
portEXIT_CRITICAL_SAFE(&timer->spinlock);
return ESP_OK;
}{ ... }
esp_err_t gptimer_get_resolution(gptimer_handle_t timer, uint32_t *out_resolution)
{
ESP_RETURN_ON_FALSE(timer && out_resolution, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
*out_resolution = timer->resolution_hz;
return ESP_OK;
}{ ... }
esp_err_t gptimer_get_captured_count(gptimer_handle_t timer, uint64_t *value)
{
ESP_RETURN_ON_FALSE_ISR(timer && value, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
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;
int group_id = group->group_id;
int timer_id = timer->timer_id;
#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) {
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
}{...}
#endif/* ... */
if (!timer->intr) {
ESP_RETURN_ON_FALSE(atomic_load(&timer->fsm) == GPTIMER_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "timer not in init state");
int isr_flags = timer->flags.intr_shared ? ESP_INTR_FLAG_SHARED | GPTIMER_INTR_ALLOC_FLAGS : GPTIMER_INTR_ALLOC_FLAGS;
if (timer->intr_priority) {
isr_flags |= 1 << (timer->intr_priority);
}{...}
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");
}{...}
portENTER_CRITICAL(&group->spinlock);
timer_ll_enable_intr(timer->hal.dev, TIMER_LL_EVENT_ALARM(timer->timer_id), cbs->on_alarm != NULL);
portEXIT_CRITICAL(&group->spinlock);
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)
{
ESP_RETURN_ON_FALSE_ISR(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
if (config) {
#if CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
ESP_RETURN_ON_FALSE_ISR(esp_ptr_internal(config), ESP_ERR_INVALID_ARG, TAG, "alarm config struct not in internal RAM");
#endif
bool valid_auto_reload = !config->flags.auto_reload_on_alarm || config->alarm_count != config->reload_count;
ESP_RETURN_ON_FALSE_ISR(valid_auto_reload, ESP_ERR_INVALID_ARG, TAG, "reload count can't equal to alarm count");
portENTER_CRITICAL_SAFE(&timer->spinlock);
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 {
portENTER_CRITICAL_SAFE(&timer->spinlock);
timer->flags.auto_reload_on_alarm = false;
timer->flags.alarm_en = false;
portEXIT_CRITICAL_SAFE(&timer->spinlock);
}{...}
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;
}{ ... }
esp_err_t gptimer_enable(gptimer_handle_t timer)
{
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
gptimer_fsm_t expected_fsm = GPTIMER_FSM_INIT;
ESP_RETURN_ON_FALSE(atomic_compare_exchange_strong(&timer->fsm, &expected_fsm, GPTIMER_FSM_ENABLE),
ESP_ERR_INVALID_STATE, TAG, "timer not in init state");
if (timer->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(timer->pm_lock), TAG, "acquire pm_lock failed");
}{...}
if (timer->intr) {
ESP_RETURN_ON_ERROR(esp_intr_enable(timer->intr), TAG, "enable interrupt service failed");
}{...}
return ESP_OK;
}{ ... }
esp_err_t gptimer_disable(gptimer_handle_t timer)
{
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
gptimer_fsm_t expected_fsm = GPTIMER_FSM_ENABLE;
ESP_RETURN_ON_FALSE(atomic_compare_exchange_strong(&timer->fsm, &expected_fsm, GPTIMER_FSM_INIT),
ESP_ERR_INVALID_STATE, TAG, "timer not in enable state");
if (timer->intr) {
ESP_RETURN_ON_ERROR(esp_intr_disable(timer->intr), TAG, "disable interrupt service failed");
}{...}
if (timer->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_release(timer->pm_lock), TAG, "release pm_lock failed");
}{...}
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");
gptimer_fsm_t expected_fsm = GPTIMER_FSM_ENABLE;
if (atomic_compare_exchange_strong(&timer->fsm, &expected_fsm, GPTIMER_FSM_RUN_WAIT)) {
portENTER_CRITICAL_SAFE(&timer->spinlock);
timer_ll_enable_alarm(timer->hal.dev, timer->timer_id, timer->flags.alarm_en);
timer_ll_enable_counter(timer->hal.dev, timer->timer_id, true);
atomic_store(&timer->fsm, GPTIMER_FSM_RUN);
portEXIT_CRITICAL_SAFE(&timer->spinlock);
}{...} else {
ESP_RETURN_ON_FALSE_ISR(false, ESP_ERR_INVALID_STATE, TAG, "timer is not ready for a new start");
}{...}
return ESP_OK;
}{ ... }
esp_err_t gptimer_stop(gptimer_handle_t timer)
{
ESP_RETURN_ON_FALSE_ISR(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
gptimer_fsm_t expected_fsm = GPTIMER_FSM_RUN;
if (atomic_compare_exchange_strong(&timer->fsm, &expected_fsm, GPTIMER_FSM_ENABLE_WAIT)) {
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);
atomic_store(&timer->fsm, GPTIMER_FSM_ENABLE);
portEXIT_CRITICAL_SAFE(&timer->spinlock);
}{...} else {
ESP_RETURN_ON_FALSE_ISR(false, ESP_ERR_INVALID_STATE, TAG, "timer is not running");
}{...}
return ESP_OK;
}{ ... }
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)) {
gptimer_alarm_event_data_t edata = {
.count_value = timer_hal_capture_and_get_counter_value(&timer->hal),
.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));
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();
}{...}
}{ ... }