feat(parlio_rx): implement parallel io rx driver

This commit is contained in:
laokaiyao 2023-05-22 18:51:13 +08:00
parent 188864bcbd
commit 04d267b023
18 changed files with 1419 additions and 126 deletions

View File

@ -36,7 +36,9 @@ endif()
# Parallel IO related source files
if(CONFIG_SOC_PARLIO_SUPPORTED)
list(APPEND srcs "parlio/parlio_common.c" "parlio/parlio_tx.c")
list(APPEND srcs "parlio/parlio_common.c"
"parlio/parlio_tx.c"
"parlio/parlio_rx.c")
endif()
# GPTimer legacy driver

View File

@ -6,6 +6,7 @@
#pragma once
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "esp_err.h"
@ -19,80 +20,282 @@ extern "C" {
* @brief Parallel IO RX unit configuration
*/
typedef struct {
parlio_clock_source_t clk_src; /*!< Parallel IO clock source */
size_t data_width; /*!< Parallel IO data width */
int data_gpio_nums[PARLIO_RX_UNIT_MAX_DATA_WIDTH]; /*!< Parallel IO data GPIO numbers, set to `-1` if it's not used */
int clk_gpio_num; /*!< Parallel IO clock GPIO number, set to `-1` if the clock is not clocked from gpio pad*/
uint32_t clk_freq_hz; /*!< Parallel IO clock frequency in Hz */
size_t trans_queue_depth; /*!< Depth of internal transaction queue */
size_t max_recv_size; /*!< Maximum receive size in one transaction, in bytes. This decides the number of DMA nodes will be used for each transaction */
size_t data_width; /*!< Parallel IO data width, can set to 1/2/4/8/..., but can't bigger than PARLIO_RX_UNIT_MAX_DATA_WIDTH */
parlio_clock_source_t clk_src; /*!< Parallel IO clock source */
uint32_t clk_freq_hz; /*!< The source clock frequency for external when the clock source is selected as PARLIO_CLK_SRC_EXTERNAL;
The expected output clock frequency when the clock source is from internal */
gpio_num_t clk_gpio_num; /*!< The input or output clock pin. If the clock source is input from external,
the clock gpio must be set, otherwise set `-1` can disable the clock output if not needed */
gpio_num_t valid_gpio_num; /*!< GPIO number of the valid signal. The signal on this pin is used to indicate whether the data on the data lines are valid.
Only takes effect when using level or pulse delimiter, set to `-1` if only use the soft delimiter */
gpio_num_t data_gpio_nums[PARLIO_RX_UNIT_MAX_DATA_WIDTH]; /*!< Parallel IO data GPIO numbers, set to `-1` if it's not used,
The driver will take [0 .. (data_width - 1)] as the data pins */
struct {
uint32_t free_clk : 1; /*!< Whether the input external clock is a free-running clock. A free-running clock will always keep running (e.g. I2S bclk),
a non-free-running clock will start when there are data transporting and stop when the bus idle (e.g. SPI).
This flag only takes effect when the clock source is selected as PARLIO_CLK_SRC_EXTERNAL */
uint32_t clk_gate_en : 1; /*!< Enable RX clock gating, only available when the clock direction is output(not supported on ESP32-C6)
the output clock will be controlled by the valid gpio,
i.e. high level of valid gpio to enable the clock output, low to disable */
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */
uint32_t io_no_init: 1; /*!< Set to skip initializing the GPIO, but only attach the pralio rx signals to those GPIOs via IO Matrix.
So that the signals that have attached to those GPIO won't be overwritten. Mainly used for self communication or self monitoring */
} flags;
} parlio_rx_unit_config_t;
/**
* @brief Create a Parallel IO RX unit
*
* @param[in] config Parallel IO RX unit configuration
* @param[out] ret_unit Returned Parallel IO RX unit handle
* @return esp_err_t
*/
esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_unit_handle_t *ret_unit);
esp_err_t parlio_del_rx_unit(parlio_rx_unit_handle_t unit);
esp_err_t parlio_rx_unit_enable(parlio_rx_unit_handle_t unit);
esp_err_t parlio_rx_unit_disable(parlio_rx_unit_handle_t unit);
/**
* @brief Delete a Parallel IO RX unit
*
* @param[in] rx_unit Parallel IO RX unit handle that created by `parlio_new_rx_unit`
* @return esp_err_t
*/
esp_err_t parlio_del_rx_unit(parlio_rx_unit_handle_t rx_unit);
/**
* @brief Configuration of level delimiter
*/
typedef struct {
uint32_t data_line_as_enable; /*!< Which IO data line will be used as data enable/valid signal */
uint32_t valid_sig_line_id; /*!< The data line id of valid/enable signal.
The selected data line will be used as the valid/enable signal (i.e. level signal) in this delimiter.
As the line of valid/enable signal is shared with the data line, this line_id will be conflict
with the data line if set the id within 'data_width',
therefore the range is (data_width, PARLIO_RX_UNIT_MAX_DATA_WIDTH]. */
parlio_sample_edge_t sample_edge; /*!< Parallel IO sample edge */
parlio_bit_pack_order_t bit_pack_order; /*!< Set how we pack the bits into one bytes */
uint32_t eof_data_len; /*!< Set the data length to trigger the End Of Frame (EOF, i.e. transaction done)
interrupt, if the data length is set to `0`, that mean the EOF will only triggers
when the enable signal inactivated */
uint32_t timeout_ticks; /*!< The number of source clock ticks to trigger timeout interrupt. Set 0 to disable the receive timeout interrupt
The timeout counter starts when the valid/enable signal is invalid/disabled. */
struct {
uint32_t active_level: 1; /*!< Which level indicates the validation of the transmitting data */
} flags; /*!< Extra flags */
uint32_t active_level: 1; /*!< Which level indicates the validation of the transmitting data */
} flags; /*!< Extra flags */
} parlio_rx_level_delimiter_config_t;
/**
* @brief
* @brief Create a level delimiter
*
* @note The enable signal must be aligned with the valid data.
* @note There're at most `SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1` IO pins left for RXD
*
* @param config
* @param ret_delimiter
* @param[in] config Level delimiter configuration
* @param[out] ret_delimiter Returned delimiter handle
* @return esp_err_t
*/
esp_err_t parlio_new_rx_level_delimiter(const parlio_rx_level_delimiter_config_t *config, parlio_rx_delimiter_handle_t *ret_delimiter);
esp_err_t parlio_new_rx_level_delimiter(const parlio_rx_level_delimiter_config_t *config,
parlio_rx_delimiter_handle_t *ret_delimiter);
/**
* @brief Configuration of pulse delimiter
*/
typedef struct {
uint32_t data_line_as_enable; /*!< Which IO data line will be used as data enable signal */
uint32_t valid_sig_line_id; /*!< The data line id of valid/enable signal.
The selected data line will be used as the valid/enable signal (i.e. pulse signal) in this delimiter.
As the line of valid/enable signal is shared with the data line, this line_id will be conflict
with the data line if set the id within 'data_width',
therefore the range is (data_width, PARLIO_RX_UNIT_MAX_DATA_WIDTH]. */
parlio_sample_edge_t sample_edge; /*!< Parallel IO sample edge */
parlio_bit_pack_order_t bit_pack_order; /*!< Set how we pack the bits into one bytes */
uint32_t eof_data_len; /*!< Set the data length to trigger the End Of Frame (EOF, i.e. transaction done)
interrupt, if the data length is set to `0`, that mean the EOF will only triggers
when the end pulse detected, please ensure there is an end pulse for a frame and
`has_end_pulse` flag is set */
uint32_t timeout_ticks; /*!< The number of source clock ticks to trigger timeout interrupt. Set 0 to disable the receive timeout interrupt
The timeout counter starts when the valid/enable signal is invalid/disabled. */
struct {
uint32_t active_level: 1; /*!< On which level the pulse is considered active */
uint32_t start_bit_included: 1; /*!< Whether data bit is included in the start pulse */
uint32_t has_end_pulse: 1; /*!< Whether there's an end pulse to terminate the transaction,
if no, the transaction will be terminated by user configured transcation length */
uint32_t end_bit_included: 1; /*!< Whether data bit is included in the end pulse, only valid when `has_end_pulse` is true */
uint32_t gen_eof_by_end_pulse: 1; /*!< Whether the DMA EOF event is generated by the end pulse instead of data length,
only valid when `end_bit_included` is true */
} flags; /*!< Extra flags */
uint32_t start_bit_included: 1; /*!< Whether data bit is included in the start pulse */
uint32_t end_bit_included: 1; /*!< Whether data bit is included in the end pulse, only valid when `has_end_pulse` is true */
uint32_t has_end_pulse: 1; /*!< Whether there's an end pulse to terminate the transaction,
if no, the transaction will be terminated by user configured transcation length */
uint32_t pulse_invert: 1; /*!< Whether to invert the pulse */
} flags; /*!< Extra flags */
} parlio_rx_pulse_delimiter_config_t;
/**
* @brief
* @brief Create a pulse delimiter
*
* @note There're at most `SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1` IO pins left for RXD
*
* @param config
* @param ret_delimiter
* @param[in] config Pulse delimiter configuration
* @param[out] ret_delimiter Returned delimiter handle
* @return esp_err_t
*/
esp_err_t parlio_new_rx_pulse_delimiter(const parlio_rx_pulse_delimiter_config_t *config, parlio_rx_delimiter_handle_t *ret_delimiter);
esp_err_t parlio_new_rx_pulse_delimiter(const parlio_rx_pulse_delimiter_config_t *config,
parlio_rx_delimiter_handle_t *ret_delimiter);
/**
* @brief Configuration of soft delimiter
*/
typedef struct {
parlio_sample_edge_t sample_edge; /*!< Parallel IO sample edge */
parlio_bit_pack_order_t bit_pack_order; /*!< Set how we pack the bits into one bytes, set 1 to pack the bits into a byte from LSB,
otherwise from MSB */
uint32_t eof_data_len; /*!< Set the data length to trigger the End Of Frame (EOF, i.e. transaction done)
interrupt, if the data length is set to `0`, that mean the EOF will only triggers
when the end pulse detected, please ensure there is an end pulse for a frame and
`has_end_pulse` flag is set */
uint32_t timeout_ticks; /*!< The number of APB clock ticks to trigger timeout interrupt. Set 0 to disable the receive timeout interrupt */
} parlio_rx_soft_delimiter_config_t;
esp_err_t parlio_new_rx_soft_delimiter(const parlio_rx_soft_delimiter_config_t *config, parlio_rx_delimiter_handle_t *ret_delimiter);
esp_err_t parlio_rx_soft_delimiter_begin_end(parlio_rx_delimiter_handle_t delimiter, bool begin_end);
/**
* @brief Create a pulse delimiter
*
* @param[in] config Soft delimiter configuration
* @param[out] ret_delimiter Returned delimiter handle
* @return esp_err_t
*/
esp_err_t parlio_new_rx_soft_delimiter(const parlio_rx_soft_delimiter_config_t *config,
parlio_rx_delimiter_handle_t *ret_delimiter);
/**
* @brief Start/stop the soft delimiter
* @note Soft delimiter need to start or stop manually because it has no validating/enabling signal to indicate the data has started or stopped
*
* @param[in] rx_unit Parallel IO RX unit handle that created by `parlio_new_rx_unit`
* @param[in] delimiter Delimiter handle
* @param[in] start_stop Set true to start, set false to stop
* @return esp_err_t
*/
esp_err_t parlio_rx_soft_delimiter_start_stop(parlio_rx_unit_handle_t rx_unit, parlio_rx_delimiter_handle_t delimiter, bool start_stop);
/**
* @brief Generic Selection of the delimiter create functions
* @note This is a helper function for creating the delimiter generically,
* input different type of configuration to create a corresponding delimiter
*
* @param[in] config The configuration of the delimiter, whose type decides the delimiter type that created,
* Can be `parlio_rx_level_delimiter_config_t`, `parlio_rx_pulse_delimiter_config_t` or `parlio_rx_soft_delimiter_config_t`
* @param[out] ret_delimiter Returned delimiter handle
*/
#define parlio_new_rx_delimiter(config, ret_delimiter) _Generic((config), \
parlio_rx_level_delimiter_config_t* : parlio_new_rx_level_delimiter, \
parlio_rx_pulse_delimiter_config_t* : parlio_new_rx_pulse_delimiter, \
parlio_rx_soft_delimiter_config_t* : parlio_new_rx_soft_delimiter, \
default: parlio_new_rx_soft_delimiter) (config, ret_delimiter)
/**
* @brief Delete the delimiter
* @note To delete the delimiter safely, please delete it after disable all the RX units
*
* @param[in] delimiter Delimiter handle
* @return esp_err_t
*/
esp_err_t parlio_del_rx_delimiter(parlio_rx_delimiter_handle_t delimiter);
/**
* @brief Enable the Parallel IO RX unit
*
* @param[in] rx_unit Parallel IO RX unit handle that created by `parlio_new_rx_unit`
* @param[in] reset_queue Whether to reset the receiving queue.
* If set to false, the legacy receive transactions in the queue are still available,
* If set to true, the legacy receive transactions in the queue are dropped.
* @return esp_err_t
*/
esp_err_t parlio_rx_unit_enable(parlio_rx_unit_handle_t rx_unit, bool reset_queue);
/**
* @brief Disable the Parallel IO TX unit
*
* @param[in] rx_unit Parallel IO RX unit handle that created by `parlio_new_rx_unit`
* @return esp_err_t
*/
esp_err_t parlio_rx_unit_disable(parlio_rx_unit_handle_t rx_unit);
/**
* @brief Configuration of a receive transaction
*/
typedef struct {
parlio_sample_edge_t sample_edge; /*!< Parallel IO sample edge */
parlio_bit_pack_endian_t bit_pack_endian; /*!< Set how we pack the bits into one bytes */
parlio_rx_delimiter_handle_t delimiter; /*!< Specify the frame delimiter, either from software or external signal (level or pulse) */
uint32_t timeout_us; /*!< Timeout in us, 0 means no timeout */
parlio_rx_delimiter_handle_t delimiter; /*!< The delimiter of this receiving transaction */
struct {
} flags; /*!< Extra flags */
uint32_t is_infinite: 1; /*!< Whether this is an infinite transaction that supposed to receive continuously */
uint32_t indirect_mount: 1;/*!< This flag only take effect when `is_infinite` is enabled.
* Enable this flag, the DMA descriptor will mount to an internal DMA buffer instead,
* The data will be copy to the payload in every interrupt. So that to guarantee the payload buffer
* is valid during the `on_receive_done` callback.
* Either `is_infinite` or `indirect_mount` is disabled,
* the finite payload will be mounted to the DMA descriptor directly.
* By default, the receive payload will be mounted to the DMA descriptor directly,
*/
} flags; /*!< Extra flags */
} parlio_receive_config_t;
esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit, void *buffer, size_t buffer_size, const parlio_receive_config_t *config);
/**
* @brief Receive data by Parallel IO RX unit
* @note The receive transaction will start immediately when there is not other transaction on receiving,
* Otherwise it will be sent to the transaction queue to wait for the bus.
*
* @param[in] rx_unit Parallel IO RX unit handle that created by `parlio_new_rx_unit`
* @param[in] payload The payload buffer pointer
* @param[in] payload_size The size of the payload buffer, in bytes.
* @param[in] recv_cfg The configuration of this receive transaction
* @return esp_err_t
*/
esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit,
void *payload,
size_t payload_size,
const parlio_receive_config_t* recv_cfg);
/**
* @brief Wait for all pending RX transactions done
*
* @param[in] rx_unit Parallel IO RX unit handle that created by `parlio_new_rx_unit`
* @param[in] timeout_ms Timeout in milliseconds, `-1` means to wait forever (software timeout)
* @return esp_err_t
*/
esp_err_t parlio_rx_unit_wait_all_done(parlio_rx_unit_handle_t rx_unit, int timeout_ms);
/**
* @brief Event callback data
*/
typedef struct {
parlio_rx_delimiter_handle_t delimiter; /*!< The current delimiter of this receiving event */
void *data; /*!< The data buffer address that just finished receiving */
size_t size; /*!< The total size of the data buffer , in byte */
size_t recv_bytes; /*!< The number of received bytes in the data buffer */
} parlio_rx_event_data_t;
/**
* @brief The template of the Parallel IO RX callback function
*
* @param[in] rx_unit Parallel IO RX unit handle that given from ISR
* @param[in] edata The event data that given from ISR
* @param[in] user_data The user specified data that given while registering the callbacks
*
* @return
* - True: to awoke high priority tasks
* - False: not to awoke high priority tasks
*/
typedef bool (*parlio_rx_callback_t)(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_data);
/**
* @brief Parallel IO RX event callbacks
*/
typedef struct {
parlio_rx_callback_t on_partial_receive; /*!< Callback of received partial data */
parlio_rx_callback_t on_receive_done; /*!< Callback of receiving transaction done */
parlio_rx_callback_t on_timeout; /*!< Callback of hardware receiving timeout */
} parlio_rx_event_callbacks_t;
/**
* @brief Register event callbacks for Parallel IO RX unit
*
* @param[in] rx_unit Parallel IO RX unit handle that created by `parlio_new_rx_unit`
* @param[in] cbs Callbacks
* @param[in] user_data User specified data that will be transported to the callbacks
* @return esp_err_t
*/
esp_err_t parlio_rx_unit_register_event_callbacks(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_callbacks_t *cbs, void *user_data);
#ifdef __cplusplus
}

View File

@ -89,3 +89,52 @@ void parlio_release_group_handle(parlio_group_t *group)
ESP_LOGD(TAG, "del group(%d)", group_id);
}
}
esp_err_t parlio_register_unit_to_group(parlio_unit_base_handle_t unit)
{
parlio_group_t *group = NULL;
int unit_id = -1;
for (int i = 0; i < SOC_PARLIO_GROUPS; i++) {
group = parlio_acquire_group_handle(i);
parlio_unit_base_handle_t *group_unit = NULL;
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no memory for group (%d)", i);
portENTER_CRITICAL(&group->spinlock);
for (int j = 0; j < SOC_PARLIO_RX_UNITS_PER_GROUP; j++) {
group_unit = (unit->dir == PARLIO_DIR_TX) ? &group->tx_units[j] : &group->rx_units[j];
if (*group_unit == NULL) {
*group_unit = unit;
unit_id = j;
break;
}
}
portEXIT_CRITICAL(&group->spinlock);
if (unit_id < 0) {
/* didn't find a free unit slot in the group */
parlio_release_group_handle(group);
group = NULL;
} else {
unit->unit_id = unit_id;
unit->group = group;
break;
}
}
ESP_RETURN_ON_FALSE(unit_id >= 0, ESP_ERR_NOT_FOUND, TAG,
"no free %s unit", unit->dir == PARLIO_DIR_TX ? "tx" : "rx");
return ESP_OK;
}
void parlio_unregister_unit_from_group(parlio_unit_base_handle_t unit)
{
if (unit) {
parlio_group_t *group = unit->group;
portENTER_CRITICAL(&group->spinlock);
if (unit->dir == PARLIO_DIR_TX) {
group->tx_units[unit->unit_id] = NULL;
} else {
group->rx_units[unit->unit_id] = NULL;
}
portEXIT_CRITICAL(&group->spinlock);
/* the rx unit has a reference of the group, release it now */
parlio_release_group_handle(group);
}
}

View File

@ -84,6 +84,11 @@ enum {
PARLIO_TX_QUEUE_MAX,
};
typedef enum {
PARLIO_DIR_TX,
PARLIO_DIR_RX,
} parlio_dir_t;
typedef enum {
PARLIO_TX_FSM_INIT_WAIT,
PARLIO_TX_FSM_INIT,
@ -93,17 +98,34 @@ typedef enum {
PARLIO_TX_FSM_RUN,
} parlio_tx_fsm_t;
typedef struct parlio_unit_t *parlio_unit_base_handle_t;
typedef struct parlio_group_t {
int group_id; // group ID, index from 0
portMUX_TYPE spinlock; // to protect per-group register level concurrent access
parlio_hal_context_t hal; // hal layer for each group
parlio_tx_unit_handle_t tx_units[SOC_PARLIO_TX_UNITS_PER_GROUP]; // tx unit handles
int group_id; // group ID, index from 0
portMUX_TYPE spinlock; // to protect per-group register level concurrent access
parlio_hal_context_t hal; // hal layer for each group
parlio_unit_base_handle_t tx_units[SOC_PARLIO_TX_UNITS_PER_GROUP]; // tx unit handles
parlio_unit_base_handle_t rx_units[SOC_PARLIO_RX_UNITS_PER_GROUP]; // rx unit handles
} parlio_group_t;
/**
* @brief The common field of rx and tx unit structure
*
*/
struct parlio_unit_t {
int unit_id; // unit id
parlio_dir_t dir;
parlio_group_t *group; // group handle
};
parlio_group_t *parlio_acquire_group_handle(int group_id);
void parlio_release_group_handle(parlio_group_t *group);
esp_err_t parlio_register_unit_to_group(parlio_unit_base_handle_t unit);
void parlio_unregister_unit_from_group(parlio_unit_base_handle_t unit);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,904 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include <stdatomic.h>
#include <sys/cdefs.h>
#include <sys/param.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/idf_additions.h"
#if CONFIG_PARLIO_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "esp_log.h"
#include "esp_check.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_rom_gpio.h"
#include "esp_pm.h"
#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_rx.h"
#include "parlio_private.h"
#include "esp_memory_utils.h"
#include "esp_clk_tree.h"
#include "esp_attr.h"
#include "esp_private/gdma.h"
static const char *TAG = "parlio-rx";
/**
* @brief Parlio RX transaction
*/
typedef struct {
parlio_rx_delimiter_handle_t delimiter; /*!< Delimiter of this transaction */
void *payload; /*!< The payload of this transaction, will be mounted to DMA descriptor */
size_t size; /*!< The payload size in byte */
size_t recv_bytes; /*!< The received bytes of this transaction
will be reset when all data filled in the infinite transaction */
struct {
uint32_t infinite : 1; /*!< Whether this is an infinite transaction */
} flags;
} parlio_rx_transaction_t;
/**
* @brief Parlio RX unit resource management
*/
typedef struct parlio_rx_unit_t {
/* Unit general Resources */
int unit_id; /*!< unit id */
parlio_dir_t dir; /*!< unit direction */
parlio_group_t *group; /*!< group handle */
parlio_clock_source_t clk_src; /*!< clock source of the unit */
parlio_rx_unit_config_t cfg; /*!< basic configuration of the rx unit */
bool is_enabled; /*!< State flag that indicates whether the unit is enabled */
/* Mutex Lock */
SemaphoreHandle_t mutex; /*!< Mutex lock for concurrence safety,
* which should be acquired and released in a same function */
/* Power Management */
esp_pm_lock_handle_t pm_lock; /*!< power management lock */
#if CONFIG_PM_ENABLE
char pm_lock_name[PARLIO_PM_LOCK_NAME_LEN_MAX]; /*!< pm lock name */
#endif
/* Transaction Resources */
QueueHandle_t trans_que; /*!< Static transaction queue handle */
parlio_rx_transaction_t curr_trans; /*!< The current transaction */
SemaphoreHandle_t trans_sem; /*!< Binary semaphore to deliver transaction done signal,
* which can be acquired and released between different functions */
/* DMA Resources */
gdma_channel_handle_t dma_chan; /*!< DMA channel */
size_t max_recv_size; /*!< Maximum receive size for a normal transaction */
size_t desc_num; /*!< DMA descriptor number */
dma_descriptor_t *dma_descs; /*!< DMA descriptor array pointer */
dma_descriptor_t *curr_desc; /*!< The pointer of the current descriptor */
void *usr_recv_buf; /*!< The pointe to the user's receiving buffer */
/* Infinite transaction specific */
void *dma_buf; /*!< Additional internal DMA buffer only for infinite transactions */
/* Callback */
parlio_rx_event_callbacks_t cbs; /*!< The group of callback function pointers */
void *user_data; /*!< User data that supposed to be transported to the callback functions */
} parlio_rx_unit_t;
/**
* @brief Delimiter mode
*/
typedef enum {
PARLIO_RX_LEVEL_MODE, /*!< Delimit by the level of valid signal */
PARLIO_RX_PULSE_MODE, /*!< Delimit by the pulse of valid signal */
PARLIO_RX_SOFT_MODE, /*!< Delimit by the length of received data */
} parlio_rx_delimiter_mode_t;
/**
* @brief Pralio RX delimiter management
*/
typedef struct parlio_rx_delimiter_t {
parlio_rx_delimiter_mode_t mode; /*!< Delimiter mode */
bool under_using; /*!< Whether this delimiter is under using */
uint32_t valid_sig;
gpio_num_t valid_sig_line_id; /*!< The data line id for the valid signal */
parlio_sample_edge_t sample_edge; /*!< The sampling edge of the data */
parlio_bit_pack_order_t bit_pack_order; /*!< The order to pack the bit on the data line */
uint32_t eof_data_len; /*!< The length of the data to trigger the eof interrupt */
uint32_t timeout_ticks; /*!< The ticks of source clock that can trigger hardware timeout */
struct {
uint32_t active_level: 1; /*!< Which level indicates the validation of the transmitting data */
uint32_t start_bit_included: 1; /*!< Whether data bit is included in the start pulse */
uint32_t end_bit_included: 1; /*!< Whether data bit is included in the end pulse, only valid when `has_end_pulse` is true */
uint32_t has_end_pulse: 1; /*!< Whether there's an end pulse to terminate the transaction,
if no, the transaction will be terminated by user configured transcation length */
uint32_t pulse_invert: 1; /*!< Whether to invert the pulse */
} flags;
} parlio_rx_delimiter_t;
static IRAM_ATTR size_t s_parlio_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parlio_rx_transaction_t *trans)
{
dma_descriptor_t *p_desc = rx_unit->dma_descs;
/* Update the current transaction to the next one, and declare the delimiter is under using of the rx unit */
memcpy(&rx_unit->curr_trans, trans, sizeof(parlio_rx_transaction_t));
trans->delimiter->under_using = true;
uint32_t desc_num = trans->size / DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED;
uint32_t remain_num = trans->size % DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED;
/* If there are still data remained, need one more descriptor */
desc_num += remain_num ? 1 : 0;
if (trans->flags.infinite && desc_num < 2) {
/* At least 2 descriptors needed */
desc_num = 2;
}
size_t mount_size = 0;
size_t offset = 0;
for (int i = 0; i < desc_num; i++) {
size_t rest_size = trans->size - offset;
if (rest_size >= 2 * DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED) {
mount_size = trans->size / desc_num;
}
else if (rest_size <= DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED) {
mount_size = (desc_num == 2) && (i == 0) ? rest_size / 2 : rest_size;
}
else {
mount_size = rest_size / 2;
}
p_desc[i].buffer = (void *)((uint8_t *)trans->payload + offset);
p_desc[i].dw0.size = mount_size;
p_desc[i].dw0.length = mount_size;
p_desc[i].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
// Link the descriptor
if (i > 0) {
p_desc[i - 1].next = &p_desc[i];
}
offset += mount_size;
}
/* For infinite transaction, link the descriptor as a ring */
p_desc[desc_num - 1].next = trans->flags.infinite ? &p_desc[0] : NULL;
/* Reset the current DMA node */
rx_unit->curr_desc = p_desc;
return offset;
}
static IRAM_ATTR void s_parlio_set_delimiter_config(parlio_rx_unit_handle_t rx_unit, parlio_rx_delimiter_handle_t deli)
{
parlio_hal_context_t *hal = &(rx_unit->group->hal);
/* Set the clock sampling edge and the bit order */
parlio_ll_rx_set_sample_clock_edge(hal->regs, deli->sample_edge);
parlio_ll_rx_set_bit_pack_order(hal->regs, deli->bit_pack_order);
/* Set receive mode according to the delimiter */
switch (deli->mode) {
case PARLIO_RX_LEVEL_MODE:
/* Select the level receive mode */
parlio_ll_rx_set_level_recv_mode(hal->regs, deli->flags.active_level);
parlio_ll_rx_treat_data_line_as_en(hal->regs, deli->valid_sig_line_id);
break;
case PARLIO_RX_PULSE_MODE:
/* Select the pulse receive mode */
parlio_ll_rx_set_pulse_recv_mode(hal->regs, deli->flags.start_bit_included,
deli->flags.end_bit_included,
!deli->flags.has_end_pulse,
deli->flags.pulse_invert);
parlio_ll_rx_treat_data_line_as_en(hal->regs, deli->valid_sig_line_id);
break;
default:
/* Select the soft receive mode */
parlio_ll_rx_set_soft_recv_mode(hal->regs);
break;
}
/* Set EOF configuration */
if (deli->eof_data_len) {
/* If EOF data length specified, set the eof condition to data length and set data bytes */
parlio_ll_rx_set_recv_bit_len(hal->regs, deli->eof_data_len * 8);
parlio_ll_rx_set_eof_condition(hal->regs, PARLIO_LL_RX_EOF_COND_RX_FULL);
} else {
/* If EOF data length not specified, set the eof condition to the external enable signal */
parlio_ll_rx_set_eof_condition(hal->regs, PARLIO_LL_RX_EOF_COND_EN_INACTIVE);
}
/* Set timeout configuration */
if (deli->timeout_ticks) {
parlio_ll_rx_enable_timeout(hal->regs, true);
parlio_ll_rx_set_timeout_thres(hal->regs, deli->timeout_ticks);
} else {
parlio_ll_rx_enable_timeout(hal->regs, false);
}
/* Set the validation signal if the validation signal number is set for level or pulse delimiter */
if (deli->mode != PARLIO_RX_SOFT_MODE) {
esp_rom_gpio_connect_in_signal(rx_unit->cfg.valid_gpio_num, deli->valid_sig, false);
/* Update the valid_sig_line_num */
parlio_ll_rx_treat_data_line_as_en(hal->regs, deli->valid_sig_line_id);
}
/* Update/synchronize the new configurations */
parlio_ll_rx_update_config(hal->regs);
}
static esp_err_t s_parlio_rx_unit_set_gpio(parlio_rx_unit_handle_t rx_unit, const parlio_rx_unit_config_t *config)
{
int group_id = rx_unit->group->group_id;
int unit_id = rx_unit->unit_id;
gpio_config_t gpio_conf = {
.intr_type = GPIO_INTR_DISABLE,
.pull_down_en = false,
.pull_up_en = true,
};
if (config->clk_src == PARLIO_CLK_SRC_EXTERNAL) {
ESP_RETURN_ON_FALSE(config->clk_gpio_num >= 0, ESP_ERR_INVALID_ARG, TAG, "clk_gpio_num must be set while the clock input from external");
/* Connect the clock in signal to the GPIO matrix if it is set */
if (!config->flags.io_no_init) {
gpio_conf.mode = config->flags.io_loop_back ? GPIO_MODE_INPUT_OUTPUT : GPIO_MODE_INPUT;
gpio_conf.pin_bit_mask = BIT64(config->clk_gpio_num);
ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config clk in GPIO failed");
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->clk_gpio_num], PIN_FUNC_GPIO);
}
esp_rom_gpio_connect_in_signal(config->clk_gpio_num,
parlio_periph_signals.groups[group_id].rx_units[unit_id].clk_in_sig, false);
}
else if (config->clk_gpio_num >= 0) {
#if SOC_PARLIO_RX_CLK_SUPPORT_OUTPUT
gpio_conf.mode = GPIO_MODE_OUTPUT;
gpio_conf.pin_bit_mask = BIT64(config->clk_gpio_num);
ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config clk in GPIO failed");
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->clk_gpio_num], PIN_FUNC_GPIO);
esp_rom_gpio_connect_out_signal(config->clk_gpio_num,
parlio_periph_signals.groups[group_id].rx_units[unit_id].clk_out_sig, false, false);
#else
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "this target not support to output the clock");
#endif // SOC_PARLIO_RX_CLK_SUPPORT_OUTPUT
}
gpio_conf.mode = GPIO_MODE_INPUT;
if (config->valid_gpio_num >= 0) {
if (!config->flags.io_no_init) {
gpio_conf.pin_bit_mask = BIT64(config->valid_gpio_num);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->valid_gpio_num], PIN_FUNC_GPIO);
}
ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config data GPIO failed");
/* Not connect the signal here, the signal is lazy connected until the delimiter takes effect */
}
for (int i = 0; i < config->data_width; i++) {
/* Loop the data_gpio_nums to connect data and valid signals via GPIO matrix */
if (config->data_gpio_nums[i] >= 0) {
if (!config->flags.io_no_init) {
gpio_conf.pin_bit_mask = BIT64(config->data_gpio_nums[i]);
ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config data GPIO failed");
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->data_gpio_nums[i]], PIN_FUNC_GPIO);
}
esp_rom_gpio_connect_in_signal(config->data_gpio_nums[i],
parlio_periph_signals.groups[group_id].rx_units[unit_id].data_sigs[i], false);
} else {
ESP_LOGW(TAG, "data line %d not assigned", i);
}
}
return ESP_OK;
}
static IRAM_ATTR bool s_parlio_rx_default_trans_done_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
{
parlio_rx_unit_handle_t rx_unit = (parlio_rx_unit_handle_t )user_data;
BaseType_t high_task_woken = pdFALSE;
bool need_yield = false;
/* If configured on_receive_done callback and transaction just done */
if (rx_unit->cbs.on_receive_done) {
parlio_rx_event_data_t evt_data = {
.delimiter = rx_unit->curr_trans.delimiter,
.data = rx_unit->usr_recv_buf,
.size = rx_unit->curr_trans.size,
.recv_bytes = rx_unit->curr_trans.recv_bytes,
};
need_yield |= rx_unit->cbs.on_receive_done(rx_unit, &evt_data, rx_unit->user_data);
}
if (rx_unit->curr_trans.flags.infinite) {
/* For infinite transactions, reset the receiving bytes when the transaction is done */
rx_unit->curr_trans.recv_bytes = 0;
} else {
parlio_rx_transaction_t next_trans;
/* The current transaction finished, try to get the next transaction from the transaction queue */
if (xQueueReceiveFromISR(rx_unit->trans_que, &next_trans, &high_task_woken) == pdTRUE) {
if (rx_unit->cfg.flags.free_clk) {
parlio_ll_rx_enable_clock(rx_unit->group->hal.regs, false);
}
/* If the delimiter of the next transaction is not same as the current one, need to re-config the hardware */
if (next_trans.delimiter != rx_unit->curr_trans.delimiter) {
s_parlio_set_delimiter_config(rx_unit, next_trans.delimiter);
}
/* Mount the new transaction buffer and start the new transaction */
s_parlio_mount_transaction_buffer(rx_unit, &rx_unit->curr_trans);
gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->dma_descs);
if (rx_unit->cfg.flags.free_clk) {
parlio_ll_rx_start(rx_unit->group->hal.regs, true);
parlio_ll_rx_enable_clock(rx_unit->group->hal.regs, true);
}
} else {
/* No more transaction pending to receive, clear the current transaction */
rx_unit->curr_trans.delimiter->under_using = false;
memset(&rx_unit->curr_trans, 0, sizeof(parlio_rx_transaction_t));
need_yield |= high_task_woken == pdTRUE;
xSemaphoreGiveFromISR(rx_unit->trans_sem, &high_task_woken);
}
}
need_yield |= high_task_woken == pdTRUE;
return need_yield;
}
static IRAM_ATTR bool s_parlio_rx_default_desc_done_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
{
parlio_rx_unit_handle_t rx_unit = (parlio_rx_unit_handle_t )user_data;
bool need_yield = false;
/* Get the finished descriptor from the current descriptor */
dma_descriptor_t *finished_desc = rx_unit->curr_desc;
parlio_rx_event_data_t evt_data = {
.delimiter = rx_unit->curr_trans.delimiter,
.data = finished_desc->buffer,
.size = finished_desc->dw0.size,
.recv_bytes = finished_desc->dw0.length,
};
if (rx_unit->cbs.on_partial_receive) {
need_yield |= rx_unit->cbs.on_partial_receive(rx_unit, &evt_data, rx_unit->user_data);
}
/* For the infinite transaction, need to copy the data in DMA buffer to the user receiving buffer */
if (rx_unit->curr_trans.flags.infinite) {
memcpy(rx_unit->usr_recv_buf + rx_unit->curr_trans.recv_bytes, evt_data.data, evt_data.recv_bytes);
} else {
rx_unit->curr_trans.delimiter->under_using = false;
}
/* Update received bytes */
if (rx_unit->curr_trans.recv_bytes >= rx_unit->curr_trans.size) {
rx_unit->curr_trans.recv_bytes = 0;
}
rx_unit->curr_trans.recv_bytes += evt_data.recv_bytes;
/* Move to the next DMA descriptor */
rx_unit->curr_desc = rx_unit->curr_desc->next;
return need_yield;
}
static IRAM_ATTR bool s_parlio_rx_default_timeout_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
{
parlio_rx_unit_handle_t rx_unit = (parlio_rx_unit_handle_t )user_data;
bool need_yield = false;
parlio_rx_delimiter_handle_t deli = rx_unit->curr_trans.delimiter;
if (rx_unit->cbs.on_timeout && deli) {
parlio_rx_event_data_t evt_data = {
.delimiter = deli,
};
need_yield |= rx_unit->cbs.on_timeout(rx_unit, &evt_data, rx_unit->user_data);
}
return need_yield;
}
static esp_err_t s_parlio_rx_create_dma_descriptors(parlio_rx_unit_handle_t rx_unit, uint32_t max_recv_size)
{
ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid param");
uint32_t desc_num =max_recv_size / DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED + 1;
/* set at least 2 descriptors */
if (desc_num < 2) {
desc_num = 4;
}
rx_unit->desc_num = desc_num;
/* Allocated and link the descriptor nodes */
rx_unit->dma_descs = (dma_descriptor_t *)heap_caps_calloc(desc_num, sizeof(dma_descriptor_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
ESP_RETURN_ON_FALSE(rx_unit->dma_descs, ESP_ERR_NO_MEM, TAG, "no memory for DMA descriptors");
rx_unit->max_recv_size = max_recv_size;
return ESP_OK;
}
static esp_err_t s_parlio_rx_unit_init_dma(parlio_rx_unit_handle_t rx_unit)
{
/* Allocate and connect the GDMA channel */
gdma_channel_alloc_config_t dma_chan_config = {
.direction = GDMA_CHANNEL_DIRECTION_RX,
};
ESP_RETURN_ON_ERROR(gdma_new_channel(&dma_chan_config, &rx_unit->dma_chan), TAG, "allocate RX DMA channel failed");
gdma_connect(rx_unit->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_PARLIO, 0));
/* Set GDMA strategy */
gdma_strategy_config_t gdma_strategy_conf = {
.auto_update_desc = true,
.owner_check = false, // no need to check owner
};
gdma_apply_strategy(rx_unit->dma_chan, &gdma_strategy_conf);
/* Register callbacks */
gdma_rx_event_callbacks_t cbs = {
.on_recv_eof = s_parlio_rx_default_trans_done_callback,
.on_recv_done = s_parlio_rx_default_desc_done_callback,
// TODO: not on_err_desc, wait for GDMA supports on_err_eof
// .on_err_eof = s_parlio_rx_default_timeout_callback,
.on_descr_err = s_parlio_rx_default_timeout_callback,
};
gdma_register_rx_event_callbacks(rx_unit->dma_chan, &cbs, rx_unit);
return ESP_OK;
}
static esp_err_t s_parlio_select_periph_clock(parlio_rx_unit_handle_t rx_unit, const parlio_rx_unit_config_t *config)
{
parlio_hal_context_t *hal = &rx_unit->group->hal;
parlio_clock_source_t clk_src = config->clk_src;
uint32_t periph_src_clk_hz = 0;
uint32_t div = 1;
/* if the source clock is input from the GPIO, then we're in the slave mode */
if (clk_src != PARLIO_CLK_SRC_EXTERNAL) {
ESP_RETURN_ON_FALSE(config->clk_freq_hz, ESP_ERR_INVALID_ARG, TAG, "clock frequency not set");
/* get the internal clock source frequency */
esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &periph_src_clk_hz);
/* set clock division, round up */
div = (periph_src_clk_hz + config->clk_freq_hz - 1) / config->clk_freq_hz;
} else {
periph_src_clk_hz = config->clk_freq_hz;
}
#if CONFIG_PM_ENABLE
if (clk_src != PARLIO_CLK_SRC_EXTERNAL) {
/* XTAL and PLL clock source will be turned off in light sleep, so we need to create a NO_LIGHT_SLEEP lock */
sprintf(rx_unit->pm_lock_name, "parlio_rx_%d_%d", rx_unit->group->group_id, rx_unit->unit_id); // e.g. parlio_rx_0_0
esp_err_t ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, rx_unit->pm_lock_name, &rx_unit->pm_lock);
ESP_RETURN_ON_ERROR(ret, TAG, "create NO_LIGHT_SLEEP lock failed");
}
#endif
/* Set clock configuration */
parlio_ll_rx_set_clock_source(hal->regs, clk_src);
parlio_ll_rx_set_clock_div(hal->regs, div);
rx_unit->clk_src = clk_src;
rx_unit->cfg.clk_freq_hz = periph_src_clk_hz / div;
/* warning if precision lost due to division */
if ((clk_src != PARLIO_CLK_SRC_EXTERNAL) &&
(config->clk_freq_hz != rx_unit->cfg.clk_freq_hz )) {
ESP_LOGW(TAG, "precision loss, real output frequency: %"PRIu32, rx_unit->cfg.clk_freq_hz );
}
return ESP_OK;
}
static esp_err_t s_parlio_destroy_rx_unit(parlio_rx_unit_handle_t rx_unit)
{
/* Free the transaction queue */
if (rx_unit->trans_que) {
vQueueDeleteWithCaps(rx_unit->trans_que);
}
/* Free the mutex lock */
if (rx_unit->mutex) {
vSemaphoreDeleteWithCaps(rx_unit->mutex);
}
/* Free the transaction semaphore */
if (rx_unit->trans_sem) {
vSemaphoreDeleteWithCaps(rx_unit->trans_sem);
}
/* Free the power management lock */
if (rx_unit->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_delete(rx_unit->pm_lock), TAG, "delete pm lock failed");
}
/* Delete the GDMA channel */
if (rx_unit->dma_chan) {
ESP_RETURN_ON_ERROR(gdma_disconnect(rx_unit->dma_chan), TAG, "disconnect dma channel failed");
ESP_RETURN_ON_ERROR(gdma_del_channel(rx_unit->dma_chan), TAG, "delete dma channel failed");
}
/* Free the DMA descriptors */
if (rx_unit->dma_descs) {
free(rx_unit->dma_descs);
}
/* Free the internal DMA buffer */
if (rx_unit->dma_buf) {
free(rx_unit->dma_buf);
}
/* Unregister the RX unit from the PARLIO group */
if (rx_unit->group) {
parlio_unregister_unit_from_group((parlio_unit_base_handle_t)rx_unit);
}
/* Free the RX unit */
free(rx_unit);
return ESP_OK;
}
esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_unit_handle_t *ret_unit)
{
#if CONFIG_PARLIO_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
ESP_RETURN_ON_FALSE(config && ret_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
/* Check the data width to be the the power of 2 */
ESP_RETURN_ON_FALSE(__builtin_popcount(config->data_width) == 1, ESP_ERR_INVALID_ARG, TAG,
"data line number should be the power of 2 without counting valid signal");
esp_err_t ret = ESP_OK;
parlio_rx_unit_handle_t unit = NULL;
/* Allocate unit memory */
unit = heap_caps_calloc(1, sizeof(parlio_rx_unit_t), PARLIO_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(unit, ESP_ERR_NO_MEM, err, TAG, "no memory for rx unit");
unit->dir = PARLIO_DIR_RX;
unit->is_enabled = false;
/* Initialize mutex lock */
unit->mutex = xSemaphoreCreateMutexWithCaps(PARLIO_MEM_ALLOC_CAPS);
/* Create transaction binary semaphore */
unit->trans_sem = xSemaphoreCreateBinaryWithCaps(PARLIO_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(unit->trans_sem, ESP_ERR_NO_MEM, err, TAG, "no memory for transaction semaphore");
xSemaphoreGive(unit->trans_sem);
/* Create the transaction queue. Choose `parlio_rx_transaction_t` as the queue element instead of its pointer
* Because the queue will do the copy to the element, no need to worry about the item in the queue will expire,
* so that we don't have to allocate additional memory to store the transaction. */
unit->trans_que = xQueueCreateWithCaps(config->trans_queue_depth, sizeof(parlio_rx_transaction_t), PARLIO_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(unit->trans_que, ESP_ERR_NO_MEM, err, TAG, "no memory for transaction queue");
ESP_GOTO_ON_ERROR(s_parlio_rx_create_dma_descriptors(unit, config->max_recv_size), err, TAG, "create dma descriptor failed");
/* Register and attach the rx unit to the group */
ESP_GOTO_ON_ERROR(parlio_register_unit_to_group((parlio_unit_base_handle_t)unit), err, TAG, "failed to register the rx unit to the group");
memcpy(&unit->cfg, config, sizeof(parlio_rx_unit_config_t));
/* If not using external clock source, the internal clock is always a free running clock */
if (config->clk_src != PARLIO_CLK_SRC_EXTERNAL) {
unit->cfg.flags.free_clk = 1;
}
parlio_group_t *group = unit->group;
parlio_hal_context_t *hal = &group->hal;
/* Initialize GPIO */
ESP_GOTO_ON_ERROR(s_parlio_rx_unit_set_gpio(unit, config), err, TAG, "failed to set GPIO");
/* Install DMA service */
ESP_GOTO_ON_ERROR(s_parlio_rx_unit_init_dma(unit), err, TAG, "install rx DMA failed");
/* Reset RX module */
parlio_ll_rx_reset_clock(hal->regs);
parlio_ll_rx_reset_fifo(hal->regs);
parlio_ll_rx_enable_clock(hal->regs, false);
parlio_ll_rx_start(hal->regs, false);
/* parlio_ll_clock_source_t and parlio_clock_source_t are binary compatible if the clock source is from internal */
ESP_GOTO_ON_ERROR(s_parlio_select_periph_clock(unit, config), err, TAG, "set clock source failed");
/* Set the data width */
parlio_ll_rx_set_bus_width(hal->regs, config->data_width);
#if SOC_PARLIO_RX_CLK_SUPPORT_GATING
parlio_ll_rx_enable_clock_gating(hal->regs, config->flags.clk_gate_en);
#endif // SOC_PARLIO_RX_CLK_SUPPORT_GATING
/* return RX unit handle */
*ret_unit = unit;
ESP_LOGD(TAG, "new rx unit(%d,%d) at %p, trans_queue_depth=%zu",
group->group_id, unit->unit_id, (void *)unit, unit->cfg.trans_queue_depth);
return ESP_OK;
err:
if (unit) {
s_parlio_destroy_rx_unit(unit);
}
return ret;
}
esp_err_t parlio_del_rx_unit(parlio_rx_unit_handle_t rx_unit)
{
ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(!rx_unit->is_enabled, ESP_ERR_INVALID_STATE, TAG, "the unit has not disabled");
ESP_LOGD(TAG, "del rx unit (%d, %d)", rx_unit->group->group_id, rx_unit->unit_id);
return s_parlio_destroy_rx_unit(rx_unit);
}
esp_err_t parlio_rx_unit_enable(parlio_rx_unit_handle_t rx_unit, bool reset_queue)
{
ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(!rx_unit->is_enabled, ESP_ERR_INVALID_STATE, TAG, "the unit has enabled or running");
rx_unit->is_enabled = true;
parlio_hal_context_t *hal = &rx_unit->group->hal;
xSemaphoreTake(rx_unit->mutex, portMAX_DELAY);
/* Acquire the power management lock incase */
if (rx_unit->pm_lock) {
esp_pm_lock_acquire(rx_unit->pm_lock);
}
/* For non-free running clock, the unit can't stop once enabled, otherwise the data alignment will go wrong */
if (!rx_unit->cfg.flags.free_clk) {
parlio_ll_rx_reset_fifo(hal->regs);
parlio_ll_rx_start(hal->regs, true);
parlio_ll_rx_enable_clock(hal->regs, true);
}
/* Check if we need to start a pending transaction */
parlio_rx_transaction_t trans;
if (reset_queue) {
xQueueReset(rx_unit->trans_que);
xSemaphoreGive(rx_unit->trans_sem);
} else if (xQueueReceive(rx_unit->trans_que, &trans, 0) == pdTRUE) {
xSemaphoreTake(rx_unit->trans_sem, 0);
if (rx_unit->cfg.flags.free_clk) {
parlio_ll_rx_enable_clock(hal->regs, false);
}
s_parlio_set_delimiter_config(rx_unit, trans.delimiter);
s_parlio_mount_transaction_buffer(rx_unit, &rx_unit->curr_trans);
gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->curr_desc);
if (rx_unit->cfg.flags.free_clk) {
parlio_ll_rx_start(hal->regs, true);
parlio_ll_rx_enable_clock(hal->regs, true);
}
}
xSemaphoreGive(rx_unit->mutex);
return ESP_OK;
}
esp_err_t parlio_rx_unit_disable(parlio_rx_unit_handle_t rx_unit)
{
ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(rx_unit->is_enabled, ESP_ERR_INVALID_STATE, TAG, "the unit has disabled");
rx_unit->is_enabled = false;
/* stop the RX engine */
parlio_hal_context_t *hal = &rx_unit->group->hal;
xSemaphoreTake(rx_unit->mutex, portMAX_DELAY);
gdma_stop(rx_unit->dma_chan);
parlio_ll_rx_enable_clock(hal->regs, false);
parlio_ll_rx_start(hal->regs, false);
if (rx_unit->curr_trans.delimiter) {
rx_unit->curr_trans.delimiter->under_using = false;
}
/* For continuous receiving, free the temporary buffer and stop the DMA */
if (rx_unit->dma_buf) {
free(rx_unit->dma_buf);
rx_unit->dma_buf = NULL;
}
/* release power management lock */
if (rx_unit->pm_lock) {
esp_pm_lock_release(rx_unit->pm_lock);
}
/* Erase the current transaction */
memset(&rx_unit->curr_trans, 0, sizeof(parlio_rx_transaction_t));
xSemaphoreGive(rx_unit->mutex);
return ESP_OK;
}
esp_err_t parlio_new_rx_level_delimiter(const parlio_rx_level_delimiter_config_t *config,
parlio_rx_delimiter_handle_t *ret_delimiter)
{
ESP_RETURN_ON_FALSE(config && ret_delimiter, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
/* Validation signal line must specified for level delimiter */
ESP_RETURN_ON_FALSE(config->valid_sig_line_id < PARLIO_RX_UNIT_MAX_DATA_WIDTH,
ESP_ERR_INVALID_ARG, TAG, "no valid signal line specified");
parlio_rx_delimiter_handle_t delimiter = NULL;
/* Allocate memory for the delimiter */
delimiter = (parlio_rx_delimiter_handle_t)heap_caps_calloc(1, sizeof(parlio_rx_delimiter_t), PARLIO_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(delimiter, ESP_ERR_NO_MEM, TAG, "no memory for rx delimiter");
/* Assign configuration for the level delimiter */
delimiter->mode = PARLIO_RX_LEVEL_MODE;
delimiter->valid_sig_line_id = config->valid_sig_line_id;
delimiter->sample_edge = config->sample_edge;
delimiter->bit_pack_order = config->bit_pack_order;
delimiter->eof_data_len = config->eof_data_len;
delimiter->timeout_ticks = config->timeout_ticks;
delimiter->flags.active_level = config->flags.active_level;
*ret_delimiter = delimiter;
return ESP_OK;
}
esp_err_t parlio_new_rx_pulse_delimiter(const parlio_rx_pulse_delimiter_config_t *config,
parlio_rx_delimiter_handle_t *ret_delimiter)
{
ESP_RETURN_ON_FALSE(config && ret_delimiter, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
/* Validation signal line must specified for pulse delimiter */
ESP_RETURN_ON_FALSE(config->valid_sig_line_id < PARLIO_RX_UNIT_MAX_DATA_WIDTH,
ESP_ERR_INVALID_ARG, TAG, "no valid signal line specified");
/* Guarantee there is an end symbol, end by length or pulse */
ESP_RETURN_ON_FALSE(config->eof_data_len || config->flags.has_end_pulse,
ESP_ERR_INVALID_ARG, TAG, "Either eof_data_len or has_end_pulse should be set");
/* If end by length, the maximum length is limited */
ESP_RETURN_ON_FALSE(config->eof_data_len <= PARLIO_LL_RX_MAX_BYTES_PER_FRAME, ESP_ERR_INVALID_ARG,
TAG, "EOF data length exceed the max value %d", PARLIO_LL_RX_MAX_BYTES_PER_FRAME);
parlio_rx_delimiter_handle_t delimiter = NULL;
/* Allocate memory for the delimiter */
delimiter = (parlio_rx_delimiter_handle_t)heap_caps_calloc(1, sizeof(parlio_rx_delimiter_t), PARLIO_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(delimiter, ESP_ERR_NO_MEM, TAG, "no memory for rx delimiter");
/* Assign configuration for the pulse delimiter */
delimiter->mode = PARLIO_RX_PULSE_MODE;
delimiter->valid_sig_line_id = config->valid_sig_line_id;
delimiter->sample_edge = config->sample_edge;
delimiter->bit_pack_order = config->bit_pack_order;
delimiter->eof_data_len = config->eof_data_len;
delimiter->timeout_ticks = config->timeout_ticks;
delimiter->flags.start_bit_included = config->flags.start_bit_included;
delimiter->flags.has_end_pulse = config->flags.has_end_pulse;
delimiter->flags.end_bit_included = config->flags.end_bit_included;
delimiter->flags.pulse_invert = config->flags.pulse_invert;
*ret_delimiter = delimiter;
return ESP_OK;
}
esp_err_t parlio_new_rx_soft_delimiter(const parlio_rx_soft_delimiter_config_t *config,
parlio_rx_delimiter_handle_t *ret_delimiter)
{
ESP_RETURN_ON_FALSE(config && ret_delimiter, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
/* The soft delimiter can only end by length, EOF length should be within range (0, PARLIO_LL_RX_MAX_BYTES_PER_FRAME] */
ESP_RETURN_ON_FALSE(config->eof_data_len > 0 && config->eof_data_len <= PARLIO_LL_RX_MAX_BYTES_PER_FRAME, ESP_ERR_INVALID_ARG,
TAG, "EOF data length is 0 or exceed the max value %d", PARLIO_LL_RX_MAX_BYTES_PER_FRAME);
parlio_rx_delimiter_handle_t delimiter = NULL;
/* Allocate memory for the delimiter */
delimiter = (parlio_rx_delimiter_handle_t)heap_caps_calloc(1, sizeof(parlio_rx_delimiter_t), PARLIO_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(delimiter, ESP_ERR_NO_MEM, TAG, "no memory for rx delimiter");
/* Assign configuration for the soft delimiter */
delimiter->mode = PARLIO_RX_SOFT_MODE;
delimiter->under_using = false;
delimiter->sample_edge = config->sample_edge;
delimiter->bit_pack_order = config->bit_pack_order;
delimiter->eof_data_len = config->eof_data_len;
delimiter->timeout_ticks = config->timeout_ticks;
*ret_delimiter = delimiter;
return ESP_OK;
}
esp_err_t parlio_rx_soft_delimiter_start_stop(parlio_rx_unit_handle_t rx_unit, parlio_rx_delimiter_handle_t delimiter, bool start_stop)
{
ESP_RETURN_ON_FALSE(rx_unit && delimiter, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(delimiter->mode == PARLIO_RX_SOFT_MODE, ESP_ERR_INVALID_ARG, TAG, "The delimiter is not soft delimiter");
ESP_RETURN_ON_FALSE(rx_unit->is_enabled, ESP_ERR_INVALID_STATE, TAG, "the unit has not enabled");
xSemaphoreTake(rx_unit->mutex, portMAX_DELAY);
parlio_hal_context_t *hal = &(rx_unit->group->hal);
parlio_ll_rx_start_soft_recv(hal->regs, start_stop);
xSemaphoreGive(rx_unit->mutex);
return ESP_OK;
}
esp_err_t parlio_del_rx_delimiter(parlio_rx_delimiter_handle_t delimiter)
{
ESP_RETURN_ON_FALSE(delimiter, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(!delimiter->under_using, ESP_ERR_INVALID_STATE, TAG, "the delimiter is under using");
free(delimiter);
return ESP_OK;
}
static esp_err_t s_parlio_rx_unit_do_transaction(parlio_rx_unit_handle_t rx_unit, parlio_rx_transaction_t *trans)
{
if (!rx_unit->curr_trans.delimiter) {
xSemaphoreTake(rx_unit->trans_sem, 0);
if (rx_unit->cfg.flags.free_clk) {
parlio_ll_rx_enable_clock(rx_unit->group->hal.regs, false);
}
if (trans->delimiter != rx_unit->curr_trans.delimiter) {
s_parlio_set_delimiter_config(rx_unit, trans->delimiter);
}
s_parlio_mount_transaction_buffer(rx_unit, trans);
gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->curr_desc);
if (rx_unit->cfg.flags.free_clk) {
parlio_ll_rx_start(rx_unit->group->hal.regs, true);
parlio_ll_rx_enable_clock(rx_unit->group->hal.regs, true);
}
} else { // Otherwise send to the queue
/* Send the transaction to the queue */
ESP_RETURN_ON_FALSE(xQueueSend(rx_unit->trans_que, trans, 0) == pdTRUE,
ESP_ERR_INVALID_STATE, TAG, "transaction queue is full, failed to send transaction to the queue");
}
return ESP_OK;
}
esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit,
void *payload,
size_t payload_size,
const parlio_receive_config_t* recv_cfg)
{
ESP_RETURN_ON_FALSE(rx_unit && payload && recv_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(recv_cfg->delimiter, ESP_ERR_INVALID_ARG, TAG, "no delimiter specified");
ESP_RETURN_ON_FALSE(payload_size <= rx_unit->max_recv_size, ESP_ERR_INVALID_ARG, TAG, "trans length too large");
#if CONFIG_GDMA_ISR_IRAM_SAFE
ESP_RETURN_ON_FALSE(esp_ptr_internal(payload), ESP_ERR_INVALID_ARG, TAG, "payload not in internal RAM");
#endif
if (recv_cfg->delimiter->eof_data_len) {
ESP_RETURN_ON_FALSE(payload_size >= recv_cfg->delimiter->eof_data_len, ESP_ERR_INVALID_ARG,
TAG, "payload size should be greater than eof_data_len");
}
if (recv_cfg->delimiter->mode != PARLIO_RX_SOFT_MODE) {
ESP_RETURN_ON_FALSE(rx_unit->cfg.valid_gpio_num >= 0, ESP_ERR_INVALID_ARG, TAG, "The validate gpio of this unit is not set");
/* Check if the valid_sig_line_id is equal or greater than data width, otherwise valid_sig_line_id is conflict with data signal.
* Specifically, level or pulse delimiter requires one data line as valid signal, so these two delimiters can't support PARLIO_RX_UNIT_MAX_DATA_WIDTH */
ESP_RETURN_ON_FALSE(recv_cfg->delimiter->valid_sig_line_id >= rx_unit->cfg.data_width,
ESP_ERR_INVALID_ARG, TAG, "the valid_sig_line_id of this delimiter is conflict with rx unit data width");
/* Assign the signal here to ensure iram safe */
recv_cfg->delimiter->valid_sig = parlio_periph_signals.groups[rx_unit->group->group_id].
rx_units[rx_unit->unit_id].
data_sigs[recv_cfg->delimiter->valid_sig_line_id];
}
void *p_buffer = payload;
/* Create the internal DMA buffer for the infinite transaction if indirect_mount is set */
if (recv_cfg->flags.is_infinite && recv_cfg->flags.indirect_mount) {
/* Allocate the internal DMA buffer to store the data temporary */
rx_unit->dma_buf = heap_caps_calloc(1, payload_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
ESP_RETURN_ON_FALSE(rx_unit->dma_buf, ESP_ERR_NO_MEM, TAG, "No memory for the internal DMA buffer");
/* Use the internal DMA buffer so that the user buffer can always be available */
p_buffer = rx_unit->dma_buf;
}
/* Create the transaction */
parlio_rx_transaction_t transaction = {
.delimiter = recv_cfg->delimiter,
.payload = p_buffer,
.size = payload_size,
.recv_bytes = 0,
.flags.infinite = recv_cfg->flags.is_infinite,
};
rx_unit->usr_recv_buf = payload;
xSemaphoreTake(rx_unit->mutex, portMAX_DELAY);
esp_err_t ret = s_parlio_rx_unit_do_transaction(rx_unit, &transaction);
xSemaphoreGive(rx_unit->mutex);
return ret;
}
esp_err_t parlio_rx_unit_wait_all_done(parlio_rx_unit_handle_t rx_unit, int timeout_ms)
{
ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
xSemaphoreTake(rx_unit->mutex, portMAX_DELAY);
TickType_t ticks = timeout_ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
/* Waiting for the all transaction done signal */
if (xSemaphoreTake(rx_unit->trans_sem, ticks) == pdFALSE) {
xSemaphoreGive(rx_unit->mutex);
return ESP_ERR_TIMEOUT;
}
/* Put back the signal */
xSemaphoreGive(rx_unit->trans_sem);
xSemaphoreGive(rx_unit->mutex);
return ESP_OK;
}
esp_err_t parlio_rx_unit_register_event_callbacks(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_callbacks_t *cbs, void *user_data)
{
ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(!rx_unit->is_enabled, ESP_ERR_INVALID_STATE, TAG, "the unit has enabled or running");
xSemaphoreTake(rx_unit->mutex, portMAX_DELAY);
memcpy(&rx_unit->cbs, cbs, sizeof(parlio_rx_event_callbacks_t));
rx_unit->user_data = user_data;
xSemaphoreGive(rx_unit->mutex);
return ESP_OK;
}

View File

@ -46,8 +46,9 @@ typedef struct {
typedef struct parlio_tx_unit_t {
int unit_id; // unit id
size_t data_width; // data width
parlio_dir_t dir;
parlio_group_t *group; // group handle
size_t data_width; // data width
intr_handle_t intr; // allocated interrupt handle
esp_pm_lock_handle_t pm_lock; // power management lock
gdma_channel_handle_t dma_chan; // DMA channel
@ -71,44 +72,6 @@ typedef struct parlio_tx_unit_t {
static void parlio_tx_default_isr(void *args);
static esp_err_t parlio_tx_register_to_group(parlio_tx_unit_t *unit)
{
parlio_group_t *group = NULL;
int unit_id = -1;
for (int i = 0; i < SOC_PARLIO_GROUPS; i++) {
group = parlio_acquire_group_handle(i);
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no memory for group (%d)", i);
portENTER_CRITICAL(&group->spinlock);
for (int j = 0; j < SOC_PARLIO_TX_UNITS_PER_GROUP; j++) {
if (group->tx_units[j] == NULL) {
group->tx_units[j] = unit;
unit_id = j;
break;
}
}
portEXIT_CRITICAL(&group->spinlock);
if (unit_id < 0) {
// didn't find a free unit slot in the group
parlio_release_group_handle(group);
} else {
unit->unit_id = unit_id;
unit->group = group;
break;
}
}
ESP_RETURN_ON_FALSE(unit_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free tx unit");
return ESP_OK;
}
static void parlio_tx_unregister_to_group(parlio_tx_unit_t *unit, parlio_group_t *group)
{
portENTER_CRITICAL(&group->spinlock);
group->tx_units[unit->unit_id] = NULL;
portEXIT_CRITICAL(&group->spinlock);
// the tx unit has a reference of the group, release it now
parlio_release_group_handle(group);
}
static esp_err_t parlio_tx_create_trans_queue(parlio_tx_unit_t *tx_unit, const parlio_tx_unit_config_t *config)
{
esp_err_t ret;
@ -159,7 +122,7 @@ static esp_err_t parlio_destroy_tx_unit(parlio_tx_unit_t *tx_unit)
}
if (tx_unit->group) {
// de-register from group
parlio_tx_unregister_to_group(tx_unit, tx_unit->group);
parlio_unregister_unit_from_group((parlio_unit_base_handle_t)tx_unit);
}
free(tx_unit->dma_nodes);
free(tx_unit);
@ -185,6 +148,7 @@ static esp_err_t parlio_tx_unit_configure_gpio(parlio_tx_unit_t *tx_unit, const
esp_rom_gpio_connect_out_signal(config->data_gpio_nums[i],
parlio_periph_signals.groups[group_id].tx_units[unit_id].data_sigs[i], false, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->data_gpio_nums[i]], PIN_FUNC_GPIO);
}
}
// Note: the valid signal will override TXD[PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG]
@ -232,12 +196,10 @@ static esp_err_t parlio_tx_unit_init_dma(parlio_tx_unit_t *tx_unit)
static esp_err_t parlio_select_periph_clock(parlio_tx_unit_t *tx_unit, const parlio_tx_unit_config_t *config)
{
parlio_hal_context_t *hal = &tx_unit->group->hal;
// parlio_ll_clock_source_t and parlio_clock_source_t are binary compatible if the clock source is from internal
parlio_ll_clock_source_t clk_src = (parlio_ll_clock_source_t)(config->clk_src);
parlio_clock_source_t clk_src = config->clk_in_gpio_num >= 0 ? PARLIO_CLK_SRC_EXTERNAL : config->clk_src;
uint32_t periph_src_clk_hz = 0;
// if the source clock is input from the GPIO, then we're in the slave mode
if (config->clk_in_gpio_num >= 0) {
clk_src = PARLIO_LL_CLK_SRC_PAD;
if (clk_src == PARLIO_CLK_SRC_EXTERNAL) {
periph_src_clk_hz = config->input_clk_src_freq_hz;
} else {
// get the internal clock source frequency
@ -246,7 +208,7 @@ static esp_err_t parlio_select_periph_clock(parlio_tx_unit_t *tx_unit, const par
ESP_RETURN_ON_FALSE(periph_src_clk_hz, ESP_ERR_INVALID_ARG, TAG, "invalid clock source frequency");
#if CONFIG_PM_ENABLE
if (clk_src != PARLIO_LL_CLK_SRC_PAD) {
if (clk_src != PARLIO_CLK_SRC_EXTERNAL) {
// XTAL and PLL clock source will be turned off in light sleep, so we need to create a NO_LIGHT_SLEEP lock
sprintf(tx_unit->pm_lock_name, "parlio_tx_%d_%d", tx_unit->group->group_id, tx_unit->unit_id); // e.g. parlio_tx_0_0
esp_err_t ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, tx_unit->pm_lock_name, &tx_unit->pm_lock);
@ -318,13 +280,13 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
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->dir = PARLIO_DIR_TX;
unit->data_width = data_width;
//create transaction queue
ESP_GOTO_ON_ERROR(parlio_tx_create_trans_queue(unit, config), err, TAG, "create transaction queue failed");
// register the unit to a group
ESP_GOTO_ON_ERROR(parlio_tx_register_to_group(unit), err, TAG, "register unit to group failed");
ESP_GOTO_ON_ERROR(parlio_register_unit_to_group((parlio_unit_base_handle_t)unit), err, TAG, "register unit to group failed");
parlio_group_t *group = unit->group;
parlio_hal_context_t *hal = &group->hal;
// select the clock source

View File

@ -1,2 +1,2 @@
| Supported Targets | ESP32-C6 | ESP32-H2 | ESP32-P4 |
| ----------------- | -------- | -------- | -------- |
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |

View File

@ -39,12 +39,6 @@
extern "C" {
#endif
typedef enum {
PARLIO_LL_CLK_SRC_XTAL = PARLIO_CLK_SRC_XTAL,
PARLIO_LL_CLK_SRC_PLL_F240M = PARLIO_CLK_SRC_PLL_F240M,
PARLIO_LL_CLK_SRC_PAD, // 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 */
@ -82,18 +76,21 @@ static inline void parlio_ll_reset_register(int group_id)
* @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)
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_LL_CLK_SRC_XTAL:
case PARLIO_CLK_SRC_XTAL:
clk_sel = 0;
break;
case PARLIO_LL_CLK_SRC_PLL_F240M:
case PARLIO_CLK_SRC_PLL_F240M:
clk_sel = 1;
break;
case PARLIO_LL_CLK_SRC_PAD:
case PARLIO_CLK_SRC_RC_FAST:
clk_sel = 2;
break;
case PARLIO_CLK_SRC_EXTERNAL:
clk_sel = 3;
break;
@ -104,6 +101,33 @@ static inline void parlio_ll_rx_set_clock_source(parl_io_dev_t *dev, parlio_ll_c
PCR.parl_clk_rx_conf.parl_clk_rx_sel = clk_sel;
}
/**
* @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_clock_source_t parlio_ll_rx_get_clock_source(parl_io_dev_t *dev)
{
(void)dev;
uint32_t clk_sel = PCR.parl_clk_rx_conf.parl_clk_rx_sel;
switch (clk_sel) {
case 0:
return PARLIO_CLK_SRC_XTAL;
case 1:
return PARLIO_CLK_SRC_PLL_F240M;
case 2:
return PARLIO_CLK_SRC_RC_FAST;
case 3:
return PARLIO_CLK_SRC_EXTERNAL;
default: // unsupported clock source
HAL_ASSERT(false);
break;
}
return PARLIO_CLK_SRC_DEFAULT;
}
/**
* @brief Set the clock divider for the RX unit
*
@ -149,6 +173,7 @@ static inline void parlio_ll_rx_enable_clock(parl_io_dev_t *dev, bool en)
* @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_cfg0.rx_eof_gen_sel = cond;
@ -160,6 +185,7 @@ static inline void parlio_ll_rx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_
* @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->rx_cfg0.rx_start = en;
@ -173,6 +199,7 @@ static inline void parlio_ll_rx_start(parl_io_dev_t *dev, bool en)
* @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)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->rx_cfg0, rx_data_bytelen, bitlen / 8);
@ -184,6 +211,7 @@ static inline void parlio_ll_rx_set_recv_bit_len(parl_io_dev_t *dev, uint32_t bi
* @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_cfg0.rx_smp_mode_sel = 0;
@ -199,6 +227,7 @@ static inline void parlio_ll_rx_set_level_recv_mode(parl_io_dev_t *dev, bool act
* @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;
@ -226,6 +255,7 @@ static inline void parlio_ll_rx_set_pulse_recv_mode(parl_io_dev_t *dev, bool sta
*
* @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_cfg0.rx_smp_mode_sel = 2;
@ -248,6 +278,7 @@ static inline void parlio_ll_rx_start_soft_recv(parl_io_dev_t *dev, bool en)
* @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_cfg0.rx_clk_edge_sel = edge;
@ -259,6 +290,7 @@ static inline void parlio_ll_rx_set_sample_clock_edge(parl_io_dev_t *dev, parlio
* @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_cfg0.rx_bit_pack_order = order;
@ -318,6 +350,7 @@ static inline void parlio_ll_rx_reset_fifo(parl_io_dev_t *dev)
* @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_cfg1.rx_ext_en_sel = line_num;
@ -329,6 +362,7 @@ static inline void parlio_ll_rx_treat_data_line_as_en(parl_io_dev_t *dev, uint32
* @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_cfg1.rx_timeout_en = en;
@ -340,6 +374,7 @@ static inline void parlio_ll_rx_enable_timeout(parl_io_dev_t *dev, bool en)
* @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_cfg1, rx_timeout_threshold, thres);
@ -350,6 +385,7 @@ static inline void parlio_ll_rx_set_timeout_thres(parl_io_dev_t *dev, uint32_t t
*
* @param dev Parallel IO register base address
*/
__attribute__((always_inline))
static inline void parlio_ll_rx_update_config(parl_io_dev_t *dev)
{
dev->rx_cfg1.rx_reg_update = 1;
@ -364,18 +400,21 @@ static inline void parlio_ll_rx_update_config(parl_io_dev_t *dev)
* @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)
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_LL_CLK_SRC_XTAL:
case PARLIO_CLK_SRC_XTAL:
clk_sel = 0;
break;
case PARLIO_LL_CLK_SRC_PLL_F240M:
case PARLIO_CLK_SRC_PLL_F240M:
clk_sel = 1;
break;
case PARLIO_LL_CLK_SRC_PAD:
case PARLIO_CLK_SRC_RC_FAST:
clk_sel = 2;
break;
case PARLIO_CLK_SRC_EXTERNAL:
clk_sel = 3;
break;
@ -386,6 +425,33 @@ static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_ll_c
PCR.parl_clk_tx_conf.parl_clk_tx_sel = clk_sel;
}
/**
* @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_clock_source_t parlio_ll_tx_get_clock_source(parl_io_dev_t *dev)
{
(void)dev;
uint32_t clk_sel = PCR.parl_clk_tx_conf.parl_clk_tx_sel;
switch (clk_sel) {
case 0:
return PARLIO_CLK_SRC_XTAL;
case 1:
return PARLIO_CLK_SRC_PLL_F240M;
case 2:
return PARLIO_CLK_SRC_RC_FAST;
case 3:
return PARLIO_CLK_SRC_EXTERNAL;
default: // unsupported clock source
HAL_ASSERT(false);
break;
}
return PARLIO_CLK_SRC_DEFAULT;
}
/**
* @brief Set the clock divider for the TX unit
*

View File

@ -41,12 +41,6 @@
extern "C" {
#endif
typedef enum {
PARLIO_LL_CLK_SRC_XTAL = PARLIO_CLK_SRC_XTAL,
PARLIO_LL_CLK_SRC_PLL_F96M = PARLIO_CLK_SRC_PLL_F96M,
PARLIO_LL_CLK_SRC_PAD, // 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 */
@ -84,18 +78,21 @@ static inline void parlio_ll_reset_register(int group_id)
* @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)
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_LL_CLK_SRC_XTAL:
case PARLIO_CLK_SRC_XTAL:
clk_sel = 0;
break;
case PARLIO_LL_CLK_SRC_PLL_F96M:
case PARLIO_CLK_SRC_PLL_F96M:
clk_sel = 1;
break;
case PARLIO_LL_CLK_SRC_PAD:
case PARLIO_CLK_SRC_RC_FAST:
clk_sel = 2;
break;
case PARLIO_CLK_SRC_EXTERNAL:
clk_sel = 3;
break;
@ -106,6 +103,33 @@ static inline void parlio_ll_rx_set_clock_source(parl_io_dev_t *dev, parlio_ll_c
PCR.parl_clk_rx_conf.parl_clk_rx_sel = clk_sel;
}
/**
* @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_clock_source_t parlio_ll_rx_get_clock_source(parl_io_dev_t *dev)
{
(void)dev;
uint32_t clk_sel = PCR.parl_clk_rx_conf.parl_clk_rx_sel;
switch (clk_sel) {
case 0:
return PARLIO_CLK_SRC_XTAL;
case 1:
return PARLIO_CLK_SRC_PLL_F96M;
case 2:
return PARLIO_CLK_SRC_RC_FAST;
case 3:
return PARLIO_CLK_SRC_EXTERNAL;
default: // unsupported clock source
HAL_ASSERT(false);
break;
}
return PARLIO_CLK_SRC_DEFAULT;
}
/**
* @brief Set the clock divider for the RX unit
*
@ -137,6 +161,7 @@ static inline void parlio_ll_rx_reset_clock(parl_io_dev_t *dev)
* @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;
@ -149,6 +174,7 @@ static inline void parlio_ll_rx_enable_clock(parl_io_dev_t *dev, bool en)
* @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;
@ -160,6 +186,7 @@ static inline void parlio_ll_rx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_
* @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->rx_start_cfg.rx_start = en;
@ -173,6 +200,7 @@ static inline void parlio_ll_rx_start(parl_io_dev_t *dev, bool en)
* @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;
@ -184,6 +212,7 @@ static inline void parlio_ll_rx_set_recv_bit_len(parl_io_dev_t *dev, uint32_t bi
* @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;
@ -199,6 +228,7 @@ static inline void parlio_ll_rx_set_level_recv_mode(parl_io_dev_t *dev, bool act
* @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;
@ -224,6 +254,7 @@ static inline void parlio_ll_rx_set_pulse_recv_mode(parl_io_dev_t *dev, bool sta
*
* @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;
@ -246,6 +277,7 @@ static inline void parlio_ll_rx_start_soft_recv(parl_io_dev_t *dev, bool en)
* @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;
@ -258,6 +290,7 @@ static inline void parlio_ll_rx_set_sample_clock_edge(parl_io_dev_t *dev, parlio
* @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;
@ -313,6 +346,7 @@ static inline void parlio_ll_rx_reset_fifo(parl_io_dev_t *dev)
* @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;
@ -335,6 +369,7 @@ static inline void parlio_ll_rx_enable_clock_gating(parl_io_dev_t *dev, bool en)
* @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;
@ -346,6 +381,7 @@ static inline void parlio_ll_rx_enable_timeout(parl_io_dev_t *dev, bool en)
* @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);
@ -356,6 +392,7 @@ static inline void parlio_ll_rx_set_timeout_thres(parl_io_dev_t *dev, uint32_t t
*
* @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;
@ -370,18 +407,21 @@ static inline void parlio_ll_rx_update_config(parl_io_dev_t *dev)
* @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)
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_LL_CLK_SRC_XTAL:
case PARLIO_CLK_SRC_XTAL:
clk_sel = 0;
break;
case PARLIO_LL_CLK_SRC_PLL_F96M:
case PARLIO_CLK_SRC_PLL_F96M:
clk_sel = 1;
break;
case PARLIO_LL_CLK_SRC_PAD:
case PARLIO_CLK_SRC_RC_FAST:
clk_sel = 2;
break;
case PARLIO_CLK_SRC_EXTERNAL:
clk_sel = 3;
break;
@ -392,6 +432,33 @@ static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_ll_c
PCR.parl_clk_tx_conf.parl_clk_tx_sel = clk_sel;
}
/**
* @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_clock_source_t parlio_ll_tx_get_clock_source(parl_io_dev_t *dev)
{
(void)dev;
uint32_t clk_sel = PCR.parl_clk_tx_conf.parl_clk_tx_sel;
switch (clk_sel) {
case 0:
return PARLIO_CLK_SRC_XTAL;
case 1:
return PARLIO_CLK_SRC_PLL_F96M;
case 2:
return PARLIO_CLK_SRC_RC_FAST;
case 3:
return PARLIO_CLK_SRC_EXTERNAL;
default: // unsupported clock source
HAL_ASSERT(false);
break;
}
return PARLIO_CLK_SRC_DEFAULT;
}
/**
* @brief Set the clock divider for the TX unit
*

View File

@ -199,6 +199,7 @@ static inline void parlio_ll_rx_reset_clock(parl_io_dev_t *dev)
* @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;
@ -227,6 +228,7 @@ static inline void parlio_ll_rx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_
* @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->rx_start_cfg.rx_start = en;

View File

@ -13,6 +13,7 @@
#pragma once
#include <stdint.h>
#include "hal/parlio_types.h"
#ifdef __cplusplus
extern "C" {

View File

@ -49,6 +49,11 @@ typedef enum {
PARLIO_BIT_PACK_ORDER_MSB, /*!< Bit pack order: MSB */
} parlio_bit_pack_order_t;
typedef enum {
PARIO_CLK_TYPE_FREE,
PARIO_CLK_TYPE_NON_FREE,
} parlio_clock_type_t;
#if SOC_PARLIO_SUPPORTED
/**
* @brief Parallel IO clock source

View File

@ -460,6 +460,8 @@ typedef enum {
typedef enum {
PARLIO_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */
PARLIO_CLK_SRC_PLL_F240M = SOC_MOD_CLK_PLL_F240M, /*!< Select PLL_F240M 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_F240M, /*!< Select PLL_F240M as the default clock choice */
} soc_periph_parlio_clk_src_t;

View File

@ -815,6 +815,14 @@ config SOC_PARLIO_TX_CLK_SUPPORT_GATING
bool
default y
config SOC_PARLIO_RX_CLK_SUPPORT_GATING
bool
default y
config SOC_PARLIO_RX_CLK_SUPPORT_OUTPUT
bool
default y
config SOC_PARLIO_TRANS_BIT_ALIGN
bool
default y

View File

@ -451,7 +451,7 @@ typedef enum {
/**
* @brief Array initializer for all supported clock sources of PARLIO
*/
#define SOC_PARLIO_CLKS {SOC_MOD_CLK_XTAL, SOC_MOD_CLK_PLL_F96M}
#define SOC_PARLIO_CLKS {SOC_MOD_CLK_XTAL, SOC_MOD_CLK_PLL_F96M, SOC_MOD_CLK_RC_FAST}
/**
* @brief PARLIO clock source
@ -459,6 +459,8 @@ typedef enum {
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;

View File

@ -323,6 +323,8 @@
#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_RX_CLK_SUPPORT_OUTPUT 1 /*!< Support output RX clock to a GPIO */
#define SOC_PARLIO_TRANS_BIT_ALIGN 1 /*!< Support bit alignment in transaction */
/*--------------------------- MPI CAPS ---------------------------------------*/

View File

@ -47,10 +47,6 @@ config SOC_ETM_SUPPORTED
bool
default y
config SOC_PARLIO_SUPPORTED
bool
default y
config SOC_ASYNC_MEMCPY_SUPPORTED
bool
default y

View File

@ -32,7 +32,7 @@
#define SOC_MCPWM_SUPPORTED 1
#define SOC_TWAI_SUPPORTED 1
#define SOC_ETM_SUPPORTED 1
#define SOC_PARLIO_SUPPORTED 1 //TODO: IDF-7471
// #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