diff --git a/components/driver/CMakeLists.txt b/components/driver/CMakeLists.txt index 2751184b60..d79742aee0 100644 --- a/components/driver/CMakeLists.txt +++ b/components/driver/CMakeLists.txt @@ -65,6 +65,10 @@ if(CONFIG_SOC_PCNT_SUPPORTED) list(APPEND srcs "pulse_cnt.c" "deprecated/pcnt_legacy.c") endif() +if(CONFIG_SOC_GPIO_SUPPORT_ETM) + list(APPEND srcs "gpio/gpio_etm.c") +endif() + if(CONFIG_SOC_SDMMC_HOST_SUPPORTED) list(APPEND srcs "sdmmc_transaction.c" "sdmmc_host.c") endif() diff --git a/components/driver/gpio/gpio_etm.c b/components/driver/gpio/gpio_etm.c new file mode 100644 index 0000000000..aa2de292dd --- /dev/null +++ b/components/driver/gpio/gpio_etm.c @@ -0,0 +1,305 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "sdkconfig.h" +#if CONFIG_ETM_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 +#include "freertos/FreeRTOS.h" +#include "driver/gpio.h" +#include "driver/gpio_etm.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_check.h" +#include "soc/soc_caps.h" +#include "hal/gpio_ll.h" +#include "hal/gpio_etm_ll.h" +#include "esp_private/etm_interface.h" + +#define ETM_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT + +static const char *TAG = "gpio-etm"; + +typedef struct gpio_etm_task_t gpio_etm_task_t; +typedef struct gpio_etm_event_t gpio_etm_event_t; + +typedef struct gpio_etm_group_t { + portMUX_TYPE spinlock; + gpio_etm_dev_t *dev; + gpio_etm_task_t *tasks[SOC_GPIO_ETM_TASKS_PER_GROUP]; + gpio_etm_event_t *events[SOC_GPIO_ETM_EVENTS_PER_GROUP]; +} gpio_etm_group_t; + +struct gpio_etm_event_t { + esp_etm_event_t base; + int chan_id; + gpio_etm_group_t *group; +}; + +struct gpio_etm_task_t { + esp_etm_task_t base; + int chan_id; + gpio_etm_group_t *group; + size_t num_of_gpios; // record the number of GPIOs that are bound to the etm task +}; + +static gpio_etm_group_t s_gpio_etm_group = { + .dev = &GPIO_ETM, + .spinlock = portMUX_INITIALIZER_UNLOCKED, +}; + +static esp_err_t gpio_etm_event_register_to_group(gpio_etm_event_t *event) +{ + gpio_etm_group_t *group = &s_gpio_etm_group; + int chan_id = -1; + // loop to search free one in the group + portENTER_CRITICAL(&group->spinlock); + for (int j = 0; j < SOC_GPIO_ETM_EVENTS_PER_GROUP; j++) { + if (!group->events[j]) { + chan_id = j; + group->events[j] = event; + break; + } + } + portEXIT_CRITICAL(&group->spinlock); + + ESP_RETURN_ON_FALSE(chan_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free event channel"); + event->group = group; + event->chan_id = chan_id; + return ESP_OK; +} + +static esp_err_t gpio_etm_task_register_to_group(gpio_etm_task_t *task) +{ + gpio_etm_group_t *group = &s_gpio_etm_group; + int chan_id = -1; + // loop to search free one in the group + portENTER_CRITICAL(&group->spinlock); + for (int j = 0; j < SOC_GPIO_ETM_TASKS_PER_GROUP; j++) { + if (!group->tasks[j]) { + chan_id = j; + group->tasks[j] = task; + break; + } + } + portEXIT_CRITICAL(&group->spinlock); + + ESP_RETURN_ON_FALSE(chan_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free task channel"); + task->group = group; + task->chan_id = chan_id; + return ESP_OK; +} + +static void gpio_etm_event_unregister_from_group(gpio_etm_event_t *event) +{ + gpio_etm_group_t *group = event->group; + int chan_id = event->chan_id; + portENTER_CRITICAL(&group->spinlock); + group->events[chan_id] = NULL; + portEXIT_CRITICAL(&group->spinlock); +} + +static void gpio_etm_task_unregister_from_group(gpio_etm_task_t *task) +{ + gpio_etm_group_t *group = task->group; + int chan_id = task->chan_id; + portENTER_CRITICAL(&group->spinlock); + group->tasks[chan_id] = NULL; + portEXIT_CRITICAL(&group->spinlock); +} + +static esp_err_t gpio_etm_event_destroy(gpio_etm_event_t *event) +{ + if (event->group) { + gpio_etm_event_unregister_from_group(event); + } + free(event); + return ESP_OK; +} + +static esp_err_t gpio_etm_task_destroy(gpio_etm_task_t *task) +{ + if (task->group) { + gpio_etm_task_unregister_from_group(task); + } + free(task); + return ESP_OK; +} + +static esp_err_t gpio_del_etm_event(esp_etm_event_t *event) +{ + gpio_etm_event_t *gpio_event = __containerof(event, gpio_etm_event_t, base); + gpio_etm_group_t *group = gpio_event->group; + // disable event channel + gpio_ll_etm_enable_event_channel(group->dev, gpio_event->chan_id, false); + gpio_etm_event_destroy(gpio_event); + return ESP_OK; +} + +static esp_err_t gpio_del_etm_task(esp_etm_task_t *task) +{ + gpio_etm_task_t *gpio_task = __containerof(task, gpio_etm_task_t, base); + // make sure user has called `gpio_etm_task_rm_gpio` to clean the etm task channel + ESP_RETURN_ON_FALSE(gpio_task->num_of_gpios == 0, ESP_ERR_INVALID_STATE, TAG, "some GPIO till bounded to the etm task"); + gpio_etm_task_destroy(gpio_task); + return ESP_OK; +} + +esp_err_t gpio_new_etm_event(const gpio_etm_event_config_t *config, esp_etm_event_handle_t *ret_event) +{ +#if CONFIG_ETM_ENABLE_DEBUG_LOG + esp_log_level_set(TAG, ESP_LOG_DEBUG); +#endif + esp_err_t ret = ESP_OK; + gpio_etm_event_t *event = NULL; + ESP_GOTO_ON_FALSE(config && ret_event, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + + event = heap_caps_calloc(1, sizeof(gpio_etm_event_t), ETM_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(event, ESP_ERR_NO_MEM, err, TAG, "no mem for event channel"); + // register the event channel to the group + ESP_GOTO_ON_ERROR(gpio_etm_event_register_to_group(event), err, TAG, "register event channel to group failed"); + int chan_id = event->chan_id; + + uint32_t event_id = 0; + switch (config->edge) { + case GPIO_ETM_EVENT_EDGE_ANY: + event_id = GPIO_LL_ETM_EVENT_ID_ANY_EDGE(chan_id); + break; + case GPIO_ETM_EVENT_EDGE_POS: + event_id = GPIO_LL_ETM_EVENT_ID_POS_EDGE(chan_id); + break; + case GPIO_ETM_EVENT_EDGE_NEG: + event_id = GPIO_LL_ETM_EVENT_ID_NEG_EDGE(chan_id); + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid edge"); + } + + event->base.del = gpio_del_etm_event; + event->base.event_id = event_id; + event->base.trig_periph = ETM_TRIG_PERIPH_GPIO; + ESP_LOGD(TAG, "new event @%p, event_id=%"PRIu32", chan_id=%d", event, event_id, chan_id); + *ret_event = &event->base; + return ESP_OK; + +err: + if (event) { + gpio_etm_event_destroy(event); + } + return ret; +} + +esp_err_t gpio_new_etm_task(const gpio_etm_task_config_t *config, esp_etm_task_handle_t *ret_task) +{ +#if CONFIG_ETM_ENABLE_DEBUG_LOG + esp_log_level_set(TAG, ESP_LOG_DEBUG); +#endif + esp_err_t ret = ESP_OK; + gpio_etm_task_t *task = NULL; + ESP_GOTO_ON_FALSE(config && ret_task, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + + task = heap_caps_calloc(1, sizeof(gpio_etm_task_t), ETM_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(task, ESP_ERR_NO_MEM, err, TAG, "no mem for task channel"); + // register the task channel to the group + ESP_GOTO_ON_ERROR(gpio_etm_task_register_to_group(task), err, TAG, "register task channel to group failed"); + int chan_id = task->chan_id; + + uint32_t task_id = 0; + switch (config->action) { + case GPIO_ETM_TASK_ACTION_SET: + task_id = GPIO_LL_ETM_TASK_ID_SET(chan_id); + break; + case GPIO_ETM_TASK_ACTION_CLR: + task_id = GPIO_LL_ETM_TASK_ID_CLR(chan_id); + break; + case GPIO_ETM_TASK_ACTION_TOG: + task_id = GPIO_LL_ETM_TASK_ID_TOG(chan_id); + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid action"); + } + + task->base.del = gpio_del_etm_task; + task->base.task_id = task_id; + task->base.trig_periph = ETM_TRIG_PERIPH_GPIO; + ESP_LOGD(TAG, "new task @%p, task_id=%"PRIu32", chan_id=%d", task, task_id, chan_id); + *ret_task = &task->base; + return ESP_OK; + +err: + if (task) { + gpio_etm_task_destroy(task); + } + return ret; +} + +esp_err_t gpio_etm_event_bind_gpio(esp_etm_event_handle_t event, int gpio_num) +{ + ESP_RETURN_ON_FALSE(event, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(event->trig_periph == ETM_TRIG_PERIPH_GPIO, ESP_ERR_INVALID_ARG, TAG, "not a gpio etm event"); + ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "gpio is not input capable"); + gpio_etm_event_t *gpio_event = __containerof(event, gpio_etm_event_t, base); + gpio_etm_group_t *group = gpio_event->group; + // disable gpio etm event channel first + gpio_ll_etm_enable_event_channel(group->dev, gpio_event->chan_id, false); + // then set the gpio number + gpio_ll_etm_event_channel_set_gpio(group->dev, gpio_event->chan_id, gpio_num); + // enable gpio etm event channel again + gpio_ll_etm_enable_event_channel(group->dev, gpio_event->chan_id, true); + return ESP_OK; +} + +esp_err_t gpio_etm_task_add_gpio(esp_etm_task_handle_t task, int gpio_num) +{ + ESP_RETURN_ON_FALSE(task, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(task->trig_periph == ETM_TRIG_PERIPH_GPIO, ESP_ERR_INVALID_ARG, TAG, "not a gpio etm task"); + ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "gpio is not output capable"); + gpio_etm_task_t *gpio_task = __containerof(task, gpio_etm_task_t, base); + gpio_etm_group_t *group = gpio_task->group; + bool gpio_not_enabled = true; + // use spinlock as this function may be called with different task object in different threads + // and the gpio_num might reside in the same register + portENTER_CRITICAL(&group->spinlock); + // check if the gpio has been enabled + if (!gpio_ll_etm_is_task_gpio_enabled(group->dev, gpio_num)) { + gpio_ll_etm_gpio_set_task_channel(group->dev, gpio_num, gpio_task->chan_id); + gpio_ll_etm_enable_task_gpio(group->dev, gpio_num, true); + } else { + gpio_not_enabled = false; + } + portEXIT_CRITICAL(&group->spinlock); + ESP_RETURN_ON_FALSE(gpio_not_enabled, ESP_ERR_INVALID_STATE, TAG, "gpio already enabled by other task channel"); + gpio_task->num_of_gpios++; + return ESP_OK; +} + +esp_err_t gpio_etm_task_rm_gpio(esp_etm_task_handle_t task, int gpio_num) +{ + ESP_RETURN_ON_FALSE(task, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "gpio is not output capable"); + gpio_etm_task_t *gpio_task = __containerof(task, gpio_etm_task_t, base); + gpio_etm_group_t *group = gpio_task->group; + bool gpio_enabled_by_this_task = true; + // use spinlock as this function may be called with different task object in different threads + // and the gpio_num might reside in the same register + portENTER_CRITICAL(&group->spinlock); + // check if the gpio is managed by this etm task channel + if (gpio_ll_etm_is_task_gpio_enabled(group->dev, gpio_num) && + (gpio_ll_etm_gpio_get_task_channel(group->dev, gpio_num) == gpio_task->chan_id)) { + gpio_ll_etm_enable_task_gpio(group->dev, gpio_num, false); + } else { + gpio_enabled_by_this_task = false; + } + portEXIT_CRITICAL(&group->spinlock); + ESP_RETURN_ON_FALSE(gpio_enabled_by_this_task, ESP_ERR_INVALID_STATE, TAG, "gpio is not enabled by this task channel"); + gpio_task->num_of_gpios--; + return ESP_OK; +} diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 2b30018e08..4c2d971c9d 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -13,6 +13,7 @@ #include "soc/soc_caps.h" #include "hal/gpio_types.h" #include "esp_rom_gpio.h" +#include "driver/gpio_etm.h" #ifdef __cplusplus extern "C" { @@ -150,7 +151,7 @@ esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level); int gpio_get_level(gpio_num_t gpio_num); /** - * @brief GPIO set direction + * @brief GPIO set direction * * Configure GPIO direction,such as output_only,input_only,output_and_input * @@ -480,7 +481,7 @@ esp_err_t gpio_sleep_sel_en(gpio_num_t gpio_num); esp_err_t gpio_sleep_sel_dis(gpio_num_t gpio_num); /** - * @brief GPIO set direction at sleep + * @brief GPIO set direction at sleep * * Configure GPIO direction,such as output_only,input_only,output_and_input * diff --git a/components/driver/include/driver/gpio_etm.h b/components/driver/include/driver/gpio_etm.h new file mode 100644 index 0000000000..e7acf722d5 --- /dev/null +++ b/components/driver/include/driver/gpio_etm.h @@ -0,0 +1,132 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "esp_err.h" +#include "esp_etm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief GPIO edges that can be used as ETM event + */ +typedef enum { + GPIO_ETM_EVENT_EDGE_POS, /*!< A rising edge on the GPIO will generate an ETM event signal */ + GPIO_ETM_EVENT_EDGE_NEG, /*!< A falling edge on the GPIO will generate an ETM event signal */ + GPIO_ETM_EVENT_EDGE_ANY, /*!< Any edge on the GPIO can generate an ETM event signal */ +} gpio_etm_event_edge_t; + +/** + * @brief GPIO ETM event configuration + */ +typedef struct { + gpio_etm_event_edge_t edge; /*!< Which kind of edge can trigger the ETM event module */ +} gpio_etm_event_config_t; + +/** + * @brief Create an ETM event object for the GPIO peripheral + * + * @note The created ETM event object can be deleted later by calling `esp_etm_del_event` + * @note The newly created ETM event object is not bind to any GPIO, you need to call `gpio_etm_event_bind_gpio` to bind the wanted GPIO + * + * @param[in] config GPIO ETM event configuration + * @param[out] ret_event Returned ETM event handle + * @return + * - ESP_OK: Create ETM event successfully + * - ESP_ERR_INVALID_ARG: Create ETM event failed because of invalid argument + * - ESP_ERR_NO_MEM: Create ETM event failed because of out of memory + * - ESP_ERR_NOT_FOUND: Create ETM event failed because all events are used up and no more free one + * - ESP_FAIL: Create ETM event failed because of other reasons + */ +esp_err_t gpio_new_etm_event(const gpio_etm_event_config_t *config, esp_etm_event_handle_t *ret_event); + +/** + * @brief Bind the GPIO with the ETM event + * + * @note Calling this function multiple times with different GPIO number can override the previous setting immediately. + * @note Only GPIO ETM object can call this function + * + * @param[in] event ETM event handle that created by `gpio_new_etm_event` + * @param[in] gpio_num GPIO number that can trigger the ETM event + * @return + * - ESP_OK: Set the GPIO for ETM event successfully + * - ESP_ERR_INVALID_ARG: Set the GPIO for ETM event failed because of invalid argument, e.g. GPIO is not input capable, ETM event is not of GPIO type + * - ESP_FAIL: Set the GPIO for ETM event failed because of other reasons + */ +esp_err_t gpio_etm_event_bind_gpio(esp_etm_event_handle_t event, int gpio_num); + +/** + * @brief GPIO actions that can be taken by the ETM task + */ +typedef enum { + GPIO_ETM_TASK_ACTION_SET, /*!< Set the GPIO level to high */ + GPIO_ETM_TASK_ACTION_CLR, /*!< Clear the GPIO level to low */ + GPIO_ETM_TASK_ACTION_TOG, /*!< Toggle the GPIO level */ +} gpio_etm_task_action_t; + +/** + * @brief GPIO ETM task configuration + */ +typedef struct { + gpio_etm_task_action_t action; /*!< Which action to take by the ETM task module */ +} gpio_etm_task_config_t; + +/** + * @brief Create an ETM task object for the GPIO peripheral + * + * @note The created ETM task object can be deleted later by calling `esp_etm_del_task` + * @note The GPIO ETM task works like a container, a newly created ETM task object doesn't have GPIO members to be managed. + * You need to call `gpio_etm_task_add_gpio` to put one or more GPIOs to the container. + * + * @param[in] config GPIO ETM task configuration + * @param[out] ret_task Returned ETM task handle + * @return + * - ESP_OK: Create ETM task successfully + * - ESP_ERR_INVALID_ARG: Create ETM task failed because of invalid argument + * - ESP_ERR_NO_MEM: Create ETM task failed because of out of memory + * - ESP_ERR_NOT_FOUND: Create ETM task failed because all tasks are used up and no more free one + * - ESP_FAIL: Create ETM task failed because of other reasons + */ +esp_err_t gpio_new_etm_task(const gpio_etm_task_config_t *config, esp_etm_task_handle_t *ret_task); + +/** + * @brief Add GPIO to the ETM task. + * + * @note You can call this function multiple times to add more GPIOs + * @note Only GPIO ETM object can call this function + * + * @param[in] task ETM task handle that created by `gpio_new_etm_task` + * @param[in] gpio_num GPIO number that can be controlled by the ETM task + * @return + * - ESP_OK: Add GPIO to the ETM task successfully + * - ESP_ERR_INVALID_ARG: Add GPIO to the ETM task failed because of invalid argument, e.g. GPIO is not output capable, ETM task is not of GPIO type + * - ESP_ERR_INVALID_STATE: Add GPIO to the ETM task failed because the GPIO is used by other ETM task already + * - ESP_FAIL: Add GPIO to the ETM task failed because of other reasons + */ +esp_err_t gpio_etm_task_add_gpio(esp_etm_task_handle_t task, int gpio_num); + +/** + * @brief Remove the GPIO from the ETM task + * + * @note Before deleting the ETM task, you need to remove all the GPIOs from the ETM task by this function + * @note Only GPIO ETM object can call this function + * + * @param[in] task ETM task handle that created by `gpio_new_etm_task` + * @param[in] gpio_num GPIO number that to be remove from the ETM task + * @return + * - ESP_OK: Remove the GPIO from the ETM task successfully + * - ESP_ERR_INVALID_ARG: Remove the GPIO from the ETM task failed because of invalid argument + * - ESP_ERR_INVALID_STATE: Remove the GPIO from the ETM task failed because the GPIO is not controlled by this ETM task + * - ESP_FAIL: Remove the GPIO from the ETM task failed because of other reasons + */ +esp_err_t gpio_etm_task_rm_gpio(esp_etm_task_handle_t task, int gpio_num); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt b/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt index be94405b35..a502495e43 100644 --- a/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt +++ b/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt @@ -1,6 +1,9 @@ set(srcs "test_app_main.c" "test_etm_core.c") +if(CONFIG_SOC_GPIO_SUPPORT_ETM) + list(APPEND srcs "test_gpio_etm.c") +endif() # In order for the cases defined by `TEST_CASE` to be linked into the final elf, diff --git a/components/esp_hw_support/test_apps/etm/main/test_gpio_etm.c b/components/esp_hw_support/test_apps/etm/main/test_gpio_etm.c new file mode 100644 index 0000000000..ee80ca8a4d --- /dev/null +++ b/components/esp_hw_support/test_apps/etm/main/test_gpio_etm.c @@ -0,0 +1,92 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "unity.h" +#include "unity_test_utils.h" +#include "esp_attr.h" +#include "driver/gpio_etm.h" +#include "driver/gpio.h" + +TEST_CASE("gpio_etm_self_trigger", "[etm]") +{ + // GPIO any edge ---> EMT channel ---> GPIO toggle + const uint32_t input_gpio = 0; + const uint32_t output_gpio = 1; + printf("allocate etm channels\r\n"); + esp_etm_channel_config_t etm_config = {}; + esp_etm_channel_handle_t etm_channel_a = NULL; + TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel_a)); + + printf("allocate GPIO etm event and task\r\n"); + esp_etm_task_handle_t gpio_task = NULL; + esp_etm_event_handle_t gpio_event = NULL; + gpio_etm_event_config_t gpio_event_config = { + .edge = GPIO_ETM_EVENT_EDGE_ANY, + }; + TEST_ESP_OK(gpio_new_etm_event(&gpio_event_config, &gpio_event)); + gpio_etm_task_config_t gpio_task_config = { + .action = GPIO_ETM_TASK_ACTION_TOG, + }; + TEST_ESP_OK(gpio_new_etm_task(&gpio_task_config, &gpio_task)); + + // bind GPIO to the event and task + TEST_ESP_OK(gpio_etm_event_bind_gpio(gpio_event, input_gpio)); + TEST_ESP_OK(gpio_etm_task_add_gpio(gpio_task, output_gpio)); + + printf("initialize gpio\r\n"); + gpio_config_t task_gpio_config = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_INPUT_OUTPUT, // we want to read the GPIO value, so it should be input and output + .pin_bit_mask = 1ULL << output_gpio, + }; + TEST_ESP_OK(gpio_config(&task_gpio_config)); + // set the initial level + TEST_ESP_OK(gpio_set_level(output_gpio, 0)); + + gpio_config_t event_gpio_config = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_INPUT_OUTPUT, // we want to simulate the edge signal by software, so it should be input and output + .pull_up_en = GPIO_PULLUP_ENABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .pin_bit_mask = 1ULL << input_gpio, + }; + TEST_ESP_OK(gpio_config(&event_gpio_config)); + + printf("connect event and task to the channel\r\n"); + TEST_ESP_OK(esp_etm_channel_connect(etm_channel_a, gpio_event, gpio_task)); + + TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a)); + + for (int i = 0; i < 10; i++) { + TEST_ESP_OK(gpio_set_level(input_gpio, i & 0x01)); + vTaskDelay(pdMS_TO_TICKS(100)); + } + // check the final level + TEST_ASSERT_EQUAL(1, gpio_get_level(output_gpio)); + + vTaskDelay(pdMS_TO_TICKS(100)); + + for (int i = 0; i < 5; i++) { + TEST_ESP_OK(gpio_set_level(input_gpio, i & 0x01)); + vTaskDelay(pdMS_TO_TICKS(100)); + } + // check the final level + TEST_ASSERT_EQUAL(0, gpio_get_level(output_gpio)); + + // delete gpio etm task without remove all bounded GPIOs should fail + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_etm_del_task(gpio_task)); + // remove unrelated GPIO from the task should fail + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, gpio_etm_task_rm_gpio(gpio_task, 10)); + + // delete etm primitives + TEST_ESP_OK(gpio_etm_task_rm_gpio(gpio_task, output_gpio)); + TEST_ESP_OK(esp_etm_channel_disable(etm_channel_a)); + TEST_ESP_OK(esp_etm_del_task(gpio_task)); + TEST_ESP_OK(esp_etm_del_event(gpio_event)); + TEST_ESP_OK(esp_etm_del_channel(etm_channel_a)); +} diff --git a/components/hal/esp32c6/include/hal/gpio_etm_ll.h b/components/hal/esp32c6/include/hal/gpio_etm_ll.h new file mode 100644 index 0000000000..6d380b27ae --- /dev/null +++ b/components/hal/esp32c6/include/hal/gpio_etm_ll.h @@ -0,0 +1,119 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// Note that most of the register operations in this layer are non-atomic operations. + +#pragma once + +#include +#include "hal/assert.h" +#include "hal/misc.h" +#include "soc/gpio_ext_struct.h" +#include "soc/soc_etm_source.h" + +#define GPIO_LL_ETM_EVENT_ID_POS_EDGE(ch) (GPIO_EVT_CH0_RISE_EDGE + (ch)) +#define GPIO_LL_ETM_EVENT_ID_NEG_EDGE(ch) (GPIO_EVT_CH0_FALL_EDGE + (ch)) +#define GPIO_LL_ETM_EVENT_ID_ANY_EDGE(ch) (GPIO_EVT_CH0_ANY_EDGE + (ch)) + +#define GPIO_LL_ETM_TASK_ID_SET(ch) (GPIO_TASK_CH0_SET + (ch)) +#define GPIO_LL_ETM_TASK_ID_CLR(ch) (GPIO_TASK_CH0_CLEAR + (ch)) +#define GPIO_LL_ETM_TASK_ID_TOG(ch) (GPIO_TASK_CH0_TOGGLE + (ch)) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set which GPIO to be bounded to the event channel + * + * @param dev Register base address + * @param chan Channel number + * @param gpio_num GPIO number + */ +static inline void gpio_ll_etm_event_channel_set_gpio(gpio_etm_dev_t *dev, uint32_t chan, uint32_t gpio_num) +{ + dev->event_chn_cfg[chan].etm_ch0_event_sel = gpio_num; +} + +/** + * @brief Wether to enable the event channel + * + * @param dev Register base address + * @param chan Channel number + * @param enable True to enable, false to disable + */ +static inline void gpio_ll_etm_enable_event_channel(gpio_etm_dev_t *dev, uint32_t chan, bool enable) +{ + dev->event_chn_cfg[chan].etm_ch0_event_en = enable; +} + +/** + * @brief Set which GPIO to be bounded to the task channel + * + * @note One channel can be bounded to multiple different GPIOs + * + * @param dev Register base address + * @param chan Channel number + * @param gpio_num GPIO number + */ +static inline void gpio_ll_etm_gpio_set_task_channel(gpio_etm_dev_t *dev, uint32_t gpio_num, uint32_t chan) +{ + int g_p = gpio_num / 4; + int g_idx = gpio_num % 4; + uint32_t reg_val = dev->etm_task_pn_cfg[g_p].val; + reg_val &= ~(0x07 << (g_idx * 8 + 1)); + reg_val |= ((chan & 0x07) << (g_idx * 8 + 1)); + dev->etm_task_pn_cfg[g_p].val = reg_val; +} + +/** + * @brief Wether to enable the GPIO to be managed by the task channel + * + * @param dev Register base address + * @param gpio_num GPIO number + * @param enable True to enable, false to disable + */ +static inline void gpio_ll_etm_enable_task_gpio(gpio_etm_dev_t *dev, uint32_t gpio_num, bool enable) +{ + int g_p = gpio_num / 4; + int g_idx = gpio_num % 4; + uint32_t reg_val = dev->etm_task_pn_cfg[g_p].val; + reg_val &= ~(0x01 << (g_idx * 8)); + reg_val |= ((enable & 0x01) << (g_idx * 8)); + dev->etm_task_pn_cfg[g_p].val = reg_val; +} + +/** + * @brief Check whether a GPIO has been enabled and managed by a task channel + * + * @param dev Register base address + * @param gpio_num GPIO number + * @return True if enabled, false otherwise + */ +static inline bool gpio_ll_etm_is_task_gpio_enabled(gpio_etm_dev_t *dev, uint32_t gpio_num) +{ + int g_p = gpio_num / 4; + int g_idx = gpio_num % 4; + return dev->etm_task_pn_cfg[g_p].val & (0x01 << (g_idx * 8)); +} + +/** + * @brief Get the channel number that the GPIO is bounded to + * + * @param dev Register base address + * @param gpio_num GPIO number + * @return GPIO ETM Task channel number + */ +static inline uint32_t gpio_ll_etm_gpio_get_task_channel(gpio_etm_dev_t *dev, uint32_t gpio_num) +{ + int g_p = gpio_num / 4; + int g_idx = gpio_num % 4; + return (dev->etm_task_pn_cfg[g_p].val >> (g_idx * 8 + 1)) & 0x07; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in index 403ef3c9d7..2fc9611a6c 100644 --- a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in @@ -263,6 +263,18 @@ config SOC_GPIO_PIN_COUNT int default 31 +config SOC_GPIO_SUPPORT_ETM + bool + default y + +config SOC_GPIO_ETM_EVENTS_PER_GROUP + int + default 8 + +config SOC_GPIO_ETM_TASKS_PER_GROUP + int + default 8 + config SOC_GPIO_SUPPORT_RTC_INDEPENDENT bool default y diff --git a/components/soc/esp32c6/include/soc/gpio_ext_struct.h b/components/soc/esp32c6/include/soc/gpio_ext_struct.h index b06cd88027..7bcb54adf2 100644 --- a/components/soc/esp32c6/include/soc/gpio_ext_struct.h +++ b/components/soc/esp32c6/include/soc/gpio_ext_struct.h @@ -155,314 +155,7 @@ typedef union { uint32_t reserved_28:4; }; uint32_t val; -} gpio_etm_task_p0_cfg_reg_t; - -/** Type of etm_task_p1_cfg register - * Etm Configure Register to decide which GPIO been chosen - */ -typedef union { - struct { - /** etm_task_gpio4_en : R/W; bitpos: [0]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio4_en:1; - /** etm_task_gpio4_sel : R/W; bitpos: [3:1]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio4_sel:3; - uint32_t reserved_4:4; - /** etm_task_gpio5_en : R/W; bitpos: [8]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio5_en:1; - /** etm_task_gpio5_sel : R/W; bitpos: [11:9]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio5_sel:3; - uint32_t reserved_12:4; - /** etm_task_gpio6_en : R/W; bitpos: [16]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio6_en:1; - /** etm_task_gpio6_sel : R/W; bitpos: [19:17]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio6_sel:3; - uint32_t reserved_20:4; - /** etm_task_gpio7_en : R/W; bitpos: [24]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio7_en:1; - /** etm_task_gpio7_sel : R/W; bitpos: [27:25]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio7_sel:3; - uint32_t reserved_28:4; - }; - uint32_t val; -} gpio_etm_task_p1_cfg_reg_t; - -/** Type of etm_task_p2_cfg register - * Etm Configure Register to decide which GPIO been chosen - */ -typedef union { - struct { - /** etm_task_gpio8_en : R/W; bitpos: [0]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio8_en:1; - /** etm_task_gpio8_sel : R/W; bitpos: [3:1]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio8_sel:3; - uint32_t reserved_4:4; - /** etm_task_gpio9_en : R/W; bitpos: [8]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio9_en:1; - /** etm_task_gpio9_sel : R/W; bitpos: [11:9]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio9_sel:3; - uint32_t reserved_12:4; - /** etm_task_gpio10_en : R/W; bitpos: [16]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio10_en:1; - /** etm_task_gpio10_sel : R/W; bitpos: [19:17]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio10_sel:3; - uint32_t reserved_20:4; - /** etm_task_gpio11_en : R/W; bitpos: [24]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio11_en:1; - /** etm_task_gpio11_sel : R/W; bitpos: [27:25]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio11_sel:3; - uint32_t reserved_28:4; - }; - uint32_t val; -} gpio_etm_task_p2_cfg_reg_t; - -/** Type of etm_task_p3_cfg register - * Etm Configure Register to decide which GPIO been chosen - */ -typedef union { - struct { - /** etm_task_gpio12_en : R/W; bitpos: [0]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio12_en:1; - /** etm_task_gpio12_sel : R/W; bitpos: [3:1]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio12_sel:3; - uint32_t reserved_4:4; - /** etm_task_gpio13_en : R/W; bitpos: [8]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio13_en:1; - /** etm_task_gpio13_sel : R/W; bitpos: [11:9]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio13_sel:3; - uint32_t reserved_12:4; - /** etm_task_gpio14_en : R/W; bitpos: [16]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio14_en:1; - /** etm_task_gpio14_sel : R/W; bitpos: [19:17]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio14_sel:3; - uint32_t reserved_20:4; - /** etm_task_gpio15_en : R/W; bitpos: [24]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio15_en:1; - /** etm_task_gpio15_sel : R/W; bitpos: [27:25]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio15_sel:3; - uint32_t reserved_28:4; - }; - uint32_t val; -} gpio_etm_task_p3_cfg_reg_t; - -/** Type of etm_task_p4_cfg register - * Etm Configure Register to decide which GPIO been chosen - */ -typedef union { - struct { - /** etm_task_gpio16_en : R/W; bitpos: [0]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio16_en:1; - /** etm_task_gpio16_sel : R/W; bitpos: [3:1]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio16_sel:3; - uint32_t reserved_4:4; - /** etm_task_gpio17_en : R/W; bitpos: [8]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio17_en:1; - /** etm_task_gpio17_sel : R/W; bitpos: [11:9]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio17_sel:3; - uint32_t reserved_12:4; - /** etm_task_gpio18_en : R/W; bitpos: [16]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio18_en:1; - /** etm_task_gpio18_sel : R/W; bitpos: [19:17]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio18_sel:3; - uint32_t reserved_20:4; - /** etm_task_gpio19_en : R/W; bitpos: [24]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio19_en:1; - /** etm_task_gpio19_sel : R/W; bitpos: [27:25]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio19_sel:3; - uint32_t reserved_28:4; - }; - uint32_t val; -} gpio_etm_task_p4_cfg_reg_t; - -/** Type of etm_task_p5_cfg register - * Etm Configure Register to decide which GPIO been chosen - */ -typedef union { - struct { - /** etm_task_gpio20_en : R/W; bitpos: [0]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio20_en:1; - /** etm_task_gpio20_sel : R/W; bitpos: [3:1]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio20_sel:3; - uint32_t reserved_4:4; - /** etm_task_gpio21_en : R/W; bitpos: [8]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio21_en:1; - /** etm_task_gpio21_sel : R/W; bitpos: [11:9]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio21_sel:3; - uint32_t reserved_12:4; - /** etm_task_gpio22_en : R/W; bitpos: [16]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio22_en:1; - /** etm_task_gpio22_sel : R/W; bitpos: [19:17]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio22_sel:3; - uint32_t reserved_20:4; - /** etm_task_gpio23_en : R/W; bitpos: [24]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio23_en:1; - /** etm_task_gpio23_sel : R/W; bitpos: [27:25]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio23_sel:3; - uint32_t reserved_28:4; - }; - uint32_t val; -} gpio_etm_task_p5_cfg_reg_t; - -/** Type of etm_task_p6_cfg register - * Etm Configure Register to decide which GPIO been chosen - */ -typedef union { - struct { - /** etm_task_gpio24_en : R/W; bitpos: [0]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio24_en:1; - /** etm_task_gpio24_sel : R/W; bitpos: [3:1]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio24_sel:3; - uint32_t reserved_4:4; - /** etm_task_gpio25_en : R/W; bitpos: [8]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio25_en:1; - /** etm_task_gpio25_sel : R/W; bitpos: [11:9]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio25_sel:3; - uint32_t reserved_12:4; - /** etm_task_gpio26_en : R/W; bitpos: [16]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio26_en:1; - /** etm_task_gpio26_sel : R/W; bitpos: [19:17]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio26_sel:3; - uint32_t reserved_20:4; - /** etm_task_gpio27_en : R/W; bitpos: [24]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio27_en:1; - /** etm_task_gpio27_sel : R/W; bitpos: [27:25]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio27_sel:3; - uint32_t reserved_28:4; - }; - uint32_t val; -} gpio_etm_task_p6_cfg_reg_t; - -/** Type of etm_task_p7_cfg register - * Etm Configure Register to decide which GPIO been chosen - */ -typedef union { - struct { - /** etm_task_gpio28_en : R/W; bitpos: [0]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio28_en:1; - /** etm_task_gpio28_sel : R/W; bitpos: [3:1]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio28_sel:3; - uint32_t reserved_4:4; - /** etm_task_gpio29_en : R/W; bitpos: [8]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio29_en:1; - /** etm_task_gpio29_sel : R/W; bitpos: [11:9]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio29_sel:3; - uint32_t reserved_12:4; - /** etm_task_gpio30_en : R/W; bitpos: [16]; default: 0; - * Enable bit of GPIO response etm task. - */ - uint32_t etm_task_gpio30_en:1; - /** etm_task_gpio30_sel : R/W; bitpos: [19:17]; default: 0; - * GPIO choose a etm task channel. - */ - uint32_t etm_task_gpio30_sel:3; - uint32_t reserved_20:12; - }; - uint32_t val; -} gpio_etm_task_p7_cfg_reg_t; - +} gpio_etm_task_pn_cfg_reg_t; /** Group: Version Register */ /** Type of version register @@ -491,17 +184,10 @@ typedef struct { volatile gpio_glitch_filter_chn_reg_t glitch_filter_chn[8]; } gpio_glitch_filter_dev_t; -typedef struct { +typedef struct gpio_etm_dev_t { volatile gpio_etm_event_chn_cfg_reg_t event_chn_cfg[8]; uint32_t reserved_080[8]; - volatile gpio_etm_task_p0_cfg_reg_t etm_task_p0_cfg; - volatile gpio_etm_task_p1_cfg_reg_t etm_task_p1_cfg; - volatile gpio_etm_task_p2_cfg_reg_t etm_task_p2_cfg; - volatile gpio_etm_task_p3_cfg_reg_t etm_task_p3_cfg; - volatile gpio_etm_task_p4_cfg_reg_t etm_task_p4_cfg; - volatile gpio_etm_task_p5_cfg_reg_t etm_task_p5_cfg; - volatile gpio_etm_task_p6_cfg_reg_t etm_task_p6_cfg; - volatile gpio_etm_task_p7_cfg_reg_t etm_task_p7_cfg; + volatile gpio_etm_task_pn_cfg_reg_t etm_task_pn_cfg[8]; } gpio_etm_dev_t; typedef struct gpio_ext_dev_t { @@ -517,6 +203,7 @@ typedef struct gpio_ext_dev_t { extern gpio_sd_dev_t SDM; extern gpio_glitch_filter_dev_t GLITCH_FILTER; extern gpio_etm_dev_t GPIO_ETM; +extern gpio_ext_dev_t GPIO_EXT; #ifndef __cplusplus _Static_assert(sizeof(gpio_ext_dev_t) == 0x100, "Invalid size of gpio_ext_dev_t structure"); diff --git a/components/soc/esp32c6/include/soc/soc_caps.h b/components/soc/esp32c6/include/soc/soc_caps.h index 939fbf4a67..1ea341d408 100644 --- a/components/soc/esp32c6/include/soc/soc_caps.h +++ b/components/soc/esp32c6/include/soc/soc_caps.h @@ -151,6 +151,11 @@ #define SOC_GPIO_PORT (1U) #define SOC_GPIO_PIN_COUNT (31) +// GPIO peripheral has the ETM extension +#define SOC_GPIO_SUPPORT_ETM 1 +#define SOC_GPIO_ETM_EVENTS_PER_GROUP 8 +#define SOC_GPIO_ETM_TASKS_PER_GROUP 8 + // Target has the full LP IO subsystem // On ESP32-C6, Digital IOs have their own registers to control pullup/down capability, independent of LP registers. #define SOC_GPIO_SUPPORT_RTC_INDEPENDENT (1) diff --git a/components/soc/esp32c6/ld/esp32c6.peripherals.ld b/components/soc/esp32c6/ld/esp32c6.peripherals.ld index 92c6203c47..2a9acb4b10 100644 --- a/components/soc/esp32c6/ld/esp32c6.peripherals.ld +++ b/components/soc/esp32c6/ld/esp32c6.peripherals.ld @@ -46,7 +46,7 @@ PROVIDE ( HMAC = 0x6008D000 ); PROVIDE ( IO_MUX = 0x60090000 ); PROVIDE ( GPIO = 0x60091000 ); -PROVIDE ( GPIO_EXT = 0x60091f00 ); /*ESP32C6-TODO*/ +PROVIDE ( GPIO_EXT = 0x60091f00 ); PROVIDE ( SDM = 0x60091f00 ); PROVIDE ( GLITCH_FILTER = 0x60091f30 ); PROVIDE ( GPIO_ETM = 0x60091f60 );