From 7c56253e3301dcf4058a8a0ad98dd0582eb44c36 Mon Sep 17 00:00:00 2001 From: Hu Rui Date: Tue, 31 Mar 2026 10:55:22 +0800 Subject: [PATCH] feat(uhci): Add support for uhci on esp32h4 --- .../esp_driver_uart/include/driver/uhci.h | 2 +- .../esp_driver_uart/test_apps/uhci/README.md | 4 +- .../esp32h4/include/hal/uhci_ll.h | 220 ++++++++++++++++++ .../esp_hal_uart/include/hal/uhci_types.h | 2 +- .../esp32h4/include/soc/Kconfig.soc_caps.in | 4 + components/soc/esp32h4/include/soc/soc_caps.h | 1 + .../soc/esp32h4/register/soc/uhci_struct.h | 202 ++-------------- .../peripherals/uart/uart_dma_ota/README.md | 4 +- 8 files changed, 249 insertions(+), 190 deletions(-) create mode 100644 components/esp_hal_uart/esp32h4/include/hal/uhci_ll.h diff --git a/components/esp_driver_uart/include/driver/uhci.h b/components/esp_driver_uart/include/driver/uhci.h index fb7be8238e..7a66a1cdfe 100644 --- a/components/esp_driver_uart/include/driver/uhci.h +++ b/components/esp_driver_uart/include/driver/uhci.h @@ -72,7 +72,7 @@ esp_err_t uhci_new_controller(const uhci_controller_config_t *config, uhci_contr * The buffer must be pre-allocated by the caller. * @param[in] buffer_size The size of read buffer. * - * @note @note The function is non-blocking, it just mounts the user buffer to the DMA. + * @note The function is non-blocking, it just mounts the user buffer to the DMA. * The return from the function doesn't mean a finished receive. You need to register corresponding * callback function to get notification. * diff --git a/components/esp_driver_uart/test_apps/uhci/README.md b/components/esp_driver_uart/test_apps/uhci/README.md index 92cda2b7a7..a95026d7f3 100644 --- a/components/esp_driver_uart/test_apps/uhci/README.md +++ b/components/esp_driver_uart/test_apps/uhci/README.md @@ -1,2 +1,2 @@ -| Supported Targets | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-P4 | ESP32-S3 | -| ----------------- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | +| Supported Targets | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | diff --git a/components/esp_hal_uart/esp32h4/include/hal/uhci_ll.h b/components/esp_hal_uart/esp32h4/include/hal/uhci_ll.h new file mode 100644 index 0000000000..6319660692 --- /dev/null +++ b/components/esp_hal_uart/esp32h4/include/hal/uhci_ll.h @@ -0,0 +1,220 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include +#include "hal/uhci_types.h" +#include "soc/uhci_struct.h" +#include "soc/pcr_struct.h" +#include "hal/misc.h" + +#define UHCI_LL_NUM (1UL) +#define UHCI_LL_GET_HW(num) (((num) == 0) ? (&UHCI0) : (NULL)) +#define UHCI_LL_MAX_RECEIVE_PACKET_THRESHOLD (8192) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + UHCI_RX_BREAK_CHR_EOF = 0x1, + UHCI_RX_IDLE_EOF = 0x2, + UHCI_RX_LEN_EOF = 0x4, + 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; +} + +/** + * @brief Initialize the UHCI module. Reset configuration registers and keep the clock enabled + * + * @param hw Pointer to the UHCI device + */ +static inline void uhci_ll_init(uhci_dev_t *hw) +{ + uhci_conf0_reg_t conf0_reg; + conf0_reg.val = 0; + conf0_reg.clk_en = 1; + hw->conf0.val = conf0_reg.val; + hw->conf1.val = 0; +} + +/** + * @brief Attach the UHCI module to a UART port + * + * @param hw Pointer to the UHCI device + * @param uart_num The UART port number to attach the UHCI module to + */ +static inline void uhci_ll_attach_uart_port(uhci_dev_t *hw, int uart_num) +{ + hw->conf0.uart_sel = uart_num; +} + +/** + * @brief UHCI escape sequence configuration + * + * @param hw Pointer to the UHCI device + * @param seper_char UHCI escape sequence configuration + */ +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; + uhci_esc_conf0_reg_t 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->conf0.seper_en = 0; + hw->escape_conf.val = 0; + } +} + +/** + * @brief UHCI software flow control configuration + * + * @param hw Pointer to the UHCI device + * @param sub_ctr UHCI software flow control configuration + */ +static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_ctrl_sub_chr_t *sub_ctr) +{ + uhci_escape_conf_reg_t escape_conf_reg; + escape_conf_reg.val = hw->escape_conf.val; + + if (sub_ctr->flow_en == 1) { + uhci_esc_conf2_reg_t esc_conf2_reg; + esc_conf2_reg.val = hw->esc_conf2.val; + uhci_esc_conf3_reg_t esc_conf3_reg; + esc_conf3_reg.val = hw->esc_conf3.val; + + 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; + escape_conf_reg.rx_13_esc_en = 1; + hw->esc_conf2.val = esc_conf2_reg.val; + hw->esc_conf3.val = esc_conf3_reg.val; + } else { + escape_conf_reg.tx_11_esc_en = 0; + escape_conf_reg.tx_13_esc_en = 0; + escape_conf_reg.rx_11_esc_en = 0; + escape_conf_reg.rx_13_esc_en = 0; + } + hw->escape_conf.val = escape_conf_reg.val; +} + +/** + * @brief Enable UHCI interrupt + * + * @param hw Pointer to the UHCI device + * @param intr_mask The interrupt mask to enable + */ +static inline void uhci_ll_enable_intr(uhci_dev_t *hw, uint32_t intr_mask) +{ + hw->int_ena.val |= intr_mask; +} + +/** + * @brief Disable UHCI interrupt + * + * @param hw Pointer to the UHCI device + * @param intr_mask The interrupt mask to disable + */ +static inline void uhci_ll_disable_intr(uhci_dev_t *hw, uint32_t intr_mask) +{ + hw->int_ena.val &= (~intr_mask); +} + +/** + * @brief Clear UHCI interrupt + * + * @param hw Pointer to the UHCI device + * @param intr_mask The interrupt mask to clear + */ +static inline void uhci_ll_clear_intr(uhci_dev_t *hw, uint32_t intr_mask) +{ + hw->int_clr.val = intr_mask; +} + +/** + * @brief Get UHCI interrupt masked status + * + * @param hw Pointer to the UHCI device + * @return The interrupt masked status + */ +static inline uint32_t uhci_ll_get_intr(uhci_dev_t *hw) +{ + return hw->int_st.val; +} + +/** + * @brief Set the EOF mode for payload receive + * + * @param hw Pointer to the UHCI device + * @param eof_mode The EOF mode to set. The following modes are supported: + * - UHCI_RX_BREAK_CHR_EOF: UHCI will end payload receive process when NULL frame is received by UART. + * - UHCI_RX_IDLE_EOF: UHCI will end payload receive process when UART has been in idle state. + * - UHCI_RX_LEN_EOF: UHCI will end payload receive process when the receiving byte count has reached the specific value. + */ +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; + } + if (eof_mode & UHCI_RX_IDLE_EOF) { + hw->conf0.uart_idle_eof_en = 1; + } + if (eof_mode & UHCI_RX_LEN_EOF) { + hw->conf0.len_eof_en = 1; + } +} + +/** + * @brief Set the packet threshold (maximum value of the packet length) + * + * @param hw Pointer to the UHCI device + * @param length Measurement unit: byte. + */ +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 diff --git a/components/esp_hal_uart/include/hal/uhci_types.h b/components/esp_hal_uart/include/hal/uhci_types.h index 0314151a20..fe2a1ba64a 100644 --- a/components/esp_hal_uart/include/hal/uhci_types.h +++ b/components/esp_hal_uart/include/hal/uhci_types.h @@ -20,7 +20,7 @@ typedef struct { uint8_t seper_chr; /*!< escape sequence character */ uint8_t sub_chr1; /*!< escape sequence sub-character 1 */ uint8_t sub_chr2; /*!< escape sequence sub-character 2 */ - bool sub_chr_en; /*!< enable use of sub-chaacter of escape sequence */ + bool sub_chr_en; /*!< enable use of sub-character of escape sequence */ } uhci_seper_chr_t; /** diff --git a/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in index 17d21fbebb..0f25ed1124 100644 --- a/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in @@ -11,6 +11,10 @@ config SOC_UART_SUPPORTED bool default y +config SOC_UHCI_SUPPORTED + bool + default y + config SOC_GDMA_SUPPORTED bool default y diff --git a/components/soc/esp32h4/include/soc/soc_caps.h b/components/soc/esp32h4/include/soc/soc_caps.h index 3e744fac91..017a8332ef 100644 --- a/components/soc/esp32h4/include/soc/soc_caps.h +++ b/components/soc/esp32h4/include/soc/soc_caps.h @@ -35,6 +35,7 @@ // #define SOC_ANA_CMPR_SUPPORTED 1 // TODO: [ESP32H4] IDF-12395 big change!! #define SOC_DEDICATED_GPIO_SUPPORTED 1 #define SOC_UART_SUPPORTED 1 +#define SOC_UHCI_SUPPORTED 1 #define SOC_GDMA_SUPPORTED 1 #define SOC_AHB_GDMA_SUPPORTED 1 #define SOC_GPTIMER_SUPPORTED 1 diff --git a/components/soc/esp32h4/register/soc/uhci_struct.h b/components/soc/esp32h4/register/soc/uhci_struct.h index c1541373a8..96dcb9bdac 100644 --- a/components/soc/esp32h4/register/soc/uhci_struct.h +++ b/components/soc/esp32h4/register/soc/uhci_struct.h @@ -1,5 +1,5 @@ /** - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -315,187 +315,31 @@ typedef union { uint32_t val; } uhci_quick_sent_reg_t; -/** Type of reg_q0_word0 register - * Q0 WORD0 quick send register +/** Type of reg_qn_word0 register + * a */ typedef union { struct { - /** send_q0_word0 : R/W; bitpos: [31:0]; default: 0; - * Data to be transmitted in Q0 register. + /** send_word0 : R/W; bitpos: [31:0]; default: 0; + * a */ - uint32_t send_q0_word0:32; + uint32_t send_word0:32; }; uint32_t val; -} uhci_reg_q0_word0_reg_t; +} uhci_reg_qn_word0_reg_t; -/** Type of reg_q0_word1 register - * Q0 WORD1 quick send register +/** Type of reg_qn_word1 register + * a */ typedef union { struct { - /** send_q0_word1 : R/W; bitpos: [31:0]; default: 0; - * Data to be transmitted in Q0 register. + /** send_word1 : R/W; bitpos: [31:0]; default: 0; + * a */ - uint32_t send_q0_word1:32; + uint32_t send_word1:32; }; uint32_t val; -} uhci_reg_q0_word1_reg_t; - -/** Type of reg_q1_word0 register - * Q1 WORD0 quick send register - */ -typedef union { - struct { - /** send_q1_word0 : R/W; bitpos: [31:0]; default: 0; - * Data to be transmitted in Q1 register. - */ - uint32_t send_q1_word0:32; - }; - uint32_t val; -} uhci_reg_q1_word0_reg_t; - -/** Type of reg_q1_word1 register - * Q1 WORD1 quick send register - */ -typedef union { - struct { - /** send_q1_word1 : R/W; bitpos: [31:0]; default: 0; - * Data to be transmitted in Q1 register. - */ - uint32_t send_q1_word1:32; - }; - uint32_t val; -} uhci_reg_q1_word1_reg_t; - -/** Type of reg_q2_word0 register - * Q2 WORD0 quick send register - */ -typedef union { - struct { - /** send_q2_word0 : R/W; bitpos: [31:0]; default: 0; - * Data to be transmitted in Q2 register. - */ - uint32_t send_q2_word0:32; - }; - uint32_t val; -} uhci_reg_q2_word0_reg_t; - -/** Type of reg_q2_word1 register - * Q2 WORD1 quick send register - */ -typedef union { - struct { - /** send_q2_word1 : R/W; bitpos: [31:0]; default: 0; - * Data to be transmitted in Q2 register. - */ - uint32_t send_q2_word1:32; - }; - uint32_t val; -} uhci_reg_q2_word1_reg_t; - -/** Type of reg_q3_word0 register - * Q3 WORD0 quick send register - */ -typedef union { - struct { - /** send_q3_word0 : R/W; bitpos: [31:0]; default: 0; - * Data to be transmitted in Q3 register. - */ - uint32_t send_q3_word0:32; - }; - uint32_t val; -} uhci_reg_q3_word0_reg_t; - -/** Type of reg_q3_word1 register - * Q3 WORD1 quick send register - */ -typedef union { - struct { - /** send_q3_word1 : R/W; bitpos: [31:0]; default: 0; - * Data to be transmitted in Q3 register. - */ - uint32_t send_q3_word1:32; - }; - uint32_t val; -} uhci_reg_q3_word1_reg_t; - -/** Type of reg_q4_word0 register - * Q4 WORD0 quick send register - */ -typedef union { - struct { - /** send_q4_word0 : R/W; bitpos: [31:0]; default: 0; - * Data to be transmitted in Q4 register. - */ - uint32_t send_q4_word0:32; - }; - uint32_t val; -} uhci_reg_q4_word0_reg_t; - -/** Type of reg_q4_word1 register - * Q4 WORD1 quick send register - */ -typedef union { - struct { - /** send_q4_word1 : R/W; bitpos: [31:0]; default: 0; - * Data to be transmitted in Q4 register. - */ - uint32_t send_q4_word1:32; - }; - uint32_t val; -} uhci_reg_q4_word1_reg_t; - -/** Type of reg_q5_word0 register - * Q5 WORD0 quick send register - */ -typedef union { - struct { - /** send_q5_word0 : R/W; bitpos: [31:0]; default: 0; - * Data to be transmitted in Q5 register. - */ - uint32_t send_q5_word0:32; - }; - uint32_t val; -} uhci_reg_q5_word0_reg_t; - -/** Type of reg_q5_word1 register - * Q5 WORD1 quick send register - */ -typedef union { - struct { - /** send_q5_word1 : R/W; bitpos: [31:0]; default: 0; - * Data to be transmitted in Q5 register. - */ - uint32_t send_q5_word1:32; - }; - uint32_t val; -} uhci_reg_q5_word1_reg_t; - -/** Type of reg_q6_word0 register - * Q6 WORD0 quick send register - */ -typedef union { - struct { - /** send_q6_word0 : R/W; bitpos: [31:0]; default: 0; - * Data to be transmitted in Q6 register. - */ - uint32_t send_q6_word0:32; - }; - uint32_t val; -} uhci_reg_q6_word0_reg_t; - -/** Type of reg_q6_word1 register - * Q6 WORD1 quick register - */ -typedef union { - struct { - /** send_q6_word1 : R/W; bitpos: [31:0]; default: 0; - * Data to be transmitted in Q6 register. - */ - uint32_t send_q6_word1:32; - }; - uint32_t val; -} uhci_reg_q6_word1_reg_t; +} uhci_reg_qn_word1_reg_t; /** Type of esc_conf0 register * Escape sequence configuration register 0 @@ -861,7 +705,7 @@ typedef union { } uhci_date_reg_t; -typedef struct { +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; @@ -875,20 +719,10 @@ typedef struct { volatile uhci_ack_num_reg_t ack_num; volatile uhci_rx_head_reg_t rx_head; volatile uhci_quick_sent_reg_t quick_sent; - volatile uhci_reg_q0_word0_reg_t reg_q0_word0; - volatile uhci_reg_q0_word1_reg_t reg_q0_word1; - volatile uhci_reg_q1_word0_reg_t reg_q1_word0; - volatile uhci_reg_q1_word1_reg_t reg_q1_word1; - volatile uhci_reg_q2_word0_reg_t reg_q2_word0; - volatile uhci_reg_q2_word1_reg_t reg_q2_word1; - volatile uhci_reg_q3_word0_reg_t reg_q3_word0; - volatile uhci_reg_q3_word1_reg_t reg_q3_word1; - volatile uhci_reg_q4_word0_reg_t reg_q4_word0; - volatile uhci_reg_q4_word1_reg_t reg_q4_word1; - volatile uhci_reg_q5_word0_reg_t reg_q5_word0; - volatile uhci_reg_q5_word1_reg_t reg_q5_word1; - volatile uhci_reg_q6_word0_reg_t reg_q6_word0; - volatile uhci_reg_q6_word1_reg_t reg_q6_word1; + volatile struct { + uhci_reg_qn_word0_reg_t word0; + uhci_reg_qn_word1_reg_t word1; + } q_data[7]; volatile uhci_esc_conf0_reg_t esc_conf0; volatile uhci_esc_conf1_reg_t esc_conf1; volatile uhci_esc_conf2_reg_t esc_conf2; diff --git a/examples/peripherals/uart/uart_dma_ota/README.md b/examples/peripherals/uart/uart_dma_ota/README.md index 085a3be363..a7b9c49649 100644 --- a/examples/peripherals/uart/uart_dma_ota/README.md +++ b/examples/peripherals/uart/uart_dma_ota/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-P4 | ESP32-S3 | -| ----------------- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | +| Supported Targets | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | # UART OTA Example