Merge branch 'feat/uart_dma_support_v5.1' into 'release/v5.1'

feat(uart): uart dma support for insternal usage on v5.1

See merge request espressif/esp-idf!41691
This commit is contained in:
morris
2025-09-25 14:57:31 +08:00
38 changed files with 1335 additions and 202 deletions

View File

@@ -119,7 +119,7 @@ STAILQ_HEAD(g_hci_rxinfo_list, hci_message);
DRAM_ATTR struct g_hci_rxinfo_list g_hci_rxinfo_head;
static DRAM_ATTR struct uart_env_tag uart_env;
static volatile uhci_dev_t *s_uhci_hw = &UHCI0;
static uhci_dev_t *s_uhci_hw = &UHCI0;
static DRAM_ATTR gdma_channel_handle_t s_rx_channel;
static DRAM_ATTR gdma_channel_handle_t s_tx_channel;
@@ -470,8 +470,8 @@ static void hci_driver_uart_dma_install(void)
gdma_register_tx_event_callbacks(s_tx_channel, &tx_cbs, NULL);
// configure UHCI
uhci_ll_init((uhci_dev_t *)s_uhci_hw);
// uhci_ll_set_eof_mode((uhci_dev_t *)s_uhci_hw, UHCI_RX_LEN_EOF);
uhci_ll_set_eof_mode((uhci_dev_t *)s_uhci_hw, UHCI_RX_IDLE_EOF);
// uhci_ll_rx_set_eof_mode((uhci_dev_t *)s_uhci_hw, UHCI_RX_LEN_EOF);
uhci_ll_rx_set_eof_mode((uhci_dev_t *)s_uhci_hw, UHCI_RX_IDLE_EOF);
// disable software flow control
s_uhci_hw->escape_conf.val = 0;
uhci_ll_attach_uart_port((uhci_dev_t *)s_uhci_hw, s_hci_driver_uart_dma_env.hci_uart_params->hci_uart_port);

View File

@@ -117,3 +117,7 @@ components/driver/test_apps/touch_sensor_v2:
components/driver/test_apps/twai:
disable:
- if: SOC_TWAI_SUPPORTED != 1
components/driver/test_apps/uhci:
disable:
- if: SOC_UHCI_SUPPORTED != 1

View File

@@ -189,6 +189,11 @@ if(CONFIG_SOC_TWAI_SUPPORTED)
list(APPEND srcs "twai/twai.c")
endif()
# UHCI related source files
if(CONFIG_SOC_UHCI_SUPPORTED)
list(APPEND srcs "uart/uhci.c")
endif()
# UART related source files
if(CONFIG_SOC_UART_SUPPORTED)
list(APPEND srcs "uart/uart.c")

View File

@@ -0,0 +1,10 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
set(COMPONENTS main)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(uhci_test)

View File

@@ -0,0 +1,2 @@
| Supported Targets | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
| ----------------- | -------- | -------- | -------- | -------- |

View File

@@ -0,0 +1,8 @@
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(
SRCS "test_app_main.c"
"test_uhci.c"
REQUIRES driver unity test_utils esp_psram
WHOLE_ARCHIVE
)

View File

@@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "unity_test_utils.h"
#include "esp_heap_caps.h"
#include "esp_newlib.h"
#define TEST_MEMORY_LEAK_THRESHOLD (500)
void setUp(void)
{
unity_utils_record_free_mem();
}
void tearDown(void)
{
esp_reent_cleanup(); //clean up some of the newlib's lazy allocations
unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
}
void app_main(void)
{
printf(" __ __ __ __ ______ __ \n");
printf("| | | | | | | | / || |\n");
printf("| | | | | |__| | | ,----'| |\n");
printf("| | | | | __ | | | | |\n");
printf("| `--' | | | | | | `----.| |\n");
printf(" \\______/ |__| |__| \\______||__|\n");
unity_run_menu();
}

View File

@@ -0,0 +1,121 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <sys/param.h>
#include "unity.h"
#include "test_utils.h"
#include "driver/uart.h"
#include "esp_private/uhci.h"
#define DATA_LENGTH 5024
#define EX_UART_NUM 1
#define UART_TX_IO 2
#define UART_RX_IO 3
TEST_CASE("UHCI driver memory leaking check", "[uhci]")
{
uart_config_t uart_config = {
.baud_rate = 1 * 1000 * 1000,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
//UART parameter config
TEST_ESP_OK(uart_param_config(EX_UART_NUM, &uart_config));
TEST_ESP_OK(uart_set_pin(EX_UART_NUM, UART_TX_IO, UART_RX_IO, -1, -1));
uhci_controller_config_t uhci_cfg = {
.uart_port = EX_UART_NUM,
.tx_trans_queue_depth = 30,
.max_receive_internal_mem = 10 * 1024,
.max_transmit_size = 10 * 1024,
.dma_burst_size = 32,
.rx_eof_flags.idle_eof = 1,
};
uhci_controller_handle_t uhci_ctrl;
int size = esp_get_free_heap_size();
for (int i = 0; i < 5; i++) {
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
vTaskDelay(10 / portTICK_PERIOD_MS);
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
}
TEST_ASSERT_INT_WITHIN(300, size, esp_get_free_heap_size());
}
TEST_CASE("UHCI controller install-uninstall test", "[i2c]")
{
uhci_controller_config_t uhci_cfg = {
.uart_port = EX_UART_NUM,
.tx_trans_queue_depth = 30,
.max_receive_internal_mem = 10 * 1024,
.max_transmit_size = 10 * 1024,
.dma_burst_size = 32,
.rx_eof_flags.idle_eof = 1,
};
uhci_controller_handle_t uhci_ctrl;
uhci_controller_handle_t uhci_ctrl2;
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, uhci_new_controller(&uhci_cfg, &uhci_ctrl2));
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
}
typedef enum {
UHCI_EVT_PARTIAL_DATA,
UHCI_EVT_EOF,
} uhci_event_t;
typedef struct {
QueueHandle_t uhci_queue;
size_t receive_size;
uint8_t *p_receive_data;
} uhci_context_t;
TEST_CASE("UHCI write and receive with idle eof", "[uhci]")
{
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_XTAL,
};
TEST_ESP_OK(uart_param_config(EX_UART_NUM, &uart_config));
// Connect TX and RX together for testing self send-receive
TEST_ESP_OK(uart_set_pin(EX_UART_NUM, 2, 5, -1, -1));
uhci_controller_config_t uhci_cfg = {
.uart_port = EX_UART_NUM,
.tx_trans_queue_depth = 30,
.max_receive_internal_mem = 10 * 1024,
.max_transmit_size = 10 * 1024,
.dma_burst_size = 32,
.rx_eof_flags.idle_eof = 1,
};
uhci_controller_handle_t uhci_ctrl;
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
// uint8_t data_wr[DATA_LENGTH];
uint8_t *data_wr = heap_caps_calloc(1, DATA_LENGTH, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
for (int i = 0; i < DATA_LENGTH; i++) {
data_wr[i] = i;
}
TEST_ESP_OK(uhci_transmit(uhci_ctrl, data_wr, DATA_LENGTH));
uhci_wait_all_tx_transaction_done(uhci_ctrl, portMAX_DELAY);
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
}

View File

@@ -0,0 +1,20 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
from pytest_embedded_idf.utils import soc_filtered_targets
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'release',
'cache_safe',
],
indirect=True,
)
@idf_parametrize('target', soc_filtered_targets('SOC_UHCI_SUPPORTED == 1'), indirect=['target'])
def test_uhci(dut: Dut) -> None:
dut.run_all_single_board_cases()

View File

@@ -0,0 +1,11 @@
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_UHCI_ISR_CACHE_SAFE=y
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
CONFIG_COMPILER_OPTIMIZATION_NONE=y
# place non-ISR FreeRTOS functions in Flash
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
# silent the error check, as the error string are stored in rodata, causing RTL check failure
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_HAL_ASSERTION_SILENT=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y

View File

@@ -0,0 +1,6 @@
CONFIG_PM_ENABLE=y
CONFIG_PM_DFS_INIT_AUTO=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@@ -0,0 +1,2 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT_EN=n

View File

@@ -0,0 +1,158 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "esp_private/uhci_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-W#pragma-messages"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas"
#endif
#pragma message("Disclaimer: This header is intended for internal use and authorized users only.")
#pragma message("Any use by unauthorized parties is at their own risk.")
#pragma message("This file is not maintained for this version; external bug reports or feature requests will NOT be accepted.")
#pragma message("For formal UHCI functionality, please upgrade to version v5.3 or later.")
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
/**
* @brief UHCI controller specific configurations
*/
typedef struct {
uart_port_t uart_port; /*!< UART port that connect to UHCI controller */
size_t tx_trans_queue_depth; /*!< Depth of internal transfer queue, increase this value can support more transfers pending in the background */
size_t max_transmit_size; /*!< Maximum transfer size in one transaction, in bytes. This decides the number of DMA nodes will be used for each transaction */
size_t max_receive_internal_mem; /*!< Maximum transfer size in one transaction, in bytes. Each DMA node can point to a maximum of 4096 bytes. This value determines the number of DMA nodes used for each transaction. When your transfer size is large enough, it is recommended to set this value greater than 4096 to facilitate efficient ping-pong operations, such as 10 * 1024. */
size_t dma_burst_size; /*!< DMA burst size, in bytes */
size_t max_packet_receive; /*!< Max receive size, auto stop receiving after reach this value, only valid when `length_eof` set true */
struct {
uint16_t rx_brk_eof: 1; /*!< UHCI will end payload receive process when NULL frame is received by UART. */
uint16_t idle_eof: 1; /*!< UHCI will end payload receive process when UART has been in idle state. */
uint16_t length_eof: 1; /*!< UHCI will end payload receive process when the receiving byte count has reached the specific value. */
} rx_eof_flags; /*!< UHCI eof flags */
} uhci_controller_config_t;
/**
* @brief Structure for defining callback functions for UHCI events.
*/
typedef struct {
uhci_tx_done_callback_t on_tx_trans_done; /*!< Callback function for handling the completion of a transmission. */
} uhci_event_callbacks_t;
/**
* @brief Create and initialize a new UHCI controller.
*
* This function initializes a new UHCI controller instance based on the provided configuration.
* It allocates and configures resources required for the UHCI controller, such as DMA and
* communication settings. The created controller handle is returned through the output parameter.
*
* @param[in] config Pointer to a `uhci_controller_config_t` structure containing the
* configuration parameters for the UHCI controller.
* @param[out] ret_uhci_ctrl Pointer to a variable where the handle to the newly created UHCI controller
* will be stored. This handle is used in subsequent operations involving the
* controller.
*
* @return
* - `ESP_OK`: Controller successfully created and initialized.
* - `ESP_ERR_INVALID_ARG`: One or more arguments are invalid (e.g., null pointers or invalid config).
* - `ESP_ERR_NO_MEM`: Memory allocation for the controller failed.
* - Other error codes: Indicate failure in the underlying hardware or driver initialization.
*/
esp_err_t uhci_new_controller(const uhci_controller_config_t *config, uhci_controller_handle_t *ret_uhci_ctrl);
/**
* @brief Transmit data using the UHCI controller.
*
* This function sends data from the provided buffer through the UHCI controller. It uses the DMA
* capabilities of UHCI to efficiently handle data transmission via UART.
*
* @param[in] uhci_ctrl Handle to the UHCI controller, which was previously created using
* `uhci_new_controller()`.
* @param[in] write_buffer Pointer to the buffer containing the data to be transmitted.
* The buffer must remain valid until the transmission is complete.
* @param[in] write_size The number of bytes to transmit from the buffer.
*
* @note The function is an non-blocking api, which means this function will return immediately. You can
* get corresponding event from callbacks.
*
* @return
* - `ESP_OK`: Data successfully queued for transmission.
* - `ESP_ERR_INVALID_ARG`: Invalid arguments (e.g., null buffer, invalid handle, or zero `write_size`).
*/
esp_err_t uhci_transmit(uhci_controller_handle_t uhci_ctrl, uint8_t *write_buffer, size_t write_size);
/**
* @brief Uninstall the UHCI (UART Host Controller Interface) driver and release resources.
*
* This function deinitializes the UHCI controller and frees any resources allocated during its
* initialization. It ensures proper cleanup and prevents resource leaks when the UHCI controller
* is no longer needed.
*
* @param[in] uhci_ctrl Handle to the UHCI controller, which was previously created using
* `uhci_new_controller()`. Passing an invalid or uninitialized handle
* may result in undefined behavior.
*
* @return
* - `ESP_OK`: The UHCI driver was successfully uninstalled, and resources were released.
* - `ESP_ERR_INVALID_ARG`: The provided `uhci_ctrl` handle is invalid or null.
*/
esp_err_t uhci_del_controller(uhci_controller_handle_t uhci_ctrl);
/**
* @brief Register event callback functions for a UHCI controller.
*
* This function allows the user to register callback functions to handle specific UHCI events, such as
* transmission or reception completion. The callbacks provide a mechanism to handle asynchronous events
* generated by the UHCI controller.
*
* @param[in] uhci_ctrl Handle to the UHCI controller, which was previously created using
* `uhci_new_controller()`.
* @param[in] cbs Pointer to a `uhci_event_callbacks_t` structure that defines the callback
* functions to be registered. This structure includes pointers to the callback
* functions for handling UHCI events.
* @param[in] user_data Pointer to user-defined data that will be passed to the callback functions
* when they are invoked. This can be used to provide context or state information
* specific to the application.
*
* @return
* - `ESP_OK`: Event callbacks were successfully registered.
* - `ESP_ERR_INVALID_ARG`: Invalid arguments (e.g., null `uhci_ctrl` handle or `cbs` pointer).
*/
esp_err_t uhci_register_event_callbacks(uhci_controller_handle_t uhci_ctrl, const uhci_event_callbacks_t *cbs, void *user_data);
/**
* @brief Wait for all pending TX transactions done
*
* @param[in] uhci_ctrl UHCI controller that created by `uhci_new_controller`
* @param[in] timeout_ms Timeout in milliseconds, `-1` means to wait forever
* @return
* - ESP_OK: All pending TX transactions is finished and recycled
* - ESP_ERR_INVALID_ARG: Wait for all pending TX transactions done failed because of invalid argument
* - ESP_ERR_TIMEOUT: Wait for all pending TX transactions done timeout
* - ESP_FAIL: Wait for all pending TX transactions done failed because of other error
*/
esp_err_t uhci_wait_all_tx_transaction_done(uhci_controller_handle_t uhci_ctrl, int timeout_ms);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stddef.h>
#include "hal/uart_types.h"
#include "hal/uhci_types.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief UHCI Controller Handle Type
*/
typedef struct uhci_controller_t *uhci_controller_handle_t;
/**
* @brief UHCI TX Done Event Data
*/
typedef struct {
uint8_t *buffer; /**< Pointer to the which data buffer has been finished the transaction */
size_t sent_size; /**< Size has been sent out */
} uhci_tx_done_event_data_t;
/**
* @brief UHCI TX Done Callback Function Type
* @param uhci_ctrl Handle to the UHCI controller that initiated the transmission.
* @param edata Pointer to a structure containing event data related to the completed transmission.
* This structure provides details such as the number of bytes transmitted and any
* status information relevant to the operation.
* @param user_ctx User-defined context passed during the callback registration.
* It can be used to maintain application-specific state or data.
*
* @return Whether a high priority task has been waken up by this callback function
*/
typedef bool (*uhci_tx_done_callback_t)(uhci_controller_handle_t uhci_ctrl, const uhci_tx_done_event_data_t *edata, void *user_ctx);
/**
* @brief UHCI RX Done Event Data Structure
*/
typedef struct {
uint8_t *data; /*!< Pointer to the received data buffer */
size_t recv_size; /*!< Number of bytes received */
struct {
uint32_t totally_received: 1; /*!< When callback is invoked, while this bit is not set, means the current event gives partial of whole data, the transaction has not been finished. If set, means the current event gives whole data, the transaction finished. */
} flags; /*!< I2C master config flags */
} uhci_rx_event_data_t;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,395 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdint.h>
#include "esp_intr_alloc.h"
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_check.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "driver/uart.h"
#include "esp_private/uhci.h"
#include "esp_private/uhci_types.h"
#include "soc/uhci_periph.h"
#include "soc/soc_caps.h"
#include "hal/uhci_hal.h"
#include "hal/uhci_ll.h"
#include "hal/dma_types.h"
#include "hal/cache_hal.h"
#include "hal/cache_ll.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/gdma.h"
#include "esp_memory_utils.h"
#include "uhci_private.h"
#if CONFIG_UHCI_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 "esp_log.h"
static const char* TAG = "uhci";
#define UHCI_DMA_DESC_ALIGN 4
typedef struct uhci_platform_t {
_lock_t mutex; // platform level mutex lock.
uhci_controller_handle_t controller[SOC_UHCI_NUM]; // array of UHCI instances.
} uhci_platform_t;
static uhci_platform_t s_uhci_platform = {}; // singleton platform
static bool uhci_ctrl_occupied(int port_num)
{
return s_uhci_platform.controller[port_num] != NULL;
}
static void uhci_do_transmit(uhci_controller_handle_t uhci_ctrl, uhci_transaction_desc_t *trans);
static bool uhci_gdma_tx_callback_eof(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
{
BaseType_t do_yield = pdFALSE;
uhci_controller_handle_t uhci_ctrl = (uhci_controller_handle_t) user_data;
uhci_transaction_desc_t *trans_desc = NULL;
bool need_yield = false;
uhci_tx_fsm_t expected_fsm = UHCI_TX_FSM_RUN;
if (atomic_compare_exchange_strong(&uhci_ctrl->tx_dir.tx_fsm, &expected_fsm, UHCI_TX_FSM_ENABLE_WAIT)) {
trans_desc = uhci_ctrl->tx_dir.cur_trans;
xQueueSendFromISR(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_COMPLETE], &trans_desc, &do_yield);
if (do_yield) {
need_yield = true;
}
atomic_store(&uhci_ctrl->tx_dir.tx_fsm, UHCI_TX_FSM_ENABLE);
}
if (uhci_ctrl->pm_lock) {
esp_pm_lock_release(uhci_ctrl->pm_lock);
}
if (uhci_ctrl->tx_dir.on_tx_trans_done) {
uhci_tx_done_event_data_t evt_data = {
.buffer = uhci_ctrl->tx_dir.cur_trans->buffer,
.sent_size = uhci_ctrl->tx_dir.cur_trans->buffer_size,
};
if (uhci_ctrl->tx_dir.on_tx_trans_done(uhci_ctrl, &evt_data, uhci_ctrl->user_data)) {
need_yield |= true;
}
}
expected_fsm = UHCI_TX_FSM_ENABLE;
if (atomic_compare_exchange_strong(&uhci_ctrl->tx_dir.tx_fsm, &expected_fsm, UHCI_TX_FSM_RUN_WAIT)) {
if (xQueueReceiveFromISR(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_PROGRESS], &trans_desc, &do_yield) == pdTRUE) {
atomic_store(&uhci_ctrl->tx_dir.tx_fsm, UHCI_TX_FSM_RUN);
uhci_do_transmit(uhci_ctrl, trans_desc);
if (do_yield) {
need_yield |= true;
}
} else {
atomic_store(&uhci_ctrl->tx_dir.tx_fsm, UHCI_TX_FSM_ENABLE);
}
}
return need_yield;
}
static esp_err_t uhci_gdma_initialize(uhci_controller_handle_t uhci_ctrl, const uhci_controller_config_t *config)
{
// Initialize DMA TX channel
gdma_channel_alloc_config_t tx_alloc_config = {
.direction = GDMA_CHANNEL_DIRECTION_TX,
#if CONFIG_UHCI_ISR_CACHE_SAFE
.flags.isr_cache_safe = true,
#endif
};
ESP_RETURN_ON_ERROR(gdma_new_channel(&tx_alloc_config, &uhci_ctrl->tx_dir.dma_chan), TAG, "DMA tx channel alloc failed");
gdma_connect(uhci_ctrl->tx_dir.dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0));
gdma_strategy_config_t strategy_config = {
.auto_update_desc = true,
.owner_check = true,
};
gdma_apply_strategy(uhci_ctrl->tx_dir.dma_chan, &strategy_config);
uhci_ctrl->tx_dir.ext_mem_align = 4;
uhci_ctrl->tx_dir.int_mem_align = 1;
// create DMA link list
uhci_ctrl->tx_dir.dma_nodes = heap_caps_aligned_calloc(UHCI_DMA_DESC_ALIGN, UHCI_DMA_NODE_NUM, sizeof(dma_descriptor_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
assert(uhci_ctrl->tx_dir.dma_nodes);
// Register callbacks
gdma_tx_event_callbacks_t tx_cbk = {
.on_trans_eof = uhci_gdma_tx_callback_eof,
};
ESP_RETURN_ON_ERROR(gdma_register_tx_event_callbacks(uhci_ctrl->tx_dir.dma_chan, &tx_cbk, uhci_ctrl), TAG, "register DMA callbacks failed");
return ESP_OK;
}
static esp_err_t uhci_gdma_deinitialize(uhci_controller_handle_t uhci_ctrl)
{
if (uhci_ctrl->tx_dir.dma_chan) {
ESP_RETURN_ON_ERROR(gdma_disconnect(uhci_ctrl->tx_dir.dma_chan), TAG, "disconnect tx dma channel failed");
ESP_RETURN_ON_ERROR(gdma_del_channel(uhci_ctrl->tx_dir.dma_chan), TAG, "delete tx dma channel failed");
}
if (uhci_ctrl->tx_dir.dma_nodes != NULL) {
heap_caps_free(uhci_ctrl->tx_dir.dma_nodes);
}
return ESP_OK;
}
static void uhci_do_transmit(uhci_controller_handle_t uhci_ctrl, uhci_transaction_desc_t *trans)
{
uhci_ctrl->tx_dir.cur_trans = trans;
if (trans->buffer_size > UHCI_DMA_NODE_NUM * DMA_DESCRIPTOR_BUFFER_MAX_SIZE) {
ESP_LOGE(TAG, "buffer size is greater than 3 * 4095 bytes");
abort();
}
int node_count = (trans->buffer_size + DMA_DESCRIPTOR_BUFFER_MAX_SIZE - 1) / DMA_DESCRIPTOR_BUFFER_MAX_SIZE;
size_t remaining_size = trans->buffer_size;
uint8_t *buffer_ptr = trans->buffer;
for (int i = 0; i < node_count; i++) {
size_t current_size = (remaining_size > DMA_DESCRIPTOR_BUFFER_MAX_SIZE) ? DMA_DESCRIPTOR_BUFFER_MAX_SIZE : remaining_size;
uhci_ctrl->tx_dir.dma_nodes[i].buffer = buffer_ptr;
uhci_ctrl->tx_dir.dma_nodes[i].dw0.size = current_size;
uhci_ctrl->tx_dir.dma_nodes[i].dw0.length = current_size;
uhci_ctrl->tx_dir.dma_nodes[i].dw0.owner = 1;
if (i == node_count - 1) {
// last node
uhci_ctrl->tx_dir.dma_nodes[i].dw0.suc_eof = 1;
uhci_ctrl->tx_dir.dma_nodes[i].next = NULL;
} else {
uhci_ctrl->tx_dir.dma_nodes[i].dw0.suc_eof = 0;
uhci_ctrl->tx_dir.dma_nodes[i].next = &uhci_ctrl->tx_dir.dma_nodes[i + 1];
}
buffer_ptr += current_size;
remaining_size -= current_size;
}
// acquire power manager lock
if (uhci_ctrl->pm_lock) {
esp_pm_lock_acquire(uhci_ctrl->pm_lock);
}
gdma_start(uhci_ctrl->tx_dir.dma_chan, (intptr_t)uhci_ctrl->tx_dir.dma_nodes);
}
esp_err_t uhci_transmit(uhci_controller_handle_t uhci_ctrl, uint8_t *write_buffer, size_t write_size)
{
ESP_RETURN_ON_FALSE(uhci_ctrl, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE((write_buffer != NULL), ESP_ERR_INVALID_ARG, TAG, "write buffer null");
size_t alignment = 0;
size_t cache_line_size = 0;
esp_ptr_external_ram(write_buffer) ? (alignment = uhci_ctrl->tx_dir.ext_mem_align, cache_line_size = uhci_ctrl->ext_mem_cache_line_size) : (alignment = uhci_ctrl->tx_dir.int_mem_align, cache_line_size = uhci_ctrl->int_mem_cache_line_size);
ESP_RETURN_ON_FALSE(((((uintptr_t)write_buffer) & (alignment - 1)) == 0) && (((write_size) & (alignment - 1)) == 0), ESP_ERR_INVALID_ARG,
TAG, "buffer address or size are not %d bytes aligned", alignment);
uhci_transaction_desc_t *t = NULL;
if (xQueueReceive(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_READY], &t, 0) != pdTRUE) {
if (xQueueReceive(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_COMPLETE], &t, 0) == pdTRUE) {
atomic_fetch_sub(&uhci_ctrl->tx_dir.num_trans_inflight, 1);
}
}
ESP_RETURN_ON_FALSE(t, ESP_ERR_INVALID_STATE, TAG, "no free transaction descriptor, please consider increasing trans_queue_depth");
memset(t, 0, sizeof(uhci_transaction_desc_t));
t->buffer = write_buffer;
t->buffer_size = write_size;
ESP_RETURN_ON_FALSE(xQueueSend(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_PROGRESS], &t, 0) == pdTRUE, ESP_ERR_NO_MEM, TAG, "uhci tx transaction queue full");
atomic_fetch_add(&uhci_ctrl->tx_dir.num_trans_inflight, 1);
uhci_tx_fsm_t expected_fsm = UHCI_TX_FSM_ENABLE;
if (atomic_compare_exchange_strong(&uhci_ctrl->tx_dir.tx_fsm, &expected_fsm, UHCI_TX_FSM_RUN_WAIT)) {
if (xQueueReceive(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_PROGRESS], &t, 0)) {
atomic_store(&uhci_ctrl->tx_dir.tx_fsm, UHCI_TX_FSM_RUN);
uhci_do_transmit(uhci_ctrl, t);
} else {
atomic_store(&uhci_ctrl->tx_dir.tx_fsm, UHCI_TX_FSM_ENABLE);
}
}
return ESP_OK;
}
esp_err_t uhci_del_controller(uhci_controller_handle_t uhci_ctrl)
{
ESP_RETURN_ON_FALSE(uhci_ctrl, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
if (uhci_ctrl->tx_dir.tx_fsm != UHCI_TX_FSM_ENABLE) {
ESP_LOGE(TAG, "TX transaction is not finished, delete controller failed");
return ESP_ERR_INVALID_STATE;
}
uhci_ll_enable_bus_clock(uhci_ctrl->uhci_num, false);
for (int i = 0; i < UHCI_TRANS_QUEUE_MAX; i++) {
if (uhci_ctrl->tx_dir.trans_queues[i]) {
vQueueDelete(uhci_ctrl->tx_dir.trans_queues[i]);
}
}
if (uhci_ctrl->tx_dir.trans_desc_pool) {
heap_caps_free(uhci_ctrl->tx_dir.trans_desc_pool);
}
if (uhci_ctrl->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_delete(uhci_ctrl->pm_lock), TAG, "delete rx pm_lock failed");
}
ESP_RETURN_ON_ERROR(uhci_gdma_deinitialize(uhci_ctrl), TAG, "deinitialize uhci dam channel failed");
uhci_hal_deinit(&uhci_ctrl->hal);
s_uhci_platform.controller[uhci_ctrl->uhci_num] = NULL;
heap_caps_free(uhci_ctrl);
return ESP_OK;
}
esp_err_t uhci_new_controller(const uhci_controller_config_t *config, uhci_controller_handle_t *ret_uhci_ctrl)
{
ESP_RETURN_ON_FALSE((config && ret_uhci_ctrl), ESP_ERR_INVALID_ARG, TAG, "UHCI invalid argument");
esp_err_t ret = ESP_OK;
uhci_controller_handle_t uhci_ctrl = (uhci_controller_handle_t)heap_caps_calloc(1, sizeof(uhci_controller_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(uhci_ctrl, ESP_ERR_NO_MEM, TAG, "no mem for uhci controller handle");
if (config->rx_eof_flags.length_eof) {
ESP_RETURN_ON_FALSE(config->max_packet_receive < UHCI_LL_MAX_RECEIVE_PACKET_THRESHOLD, ESP_ERR_INVALID_ARG, TAG, "max receive packet is over threshold");
}
atomic_init(&uhci_ctrl->tx_dir.tx_fsm, UHCI_TX_FSM_ENABLE);
// Auto search a free controller
bool ctrl_found = false;
_lock_acquire(&s_uhci_platform.mutex);
for (int i = 0; i < SOC_UHCI_NUM; i++) {
if (uhci_ctrl_occupied(i) == false) {
s_uhci_platform.controller[i] = uhci_ctrl;
ctrl_found = true;
uhci_ctrl->uhci_num = i;
break;
}
}
_lock_release(&s_uhci_platform.mutex);
ESP_GOTO_ON_FALSE(ctrl_found, ESP_ERR_NOT_FOUND, err, TAG, "no free controller");
for (int i = 0; i < UHCI_TRANS_QUEUE_MAX; i++) {
uhci_ctrl->tx_dir.trans_queues[i] = xQueueCreate(config->tx_trans_queue_depth, sizeof(uhci_transaction_desc_t *));
ESP_GOTO_ON_FALSE(uhci_ctrl->tx_dir.trans_queues[i], ESP_ERR_NO_MEM, err, TAG, "no mem for transaction queue");
}
uhci_ctrl->tx_dir.trans_desc_pool = heap_caps_calloc(config->tx_trans_queue_depth, sizeof(uhci_transaction_desc_t), UHCI_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(uhci_ctrl->tx_dir.trans_desc_pool, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction desc pool");
uhci_transaction_desc_t *p_trans_desc = NULL;
for (int i = 0; i < config->tx_trans_queue_depth; i++) {
p_trans_desc = &uhci_ctrl->tx_dir.trans_desc_pool[i];
xQueueSend(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_READY], &p_trans_desc, 0);
}
uhci_ll_enable_bus_clock(uhci_ctrl->uhci_num, true);
uhci_ll_reset_register(uhci_ctrl->uhci_num);
uhci_hal_init(&uhci_ctrl->hal, uhci_ctrl->uhci_num);
uhci_ll_attach_uart_port(uhci_ctrl->hal.dev, config->uart_port);
#if CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_lock_type = ESP_PM_NO_LIGHT_SLEEP;
sprintf(uhci_ctrl->pm_lock_name, "uhci_%d", uhci_ctrl->uhci_num); // e.g. uhci_0
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_lock_type, 0, uhci_ctrl->pm_lock_name, &uhci_ctrl->pm_lock), err, TAG, "create pm lock failed");
#endif
// Must set separate character as 0, otherwise uhci will lose data.
uhci_seper_chr_t seper_chr = {
.sub_chr_en = 0,
};
uhci_ll_set_seper_chr(uhci_ctrl->hal.dev, &seper_chr);
if (config->rx_eof_flags.idle_eof) {
uhci_ll_rx_set_eof_mode(uhci_ctrl->hal.dev, UHCI_RX_IDLE_EOF);
}
if (config->rx_eof_flags.length_eof) {
uhci_ll_rx_set_eof_mode(uhci_ctrl->hal.dev, UHCI_RX_LEN_EOF);
uhci_ll_rx_set_packet_threshold(uhci_ctrl->hal.dev, config->max_packet_receive);
}
if (config->rx_eof_flags.rx_brk_eof) {
uhci_ll_rx_set_eof_mode(uhci_ctrl->hal.dev, UHCI_RX_BREAK_CHR_EOF);
}
uhci_ctrl->ext_mem_cache_line_size = 1;
uhci_ctrl->int_mem_cache_line_size = 1;
ESP_GOTO_ON_ERROR(uhci_gdma_initialize(uhci_ctrl, config), err, TAG, "uhci gdma initialize failed");
*ret_uhci_ctrl = uhci_ctrl;
return ESP_OK;
err:
uhci_del_controller(uhci_ctrl);
return ret;
}
esp_err_t uhci_register_event_callbacks(uhci_controller_handle_t uhci_ctrl, const uhci_event_callbacks_t *cbs, void *user_data)
{
ESP_RETURN_ON_FALSE(uhci_ctrl && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
#if CONFIG_UHCI_ISR_CACHE_SAFE
if (cbs->on_tx_trans_done) {
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_tx_trans_done), ESP_ERR_INVALID_ARG, TAG, "on_tx_trans_done callback not in IRAM");
}
if (user_data) {
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
}
#endif
uhci_ctrl->tx_dir.on_tx_trans_done = cbs->on_tx_trans_done;
uhci_ctrl->user_data = user_data;
return ESP_OK;
}
esp_err_t uhci_wait_all_tx_transaction_done(uhci_controller_handle_t uhci_ctrl, int timeout_ms)
{
ESP_RETURN_ON_FALSE(uhci_ctrl, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
TickType_t wait_ticks = timeout_ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
uhci_transaction_desc_t *t = NULL;
size_t cnt = uhci_ctrl->tx_dir.num_trans_inflight;
for (size_t i = 0; i < cnt; i++) {
ESP_RETURN_ON_FALSE(xQueueReceive(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_COMPLETE], &t, wait_ticks) == pdTRUE,
ESP_ERR_TIMEOUT, TAG, "flush timeout");
ESP_RETURN_ON_FALSE(xQueueSend(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_READY], &t, 0) == pdTRUE,
ESP_ERR_INVALID_STATE, TAG, "ready queue full");
atomic_fetch_sub(&uhci_ctrl->tx_dir.num_trans_inflight, 1);
}
return ESP_OK;
}
#if CONFIG_UHCI_ENABLE_DEBUG_LOG
__attribute__((constructor))
static void uhci_override_default_log_level(void)
{
esp_log_level_set(TAG, ESP_LOG_VERBOSE);
}
#endif

View File

@@ -0,0 +1,105 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdatomic.h>
#include <sys/queue.h>
#include "esp_private/gdma.h"
#include "esp_private/gdma.h"
#include "hal/dma_types.h"
#include "esp_private/periph_ctrl.h"
#include "esp_pm.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct uhci_controller_t uhci_controller_t;
#define UHCI_ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
#define UHCI_MAX(a, b) (((a)>(b))?(a):(b))
#define UHCI_PM_LOCK_NAME_LEN_MAX 16
#define CONFIG_UHCI_ISR_HANDLER_IN_IRAM (0) // Should be set to 1 only if UHCI_ISR_CACHE_SAFE is 1
#define CONFIG_UHCI_ISR_CACHE_SAFE (0) // Don't forget enable GDMA_ISR_HANDLER_IN_IRAM
#define CONFIG_UHCI_ENABLE_DEBUG_LOG (0)
#if CONFIG_UHCI_ISR_HANDLER_IN_IRAM
#define UHCI_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define UHCI_MEM_ALLOC_CAPS (MALLOC_CAP_DEFAULT)
#endif
#if SOC_PERIPH_CLK_CTRL_SHARED
#define UHCI_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define UHCI_CLOCK_SRC_ATOMIC()
#endif
#if !SOC_RCC_IS_INDEPENDENT
#define UHCI_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define UHCI_RCC_ATOMIC()
#endif
#define UHCI_DMA_NODE_NUM (3)
typedef struct {
void *buffer; // buffer for saving the received symbols
size_t buffer_size; // size of the buffer, in bytes
} uhci_transaction_desc_t;
typedef enum {
UHCI_TX_FSM_ENABLE_WAIT, /**< FSM is waiting to enable the UHCI system. */
UHCI_TX_FSM_ENABLE, /**< FSM is enabling the UHCI system. */
UHCI_TX_FSM_RUN_WAIT, /**< FSM is waiting to transition to the running state. */
UHCI_TX_FSM_RUN, /**< FSM is in the running state, actively handling UHCI operations. */
} uhci_tx_fsm_t;
typedef enum {
UHCI_TRANS_QUEUE_READY, /**< The transaction queue is ready to accept new transactions. */
UHCI_TRANS_QUEUE_PROGRESS, /**< A transaction is currently in progress. */
UHCI_TRANS_QUEUE_COMPLETE, /**< All transactions in the queue are completed. */
UHCI_TRANS_QUEUE_MAX, /**< Placeholder for the maximum number of states (not a valid state). */
} uhci_trans_queue_enum_t;
typedef enum {
UHCI_RX_FSM_ENABLE_WAIT, /**< FSM is waiting to enable the UHCI system. */
UHCI_RX_FSM_ENABLE, /**< FSM is enabling the UHCI system. */
UHCI_RX_FSM_RUN_WAIT, /**< FSM is waiting to transition to the running state. */
UHCI_RX_FSM_RUN, /**< FSM is in the running state, actively handling UHCI operations. */
} uhci_rx_fsm_t;
typedef struct {
gdma_channel_handle_t dma_chan; // GDMA channel
uhci_tx_done_callback_t on_tx_trans_done; // tx transaction done callback function
uhci_transaction_desc_t *trans_desc_pool; // transaction descriptor pool
uhci_transaction_desc_t *cur_trans; // pointer to current transaction
QueueHandle_t trans_queues[UHCI_TRANS_QUEUE_MAX]; // transaction queue
_Atomic uhci_tx_fsm_t tx_fsm; // channel life cycle specific FSM
size_t int_mem_align; // Alignment for internal memory
size_t ext_mem_align; // Alignment for external memory
atomic_int num_trans_inflight; // Indicates the number of transactions that are undergoing but not recycled to ready_queue
dma_descriptor_t *dma_nodes; // DMA descriptor nodes
} uhci_tx_dir;
struct uhci_controller_t {
int uhci_num; // UHCI port number
uhci_hal_context_t hal; // uhci hal layer context
uhci_tx_dir tx_dir; // tx direction structure
void *user_data; // user data
size_t int_mem_cache_line_size; // internal memory cache line size
size_t ext_mem_cache_line_size; // external memory cache line size
esp_pm_lock_handle_t pm_lock; // power management lock
char pm_lock_name[UHCI_PM_LOCK_NAME_LEN_MAX]; // pm lock name
};
#ifdef __cplusplus
}
#endif

View File

@@ -81,6 +81,10 @@ if(NOT BOOTLOADER_BUILD)
list(APPEND srcs "mcpwm_hal.c")
endif()
if(CONFIG_SOC_UHCI_SUPPORTED)
list(APPEND srcs "uhci_hal.c")
endif()
if(CONFIG_SOC_TWAI_SUPPORTED)
list(APPEND srcs "twai_hal.c" "twai_hal_iram.c")
endif()

View File

@@ -1,16 +1,8 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// The LL layer for UHCI register operations.
// Note that most of the register operations in this layer are non-atomic operations.
@@ -20,12 +12,15 @@
#include <stdio.h>
#include "hal/uhci_types.h"
#include "soc/uhci_struct.h"
#include "soc/system_struct.h"
#include "hal/misc.h"
#ifdef __cplusplus
extern "C" {
#endif
#define UHCI_LL_GET_HW(num) (((num) == 0) ? (&UHCI0) : (NULL))
#define UHCI_LL_MAX_RECEIVE_PACKET_THRESHOLD (8192)
typedef enum {
UHCI_RX_BREAK_CHR_EOF = 0x1,
@@ -34,6 +29,19 @@ typedef enum {
UHCI_RX_EOF_MAX = 0x7,
} uhci_rxeof_cfg_t;
static inline void uhci_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
SYSTEM.perip_clk_en0.reg_uhci0_clk_en = enable;
}
static inline void uhci_ll_reset_register(int group_id)
{
(void)group_id;
SYSTEM.perip_rst_en0.reg_uhci0_rst = 1;
SYSTEM.perip_rst_en0.reg_uhci0_rst = 0;
}
static inline void uhci_ll_init(uhci_dev_t *hw)
{
typeof(hw->conf0) conf0_reg;
@@ -53,37 +61,37 @@ static inline void uhci_ll_attach_uart_port(uhci_dev_t *hw, int uart_num)
static inline void uhci_ll_set_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_char)
{
if (seper_char->sub_chr_en) {
typeof(hw->esc_conf0) esc_conf0_reg = hw->esc_conf0;
esc_conf0_reg.seper_char = seper_char->seper_chr;
esc_conf0_reg.seper_esc_char0 = seper_char->sub_chr1;
esc_conf0_reg.seper_esc_char1 = seper_char->sub_chr2;
hw->conf0.seper_en = 1;
typeof(hw->esc_conf0) esc_conf0_reg;
esc_conf0_reg.val = hw->esc_conf0.val;
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_char, seper_char->seper_chr);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char0, seper_char->sub_chr1);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char1, seper_char->sub_chr2);
hw->esc_conf0.val = esc_conf0_reg.val;
hw->escape_conf.tx_c0_esc_en = 1;
hw->escape_conf.rx_c0_esc_en = 1;
} else {
hw->escape_conf.tx_c0_esc_en = 0;
hw->escape_conf.rx_c0_esc_en = 0;
hw->conf0.seper_en = 0;
hw->escape_conf.val = 0;
}
}
static inline void uhci_ll_get_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_chr)
{
(void)hw;
(void)seper_chr;
}
static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_ctrl_sub_chr_t *sub_ctr)
{
typeof(hw->escape_conf) escape_conf_reg = hw->escape_conf;
if (sub_ctr->flow_en == 1) {
typeof(hw->esc_conf2) esc_conf2_reg = hw->esc_conf2;
typeof(hw->esc_conf3) esc_conf3_reg = hw->esc_conf3;
esc_conf2_reg.seq1 = sub_ctr->xon_chr;
esc_conf2_reg.seq1_char0 = sub_ctr->xon_sub1;
esc_conf2_reg.seq1_char1 = sub_ctr->xon_sub2;
esc_conf3_reg.seq2 = sub_ctr->xoff_chr;
esc_conf3_reg.seq2_char0 = sub_ctr->xoff_sub1;
esc_conf3_reg.seq2_char1 = sub_ctr->xoff_sub2;
typeof(hw->esc_conf2) esc_conf2_reg;
esc_conf2_reg.val = hw->esc_conf2.val;
typeof(hw->esc_conf3) esc_conf3_reg;
esc_conf3_reg.val = hw->esc_conf3.val;
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, seq1, sub_ctr->xon_chr);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, seq1_char0, sub_ctr->xon_sub1);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, seq1_char1, sub_ctr->xon_sub2);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, seq2, sub_ctr->xoff_chr);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, seq2_char0, sub_ctr->xoff_sub1);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, seq2_char1, sub_ctr->xoff_sub2);
escape_conf_reg.tx_11_esc_en = 1;
escape_conf_reg.tx_13_esc_en = 1;
escape_conf_reg.rx_11_esc_en = 1;
@@ -119,8 +127,7 @@ static inline uint32_t uhci_ll_get_intr(uhci_dev_t *hw)
return hw->int_st.val;
}
static inline void uhci_ll_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
static inline void uhci_ll_rx_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
{
if (eof_mode & UHCI_RX_BREAK_CHR_EOF) {
hw->conf0.uart_rx_brk_eof_en = 1;
@@ -133,6 +140,11 @@ static inline void uhci_ll_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
}
}
static inline void uhci_ll_rx_set_packet_threshold(uhci_dev_t *hw, uint16_t length)
{
hw->pkt_thres.thrs = length;
}
#ifdef __cplusplus
}
#endif

View File

@@ -1,19 +1,25 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// The LL layer for UHCI register operations.
// Note that most of the register operations in this layer are non-atomic operations.
#pragma once
#include <stdio.h>
#include "hal/uhci_types.h"
#include "soc/uhci_struct.h"
#include "soc/pcr_struct.h"
#include "hal/misc.h"
#ifdef __cplusplus
extern "C" {
#endif
#define UHCI_LL_GET_HW(num) (((num) == 0) ? (&UHCI0) : (NULL))
#define UHCI_LL_MAX_RECEIVE_PACKET_THRESHOLD (8192)
typedef enum {
UHCI_RX_BREAK_CHR_EOF = 0x1,
@@ -22,6 +28,30 @@ typedef enum {
UHCI_RX_EOF_MAX = 0x7,
} uhci_rxeof_cfg_t;
/**
* @brief Enable the bus clock for UHCI module
*
* @param group_id Group ID
* @param enable true to enable, false to disable
*/
static inline void uhci_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
PCR.uhci_conf.uhci_clk_en = enable;
}
/**
* @brief Reset the UHCI module
*
* @param group_id Group ID
*/
static inline void uhci_ll_reset_register(int group_id)
{
(void)group_id;
PCR.uhci_conf.uhci_rst_en = 1;
PCR.uhci_conf.uhci_rst_en = 0;
}
static inline void uhci_ll_init(uhci_dev_t *hw)
{
typeof(hw->conf0) conf0_reg;
@@ -34,34 +64,29 @@ static inline void uhci_ll_init(uhci_dev_t *hw)
static inline void uhci_ll_attach_uart_port(uhci_dev_t *hw, int uart_num)
{
hw->conf0.uart0_ce = (uart_num == 0)? 1: 0;
hw->conf0.uart1_ce = (uart_num == 1)? 1: 0;
hw->conf0.uart0_ce = (uart_num == 0) ? 1 : 0;
hw->conf0.uart1_ce = (uart_num == 1) ? 1 : 0;
}
static inline void uhci_ll_set_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_char)
{
if (seper_char->sub_chr_en) {
hw->conf0.seper_en = 1;
typeof(hw->esc_conf0) esc_conf0_reg;
esc_conf0_reg.val = hw->esc_conf0.val;
esc_conf0_reg.seper_char = seper_char->seper_chr;
esc_conf0_reg.seper_esc_char0 = seper_char->sub_chr1;
esc_conf0_reg.seper_esc_char1 = seper_char->sub_chr2;
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_char, seper_char->seper_chr);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char0, seper_char->sub_chr1);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char1, seper_char->sub_chr2);
hw->esc_conf0.val = esc_conf0_reg.val;
hw->escape_conf.tx_c0_esc_en = 1;
hw->escape_conf.rx_c0_esc_en = 1;
} else {
hw->escape_conf.tx_c0_esc_en = 0;
hw->escape_conf.rx_c0_esc_en = 0;
hw->conf0.seper_en = 0;
hw->escape_conf.val = 0;
}
}
static inline void uhci_ll_get_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_chr)
{
(void)hw;
(void)seper_chr;
}
static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_ctrl_sub_chr_t *sub_ctr)
{
typeof(hw->escape_conf) escape_conf_reg;
@@ -73,12 +98,12 @@ static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_c
typeof(hw->esc_conf3) esc_conf3_reg;
esc_conf3_reg.val = hw->esc_conf3.val;
esc_conf2_reg.esc_seq1 = sub_ctr->xon_chr;
esc_conf2_reg.esc_seq1_char0 = sub_ctr->xon_sub1;
esc_conf2_reg.esc_seq1_char1 = sub_ctr->xon_sub2;
esc_conf3_reg.esc_seq2 = sub_ctr->xoff_chr;
esc_conf3_reg.esc_seq2_char0 = sub_ctr->xoff_sub1;
esc_conf3_reg.esc_seq2_char1 = sub_ctr->xoff_sub2;
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, esc_seq1, sub_ctr->xon_chr);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, esc_seq1_char0, sub_ctr->xon_sub1);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, esc_seq1_char1, sub_ctr->xon_sub2);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, esc_seq2, sub_ctr->xoff_chr);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, esc_seq2_char0, sub_ctr->xoff_sub1);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, esc_seq2_char1, sub_ctr->xoff_sub2);
escape_conf_reg.tx_11_esc_en = 1;
escape_conf_reg.tx_13_esc_en = 1;
escape_conf_reg.rx_11_esc_en = 1;
@@ -114,8 +139,7 @@ static inline uint32_t uhci_ll_get_intr(uhci_dev_t *hw)
return hw->int_st.val;
}
static inline void uhci_ll_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
static inline void uhci_ll_rx_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
{
if (eof_mode & UHCI_RX_BREAK_CHR_EOF) {
hw->conf0.uart_rx_brk_eof_en = 1;
@@ -128,6 +152,11 @@ static inline void uhci_ll_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
}
}
static inline void uhci_ll_rx_set_packet_threshold(uhci_dev_t *hw, uint16_t length)
{
hw->pkt_thres.pkt_thrs = length;
}
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -8,12 +8,15 @@
#include <stdio.h>
#include "hal/uhci_types.h"
#include "soc/uhci_struct.h"
#include "soc/pcr_struct.h"
#include "hal/misc.h"
#ifdef __cplusplus
extern "C" {
#endif
#define UHCI_LL_GET_HW(num) (((num) == 0) ? (&UHCI0) : (NULL))
#define UHCI_LL_MAX_RECEIVE_PACKET_THRESHOLD (8192)
typedef enum {
UHCI_RX_BREAK_CHR_EOF = 0x1,
@@ -22,10 +25,33 @@ typedef enum {
UHCI_RX_EOF_MAX = 0x7,
} uhci_rxeof_cfg_t;
/**
* @brief Enable the bus clock for UHCI module
*
* @param group_id Group ID
* @param enable true to enable, false to disable
*/
static inline void uhci_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
PCR.uhci_conf.uhci_clk_en = enable;
}
/**
* @brief Reset the UHCI module
*
* @param group_id Group ID
*/
static inline void uhci_ll_reset_register(int group_id)
{
(void)group_id;
PCR.uhci_conf.uhci_rst_en = 1;
PCR.uhci_conf.uhci_rst_en = 0;
}
static inline void uhci_ll_init(uhci_dev_t *hw)
{
typeof(hw->conf0) conf0_reg;
hw->conf0.clk_en = 1;
conf0_reg.val = 0;
conf0_reg.clk_en = 1;
hw->conf0.val = conf0_reg.val;
@@ -34,34 +60,29 @@ static inline void uhci_ll_init(uhci_dev_t *hw)
static inline void uhci_ll_attach_uart_port(uhci_dev_t *hw, int uart_num)
{
hw->conf0.uart0_ce = (uart_num == 0)? 1: 0;
hw->conf0.uart1_ce = (uart_num == 1)? 1: 0;
hw->conf0.uart0_ce = (uart_num == 0) ? 1 : 0;
hw->conf0.uart1_ce = (uart_num == 1) ? 1 : 0;
}
static inline void uhci_ll_set_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_char)
{
if (seper_char->sub_chr_en) {
hw->conf0.seper_en = 1;
typeof(hw->esc_conf0) esc_conf0_reg;
esc_conf0_reg.val = hw->esc_conf0.val;
esc_conf0_reg.seper_char = seper_char->seper_chr;
esc_conf0_reg.seper_esc_char0 = seper_char->sub_chr1;
esc_conf0_reg.seper_esc_char1 = seper_char->sub_chr2;
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_char, seper_char->seper_chr);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char0, seper_char->sub_chr1);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char1, seper_char->sub_chr2);
hw->esc_conf0.val = esc_conf0_reg.val;
hw->escape_conf.tx_c0_esc_en = 1;
hw->escape_conf.rx_c0_esc_en = 1;
} else {
hw->escape_conf.tx_c0_esc_en = 0;
hw->escape_conf.rx_c0_esc_en = 0;
hw->conf0.seper_en = 0;
hw->escape_conf.val = 0;
}
}
static inline void uhci_ll_get_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_chr)
{
(void)hw;
(void)seper_chr;
}
static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_ctrl_sub_chr_t *sub_ctr)
{
typeof(hw->escape_conf) escape_conf_reg;
@@ -73,12 +94,12 @@ static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_c
typeof(hw->esc_conf3) esc_conf3_reg;
esc_conf3_reg.val = hw->esc_conf3.val;
esc_conf2_reg.esc_seq1 = sub_ctr->xon_chr;
esc_conf2_reg.esc_seq1_char0 = sub_ctr->xon_sub1;
esc_conf2_reg.esc_seq1_char1 = sub_ctr->xon_sub2;
esc_conf3_reg.esc_seq2 = sub_ctr->xoff_chr;
esc_conf3_reg.esc_seq2_char0 = sub_ctr->xoff_sub1;
esc_conf3_reg.esc_seq2_char1 = sub_ctr->xoff_sub2;
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, esc_seq1, sub_ctr->xon_chr);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, esc_seq1_char0, sub_ctr->xon_sub1);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, esc_seq1_char1, sub_ctr->xon_sub2);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, esc_seq2, sub_ctr->xoff_chr);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, esc_seq2_char0, sub_ctr->xoff_sub1);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, esc_seq2_char1, sub_ctr->xoff_sub2);
escape_conf_reg.tx_11_esc_en = 1;
escape_conf_reg.tx_13_esc_en = 1;
escape_conf_reg.rx_11_esc_en = 1;
@@ -114,8 +135,7 @@ static inline uint32_t uhci_ll_get_intr(uhci_dev_t *hw)
return hw->int_st.val;
}
static inline void uhci_ll_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
static inline void uhci_ll_rx_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
{
if (eof_mode & UHCI_RX_BREAK_CHR_EOF) {
hw->conf0.uart_rx_brk_eof_en = 1;
@@ -128,6 +148,11 @@ static inline void uhci_ll_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
}
}
static inline void uhci_ll_rx_set_packet_threshold(uhci_dev_t *hw, uint16_t length)
{
hw->pkt_thres.pkt_thrs = length;
}
#ifdef __cplusplus
}
#endif

View File

@@ -1,16 +1,8 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// The LL layer for UHCI register operations.
// Note that most of the register operations in this layer are non-atomic operations.
@@ -20,12 +12,15 @@
#include <stdio.h>
#include "hal/uhci_types.h"
#include "soc/uhci_struct.h"
#include "soc/system_struct.h"
#include "hal/misc.h"
#ifdef __cplusplus
extern "C" {
#endif
#define UHCI_LL_GET_HW(num) (((num) == 0) ? (&UHCI0) : (NULL))
#define UHCI_LL_MAX_RECEIVE_PACKET_THRESHOLD (8192)
typedef enum {
UHCI_RX_BREAK_CHR_EOF = 0x1,
@@ -34,6 +29,19 @@ typedef enum {
UHCI_RX_EOF_MAX = 0x7,
} uhci_rxeof_cfg_t;
static inline void uhci_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
SYSTEM.perip_clk_en0.uhci0_clk_en = enable;
}
static inline void uhci_ll_reset_register(int group_id)
{
(void)group_id;
SYSTEM.perip_rst_en0.uhci0_rst = 1;
SYSTEM.perip_rst_en0.uhci0_rst = 0;
}
static inline void uhci_ll_init(uhci_dev_t *hw)
{
typeof(hw->conf0) conf0_reg;
@@ -54,37 +62,37 @@ static inline void uhci_ll_attach_uart_port(uhci_dev_t *hw, int uart_num)
static inline void uhci_ll_set_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_char)
{
if (seper_char->sub_chr_en) {
typeof(hw->esc_conf0) esc_conf0_reg = hw->esc_conf0;
esc_conf0_reg.seper_char = seper_char->seper_chr;
esc_conf0_reg.seper_esc_char0 = seper_char->sub_chr1;
esc_conf0_reg.seper_esc_char1 = seper_char->sub_chr2;
hw->conf0.seper_en = 1;
typeof(hw->esc_conf0) esc_conf0_reg;
esc_conf0_reg.val = hw->esc_conf0.val;
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_char, seper_char->seper_chr);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char0, seper_char->sub_chr1);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char1, seper_char->sub_chr2);
hw->esc_conf0.val = esc_conf0_reg.val;
hw->escape_conf.tx_c0_esc_en = 1;
hw->escape_conf.rx_c0_esc_en = 1;
} else {
hw->escape_conf.tx_c0_esc_en = 0;
hw->escape_conf.rx_c0_esc_en = 0;
hw->conf0.seper_en = 0;
hw->escape_conf.val = 0;
}
}
static inline void uhci_ll_get_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_chr)
{
(void)hw;
(void)seper_chr;
}
static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_ctrl_sub_chr_t *sub_ctr)
{
typeof(hw->escape_conf) escape_conf_reg = hw->escape_conf;
if (sub_ctr->flow_en == 1) {
typeof(hw->esc_conf2) esc_conf2_reg = hw->esc_conf2;
typeof(hw->esc_conf3) esc_conf3_reg = hw->esc_conf3;
esc_conf2_reg.seq1 = sub_ctr->xon_chr;
esc_conf2_reg.seq1_char0 = sub_ctr->xon_sub1;
esc_conf2_reg.seq1_char1 = sub_ctr->xon_sub2;
esc_conf3_reg.seq2 = sub_ctr->xoff_chr;
esc_conf3_reg.seq2_char0 = sub_ctr->xoff_sub1;
esc_conf3_reg.seq2_char1 = sub_ctr->xoff_sub2;
typeof(hw->esc_conf2) esc_conf2_reg;
esc_conf2_reg.val = hw->esc_conf2.val;
typeof(hw->esc_conf3) esc_conf3_reg;
esc_conf3_reg.val = hw->esc_conf3.val;
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, seq1, sub_ctr->xon_chr);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, seq1_char0, sub_ctr->xon_sub1);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, seq1_char1, sub_ctr->xon_sub2);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, seq2, sub_ctr->xoff_chr);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, seq2_char0, sub_ctr->xoff_sub1);
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, seq2_char1, sub_ctr->xoff_sub2);
escape_conf_reg.tx_11_esc_en = 1;
escape_conf_reg.tx_13_esc_en = 1;
escape_conf_reg.rx_11_esc_en = 1;
@@ -120,8 +128,7 @@ static inline uint32_t uhci_ll_get_intr(uhci_dev_t *hw)
return hw->int_st.val;
}
static inline void uhci_ll_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
static inline void uhci_ll_rx_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
{
if (eof_mode & UHCI_RX_BREAK_CHR_EOF) {
hw->conf0.uart_rx_brk_eof_en = 1;
@@ -134,6 +141,11 @@ static inline void uhci_ll_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
}
}
static inline void uhci_ll_rx_set_packet_threshold(uhci_dev_t *hw, uint16_t length)
{
hw->pkt_thres.thrs = length;
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,59 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use in application code.
* See readme.md in hal/readme.md
******************************************************************************/
// The HAL layer for UHCI.
// There is no parameter check in the hal layer, so the caller must ensure the correctness of the parameters.
#pragma once
#include "hal/uhci_types.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_UHCI_SUPPORTED
typedef struct uhci_dev_t *uhci_soc_handle_t; // UHCI SOC layer handle
/**
* Context that should be maintained by both the driver and the HAL
*/
typedef struct {
uhci_soc_handle_t dev;
} uhci_hal_context_t;
/**
* @brief Init UHCI hardware instance.
*
* @param hal Context of the HAL layer
* @param uhci_num UHCI number
*
* @return None
*/
void uhci_hal_init(uhci_hal_context_t *hal, int uhci_num);
/**
* @brief Deinit UHCI hardware instance.
*
* @param hal Context of the HAL layer
* @param uhci_num UHCI number
*
* @return None
*/
void uhci_hal_deinit(uhci_hal_context_t *hal);
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -1,21 +1,8 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Though the UHCI driver hasn't been published, some types are defined here
// for users to develop over the HAL. See example: controller_hci_uart_esp32c3
/*
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

20
components/hal/uhci_hal.c Normal file
View File

@@ -0,0 +1,20 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// The HAL layer for UHCI (common part)
#include "hal/uhci_hal.h"
#include "hal/uhci_ll.h"
void uhci_hal_init(uhci_hal_context_t *hal, int uhci_num)
{
hal->dev = UHCI_LL_GET_HW(uhci_num);
uhci_ll_init(hal->dev);
}
void uhci_hal_deinit(uhci_hal_context_t *hal)
{
hal->dev = NULL;
}

View File

@@ -19,6 +19,10 @@ config SOC_GDMA_SUPPORTED
bool
default y
config SOC_UHCI_SUPPORTED
bool
default y
config SOC_GPTIMER_SUPPORTED
bool
default y
@@ -879,6 +883,10 @@ config SOC_UART_SUPPORT_FSM_TX_WAIT_SEND
bool
default y
config SOC_UHCI_NUM
int
default 1
config SOC_COEX_HW_PTI
bool
default y

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -29,6 +29,7 @@
#define SOC_DEDICATED_GPIO_SUPPORTED 1
#define SOC_UART_SUPPORTED 1
#define SOC_GDMA_SUPPORTED 1
#define SOC_UHCI_SUPPORTED 1
#define SOC_GPTIMER_SUPPORTED 1
#define SOC_TWAI_SUPPORTED 1
#define SOC_BT_SUPPORTED 1
@@ -387,6 +388,9 @@
// UART has an extra TX_WAIT_SEND state when the FIFO is not empty and XOFF is enabled
#define SOC_UART_SUPPORT_FSM_TX_WAIT_SEND (1)
/*--------------------------- UHCI CAPS -------------------------------------*/
#define SOC_UHCI_NUM (1UL)
/*-------------------------- COEXISTENCE HARDWARE PTI CAPS -------------------------------*/
#define SOC_COEX_HW_PTI (1)

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -10,8 +10,8 @@
extern "C" {
#endif
typedef volatile struct uhci_dev_s {
union {
typedef struct uhci_dev_t {
volatile union {
struct {
uint32_t tx_rst: 1; /*Write 1 then write 0 to this bit to reset decode state machine.*/
uint32_t rx_rst: 1; /*Write 1 then write 0 to this bit to reset encode state machine.*/
@@ -30,7 +30,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} conf0;
union {
volatile union {
struct {
uint32_t rx_start: 1; /*a*/
uint32_t tx_start: 1; /*a*/
@@ -45,7 +45,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} int_raw;
union {
volatile union {
struct {
uint32_t rx_start: 1; /*a*/
uint32_t tx_start: 1; /*a*/
@@ -60,7 +60,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} int_st;
union {
volatile union {
struct {
uint32_t rx_start: 1; /*a*/
uint32_t tx_start: 1; /*a*/
@@ -75,7 +75,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} int_ena;
union {
volatile union {
struct {
uint32_t rx_start: 1; /*a*/
uint32_t tx_start: 1; /*a*/
@@ -90,7 +90,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} int_clr;
union {
volatile union {
struct {
uint32_t check_sum_en: 1; /*a*/
uint32_t check_seq_en: 1; /*a*/
@@ -106,7 +106,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} conf1;
union {
volatile union {
struct {
uint32_t rx_err_cause: 3; /*a*/
uint32_t decode_state: 3; /*a*/
@@ -114,14 +114,14 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} state0;
union {
volatile union {
struct {
uint32_t encode_state: 3; /*a*/
uint32_t reserved3: 29;
};
uint32_t val;
} state1;
union {
volatile union {
struct {
uint32_t tx_c0_esc_en: 1; /*a*/
uint32_t tx_db_esc_en: 1; /*a*/
@@ -135,7 +135,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} escape_conf;
union {
volatile union {
struct {
uint32_t txfifo_timeout: 8; /*a*/
uint32_t txfifo_timeout_shift: 3; /*a*/
@@ -147,7 +147,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} hung_conf;
union {
volatile union {
struct {
uint32_t ack_num: 3;
uint32_t ack_num_load: 1; /*a*/
@@ -156,7 +156,7 @@ typedef volatile struct uhci_dev_s {
uint32_t val;
} ack_num;
uint32_t rx_head; /*a*/
union {
volatile union {
struct {
uint32_t single_send_num: 3; /*a*/
uint32_t single_send_en: 1; /*a*/
@@ -166,10 +166,10 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} quick_sent;
struct {
volatile struct {
uint32_t w_data[2]; /*a*/
} q_data[7];
union {
volatile union {
struct {
uint32_t seper_char: 8; /*a*/
uint32_t seper_esc_char0: 8; /*a*/
@@ -178,7 +178,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} esc_conf0;
union {
volatile union {
struct {
uint32_t seq0: 8; /*a*/
uint32_t seq0_char0: 8; /*a*/
@@ -187,7 +187,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} esc_conf1;
union {
volatile union {
struct {
uint32_t seq1: 8; /*a*/
uint32_t seq1_char0: 8; /*a*/
@@ -196,7 +196,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} esc_conf2;
union {
volatile union {
struct {
uint32_t seq2: 8; /*a*/
uint32_t seq2_char0: 8; /*a*/
@@ -205,14 +205,14 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} esc_conf3;
union {
volatile union {
struct {
uint32_t thrs: 13; /*a*/
uint32_t reserved13:19;
};
uint32_t val;
} pkt_thres;
uint32_t date; /*a*/
volatile uint32_t date; /*a*/
} uhci_dev_t;
extern uhci_dev_t UHCI0;
extern uhci_dev_t UHCI1;

View File

@@ -15,6 +15,10 @@ config SOC_UART_SUPPORTED
bool
default y
config SOC_UHCI_SUPPORTED
bool
default y
config SOC_GDMA_SUPPORTED
bool
default y
@@ -1087,6 +1091,10 @@ config SOC_UART_SUPPORT_FSM_TX_WAIT_SEND
bool
default y
config SOC_UHCI_NUM
int
default 1
config SOC_COEX_HW_PTI
bool
default y

View File

@@ -28,6 +28,7 @@
#define SOC_ADC_SUPPORTED 1
#define SOC_DEDICATED_GPIO_SUPPORTED 1
#define SOC_UART_SUPPORTED 1
#define SOC_UHCI_SUPPORTED 1
#define SOC_GDMA_SUPPORTED 1
#define SOC_GPTIMER_SUPPORTED 1
#define SOC_PCNT_SUPPORTED 1
@@ -452,6 +453,9 @@
// UART has an extra TX_WAIT_SEND state when the FIFO is not empty and XOFF is enabled
#define SOC_UART_SUPPORT_FSM_TX_WAIT_SEND (1)
/*--------------------------- UHCI CAPS -------------------------------------*/
#define SOC_UHCI_NUM (1UL)
// TODO: IDF-5679 (Copy from esp32c3, need check)
/*-------------------------- COEXISTENCE HARDWARE PTI CAPS -------------------------------*/
#define SOC_COEX_HW_PTI (1)

View File

@@ -618,7 +618,7 @@ typedef union {
} uhci_date_reg_t;
typedef struct uhci_dev_s {
typedef struct uhci_dev_t {
volatile uhci_conf0_reg_t conf0;
volatile uhci_int_raw_reg_t int_raw;
volatile uhci_int_st_reg_t int_st;

View File

@@ -23,6 +23,10 @@ config SOC_UART_SUPPORTED
bool
default y
config SOC_UHCI_SUPPORTED
bool
default y
config SOC_GDMA_SUPPORTED
bool
default y
@@ -1107,6 +1111,14 @@ config SOC_UART_SUPPORT_FSM_TX_WAIT_SEND
bool
default y
config SOC_UART_SUPPORT_SLEEP_RETENTION
bool
default y
config SOC_UHCI_NUM
int
default 1
config SOC_COEX_HW_PTI
bool
default y

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -46,6 +46,7 @@
#define SOC_ANA_CMPR_SUPPORTED 1
#define SOC_DEDICATED_GPIO_SUPPORTED 1
#define SOC_UART_SUPPORTED 1
#define SOC_UHCI_SUPPORTED 1
#define SOC_GDMA_SUPPORTED 1
#define SOC_ASYNC_MEMCPY_SUPPORTED 1
#define SOC_PCNT_SUPPORTED 1
@@ -477,6 +478,11 @@
// UART has an extra TX_WAIT_SEND state when the FIFO is not empty and XOFF is enabled
#define SOC_UART_SUPPORT_FSM_TX_WAIT_SEND (1)
#define SOC_UART_SUPPORT_SLEEP_RETENTION (1) /*!< Support back up registers before sleep */
/*--------------------------- UHCI CAPS -------------------------------------*/
#define SOC_UHCI_NUM (1UL)
// TODO: IDF-5679 (Copy from esp32c6, need check)
/*-------------------------- COEXISTENCE HARDWARE PTI CAPS -------------------------------*/
#define SOC_COEX_HW_PTI (1)

View File

@@ -618,7 +618,7 @@ typedef union {
} uhci_date_reg_t;
typedef struct uhci_dev_s {
typedef struct uhci_dev_t {
volatile uhci_conf0_reg_t conf0;
volatile uhci_int_raw_reg_t int_raw;
volatile uhci_int_st_reg_t int_st;

View File

@@ -47,6 +47,10 @@ config SOC_GDMA_SUPPORTED
bool
default y
config SOC_UHCI_SUPPORTED
bool
default y
config SOC_GPTIMER_SUPPORTED
bool
default y
@@ -935,6 +939,10 @@ config SOC_UART_REQUIRE_CORE_RESET
bool
default y
config SOC_UHCI_NUM
int
default 1
config SOC_USB_OTG_PERIPH_NUM
int
default 1

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -31,6 +31,7 @@
#define SOC_WIFI_SUPPORTED 1
#define SOC_TWAI_SUPPORTED 1
#define SOC_GDMA_SUPPORTED 1
#define SOC_UHCI_SUPPORTED 1
#define SOC_GPTIMER_SUPPORTED 1
#define SOC_LCDCAM_SUPPORTED 1
#define SOC_MCPWM_SUPPORTED 1
@@ -374,6 +375,9 @@
#define SOC_UART_SUPPORT_XTAL_CLK (1) /*!< Support XTAL clock as the clock source */
#define SOC_UART_REQUIRE_CORE_RESET (1)
/*--------------------------- UHCI CAPS -------------------------------------*/
#define SOC_UHCI_NUM (1UL)
/*-------------------------- USB CAPS ----------------------------------------*/
#define SOC_USB_OTG_PERIPH_NUM (1U)

View File

@@ -1,16 +1,8 @@
// Copyright 2017-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SOC_UHCI_STRUCT_H_
#define _SOC_UHCI_STRUCT_H_
@@ -20,8 +12,8 @@
extern "C" {
#endif
typedef volatile struct uhci_dev_s {
union {
typedef struct uhci_dev_t {
volatile union {
struct {
uint32_t tx_rst : 1;
uint32_t rx_rst : 1;
@@ -40,7 +32,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} conf0;
union {
volatile union {
struct {
uint32_t rx_start : 1;
uint32_t tx_start : 1;
@@ -55,7 +47,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} int_raw;
union {
volatile union {
struct {
uint32_t rx_start : 1;
uint32_t tx_start : 1;
@@ -70,7 +62,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} int_st;
union {
volatile union {
struct {
uint32_t rx_start : 1;
uint32_t tx_start : 1;
@@ -85,7 +77,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} int_ena;
union {
volatile union {
struct {
uint32_t rx_start : 1;
uint32_t tx_start : 1;
@@ -100,7 +92,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} int_clr;
union {
volatile union {
struct {
uint32_t app_ctrl0_int_set : 1;
uint32_t app_ctrl1_int_set : 1;
@@ -108,7 +100,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} app_int_set;
union {
volatile union {
struct {
uint32_t check_sum_en : 1;
uint32_t check_seq_en : 1;
@@ -124,7 +116,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} conf1;
union {
volatile union {
struct {
uint32_t rx_err_cause : 3;
uint32_t decode_state : 3;
@@ -132,14 +124,14 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} state0;
union {
volatile union {
struct {
uint32_t encode_state : 3;
uint32_t reserved3 : 29;
};
uint32_t val;
} state1;
union {
volatile union {
struct {
uint32_t tx_c0_esc_en : 1;
uint32_t tx_db_esc_en : 1;
@@ -153,7 +145,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} escape_conf;
union {
volatile union {
struct {
uint32_t txfifo_timeout : 8;
uint32_t txfifo_timeout_shift : 3;
@@ -165,7 +157,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} hung_conf;
union {
volatile union {
struct {
uint32_t ack_num : 3;
uint32_t ack_num_load : 1;
@@ -174,7 +166,7 @@ typedef volatile struct uhci_dev_s {
uint32_t val;
} ack_num;
uint32_t rx_head;
union {
volatile union {
struct {
uint32_t single_send_num : 3;
uint32_t single_send_en : 1;
@@ -184,10 +176,10 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} quick_sent;
struct {
volatile struct {
uint32_t word[2];
} q_data[7];
union {
volatile union {
struct {
uint32_t seper_char : 8;
uint32_t seper_esc_char0 : 8;
@@ -196,7 +188,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} esc_conf0;
union {
volatile union {
struct {
uint32_t seq0 : 8;
uint32_t seq0_char0 : 8;
@@ -205,7 +197,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} esc_conf1;
union {
volatile union {
struct {
uint32_t seq1 : 8;
uint32_t seq1_char0 : 8;
@@ -214,7 +206,7 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} esc_conf2;
union {
volatile union {
struct {
uint32_t seq2 : 8;
uint32_t seq2_char0 : 8;
@@ -223,14 +215,14 @@ typedef volatile struct uhci_dev_s {
};
uint32_t val;
} esc_conf3;
union {
volatile union {
struct {
uint32_t thrs : 13;
uint32_t reserved13 : 19;
};
uint32_t val;
} pkt_thres;
uint32_t date;
volatile uint32_t date;
} uhci_dev_t;
extern uhci_dev_t UHCI0;
extern uhci_dev_t UHCI1;

View File

@@ -51,7 +51,7 @@ struct uart_env_tag {
struct uart_env_tag uart_env;
static volatile uhci_dev_t *s_uhci_hw = &UHCI0;
static uhci_dev_t *s_uhci_hw = &UHCI0;
static gdma_channel_handle_t s_rx_channel;
static gdma_channel_handle_t s_tx_channel;
@@ -247,7 +247,7 @@ void uhci_uart_install(void)
// configure UHCI
uhci_ll_init(s_uhci_hw);
uhci_ll_set_eof_mode(s_uhci_hw, UHCI_RX_LEN_EOF);
uhci_ll_rx_set_eof_mode(s_uhci_hw, UHCI_RX_LEN_EOF);
// disable software flow control
s_uhci_hw->escape_conf.val = 0;
uhci_ll_attach_uart_port(s_uhci_hw, 1);

View File

@@ -597,7 +597,6 @@ components/hal/esp32c3/include/hal/hmac_hal.h
components/hal/esp32c3/include/hal/hmac_ll.h
components/hal/esp32c3/include/hal/mpu_ll.h
components/hal/esp32c3/include/hal/sha_ll.h
components/hal/esp32c3/include/hal/uhci_ll.h
components/hal/esp32c3/rtc_cntl_hal.c
components/hal/esp32s2/include/hal/crypto_dma_ll.h
components/hal/esp32s2/include/hal/dedic_gpio_ll.h
@@ -607,7 +606,6 @@ components/hal/esp32s2/include/hal/trace_ll.h
components/hal/esp32s2/include/hal/usb_ll.h
components/hal/esp32s3/include/hal/mpu_ll.h
components/hal/esp32s3/include/hal/sha_ll.h
components/hal/esp32s3/include/hal/uhci_ll.h
components/hal/esp32s3/include/hal/usb_ll.h
components/hal/include/hal/aes_types.h
components/hal/include/hal/dac_types.h
@@ -618,7 +616,6 @@ components/hal/include/hal/mpu_types.h
components/hal/include/hal/rtc_io_types.h
components/hal/include/hal/sdio_slave_ll.h
components/hal/include/hal/sha_hal.h
components/hal/include/hal/uhci_types.h
components/hal/include/hal/usb_hal.h
components/hal/include/hal/usb_types_private.h
components/hal/mpu_hal.c
@@ -930,7 +927,6 @@ components/soc/esp32s3/include/soc/uart_pins.h
components/soc/esp32s3/include/soc/uart_reg.h
components/soc/esp32s3/include/soc/uart_struct.h
components/soc/esp32s3/include/soc/uhci_reg.h
components/soc/esp32s3/include/soc/uhci_struct.h
components/soc/esp32s3/include/soc/usb_serial_jtag_struct.h
components/soc/esp32s3/include/soc/usb_wrap_reg.h
components/soc/esp32s3/include/soc/usb_wrap_struct.h