diff --git a/components/esp_driver_parlio/test_apps/parlio/README.md b/components/esp_driver_parlio/test_apps/parlio/README.md index 1b93f0ab44..bb8242f679 100644 --- a/components/esp_driver_parlio/test_apps/parlio/README.md +++ b/components/esp_driver_parlio/test_apps/parlio/README.md @@ -1,2 +1,2 @@ -| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H4 | ESP32-P4 | -| ----------------- | -------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | +| ----------------- | -------- | -------- | -------- | --------- | -------- | -------- | diff --git a/components/esp_driver_parlio/test_apps/parlio/main/test_board.h b/components/esp_driver_parlio/test_apps/parlio/main/test_board.h index 5203612f8f..181daa05ca 100644 --- a/components/esp_driver_parlio/test_apps/parlio/main/test_board.h +++ b/components/esp_driver_parlio/test_apps/parlio/main/test_board.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -56,6 +56,18 @@ extern "C" { #define TEST_DATA5_GPIO 11 #define TEST_DATA6_GPIO 26 #define TEST_DATA7_GPIO 12 +#elif CONFIG_IDF_TARGET_ESP32H21 +#define TEST_CLK_GPIO 14 +#define TEST_EXT_CLK_GPIO 12 +#define TEST_VALID_GPIO 9 +#define TEST_DATA0_GPIO 0 +#define TEST_DATA1_GPIO 1 +#define TEST_DATA2_GPIO 2 +#define TEST_DATA3_GPIO 3 +#define TEST_DATA4_GPIO 4 +#define TEST_DATA5_GPIO 10 +#define TEST_DATA6_GPIO 11 +#define TEST_DATA7_GPIO 6 #elif CONFIG_IDF_TARGET_ESP32H4 #define TEST_CLK_GPIO 15 #define TEST_EXT_CLK_GPIO 16 diff --git a/components/esp_hal_parlio/esp32h21/include/hal/parlio_ll.h b/components/esp_hal_parlio/esp32h21/include/hal/parlio_ll.h new file mode 100644 index 0000000000..c74eed9fa0 --- /dev/null +++ b/components/esp_hal_parlio/esp32h21/include/hal/parlio_ll.h @@ -0,0 +1,725 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// Note that most of the register operations in this layer are non-atomic operations. + +#pragma once + +#include +#include +#include "hal/assert.h" +#include "hal/misc.h" +#include "hal/hal_utils.h" +#include "soc/pcr_struct.h" +#include "soc/parl_io_struct.h" +#include "hal/parlio_types.h" + +#define PARLIO_LL_GET(attr) (PARLIO_LL_ ## attr) +#define PARLIO_LL_SUPPORT(feat) (PARLIO_LL_SUPPORT_ ## feat) +#define PARLIO_LL_INST_NUM 1 /*!< Number of parallel IO peripherals */ +#define PARLIO_LL_TX_UNITS_PER_INST 1 /*!< number of TX units in each instance */ +#define PARLIO_LL_RX_UNITS_PER_INST 1 /*!< number of RX units in each instance */ +#define PARLIO_LL_SUPPORT_RX_CLK_OUTPUT 1 /*!< Support output RX clock to a GPIO */ +#define PARLIO_LL_SUPPORT_TRANS_BIT_ALIGN 1 /*!< Support bit alignment in transaction */ +#define PARLIO_LL_SUPPORT_TX_EOF_FROM_DMA 1 /*!< Support to treat DMA EOF as TX unit EOF */ + +#define PARLIO_LL_RX_MAX_BYTES_PER_FRAME 0xFFFF +#define PARLIO_LL_RX_MAX_CLK_INT_DIV 0x10000 +#define PARLIO_LL_RX_MAX_CLK_FRACT_DIV 0 // Not support fractional divider +#define PARLIO_LL_RX_MAX_TIMEOUT 0xFFFF + +#define PARLIO_LL_TX_MAX_BITS_PER_FRAME 0x7FFFF +#define PARLIO_LL_TX_MAX_CLK_INT_DIV 0x10000 +#define PARLIO_LL_TX_MAX_CLK_FRACT_DIV 0 // Not support fractional divider + +#define PARLIO_LL_EVENT_TX_FIFO_EMPTY (1 << 0) +#define PARLIO_LL_EVENT_RX_FIFO_FULL (1 << 1) +#define PARLIO_LL_EVENT_TX_EOF (1 << 2) +#define PARLIO_LL_EVENT_TX_MASK (PARLIO_LL_EVENT_TX_FIFO_EMPTY | PARLIO_LL_EVENT_TX_EOF) +#define PARLIO_LL_EVENT_RX_MASK (PARLIO_LL_EVENT_RX_FIFO_FULL) + +#define PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG 7 // TXD[7] can be used a valid signal +#define PARLIO_LL_TX_DATA_LINE_AS_CLK_GATE 7 // TXD[7] can be used as clock gate signal + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + PARLIO_LL_RX_EOF_COND_RX_FULL, /*!< RX unit generates EOF event when it receives enough data */ + PARLIO_LL_RX_EOF_COND_EN_INACTIVE, /*!< RX unit generates EOF event when the external enable signal becomes inactive */ +} parlio_ll_rx_eof_cond_t; + +typedef enum { + PARLIO_LL_TX_EOF_COND_DATA_LEN, /*!< TX unit generates EOF event when it transmits particular data bit length that specified in `tx_bitlen`. */ + PARLIO_LL_TX_EOF_COND_DMA_EOF, /*!< TX unit generates EOF event when the DMA EOF takes place */ +} parlio_ll_tx_eof_cond_t; + +/** + * @brief Enable or disable the parlio peripheral APB clock + * + * @param group_id The group id of the parlio module + * @param enable Set true to enable, false to disable + */ +static inline void parlio_ll_enable_bus_clock(int group_id, bool enable) +{ + (void)group_id; + PCR.parl_io_conf.parl_clk_en = enable; +} + +/** + * @brief Reset the parlio module + * + * @param group_id The group id of the parlio module + */ +static inline void parlio_ll_reset_register(int group_id) +{ + (void)group_id; + PCR.parl_io_conf.parl_rst_en = 1; + PCR.parl_io_conf.parl_rst_en = 0; +} + +///////////////////////////////////////RX Unit/////////////////////////////////////// + +/** + * @brief Set the clock source for the RX unit + * + * @param dev Parallel IO register base address + * @param src Clock source + */ +static inline void parlio_ll_rx_set_clock_source(parl_io_dev_t *dev, parlio_clock_source_t src) +{ + (void)dev; + uint32_t clk_sel = 0; + switch (src) { + case PARLIO_CLK_SRC_XTAL: + clk_sel = 0; + break; + case PARLIO_CLK_SRC_PLL_F96M: + clk_sel = 1; + break; + case PARLIO_CLK_SRC_RC_FAST: + clk_sel = 2; + break; + case PARLIO_CLK_SRC_EXTERNAL: + clk_sel = 3; + break; + + default: // unsupported clock source + HAL_ASSERT(false); + break; + } + PCR.parl_clk_rx_conf.parl_clk_rx_sel = clk_sel; +} + +/** + * @brief Set the clock divider for the RX unit + * + * @param dev Parallel IO register base address + * @param clk_div Clock division with integral part, no fractional part on H21 + */ +static inline void parlio_ll_rx_set_clock_div(parl_io_dev_t *dev, const hal_utils_clk_div_t *clk_div) +{ + (void)dev; + HAL_ASSERT(clk_div->integer > 0 && clk_div->integer <= PARLIO_LL_RX_MAX_CLK_INT_DIV); + HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.parl_clk_rx_conf, parl_clk_rx_div_num, clk_div->integer - 1); +} + +/** + * @brief Reset the RX unit Core clock domain + * + * @param dev Parallel IO register base address + */ +static inline void parlio_ll_rx_reset_clock(parl_io_dev_t *dev) +{ + (void)dev; + PCR.parl_clk_rx_conf.parl_rx_rst_en = 1; + PCR.parl_clk_rx_conf.parl_rx_rst_en = 0; +} + +/** + * @brief Enable the RX unit Core clock domain + * + * @param dev Parallel IO register base address + * @param en True to enable, False to disable + */ +__attribute__((always_inline)) +static inline void parlio_ll_rx_enable_clock(parl_io_dev_t *dev, bool en) +{ + (void)dev; + PCR.parl_clk_rx_conf.parl_clk_rx_en = en; +} + +/** + * @brief Set the condition to generate the RX EOF event + * + * @param dev Parallel IO register base address + * @param cond RX EOF condition + */ +__attribute__((always_inline)) +static inline void parlio_ll_rx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_rx_eof_cond_t cond) +{ + dev->io_rx_genrl_cfg.io_rx_eof_gen_sel = cond; +} + +/** + * @brief Start RX unit to sample the input data + * + * @param dev Parallel IO register base address + * @param en True to start, False to stop + */ +__attribute__((always_inline)) +static inline void parlio_ll_rx_start(parl_io_dev_t *dev, bool en) +{ + dev->io_rx_start_cfg.io_rx_start = en; +} + +/** + * @brief Set the receive length + * + * @note The receive length can be used to generate DMA EOF signal, or to work as a frame end delimiter + * + * @param dev Parallel IO register base address + * @param bitlen Number of bits to receive in the next transaction, bitlen must be a multiple of 8 + */ +__attribute__((always_inline)) +static inline void parlio_ll_rx_set_recv_bit_len(parl_io_dev_t *dev, uint32_t bitlen) +{ + dev->io_rx_data_cfg.io_rx_bitlen = bitlen; +} + +/** + * @brief Set the sub mode of the level controlled receive mode + * + * @param dev Parallel IO register base address + * @param active_low_en Level of the external enable signal, true for active low, false for active high + */ +__attribute__((always_inline)) +static inline void parlio_ll_rx_set_level_recv_mode(parl_io_dev_t *dev, bool active_low_en) +{ + dev->io_rx_mode_cfg.io_rx_smp_mode_sel = 0; + dev->io_rx_mode_cfg.io_rx_ext_en_inv = active_low_en; +} + +/** + * @brief Set the sub mode of the pulse controlled receive mode + * + * @param dev Parallel IO register base address + * @param start_inc Whether the start pulse is counted + * @param end_inc Whether the end pulse is counted + * @param end_by_len Whether to use the frame length to determine the end of the frame + * @param pulse_inv Whether the pulse is inverted + */ +__attribute__((always_inline)) +static inline void parlio_ll_rx_set_pulse_recv_mode(parl_io_dev_t *dev, bool start_inc, bool end_inc, bool end_by_len, bool pulse_inv) +{ + uint32_t submode = 0; + uint32_t step = 1; + if (end_by_len) { + submode += 4; + } else { // end by pulse + step = 2; + if (!end_inc) { + submode += 1; + } + } + if (!start_inc) { + submode += step; + } + dev->io_rx_mode_cfg.io_rx_smp_mode_sel = 1; + dev->io_rx_mode_cfg.io_rx_pulse_submode_sel = submode; + dev->io_rx_mode_cfg.io_rx_ext_en_inv = pulse_inv; +} + +/** + * @brief Set the receive mode to software controlled receive mode + * + * @param dev Parallel IO register base address + */ +__attribute__((always_inline)) +static inline void parlio_ll_rx_set_soft_recv_mode(parl_io_dev_t *dev) +{ + dev->io_rx_mode_cfg.io_rx_smp_mode_sel = 2; +} + +/** + * @brief Whether to start the software controlled receive mode + * + * @param dev Parallel IO register base address + * @param en True to enable, False to disable + */ +static inline void parlio_ll_rx_start_soft_recv(parl_io_dev_t *dev, bool en) +{ + dev->io_rx_mode_cfg.io_rx_sw_en = en; +} + +/** + * @brief Set the sample clock edge + * + * @param dev Parallel IO register base address + * @param edge Sample clock edge + */ +__attribute__((always_inline)) +static inline void parlio_ll_rx_set_sample_clock_edge(parl_io_dev_t *dev, parlio_sample_edge_t edge) +{ + bool invert = edge == PARLIO_SAMPLE_EDGE_NEG; + dev->io_rx_clk_cfg.io_rx_clk_i_inv = invert; + dev->io_rx_clk_cfg.io_rx_clk_o_inv = invert; +} + +/** + * @brief Set the order to pack bits into one byte + * + * @param dev Parallel IO register base address + * @param order Packing order + */ +__attribute__((always_inline)) +static inline void parlio_ll_rx_set_bit_pack_order(parl_io_dev_t *dev, parlio_bit_pack_order_t order) +{ + dev->io_rx_data_cfg.io_rx_data_order_inv = order; +} + +/** + * @brief Set the bus width of the RX unit + * + * @param dev Parallel IO register base address + * @param width Bus width + */ +static inline void parlio_ll_rx_set_bus_width(parl_io_dev_t *dev, uint32_t width) +{ + uint32_t width_sel = 0; + switch (width) { + case 8: + width_sel = 3; + break; + case 4: + width_sel = 2; + break; + case 2: + width_sel = 1; + break; + case 1: + width_sel = 0; + break; + default: + HAL_ASSERT(false); + } + dev->io_rx_data_cfg.io_rx_bus_wid_sel = width_sel; +} + +/** + * @brief Reset RX Async FIFO + * + * @note During the reset of the asynchronous FIFO, it takes two clock cycles to synchronize within AHB clock domain (GDMA) and Core clock domain. + * The reset synchronization must be performed two clock cycles in advance. + * @note If the next frame transfer needs to be reset, you need to first switch to the internal free-running clock, + * and then switch to the actual clock after the reset is completed. + * + * @param dev Parallel IO register base address + */ +static inline void parlio_ll_rx_reset_fifo(parl_io_dev_t *dev) +{ + dev->io_fifo_cfg.io_rx_fifo_srst = 1; + dev->io_fifo_cfg.io_rx_fifo_srst = 0; +} + +/** + * @brief Set which data line as the enable signal + * + * @param dev Parallel IO register base address + * @param line_num Data line number (0-15) + */ +__attribute__((always_inline)) +static inline void parlio_ll_rx_treat_data_line_as_en(parl_io_dev_t *dev, uint32_t line_num) +{ + dev->io_rx_mode_cfg.io_rx_ext_en_sel = line_num; +} + +/** + * @brief Whether to enable the RX clock gating + * + * @param dev Parallel IO register base address + * @param en True to enable, False to disable + */ +static inline void parlio_ll_rx_enable_clock_gating(parl_io_dev_t *dev, bool en) +{ + dev->io_rx_genrl_cfg.io_rx_gating_en = en; +} + +/** + * @brief Enable RX timeout feature + * + * @param dev Parallel IO register base address + * @param en True to enable, False to disable + */ +__attribute__((always_inline)) +static inline void parlio_ll_rx_enable_timeout(parl_io_dev_t *dev, bool en) +{ + dev->io_rx_genrl_cfg.io_rx_timeout_en = en; +} + +/** + * @brief Set the threshold of RX timeout + * + * @param dev Parallel IO register base address + * @param thres Threshold of RX timeout + */ +__attribute__((always_inline)) +static inline void parlio_ll_rx_set_timeout_thres(parl_io_dev_t *dev, uint32_t thres) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(dev->io_rx_genrl_cfg, io_rx_timeout_thres, thres); +} + +/** + * @brief Update the RX configuration, to make the new configuration take effect + * + * @param dev Parallel IO register base address + */ +__attribute__((always_inline)) +static inline void parlio_ll_rx_update_config(parl_io_dev_t *dev) +{ + dev->io_reg_update.io_rx_reg_update = 1; + while (dev->io_reg_update.io_rx_reg_update); +} + +/** + * @brief Get the RX fifo cycle count + * + * @param dev Parallel IO register base address + * @return + * - RX fifo cycle count + */ +static inline uint32_t parlio_ll_rx_get_fifo_cycle_cnt(parl_io_dev_t *dev) +{ + return dev->io_rx_st0.io_rx_cnt; +} + +///////////////////////////////////TX Unit/////////////////////////////////////// + +/** + * @brief Set the clock source for the TX unit + * + * @param dev Parallel IO register base address + * @param src Clock source + */ +__attribute__((always_inline)) +static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_clock_source_t src) +{ + (void)dev; + uint32_t clk_sel = 0; + switch (src) { + case PARLIO_CLK_SRC_XTAL: + clk_sel = 0; + break; + case PARLIO_CLK_SRC_PLL_F96M: + clk_sel = 1; + break; + case PARLIO_CLK_SRC_RC_FAST: + clk_sel = 2; + break; + case PARLIO_CLK_SRC_EXTERNAL: + clk_sel = 3; + break; + + default: // unsupported clock source + HAL_ASSERT(false); + break; + } + PCR.parl_clk_tx_conf.parl_clk_tx_sel = clk_sel; +} + +/** + * @brief Set the clock divider for the TX unit + * + * @param dev Parallel IO register base address + * @param clk_div Clock division with integral part, no fractional part on H21 + */ +static inline void parlio_ll_tx_set_clock_div(parl_io_dev_t *dev, const hal_utils_clk_div_t *clk_div) +{ + (void)dev; + HAL_ASSERT(clk_div->integer > 0 && clk_div->integer <= PARLIO_LL_RX_MAX_CLK_INT_DIV); + HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.parl_clk_tx_conf, parl_clk_tx_div_num, clk_div->integer - 1); +} + +/** + * @brief Reset the TX unit Core clock domain + * + * @param dev Parallel IO register base address + */ +__attribute__((always_inline)) +static inline void parlio_ll_tx_reset_clock(parl_io_dev_t *dev) +{ + (void)dev; + PCR.parl_clk_tx_conf.parl_tx_rst_en = 1; + PCR.parl_clk_tx_conf.parl_tx_rst_en = 0; +} + +/** + * @brief Enable the TX unit Core clock domain + * + * @param dev Parallel IO register base address + * @param en True to enable, False to disable + */ +__attribute__((always_inline)) +static inline void parlio_ll_tx_enable_clock(parl_io_dev_t *dev, bool en) +{ + (void)dev; + PCR.parl_clk_tx_conf.parl_clk_tx_en = en; +} + +/** + * @brief Set the data length to be transmitted + * + * @param dev Parallel IO register base address + * @param bitlen Data length in bits, must be a multiple of 8 + */ +__attribute__((always_inline)) +static inline void parlio_ll_tx_set_trans_bit_len(parl_io_dev_t *dev, uint32_t bitlen) +{ + dev->io_tx_data_cfg.io_tx_bitlen = bitlen; +} + +/** + * @brief Check if tx size can be determined by DMA + * + * @param dev Parallel IO register base address (not used) + */ +static inline bool parlio_ll_tx_support_dma_eof(parl_io_dev_t *dev) +{ + (void)dev; + return true; +} + +/** + * @brief Set the condition to generate the TX EOF event + * + * @param dev Parallel IO register base address + * @param cond TX EOF condition + */ +__attribute__((always_inline)) +static inline void parlio_ll_tx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_tx_eof_cond_t cond) +{ + dev->io_tx_genrl_cfg.io_tx_eof_gen_sel = cond; +} + +/** + * @brief Whether to enable the TX clock gating + * + * @note The MSB of TXD will be taken as the gating enable signal + * + * @param dev Parallel IO register base address + * @param en True to enable, False to disable + */ +static inline void parlio_ll_tx_enable_clock_gating(parl_io_dev_t *dev, bool en) +{ + dev->io_tx_genrl_cfg.io_tx_gating_en = en; +} + +/** + * @brief Start TX unit to transmit data + * + * @note The hardware monitors the rising edge of tx_start as the trigger signal. + * Once the transmission starts, it cannot be stopped by clearing tx_start. + * + * @param dev Parallel IO register base address + * @param en True to start, False to reset the reg state (not meaning the TX unit will be stopped) + */ +__attribute__((always_inline)) +static inline void parlio_ll_tx_start(parl_io_dev_t *dev, bool en) +{ + dev->io_tx_start_cfg.io_tx_start = en; +} + +/** + * @brief Whether to treat the MSB of TXD as the valid signal + * + * @note If enabled, TXD[7] will work as valid signal, which stay high during data transmission. + * + * @param dev Parallel IO register base address + * @param en True to enable, False to disable + */ +static inline void parlio_ll_tx_treat_msb_as_valid(parl_io_dev_t *dev, bool en) +{ + dev->io_tx_genrl_cfg.io_tx_valid_output_en = en; +} + +/** + * @brief Set TX valid signal delay + * + * @param dev Parallel IO register base address + * @param start_delay Number of clock cycles to delay + * @param stop_delay Number of clock cycles to delay + * @return true: success, false: valid delay is not supported + */ +static inline bool parlio_ll_tx_set_valid_delay(parl_io_dev_t *dev, uint32_t start_delay, uint32_t stop_delay) +{ + (void)dev; + if (start_delay == 0 && stop_delay == 0) { + return true; + } + return false; +} + +/** + * @brief Set the shift clock edge + * + * @param dev Parallel IO register base address + * @param edge Shift clock edge + */ +static inline void parlio_ll_tx_set_shift_clock_edge(parl_io_dev_t *dev, parlio_shift_edge_t edge) +{ + dev->io_tx_clk_cfg.io_tx_clk_i_inv = edge; + dev->io_tx_clk_cfg.io_tx_clk_o_inv = edge; +} + +/** + * @brief Set the order to unpack bits from a byte + * + * @param dev Parallel IO register base address + * @param order Packing order + */ +static inline void parlio_ll_tx_set_bit_pack_order(parl_io_dev_t *dev, parlio_bit_pack_order_t order) +{ + dev->io_tx_data_cfg.io_tx_data_order_inv = order; +} + +/** + * @brief Set the bus width of the TX unit + * + * @param dev Parallel IO register base address + * @param width Bus width + */ +static inline void parlio_ll_tx_set_bus_width(parl_io_dev_t *dev, uint32_t width) +{ + uint32_t width_sel = 0; + switch (width) { + case 8: + width_sel = 3; + break; + case 4: + width_sel = 2; + break; + case 2: + width_sel = 1; + break; + case 1: + width_sel = 0; + break; + default: + HAL_ASSERT(false); + } + dev->io_tx_data_cfg.io_tx_bus_wid_sel = width_sel; +} + +/** + * @brief Reset TX Async FIFO + * + * @note During the reset of the asynchronous FIFO, it takes two clock cycles to synchronize within AHB clock domain (GDMA) and Core clock domain. + * The reset synchronization must be performed two clock cycles in advance. + * @note If the next frame transfer needs to be reset, you need to first switch to the internal free-running clock, + * and then switch to the actual clock after the reset is completed. + * + * @param dev Parallel IO register base address + */ +__attribute__((always_inline)) +static inline void parlio_ll_tx_reset_fifo(parl_io_dev_t *dev) +{ + dev->io_fifo_cfg.io_tx_fifo_srst = 1; + dev->io_fifo_cfg.io_tx_fifo_srst = 0; +} + +/** + * @brief Set the value to output on the TXD when the TX unit is in IDLE state + * + * @param dev Parallel IO register base address + * @param value Value to output + */ +__attribute__((always_inline)) +static inline void parlio_ll_tx_set_idle_data_value(parl_io_dev_t *dev, uint32_t value) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(dev->io_tx_genrl_cfg, io_tx_idle_value, value); +} + +/** + * @brief Check whether the TX unit is ready + * + * @param dev Parallel IO register base address + * @return true: ready, false: busy + */ +__attribute__((always_inline)) +static inline bool parlio_ll_tx_is_ready(parl_io_dev_t *dev) +{ + return dev->io_st.io_tx_ready; +} + +////////////////////////////////////Interrupt//////////////////////////////////////////////// + +/** + * @brief Enable Parallel IO interrupt for specific event mask + * + * @param dev Parallel IO register base address + * @param mask Event mask + * @param enable True to enable, False to disable + */ +static inline void parlio_ll_enable_interrupt(parl_io_dev_t *dev, uint32_t mask, bool enable) +{ + if (enable) { + dev->io_int_ena.val |= mask; + } else { + dev->io_int_ena.val &= ~mask; + } +} + +/** + * @brief Get interrupt status for TX unit + * + * @param dev Parallel IO register base address + * @return Interrupt status + */ +__attribute__((always_inline)) +static inline uint32_t parlio_ll_tx_get_interrupt_status(parl_io_dev_t *dev) +{ + return dev->io_int_st.val & PARLIO_LL_EVENT_TX_MASK; +} + +/** + * @brief Get interrupt status for RX unit + * + * @param dev Parallel IO register base address + * @return Interrupt status + */ +__attribute__((always_inline)) +static inline uint32_t parlio_ll_rx_get_interrupt_status(parl_io_dev_t *dev) +{ + return dev->io_int_st.val & PARLIO_LL_EVENT_RX_MASK; +} + +/** + * @brief Clear Parallel IO interrupt status by mask + * + * @param dev Parallel IO register base address + * @param mask Interrupt status mask + */ +__attribute__((always_inline)) +static inline void parlio_ll_clear_interrupt_status(parl_io_dev_t *dev, uint32_t mask) +{ + dev->io_int_clr.val = mask; +} + +/** + * @brief Get interrupt status register address + * + * @param dev Parallel IO register base address + * @return Register address + */ +static inline volatile void *parlio_ll_get_interrupt_status_reg(parl_io_dev_t *dev) +{ + return &dev->io_int_st; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hal_parlio/esp32h21/parlio_periph.c b/components/esp_hal_parlio/esp32h21/parlio_periph.c new file mode 100644 index 0000000000..32ebbefb9e --- /dev/null +++ b/components/esp_hal_parlio/esp32h21/parlio_periph.c @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "hal/parlio_periph.h" +#include "soc/gpio_sig_map.h" + +const soc_parlio_signal_desc_t soc_parlio_signals[1] = { + [0] = { + .module_name = "PARLIO0", + .tx_irq_id = ETS_PARL_IO_TX_INTR_SOURCE, + .rx_irq_id = ETS_PARL_IO_RX_INTR_SOURCE, + .tx_units = { + [0] = { + .data_sigs = { + PARL_TX_DATA0_IDX, + PARL_TX_DATA1_IDX, + PARL_TX_DATA2_IDX, + PARL_TX_DATA3_IDX, + PARL_TX_DATA4_IDX, + PARL_TX_DATA5_IDX, + PARL_TX_DATA6_IDX, + PARL_TX_DATA7_IDX, + }, + .clk_out_sig = PARL_TX_CLK_OUT_IDX, + .clk_in_sig = PARL_TX_CLK_IN_IDX, + .cs_sig = -1, + } + }, + .rx_units = { + [0] = { + .data_sigs = { + PARL_RX_DATA0_IDX, + PARL_RX_DATA1_IDX, + PARL_RX_DATA2_IDX, + PARL_RX_DATA3_IDX, + PARL_RX_DATA4_IDX, + PARL_RX_DATA5_IDX, + PARL_RX_DATA6_IDX, + PARL_RX_DATA7_IDX, + }, + .clk_out_sig = PARL_RX_CLK_OUT_IDX, + .clk_in_sig = PARL_RX_CLK_IN_IDX, + } + } + } +}; + +/** + * PARLIO Registers to be saved during sleep retention + * - Tx Configuration registers, e.g.: PARL_IO_TX_DATA_CFG_REG, PARL_IO_TX_GENRL_CFG_REG + * - Rx Configuration registers, e.g.: PARL_IO_RX_MODE_CFG_REG, PARL_IO_RX_DATA_CFG_REG, PARL_IO_RX_GENRL_CFG_REG + * - CLK Configuration registers, e.g.: PARL_IO_RX_CLK_CFG_REG, PARL_IO_TX_CLK_CFG_REG + * - Interrupt enable registers, e.g.: PARL_IO_INT_ENA_REG +*/ +#define PARLIO_RETENTION_REGS_CNT 8 +#define PARLIO_RETENTION_REGS_BASE (DR_REG_PARL_IO_BASE + 0x0) +static const uint32_t parlio_regs_map[4] = {0x60457, 0x0, 0x0, 0x0}; +static const regdma_entries_config_t parlio_regs_retention[] = { + [0] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_PARLIO_LINK(0x00), \ + PARLIO_RETENTION_REGS_BASE, PARLIO_RETENTION_REGS_BASE, \ + PARLIO_RETENTION_REGS_CNT, 0, 0, \ + parlio_regs_map[0], parlio_regs_map[1], \ + parlio_regs_map[2], parlio_regs_map[3]), \ + .owner = ENTRY(0) | ENTRY(2) + }, \ +}; +const parlio_reg_retention_info_t parlio_reg_retention_info[1] = { + [0] = { + .regdma_entry_array = parlio_regs_retention, + .array_size = ARRAY_SIZE(parlio_regs_retention), + .retention_module = SLEEP_RETENTION_MODULE_PARLIO0 + }, +}; diff --git a/components/esp_lcd/test_apps/parlio_lcd/README.md b/components/esp_lcd/test_apps/parlio_lcd/README.md index 31bfbd518a..b1154bec8d 100644 --- a/components/esp_lcd/test_apps/parlio_lcd/README.md +++ b/components/esp_lcd/test_apps/parlio_lcd/README.md @@ -1,4 +1,4 @@ -| Supported Targets | ESP32-C5 | ESP32-H2 | ESP32-H4 | ESP32-P4 | -| ----------------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32-C5 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | +| ----------------- | -------- | -------- | --------- | -------- | -------- | This test app is used to test LCDs with intel 8080 interface. diff --git a/components/esp_lcd/test_apps/parlio_lcd/main/test_parlio_board.h b/components/esp_lcd/test_apps/parlio_lcd/main/test_parlio_board.h index cfe596f1ce..9730dbd865 100644 --- a/components/esp_lcd/test_apps/parlio_lcd/main/test_parlio_board.h +++ b/components/esp_lcd/test_apps/parlio_lcd/main/test_parlio_board.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -61,6 +61,13 @@ extern "C" { #define TEST_LCD_DATA5_GPIO (35) #define TEST_LCD_DATA6_GPIO (19) #define TEST_LCD_DATA7_GPIO (34) +#elif CONFIG_IDF_TARGET_ESP32H21 +#define TEST_LCD_BK_LIGHT_GPIO (6) +#define TEST_LCD_RST_GPIO (2) +#define TEST_LCD_PCLK_GPIO (4) +#define TEST_LCD_CS_GPIO (0) +#define TEST_LCD_DC_GPIO (1) +#define TEST_LCD_DATA0_GPIO (3) #endif diff --git a/components/esp_lcd/test_apps/parlio_lcd/main/test_parlio_lcd_panel.c b/components/esp_lcd/test_apps/parlio_lcd/main/test_parlio_lcd_panel.c index 3e810d1e27..c4695ea757 100644 --- a/components/esp_lcd/test_apps/parlio_lcd/main/test_parlio_lcd_panel.c +++ b/components/esp_lcd/test_apps/parlio_lcd/main/test_parlio_lcd_panel.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ @@ -9,7 +9,6 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "unity.h" -#include "esp_random.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_panel_vendor.h" #include "esp_lcd_panel_ops.h" @@ -55,9 +54,9 @@ static void lcd_parlio_panel_with_st7789_interface(esp_lcd_panel_io_handle_t io_ gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 1); for (int i = 0; i < 200; i++) { - uint8_t color_byte = esp_random() & 0xFF; - int x_start = esp_random() % (TEST_LCD_H_RES - 100); - int y_start = esp_random() % (TEST_LCD_V_RES - 100); + uint8_t color_byte = rand() & 0xFF; + int x_start = rand() % (TEST_LCD_H_RES - 100); + int y_start = rand() % (TEST_LCD_V_RES - 100); memset(img, color_byte, TEST_IMG_SIZE); esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img); } @@ -150,7 +149,7 @@ static void lcd_parlio_send_colors_to_fixed_region(size_t data_width) size_t color_size = (x_end - x_start) * (y_end - y_start) * 2; void *color_data = heap_caps_calloc(1, color_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); TEST_ASSERT_NOT_NULL(color_data); - uint8_t color_byte = esp_random() & 0xFF; + uint8_t color_byte = rand() & 0xFF; memset(color_data, color_byte, color_size); uint32_t isr_counter = 0; esp_lcd_panel_io_handle_t io_handle = NULL; @@ -238,7 +237,7 @@ static void lcd_parlio_send_colors_to_fixed_region(size_t data_width) vTaskDelay(pdMS_TO_TICKS(1000)); TEST_ASSERT_EQUAL(steps, isr_counter); // change to another color - color_byte = esp_random() & 0xFF; + color_byte = rand() & 0xFF; memset(color_data, color_byte, color_size); for (int i = 0; i < steps; i++) { TEST_ESP_OK(esp_lcd_panel_io_tx_color(io_handle, -1, color_data + i * color_size_per_step, color_size_per_step)); diff --git a/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in index 33b4217b36..bcc87f73d6 100644 --- a/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in @@ -175,6 +175,14 @@ config SOC_ETM_SUPPORTED bool default y +config SOC_PARLIO_SUPPORTED + bool + default y + +config SOC_PARLIO_LCD_SUPPORTED + bool + default y + config SOC_RMT_SUPPORTED bool default y @@ -607,6 +615,30 @@ config SOC_MCPWM_SUPPORT_SLEEP_RETENTION bool default y +config SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH + int + default 8 + +config SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH + int + default 8 + +config SOC_PARLIO_TX_CLK_SUPPORT_GATING + bool + default y + +config SOC_PARLIO_RX_CLK_SUPPORT_GATING + bool + default y + +config SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION + bool + default y + +config SOC_PARLIO_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_MPI_MEM_BLOCKS_NUM int default 4 diff --git a/components/soc/esp32h21/include/soc/clk_tree_defs.h b/components/soc/esp32h21/include/soc/clk_tree_defs.h index b0932637e9..7e0b834c98 100644 --- a/components/soc/esp32h21/include/soc/clk_tree_defs.h +++ b/components/soc/esp32h21/include/soc/clk_tree_defs.h @@ -491,6 +491,24 @@ typedef enum { I2S_CLK_SRC_EXTERNAL = -1, /*!< Select external clock as source clock */ } soc_periph_i2s_clk_src_t; +//////////////////////////////////////////////////PARLIO//////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of PARLIO + */ +#define SOC_PARLIO_CLKS {SOC_MOD_CLK_XTAL, SOC_MOD_CLK_PLL_F96M, SOC_MOD_CLK_RC_FAST} + +/** + * @brief PARLIO clock source + */ +typedef enum { + PARLIO_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + PARLIO_CLK_SRC_PLL_F96M = SOC_MOD_CLK_PLL_F96M, /*!< Select PLL_F96M as the source clock */ + PARLIO_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST, /*!< Select RC_FAST as the source clock */ + PARLIO_CLK_SRC_EXTERNAL = -1, /*!< Select EXTERNAL clock as the source clock */ + PARLIO_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F96M, /*!< Select PLL_F96M as the default clock choice */ +} soc_periph_parlio_clk_src_t; + #ifdef __cplusplus } #endif diff --git a/components/soc/esp32h21/include/soc/soc_caps.h b/components/soc/esp32h21/include/soc/soc_caps.h index 013a772d23..25cac97c59 100644 --- a/components/soc/esp32h21/include/soc/soc_caps.h +++ b/components/soc/esp32h21/include/soc/soc_caps.h @@ -78,7 +78,8 @@ #define SOC_MCPWM_SUPPORTED 1 #define SOC_TWAI_SUPPORTED 1 #define SOC_ETM_SUPPORTED 1 -// #define SOC_PARLIO_SUPPORTED 1 //TODO: [ESP32H21] IDF-11570, IDF-11572 +#define SOC_PARLIO_SUPPORTED 1 +#define SOC_PARLIO_LCD_SUPPORTED 1 #define SOC_RMT_SUPPORTED 1 #define SOC_AES_SUPPORTED 1 // #define SOC_SDIO_SLAVE_SUPPORTED 1 @@ -304,10 +305,12 @@ // #define SOC_USB_SERIAL_JTAG_SUPPORT_LIGHT_SLEEP (1) /*!< Support to maintain minimum usb communication during light sleep */ // TODO: IDF-6395 /*-------------------------- PARLIO CAPS --------------------------------------*/ -// #define SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH 8 /*!< Number of data lines of the TX unit */ -// #define SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH 8 /*!< Number of data lines of the RX unit */ -// #define SOC_PARLIO_TX_CLK_SUPPORT_GATING 1 /*!< Support gating TX clock */ -// #define SOC_PARLIO_RX_CLK_SUPPORT_GATING 1 /*!< Support gating RX clock */ +#define SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH 8 /*!< Number of data lines of the TX unit */ +#define SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH 8 /*!< Number of data lines of the RX unit */ +#define SOC_PARLIO_TX_CLK_SUPPORT_GATING 1 /*!< Support gating TX clock */ +#define SOC_PARLIO_RX_CLK_SUPPORT_GATING 1 /*!< Support gating RX clock */ +#define SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION 1 /*!< Support loop transmission */ +#define SOC_PARLIO_SUPPORT_SLEEP_RETENTION 1 /*!< Support back up registers before sleep */ /*--------------------------- MPI CAPS ---------------------------------------*/ #define SOC_MPI_MEM_BLOCKS_NUM (4) diff --git a/components/soc/esp32h21/register/soc/parl_io_struct.h b/components/soc/esp32h21/register/soc/parl_io_struct.h index da452f6208..be578fbbec 100644 --- a/components/soc/esp32h21/register/soc/parl_io_struct.h +++ b/components/soc/esp32h21/register/soc/parl_io_struct.h @@ -468,7 +468,7 @@ typedef union { } parl_io_version_reg_t; -typedef struct { +typedef struct parl_io_dev_t { volatile parl_io_rx_mode_cfg_reg_t io_rx_mode_cfg; volatile parl_io_rx_data_cfg_reg_t io_rx_data_cfg; volatile parl_io_rx_genrl_cfg_reg_t io_rx_genrl_cfg; diff --git a/docs/docs_not_updated/esp32h21.txt b/docs/docs_not_updated/esp32h21.txt index 0b43959951..8e607fb066 100644 --- a/docs/docs_not_updated/esp32h21.txt +++ b/docs/docs_not_updated/esp32h21.txt @@ -110,7 +110,6 @@ api-reference/peripherals/sdspi_share.rst api-reference/peripherals/adc_continuous.rst api-reference/peripherals/sdspi_host.rst api-reference/peripherals/vad.rst -api-reference/peripherals/parlio.rst api-reference/peripherals/adc_calibration.rst api-reference/peripherals/spi_flash/index.rst api-reference/peripherals/spi_flash/spi_flash_concurrency.rst diff --git a/examples/peripherals/lcd/parlio_simulate/README.md b/examples/peripherals/lcd/parlio_simulate/README.md index b92cd23f84..4e37445a4d 100644 --- a/examples/peripherals/lcd/parlio_simulate/README.md +++ b/examples/peripherals/lcd/parlio_simulate/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-C5 | ESP32-H2 | ESP32-H4 | ESP32-P4 | -| ----------------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32-C5 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | +| ----------------- | -------- | -------- | --------- | -------- | -------- | # Parallel IO simulation of SPI or I80 Interfaced LCD example diff --git a/examples/peripherals/lcd/parlio_simulate/sdkconfig.defaults.esp32h21 b/examples/peripherals/lcd/parlio_simulate/sdkconfig.defaults.esp32h21 new file mode 100644 index 0000000000..d638ba81ea --- /dev/null +++ b/examples/peripherals/lcd/parlio_simulate/sdkconfig.defaults.esp32h21 @@ -0,0 +1,6 @@ +CONFIG_EXAMPLE_PIN_NUM_BK_LIGHT=6 +CONFIG_EXAMPLE_PIN_NUM_RST=2 +CONFIG_EXAMPLE_PIN_NUM_PCLK=4 +CONFIG_EXAMPLE_PIN_NUM_CS=0 +CONFIG_EXAMPLE_PIN_NUM_DC=1 +CONFIG_EXAMPLE_PIN_NUM_DATA0=3 diff --git a/examples/peripherals/parlio/parlio_rx/logic_analyzer/README.md b/examples/peripherals/parlio/parlio_rx/logic_analyzer/README.md index f586368306..b8acc9cff9 100644 --- a/examples/peripherals/parlio/parlio_rx/logic_analyzer/README.md +++ b/examples/peripherals/parlio/parlio_rx/logic_analyzer/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H4 | ESP32-P4 | -| ----------------- | -------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | +| ----------------- | -------- | -------- | -------- | --------- | -------- | -------- | # Logic Analyzer Example @@ -95,7 +95,7 @@ Before conversion, we need to know how many channels in the raw data. In this ex For the default partition in this example: ```bash - parttool.py -p -b 406800 -f partitions.csv read_partition -h -n storage --output probe_raw.dat + parttool.py -p -b 460800 -f partitions.csv read_partition -n storage --output probe_raw.dat ``` 2. Convert the raw data into VCD format: diff --git a/examples/peripherals/parlio/parlio_rx/logic_analyzer/pytest_logic_analyzer.py b/examples/peripherals/parlio/parlio_rx/logic_analyzer/pytest_logic_analyzer.py index 1899df802b..f043f09c67 100644 --- a/examples/peripherals/parlio/parlio_rx/logic_analyzer/pytest_logic_analyzer.py +++ b/examples/peripherals/parlio/parlio_rx/logic_analyzer/pytest_logic_analyzer.py @@ -3,6 +3,7 @@ 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 @@ -13,7 +14,7 @@ from pytest_embedded_idf.utils import idf_parametrize ], indirect=True, ) -@idf_parametrize('target', ['esp32c5', 'esp32c6', 'esp32h2', 'esp32p4'], indirect=['target']) +@idf_parametrize('target', soc_filtered_targets('SOC_PARLIO_SUPPORTED == 1'), indirect=['target']) def test_logic_analyzer_flash_stream(dut: Dut) -> None: dut.expect(r'flash_fat: Probe data partition base addr: \w+ size: \w+') dut.expect(r'flash_fat: flash FATFS mounted') diff --git a/examples/peripherals/parlio/parlio_tx/simple_rgb_led_matrix/README.md b/examples/peripherals/parlio/parlio_tx/simple_rgb_led_matrix/README.md index 39ac38abe4..20895d5a47 100644 --- a/examples/peripherals/parlio/parlio_tx/simple_rgb_led_matrix/README.md +++ b/examples/peripherals/parlio/parlio_tx/simple_rgb_led_matrix/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H4 | ESP32-P4 | -| ----------------- | -------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | +| ----------------- | -------- | -------- | -------- | --------- | -------- | -------- | # Parallel IO TX Example: Simple RGB LED Matrix