mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
gptimer: acquire pm lock for xtal clock source
This commit is contained in:
parent
885e501d99
commit
de8409fa88
@ -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
|
||||
*/
|
||||
@ -330,9 +330,14 @@ esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, const timer
|
||||
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
|
||||
timer_hal_init(hal, group_num, timer_num);
|
||||
timer_hal_set_counter_value(hal, 0);
|
||||
|
||||
timer_src_clk_t clk_src = TIMER_SRC_CLK_DEFAULT;
|
||||
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)config->clk_src);
|
||||
timer_ll_set_clock_source(p_timer_obj[group_num][timer_num]->hal.dev, timer_num, (gptimer_clock_source_t)clk_src);
|
||||
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);
|
||||
@ -340,7 +345,7 @@ esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, const timer
|
||||
timer_ll_enable_alarm(hal->dev, timer_num, config->alarm_en);
|
||||
timer_ll_enable_auto_reload(hal->dev, timer_num, config->auto_reload);
|
||||
timer_ll_enable_counter(hal->dev, timer_num, config->counter_en);
|
||||
p_timer_obj[group_num][timer_num]->clk_src = config->clk_src;
|
||||
p_timer_obj[group_num][timer_num]->clk_src = clk_src;
|
||||
p_timer_obj[group_num][timer_num]->alarm_en = config->alarm_en;
|
||||
p_timer_obj[group_num][timer_num]->auto_reload_en = config->auto_reload;
|
||||
p_timer_obj[group_num][timer_num]->direction = config->counter_dir;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -433,44 +433,39 @@ 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)
|
||||
{
|
||||
unsigned int counter_src_hz = 0;
|
||||
esp_err_t ret = ESP_OK;
|
||||
int timer_id = timer->timer_id;
|
||||
// [clk_tree] TODO: replace the following switch table by clk_tree API
|
||||
|
||||
// to make the gptimer work reliable, the source clock must stay alive and unchanged
|
||||
// driver will create different pm lock for that purpose, according to different clock source
|
||||
__attribute__((unused)) esp_pm_lock_type_t pm_lock_type = ESP_PM_NO_LIGHT_SLEEP;
|
||||
|
||||
switch (src_clk) {
|
||||
#if SOC_TIMER_GROUP_SUPPORT_APB
|
||||
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
|
||||
// APB clock frequency can be changed during DFS
|
||||
pm_lock_type = ESP_PM_APB_FREQ_MAX;
|
||||
break;
|
||||
#endif // SOC_TIMER_GROUP_SUPPORT_APB
|
||||
|
||||
#if SOC_TIMER_GROUP_SUPPORT_PLL_F40M
|
||||
case GPTIMER_CLK_SRC_PLL_F40M:
|
||||
counter_src_hz = 40 * 1000 * 1000;
|
||||
#if CONFIG_PM_ENABLE
|
||||
sprintf(timer->pm_lock_name, "gptimer_%d_%d", timer->group->group_id, timer_id); // e.g. gptimer_0_0
|
||||
// PLL_F40M will be turned off when DFS switches CPU clock source to XTAL
|
||||
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;
|
||||
#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
|
||||
|
||||
#if SOC_TIMER_GROUP_SUPPORT_XTAL
|
||||
case GPTIMER_CLK_SRC_XTAL:
|
||||
counter_src_hz = esp_clk_xtal_freq();
|
||||
break;
|
||||
#endif // SOC_TIMER_GROUP_SUPPORT_XTAL
|
||||
|
||||
default:
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "clock source %d is not support", src_clk);
|
||||
break;
|
||||
@ -482,7 +477,14 @@ static esp_err_t gptimer_select_periph_clock(gptimer_t *timer, gptimer_clock_sou
|
||||
if (timer->resolution_hz != resolution_hz) {
|
||||
ESP_LOGW(TAG, "resolution lost, expect %"PRIu32", real %"PRIu32, resolution_hz, timer->resolution_hz);
|
||||
}
|
||||
return ret;
|
||||
|
||||
// create pm lock
|
||||
#if CONFIG_PM_ENABLE
|
||||
sprintf(timer->pm_lock_name, "gptimer_%d_%d", timer->group->group_id, timer_id); // e.g. gptimer_0_0
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_lock_type, 0, timer->pm_lock_name, &timer->pm_lock), TAG, "create pm lock failed");
|
||||
#endif // CONFIG_PM_ENABLE
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Put the default ISR handler in the IRAM for better performance
|
||||
|
@ -261,11 +261,9 @@ Alarm value can be updated dynamically inside the ISR handler callback, by chang
|
||||
Power Management
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
When power management is enabled (i.e. :ref:`CONFIG_PM_ENABLE` is on), the system will adjust the APB frequency before going into Light-sleep mode, thus potentially changing the period of a GPTimer's counting step and leading to inaccurate time keeping.
|
||||
There're some power management strategies, which might turn off or change the frequency of GPTimer's source clock to save power consumption. For example, during DFS, APB clock will be scaled down. If light-sleep is also enabled, PLL and XTAL clocks will be powered off. Both of them can result in an inaccurate time keeping.
|
||||
|
||||
However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_APB_FREQ_MAX`. Whenever the driver creates a GPTimer instance that has selected :cpp:enumerator:`GPTIMER_CLK_SRC_APB` as its clock source, the driver will guarantee that the power management lock is acquired when enabling the timer by :cpp:func:`gptimer_enable`. Likewise, the driver releases the lock when :cpp:func:`gptimer_disable` is called for that timer.
|
||||
|
||||
If other gptimer clock sources are selected such as :cpp:enumerator:`GPTIMER_CLK_SRC_XTAL`, then the driver will not install power management lock. The XTAL clock source is more suitable for a low power application as long as the source clock can still provide sufficient resolution.
|
||||
The driver can prevent the above situation from happening by creating different power management lock according to different clock source. The driver will increase the reference count of that power management lock in the :cpp:func:`gptimer_enable` and decrease it in the :cpp:func:`gptimer_disable`. So we can ensure the clock source is stable between :cpp:func:`gptimer_enable` and :cpp:func:`gptimer_disable`.
|
||||
|
||||
.. _iram-safe:
|
||||
|
||||
|
@ -261,11 +261,9 @@
|
||||
电源管理
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
当使能电源管理时(即 :ref:`CONFIG_PM_ENABLE` 已打开),系统将在进入 Light-sleep 模式之前调整 APB 频率,从而可能会改变通用定时器的计数步骤周期,导致计时不准确。
|
||||
有些电源管理的策略会在某些时刻关闭时钟源,或者改变时钟源的频率,以求降低功耗。比如在启用 DFS 后, APB 时钟源会降低频率。如果浅睡眠(light sleep) 模式也被开启, PLL 和 XTAL 时钟都会被默认关闭,从而导致 GPTimer 的计时不准确。
|
||||
|
||||
然而,驱动程序可以通过获取类型为 :cpp:enumerator:`ESP_PM_APB_FREQ_MAX` 的电源管理锁来阻止系统更改 APB 频率。每当驱动程序创建一个通用定时器实例,且该实例选择 :cpp:enumerator:`GPTIMER_CLK_SRC_APB` 作为其时钟源的时,驱动程序会确保在通过 :cpp:func:`gptimer_enable` 使能定时器时,已经获取了电源管理锁。同样,当为该定时器调用 :cpp:func:`gptimer_disable` 时,驱动程序会释放电源管理锁。
|
||||
|
||||
如果选择 :cpp:enumerator:`GPTIMER_CLK_SRC_XTAL` 等其他时钟源,那么驱动程序不会安装电源管理锁。只要时钟源仍可提供足够的分辨率,XTAL 时钟源就更适合低功耗应用。
|
||||
驱动程序会根据具体的时钟源选择,通过创建不同的电源锁来避免上述情况的发生。驱动会在 :cpp:func:`gptimer_enable` 函数中增加电源锁的引用计数,并在 :cpp:func:`gptimer_disable` 函数中减少电源锁的引用计数,从而保证了在 :cpp:func:`gptimer_enable` 和 :cpp:func:`gptimer_disable` 之间, GPTimer 的时钟源始处于稳定工作的状态。
|
||||
|
||||
.. _iram-safe:
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user