Merge branch 'refactor/enable_io_mux_clock' into 'master'

fix(iomux): move source gating to IOMUX consumers

Closes IDF-12975 and IDF-12852

See merge request espressif/esp-idf!48530
This commit is contained in:
morris
2026-05-25 12:31:50 +08:00
35 changed files with 395 additions and 357 deletions

View File

@@ -13,6 +13,7 @@
#include "esp_private/gpio.h"
#include "esp_private/io_mux.h"
#include "esp_private/esp_clk.h"
#include "esp_private/esp_clk_tree_common.h"
#include "ana_cmpr_private.h"
/* Global static object of the Analog Comparator unit */
@@ -133,12 +134,22 @@ static void ana_cmpr_destroy_unit(ana_cmpr_handle_t cmpr)
if (cmpr->intr_handle) {
esp_intr_free(cmpr->intr_handle);
}
free(cmpr);
// Disable function clock first
analog_cmpr_ll_enable_function_clock(unit_id, false);
// Disable bus clock last
analog_cmpr_ll_enable_bus_clock(unit_id, false);
// Disable the clock source if it is enabled by this driver
#if ANALOG_CMPR_LL_GET(IP_VERSION) <= 1
if (cmpr->io_mux_acquired) {
io_mux_release_clock_source(cmpr->clk_src);
cmpr->io_mux_acquired = false;
}
#endif
if (cmpr->clk_src != SOC_MOD_CLK_INVALID) {
esp_clk_tree_enable_src(cmpr->clk_src, false);
}
free(cmpr);
}
#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1
@@ -301,6 +312,8 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t *
// analog comparator unit must be allocated from internal memory because it contains atomic variable
ana_cmpr_hdl = heap_caps_calloc(1, sizeof(struct ana_cmpr_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_GOTO_ON_FALSE(ana_cmpr_hdl, ESP_ERR_NO_MEM, err, TAG, "no memory for analog comparator object");
ana_cmpr_hdl->clk_src = SOC_MOD_CLK_INVALID;
ana_cmpr_hdl->io_mux_acquired = false;
/* Assign analog comparator unit */
ana_cmpr_hdl->dev = ANALOG_CMPR_LL_GET_HW(unit_id);
@@ -316,18 +329,27 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t *
analog_cmpr_ll_reset_core(unit_id);
// Set clock source (use default if not specified in config)
ana_cmpr_clk_src_t clk_src = config->clk_src ? config->clk_src : ANA_CMPR_CLK_SRC_DEFAULT;
soc_module_clk_t clk_src = config->clk_src ? config->clk_src : ANA_CMPR_CLK_SRC_DEFAULT;
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src(clk_src, true), err, TAG, "enable clock source failed");
#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1
analog_cmpr_ll_set_clk_src(unit_id, clk_src);
// Set clock divider to 1
analog_cmpr_ll_set_clk_div(unit_id, 1);
// Enable function clock
analog_cmpr_ll_enable_function_clock(unit_id, true);
ana_cmpr_hdl->clk_src = clk_src;
#else
// Analog comparator located in the IO MUX module in older chips, so the clock source is shared with IO MUX.
ESP_GOTO_ON_ERROR(io_mux_set_clock_source((soc_module_clk_t)clk_src), err, TAG, "clock source conflicts with other IOMUX consumers");
ret = io_mux_acquire_clock_source(clk_src);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "clock source conflicts with other IOMUX consumers");
esp_clk_tree_enable_src(clk_src, false);
goto err;
}
ana_cmpr_hdl->io_mux_acquired = true;
ana_cmpr_hdl->clk_src = clk_src;
#endif
ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &ana_cmpr_hdl->src_clk_freq_hz),
ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &ana_cmpr_hdl->src_clk_freq_hz),
err, TAG, "get source clock frequency failed");
// init the default source and reference channels according to the config

View File

@@ -103,6 +103,8 @@ struct ana_cmpr_t {
intr_handle_t intr_handle; // Interrupt handle
uint32_t intr_mask; // Interrupt mask
int intr_priority; // Interrupt priority
soc_module_clk_t clk_src; // Clock source of the Analog Comparator unit
bool io_mux_acquired; // Whether IO MUX clock source was acquired (IP v1 only)
uint32_t src_clk_freq_hz; // Source clock frequency of the Analog Comparator unit
ana_cmpr_src_chan_t src_chans[ANALOG_CMPR_LL_GET(SRC_CHANNEL_NUM)]; // The source channel objects in the unit
ana_cmpr_ref_chan_t ref_chan; // The reference channel object in the unit

View File

@@ -15,6 +15,9 @@
#define GLITCH_FILTER_PM_LOCK_NAME_LEN_MAX 16
///!< Logging settings
#define TAG "gpio-filter"
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -5,16 +5,16 @@
*/
#include <sys/cdefs.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "esp_check.h"
#include "esp_private/glitch_filter_priv.h"
#include "glitch_filter_priv.h"
#include "esp_private/io_mux.h"
#include "soc/soc_caps.h"
#include "hal/gpio_glitch_filter_ll.h"
#include "esp_pm.h"
#include "esp_clk_tree.h"
static const char *TAG = "gpio-filter";
#include "esp_private/esp_clk_tree_common.h"
typedef struct gpio_flex_glitch_filter_t gpio_flex_glitch_filter_t;
@@ -28,6 +28,8 @@ struct gpio_flex_glitch_filter_t {
gpio_glitch_filter_t base;
gpio_flex_glitch_filter_group_t *group;
uint32_t filter_id;
soc_module_clk_t clk_src;
bool io_mux_clk_acquired;
#if CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock;
char pm_lock_name[GLITCH_FILTER_PM_LOCK_NAME_LEN_MAX]; // pm lock name
@@ -62,11 +64,10 @@ static esp_err_t gpio_filter_register_to_group(gpio_flex_glitch_filter_t *filter
static esp_err_t gpio_filter_destroy(gpio_flex_glitch_filter_t *filter)
{
gpio_flex_glitch_filter_group_t *group = &s_gpio_glitch_filter_group;
gpio_flex_glitch_filter_group_t *group = filter->group;
int filter_id = filter->filter_id;
// unregister the filter from the group
if (filter->group) {
if (group) {
portENTER_CRITICAL(&group->spinlock);
group->filters[filter_id] = NULL;
portEXIT_CRITICAL(&group->spinlock);
@@ -78,6 +79,13 @@ static esp_err_t gpio_filter_destroy(gpio_flex_glitch_filter_t *filter)
}
#endif
if (filter->io_mux_clk_acquired) {
io_mux_release_clock_source(filter->clk_src);
}
if (filter->clk_src != SOC_MOD_CLK_INVALID) {
esp_clk_tree_enable_src(filter->clk_src, false);
}
free(filter);
return ESP_OK;
}
@@ -101,8 +109,7 @@ static esp_err_t gpio_flex_glitch_filter_enable(gpio_glitch_filter_t *filter)
}
#endif
int filter_id = flex_filter->filter_id;
gpio_ll_glitch_filter_enable(s_gpio_glitch_filter_group.hw, filter_id, true);
gpio_ll_glitch_filter_enable(s_gpio_glitch_filter_group.hw, flex_filter->filter_id, true);
filter->fsm = GLITCH_FILTER_FSM_ENABLE;
return ESP_OK;
}
@@ -112,8 +119,7 @@ static esp_err_t gpio_flex_glitch_filter_disable(gpio_glitch_filter_t *filter)
ESP_RETURN_ON_FALSE(filter->fsm == GLITCH_FILTER_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "filter not in enable state");
gpio_flex_glitch_filter_t *flex_filter = __containerof(filter, gpio_flex_glitch_filter_t, base);
int filter_id = flex_filter->filter_id;
gpio_ll_glitch_filter_enable(s_gpio_glitch_filter_group.hw, filter_id, false);
gpio_ll_glitch_filter_enable(s_gpio_glitch_filter_group.hw, flex_filter->filter_id, false);
#if CONFIG_PM_ENABLE
// release pm lock
@@ -130,20 +136,27 @@ esp_err_t gpio_new_flex_glitch_filter(const gpio_flex_glitch_filter_config_t *co
{
esp_err_t ret = ESP_OK;
gpio_flex_glitch_filter_t *filter = NULL;
ESP_GOTO_ON_FALSE(config && ret_filter, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
ESP_GOTO_ON_FALSE(GPIO_IS_VALID_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, err, TAG, "invalid gpio number");
ESP_RETURN_ON_FALSE(config && ret_filter, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, TAG, "invalid gpio number");
// allocate driver object
filter = heap_caps_calloc(1, sizeof(gpio_flex_glitch_filter_t), FILTER_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(filter, ESP_ERR_NO_MEM, err, TAG, "no memory for flex glitch filter");
ESP_RETURN_ON_FALSE(filter, ESP_ERR_NO_MEM, TAG, "no memory for flex glitch filter");
filter->clk_src = SOC_MOD_CLK_INVALID; // default to invalid, will be set later
filter->io_mux_clk_acquired = false;
// register the filter to the group
ESP_GOTO_ON_ERROR(gpio_filter_register_to_group(filter), err, TAG, "register filter to group failed");
int filter_id = filter->filter_id;
// set clock source
soc_module_clk_t clk_src = config->clk_src ? config->clk_src : GLITCH_FILTER_CLK_SRC_DEFAULT;
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src(clk_src, true), err, TAG, "enable IOMUX clock source failed");
filter->clk_src = clk_src;
ESP_GOTO_ON_ERROR(io_mux_acquire_clock_source(clk_src), err, TAG, "acquire IOMUX clock source failed");
filter->io_mux_clk_acquired = true;
uint32_t clk_freq_hz = 0;
ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)config->clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_freq_hz),
ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_freq_hz),
err, TAG, "get clock source frequency failed");
// create pm_lock according to different clock source
@@ -159,9 +172,6 @@ esp_err_t gpio_new_flex_glitch_filter(const gpio_flex_glitch_filter_config_t *co
ESP_GOTO_ON_FALSE(window_thres_ticks && window_thres_ticks <= window_width_ticks && window_width_ticks <= GPIO_LL_GLITCH_FILTER_MAX_WINDOW,
ESP_ERR_INVALID_ARG, err, TAG, "invalid or out of range window width/threshold");
// Glitch filter's clock source is same to the IOMUX clock
ESP_GOTO_ON_ERROR(io_mux_set_clock_source((soc_module_clk_t)(config->clk_src)), err, TAG, "set IO MUX clock source failed");
// make sure the filter is disabled
gpio_ll_glitch_filter_enable(s_gpio_glitch_filter_group.hw, filter_id, false);
// apply the filter to the GPIO

View File

@@ -1,13 +1,11 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_check.h"
#include "esp_private/glitch_filter_priv.h"
static const char *TAG = "gpio-filter";
#include "glitch_filter_priv.h"
/////////// Public abstract functions ///////////

View File

@@ -5,22 +5,24 @@
*/
#include <sys/cdefs.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "esp_check.h"
#include "esp_pm.h"
#include "esp_private/glitch_filter_priv.h"
#include "glitch_filter_priv.h"
#include "hal/gpio_ll.h"
#include "esp_clk_tree.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/io_mux.h"
#include "hal/gpio_caps.h"
static const char *TAG = "gpio-filter";
/**
* @brief Type of GPIO pin glitch filter
*/
typedef struct gpio_pin_glitch_filter_t {
gpio_glitch_filter_t base;
soc_module_clk_t clk_src;
bool io_mux_clk_acquired;
#if CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock;
char pm_lock_name[GLITCH_FILTER_PM_LOCK_NAME_LEN_MAX]; // pm lock name
@@ -29,6 +31,13 @@ typedef struct gpio_pin_glitch_filter_t {
static esp_err_t gpio_filter_destroy(gpio_pin_glitch_filter_t *filter)
{
if (filter->io_mux_clk_acquired) {
io_mux_release_clock_source(filter->clk_src);
}
if (filter->clk_src != SOC_MOD_CLK_INVALID) {
esp_clk_tree_enable_src(filter->clk_src, false);
}
#if CONFIG_PM_ENABLE
if (filter->pm_lock) {
esp_pm_lock_delete(filter->pm_lock);
@@ -90,12 +99,20 @@ esp_err_t gpio_new_pin_glitch_filter(const gpio_pin_glitch_filter_config_t *conf
filter = heap_caps_calloc(1, sizeof(gpio_pin_glitch_filter_t), FILTER_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(filter, ESP_ERR_NO_MEM, err, TAG, "no memory for pin glitch filter");
filter->clk_src = SOC_MOD_CLK_INVALID;
filter->io_mux_clk_acquired = false;
soc_module_clk_t clk_src = config->clk_src ? config->clk_src : GLITCH_FILTER_CLK_SRC_DEFAULT;
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src(clk_src, true), err, TAG, "enable IOMUX clock source failed");
filter->clk_src = clk_src;
ESP_GOTO_ON_ERROR(io_mux_acquire_clock_source(clk_src), err, TAG, "acquire IOMUX clock source failed");
filter->io_mux_clk_acquired = true;
// create pm lock according to different clock source
#if CONFIG_PM_ENABLE
esp_pm_lock_type_t lock_type = ESP_PM_NO_LIGHT_SLEEP;
#if GPIO_CAPS_GET(FILTER_CLK_SUPPORT_APB)
if (config->clk_src == GLITCH_FILTER_CLK_SRC_APB) {
if (clk_src == (soc_module_clk_t)GLITCH_FILTER_CLK_SRC_APB) {
lock_type = ESP_PM_APB_FREQ_MAX;
}
#endif // GPIO_CAPS_GET(FILTER_CLK_SUPPORT_APB)
@@ -104,9 +121,6 @@ esp_err_t gpio_new_pin_glitch_filter(const gpio_pin_glitch_filter_config_t *conf
err, TAG, "create pm_lock failed");
#endif // CONFIG_PM_ENABLE
// Glitch filter's clock source is same to the IOMUX clock
ESP_GOTO_ON_ERROR(io_mux_set_clock_source((soc_module_clk_t)(config->clk_src)), err, TAG, "set IO MUX clock source failed");
filter->base.gpio_num = config->gpio_num;
filter->base.fsm = GLITCH_FILTER_FSM_INIT;
filter->base.del = gpio_pin_glitch_filter_del;

View File

@@ -5,6 +5,7 @@
*/
#include <stdlib.h>
#include <stdbool.h>
#include <stdatomic.h>
#include <sys/lock.h>
#include "sdkconfig.h"
@@ -29,6 +30,7 @@
#include "hal/sdm_ll.h"
#include "hal/hal_utils.h"
#include "esp_private/esp_clk.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/io_mux.h"
#include "esp_private/gpio.h"
#include "esp_private/sleep_retention.h"
@@ -60,7 +62,9 @@ struct sdm_group_t {
portMUX_TYPE spinlock; // to protect per-group register level concurrent access
sdm_hal_context_t hal; // hal context
sdm_channel_t *channels[SDM_CAPS_GET(CHANS_PER_INST)]; // array of sdm channels
sdm_clock_source_t clk_src; // Clock source
soc_module_clk_t clk_src; // Clock source
bool io_mux_clk_acquired;
uint32_t src_clk_hz; // Source clock frequency in Hz
#if CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock; // PM lock, to prevent the system going into light sleep when SDM is running
#endif
@@ -113,107 +117,158 @@ static void sdm_create_retention_module(sdm_group_t *group)
}
#endif // SDM_USE_RETENTION_LINK
static sdm_group_t *sdm_acquire_group_handle(int group_id, sdm_clock_source_t clk_src)
static sdm_group_t *sdm_group_acquire(int group_id)
{
bool new_group = false;
sdm_group_t *group = NULL;
// prevent install sdm group concurrently
_lock_acquire(&s_platform.mutex);
if (!s_platform.groups[group_id]) {
group = heap_caps_calloc(1, sizeof(sdm_group_t), SDM_MEM_ALLOC_CAPS);
if (group) {
new_group = true;
s_platform.groups[group_id] = group; // register to platform
// initialize sdm group members
group->group_id = group_id;
group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
group->clk_src = clk_src;
#if SDM_USE_RETENTION_LINK
sleep_retention_module_t module = soc_sdm_retention_infos[group_id].module;
sleep_retention_module_init_param_t init_param = {
.cbs = {
.create = {
.handle = sdm_create_sleep_retention_link_cb,
.arg = group,
},
},
.attribute = SLEEP_RETENTION_MODULE_ATTR_ATTACH,
.depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM)
};
// retention module init must be called BEFORE the hal init
if (sleep_retention_module_init(module, &init_param) != ESP_OK) {
ESP_LOGW(TAG, "init sleep retention failed on SDM Group%d, power domain may be turned off during sleep", group_id);
}
#endif // SDM_USE_RETENTION_LINK
// [IDF-12975]: enable APB register clock explicitly
// initialize HAL context
sdm_hal_init_config_t hal_config = {
.group_id = group_id,
};
sdm_hal_init(&group->hal, &hal_config);
if (!group) {
_lock_release(&s_platform.mutex);
return NULL;
}
s_platform.groups[group_id] = group;
group->group_id = group_id;
group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
group->clk_src = SOC_MOD_CLK_INVALID;
} else {
group = s_platform.groups[group_id];
}
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]++;
}
s_platform.group_ref_counts[group_id]++;
_lock_release(&s_platform.mutex);
if (new_group) {
ESP_LOGD(TAG, "new group (%d) at %p", group_id, group);
#if CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_type = ESP_PM_NO_LIGHT_SLEEP;
#if SDM_CAPS_GET(FUNC_CLOCK_SUPPORT_APB)
if (clk_src == SDM_CLK_SRC_APB) {
pm_type = ESP_PM_APB_FREQ_MAX;
}
#endif // SDM_CAPS_GET(FUNC_CLOCK_SUPPORT_APB)
if (esp_pm_lock_create(pm_type, 0, soc_sdm_signals[group_id].module_name, &group->pm_lock) != ESP_OK) {
ESP_LOGE(TAG, "fail to create PM lock for group %d", group_id);
}
#endif // CONFIG_PM_ENABLE
}
return group;
}
static void sdm_release_group_handle(sdm_group_t *group)
static esp_err_t sdm_group_install(sdm_group_t *group, soc_module_clk_t clk_src)
{
esp_err_t ret = ESP_OK;
int group_id = group->group_id;
_lock_acquire(&s_platform.mutex);
if (group->clk_src != SOC_MOD_CLK_INVALID) {
ret = (group->clk_src == clk_src) ? ESP_OK : ESP_ERR_INVALID_STATE;
_lock_release(&s_platform.mutex);
return ret;
}
#if SDM_USE_RETENTION_LINK
sleep_retention_module_t module = soc_sdm_retention_infos[group_id].module;
sleep_retention_module_init_param_t init_param = {
.cbs = {
.create = {
.handle = sdm_create_sleep_retention_link_cb,
.arg = group,
},
},
.attribute = SLEEP_RETENTION_MODULE_ATTR_ATTACH,
.depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM)
};
// retention module init must be called BEFORE the hal init
if (sleep_retention_module_init(module, &init_param) != ESP_OK) {
ESP_LOGW(TAG, "init sleep retention failed on SDM Group%d, power domain may be turned off during sleep", group_id);
}
#endif // SDM_USE_RETENTION_LINK
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src(clk_src, true), err, TAG, "enable clock source failed for group %d", group_id);
group->clk_src = clk_src;
// SDM clock comes from IO MUX, but IO MUX clock might be shared with other submodules as well
ESP_GOTO_ON_ERROR(io_mux_acquire_clock_source(clk_src), err, TAG, "acquire IO MUX clock source failed for group %d", group_id);
group->io_mux_clk_acquired = true;
esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &group->src_clk_hz);
sdm_hal_init_config_t hal_config = {
.group_id = group_id,
};
sdm_hal_init(&group->hal, &hal_config);
#if CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_type = ESP_PM_NO_LIGHT_SLEEP;
#if SDM_CAPS_GET(FUNC_CLOCK_SUPPORT_APB)
if (clk_src == (soc_module_clk_t)SDM_CLK_SRC_APB) {
pm_type = ESP_PM_APB_FREQ_MAX;
}
#endif // SDM_CAPS_GET(FUNC_CLOCK_SUPPORT_APB)
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_type, 0, soc_sdm_signals[group_id].module_name, &group->pm_lock),
err, TAG, "fail to create PM lock for group %d", group_id);
#endif // CONFIG_PM_ENABLE
_lock_release(&s_platform.mutex);
ESP_LOGD(TAG, "new group (%d) at %p", group_id, group);
return ESP_OK;
err:
#if CONFIG_PM_ENABLE
if (group->pm_lock) {
esp_pm_lock_delete(group->pm_lock);
group->pm_lock = NULL;
}
#endif
if (group->io_mux_clk_acquired) {
sdm_hal_deinit(&group->hal);
io_mux_release_clock_source(group->clk_src);
group->io_mux_clk_acquired = false;
}
if (group->clk_src != SOC_MOD_CLK_INVALID) {
esp_clk_tree_enable_src(group->clk_src, false);
group->clk_src = SOC_MOD_CLK_INVALID;
}
_lock_release(&s_platform.mutex);
return ret;
}
static void sdm_group_uninstall(sdm_group_t *group)
{
if (group->clk_src == SOC_MOD_CLK_INVALID) {
return;
}
sdm_hal_deinit(&group->hal);
if (group->io_mux_clk_acquired) {
io_mux_release_clock_source(group->clk_src);
group->io_mux_clk_acquired = false;
}
if (group->clk_src != SOC_MOD_CLK_INVALID) {
esp_clk_tree_enable_src(group->clk_src, false);
group->clk_src = SOC_MOD_CLK_INVALID;
}
#if SDM_USE_RETENTION_LINK
sleep_retention_module_t module = soc_sdm_retention_infos[group->group_id].module;
sleep_retention_module_detach(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 // SDM_USE_RETENTION_LINK
#if CONFIG_PM_ENABLE
if (group->pm_lock) {
esp_pm_lock_delete(group->pm_lock);
group->pm_lock = NULL;
}
#endif // CONFIG_PM_ENABLE
}
static void sdm_group_release(sdm_group_t *group)
{
int group_id = group->group_id;
bool do_deinitialize = false;
bool do_teardown = 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; // deregister from platform
sdm_hal_deinit(&group->hal);
#if SDM_USE_RETENTION_LINK
sleep_retention_module_t module = soc_sdm_retention_infos[group_id].module;
sleep_retention_module_detach(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 // SDM_USE_RETENTION_LINK
s_platform.groups[group_id] = NULL;
do_teardown = true;
}
_lock_release(&s_platform.mutex);
if (do_deinitialize) {
#if CONFIG_PM_ENABLE
if (group->pm_lock) {
esp_pm_lock_delete(group->pm_lock);
}
#endif
if (do_teardown) {
sdm_group_uninstall(group);
free(group);
ESP_LOGD(TAG, "del group (%d)", group_id);
}
@@ -221,11 +276,20 @@ static void sdm_release_group_handle(sdm_group_t *group)
static esp_err_t sdm_register_to_group(sdm_channel_t *chan, sdm_clock_source_t clk_src)
{
esp_err_t ret = ESP_OK;
sdm_group_t *group = NULL;
int chan_id = -1;
for (int i = 0; i < SDM_CAPS_GET(INST_NUM); i++) {
group = sdm_acquire_group_handle(i, clk_src);
group = sdm_group_acquire(i);
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", i);
ret = sdm_group_install(group, clk_src);
if (ret == ESP_ERR_INVALID_STATE) {
sdm_group_release(group);
continue;
}
ESP_GOTO_ON_ERROR(ret, err, TAG, "install group (%d) failed", i);
// loop to search free unit in the group
portENTER_CRITICAL(&group->spinlock);
for (int j = 0; j < SDM_CAPS_GET(CHANS_PER_INST); j++) {
@@ -239,13 +303,19 @@ static esp_err_t sdm_register_to_group(sdm_channel_t *chan, sdm_clock_source_t c
}
portEXIT_CRITICAL(&group->spinlock);
if (chan_id < 0) {
sdm_release_group_handle(group);
sdm_group_release(group);
} else {
break;
}
}
ESP_RETURN_ON_FALSE(chan_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free channels");
return ESP_OK;
err:
if (group) {
sdm_group_release(group);
}
return ret;
}
static void sdm_unregister_from_group(sdm_channel_t *chan)
@@ -256,18 +326,18 @@ static void sdm_unregister_from_group(sdm_channel_t *chan)
group->channels[chan_id] = NULL;
portEXIT_CRITICAL(&group->spinlock);
// channel has a reference on group, release it now
sdm_release_group_handle(group);
sdm_group_release(group);
}
static esp_err_t sdm_destroy(sdm_channel_t *chan)
{
if (chan->group) {
sdm_unregister_from_group(chan);
}
if (chan->gpio_num >= 0) {
gpio_output_disable(chan->gpio_num);
esp_gpio_revoke(BIT64(chan->gpio_num));
}
if (chan->group) {
sdm_unregister_from_group(chan);
}
free(chan);
return ESP_OK;
}
@@ -289,22 +359,13 @@ esp_err_t sdm_new_channel(const sdm_config_t *config, sdm_channel_handle_t *ret_
ESP_RETURN_ON_FALSE(chan, ESP_ERR_NO_MEM, TAG, "no mem for channel");
chan->gpio_num = GPIO_NUM_NC; // default to NC, will be set later
sdm_clock_source_t clk_src = config->clk_src ? config->clk_src : SDM_CLK_SRC_DEFAULT;
soc_module_clk_t clk_src = config->clk_src ? config->clk_src : SDM_CLK_SRC_DEFAULT;
// register channel to the group
ESP_GOTO_ON_ERROR(sdm_register_to_group(chan, clk_src), err, TAG, "register to group failed");
sdm_group_t *group = chan->group;
int group_id = group->group_id;
int chan_id = chan->chan_id;
ESP_GOTO_ON_FALSE(group->clk_src == clk_src, ESP_ERR_INVALID_ARG, err, TAG, "clock source conflict");
// SDM clock comes from IO MUX, but IO MUX clock might be shared with other submodules as well
ESP_GOTO_ON_ERROR(io_mux_set_clock_source((soc_module_clk_t)clk_src), err, TAG, "set IO MUX clock source failed");
uint32_t src_clk_hz = 0;
ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src,
ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_hz), err, TAG, "get source clock frequency failed");
// Reserve the new GPIO
uint64_t old_gpio_rsv_mask = esp_gpio_reserve(BIT64(config->gpio_num));
if (old_gpio_rsv_mask & BIT64(config->gpio_num)) {
@@ -314,6 +375,7 @@ esp_err_t sdm_new_channel(const sdm_config_t *config, sdm_channel_handle_t *ret_
gpio_matrix_output(config->gpio_num, soc_sdm_signals[group_id].channels[chan_id].sig_id_matrix, config->flags.invert_out, false);
chan->gpio_num = config->gpio_num;
uint32_t src_clk_hz = group->src_clk_hz;
// set prescale based on sample rate
uint32_t prescale = 0;
hal_utils_clk_info_t clk_info = {

View File

@@ -39,6 +39,8 @@
#define GPIO_LL_INTR_SOURCE0 ETS_GPIO_INTR_SOURCE
#define GPIO_LL_CLK_SRC_SELECTABLE 1
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -38,6 +38,8 @@
#define GPIO_LL_INTR_SOURCE0 ETS_GPIO_INTR_SOURCE
#define GPIO_LL_CLK_SRC_SELECTABLE 1
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -40,6 +40,8 @@
#define GPIO_LL_INTR_SOURCE0 ETS_GPIO_INTERRUPT_PRO_SOURCE
#define GPIO_LL_CLK_SRC_SELECTABLE 1
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -38,6 +38,8 @@
#define GPIO_LL_INTR_SOURCE0 ETS_GPIO_INTR_SOURCE
#define GPIO_LL_CLK_SRC_SELECTABLE 1
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -38,6 +38,8 @@
#define GPIO_LL_INTR_SOURCE0 ETS_GPIO_INTERRUPT_PRO_SOURCE
#define GPIO_LL_CLK_SRC_SELECTABLE 1
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -39,6 +39,8 @@
#define GPIO_LL_PRO_CPU_2_INTR_ENA (BIT(1))
#define GPIO_LL_INTR_SOURCE0 ETS_GPIO_INTERRUPT_PRO_SOURCE
#define GPIO_LL_CLK_SRC_SELECTABLE 1
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -45,6 +45,8 @@
#define GPIO_LL_INTR_SOURCE0 ETS_GPIO_INTR0_SOURCE
#define GPIO_LL_CLK_SRC_SELECTABLE 1
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -46,6 +46,8 @@
#define GPIO_LL_INTR_SOURCE0 ETS_GPIO_INTR0_SOURCE
#define GPIO_LL_CLK_SRC_SELECTABLE 1
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -38,6 +38,7 @@ if(NOT non_os_build)
"esp_gpio_reserve.c"
"sar_tsens_ctrl.c"
"sar_periph_ctrl_common.c"
"io_mux.c"
"port/${target}/io_mux.c"
"port/${target}/esp_clk_tree.c"
"spi_bus_lock.c"

View File

@@ -17,17 +17,30 @@ extern "C" {
#endif
/**
* @brief Set the clock source for IO MUX
* @brief Acquire the clock source for IO MUX
*
* @note IO MUX clock is shared by submodules like SDM, Glitch Filter.
* The submodule drivers should call this function to detect if the user set the clock differently.
* Each consumer should call this function when starting to use a specific clock source,
* and call @ref io_mux_release_clock_source when done.
*
* @param clk_src The clock source for IO MUX
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALID_STATE: The IO MUX has been set to another clock source
* - ESP_ERR_INVALID_STATE: The IO MUX has been acquired with another clock source
*/
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src);
esp_err_t io_mux_acquire_clock_source(soc_module_clk_t clk_src);
/**
* @brief Release the clock source for IO MUX
*
* When the last consumer releases, the clock source will be switched back to SOC_MOD_CLK_XTAL.
*
* @param clk_src The clock source to release (must match the one used in acquire)
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALID_STATE: Reference count is zero or clock source mismatch
*/
esp_err_t io_mux_release_clock_source(soc_module_clk_t clk_src);
#if SOC_LP_IO_CLOCK_IS_INDEPENDENT
typedef struct {

View File

@@ -0,0 +1,90 @@
/*
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_private/io_mux.h"
#include "esp_private/critical_section.h"
#include "esp_private/periph_ctrl.h"
#include "soc/clk_tree_defs.h"
#include "hal/gpio_ll.h"
#if GPIO_LL_CLK_SRC_SELECTABLE
DEFINE_CRIT_SECTION_LOCK_STATIC(s_io_mux_clk_spinlock);
static soc_module_clk_t s_io_mux_clk_src = 0;
static uint32_t s_io_mux_clk_ref_count = 0;
#endif // GPIO_LL_CLK_SRC_SELECTABLE
esp_err_t io_mux_acquire_clock_source(soc_module_clk_t clk_src)
{
#if GPIO_LL_CLK_SRC_SELECTABLE
bool clk_conflict = false;
bool need_update_hw = false;
esp_os_enter_critical(&s_io_mux_clk_spinlock);
if (s_io_mux_clk_ref_count > 0 && s_io_mux_clk_src != clk_src) {
clk_conflict = true;
} else {
if (s_io_mux_clk_ref_count == 0) {
s_io_mux_clk_src = clk_src;
need_update_hw = true;
}
s_io_mux_clk_ref_count++;
}
esp_os_exit_critical(&s_io_mux_clk_spinlock);
if (clk_conflict) {
return ESP_ERR_INVALID_STATE;
}
if (need_update_hw) {
PERIPH_RCC_ATOMIC() {
gpio_ll_iomux_set_clk_src(clk_src);
}
}
return ESP_OK;
#else
(void)clk_src;
return ESP_OK;
#endif
}
esp_err_t io_mux_release_clock_source(soc_module_clk_t clk_src)
{
#if GPIO_LL_CLK_SRC_SELECTABLE
bool invalid = false;
bool need_revert_hw = false;
esp_os_enter_critical(&s_io_mux_clk_spinlock);
if (s_io_mux_clk_ref_count == 0 || s_io_mux_clk_src != clk_src) {
invalid = true;
} else {
s_io_mux_clk_ref_count--;
if (s_io_mux_clk_ref_count == 0) {
s_io_mux_clk_src = 0;
need_revert_hw = true;
}
}
esp_os_exit_critical(&s_io_mux_clk_spinlock);
if (invalid) {
return ESP_ERR_INVALID_STATE;
}
if (need_revert_hw) {
PERIPH_RCC_ATOMIC() {
gpio_ll_iomux_set_clk_src(SOC_MOD_CLK_XTAL);
}
}
return ESP_OK;
#else
(void)clk_src;
return ESP_OK;
#endif
}

View File

@@ -6,12 +6,6 @@
#include "esp_private/io_mux.h"
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
{
// IO MUX clock source is not selectable
return ESP_OK;
}
bool io_mux_is_lp_io_in_use(gpio_num_t gpio_num)
{
// ESP32 does not have SOC_LP_IO_CLOCK_IS_INDEPENDENT

View File

@@ -5,9 +5,3 @@
*/
#include "esp_private/io_mux.h"
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
{
// IO MUX clock source is not selectable
return ESP_OK;
}

View File

@@ -5,9 +5,3 @@
*/
#include "esp_private/io_mux.h"
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
{
// IO MUX clock source is not selectable
return ESP_OK;
}

View File

@@ -5,17 +5,14 @@
*/
#include "sdkconfig.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/io_mux.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/critical_section.h"
#include "hal/gpio_ll.h"
#include "hal/rtc_io_ll.h"
#define RTCIO_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
DEFINE_CRIT_SECTION_LOCK_STATIC(s_io_mux_spinlock);
static soc_module_clk_t s_io_mux_clk_src = 0; // by default, the clock source is not set explicitly by any consumer (e.g. SDM, Filter)
#if CONFIG_ULP_COPROC_ENABLED
RTC_DATA_ATTR
@@ -25,28 +22,6 @@ static rtc_io_status_t s_rtc_io_status = {
.rtc_io_using_mask = 0
};
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
{
bool clk_conflict = false;
// check if the IO MUX has been set to another clock source
esp_os_enter_critical(&s_io_mux_spinlock);
if (s_io_mux_clk_src != 0 && s_io_mux_clk_src != clk_src) {
clk_conflict = true;
} else {
s_io_mux_clk_src = clk_src;
}
esp_os_exit_critical(&s_io_mux_spinlock);
if (clk_conflict) {
return ESP_ERR_INVALID_STATE;
}
ESP_ERROR_CHECK(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true));
gpio_ll_iomux_set_clk_src(clk_src);
return ESP_OK;
}
void io_mux_enable_lp_io_clock(gpio_num_t gpio_num, bool enable)
{
uint32_t rtc_io_num = gpio_num - RTCIO_LL_GPIO_NUM_OFFSET;

View File

@@ -9,13 +9,11 @@
#include "esp_private/io_mux.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/critical_section.h"
#include "hal/gpio_ll.h"
#include "hal/rtc_io_ll.h"
#define RTCIO_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
DEFINE_CRIT_SECTION_LOCK_STATIC(s_io_mux_spinlock);
static soc_module_clk_t s_io_mux_clk_src = 0; // by default, the clock source is not set explicitly by any consumer (e.g. SDM, Filter)
#if CONFIG_ULP_COPROC_ENABLED
RTC_DATA_ATTR
@@ -25,27 +23,6 @@ static rtc_io_status_t s_rtc_io_status = {
.rtc_io_using_mask = 0
};
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
{
bool clk_conflict = false;
// check if the IO MUX has been set to another clock source
esp_os_enter_critical(&s_io_mux_spinlock);
if (s_io_mux_clk_src != 0 && s_io_mux_clk_src != clk_src) {
clk_conflict = true;
} else {
s_io_mux_clk_src = clk_src;
}
esp_os_exit_critical(&s_io_mux_spinlock);
if (clk_conflict) {
return ESP_ERR_INVALID_STATE;
}
gpio_ll_iomux_set_clk_src(clk_src);
return ESP_OK;
}
void io_mux_enable_lp_io_clock(gpio_num_t gpio_num, bool enable)
{
uint32_t rtc_io_num = gpio_num - RTCIO_LL_GPIO_NUM_OFFSET;

View File

@@ -7,40 +7,17 @@
#include "esp_private/io_mux.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/critical_section.h"
#include "hal/gpio_ll.h"
#include "hal/rtc_io_ll.h"
#define RTCIO_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
DEFINE_CRIT_SECTION_LOCK_STATIC(s_io_mux_spinlock);
static soc_module_clk_t s_io_mux_clk_src = 0; // by default, the clock source is not set explicitly by any consumer (e.g. SDM, Filter)
static rtc_io_status_t s_rtc_io_status = {
.rtc_io_enabled_cnt = { 0 },
.rtc_io_using_mask = 0
};
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
{
bool clk_conflict = false;
// check if the IO MUX has been set to another clock source
esp_os_enter_critical(&s_io_mux_spinlock);
if (s_io_mux_clk_src != 0 && s_io_mux_clk_src != clk_src) {
clk_conflict = true;
} else {
s_io_mux_clk_src = clk_src;
}
esp_os_exit_critical(&s_io_mux_spinlock);
if (clk_conflict) {
return ESP_ERR_INVALID_STATE;
}
gpio_ll_iomux_set_clk_src(clk_src);
return ESP_OK;
}
void io_mux_enable_lp_io_clock(gpio_num_t gpio_num, bool enable)
{
uint32_t rtc_io_num = gpio_num - RTCIO_LL_GPIO_NUM_OFFSET;

View File

@@ -7,40 +7,17 @@
#include "esp_private/io_mux.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/critical_section.h"
#include "hal/gpio_ll.h"
#include "hal/rtc_io_ll.h"
#define RTCIO_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
DEFINE_CRIT_SECTION_LOCK_STATIC(s_io_mux_spinlock);
static soc_module_clk_t s_io_mux_clk_src = 0; // by default, the clock source is not set explicitly by any consumer (e.g. SDM, Filter)
static rtc_io_status_t s_rtc_io_status = {
.rtc_io_enabled_cnt = { 0 },
.rtc_io_using_mask = 0
};
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
{
bool clk_conflict = false;
// check if the IO MUX has been set to another clock source
esp_os_enter_critical(&s_io_mux_spinlock);
if (s_io_mux_clk_src != 0 && s_io_mux_clk_src != clk_src) {
clk_conflict = true;
} else {
s_io_mux_clk_src = clk_src;
}
esp_os_exit_critical(&s_io_mux_spinlock);
if (clk_conflict) {
return ESP_ERR_INVALID_STATE;
}
gpio_ll_iomux_set_clk_src(clk_src);
return ESP_OK;
}
void io_mux_enable_lp_io_clock(gpio_num_t gpio_num, bool enable)
{
uint32_t rtc_io_num = gpio_num - RTCIO_LL_GPIO_NUM_OFFSET;

View File

@@ -8,40 +8,17 @@
#include "esp_private/io_mux.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/critical_section.h"
#include "hal/gpio_ll.h"
#include "hal/rtc_io_ll.h"
#define RTCIO_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
DEFINE_CRIT_SECTION_LOCK_STATIC(s_io_mux_spinlock);
static soc_module_clk_t s_io_mux_clk_src = 0; // by default, the clock source is not set explicitly by any consumer (e.g. SDM, Filter)
static rtc_io_status_t s_rtc_io_status = {
.rtc_io_enabled_cnt = { 0 },
.rtc_io_using_mask = 0
};
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
{
bool clk_conflict = false;
// check if the IO MUX has been set to another clock source
esp_os_enter_critical(&s_io_mux_spinlock);
if (s_io_mux_clk_src != 0 && s_io_mux_clk_src != clk_src) {
clk_conflict = true;
} else {
s_io_mux_clk_src = clk_src;
}
esp_os_exit_critical(&s_io_mux_spinlock);
if (clk_conflict) {
return ESP_ERR_INVALID_STATE;
}
gpio_ll_iomux_set_clk_src(clk_src);
return ESP_OK;
}
void io_mux_enable_lp_io_clock(gpio_num_t gpio_num, bool enable)
{
uint32_t rtc_io_num = gpio_num - RTCIO_LL_GPIO_NUM_OFFSET;

View File

@@ -8,40 +8,17 @@
#include "esp_private/io_mux.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/critical_section.h"
#include "hal/gpio_ll.h"
#include "hal/rtc_io_ll.h"
#define RTCIO_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
DEFINE_CRIT_SECTION_LOCK_STATIC(s_io_mux_spinlock);
static soc_module_clk_t s_io_mux_clk_src = 0; // by default, the clock source is not set explicitly by any consumer (e.g. SDM, Filter)
static rtc_io_status_t s_rtc_io_status = {
.rtc_io_enabled_cnt = { 0 },
.rtc_io_using_mask = 0
};
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
{
bool clk_conflict = false;
// check if the IO MUX has been set to another clock source
esp_os_enter_critical(&s_io_mux_spinlock);
if (s_io_mux_clk_src != 0 && s_io_mux_clk_src != clk_src) {
clk_conflict = true;
} else {
s_io_mux_clk_src = clk_src;
}
esp_os_exit_critical(&s_io_mux_spinlock);
if (clk_conflict) {
return ESP_ERR_INVALID_STATE;
}
gpio_ll_iomux_set_clk_src(clk_src);
return ESP_OK;
}
void io_mux_enable_lp_io_clock(gpio_num_t gpio_num, bool enable)
{
uint32_t rtc_io_num = gpio_num - RTCIO_LL_GPIO_NUM_OFFSET;

View File

@@ -6,19 +6,15 @@
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_check.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/io_mux.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/critical_section.h"
#include "hal/gpio_ll.h"
#include "hal/rtc_io_ll.h"
#include "soc/soc_caps.h"
#define RTCIO_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
DEFINE_CRIT_SECTION_LOCK_STATIC(s_io_mux_spinlock);
static soc_module_clk_t s_io_mux_clk_src = 0; // by default, the clock source is not set explicitly by any consumer (e.g. SDM, Filter)
#if CONFIG_ULP_COPROC_ENABLED
RTC_DATA_ATTR
@@ -28,29 +24,6 @@ static rtc_io_status_t s_rtc_io_status = {
.rtc_io_using_mask = 0
};
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
{
bool clk_conflict = false;
// check if the IO MUX has been set to another clock source
esp_os_enter_critical(&s_io_mux_spinlock);
if (s_io_mux_clk_src != 0 && s_io_mux_clk_src != clk_src) {
clk_conflict = true;
} else {
s_io_mux_clk_src = clk_src;
}
esp_os_exit_critical(&s_io_mux_spinlock);
if (clk_conflict) {
return ESP_ERR_INVALID_STATE;
}
ESP_ERROR_CHECK(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true));
PERIPH_RCC_ATOMIC() {
gpio_ll_iomux_set_clk_src(clk_src);
}
return ESP_OK;
}
void io_mux_enable_lp_io_clock(gpio_num_t gpio_num, bool enable)
{
uint32_t rtc_io_num = gpio_num - RTCIO_LL_GPIO_NUM_OFFSET;

View File

@@ -15,12 +15,6 @@
_rc_cnt ? (esp_os_enter_critical(&rtc_spinlock), 1) : 0; \
esp_os_exit_critical(&rtc_spinlock), _rc_cnt--)
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
{
// IO MUX clock source is not selectable
return ESP_OK;
}
extern portMUX_TYPE rtc_spinlock;
DEFINE_CRIT_SECTION_LOCK_STATIC(s_io_mux_spinlock);

View File

@@ -15,12 +15,6 @@
_rc_cnt ? (esp_os_enter_critical(&rtc_spinlock), 1) : 0; \
esp_os_exit_critical(&rtc_spinlock), _rc_cnt--)
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
{
// IO MUX clock source is not selectable
return ESP_OK;
}
extern portMUX_TYPE rtc_spinlock;
DEFINE_CRIT_SECTION_LOCK_STATIC(s_io_mux_spinlock);

View File

@@ -6,19 +6,15 @@
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_check.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/io_mux.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/critical_section.h"
#include "hal/gpio_ll.h"
#include "hal/rtc_io_ll.h"
#include "soc/soc_caps.h"
#define RTCIO_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
DEFINE_CRIT_SECTION_LOCK_STATIC(s_io_mux_spinlock);
static soc_module_clk_t s_io_mux_clk_src = 0; // by default, the clock source is not set explicitly by any consumer (e.g. SDM, Filter)
#if CONFIG_ULP_COPROC_ENABLED
RTC_DATA_ATTR
@@ -28,29 +24,6 @@ static rtc_io_status_t s_rtc_io_status = {
.rtc_io_using_mask = 0
};
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
{
bool clk_conflict = false;
// check if the IO MUX has been set to another clock source
esp_os_enter_critical(&s_io_mux_spinlock);
if (s_io_mux_clk_src != 0 && s_io_mux_clk_src != clk_src) {
clk_conflict = true;
} else {
s_io_mux_clk_src = clk_src;
}
esp_os_exit_critical(&s_io_mux_spinlock);
if (clk_conflict) {
return ESP_ERR_INVALID_STATE;
}
ESP_ERROR_CHECK(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true));
PERIPH_RCC_ATOMIC() {
gpio_ll_iomux_set_clk_src(clk_src);
}
return ESP_OK;
}
void io_mux_enable_lp_io_clock(gpio_num_t gpio_num, bool enable)
{
uint32_t rtc_io_num = gpio_num - RTCIO_LL_GPIO_NUM_OFFSET;

View File

@@ -30,6 +30,8 @@ esp_err_t esp_lcd_new_dsi_bus(const esp_lcd_dsi_bus_config_t *bus_config, esp_lc
esp_lcd_dsi_bus_t *dsi_bus = heap_caps_calloc(1, sizeof(esp_lcd_dsi_bus_t), DSI_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(dsi_bus, ESP_ERR_NO_MEM, TAG, "no memory for DSI bus");
dsi_bus->bus_id = bus_id;
dsi_bus->phy_pllref_clk_src = SOC_MOD_CLK_INVALID;
dsi_bus->phy_cfg_clk_src = SOC_MOD_CLK_INVALID;
// Enable the APB clock for accessing the DSI host and bridge registers
PERIPH_RCC_ATOMIC() {
@@ -47,9 +49,11 @@ esp_err_t esp_lcd_new_dsi_bus(const esp_lcd_dsi_bus_config_t *bus_config, esp_lc
#endif
}
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)phy_clk_src, true), err, TAG, "clock source enable failed");
dsi_bus->phy_pllref_clk_src = (soc_module_clk_t)phy_clk_src;
// always use the default clock source for the DSI PHY configuration
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)MIPI_DSI_PHY_CFG_CLK_SRC_DEFAULT, true), err, TAG, "clock source enable failed");
dsi_bus->phy_cfg_clk_src = (soc_module_clk_t)MIPI_DSI_PHY_CFG_CLK_SRC_DEFAULT;
// enable the clock source for DSI PHY
PERIPH_RCC_ATOMIC() {
@@ -143,6 +147,14 @@ esp_err_t esp_lcd_del_dsi_bus(esp_lcd_dsi_bus_handle_t bus)
mipi_dsi_ll_enable_phy_pllref_clock(bus_id, false);
mipi_dsi_ll_enable_phy_config_clock(bus_id, false);
}
if (bus->phy_pllref_clk_src != SOC_MOD_CLK_INVALID) {
esp_clk_tree_enable_src(bus->phy_pllref_clk_src, false);
bus->phy_pllref_clk_src = SOC_MOD_CLK_INVALID;
}
if (bus->phy_cfg_clk_src != SOC_MOD_CLK_INVALID) {
esp_clk_tree_enable_src(bus->phy_cfg_clk_src, false);
bus->phy_cfg_clk_src = SOC_MOD_CLK_INVALID;
}
// disable the APB clock for accessing the DSI peripheral registers
PERIPH_RCC_ATOMIC() {
mipi_dsi_ll_enable_bus_clock(bus_id, false);

View File

@@ -34,6 +34,7 @@ struct esp_lcd_dpi_panel_t {
uint32_t v_pixels; // Vertical pixels
size_t fb_size; // Frame buffer size, in bytes
size_t bits_per_pixel; // Bits per pixel
soc_module_clk_t clk_src; // DPI clock source, SOC_MOD_CLK_INVALID if not enabled
lcd_color_format_t in_color_format; // Input color format
lcd_color_format_t out_color_format; // Output color format
dw_gdma_channel_handle_t dma_chan; // DMA channel
@@ -215,6 +216,7 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
dpi_panel = heap_caps_calloc(1, sizeof(esp_lcd_dpi_panel_t), DSI_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(dpi_panel, ESP_ERR_NO_MEM, err, TAG, "no memory for DPI panel");
dpi_panel->clk_src = SOC_MOD_CLK_INVALID;
dpi_panel->virtual_channel = panel_config->virtual_channel;
dpi_panel->in_color_format = in_color_format;
dpi_panel->out_color_format = out_color_format;
@@ -253,6 +255,7 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
float dpi_clk_src_freq_mhz = (float)dpi_clk_src_freq_hz / 1000.0f / 1000.0f;
uint32_t dpi_div = mipi_dsi_hal_host_dpi_calculate_divider(hal, dpi_clk_src_freq_mhz, panel_config->dpi_clock_freq_mhz);
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)dpi_clk_src, true), err, TAG, "clock source enable failed");
dpi_panel->clk_src = (soc_module_clk_t)dpi_clk_src;
// set the clock source, set the divider, and enable the dpi clock
PERIPH_RCC_ATOMIC() {
mipi_dsi_ll_set_dpi_clock_source(bus_id, dpi_clk_src);
@@ -360,6 +363,10 @@ static esp_err_t dpi_panel_del(esp_lcd_panel_t *panel)
PERIPH_RCC_ATOMIC() {
mipi_dsi_ll_enable_dpi_clock(bus_id, false);
}
if (dpi_panel->clk_src != SOC_MOD_CLK_INVALID) {
esp_clk_tree_enable_src(dpi_panel->clk_src, false);
dpi_panel->clk_src = SOC_MOD_CLK_INVALID;
}
// disable the DSI bridge
mipi_dsi_brg_ll_enable(hal->bridge, false);
// free memory

View File

@@ -46,6 +46,8 @@ extern "C" {
typedef struct esp_lcd_dsi_bus_t {
int bus_id;
mipi_dsi_hal_context_t hal;
soc_module_clk_t phy_pllref_clk_src; // PHY PLL reference clock source, SOC_MOD_CLK_INVALID if not enabled
soc_module_clk_t phy_cfg_clk_src; // PHY config clock source, SOC_MOD_CLK_INVALID if not enabled
#if CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock;
#endif

View File

@@ -65,6 +65,7 @@ struct esp_lcd_i80_bus_t {
uint8_t *format_buffer; // The driver allocates an internal buffer for DMA to do data format transformer
uint8_t *format_buffer_nc; // Non-cacheable version of format buffer
size_t resolution_hz; // LCD_CLK resolution, determined by selected clock source
soc_module_clk_t clk_src; // peripheral clock source, SOC_MOD_CLK_INVALID if not enabled
size_t max_transfer_bytes; // Maximum number of bytes that can be transferred in one transaction
gdma_channel_handle_t dma_chan; // DMA channel handle
gdma_link_list_handle_t dma_link; // DMA link list handle
@@ -139,6 +140,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
ESP_GOTO_ON_FALSE(bus, ESP_ERR_NO_MEM, err, TAG, "no mem for i80 bus");
bus->bus_width = bus_config->bus_width;
bus->bus_id = -1;
bus->clk_src = SOC_MOD_CLK_INVALID;
// allocate the format buffer from internal memory, with DMA capability
bus->format_buffer = heap_caps_calloc(1, LCD_I80_IO_FORMAT_BUF_SIZE,
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
@@ -262,6 +264,10 @@ err:
if (bus->format_buffer) {
free(bus->format_buffer);
}
if (bus->clk_src != SOC_MOD_CLK_INVALID) {
esp_clk_tree_enable_src(bus->clk_src, false);
bus->clk_src = SOC_MOD_CLK_INVALID;
}
#if CONFIG_PM_ENABLE
if (bus->pm_lock) {
esp_pm_lock_delete(bus->pm_lock);
@@ -281,6 +287,10 @@ esp_err_t esp_lcd_del_i80_bus(esp_lcd_i80_bus_handle_t bus)
PERIPH_RCC_ATOMIC() {
lcd_ll_enable_clock(bus->hal.dev, false);
}
if (bus->clk_src != SOC_MOD_CLK_INVALID) {
esp_clk_tree_enable_src(bus->clk_src, false);
bus->clk_src = SOC_MOD_CLK_INVALID;
}
#if I80_USE_RETENTION_LINK
const periph_retention_module_t module_id = soc_i80_lcd_retention_info[bus_id].retention_module;
sleep_retention_module_detach(module_id);
@@ -631,12 +641,12 @@ static void lcd_i80_create_retention_module(esp_lcd_i80_bus_t *bus)
static esp_err_t lcd_i80_select_periph_clock(esp_lcd_i80_bus_handle_t bus, lcd_clock_source_t clk_src)
{
ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true), TAG, "clock source enable failed");
bus->clk_src = (soc_module_clk_t)clk_src;
// get clock source frequency
uint32_t src_clk_hz = 0;
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_hz),
TAG, "get clock source frequency failed");
ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true), TAG, "clock source enable failed");
PERIPH_RCC_ATOMIC() {
lcd_ll_select_clk_src(bus->hal.dev, clk_src);
// force to use integer division, as fractional division might lead to clock jitter