diff --git a/components/driver/parlio/parlio_common.c b/components/driver/parlio/parlio_common.c index f3450ff79d..409a3f7f0f 100644 --- a/components/driver/parlio/parlio_common.c +++ b/components/driver/parlio/parlio_common.c @@ -18,7 +18,6 @@ #include "soc/parlio_periph.h" #include "hal/parlio_ll.h" #include "esp_private/esp_clk.h" -#include "esp_private/periph_ctrl.h" #include "parlio_private.h" static const char *TAG = "parlio"; @@ -45,9 +44,10 @@ parlio_group_t *parlio_acquire_group_handle(int group_id) s_platform.groups[group_id] = group; group->group_id = group_id; group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; - // enable APB access PARLIO registers - periph_module_enable(parlio_periph_signals.groups[group_id].module); - periph_module_reset(parlio_periph_signals.groups[group_id].module); + PARLIO_RCC_ATOMIC() { + parlio_ll_enable_bus_clock(group_id, true); + parlio_ll_reset_register(group_id); + } // hal layer initialize parlio_hal_init(&group->hal); } @@ -78,7 +78,9 @@ void parlio_release_group_handle(parlio_group_t *group) s_platform.groups[group_id] = NULL; // hal layer deinitialize parlio_hal_deinit(&group->hal); - periph_module_disable(parlio_periph_signals.groups[group_id].module); + PARLIO_RCC_ATOMIC() { + parlio_ll_enable_bus_clock(group_id, false); + } free(group); } _lock_release(&s_platform.mutex); diff --git a/components/driver/parlio/parlio_private.h b/components/driver/parlio/parlio_private.h index 1eabbe81cc..b7418b3ff9 100644 --- a/components/driver/parlio/parlio_private.h +++ b/components/driver/parlio/parlio_private.h @@ -11,8 +11,14 @@ #include "soc/soc_caps.h" #include "hal/parlio_types.h" #include "hal/parlio_hal.h" +#include "hal/parlio_ll.h" +#include "hal/dma_types.h" +#include "hal/cache_hal.h" +#include "hal/cache_ll.h" +#include "rom/cache.h" #include "esp_heap_caps.h" #include "driver/parlio_types.h" +#include "esp_private/periph_ctrl.h" #if CONFIG_PARLIO_ISR_IRAM_SAFE #define PARLIO_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) @@ -32,6 +38,41 @@ #define PARLIO_INTR_ALLOC_FLAG (ESP_INTR_FLAG_LOWMED | PARLIO_INTR_ALLOC_FLAG_SHARED) #endif +#if SOC_GDMA_TRIG_PERIPH_PARLIO0_BUS == SOC_GDMA_BUS_AXI +/* The parlio peripheral uses DMA via AXI bus, which requires the descriptor aligned with 8 */ +typedef dma_descriptor_align8_t parlio_dma_desc_t; +#else +typedef dma_descriptor_align4_t parlio_dma_desc_t; +#endif + +#ifdef CACHE_LL_L2MEM_NON_CACHE_ADDR +/* The descriptor address can be mapped by a fixed offset */ +#define PARLIO_GET_NON_CACHED_DESC_ADDR(desc) (desc ? (parlio_dma_desc_t *)(CACHE_LL_L2MEM_NON_CACHE_ADDR(desc)) : NULL) +#else +#define PARLIO_GET_NON_CACHED_DESC_ADDR(desc) (desc) +#endif // CACHE_LL_L2MEM_NON_CACHE_ADDR + +#if SOC_GDMA_TRIG_PERIPH_PARLIO0_BUS == SOC_GDMA_BUS_AXI +#define PARLIO_DMA_DESC_ALIGNMENT 8 +#define PARLIO_DMA_DESC_SIZE 8 +#else +#define PARLIO_DMA_DESC_ALIGNMENT 4 +#define PARLIO_DMA_DESC_SIZE sizeof(parlio_dma_desc_t) +#endif + +#if SOC_PERIPH_CLK_CTRL_SHARED +#define PARLIO_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC() +#else +#define PARLIO_CLOCK_SRC_ATOMIC() +#endif + +#if !SOC_RCC_IS_INDEPENDENT +// Reset and Clock Control registers are mixing with other peripherals, so we need to use a critical section +#define PARLIO_RCC_ATOMIC() PERIPH_RCC_ATOMIC() +#else +#define PARLIO_RCC_ATOMIC() +#endif // SOC_RCC_IS_INDEPENDENT + #define PARLIO_PM_LOCK_NAME_LEN_MAX 16 #ifdef __cplusplus diff --git a/components/driver/parlio/parlio_tx.c b/components/driver/parlio/parlio_tx.c index 81d0eba5bb..82d5b0b3d0 100644 --- a/components/driver/parlio/parlio_tx.c +++ b/components/driver/parlio/parlio_tx.c @@ -29,7 +29,6 @@ #include "soc/parlio_periph.h" #include "hal/parlio_ll.h" #include "hal/gpio_hal.h" -#include "hal/dma_types.h" #include "driver/gpio.h" #include "driver/parlio_tx.h" #include "parlio_private.h" @@ -66,7 +65,7 @@ typedef struct parlio_tx_unit_t { _Atomic parlio_tx_fsm_t fsm; // Driver FSM state parlio_tx_done_callback_t on_trans_done; // callback function when the transmission is done void *user_data; // user data passed to the callback function - dma_descriptor_t *dma_nodes; // DMA descriptor nodes + parlio_dma_desc_t *dma_nodes; // DMA descriptor nodes parlio_tx_trans_desc_t trans_desc_pool[]; // transaction descriptor pool } parlio_tx_unit_t; @@ -258,13 +257,26 @@ static esp_err_t parlio_select_periph_clock(parlio_tx_unit_t *tx_unit, const par ESP_RETURN_ON_ERROR(ret, TAG, "create NO_LIGHT_SLEEP lock failed"); } #endif - - parlio_ll_tx_set_clock_source(hal->regs, clk_src); - // set clock division, round up - uint32_t div = (periph_src_clk_hz + config->output_clk_freq_hz - 1) / config->output_clk_freq_hz; - parlio_ll_tx_set_clock_div(hal->regs, div); - // precision lost due to division, calculate the real frequency - tx_unit->out_clk_freq_hz = periph_src_clk_hz / div; + hal_utils_clk_div_t clk_div = {}; + hal_utils_clk_info_t clk_info = { + .src_freq_hz = periph_src_clk_hz, + .exp_freq_hz = config->output_clk_freq_hz, + .max_integ = PARLIO_LL_TX_MAX_CLK_INT_DIV, + .min_integ = 1, + .round_opt = HAL_DIV_ROUND, + }; +#if PARLIO_LL_TX_MAX_CLK_FRACT_DIV + clk_info.max_fract = PARLIO_LL_TX_MAX_CLK_FRACT_DIV; + tx_unit->out_clk_freq_hz = hal_utils_calc_clk_div_frac_accurate(&clk_info, &clk_div); +#else + tx_unit->out_clk_freq_hz = hal_utils_calc_clk_div_integer(&clk_info, &clk_div.integer); +#endif + PARLIO_CLOCK_SRC_ATOMIC() { + parlio_ll_tx_set_clock_source(hal->regs, clk_src); + // set clock division + parlio_ll_tx_set_clock_div(hal->regs, &clk_div); + } + // precision lost due to division if (tx_unit->out_clk_freq_hz != config->output_clk_freq_hz) { ESP_LOGW(TAG, "precision loss, real output frequency: %"PRIu32, tx_unit->out_clk_freq_hz); } @@ -302,8 +314,13 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un ESP_GOTO_ON_FALSE(unit, ESP_ERR_NO_MEM, err, TAG, "no memory for tx unit"); size_t dma_nodes_num = config->max_transfer_size / DMA_DESCRIPTOR_BUFFER_MAX_SIZE + 1; // DMA descriptors must be placed in internal SRAM - unit->dma_nodes = heap_caps_calloc(dma_nodes_num, sizeof(dma_descriptor_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); + + unit->dma_nodes = heap_caps_aligned_calloc(PARLIO_DMA_DESC_ALIGNMENT, dma_nodes_num, PARLIO_DMA_DESC_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); ESP_GOTO_ON_FALSE(unit->dma_nodes, ESP_ERR_NO_MEM, err, TAG, "no memory for DMA nodes"); + // Link the descriptors + for (int i = 0; i < dma_nodes_num; i++) { + unit->dma_nodes[i].next = (i == dma_nodes_num - 1) ? NULL : &(unit->dma_nodes[i+1]); + } unit->max_transfer_bits = config->max_transfer_size * 8; unit->data_width = data_width; @@ -328,10 +345,14 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un ESP_GOTO_ON_ERROR(parlio_tx_unit_init_dma(unit), err, TAG, "install tx DMA failed"); // reset fifo and core clock domain - parlio_ll_tx_reset_clock(hal->regs); + PARLIO_RCC_ATOMIC() { + parlio_ll_tx_reset_clock(hal->regs); + } parlio_ll_tx_reset_fifo(hal->regs); - // stop output clock - parlio_ll_tx_enable_clock(hal->regs, false); + PARLIO_CLOCK_SRC_ATOMIC() { + // stop output clock + parlio_ll_tx_enable_clock(hal->regs, false); + } // clock gating parlio_ll_tx_enable_clock_gating(hal->regs, config->flags.clk_gate_en); // set data width @@ -351,6 +372,11 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un // set sample clock edge parlio_ll_tx_set_sample_clock_edge(hal->regs, config->sample_edge); +#if SOC_PARLIO_TX_SIZE_BY_DMA + // Always use DMA EOF as the Parlio TX EOF + parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DMA_EOF); +#endif // SOC_PARLIO_TX_SIZE_BY_DMA + // clear any pending interrupt parlio_ll_clear_interrupt_status(hal->regs, PARLIO_LL_EVENT_TX_MASK); @@ -380,30 +406,28 @@ esp_err_t parlio_del_tx_unit(parlio_tx_unit_handle_t unit) return parlio_destroy_tx_unit(unit); } -static void IRAM_ATTR parlio_tx_mount_dma_data(dma_descriptor_t *desc_head, const void *buffer, size_t len) +static void IRAM_ATTR parlio_tx_mount_dma_data(parlio_dma_desc_t *desc_head, const void *buffer, size_t len) { size_t prepared_length = 0; uint8_t *data = (uint8_t *)buffer; - dma_descriptor_t *desc = desc_head; - while (len > DMA_DESCRIPTOR_BUFFER_MAX_SIZE) { - desc->dw0.suc_eof = 0; // not the end of the transaction - desc->dw0.size = DMA_DESCRIPTOR_BUFFER_MAX_SIZE; - desc->dw0.length = DMA_DESCRIPTOR_BUFFER_MAX_SIZE; - desc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; - desc->buffer = &data[prepared_length]; + parlio_dma_desc_t *desc = desc_head; + + while (len) { + parlio_dma_desc_t *non_cache_desc = PARLIO_GET_NON_CACHED_DESC_ADDR(desc); + uint32_t mount_bytes = len > DMA_DESCRIPTOR_BUFFER_MAX_SIZE ? DMA_DESCRIPTOR_BUFFER_MAX_SIZE : len; + len -= mount_bytes; + non_cache_desc->dw0.suc_eof = len == 0; // whether the last frame + non_cache_desc->dw0.size = mount_bytes; + non_cache_desc->dw0.length = mount_bytes; + non_cache_desc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; + non_cache_desc->buffer = &data[prepared_length]; desc = desc->next; // move to next descriptor - prepared_length += DMA_DESCRIPTOR_BUFFER_MAX_SIZE; - len -= DMA_DESCRIPTOR_BUFFER_MAX_SIZE; - } - if (len) { - desc->dw0.suc_eof = 1; // end of the transaction - desc->dw0.size = len; - desc->dw0.length = len; - desc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; - desc->buffer = &data[prepared_length]; - desc = desc->next; // move to next descriptor - prepared_length += len; + prepared_length += mount_bytes; } +#if CONFIG_IDF_TARGET_ESP32P4 + // Write back to cache to synchronize the cache before DMA start + Cache_WriteBack_Addr(CACHE_MAP_L1_DCACHE, (uint32_t)buffer, len); +#endif // CONFIG_IDF_TARGET_ESP32P4 } esp_err_t parlio_tx_unit_wait_all_done(parlio_tx_unit_handle_t tx_unit, int timeout_ms) @@ -451,7 +475,9 @@ static void IRAM_ATTR parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio parlio_tx_mount_dma_data(tx_unit->dma_nodes, t->payload, (t->payload_bits + 7) / 8); parlio_ll_tx_reset_fifo(hal->regs); - parlio_ll_tx_reset_clock(hal->regs); + PARLIO_RCC_ATOMIC() { + parlio_ll_tx_reset_clock(hal->regs); + } parlio_ll_tx_set_idle_data_value(hal->regs, t->idle_value); parlio_ll_tx_set_trans_bit_len(hal->regs, t->payload_bits); @@ -460,7 +486,9 @@ static void IRAM_ATTR parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio while (parlio_ll_tx_is_ready(hal->regs) == false); // turn on the core clock after we start the TX unit parlio_ll_tx_start(hal->regs, true); - parlio_ll_tx_enable_clock(hal->regs, true); + PARLIO_CLOCK_SRC_ATOMIC() { + parlio_ll_tx_enable_clock(hal->regs, true); + } } esp_err_t parlio_tx_unit_enable(parlio_tx_unit_handle_t tx_unit) @@ -592,7 +620,9 @@ static void IRAM_ATTR parlio_tx_default_isr(void *args) if (status & PARLIO_LL_EVENT_TX_EOF) { parlio_ll_clear_interrupt_status(hal->regs, PARLIO_LL_EVENT_TX_EOF); - parlio_ll_tx_enable_clock(hal->regs, false); + PARLIO_CLOCK_SRC_ATOMIC() { + parlio_ll_tx_enable_clock(hal->regs, false); + } parlio_ll_tx_start(hal->regs, false); parlio_tx_trans_desc_t *trans_desc = NULL; diff --git a/components/driver/test_apps/.build-test-rules.yml b/components/driver/test_apps/.build-test-rules.yml index d13e5809a9..dc0a8ef9bf 100644 --- a/components/driver/test_apps/.build-test-rules.yml +++ b/components/driver/test_apps/.build-test-rules.yml @@ -88,6 +88,10 @@ components/driver/test_apps/mcpwm: components/driver/test_apps/parlio: disable: - if: SOC_PARLIO_SUPPORTED != 1 + disable_test: + - if: IDF_TARGET == "esp32p4" + temporary: true + reason: lack of runner components/driver/test_apps/pulse_cnt: disable: diff --git a/components/driver/test_apps/parlio/README.md b/components/driver/test_apps/parlio/README.md index b450dc5ffa..7b822bdb0e 100644 --- a/components/driver/test_apps/parlio/README.md +++ b/components/driver/test_apps/parlio/README.md @@ -1,2 +1,2 @@ -| Supported Targets | ESP32-C6 | ESP32-H2 | -| ----------------- | -------- | -------- | +| Supported Targets | ESP32-C6 | ESP32-H2 | ESP32-P4 | +| ----------------- | -------- | -------- | -------- | diff --git a/components/driver/test_apps/parlio/main/test_board.h b/components/driver/test_apps/parlio/main/test_board.h index 4d6c0139f6..08aecfd081 100644 --- a/components/driver/test_apps/parlio/main/test_board.h +++ b/components/driver/test_apps/parlio/main/test_board.h @@ -31,6 +31,16 @@ extern "C" { #define TEST_DATA5_GPIO 5 #define TEST_DATA6_GPIO 8 #define TEST_DATA7_GPIO 9 +#elif CONFIG_IDF_TARGET_ESP32P4 +#define TEST_CLK_GPIO 20 +#define TEST_DATA0_GPIO 21 +#define TEST_DATA1_GPIO 22 +#define TEST_DATA2_GPIO 34 +#define TEST_DATA3_GPIO 35 +#define TEST_DATA4_GPIO 48 +#define TEST_DATA5_GPIO 49 +#define TEST_DATA6_GPIO 10 +#define TEST_DATA7_GPIO 11 #else #error "Unsupported target" #endif diff --git a/components/hal/esp32c6/include/hal/parlio_ll.h b/components/hal/esp32c6/include/hal/parlio_ll.h index a260d03521..1d1265e74e 100644 --- a/components/hal/esp32c6/include/hal/parlio_ll.h +++ b/components/hal/esp32c6/include/hal/parlio_ll.h @@ -12,17 +12,20 @@ #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_RX_MAX_BYTES_PER_FRAME 0xFFFF -#define PARLIO_LL_RX_MAX_CLOCK_DIV 0x10000 +#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_BYTES_PER_FRAME 0xFFFF #define PARLIO_LL_TX_MAX_BITS_PER_FRAME (PARLIO_LL_TX_MAX_BYTES_PER_FRAME * 8) -#define PARLIO_LL_TX_MAX_CLOCK_DIV 0x10000 +#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) @@ -47,6 +50,30 @@ typedef enum { 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; +/** + * @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/////////////////////////////////////// /** @@ -81,13 +108,13 @@ static inline void parlio_ll_rx_set_clock_source(parl_io_dev_t *dev, parlio_ll_c * @brief Set the clock divider for the RX unit * * @param dev Parallel IO register base address - * @param div Clock divider + * @param clk_div Clock division with integral part, no fractional part on C6 */ -static inline void parlio_ll_rx_set_clock_div(parl_io_dev_t *dev, uint32_t div) +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(div > 0 && div <= PARLIO_LL_RX_MAX_CLOCK_DIV); - PCR.parl_clk_rx_conf.parl_clk_rx_div_num = div - 1; + 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); } /** @@ -363,13 +390,13 @@ static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_ll_c * @brief Set the clock divider for the TX unit * * @param dev Parallel IO register base address - * @param div Clock divider + * @param clk_div Clock division with integral part, no fractional part on C6 */ -static inline void parlio_ll_tx_set_clock_div(parl_io_dev_t *dev, uint32_t div) +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(div > 0 && div <= PARLIO_LL_TX_MAX_CLOCK_DIV); - PCR.parl_clk_tx_conf.parl_clk_tx_div_num = div - 1; + 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); } /** diff --git a/components/hal/esp32h2/include/hal/parlio_ll.h b/components/hal/esp32h2/include/hal/parlio_ll.h index ddf7946b99..f4938b1c4b 100644 --- a/components/hal/esp32h2/include/hal/parlio_ll.h +++ b/components/hal/esp32h2/include/hal/parlio_ll.h @@ -12,16 +12,19 @@ #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_RX_MAX_BYTES_PER_FRAME 0xFFFF -#define PARLIO_LL_RX_MAX_CLOCK_DIV 0x10000 +#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_CLOCK_DIV 0x10000 +#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) @@ -32,6 +35,8 @@ #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 +#define PARLIO_LL_CLK_DIVIDER_MAX (0) // Not support fractional divider + #ifdef __cplusplus extern "C" { #endif @@ -47,6 +52,30 @@ typedef enum { 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; +/** + * @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/////////////////////////////////////// /** @@ -81,13 +110,13 @@ static inline void parlio_ll_rx_set_clock_source(parl_io_dev_t *dev, parlio_ll_c * @brief Set the clock divider for the RX unit * * @param dev Parallel IO register base address - * @param div Clock divider + * @param clk_div Clock division with integral part, no fractional part on H2 */ -static inline void parlio_ll_rx_set_clock_div(parl_io_dev_t *dev, uint32_t div) +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(div > 0 && div <= PARLIO_LL_RX_MAX_CLOCK_DIV); - PCR.parl_clk_rx_conf.parl_clk_rx_div_num = div - 1; + 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); } /** @@ -367,13 +396,13 @@ static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_ll_c * @brief Set the clock divider for the TX unit * * @param dev Parallel IO register base address - * @param div Clock divider + * @param clk_div Clock division with integral part, no fractional part on H2 */ -static inline void parlio_ll_tx_set_clock_div(parl_io_dev_t *dev, uint32_t div) +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(div > 0 && div <= PARLIO_LL_TX_MAX_CLOCK_DIV); - PCR.parl_clk_tx_conf.parl_clk_tx_div_num = div - 1; + 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); } /** diff --git a/components/hal/esp32p4/include/hal/clk_gate_ll.h b/components/hal/esp32p4/include/hal/clk_gate_ll.h index 566a112b40..48e7a09509 100644 --- a/components/hal/esp32p4/include/hal/clk_gate_ll.h +++ b/components/hal/esp32p4/include/hal/clk_gate_ll.h @@ -66,8 +66,6 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph) return HP_SYS_CLKRST_REG_GPSPI2_MST_CLK_EN; case PERIPH_GPSPI3_MODULE: return HP_SYS_CLKRST_REG_GPSPI3_MST_CLK_EN; - case PERIPH_PARLIO_MODULE: - return HP_SYS_CLKRST_REG_PARLIO_RX_CLK_EN | HP_SYS_CLKRST_REG_PARLIO_TX_CLK_EN; case PERIPH_I3C_MODULE: return HP_SYS_CLKRST_REG_I3C_MST_CLK_EN; case PERIPH_CAM_MODULE: @@ -153,8 +151,6 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en return HP_SYS_CLKRST_REG_RST_EN_CAN2; case PERIPH_LEDC_MODULE: return HP_SYS_CLKRST_REG_RST_EN_LEDC; - case PERIPH_PARLIO_MODULE: - return HP_SYS_CLKRST_REG_RST_EN_PARLIO | HP_SYS_CLKRST_REG_RST_EN_PARLIO_RX | HP_SYS_CLKRST_REG_RST_EN_PARLIO_TX; case PERIPH_I2S0_MODULE: return HP_SYS_CLKRST_REG_RST_EN_I2S0_APB; case PERIPH_I2S1_MODULE: @@ -298,7 +294,6 @@ static inline uint32_t periph_ll_get_rst_en_reg(periph_module_t periph) case PERIPH_TWAI1_MODULE: case PERIPH_TWAI2_MODULE: case PERIPH_LEDC_MODULE: - case PERIPH_PARLIO_MODULE: case PERIPH_I2S0_MODULE: return HP_SYS_CLKRST_HP_RST_EN1_REG; case PERIPH_I2S1_MODULE: diff --git a/components/hal/esp32p4/include/hal/parlio_ll.h b/components/hal/esp32p4/include/hal/parlio_ll.h new file mode 100644 index 0000000000..21a543f995 --- /dev/null +++ b/components/hal/esp32p4/include/hal/parlio_ll.h @@ -0,0 +1,790 @@ +/* + * SPDX-FileCopyrightText: 2023 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/parlio_types.h" +#include "hal/hal_utils.h" +#include "soc/hp_sys_clkrst_struct.h" +#include "soc/parl_io_struct.h" +#include "soc/hp_sys_clkrst_struct.h" + +#define PARLIO_LL_RX_MAX_BYTES_PER_FRAME 0xFFFF +#define PARLIO_LL_RX_MAX_CLK_INT_DIV 0x100 +#define PARLIO_LL_RX_MAX_CLK_FRACT_DIV 0x100 +#define PARLIO_LL_RX_MAX_TIMEOUT 0xFFFF + +#define PARLIO_LL_TX_MAX_BITS_PER_FRAME 0x7FFFF +#define PARLIO_LL_TX_MAX_CLK_INT_DIV 0x100 +#define PARLIO_LL_TX_MAX_CLK_FRACT_DIV 0x100 + +#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 15 // TXD[15] can be used a valid signal +#define PARLIO_LL_TX_DATA_LINE_AS_CLK_GATE 15 // TXD[15] can be used as clock gate signal + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + PARLIO_LL_CLK_SRC_XTAL = PARLIO_CLK_SRC_XTAL, + PARLIO_LL_CLK_SRC_PLL_F160M = PARLIO_CLK_SRC_PLL_F160M, + PARLIO_LL_CLK_SRC_RC_FAST = PARLIO_CLK_SRC_RC_FAST, + PARLIO_LL_CLK_SRC_PAD = PARLIO_CLK_SRC_EXTERNAL, // clock source from GPIO pad +} parlio_ll_clock_source_t; + +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; + HP_SYS_CLKRST.soc_clk_ctrl2.reg_parlio_apb_clk_en = enable; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define parlio_ll_enable_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; parlio_ll_enable_bus_clock(__VA_ARGS__) + +/** + * @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; + HP_SYS_CLKRST.hp_rst_en2.reg_rst_en_parlio = 1; + HP_SYS_CLKRST.hp_rst_en2.reg_rst_en_parlio = 0; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define parlio_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; parlio_ll_reset_register(__VA_ARGS__) + +///////////////////////////////////////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_ll_clock_source_t src) +{ + (void)dev; + uint32_t clk_sel = 0; + switch (src) { + case PARLIO_LL_CLK_SRC_XTAL: + clk_sel = 0; + break; + case PARLIO_LL_CLK_SRC_RC_FAST: + clk_sel = 1; + break; + case PARLIO_LL_CLK_SRC_PLL_F160M: + clk_sel = 2; + break; + case PARLIO_LL_CLK_SRC_PAD: + clk_sel = 3; + break; + + default: // unsupported clock source + HAL_ASSERT(false); + break; + } + HP_SYS_CLKRST.peri_clk_ctrl117.reg_parlio_rx_clk_src_sel = clk_sel; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define parlio_ll_rx_set_clock_source(...) (void)__DECLARE_RCC_ATOMIC_ENV; parlio_ll_rx_set_clock_source(__VA_ARGS__) + +/** + * @brief Get the clock source for the RX unit + * + * @param dev Parallel IO register base address + * @return + * parlio_clock_source_t RX core clock source + */ +static inline parlio_ll_clock_source_t parlio_ll_rx_get_clock_source(parl_io_dev_t *dev) +{ + (void)dev; + uint32_t clk_sel = HP_SYS_CLKRST.peri_clk_ctrl117.reg_parlio_rx_clk_src_sel; + switch (clk_sel) { + case 0: + return PARLIO_LL_CLK_SRC_XTAL; + case 1: + return PARLIO_LL_CLK_SRC_RC_FAST; + case 2: + return PARLIO_LL_CLK_SRC_PLL_F160M; + case 3: + return PARLIO_LL_CLK_SRC_PAD; + default: // unsupported clock source + HAL_ASSERT(false); + break; + } + return PARLIO_LL_CLK_SRC_XTAL; +} + + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define parlio_ll_rx_get_clock_source(...) (void)__DECLARE_RCC_ATOMIC_ENV; parlio_ll_rx_get_clock_source(__VA_ARGS__) + +/** + * @brief Set the clock divider for the RX unit + * + * @param dev Parallel IO register base address + * @param clk_div Clock division with integral and decimal part + */ +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(HP_SYS_CLKRST.peri_clk_ctrl117, reg_parlio_rx_clk_div_num, clk_div->integer - 1); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl118, reg_parlio_rx_clk_div_denominator, clk_div->denominator); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl118, reg_parlio_rx_clk_div_numerator, clk_div->numerator); +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define parlio_ll_rx_set_clock_div(...) (void)__DECLARE_RCC_ATOMIC_ENV; parlio_ll_rx_set_clock_div(__VA_ARGS__) + +/** + * @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; + HP_SYS_CLKRST.hp_rst_en2.reg_rst_en_parlio_rx = 1; + HP_SYS_CLKRST.hp_rst_en2.reg_rst_en_parlio_rx = 0; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define parlio_ll_rx_reset_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; parlio_ll_rx_reset_clock(__VA_ARGS__) + +/** + * @brief Enable the RX unit Core clock domain + * + * @param dev Parallel IO register base address + * @param en True to enable, False to disable + */ +static inline void parlio_ll_rx_enable_clock(parl_io_dev_t *dev, bool en) +{ + (void)dev; + HP_SYS_CLKRST.peri_clk_ctrl117.reg_parlio_rx_clk_en = en; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define parlio_ll_rx_enable_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; parlio_ll_rx_enable_clock(__VA_ARGS__) + +/** + * @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->rx_genrl_cfg.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 + */ +static inline void parlio_ll_rx_start(parl_io_dev_t *dev, bool en) +{ + dev->rx_start_cfg.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->rx_data_cfg.rx_bitlen = bitlen; +} + +/** + * @brief Set the sub mode of the level controlled receive mode + * + * @param dev Parallel IO register base address + * @param active_level Level of the external enable signal, true for active high, false for active low + */ +__attribute__((always_inline)) +static inline void parlio_ll_rx_set_level_recv_mode(parl_io_dev_t *dev, bool active_level) +{ + dev->rx_mode_cfg.rx_smp_mode_sel = 0; + dev->rx_mode_cfg.rx_ext_en_inv = !active_level; // 0: active low, 1: active high +} + +/** + * @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->rx_mode_cfg.rx_smp_mode_sel = 1; + dev->rx_mode_cfg.rx_pulse_submode_sel = submode; + dev->rx_mode_cfg.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->rx_mode_cfg.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->rx_mode_cfg.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) +{ + dev->rx_clk_cfg.rx_clk_i_inv = edge; + dev->rx_clk_cfg.rx_clk_o_inv = edge; +} + +/** + * @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->rx_data_cfg.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 16: + width_sel = 4; + break; + 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->rx_data_cfg.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->fifo_cfg.rx_fifo_srst = 1; + dev->fifo_cfg.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->rx_mode_cfg.rx_ext_en_sel = line_num; +} + +/** + * @brief Wether 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->rx_genrl_cfg.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->rx_genrl_cfg.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->rx_genrl_cfg, 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->reg_update.rx_reg_update = 1; + while (dev->reg_update.rx_reg_update); +} + +///////////////////////////////////TX Unit/////////////////////////////////////// + +/** + * @brief Set the clock source for the TX unit + * + * @param dev Parallel IO register base address + * @param src Clock source + */ +static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_ll_clock_source_t src) +{ + (void)dev; + uint32_t clk_sel = 0; + switch (src) { + case PARLIO_LL_CLK_SRC_XTAL: + clk_sel = 0; + break; + case PARLIO_LL_CLK_SRC_RC_FAST: + clk_sel = 1; + break; + case PARLIO_LL_CLK_SRC_PLL_F160M: + clk_sel = 2; + break; + case PARLIO_LL_CLK_SRC_PAD: + clk_sel = 3; + break; + + default: // unsupported clock source + HAL_ASSERT(false); + break; + } + HP_SYS_CLKRST.peri_clk_ctrl118.reg_parlio_tx_clk_src_sel = clk_sel; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define parlio_ll_tx_set_clock_source(...) (void)__DECLARE_RCC_ATOMIC_ENV; parlio_ll_tx_set_clock_source(__VA_ARGS__) + +/** + * @brief Get the clock source for the TX unit + * + * @param dev Parallel IO register base address + * @return + * parlio_clock_source_t TX core clock source + */ +static inline parlio_ll_clock_source_t parlio_ll_tx_get_clock_source(parl_io_dev_t *dev) +{ + (void)dev; + uint32_t clk_sel = HP_SYS_CLKRST.peri_clk_ctrl118.reg_parlio_tx_clk_src_sel; + switch (clk_sel) { + case 0: + return PARLIO_LL_CLK_SRC_XTAL; + case 1: + return PARLIO_LL_CLK_SRC_RC_FAST; + case 2: + return PARLIO_LL_CLK_SRC_PLL_F160M; + case 3: + return PARLIO_LL_CLK_SRC_PAD; + default: // unsupported clock source + HAL_ASSERT(false); + break; + } + return PARLIO_LL_CLK_SRC_XTAL; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define parlio_ll_tx_get_clock_source(...) (void)__DECLARE_RCC_ATOMIC_ENV; parlio_ll_tx_get_clock_source(__VA_ARGS__) + +/** + * @brief Set the clock divider for the TX unit + * + * @param dev Parallel IO register base address + * @param clk_div Clock division with integral and decimal part + */ +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(HP_SYS_CLKRST.peri_clk_ctrl118, reg_parlio_tx_clk_div_num, clk_div->integer - 1); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl119, reg_parlio_tx_clk_div_denominator, clk_div->denominator); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl119, reg_parlio_tx_clk_div_numerator, clk_div->numerator); +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define parlio_ll_tx_set_clock_source(...) (void)__DECLARE_RCC_ATOMIC_ENV; parlio_ll_tx_set_clock_source(__VA_ARGS__) + +/** + * @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; + HP_SYS_CLKRST.hp_rst_en2.reg_rst_en_parlio_tx = 1; + HP_SYS_CLKRST.hp_rst_en2.reg_rst_en_parlio_tx = 0; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define parlio_ll_tx_reset_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; parlio_ll_tx_reset_clock(__VA_ARGS__) + +/** + * @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; + HP_SYS_CLKRST.peri_clk_ctrl118.reg_parlio_tx_clk_en = en; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define parlio_ll_tx_enable_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; parlio_ll_tx_enable_clock(__VA_ARGS__) + +/** + * @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->tx_data_cfg.tx_bitlen = bitlen; +} + +/** + * @brief Set the condition to generate the TX EOF event + * + * @param dev Parallel IO register base address + * @param cond TX EOF condition + */ +static inline void parlio_ll_tx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_tx_eof_cond_t cond) +{ + dev->tx_genrl_cfg.tx_eof_gen_sel = cond; +} + +/** + * @brief Wether 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->tx_genrl_cfg.tx_gating_en = en; +} + +/** + * @brief Start TX unit to transmit data + * + * @param dev Parallel IO register base address + * @param en True to start, False to stop + */ +__attribute__((always_inline)) +static inline void parlio_ll_tx_start(parl_io_dev_t *dev, bool en) +{ + dev->tx_start_cfg.tx_start = en; +} + +/** + * @brief Whether to treat the MSB of TXD as the valid signal + * + * @note If enabled, TXD[15] 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->tx_genrl_cfg.tx_valid_output_en = en; +} + +/** + * @brief Set the sample clock edge + * + * @param dev Parallel IO register base address + * @param edge Sample clock edge + */ +static inline void parlio_ll_tx_set_sample_clock_edge(parl_io_dev_t *dev, parlio_sample_edge_t edge) +{ + dev->tx_clk_cfg.tx_clk_i_inv = edge; + dev->tx_clk_cfg.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->tx_data_cfg.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) { + // TODO: check this field (IDF-8284) + case 16: + width_sel = 4; + break; + 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->tx_data_cfg.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->fifo_cfg.tx_fifo_srst = 1; + dev->fifo_cfg.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) +{ + dev->tx_genrl_cfg.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->st.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->int_ena.val |= mask; + } else { + dev->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->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->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->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->int_st; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/parlio_hal.c b/components/hal/parlio_hal.c index d443008829..af8b9bb7c6 100644 --- a/components/hal/parlio_hal.c +++ b/components/hal/parlio_hal.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include "hal/parlio_hal.h" #include "hal/parlio_ll.h" diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 050a05c4af..52c495ddd6 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -35,6 +35,10 @@ config SOC_ETM_SUPPORTED bool default y +config SOC_PARLIO_SUPPORTED + bool + default y + config SOC_ASYNC_MEMCPY_SUPPORTED bool default y @@ -651,7 +655,7 @@ config SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH int default 16 -config SOC_PARLIO_TX_RX_SHARE_INTERRUPT +config SOC_PARLIO_TX_SIZE_BY_DMA bool default y diff --git a/components/soc/esp32p4/include/soc/clk_tree_defs.h b/components/soc/esp32p4/include/soc/clk_tree_defs.h index 8e3e34bc9c..a8bd52ee95 100644 --- a/components/soc/esp32p4/include/soc/clk_tree_defs.h +++ b/components/soc/esp32p4/include/soc/clk_tree_defs.h @@ -448,6 +448,26 @@ typedef enum { //////////////////////////////////////////////////PARLIO//////////////////////////////////////////////////////////////// +/** + * @brief Array initializer for all supported clock sources of PARLIO + */ +#define SOC_PARLIO_CLKS {SOC_MOD_CLK_XTAL, SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_RC_FAST, PARLIO_CLK_SRC_EXTERNAL} + +/** + * @brief PARLIO clock source + */ +typedef enum { + PARLIO_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + PARLIO_CLK_SRC_PLL_F160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M 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 */ +#if SOC_CLK_TREE_SUPPORTED + PARLIO_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default clock choice */ +#else + PARLIO_CLK_SRC_DEFAULT = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the default clock choice */ +#endif +} soc_periph_parlio_clk_src_t; + #ifdef __cplusplus } #endif diff --git a/components/soc/esp32p4/include/soc/parl_io_struct.h b/components/soc/esp32p4/include/soc/parl_io_struct.h index 7c1693b9bc..aea2cb383b 100644 --- a/components/soc/esp32p4/include/soc/parl_io_struct.h +++ b/components/soc/esp32p4/include/soc/parl_io_struct.h @@ -498,6 +498,7 @@ typedef struct parl_io_dev_t { volatile parl_io_version_reg_t version; } parl_io_dev_t; +extern parl_io_dev_t PARL_IO; #ifndef __cplusplus _Static_assert(sizeof(parl_io_dev_t) == 0x400, "Invalid size of parl_io_dev_t structure"); diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 0ae4f43de6..a9a1dcb64a 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -37,7 +37,7 @@ #define SOC_MCPWM_SUPPORTED 1 // #define SOC_TWAI_SUPPORTED 1 //TODO: IDF-7470 #define SOC_ETM_SUPPORTED 1 -// #define SOC_PARLIO_SUPPORTED 1 //TODO: IDF-7471, TODO: IDF-7472 +#define SOC_PARLIO_SUPPORTED 1 //TODO: IDF-7471 #define SOC_ASYNC_MEMCPY_SUPPORTED 1 // disable usb serial jtag for esp32p4, current image does not support // #define SOC_USB_SERIAL_JTAG_SUPPORTED 1 //TODO: IDF-7496 @@ -317,7 +317,7 @@ #define SOC_PARLIO_RX_UNITS_PER_GROUP 1U /*!< number of RX units in each group */ #define SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH 16 /*!< Number of data lines of the TX unit */ #define SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH 16 /*!< Number of data lines of the RX unit */ -#define SOC_PARLIO_TX_RX_SHARE_INTERRUPT 1 /*!< TX and RX unit share the same interrupt source number */ +#define SOC_PARLIO_TX_SIZE_BY_DMA 1 /*!< Transaction length is controlled by DMA instead of indicated by register */ /*--------------------------- MPI CAPS ---------------------------------------*/ #define SOC_MPI_MEM_BLOCKS_NUM (4) diff --git a/components/soc/esp32p4/parlio_periph.c b/components/soc/esp32p4/parlio_periph.c index 827c614415..5dcafc7206 100644 --- a/components/soc/esp32p4/parlio_periph.c +++ b/components/soc/esp32p4/parlio_periph.c @@ -8,5 +8,59 @@ #include "soc/gpio_sig_map.h" const parlio_signal_conn_t parlio_periph_signals = { - + .groups = { + [0] = { + .module = PERIPH_PARLIO_MODULE, + .tx_irq_id = ETS_HP_PARLIO_TX_INTR_SOURCE, + .rx_irq_id = ETS_HP_PARLIO_RX_INTR_SOURCE, + .tx_units = { + [0] = { + .data_sigs = { + PARLIO_TX_DATA0_PAD_OUT_IDX, + PARLIO_TX_DATA1_PAD_OUT_IDX, + PARLIO_TX_DATA2_PAD_OUT_IDX, + PARLIO_TX_DATA3_PAD_OUT_IDX, + PARLIO_TX_DATA4_PAD_OUT_IDX, + PARLIO_TX_DATA5_PAD_OUT_IDX, + PARLIO_TX_DATA6_PAD_OUT_IDX, + PARLIO_TX_DATA7_PAD_OUT_IDX, + PARLIO_TX_DATA8_PAD_OUT_IDX, + PARLIO_TX_DATA9_PAD_OUT_IDX, + PARLIO_TX_DATA10_PAD_OUT_IDX, + PARLIO_TX_DATA11_PAD_OUT_IDX, + PARLIO_TX_DATA12_PAD_OUT_IDX, + PARLIO_TX_DATA13_PAD_OUT_IDX, + PARLIO_TX_DATA14_PAD_OUT_IDX, + PARLIO_TX_DATA15_PAD_OUT_IDX, + }, + .clk_out_sig = PARLIO_TX_CLK_PAD_OUT_IDX, + .clk_in_sig = PARLIO_TX_CLK_PAD_IN_IDX, + } + }, + .rx_units = { + [0] = { + .data_sigs = { + PARLIO_RX_DATA0_PAD_IN_IDX, + PARLIO_RX_DATA1_PAD_IN_IDX, + PARLIO_RX_DATA2_PAD_IN_IDX, + PARLIO_RX_DATA3_PAD_IN_IDX, + PARLIO_RX_DATA4_PAD_IN_IDX, + PARLIO_RX_DATA5_PAD_IN_IDX, + PARLIO_RX_DATA6_PAD_IN_IDX, + PARLIO_RX_DATA7_PAD_IN_IDX, + PARLIO_RX_DATA8_PAD_IN_IDX, + PARLIO_RX_DATA9_PAD_IN_IDX, + PARLIO_RX_DATA10_PAD_IN_IDX, + PARLIO_RX_DATA11_PAD_IN_IDX, + PARLIO_RX_DATA12_PAD_IN_IDX, + PARLIO_RX_DATA13_PAD_IN_IDX, + PARLIO_RX_DATA14_PAD_IN_IDX, + PARLIO_RX_DATA15_PAD_IN_IDX, + }, + .clk_out_sig = PARLIO_RX_CLK_PAD_OUT_IDX, + .clk_in_sig = PARLIO_RX_CLK_PAD_IN_IDX, + } + } + }, + }, }; diff --git a/examples/peripherals/.build-test-rules.yml b/examples/peripherals/.build-test-rules.yml index 02974dcc02..72196d2c6e 100644 --- a/examples/peripherals/.build-test-rules.yml +++ b/examples/peripherals/.build-test-rules.yml @@ -132,6 +132,10 @@ examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop: examples/peripherals/parlio: disable: - if: SOC_PARLIO_SUPPORTED != 1 + disable_test: + - if: IDF_TARGET == "esp32p4" + temporary: true + reason: lack of runner examples/peripherals/parlio/simple_rgb_led_matrix: disable: