gpio: support etm extension

This commit is contained in:
morris 2022-08-22 22:03:30 +08:00
parent fb26d0e11f
commit 494516d5f3
11 changed files with 680 additions and 320 deletions

View File

@ -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()

View File

@ -0,0 +1,305 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include <sys/cdefs.h>
#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;
}

View File

@ -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
*

View File

@ -0,0 +1,132 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#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

View File

@ -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,

View File

@ -0,0 +1,92 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <inttypes.h>
#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));
}

View File

@ -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 <stdbool.h>
#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

View File

@ -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

View File

@ -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");

View File

@ -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)

View File

@ -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 );