From ac05d033b9cb8b77ab186783f8108f86e7cf8d05 Mon Sep 17 00:00:00 2001 From: morris Date: Tue, 10 Jan 2023 10:28:41 +0800 Subject: [PATCH 1/2] gdma: correct the dma trigger of uart GDMA trigger actually is not assigned to UART controller, but for UHCI controller --- components/esp_hw_support/include/esp_private/gdma.h | 6 +++--- components/soc/esp32c2/include/soc/gdma_channel.h | 2 +- components/soc/esp32c3/include/soc/gdma_channel.h | 2 +- components/soc/esp32h2/include/soc/gdma_channel.h | 2 +- components/soc/esp32s3/include/soc/gdma_channel.h | 2 +- .../hci/controller_hci_uart_esp32c3_and_esp32s3/main/main.c | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/components/esp_hw_support/include/esp_private/gdma.h b/components/esp_hw_support/include/esp_private/gdma.h index f888cd5bc9..1e0bb51003 100644 --- a/components/esp_hw_support/include/esp_private/gdma.h +++ b/components/esp_hw_support/include/esp_private/gdma.h @@ -30,7 +30,7 @@ typedef struct gdma_channel_t *gdma_channel_handle_t; */ typedef enum { GDMA_TRIG_PERIPH_M2M, /*!< GDMA trigger peripheral: M2M */ - GDMA_TRIG_PERIPH_UART, /*!< GDMA trigger peripheral: UART */ + GDMA_TRIG_PERIPH_UHCI, /*!< GDMA trigger peripheral: UHCI */ GDMA_TRIG_PERIPH_SPI, /*!< GDMA trigger peripheral: SPI */ GDMA_TRIG_PERIPH_I2S, /*!< GDMA trigger peripheral: I2S */ GDMA_TRIG_PERIPH_AES, /*!< GDMA trigger peripheral: AES */ @@ -124,13 +124,13 @@ typedef struct { */ typedef struct { gdma_trigger_peripheral_t periph; /*!< Target peripheral which will trigger DMA operations */ - int instance_id; /*!< Peripheral instance ID. Supported IDs are listed in `soc/gdma_channel.h`, e.g. SOC_GDMA_TRIG_PERIPH_UART0 */ + int instance_id; /*!< Peripheral instance ID. Supported IDs are listed in `soc/gdma_channel.h`, e.g. SOC_GDMA_TRIG_PERIPH_UHCI0 */ } gdma_trigger_t; /** * @brief Helper macro to initialize GDMA trigger * @note value of `peri` must be selected from `gdma_trigger_peripheral_t` enum. - * e.g. GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UART,0) + * e.g. GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_I2S,0) * */ #define GDMA_MAKE_TRIGGER(peri, id) \ diff --git a/components/soc/esp32c2/include/soc/gdma_channel.h b/components/soc/esp32c2/include/soc/gdma_channel.h index 1c84a35249..d45217826b 100644 --- a/components/soc/esp32c2/include/soc/gdma_channel.h +++ b/components/soc/esp32c2/include/soc/gdma_channel.h @@ -9,6 +9,6 @@ // The following macros have a format SOC_[periph][instance_id] to make it work with `GDMA_MAKE_TRIGGER` #define SOC_GDMA_TRIG_PERIPH_M2M0 (-1) #define SOC_GDMA_TRIG_PERIPH_SPI2 (0) -#define SOC_GDMA_TRIG_PERIPH_UART0 (2) +#define SOC_GDMA_TRIG_PERIPH_UHCI0 (2) #define SOC_GDMA_TRIG_PERIPH_SHA0 (7) #define SOC_GDMA_TRIG_PERIPH_ADC0 (8) diff --git a/components/soc/esp32c3/include/soc/gdma_channel.h b/components/soc/esp32c3/include/soc/gdma_channel.h index ca3f0f7951..5f0b818bd2 100644 --- a/components/soc/esp32c3/include/soc/gdma_channel.h +++ b/components/soc/esp32c3/include/soc/gdma_channel.h @@ -9,7 +9,7 @@ // The following macros have a format SOC_[periph][instance_id] to make it work with `GDMA_MAKE_TRIGGER` #define SOC_GDMA_TRIG_PERIPH_M2M0 (-1) #define SOC_GDMA_TRIG_PERIPH_SPI2 (0) -#define SOC_GDMA_TRIG_PERIPH_UART0 (2) +#define SOC_GDMA_TRIG_PERIPH_UHCI0 (2) #define SOC_GDMA_TRIG_PERIPH_I2S0 (3) #define SOC_GDMA_TRIG_PERIPH_AES0 (6) #define SOC_GDMA_TRIG_PERIPH_SHA0 (7) diff --git a/components/soc/esp32h2/include/soc/gdma_channel.h b/components/soc/esp32h2/include/soc/gdma_channel.h index ca3f0f7951..5f0b818bd2 100644 --- a/components/soc/esp32h2/include/soc/gdma_channel.h +++ b/components/soc/esp32h2/include/soc/gdma_channel.h @@ -9,7 +9,7 @@ // The following macros have a format SOC_[periph][instance_id] to make it work with `GDMA_MAKE_TRIGGER` #define SOC_GDMA_TRIG_PERIPH_M2M0 (-1) #define SOC_GDMA_TRIG_PERIPH_SPI2 (0) -#define SOC_GDMA_TRIG_PERIPH_UART0 (2) +#define SOC_GDMA_TRIG_PERIPH_UHCI0 (2) #define SOC_GDMA_TRIG_PERIPH_I2S0 (3) #define SOC_GDMA_TRIG_PERIPH_AES0 (6) #define SOC_GDMA_TRIG_PERIPH_SHA0 (7) diff --git a/components/soc/esp32s3/include/soc/gdma_channel.h b/components/soc/esp32s3/include/soc/gdma_channel.h index 373cf6cabd..6178c1f1ab 100644 --- a/components/soc/esp32s3/include/soc/gdma_channel.h +++ b/components/soc/esp32s3/include/soc/gdma_channel.h @@ -10,7 +10,7 @@ #define SOC_GDMA_TRIG_PERIPH_M2M0 (-1) #define SOC_GDMA_TRIG_PERIPH_SPI2 (0) #define SOC_GDMA_TRIG_PERIPH_SPI3 (1) -#define SOC_GDMA_TRIG_PERIPH_UART0 (2) +#define SOC_GDMA_TRIG_PERIPH_UHCI0 (2) #define SOC_GDMA_TRIG_PERIPH_I2S0 (3) #define SOC_GDMA_TRIG_PERIPH_I2S1 (4) #define SOC_GDMA_TRIG_PERIPH_LCD0 (5) diff --git a/examples/bluetooth/hci/controller_hci_uart_esp32c3_and_esp32s3/main/main.c b/examples/bluetooth/hci/controller_hci_uart_esp32c3_and_esp32s3/main/main.c index 0cacc7b4ca..8770990c8e 100644 --- a/examples/bluetooth/hci/controller_hci_uart_esp32c3_and_esp32s3/main/main.c +++ b/examples/bluetooth/hci/controller_hci_uart_esp32c3_and_esp32s3/main/main.c @@ -221,8 +221,8 @@ void uhci_uart_install(void) }; ESP_ERROR_CHECK(gdma_new_channel(&rx_channel_config, &s_rx_channel)); - gdma_connect(s_tx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UART, 0)); - gdma_connect(s_rx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UART, 0)); + gdma_connect(s_tx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0)); + gdma_connect(s_rx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0)); gdma_strategy_config_t strategy_config = { .auto_update_desc = false, From 6b8d4dfe1470d07330d8ebc6b48a13a74f22b7b3 Mon Sep 17 00:00:00 2001 From: morris Date: Fri, 20 Jan 2023 10:49:43 +0800 Subject: [PATCH 2/2] gdma: prevent mutli-channels connect to the same peripheral 1. add check in the gdma driver, to prevent multiple channels connecting to the same peripheral 2. memory copy DMA ID will occupy the peripheral's DMA ID on some ESP targets (e.g. esp32c3/s3). We should search for a free one when install async memcpy driver. Closes https://github.com/espressif/esp-idf/issues/10575 --- components/driver/i2s/i2s_common.c | 1 + components/esp_hw_support/gdma.c | 98 ++++++++++++++----- .../esp_hw_support/include/esp_private/gdma.h | 45 ++++----- .../port/async_memcpy_impl_gdma.c | 9 +- components/esp_hw_support/test/test_gdma.c | 19 ++-- components/hal/esp32c2/include/hal/gdma_ll.h | 41 +++++--- components/hal/esp32c3/include/hal/gdma_ll.h | 41 +++++--- components/hal/esp32h2/include/hal/gdma_ll.h | 41 +++++--- components/hal/esp32s3/include/hal/gdma_ll.h | 42 +++++--- components/hal/include/hal/gdma_types.h | 43 ++++++++ 10 files changed, 257 insertions(+), 123 deletions(-) create mode 100644 components/hal/include/hal/gdma_types.h diff --git a/components/driver/i2s/i2s_common.c b/components/driver/i2s/i2s_common.c index 86cc8993fe..28335b42ee 100644 --- a/components/driver/i2s/i2s_common.c +++ b/components/driver/i2s/i2s_common.c @@ -898,6 +898,7 @@ esp_err_t i2s_del_channel(i2s_chan_handle_t handle) #endif if (handle->dma.dma_chan) { #if SOC_GDMA_SUPPORTED + gdma_disconnect(handle->dma.dma_chan); gdma_del_channel(handle->dma.dma_chan); #else esp_intr_free(handle->dma.dma_chan); diff --git a/components/esp_hw_support/gdma.c b/components/esp_hw_support/gdma.c index 6ffa28fc8b..d8a2d86349 100644 --- a/components/esp_hw_support/gdma.c +++ b/components/esp_hw_support/gdma.c @@ -71,6 +71,8 @@ struct gdma_group_t { int group_id; // Group ID, index from 0 gdma_hal_context_t hal; // HAL instance is at group level portMUX_TYPE spinlock; // group level spinlock + uint32_t tx_periph_in_use_mask; // each bit indicates which peripheral (TX direction) has been occupied + uint32_t rx_periph_in_use_mask; // each bit indicates which peripheral (RX direction) has been occupied gdma_pair_t *pairs[SOC_GDMA_PAIRS_PER_GROUP]; // handles of GDMA pairs int pair_ref_counts[SOC_GDMA_PAIRS_PER_GROUP]; // reference count used to protect pair install/uninstall }; @@ -246,53 +248,97 @@ err: esp_err_t gdma_connect(gdma_channel_handle_t dma_chan, gdma_trigger_t trig_periph) { - esp_err_t ret = ESP_OK; gdma_pair_t *pair = NULL; gdma_group_t *group = NULL; - ESP_GOTO_ON_FALSE(dma_chan, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); - ESP_GOTO_ON_FALSE(dma_chan->periph_id == GDMA_INVALID_PERIPH_TRIG, ESP_ERR_INVALID_STATE, err, TAG, "channel is using by peripheral: %d", dma_chan->periph_id); + ESP_RETURN_ON_FALSE(dma_chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(dma_chan->periph_id == GDMA_INVALID_PERIPH_TRIG, ESP_ERR_INVALID_STATE, TAG, "channel is using by peripheral: %d", dma_chan->periph_id); pair = dma_chan->pair; group = pair->group; - - dma_chan->periph_id = trig_periph.instance_id; - // enable/disable m2m mode - gdma_ll_enable_m2m_mode(group->hal.dev, pair->pair_id, trig_periph.periph == GDMA_TRIG_PERIPH_M2M); + bool periph_conflict = false; if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_TX) { - gdma_ll_tx_reset_channel(group->hal.dev, pair->pair_id); // reset channel - if (trig_periph.periph != GDMA_TRIG_PERIPH_M2M) { - gdma_ll_tx_connect_to_periph(group->hal.dev, pair->pair_id, trig_periph.instance_id); + if (trig_periph.instance_id >= 0) { + portENTER_CRITICAL(&group->spinlock); + if (group->tx_periph_in_use_mask & (1 << trig_periph.instance_id)) { + periph_conflict = true; + } else { + group->tx_periph_in_use_mask |= (1 << trig_periph.instance_id); + } + portEXIT_CRITICAL(&group->spinlock); + } + if (!periph_conflict) { + gdma_ll_tx_reset_channel(group->hal.dev, pair->pair_id); // reset channel + gdma_ll_tx_connect_to_periph(group->hal.dev, pair->pair_id, trig_periph.periph, trig_periph.instance_id); } } else { - gdma_ll_rx_reset_channel(group->hal.dev, pair->pair_id); // reset channel - if (trig_periph.periph != GDMA_TRIG_PERIPH_M2M) { - gdma_ll_rx_connect_to_periph(group->hal.dev, pair->pair_id, trig_periph.instance_id); + if (trig_periph.instance_id >= 0) { + portENTER_CRITICAL(&group->spinlock); + if (group->rx_periph_in_use_mask & (1 << trig_periph.instance_id)) { + periph_conflict = true; + } else { + group->rx_periph_in_use_mask |= (1 << trig_periph.instance_id); + } + portEXIT_CRITICAL(&group->spinlock); + } + if (!periph_conflict) { + gdma_ll_rx_reset_channel(group->hal.dev, pair->pair_id); // reset channel + gdma_ll_rx_connect_to_periph(group->hal.dev, pair->pair_id, trig_periph.periph, trig_periph.instance_id); } } -err: - return ret; + ESP_RETURN_ON_FALSE(!periph_conflict, ESP_ERR_INVALID_STATE, TAG, "peripheral %d is already used by another channel", trig_periph.instance_id); + dma_chan->periph_id = trig_periph.instance_id; + return ESP_OK; } esp_err_t gdma_disconnect(gdma_channel_handle_t dma_chan) { - esp_err_t ret = ESP_OK; gdma_pair_t *pair = NULL; gdma_group_t *group = NULL; - ESP_GOTO_ON_FALSE(dma_chan, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); - ESP_GOTO_ON_FALSE(dma_chan->periph_id != GDMA_INVALID_PERIPH_TRIG, ESP_ERR_INVALID_STATE, err, TAG, "no peripheral is connected to the channel"); + ESP_RETURN_ON_FALSE(dma_chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(dma_chan->periph_id != GDMA_INVALID_PERIPH_TRIG, ESP_ERR_INVALID_STATE, TAG, "no peripheral is connected to the channel"); + + pair = dma_chan->pair; + group = pair->group; + int save_periph_id = dma_chan->periph_id; + + if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_TX) { + if (save_periph_id >= 0) { + portENTER_CRITICAL(&group->spinlock); + group->tx_periph_in_use_mask &= ~(1 << save_periph_id); + portEXIT_CRITICAL(&group->spinlock); + } + gdma_ll_tx_disconnect_from_periph(group->hal.dev, pair->pair_id); + } else { + if (save_periph_id >= 0) { + portENTER_CRITICAL(&group->spinlock); + group->rx_periph_in_use_mask &= ~(1 << save_periph_id); + portEXIT_CRITICAL(&group->spinlock); + } + gdma_ll_rx_disconnect_from_periph(group->hal.dev, pair->pair_id); + } + + dma_chan->periph_id = GDMA_INVALID_PERIPH_TRIG; + return ESP_OK; +} + +esp_err_t gdma_get_free_m2m_trig_id_mask(gdma_channel_handle_t dma_chan, uint32_t *mask) +{ + gdma_pair_t *pair = NULL; + gdma_group_t *group = NULL; + ESP_RETURN_ON_FALSE(dma_chan && mask, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + uint32_t free_mask = GDMA_LL_M2M_FREE_PERIPH_ID_MASK; pair = dma_chan->pair; group = pair->group; - dma_chan->periph_id = GDMA_INVALID_PERIPH_TRIG; - if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_TX) { - gdma_ll_tx_connect_to_periph(group->hal.dev, pair->pair_id, GDMA_INVALID_PERIPH_TRIG); - } else { - gdma_ll_rx_connect_to_periph(group->hal.dev, pair->pair_id, GDMA_INVALID_PERIPH_TRIG); - } + portENTER_CRITICAL(&group->spinlock); + free_mask &= ~(group->tx_periph_in_use_mask); + free_mask &= ~(group->rx_periph_in_use_mask); + portEXIT_CRITICAL(&group->spinlock); -err: - return ret; + *mask = free_mask; + return ESP_OK; } esp_err_t gdma_set_transfer_ability(gdma_channel_handle_t dma_chan, const gdma_transfer_ability_t *ability) diff --git a/components/esp_hw_support/include/esp_private/gdma.h b/components/esp_hw_support/include/esp_private/gdma.h index 1e0bb51003..eb9cc27ef7 100644 --- a/components/esp_hw_support/include/esp_private/gdma.h +++ b/components/esp_hw_support/include/esp_private/gdma.h @@ -11,6 +11,7 @@ #include #include "soc/gdma_channel.h" +#include "hal/gdma_types.h" #include "esp_err.h" #ifdef __cplusplus @@ -23,34 +24,6 @@ extern "C" { */ typedef struct gdma_channel_t *gdma_channel_handle_t; -/** - * @brief Enumeration of peripherals which have the DMA capability - * @note Some peripheral might not be available on certain chip, please refer to `soc_caps.h` for detail. - * - */ -typedef enum { - GDMA_TRIG_PERIPH_M2M, /*!< GDMA trigger peripheral: M2M */ - GDMA_TRIG_PERIPH_UHCI, /*!< GDMA trigger peripheral: UHCI */ - GDMA_TRIG_PERIPH_SPI, /*!< GDMA trigger peripheral: SPI */ - GDMA_TRIG_PERIPH_I2S, /*!< GDMA trigger peripheral: I2S */ - GDMA_TRIG_PERIPH_AES, /*!< GDMA trigger peripheral: AES */ - GDMA_TRIG_PERIPH_SHA, /*!< GDMA trigger peripheral: SHA */ - GDMA_TRIG_PERIPH_ADC, /*!< GDMA trigger peripheral: ADC */ - GDMA_TRIG_PERIPH_DAC, /*!< GDMA trigger peripheral: DAC */ - GDMA_TRIG_PERIPH_LCD, /*!< GDMA trigger peripheral: LCD */ - GDMA_TRIG_PERIPH_CAM, /*!< GDMA trigger peripheral: CAM */ - GDMA_TRIG_PERIPH_RMT, /*!< GDMA trigger peripheral: RMT */ -} gdma_trigger_peripheral_t; - -/** - * @brief Enumeration of GDMA channel direction - * - */ -typedef enum { - GDMA_CHANNEL_DIRECTION_TX, /*!< GDMA channel direction: TX */ - GDMA_CHANNEL_DIRECTION_RX, /*!< GDMA channel direction: RX */ -} gdma_channel_direction_t; - /** * @brief Collection of configuration items that used for allocating GDMA channel * @@ -325,6 +298,22 @@ esp_err_t gdma_append(gdma_channel_handle_t dma_chan); */ esp_err_t gdma_reset(gdma_channel_handle_t dma_chan); +/** + * @brief Get the mask of free M2M trigger IDs + * + * @note On some ESP targets (e.g. ESP32C3/S3), DMA trigger used for memory copy can be any of valid peripheral's trigger ID, + * which can bring conflict if the peripheral is also using the same trigger ID. This function can return the free IDs + * for memory copy, at the runtime. + * + * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel` + * @param[out] mask Returned mask of free M2M trigger IDs + * @return + * - ESP_OK: Get free M2M trigger IDs successfully + * - ESP_ERR_INVALID_ARG: Get free M2M trigger IDs failed because of invalid argument + * - ESP_FAIL: Get free M2M trigger IDs failed because of other error + */ +esp_err_t gdma_get_free_m2m_trig_id_mask(gdma_channel_handle_t dma_chan, uint32_t *mask); + #ifdef __cplusplus } #endif diff --git a/components/esp_hw_support/port/async_memcpy_impl_gdma.c b/components/esp_hw_support/port/async_memcpy_impl_gdma.c index ee56d174a9..a5dea2b2fd 100644 --- a/components/esp_hw_support/port/async_memcpy_impl_gdma.c +++ b/components/esp_hw_support/port/async_memcpy_impl_gdma.c @@ -48,8 +48,13 @@ esp_err_t async_memcpy_impl_init(async_memcpy_impl_t *impl) goto err; } - gdma_connect(impl->rx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0)); - gdma_connect(impl->tx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0)); + gdma_trigger_t m2m_trigger = GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0); + // get a free DMA trigger ID for memory copy + uint32_t free_m2m_id_mask = 0; + gdma_get_free_m2m_trig_id_mask(impl->tx_channel, &free_m2m_id_mask); + m2m_trigger.instance_id = __builtin_ctz(free_m2m_id_mask); + gdma_connect(impl->rx_channel, m2m_trigger); + gdma_connect(impl->tx_channel, m2m_trigger); gdma_strategy_config_t strategy_config = { .auto_update_desc = true, diff --git a/components/esp_hw_support/test/test_gdma.c b/components/esp_hw_support/test/test_gdma.c index 6dc447ddcb..83ace6a75d 100644 --- a/components/esp_hw_support/test/test_gdma.c +++ b/components/esp_hw_support/test/test_gdma.c @@ -18,31 +18,27 @@ TEST_CASE("GDMA channel allocation", "[gdma]") gdma_tx_event_callbacks_t tx_cbs = {}; gdma_rx_event_callbacks_t rx_cbs = {}; - // install TX channels for different peripherals + // install TX channels for (int i = 0; i < SOC_GDMA_PAIRS_PER_GROUP; i++) { TEST_ESP_OK(gdma_new_channel(&channel_config, &tx_channels[i])); - TEST_ESP_OK(gdma_connect(tx_channels[i], GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0))); TEST_ESP_OK(gdma_register_tx_event_callbacks(tx_channels[i], &tx_cbs, NULL)); }; TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, gdma_new_channel(&channel_config, &tx_channels[0])); // Free interrupts before installing RX interrupts to ensure enough free interrupts for (int i = 0; i < SOC_GDMA_PAIRS_PER_GROUP; i++) { - TEST_ESP_OK(gdma_disconnect(tx_channels[i])); TEST_ESP_OK(gdma_del_channel(tx_channels[i])); } - // install RX channels for different peripherals + // install RX channels channel_config.direction = GDMA_CHANNEL_DIRECTION_RX; for (int i = 0; i < SOC_GDMA_PAIRS_PER_GROUP; i++) { TEST_ESP_OK(gdma_new_channel(&channel_config, &rx_channels[i])); - TEST_ESP_OK(gdma_connect(rx_channels[i], GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0))); TEST_ESP_OK(gdma_register_rx_event_callbacks(rx_channels[i], &rx_cbs, NULL)); } TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, gdma_new_channel(&channel_config, &rx_channels[0])); for (int i = 0; i < SOC_GDMA_PAIRS_PER_GROUP; i++) { - TEST_ESP_OK(gdma_disconnect(rx_channels[i])); TEST_ESP_OK(gdma_del_channel(rx_channels[i])); } @@ -63,7 +59,18 @@ TEST_CASE("GDMA channel allocation", "[gdma]") TEST_ESP_OK(gdma_new_channel(&channel_config, &rx_channels[1])); channel_config.sibling_chan = NULL; TEST_ESP_OK(gdma_new_channel(&channel_config, &rx_channels[0])); + + TEST_ESP_OK(gdma_connect(tx_channels[0], GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_SPI, 2))); + // can't connect multiple channels to the same peripheral + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, gdma_connect(tx_channels[1], GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_SPI, 2))); + TEST_ESP_OK(gdma_connect(tx_channels[1], GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0))); + + TEST_ESP_OK(gdma_connect(rx_channels[0], GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_SPI, 2))); + // but rx and tx can connect to the same peripheral + TEST_ESP_OK(gdma_connect(rx_channels[1], GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0))); for (int i = 0; i < 2; i++) { + TEST_ESP_OK(gdma_disconnect(tx_channels[i])); + TEST_ESP_OK(gdma_disconnect(rx_channels[i])); TEST_ESP_OK(gdma_del_channel(tx_channels[i])); TEST_ESP_OK(gdma_del_channel(rx_channels[i])); } diff --git a/components/hal/esp32c2/include/hal/gdma_ll.h b/components/hal/esp32c2/include/hal/gdma_ll.h index 3db45a5f44..639309f972 100644 --- a/components/hal/esp32c2/include/hal/gdma_ll.h +++ b/components/hal/esp32c2/include/hal/gdma_ll.h @@ -8,6 +8,7 @@ #include /* Required for NULL constant */ #include #include +#include "hal/gdma_types.h" #include "soc/gdma_struct.h" #include "soc/gdma_reg.h" @@ -20,6 +21,10 @@ extern "C" { #define GDMA_LL_RX_EVENT_MASK (0x06A7) #define GDMA_LL_TX_EVENT_MASK (0x1958) +// any "valid" peripheral ID can be used for M2M mode +#define GDMA_LL_M2M_FREE_PERIPH_ID_MASK (0x185) +#define GDMA_LL_INVALID_PERIPH_ID (0x3F) + #define GDMA_LL_EVENT_TX_FIFO_UDF (1<<12) #define GDMA_LL_EVENT_TX_FIFO_OVF (1<<11) #define GDMA_LL_EVENT_RX_FIFO_UDF (1<<10) @@ -35,19 +40,6 @@ extern "C" { #define GDMA_LL_EVENT_RX_DONE (1<<0) ///////////////////////////////////// Common ///////////////////////////////////////// -/** - * @brief Enable DMA channel M2M mode (TX channel n forward data to RX channel n), disabled by default - */ -static inline void gdma_ll_enable_m2m_mode(gdma_dev_t *dev, uint32_t channel, bool enable) -{ - dev->channel[channel].in.in_conf0.mem_trans_en = enable; - if (enable) { - // to enable m2m mode, the tx chan has to be the same to rx chan, and set to a valid value - dev->channel[channel].in.in_peri_sel.sel = 0; - dev->channel[channel].out.out_peri_sel.sel = 0; - } -} - /** * @brief Enable DMA clock gating */ @@ -255,9 +247,19 @@ static inline void gdma_ll_rx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA RX channel to a given peripheral */ -static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { dev->channel[channel].in.in_peri_sel.sel = periph_id; + dev->channel[channel].in.in_conf0.mem_trans_en = (periph == GDMA_TRIG_PERIPH_M2M); +} + +/** + * @brief Disconnect DMA RX channel from peripheral + */ +static inline void gdma_ll_rx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].in.in_peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; + dev->channel[channel].in.in_conf0.mem_trans_en = false; } ///////////////////////////////////// TX ///////////////////////////////////////// @@ -458,11 +460,20 @@ static inline void gdma_ll_tx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA TX channel to a given peripheral */ -static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { + (void)periph; dev->channel[channel].out.out_peri_sel.sel = periph_id; } +/** + * @brief Disconnect DMA TX channel from peripheral + */ +static inline void gdma_ll_tx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].out.out_peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32c3/include/hal/gdma_ll.h b/components/hal/esp32c3/include/hal/gdma_ll.h index 67aa058e42..617b118f21 100644 --- a/components/hal/esp32c3/include/hal/gdma_ll.h +++ b/components/hal/esp32c3/include/hal/gdma_ll.h @@ -8,6 +8,7 @@ #include /* Required for NULL constant */ #include #include +#include "hal/gdma_types.h" #include "soc/gdma_struct.h" #include "soc/gdma_reg.h" @@ -20,6 +21,10 @@ extern "C" { #define GDMA_LL_RX_EVENT_MASK (0x06A7) #define GDMA_LL_TX_EVENT_MASK (0x1958) +// any "valid" peripheral ID can be used for M2M mode +#define GDMA_LL_M2M_FREE_PERIPH_ID_MASK (0x1CD) +#define GDMA_LL_INVALID_PERIPH_ID (0x3F) + #define GDMA_LL_EVENT_TX_FIFO_UDF (1<<12) #define GDMA_LL_EVENT_TX_FIFO_OVF (1<<11) #define GDMA_LL_EVENT_RX_FIFO_UDF (1<<10) @@ -35,19 +40,6 @@ extern "C" { #define GDMA_LL_EVENT_RX_DONE (1<<0) ///////////////////////////////////// Common ///////////////////////////////////////// -/** - * @brief Enable DMA channel M2M mode (TX channel n forward data to RX channel n), disabled by default - */ -static inline void gdma_ll_enable_m2m_mode(gdma_dev_t *dev, uint32_t channel, bool enable) -{ - dev->channel[channel].in.in_conf0.mem_trans_en = enable; - if (enable) { - // to enable m2m mode, the tx chan has to be the same to rx chan, and set to a valid value - dev->channel[channel].in.in_peri_sel.sel = 0; - dev->channel[channel].out.out_peri_sel.sel = 0; - } -} - /** * @brief Enable DMA clock gating */ @@ -255,9 +247,19 @@ static inline void gdma_ll_rx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA RX channel to a given peripheral */ -static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { dev->channel[channel].in.in_peri_sel.sel = periph_id; + dev->channel[channel].in.in_conf0.mem_trans_en = (periph == GDMA_TRIG_PERIPH_M2M); +} + +/** + * @brief Disconnect DMA RX channel from peripheral + */ +static inline void gdma_ll_rx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].in.in_peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; + dev->channel[channel].in.in_conf0.mem_trans_en = false; } ///////////////////////////////////// TX ///////////////////////////////////////// @@ -458,11 +460,20 @@ static inline void gdma_ll_tx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA TX channel to a given peripheral */ -static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { + (void)periph; dev->channel[channel].out.out_peri_sel.sel = periph_id; } +/** + * @brief Disconnect DMA TX channel from peripheral + */ +static inline void gdma_ll_tx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].out.out_peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32h2/include/hal/gdma_ll.h b/components/hal/esp32h2/include/hal/gdma_ll.h index 67aa058e42..617b118f21 100644 --- a/components/hal/esp32h2/include/hal/gdma_ll.h +++ b/components/hal/esp32h2/include/hal/gdma_ll.h @@ -8,6 +8,7 @@ #include /* Required for NULL constant */ #include #include +#include "hal/gdma_types.h" #include "soc/gdma_struct.h" #include "soc/gdma_reg.h" @@ -20,6 +21,10 @@ extern "C" { #define GDMA_LL_RX_EVENT_MASK (0x06A7) #define GDMA_LL_TX_EVENT_MASK (0x1958) +// any "valid" peripheral ID can be used for M2M mode +#define GDMA_LL_M2M_FREE_PERIPH_ID_MASK (0x1CD) +#define GDMA_LL_INVALID_PERIPH_ID (0x3F) + #define GDMA_LL_EVENT_TX_FIFO_UDF (1<<12) #define GDMA_LL_EVENT_TX_FIFO_OVF (1<<11) #define GDMA_LL_EVENT_RX_FIFO_UDF (1<<10) @@ -35,19 +40,6 @@ extern "C" { #define GDMA_LL_EVENT_RX_DONE (1<<0) ///////////////////////////////////// Common ///////////////////////////////////////// -/** - * @brief Enable DMA channel M2M mode (TX channel n forward data to RX channel n), disabled by default - */ -static inline void gdma_ll_enable_m2m_mode(gdma_dev_t *dev, uint32_t channel, bool enable) -{ - dev->channel[channel].in.in_conf0.mem_trans_en = enable; - if (enable) { - // to enable m2m mode, the tx chan has to be the same to rx chan, and set to a valid value - dev->channel[channel].in.in_peri_sel.sel = 0; - dev->channel[channel].out.out_peri_sel.sel = 0; - } -} - /** * @brief Enable DMA clock gating */ @@ -255,9 +247,19 @@ static inline void gdma_ll_rx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA RX channel to a given peripheral */ -static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { dev->channel[channel].in.in_peri_sel.sel = periph_id; + dev->channel[channel].in.in_conf0.mem_trans_en = (periph == GDMA_TRIG_PERIPH_M2M); +} + +/** + * @brief Disconnect DMA RX channel from peripheral + */ +static inline void gdma_ll_rx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].in.in_peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; + dev->channel[channel].in.in_conf0.mem_trans_en = false; } ///////////////////////////////////// TX ///////////////////////////////////////// @@ -458,11 +460,20 @@ static inline void gdma_ll_tx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA TX channel to a given peripheral */ -static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { + (void)periph; dev->channel[channel].out.out_peri_sel.sel = periph_id; } +/** + * @brief Disconnect DMA TX channel from peripheral + */ +static inline void gdma_ll_tx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].out.out_peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32s3/include/hal/gdma_ll.h b/components/hal/esp32s3/include/hal/gdma_ll.h index de8337468d..2fc7029303 100644 --- a/components/hal/esp32s3/include/hal/gdma_ll.h +++ b/components/hal/esp32s3/include/hal/gdma_ll.h @@ -8,7 +8,7 @@ #include /* For NULL declaration */ #include #include -#include "soc/soc_caps.h" +#include "hal/gdma_types.h" #include "soc/gdma_struct.h" #include "soc/gdma_reg.h" @@ -21,6 +21,10 @@ extern "C" { #define GDMA_LL_RX_EVENT_MASK (0x3FF) #define GDMA_LL_TX_EVENT_MASK (0xFF) +// any "valid" peripheral ID can be used for M2M mode +#define GDMA_LL_M2M_FREE_PERIPH_ID_MASK (0x3FF) +#define GDMA_LL_INVALID_PERIPH_ID (0x3F) + #define GDMA_LL_EVENT_TX_L3_FIFO_UDF (1<<7) #define GDMA_LL_EVENT_TX_L3_FIFO_OVF (1<<6) #define GDMA_LL_EVENT_TX_L1_FIFO_UDF (1<<5) @@ -49,19 +53,6 @@ extern "C" { #define GDMA_LL_EXT_MEM_BK_SIZE_64B (2) ///////////////////////////////////// Common ///////////////////////////////////////// -/** - * @brief Enable DMA channel M2M mode (TX channel n forward data to RX channel n), disabled by default - */ -static inline void gdma_ll_enable_m2m_mode(gdma_dev_t *dev, uint32_t channel, bool enable) -{ - dev->channel[channel].in.conf0.mem_trans_en = enable; - if (enable) { - // to enable m2m mode, the tx chan has to be the same to rx chan, and set to a valid value - dev->channel[channel].in.peri_sel.sel = 0; - dev->channel[channel].out.peri_sel.sel = 0; - } -} - /** * @brief Enable DMA clock gating */ @@ -303,9 +294,19 @@ static inline void gdma_ll_rx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA RX channel to a given peripheral */ -static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { dev->channel[channel].in.peri_sel.sel = periph_id; + dev->channel[channel].in.conf0.mem_trans_en = (periph == GDMA_TRIG_PERIPH_M2M); +} + +/** + * @brief Disconnect DMA RX channel from peripheral + */ +static inline void gdma_ll_rx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].in.peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; + dev->channel[channel].in.conf0.mem_trans_en = false; } ///////////////////////////////////// TX ///////////////////////////////////////// @@ -532,11 +533,20 @@ static inline void gdma_ll_tx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA TX channel to a given peripheral */ -static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { + (void)periph; dev->channel[channel].out.peri_sel.sel = periph_id; } +/** + * @brief Disconnect DMA TX channel from peripheral + */ +static inline void gdma_ll_tx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].out.peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/include/hal/gdma_types.h b/components/hal/include/hal/gdma_types.h new file mode 100644 index 0000000000..eb1447a78f --- /dev/null +++ b/components/hal/include/hal/gdma_types.h @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Enumeration of peripherals which have the DMA capability + * @note Some peripheral might not be available on certain chip, please refer to `soc_caps.h` for detail. + * + */ +typedef enum { + GDMA_TRIG_PERIPH_M2M, /*!< GDMA trigger peripheral: M2M */ + GDMA_TRIG_PERIPH_UHCI, /*!< GDMA trigger peripheral: UHCI */ + GDMA_TRIG_PERIPH_SPI, /*!< GDMA trigger peripheral: SPI */ + GDMA_TRIG_PERIPH_I2S, /*!< GDMA trigger peripheral: I2S */ + GDMA_TRIG_PERIPH_AES, /*!< GDMA trigger peripheral: AES */ + GDMA_TRIG_PERIPH_SHA, /*!< GDMA trigger peripheral: SHA */ + GDMA_TRIG_PERIPH_ADC, /*!< GDMA trigger peripheral: ADC */ + GDMA_TRIG_PERIPH_DAC, /*!< GDMA trigger peripheral: DAC */ + GDMA_TRIG_PERIPH_LCD, /*!< GDMA trigger peripheral: LCD */ + GDMA_TRIG_PERIPH_CAM, /*!< GDMA trigger peripheral: CAM */ + GDMA_TRIG_PERIPH_RMT, /*!< GDMA trigger peripheral: RMT */ +} gdma_trigger_peripheral_t; + +/** + * @brief Enumeration of GDMA channel direction + * + */ +typedef enum { + GDMA_CHANNEL_DIRECTION_TX, /*!< GDMA channel direction: TX */ + GDMA_CHANNEL_DIRECTION_RX, /*!< GDMA channel direction: RX */ +} gdma_channel_direction_t; + +#ifdef __cplusplus +} +#endif