mirror of
https://github.com/espressif/esp-idf.git
synced 2025-07-31 19:24:33 +02:00
feat(i3c): Add support for i2c mode in i3c peripheral
This commit is contained in:
22
components/esp_driver_i3c/CMakeLists.txt
Normal file
22
components/esp_driver_i3c/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
|
||||
set(srcs)
|
||||
set(include "include")
|
||||
|
||||
# I3C related source files.
|
||||
if(CONFIG_SOC_I3C_MASTER_SUPPORTED)
|
||||
list(APPEND srcs "i3c_master.c"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(${target} STREQUAL "linux")
|
||||
set(priv_requires "")
|
||||
else()
|
||||
set(priv_requires esp_driver_gpio esp_pm esp_mm)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS ${include}
|
||||
PRIV_REQUIRES "${priv_requires}"
|
||||
LDFRAGMENTS "linker.lf"
|
||||
)
|
26
components/esp_driver_i3c/Kconfig
Normal file
26
components/esp_driver_i3c/Kconfig
Normal file
@@ -0,0 +1,26 @@
|
||||
menu "ESP-Driver:I3C Master Configurations"
|
||||
depends on SOC_I3C_MASTER_SUPPORTED
|
||||
config I3C_MASTER_ISR_CACHE_SAFE
|
||||
bool "I3C ISR Cache-Safe"
|
||||
select I3C_MASTER_ISR_HANDLER_IN_IRAM
|
||||
select GDMA_ISR_HANDLER_IN_IRAM if SOC_GDMA_SUPPORTED
|
||||
default n
|
||||
help
|
||||
Ensure the I3C interrupt is Cache-Safe by allowing the interrupt handler to be
|
||||
executable when the cache is disabled (e.g. SPI Flash write).
|
||||
|
||||
config I3C_MASTER_ENABLE_DEBUG_LOG
|
||||
bool "Enable I3C debug log"
|
||||
default n
|
||||
help
|
||||
whether to enable the debug log message for I3C driver.
|
||||
Note that this option only controls the I3C driver log, will not affect other drivers.
|
||||
|
||||
config I3C_MASTER_ISR_HANDLER_IN_IRAM
|
||||
bool "Place I3C master ISR handler into IRAM"
|
||||
select GDMA_CTRL_FUNC_IN_IRAM if SOC_GDMA_SUPPORTED
|
||||
default n
|
||||
help
|
||||
Place I3C master ISR handler into IRAM for better performance and fewer cache misses.
|
||||
|
||||
endmenu # I3C Master Configurations
|
975
components/esp_driver_i3c/i3c_master.c
Normal file
975
components/esp_driver_i3c/i3c_master.c
Normal file
@@ -0,0 +1,975 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/lock.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_types.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#if CONFIG_I3C_MASTER_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_intr_alloc.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "esp_private/esp_clk.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "freertos/idf_additions.h"
|
||||
#include "hal/i3c_master_ll.h"
|
||||
#include "driver/i3c_master.h"
|
||||
#include "driver/i3c_master_i2c.h"
|
||||
#include "esp_private/gpio.h"
|
||||
#include "i3c_master_private.h"
|
||||
#include "hal/i3c_master_hal.h"
|
||||
#include "soc/clk_tree_defs.h"
|
||||
#include "esp_clk_tree.h"
|
||||
#include "hal/dma_types.h"
|
||||
#include "soc/i3c_master_periph.h"
|
||||
#include "esp_private/esp_clk_tree_common.h"
|
||||
#include "esp_private/gdma.h"
|
||||
#include "esp_private/gdma_link.h"
|
||||
#include "esp_private/esp_dma_utils.h"
|
||||
#include "esp_cache.h"
|
||||
#include "hal/cache_hal.h"
|
||||
#include "hal/cache_ll.h"
|
||||
#include "esp_pm.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "esp_private/esp_cache_private.h"
|
||||
|
||||
static const char *TAG = "i3c.master";
|
||||
|
||||
typedef struct i3c_master_platform_t {
|
||||
portMUX_TYPE spinlock; // platform level spinlock.
|
||||
i3c_master_bus_handle_t buses[SOC_I3C_MASTER_PERIPH_NUM]; // array of I3C master bus instances.
|
||||
} i3c_master_platform_t;
|
||||
|
||||
static i3c_master_platform_t s_i3c_master_platform = {}; // singleton platform
|
||||
|
||||
static bool i3c_master_bus_occupied(i3c_master_bus_num_t bus_num)
|
||||
{
|
||||
return s_i3c_master_platform.buses[bus_num] != NULL;
|
||||
}
|
||||
|
||||
static esp_err_t s_do_dma_transaction(i3c_master_bus_handle_t bus_handle, i3c_i2c_transaction_desc_t *trans)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
bus_handle->cur_trans = trans;
|
||||
|
||||
portENTER_CRITICAL_SAFE(&bus_handle->spinlock);
|
||||
i3c_master_ll_set_i2c_fast_mode_timing(bus_handle->hal.dev, bus_handle->clock_source_freq, trans->scl_speed_hz);
|
||||
i3c_master_ll_set_address_device_table(bus_handle->hal.dev, trans->addr_table, trans->addr_table_num);
|
||||
i3c_master_ll_set_command(bus_handle->hal.dev, trans->command_table, trans->command_table_num);
|
||||
portEXIT_CRITICAL_SAFE(&bus_handle->spinlock);
|
||||
|
||||
for (int i = 0; i < trans->command_table_num; i++) {
|
||||
if (trans->command_table[i].cmd_l.regular.rnw == I3C_MASTER_WRITE_TRANSFER) {
|
||||
size_t write_size = trans->command_table[i].cmd_h.regular.dl;
|
||||
size_t dma_aligned_size = I3C_ALIGN_UP(write_size, I3C_MASTER_DMA_INTERFACE_ALIGNMENT);
|
||||
gdma_buffer_mount_config_t mount_config = {
|
||||
.buffer = trans->write_buffer,
|
||||
.length = dma_aligned_size,
|
||||
.flags = {
|
||||
.mark_eof = true,
|
||||
.mark_final = true,
|
||||
}
|
||||
};
|
||||
|
||||
portENTER_CRITICAL_SAFE(&bus_handle->spinlock);
|
||||
i3c_master_ll_enable_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_TRANSMIT_EVENT_INTR);
|
||||
i3c_master_ll_clear_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_TRANSMIT_EVENT_INTR);
|
||||
portEXIT_CRITICAL_SAFE(&bus_handle->spinlock);
|
||||
gdma_link_mount_buffers(bus_handle->tx_dma_link, 0, &mount_config, 1, NULL);
|
||||
esp_cache_msync(trans->write_buffer, write_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
|
||||
if (err != ESP_OK) {
|
||||
ESP_DRAM_LOGE(TAG, "memory sync failed");
|
||||
return err;
|
||||
}
|
||||
gdma_reset(bus_handle->dma_tx_chan);
|
||||
gdma_start(bus_handle->dma_tx_chan, gdma_link_get_head_addr(bus_handle->tx_dma_link));
|
||||
}
|
||||
if (trans->command_table[i].cmd_l.regular.rnw == I3C_MASTER_READ_TRANSFER) {
|
||||
size_t read_size = trans->command_table[i].cmd_h.regular.dl;
|
||||
size_t dma_aligned_size = I3C_ALIGN_UP(read_size, I3C_MASTER_DMA_INTERFACE_ALIGNMENT);
|
||||
gdma_buffer_mount_config_t mount_config = {
|
||||
.buffer = trans->read_buffer,
|
||||
.length = dma_aligned_size,
|
||||
.flags = {
|
||||
.mark_eof = true,
|
||||
.mark_final = true,
|
||||
}
|
||||
};
|
||||
|
||||
portENTER_CRITICAL_SAFE(&bus_handle->spinlock);
|
||||
i3c_master_ll_enable_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_RECEIVE_EVENT_INTR);
|
||||
i3c_master_ll_clear_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_RECEIVE_EVENT_INTR);
|
||||
portEXIT_CRITICAL_SAFE(&bus_handle->spinlock);
|
||||
gdma_link_mount_buffers(bus_handle->rx_dma_link, 0, &mount_config, 1, NULL);
|
||||
gdma_reset(bus_handle->dma_rx_chan);
|
||||
gdma_start(bus_handle->dma_rx_chan, gdma_link_get_head_addr(bus_handle->rx_dma_link));
|
||||
}
|
||||
}
|
||||
|
||||
// transaction start
|
||||
portENTER_CRITICAL_SAFE(&bus_handle->spinlock);
|
||||
i3c_master_ll_start_transaction(bus_handle->hal.dev);
|
||||
portEXIT_CRITICAL_SAFE(&bus_handle->spinlock);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
Handle tx buffer.
|
||||
If data to be sent is larger than fifo, we can add data and continue
|
||||
transfer with buf threshold interrupt.
|
||||
*/
|
||||
static bool handle_tx_data_buf_thld_int(i3c_master_bus_handle_t i3c_master, uint32_t int_mask)
|
||||
{
|
||||
portENTER_CRITICAL_SAFE(&i3c_master->spinlock);
|
||||
size_t empty_size = i3c_master_ll_get_tx_fifo_empty_count(i3c_master->hal.dev) * 4;
|
||||
size_t actual_write_size = 0;
|
||||
|
||||
if (i3c_master->write_buffer_left_size >= empty_size) {
|
||||
actual_write_size = empty_size;
|
||||
} else {
|
||||
actual_write_size = i3c_master->write_buffer_left_size;
|
||||
}
|
||||
|
||||
i3c_master_ll_write_tx_port(i3c_master->hal.dev, i3c_master->write_fifo_buffer_pointer, actual_write_size);
|
||||
i3c_master->write_fifo_buffer_pointer += actual_write_size;
|
||||
i3c_master->write_buffer_left_size -= actual_write_size;
|
||||
if (i3c_master->write_buffer_left_size > 0) {
|
||||
i3c_master_ll_clear_intr_mask(i3c_master->hal.dev, int_mask);
|
||||
i3c_master_ll_enable_intr_mask(i3c_master->hal.dev, int_mask);
|
||||
}
|
||||
portEXIT_CRITICAL_SAFE(&i3c_master->spinlock);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
Handle complete interrupt
|
||||
If there is still some remaining data to be received, receive data to buffer.
|
||||
If dma async transaction is enabled, start another transaction from queue.
|
||||
*/
|
||||
static bool handle_transfer_complete_int(i3c_master_bus_handle_t i3c_master)
|
||||
{
|
||||
i3c_master_event_t event = I3C_MASTER_EVENT_DONE;
|
||||
BaseType_t do_yield = pdFALSE;
|
||||
bool need_yield = false;
|
||||
|
||||
if (i3c_master->use_dma_transaction == false) {
|
||||
portENTER_CRITICAL_SAFE(&i3c_master->spinlock);
|
||||
size_t rx_fifo_content = i3c_master_ll_get_rx_fifo_full_count(i3c_master->hal.dev) * 4;
|
||||
i3c_master_ll_read_rx_port(i3c_master->hal.dev, i3c_master->read_fifo_buffer_pointer, rx_fifo_content);
|
||||
|
||||
i3c_master->read_buffer_left_size -= rx_fifo_content;
|
||||
i3c_master->read_fifo_buffer_pointer += rx_fifo_content;
|
||||
portEXIT_CRITICAL_SAFE(&i3c_master->spinlock);
|
||||
}
|
||||
|
||||
i3c_master_response_data_t response_data;
|
||||
response_data.val = i3c_master_ll_get_response_buffer_value(i3c_master->hal.dev);
|
||||
if (response_data.err_sts == I3C_MASTER_LL_ADDRESS_NACK_OR_DYNAMIC_ADDRESS_NACK) {
|
||||
event = I3C_MASTER_EVENT_NACK;
|
||||
}
|
||||
|
||||
if (i3c_master->async_transaction == false) {
|
||||
xQueueSendFromISR(i3c_master->event_queue, (void *)&event, &do_yield);
|
||||
if (do_yield) {
|
||||
need_yield |= true;
|
||||
}
|
||||
} else {
|
||||
i3c_i2c_transaction_desc_t *trans_desc = NULL;
|
||||
|
||||
i3c_fsm_t expected_fsm = I3C_FSM_RUN;
|
||||
if (atomic_compare_exchange_strong(&i3c_master->fsm, &expected_fsm, I3C_FSM_ENABLE_WAIT)) {
|
||||
trans_desc = i3c_master->cur_trans;
|
||||
xQueueSendFromISR(i3c_master->trans_queues[I3C_TRANS_QUEUE_COMPLETE], &trans_desc, &do_yield);
|
||||
if (do_yield) {
|
||||
need_yield = true;
|
||||
}
|
||||
atomic_store(&i3c_master->fsm, I3C_FSM_ENABLE);
|
||||
}
|
||||
|
||||
i3c_master_i2c_device_handle_t i2c_dev = NULL;
|
||||
i3c_i2c_master_device_list_t *device_item = NULL;
|
||||
SLIST_FOREACH(device_item, &i3c_master->device_list, next) {
|
||||
if (device_item->i2c_dev->address == i3c_master->cur_trans->addr_table[0].dat.i2c.static_addr) {
|
||||
i2c_dev = device_item->i2c_dev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i3c_master->cur_trans->read_buffer != NULL) {
|
||||
size_t dma_rcv_size = gdma_link_count_buffer_size_till_eof(i3c_master->rx_dma_link, 0);
|
||||
size_t c2m_aligned_size = I3C_ALIGN_UP(dma_rcv_size, i3c_master->cache_line_size);
|
||||
esp_cache_msync(i3c_master->cur_trans->read_buffer, c2m_aligned_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
||||
i3c_i2c_master_event_data_t evt_data = {
|
||||
.data = i3c_master->cur_trans->read_buffer,
|
||||
.recv_size = dma_rcv_size,
|
||||
.event = event,
|
||||
};
|
||||
|
||||
if (i2c_dev->on_trans_done) {
|
||||
if (i2c_dev->on_trans_done(i2c_dev, &evt_data, i2c_dev->user_ctx)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expected_fsm = I3C_FSM_ENABLE;
|
||||
if (atomic_compare_exchange_strong(&i3c_master->fsm, &expected_fsm, I3C_FSM_RUN_WAIT)) {
|
||||
if (xQueueReceiveFromISR(i3c_master->trans_queues[I3C_TRANS_QUEUE_PROGRESS], &trans_desc, &do_yield) == pdTRUE) {
|
||||
atomic_store(&i3c_master->fsm, I3C_FSM_RUN);
|
||||
s_do_dma_transaction(i3c_master, trans_desc);
|
||||
if (do_yield) {
|
||||
need_yield = true;
|
||||
}
|
||||
} else {
|
||||
atomic_store(&i3c_master->fsm, I3C_FSM_ENABLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static void handle_rx_data_buf_threshhold_int(i3c_master_bus_handle_t i3c_master)
|
||||
{
|
||||
if (i3c_master->use_dma_transaction == false) {
|
||||
portENTER_CRITICAL_SAFE(&i3c_master->spinlock);
|
||||
size_t rx_fifo_content = i3c_master_ll_get_rx_fifo_full_count(i3c_master->hal.dev) * 4;
|
||||
i3c_master_ll_read_rx_port(i3c_master->hal.dev, i3c_master->read_fifo_buffer_pointer, rx_fifo_content);
|
||||
|
||||
i3c_master->read_buffer_left_size -= rx_fifo_content;
|
||||
i3c_master->read_fifo_buffer_pointer += rx_fifo_content;
|
||||
portEXIT_CRITICAL_SAFE(&i3c_master->spinlock);
|
||||
}
|
||||
}
|
||||
|
||||
static void i3c_master_isr_handler_default(void *arg)
|
||||
{
|
||||
i3c_master_bus_handle_t i3c_master = (i3c_master_bus_t*) arg;
|
||||
uint32_t int_mask = 0;
|
||||
bool need_yield = false;
|
||||
portENTER_CRITICAL_SAFE(&i3c_master->spinlock);
|
||||
i3c_master_ll_get_intr_mask(i3c_master->hal.dev, &int_mask);
|
||||
i3c_master_ll_clear_intr_mask(i3c_master->hal.dev, int_mask);
|
||||
i3c_master_ll_disable_intr_mask(i3c_master->hal.dev, int_mask);
|
||||
portEXIT_CRITICAL_SAFE(&i3c_master->spinlock);
|
||||
|
||||
if (int_mask & I3C_MST_TX_DATA_BUF_THLD_INT_ST) {
|
||||
if (handle_tx_data_buf_thld_int(i3c_master, int_mask)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (int_mask & I3C_MST_TRANSFER_COMPLETE_INT_ST) {
|
||||
if (handle_transfer_complete_int(i3c_master)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (int_mask & I3C_MST_RX_DATA_BUF_THLD_INT_ST) {
|
||||
handle_rx_data_buf_threshhold_int(i3c_master);
|
||||
}
|
||||
|
||||
if (need_yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t i3c_master_async_transaction_preparation(i3c_master_bus_t *i3c_master_handle)
|
||||
{
|
||||
i3c_master_handle->fsm = I3C_FSM_ENABLE;
|
||||
|
||||
for (int i = 0; i < I3C_TRANS_QUEUE_MAX; i++) {
|
||||
i3c_master_handle->trans_queues[i] = xQueueCreateWithCaps(i3c_master_handle->queue_depth, sizeof(i3c_i2c_transaction_desc_t *), I3C_MASTER_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(i3c_master_handle->trans_queues[i], ESP_ERR_NO_MEM, TAG, "no mem for transaction queue");
|
||||
}
|
||||
|
||||
i3c_master_handle->trans_desc_pool = heap_caps_calloc(i3c_master_handle->queue_depth, sizeof(i3c_i2c_transaction_desc_t), I3C_MASTER_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(i3c_master_handle->trans_desc_pool, ESP_ERR_NO_MEM, TAG, "no mem for transaction desc pool");
|
||||
i3c_i2c_transaction_desc_t *p_trans_desc = NULL;
|
||||
for (int i = 0; i < i3c_master_handle->queue_depth; i++) {
|
||||
p_trans_desc = &i3c_master_handle->trans_desc_pool[i];
|
||||
ESP_RETURN_ON_FALSE(xQueueSend(i3c_master_handle->trans_queues[I3C_TRANS_QUEUE_READY], &p_trans_desc, 0) == pdTRUE,
|
||||
ESP_ERR_INVALID_STATE, TAG, "ready queue full");
|
||||
}
|
||||
i3c_master_handle->i3c_async_addr_table = (i3c_master_address_table_t(*)[SOC_I3C_MASTER_ADDRESS_TABLE_NUM])heap_caps_calloc(i3c_master_handle->queue_depth, sizeof(*i3c_master_handle->i3c_async_addr_table), I3C_MASTER_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(i3c_master_handle->i3c_async_addr_table, ESP_ERR_NO_MEM, TAG, "no mem for address table");
|
||||
|
||||
i3c_master_handle->i3c_async_command_table = (i3c_master_command_table_t(*)[SOC_I3C_MASTER_COMMAND_TABLE_NUM])heap_caps_calloc(i3c_master_handle->queue_depth, sizeof(*i3c_master_handle->i3c_async_command_table), I3C_MASTER_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(i3c_master_handle->i3c_async_command_table, ESP_ERR_NO_MEM, TAG, "no mem for command table");
|
||||
|
||||
i3c_master_handle->ops_prepare_idx = 0;
|
||||
|
||||
i3c_master_handle->async_memory_allocated = true;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i3c_master_init_dma(i3c_master_bus_t *i3c_master_handle, const i3c_master_bus_config_t *bus_config)
|
||||
{
|
||||
// Initialize i3c-dma connection
|
||||
i3c_master_ll_enable_tx_by_dma(i3c_master_handle->hal.dev, true);
|
||||
i3c_master_ll_enable_rx_by_dma(i3c_master_handle->hal.dev, true);
|
||||
|
||||
// Initialize DMA TX channel
|
||||
gdma_channel_alloc_config_t dma_cfg = {};
|
||||
dma_cfg.direction = GDMA_CHANNEL_DIRECTION_TX;
|
||||
ESP_RETURN_ON_ERROR(gdma_new_ahb_channel(&dma_cfg, &i3c_master_handle->dma_tx_chan), TAG, "DMA tx channel alloc failed");
|
||||
gdma_connect(i3c_master_handle->dma_tx_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_I3C, 0));
|
||||
|
||||
gdma_transfer_config_t transfer_cfg = {
|
||||
.access_ext_mem = false,
|
||||
.max_data_burst_size = bus_config->dma_burst_size ? bus_config->dma_burst_size : 16,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gdma_config_transfer(i3c_master_handle->dma_tx_chan, &transfer_cfg), TAG, "Config DMA tx channel transfer failed");
|
||||
|
||||
// create DMA link list
|
||||
size_t int_mem_align = 0;
|
||||
gdma_get_alignment_constraints(i3c_master_handle->dma_tx_chan, &int_mem_align, NULL);
|
||||
size_t buffer_alignment = I3C_ALIGN_UP(int_mem_align, I3C_MASTER_DMA_INTERFACE_ALIGNMENT);
|
||||
size_t num_dma_nodes = esp_dma_calculate_node_count(bus_config->max_transfer_size, buffer_alignment, DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
|
||||
gdma_link_list_config_t dma_link_config = {
|
||||
.buffer_alignment = buffer_alignment, // no special buffer alignment for i3c master buffer
|
||||
.item_alignment = 4, // 4 bytes alignment for AHB-DMA
|
||||
.num_items = num_dma_nodes, // only one item in the link list so far
|
||||
};
|
||||
|
||||
ESP_RETURN_ON_ERROR(gdma_new_link_list(&dma_link_config, &i3c_master_handle->tx_dma_link), TAG, "DMA tx link list alloc failed");
|
||||
|
||||
// Initialize DMA RX channel
|
||||
dma_cfg.direction = GDMA_CHANNEL_DIRECTION_RX;
|
||||
ESP_RETURN_ON_ERROR(gdma_new_ahb_channel(&dma_cfg, &i3c_master_handle->dma_rx_chan), TAG, "DMA rx channel alloc failed");
|
||||
gdma_connect(i3c_master_handle->dma_rx_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_I3C, 0));
|
||||
|
||||
ESP_RETURN_ON_ERROR(gdma_config_transfer(i3c_master_handle->dma_rx_chan, &transfer_cfg), TAG, "Config DMA rx channel transfer failed");
|
||||
|
||||
ESP_RETURN_ON_ERROR(gdma_new_link_list(&dma_link_config, &i3c_master_handle->rx_dma_link), TAG, "DMA rx link list alloc failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t s_i3c_pins_config(const i3c_master_bus_config_t *bus_config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
// SDA pin configurations
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_config->sda_io_num, 1), TAG, "i2c sda pin set level failed");
|
||||
gpio_input_enable(bus_config->sda_io_num);
|
||||
if (bus_config->flags.enable_internal_opendrain) {
|
||||
gpio_od_enable(bus_config->sda_io_num);
|
||||
}
|
||||
if (bus_config->flags.enable_internal_pullup) {
|
||||
gpio_pullup_en(bus_config->sda_io_num);
|
||||
} else {
|
||||
gpio_pullup_dis(bus_config->sda_io_num);
|
||||
}
|
||||
gpio_func_sel(bus_config->sda_io_num, PIN_FUNC_GPIO);
|
||||
esp_rom_gpio_connect_out_signal(bus_config->sda_io_num, i3c_master_periph_signal->sda_out_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(bus_config->sda_io_num, i3c_master_periph_signal->sda_in_sig, 0);
|
||||
|
||||
// SCL pin configurations
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_config->scl_io_num, 1), TAG, "i2c scl pin set level failed");
|
||||
gpio_input_enable(bus_config->scl_io_num);
|
||||
if (bus_config->flags.enable_internal_opendrain) {
|
||||
gpio_od_enable(bus_config->scl_io_num);
|
||||
}
|
||||
if (bus_config->flags.enable_internal_pullup) {
|
||||
gpio_pullup_en(bus_config->scl_io_num);
|
||||
} else {
|
||||
gpio_pullup_dis(bus_config->scl_io_num);
|
||||
}
|
||||
gpio_func_sel(bus_config->scl_io_num, PIN_FUNC_GPIO);
|
||||
esp_rom_gpio_connect_out_signal(bus_config->scl_io_num, i3c_master_periph_signal->scl_out_sig, 0, 0);
|
||||
esp_rom_gpio_connect_in_signal(bus_config->scl_io_num, i3c_master_periph_signal->scl_in_sig, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void i3c_master_deinit_dma(i3c_master_bus_t *i3c_master_handle)
|
||||
{
|
||||
if (i3c_master_handle->dma_tx_chan) {
|
||||
gdma_disconnect(i3c_master_handle->dma_tx_chan);
|
||||
gdma_del_channel(i3c_master_handle->dma_tx_chan);
|
||||
}
|
||||
if (i3c_master_handle->dma_rx_chan) {
|
||||
gdma_disconnect(i3c_master_handle->dma_rx_chan);
|
||||
gdma_del_channel(i3c_master_handle->dma_rx_chan);
|
||||
}
|
||||
if (i3c_master_handle->tx_dma_link) {
|
||||
gdma_del_link_list(i3c_master_handle->tx_dma_link);
|
||||
}
|
||||
if (i3c_master_handle->rx_dma_link) {
|
||||
gdma_del_link_list(i3c_master_handle->rx_dma_link);
|
||||
}
|
||||
}
|
||||
|
||||
static void i3c_master_del_async_transaction_source(i3c_master_bus_t *i3c_master_handle)
|
||||
{
|
||||
for (int i = 0; i < I3C_TRANS_QUEUE_MAX; i++) {
|
||||
if (i3c_master_handle->trans_queues[i]) {
|
||||
vQueueDeleteWithCaps(i3c_master_handle->trans_queues[i]);
|
||||
}
|
||||
}
|
||||
if (i3c_master_handle->trans_desc_pool) {
|
||||
heap_caps_free(i3c_master_handle->trans_desc_pool);
|
||||
}
|
||||
if (i3c_master_handle->i3c_async_addr_table) {
|
||||
heap_caps_free(i3c_master_handle->i3c_async_addr_table);
|
||||
}
|
||||
if (i3c_master_handle->i3c_async_command_table) {
|
||||
heap_caps_free(i3c_master_handle->i3c_async_command_table);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t i3c_master_bus_destroy(i3c_master_bus_handle_t bus_handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "no memory for i3c master bus");
|
||||
|
||||
portENTER_CRITICAL_SAFE(&s_i3c_master_platform.spinlock);
|
||||
if (s_i3c_master_platform.buses[bus_handle->i3c_num] != NULL) {
|
||||
s_i3c_master_platform.buses[bus_handle->i3c_num] = NULL;
|
||||
}
|
||||
portEXIT_CRITICAL_SAFE(&s_i3c_master_platform.spinlock);
|
||||
|
||||
if (bus_handle->bus_lock_mux) {
|
||||
vSemaphoreDeleteWithCaps(bus_handle->bus_lock_mux);
|
||||
bus_handle->bus_lock_mux = NULL;
|
||||
}
|
||||
|
||||
if (bus_handle->event_queue) {
|
||||
vQueueDeleteWithCaps(bus_handle->event_queue);
|
||||
}
|
||||
|
||||
if (bus_handle->intr_handle) {
|
||||
ESP_RETURN_ON_ERROR(esp_intr_free(bus_handle->intr_handle), TAG, "delete interrupt service failed");
|
||||
}
|
||||
|
||||
// Free DMA resources if used
|
||||
if (bus_handle->use_dma_transaction) {
|
||||
i3c_master_deinit_dma(bus_handle);
|
||||
}
|
||||
|
||||
// Free async transaction resources
|
||||
if (bus_handle->async_transaction) {
|
||||
i3c_master_del_async_transaction_source(bus_handle);
|
||||
}
|
||||
|
||||
// Free the handle itself
|
||||
heap_caps_free(bus_handle);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i3c_del_master_bus(i3c_master_bus_handle_t bus_handle)
|
||||
{
|
||||
// Check if the device list is empty
|
||||
if (!SLIST_EMPTY(&bus_handle->device_list)) {
|
||||
ESP_LOGE(TAG, "Cannot delete I3C bus: devices are still attached, please remove all devices and then delete bus");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_ERROR(i3c_master_bus_destroy(bus_handle), TAG, "destroy i3c bus failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_master_bus_handle_t *ret_bus_handle)
|
||||
{
|
||||
ESP_LOGW(TAG, "I3C driver is in developing, not all features are supported so far");
|
||||
ESP_RETURN_ON_FALSE(bus_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(bus_config->sda_io_num) && GPIO_IS_VALID_GPIO(bus_config->scl_io_num), ESP_ERR_INVALID_ARG, TAG, "invalid SDA/SCL pin number");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
i3c_master_bus_t *i3c_master_handle = NULL;
|
||||
|
||||
i3c_master_handle = (i3c_master_bus_t*) heap_caps_calloc(1, sizeof(i3c_master_bus_t), I3C_MASTER_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(i3c_master_handle, ESP_ERR_NO_MEM, TAG, "no mem for i3c master bus handle");
|
||||
|
||||
s_i3c_master_platform.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
portENTER_CRITICAL_SAFE(&s_i3c_master_platform.spinlock);
|
||||
bool bus_found = false;
|
||||
for (int i = 0; i < SOC_I3C_MASTER_PERIPH_NUM; i++) {
|
||||
if (i3c_master_bus_occupied(i) == false) {
|
||||
s_i3c_master_platform.buses[i] = i3c_master_handle;
|
||||
bus_found = true;
|
||||
i3c_master_handle->i3c_num = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL_SAFE(&s_i3c_master_platform.spinlock);
|
||||
if (bus_found == false) {
|
||||
ESP_LOGE(TAG, "acquire i3c master bus failed, no free bus");
|
||||
ret = ESP_ERR_NOT_FOUND;
|
||||
goto err;
|
||||
}
|
||||
|
||||
i3c_master_hal_init(&i3c_master_handle->hal, i3c_master_handle->i3c_num);
|
||||
|
||||
/// clock enable
|
||||
I3C_MASTER_RCC_ATOMIC() {
|
||||
i3c_master_ll_reset_register(i3c_master_handle->hal.dev);
|
||||
i3c_master_ll_enable_bus_clock(i3c_master_handle->hal.dev, true);
|
||||
}
|
||||
I3C_MASTER_CLOCK_SRC_ATOMIC() {
|
||||
i3c_master_ll_enable_controller_clock(i3c_master_handle->hal.dev, true);
|
||||
}
|
||||
|
||||
//// clock selection
|
||||
uint32_t periph_src_clk_hz = 0;
|
||||
i3c_master_handle->clock_source = bus_config->clock_source;
|
||||
|
||||
esp_clk_tree_enable_src((soc_module_clk_t)i3c_master_handle->clock_source, true);
|
||||
I3C_MASTER_CLOCK_SRC_ATOMIC() {
|
||||
i3c_master_ll_set_source_clk(i3c_master_handle->hal.dev, i3c_master_handle->clock_source);
|
||||
}
|
||||
|
||||
i3c_master_handle->use_dma_transaction = bus_config->flags.use_dma;
|
||||
i3c_master_handle->async_transaction = bus_config->flags.enable_async_trans; // TODO: Only use dma can use async transaction.
|
||||
ESP_RETURN_ON_FALSE(!(i3c_master_handle->async_transaction && !i3c_master_handle->use_dma_transaction), ESP_ERR_INVALID_ARG, TAG, "Async transaction requires DMA transaction");
|
||||
|
||||
esp_clk_tree_src_get_freq_hz(i3c_master_handle->clock_source, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &periph_src_clk_hz);
|
||||
|
||||
i3c_master_handle->clock_source_freq = periph_src_clk_hz;
|
||||
i3c_master_handle->queue_depth = bus_config->trans_queue_depth;
|
||||
|
||||
// set timing regs
|
||||
int clock_source_period_ns = 1e9 / periph_src_clk_hz;
|
||||
i3c_master_ll_set_restart_setup_time(i3c_master_handle->hal.dev, I3C_MASTER_LL_DEFAULT_SETUP_TIME, clock_source_period_ns);
|
||||
i3c_master_ll_set_start_hold_time(i3c_master_handle->hal.dev, I3C_MASTER_LL_DEFAULT_SETUP_TIME, clock_source_period_ns);
|
||||
i3c_master_ll_set_stop_hold_time(i3c_master_handle->hal.dev, I3C_MASTER_LL_DEFAULT_SETUP_TIME, clock_source_period_ns);
|
||||
i3c_master_ll_set_stop_setup_time(i3c_master_handle->hal.dev, I3C_MASTER_LL_DEFAULT_SETUP_TIME, clock_source_period_ns);
|
||||
|
||||
i3c_master_handle->bus_lock_mux = xSemaphoreCreateBinaryWithCaps(I3C_MASTER_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(i3c_master_handle->bus_lock_mux, ESP_ERR_NO_MEM, err, TAG, "No memory for binary semaphore");
|
||||
xSemaphoreGive(i3c_master_handle->bus_lock_mux);
|
||||
|
||||
i3c_master_handle->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
/// pins configuration
|
||||
ESP_GOTO_ON_ERROR(s_i3c_pins_config(bus_config), err, TAG, "pins initialization failed");
|
||||
|
||||
esp_cache_get_alignment(MALLOC_CAP_INTERNAL, &i3c_master_handle->cache_line_size);
|
||||
|
||||
#if CONFIG_PM_ENABLE
|
||||
bool need_pm_lock = true;
|
||||
// to make the I2C work reliable, the source clock must stay alive and unchanged
|
||||
// driver will create different pm lock for that purpose, according to different clock source
|
||||
esp_pm_lock_type_t pm_lock_type = ESP_PM_NO_LIGHT_SLEEP;
|
||||
|
||||
if (need_pm_lock) {
|
||||
sprintf(i3c_master_handle->pm_lock_name, "I3C_MASTER"); // e.g. PORT_0
|
||||
ret = esp_pm_lock_create(pm_lock_type, 0, i3c_master_handle->pm_lock_name, &i3c_master_handle->pm_lock);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "create pm lock failed");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (i3c_master_handle->use_dma_transaction == false) {
|
||||
i3c_master_ll_set_rx_data_fifo_wm_threshold(i3c_master_handle->hal.dev, I3C_MASTER_LL_FIFO_WM_LENGTH_16);
|
||||
i3c_master_ll_set_tx_data_fifo_wm_threshold(i3c_master_handle->hal.dev, I3C_MASTER_LL_FIFO_WM_LENGTH_16);
|
||||
}
|
||||
|
||||
i3c_master_handle->event_queue = xQueueCreateWithCaps(1, sizeof(i3c_master_event_t), I3C_MASTER_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(i3c_master_handle->event_queue, ESP_ERR_NO_MEM, err, TAG, "No memory for event queue");
|
||||
|
||||
SLIST_INIT(&i3c_master_handle->device_list);
|
||||
|
||||
// interrupt configurations
|
||||
if (bus_config->intr_priority) {
|
||||
ESP_GOTO_ON_FALSE(1 << (bus_config->intr_priority) & I3C_MASTER_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, err, TAG, "invalid interrupt priority:%d", bus_config->intr_priority);
|
||||
}
|
||||
|
||||
int isr_flags = I3C_MASTER_INTR_ALLOC_FLAG;
|
||||
if (bus_config->intr_priority) {
|
||||
isr_flags |= 1 << (bus_config->intr_priority);
|
||||
}
|
||||
|
||||
ret = esp_intr_alloc_intrstatus(i3c_master_periph_signal->irq, isr_flags, (uint32_t)i3c_master_ll_get_interrupt_status_reg(i3c_master_handle->hal.dev), I3C_LL_MASTER_EVENT_INTR, i3c_master_isr_handler_default, i3c_master_handle, &i3c_master_handle->intr_handle);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "install i3c master interrupt failed");
|
||||
|
||||
// Initialize dma if dma transaction is needed.
|
||||
if (i3c_master_handle->use_dma_transaction) {
|
||||
ESP_GOTO_ON_ERROR(i3c_master_init_dma(i3c_master_handle, bus_config), err, TAG, "dma initialization failed");
|
||||
}
|
||||
|
||||
// Initialize async transaction memory if async transaction is needed.
|
||||
if (i3c_master_handle->async_transaction) {
|
||||
ESP_GOTO_ON_ERROR(i3c_master_async_transaction_preparation(i3c_master_handle), err, TAG, "async transaction resource failed");
|
||||
}
|
||||
|
||||
*ret_bus_handle = i3c_master_handle;
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (i3c_master_handle) {
|
||||
i3c_master_bus_destroy(i3c_master_handle);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i3c_master_bus_rm_i2c_device(i3c_master_i2c_device_handle_t handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE((handle != NULL), ESP_ERR_INVALID_ARG, TAG, "this device is not initialized");
|
||||
|
||||
i3c_master_bus_handle_t i3c_master = handle->bus_handle;
|
||||
i3c_i2c_master_device_list_t *device_item;
|
||||
xSemaphoreTake(i3c_master->bus_lock_mux, portMAX_DELAY);
|
||||
SLIST_FOREACH(device_item, &i3c_master->device_list, next) {
|
||||
if (handle == device_item->i2c_dev) {
|
||||
SLIST_REMOVE(&i3c_master->device_list, device_item, i3c_i2c_master_device_list, next);
|
||||
free(device_item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(i3c_master->bus_lock_mux);
|
||||
|
||||
if (handle) {
|
||||
free(handle);
|
||||
handle = NULL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i3c_master_bus_add_i2c_device(i3c_master_bus_handle_t bus_handle, const i3c_device_i2c_config_t *dev_config, i3c_master_i2c_device_handle_t *ret_handle)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE((bus_handle != NULL), ESP_ERR_INVALID_ARG, TAG, "this bus is not initialized, please call `i3c_new_master_bus`");
|
||||
ESP_RETURN_ON_FALSE(dev_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(dev_config->scl_speed_hz > 0, ESP_ERR_INVALID_ARG, TAG, "invalid scl frequency");
|
||||
|
||||
i3c_master_i2c_dev_t *i3c_i2c_dev = (i3c_master_i2c_dev_t *)heap_caps_calloc(1, sizeof(i3c_master_i2c_dev_t), I3C_MASTER_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(i3c_i2c_dev, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
i3c_i2c_dev->address = dev_config->device_address;
|
||||
i3c_i2c_dev->bus_handle = bus_handle;
|
||||
i3c_i2c_dev->scl_speed_hz = dev_config->scl_speed_hz;
|
||||
i3c_i2c_dev->bypass_ack_check = dev_config->flags.disable_ack_check;
|
||||
|
||||
*ret_handle = i3c_i2c_dev;
|
||||
|
||||
i3c_i2c_master_device_list_t *device_item = (i3c_i2c_master_device_list_t *)heap_caps_calloc(1, sizeof(i3c_i2c_master_device_list_t), I3C_MASTER_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE((device_item != NULL), ESP_ERR_NO_MEM, err, TAG, "no memory for i3c-i2c device item`");
|
||||
device_item->i2c_dev = i3c_i2c_dev;
|
||||
|
||||
xSemaphoreTake(bus_handle->bus_lock_mux, portMAX_DELAY);
|
||||
SLIST_INSERT_HEAD(&bus_handle->device_list, device_item, next);
|
||||
xSemaphoreGive(bus_handle->bus_lock_mux);
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (i3c_i2c_dev) {
|
||||
i3c_master_bus_rm_i2c_device(i3c_i2c_dev);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t i3c_master_prepare_async_transaction(i3c_master_i2c_device_handle_t dev_handle, i3c_i2c_transaction_desc_t *trans_desc)
|
||||
{
|
||||
i3c_master_bus_t *bus_handle = dev_handle->bus_handle;
|
||||
|
||||
memcpy(bus_handle->i3c_async_addr_table[bus_handle->ops_prepare_idx], trans_desc->addr_table, trans_desc->addr_table_num * sizeof(i3c_master_address_table_t));
|
||||
memcpy(bus_handle->i3c_async_command_table[bus_handle->ops_prepare_idx], trans_desc->command_table, trans_desc->command_table_num * sizeof(i3c_master_command_table_t));
|
||||
|
||||
i3c_i2c_transaction_desc_t *t = NULL;
|
||||
|
||||
if (xQueueReceive(bus_handle->trans_queues[I3C_TRANS_QUEUE_READY], &t, 0) != pdTRUE) {
|
||||
if (xQueueReceive(bus_handle->trans_queues[I3C_TRANS_QUEUE_COMPLETE], &t, 0) == pdTRUE) {
|
||||
assert(t != NULL);
|
||||
bus_handle->num_trans_inflight--;
|
||||
}
|
||||
}
|
||||
|
||||
memset(t, 0, sizeof(i3c_i2c_transaction_desc_t));
|
||||
t->addr_table = bus_handle->i3c_async_addr_table[bus_handle->ops_prepare_idx];
|
||||
t->addr_table_num = trans_desc->addr_table_num;
|
||||
t->command_table = bus_handle->i3c_async_command_table[bus_handle->ops_prepare_idx];
|
||||
t->command_table_num = trans_desc->command_table_num;
|
||||
t->write_buffer = trans_desc->write_buffer;
|
||||
t->read_buffer = trans_desc->read_buffer;
|
||||
t->scl_speed_hz = trans_desc->scl_speed_hz;
|
||||
|
||||
ESP_RETURN_ON_FALSE(xQueueSend(bus_handle->trans_queues[I3C_TRANS_QUEUE_PROGRESS], &t, 0) == pdTRUE, ESP_ERR_NO_MEM, TAG, "i3c-i2c transaction queue full");
|
||||
bus_handle->ops_prepare_idx = (bus_handle->ops_prepare_idx + 1) % bus_handle->queue_depth;
|
||||
bus_handle->num_trans_inflight++;
|
||||
|
||||
i3c_fsm_t expected_fsm = I3C_FSM_ENABLE;
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (atomic_compare_exchange_strong(&bus_handle->fsm, &expected_fsm, I3C_FSM_RUN_WAIT)) {
|
||||
if (xQueueReceive(bus_handle->trans_queues[I3C_TRANS_QUEUE_PROGRESS], &t, 0)) {
|
||||
atomic_store(&bus_handle->fsm, I3C_FSM_RUN);
|
||||
ret = s_do_dma_transaction(bus_handle, t);
|
||||
assert(ret == ESP_OK);
|
||||
} else {
|
||||
atomic_store(&bus_handle->fsm, I3C_FSM_ENABLE);
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i3c_master_prepare_fifo_transaction(i3c_master_i2c_device_handle_t dev_handle, i3c_i2c_transaction_desc_t *trans_desc)
|
||||
{
|
||||
dev_handle->bus_handle->read_buffer_left_size = trans_desc->read_buffer ? trans_desc->command_table[0].cmd_h.regular.dl : 0;
|
||||
dev_handle->bus_handle->read_fifo_buffer_pointer = trans_desc->read_buffer;
|
||||
|
||||
size_t actual_write_size = 0;
|
||||
|
||||
portENTER_CRITICAL_SAFE(&dev_handle->bus_handle->spinlock);
|
||||
i3c_master_ll_set_i2c_fast_mode_timing(dev_handle->bus_handle->hal.dev, dev_handle->bus_handle->clock_source_freq, trans_desc->scl_speed_hz);
|
||||
|
||||
i3c_master_ll_set_address_device_table(dev_handle->bus_handle->hal.dev, trans_desc->addr_table, trans_desc->addr_table_num);
|
||||
i3c_master_ll_set_command(dev_handle->bus_handle->hal.dev, trans_desc->command_table, trans_desc->command_table_num);
|
||||
|
||||
i3c_master_ll_enable_intr_mask(dev_handle->bus_handle->hal.dev, I3C_LL_MASTER_TRANSMIT_EVENT_INTR | I3C_LL_MASTER_RECEIVE_EVENT_INTR);
|
||||
i3c_master_ll_clear_intr_mask(dev_handle->bus_handle->hal.dev, I3C_LL_MASTER_TRANSMIT_EVENT_INTR | I3C_LL_MASTER_RECEIVE_EVENT_INTR);
|
||||
if (trans_desc->write_buffer && trans_desc->command_table[0].cmd_h.regular.dl >= I3C_LL_MASTER_FIFO_LENGTH) {
|
||||
actual_write_size = I3C_LL_MASTER_FIFO_LENGTH;
|
||||
} else {
|
||||
actual_write_size = trans_desc->write_buffer ? trans_desc->command_table[0].cmd_h.regular.dl : 0;
|
||||
}
|
||||
|
||||
i3c_master_ll_write_tx_port(dev_handle->bus_handle->hal.dev, trans_desc->write_buffer, actual_write_size);
|
||||
|
||||
dev_handle->bus_handle->write_fifo_buffer_pointer = trans_desc->write_buffer;
|
||||
dev_handle->bus_handle->write_buffer_left_size = trans_desc->write_buffer ? trans_desc->command_table[0].cmd_h.regular.dl : 0;
|
||||
|
||||
dev_handle->bus_handle->write_fifo_buffer_pointer += actual_write_size;
|
||||
dev_handle->bus_handle->write_buffer_left_size -= actual_write_size;
|
||||
|
||||
i3c_master_ll_start_transaction(dev_handle->bus_handle->hal.dev);
|
||||
portEXIT_CRITICAL_SAFE(&dev_handle->bus_handle->spinlock);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles the preparation and execution of an I3C or I2C transaction on the I3C master bus.
|
||||
* Depending on the configuration, the transaction can be handled asynchronously, using DMA, or via FIFO.
|
||||
* It also manages the bus locking mechanism and power management (PM) locks, ensuring thread safety
|
||||
* and power optimization during the transaction.
|
||||
*/
|
||||
static esp_err_t i3c_master_prepare_transaction(i3c_master_i2c_device_handle_t dev_handle, i3c_i2c_transaction_desc_t *trans_desc, int xfer_timeout_ms)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
TickType_t ticks_to_wait = (xfer_timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(xfer_timeout_ms);
|
||||
|
||||
if (xSemaphoreTake(dev_handle->bus_handle->bus_lock_mux, ticks_to_wait) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "I3C software timeout");
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (dev_handle->bus_handle->pm_lock) {
|
||||
ESP_GOTO_ON_ERROR(esp_pm_lock_acquire(dev_handle->bus_handle->pm_lock), err, TAG, "acquire pm_lock failed");
|
||||
}
|
||||
|
||||
if (dev_handle->bus_handle->async_transaction) {
|
||||
ESP_GOTO_ON_ERROR(i3c_master_prepare_async_transaction(dev_handle, trans_desc), err, TAG, "Prepare async transaction failed");
|
||||
} else if (dev_handle->bus_handle->use_dma_transaction) {
|
||||
ESP_GOTO_ON_ERROR(s_do_dma_transaction(dev_handle->bus_handle, trans_desc), err, TAG, "dma transaction failed");
|
||||
} else {
|
||||
ESP_GOTO_ON_ERROR(i3c_master_prepare_fifo_transaction(dev_handle, trans_desc), err, TAG, "Prepare fifo transaction failed");
|
||||
}
|
||||
|
||||
if (dev_handle->bus_handle->async_transaction == false) {
|
||||
i3c_master_event_t event;
|
||||
if (xQueueReceive(dev_handle->bus_handle->event_queue, &event, ticks_to_wait) == pdFALSE) {
|
||||
ESP_GOTO_ON_FALSE(NULL, ESP_ERR_TIMEOUT, err, TAG, "event queue wait timeout. Please check whether stretch happened on i3c bus");
|
||||
} else {
|
||||
if (event == I3C_MASTER_EVENT_NACK) {
|
||||
ESP_GOTO_ON_FALSE(NULL, ESP_ERR_INVALID_STATE, err, TAG, "i3c-i2c nack detected");
|
||||
}
|
||||
if (event == I3C_MASTER_EVENT_DONE) {
|
||||
if (dev_handle->bus_handle->use_dma_transaction) {
|
||||
if (trans_desc->read_buffer) {
|
||||
size_t dma_rcv_size = gdma_link_count_buffer_size_till_eof(dev_handle->bus_handle->rx_dma_link, 0);
|
||||
size_t c2m_aligned_size = I3C_ALIGN_UP(dma_rcv_size, dev_handle->bus_handle->cache_line_size);
|
||||
ESP_GOTO_ON_ERROR(esp_cache_msync(trans_desc->read_buffer, c2m_aligned_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C), err, TAG, "memory sync failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (dev_handle->bus_handle->pm_lock) {
|
||||
ESP_GOTO_ON_ERROR(esp_pm_lock_release(dev_handle->bus_handle->pm_lock), err, TAG, "release pm_lock failed");
|
||||
}
|
||||
xSemaphoreGive(dev_handle->bus_handle->bus_lock_mux);
|
||||
return ret;
|
||||
|
||||
err:
|
||||
xSemaphoreGive(dev_handle->bus_handle->bus_lock_mux);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i3c_master_i2c_device_transmit(i3c_master_i2c_device_handle_t dev_handle, const uint8_t *write_buffer, size_t write_size, int xfer_timeout_ms)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "i3c-i2c handle not initialized");
|
||||
ESP_RETURN_ON_FALSE((write_buffer != NULL) && (write_size > 0), ESP_ERR_INVALID_ARG, TAG, "i3c-i2c transmit buffer or size invalid");
|
||||
|
||||
if (dev_handle->bus_handle->use_dma_transaction) {
|
||||
ESP_RETURN_ON_FALSE((((uintptr_t)write_buffer & (I3C_MASTER_DMA_INTERFACE_ALIGNMENT - 1)) == 0), ESP_ERR_INVALID_ARG, TAG, "when dma transaction, write buffer address must be %d bytes aligned", I3C_MASTER_DMA_INTERFACE_ALIGNMENT);
|
||||
}
|
||||
|
||||
i3c_master_address_table_t addr_table[1] = {
|
||||
{.dat.i2c = {.static_addr = dev_handle->address, .d = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}},
|
||||
};
|
||||
|
||||
i3c_master_command_table_t command[1] = {
|
||||
{.cmd_l.regular = {.cmd_attr = I3C_MASTER_REGULAR_COMMAND, .tid = 0x0, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = 0x0, .rnw = I3C_MASTER_WRITE_TRANSFER, .roc = (dev_handle->bypass_ack_check ? 0x0 : 0x1), .toc = 0x1}, .cmd_h.regular = {.dl = write_size}},
|
||||
};
|
||||
|
||||
i3c_i2c_transaction_desc_t trans_desc = {
|
||||
.addr_table = addr_table,
|
||||
.addr_table_num = 1,
|
||||
.command_table = command,
|
||||
.command_table_num = 1,
|
||||
.write_buffer = (uint8_t *)write_buffer,
|
||||
.read_buffer = NULL,
|
||||
.scl_speed_hz = dev_handle->scl_speed_hz,
|
||||
};
|
||||
|
||||
return i3c_master_prepare_transaction(dev_handle, &trans_desc, xfer_timeout_ms);
|
||||
}
|
||||
|
||||
esp_err_t i3c_master_i2c_device_receive(i3c_master_i2c_device_handle_t dev_handle, uint8_t *read_buffer, size_t read_size, int xfer_timeout_ms)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "i3c-i2c handle not initialized");
|
||||
ESP_RETURN_ON_FALSE((read_buffer != NULL) && (read_size > 0), ESP_ERR_INVALID_ARG, TAG, "i3c-i2c receive buffer or size invalid");
|
||||
|
||||
if (dev_handle->bus_handle->use_dma_transaction) {
|
||||
ESP_RETURN_ON_FALSE((((uintptr_t)read_buffer & (dev_handle->bus_handle->cache_line_size - 1)) == 0), ESP_ERR_INVALID_ARG, TAG, "when dma transaction, read buffer address must be %d bytes aligned", dev_handle->bus_handle->cache_line_size);
|
||||
}
|
||||
|
||||
i3c_master_address_table_t addr_table[1] = {
|
||||
{.dat.i2c = {.static_addr = dev_handle->address, .d = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}},
|
||||
};
|
||||
|
||||
i3c_master_command_table_t command[1] = {
|
||||
{.cmd_l.regular = {.cmd_attr = I3C_MASTER_REGULAR_COMMAND, .tid = 0x0, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = 0x0, .rnw = I3C_MASTER_READ_TRANSFER, .roc = (dev_handle->bypass_ack_check ? 0x0 : 0x1), .toc = 0x1}, .cmd_h.regular = {.dl = read_size}},
|
||||
};
|
||||
|
||||
i3c_i2c_transaction_desc_t trans_desc = {
|
||||
.addr_table = addr_table,
|
||||
.addr_table_num = 1,
|
||||
.command_table = command,
|
||||
.command_table_num = 1,
|
||||
.write_buffer = NULL,
|
||||
.read_buffer = read_buffer,
|
||||
.scl_speed_hz = dev_handle->scl_speed_hz,
|
||||
};
|
||||
|
||||
return i3c_master_prepare_transaction(dev_handle, &trans_desc, xfer_timeout_ms);
|
||||
}
|
||||
|
||||
esp_err_t i3c_master_i2c_device_transmit_receive(i3c_master_i2c_device_handle_t dev_handle, const uint8_t *write_buffer, size_t write_size, uint8_t *read_buffer, size_t read_size, int xfer_timeout_ms)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "i3c-i2c handle not initialized");
|
||||
ESP_RETURN_ON_FALSE((write_buffer != NULL) && (write_size > 0), ESP_ERR_INVALID_ARG, TAG, "i3c-i2c transmit buffer or size invalid");
|
||||
ESP_RETURN_ON_FALSE((read_buffer != NULL) && (read_size > 0), ESP_ERR_INVALID_ARG, TAG, "i3c-i2c receive buffer or size invalid");
|
||||
|
||||
if (dev_handle->bus_handle->use_dma_transaction) {
|
||||
ESP_RETURN_ON_FALSE((((uintptr_t)read_buffer & (dev_handle->bus_handle->cache_line_size - 1)) == 0), ESP_ERR_INVALID_ARG, TAG, "when dma transaction, read buffer address must be %d bytes aligned", dev_handle->bus_handle->cache_line_size);
|
||||
ESP_RETURN_ON_FALSE((((uintptr_t)write_buffer & (I3C_MASTER_DMA_INTERFACE_ALIGNMENT - 1)) == 0), ESP_ERR_INVALID_ARG, TAG, "when dma transaction, write buffer address must be %d bytes aligned", I3C_MASTER_DMA_INTERFACE_ALIGNMENT);
|
||||
}
|
||||
|
||||
i3c_master_address_table_t addr_table[1] = {
|
||||
{.dat.i2c = {.static_addr = dev_handle->address, .d = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}},
|
||||
};
|
||||
|
||||
i3c_master_command_table_t command[2] = {
|
||||
{.cmd_l.regular = {.cmd_attr = I3C_MASTER_REGULAR_COMMAND, .tid = 0x0, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = 0x0, .rnw = I3C_MASTER_WRITE_TRANSFER, .roc = (dev_handle->bypass_ack_check ? 0x0 : 0x1), .toc = 0x0}, .cmd_h.regular = {.dl = write_size}},
|
||||
{.cmd_l.regular = {.cmd_attr = I3C_MASTER_REGULAR_COMMAND, .tid = 0x0, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = 0x0, .rnw = I3C_MASTER_READ_TRANSFER, .roc = (dev_handle->bypass_ack_check ? 0x0 : 0x1), .toc = 0x1}, .cmd_h.regular = {.dl = read_size}},
|
||||
};
|
||||
|
||||
i3c_i2c_transaction_desc_t trans_desc = {
|
||||
.addr_table = addr_table,
|
||||
.addr_table_num = 1,
|
||||
.command_table = command,
|
||||
.command_table_num = 2,
|
||||
.write_buffer = (uint8_t *)write_buffer,
|
||||
.read_buffer = read_buffer,
|
||||
.scl_speed_hz = dev_handle->scl_speed_hz,
|
||||
};
|
||||
|
||||
return i3c_master_prepare_transaction(dev_handle, &trans_desc, xfer_timeout_ms);
|
||||
}
|
||||
|
||||
esp_err_t i3c_master_i2c_device_register_event_callbacks(i3c_master_i2c_device_handle_t dev_handle, const i3c_master_i2c_event_callbacks_t *cbs, void *user_data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "i3c-i2c handle not initialized");
|
||||
|
||||
if (dev_handle->bus_handle->async_transaction == false) {
|
||||
ESP_LOGE(TAG, "async transaction is not enabled, callback is not needed");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
#if CONFIG_I3C_MASTER_ISR_CACHE_SAFE
|
||||
if (cbs->on_trans_done) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_trans_done), ESP_ERR_INVALID_ARG, TAG, "i3c trans done callback not in IRAM");
|
||||
}
|
||||
if (user_data) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
|
||||
}
|
||||
#endif // CONFIG_I3C_MASTER_ISR_CACHE_SAFE
|
||||
|
||||
dev_handle->on_trans_done = cbs->on_trans_done;
|
||||
dev_handle->user_ctx = user_data;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i3c_master_bus_wait_all_done(i3c_master_bus_handle_t bus_handle, int timeout_ms)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
TickType_t wait_ticks = timeout_ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||
i3c_i2c_transaction_desc_t *t = NULL;
|
||||
|
||||
size_t cnt = bus_handle->num_trans_inflight;
|
||||
for (size_t i = 0; i < cnt; i++) {
|
||||
ESP_RETURN_ON_FALSE(xQueueReceive(bus_handle->trans_queues[I3C_TRANS_QUEUE_COMPLETE], &t, wait_ticks) == pdTRUE,
|
||||
ESP_ERR_TIMEOUT, TAG, "flush timeout");
|
||||
ESP_RETURN_ON_FALSE(xQueueSend(bus_handle->trans_queues[I3C_TRANS_QUEUE_READY], &t, 0) == pdTRUE,
|
||||
ESP_ERR_INVALID_STATE, TAG, "ready queue full");
|
||||
bus_handle->num_trans_inflight--;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i3c_master_toggle_transfer_async_mode(i3c_master_bus_handle_t bus_handle, bool async_mode)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(bus_handle->async_memory_allocated, ESP_ERR_INVALID_ARG, TAG, "The async memory is not initialized, which must be initialized first");
|
||||
|
||||
// Check current mode
|
||||
bool cur_mode_async = bus_handle->async_transaction;
|
||||
if (cur_mode_async == async_mode) {
|
||||
ESP_LOGW(TAG, "The current mode is as same as the mode you want, nothing changed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (async_mode == false) {
|
||||
// If this API is used to switch mode from async to sync, must check if there is any remaining check.
|
||||
if (bus_handle->num_trans_inflight != 0) {
|
||||
ESP_LOGE(TAG, "There is still some remaining transaction in queue, switch mode failed");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bus_handle->async_transaction = async_mode;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
174
components/esp_driver_i3c/i3c_master_private.h
Normal file
174
components/esp_driver_i3c/i3c_master_private.h
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <sys/queue.h>
|
||||
#include "hal/i3c_master_types.h"
|
||||
#include "hal/i3c_master_hal.h"
|
||||
#include "esp_dma_utils.h"
|
||||
#include "esp_private/gdma.h"
|
||||
#include "esp_private/gdma_link.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "esp_pm.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define I3C_MASTER_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED
|
||||
|
||||
#define I3C_MASTER_PM_LOCK_NAME_LEN_MAX 16
|
||||
|
||||
// interface between i3c and dma need 4 bytes aligned.
|
||||
#define I3C_MASTER_DMA_INTERFACE_ALIGNMENT (4)
|
||||
|
||||
#if SOC_PERIPH_CLK_CTRL_SHARED
|
||||
#define I3C_MASTER_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#else
|
||||
#define I3C_MASTER_CLOCK_SRC_ATOMIC()
|
||||
#endif
|
||||
|
||||
#if !SOC_RCC_IS_INDEPENDENT
|
||||
#define I3C_MASTER_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#else
|
||||
#define I3C_MASTER_RCC_ATOMIC()
|
||||
#endif
|
||||
|
||||
#if CONFIG_I3C_MASTER_ISR_CACHE_SAFE
|
||||
#define I3C_MASTER_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#else
|
||||
#define I3C_MASTER_MEM_ALLOC_CAPS (MALLOC_CAP_DEFAULT)
|
||||
#endif
|
||||
|
||||
#if CONFIG_I3C_MASTER_ISR_CACHE_SAFE
|
||||
#define I3C_MASTER_INTR_ALLOC_FLAG (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LOWMED)
|
||||
#else
|
||||
#define I3C_MASTER_INTR_ALLOC_FLAG (ESP_INTR_FLAG_LOWMED)
|
||||
#endif
|
||||
|
||||
#define I3C_ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
|
||||
/**
|
||||
* @brief Forward declaration of the I3C master bus structure.
|
||||
*/
|
||||
typedef struct i3c_master_bus_t i3c_master_bus_t;
|
||||
|
||||
/**
|
||||
* @brief Forward declaration of the I3C master I2C device structure.
|
||||
*/
|
||||
typedef struct i3c_master_i2c_dev_t i3c_master_i2c_dev_t;
|
||||
|
||||
/**
|
||||
* @brief I3C master bus port number.
|
||||
*/
|
||||
typedef int i3c_master_bus_num_t;
|
||||
|
||||
/**
|
||||
* @brief Structure representing a list of I2C devices managed by the I3C master.
|
||||
*/
|
||||
typedef struct i3c_i2c_master_device_list {
|
||||
i3c_master_i2c_dev_t *i2c_dev; /**< Pointer to the I2C device structure. */
|
||||
SLIST_ENTRY(i3c_i2c_master_device_list) next; /**< Pointer to the next device in the single-linked list. */
|
||||
} i3c_i2c_master_device_list_t;
|
||||
|
||||
/**
|
||||
* @brief Structure representing a transaction descriptor for I3C/I2C operations.
|
||||
*/
|
||||
typedef struct {
|
||||
i3c_master_address_table_t *addr_table; /**< Pointer to the address table, mapping device addresses. */
|
||||
size_t addr_table_num; /**< Number of entries in the address table. */
|
||||
i3c_master_command_table_t *command_table; /**< Pointer to the command table for the transaction. */
|
||||
size_t command_table_num; /**< Number of commands in the command table. */
|
||||
uint8_t *write_buffer; /**< Pointer to the data buffer for writing. */
|
||||
uint8_t *read_buffer; /**< Pointer to the data buffer for reading. */
|
||||
uint32_t scl_speed_hz; /**< Speed of the SCL clock in Hz for the transaction. */
|
||||
} i3c_i2c_transaction_desc_t;
|
||||
|
||||
/**
|
||||
* @brief Enumeration representing the states of the I3C transaction queue.
|
||||
*/
|
||||
enum {
|
||||
I3C_TRANS_QUEUE_READY, /**< The transaction queue is ready to accept new transactions. */
|
||||
I3C_TRANS_QUEUE_PROGRESS, /**< A transaction is currently in progress. */
|
||||
I3C_TRANS_QUEUE_COMPLETE, /**< All transactions in the queue are completed. */
|
||||
I3C_TRANS_QUEUE_MAX, /**< Placeholder for the maximum number of states (not a valid state). */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Enumeration representing the states of the I3C finite state machine (FSM).
|
||||
*/
|
||||
typedef enum {
|
||||
I3C_FSM_ENABLE_WAIT, /**< FSM is waiting to enable the I3C system. */
|
||||
I3C_FSM_ENABLE, /**< FSM is enabling the I3C system. */
|
||||
I3C_FSM_RUN_WAIT, /**< FSM is waiting to transition to the running state. */
|
||||
I3C_FSM_RUN, /**< FSM is in the running state, actively handling I3C operations. */
|
||||
} i3c_fsm_t;
|
||||
|
||||
/**
|
||||
* @brief Structure representing the I3C master bus.
|
||||
*
|
||||
* This structure contains all the resources and states needed for
|
||||
* managing an I3C master bus, including hardware abstraction, transaction
|
||||
* handling, and synchronization.
|
||||
*/
|
||||
struct i3c_master_bus_t {
|
||||
i3c_master_bus_num_t i3c_num; /**< I3C bus number. */
|
||||
i3c_master_hal_context_t hal; /**< HAL layer context for each port (bus). */
|
||||
i3c_master_clock_source_t clock_source; /**< Source of the I3C clock. */
|
||||
uint32_t clock_source_freq; /**< Frequency of the clock source in Hz. */
|
||||
bool use_dma_transaction; /**< Flag indicating whether DMA is used for transactions. */
|
||||
bool async_transaction; /**< Flag indicating whether asynchronous transactions are enabled. */
|
||||
QueueHandle_t event_queue; /**< Queue for handling I3C events. */
|
||||
SLIST_HEAD(i3c_i2c_master_device_list_head, i3c_i2c_master_device_list) device_list; /**< List of devices on the bus. */
|
||||
i3c_i2c_transaction_desc_t *cur_trans; /**< Pointer to the current transaction descriptor. */
|
||||
i3c_i2c_transaction_desc_t *trans_desc_pool; /**< Pool of pre-allocated transaction descriptors. */
|
||||
uint32_t ops_prepare_idx; /**< Index for preparing operations. */
|
||||
bool async_memory_allocated; /**< The async transaction is allocated or not */
|
||||
i3c_master_address_table_t (*i3c_async_addr_table)[SOC_I3C_MASTER_ADDRESS_TABLE_NUM]; /**< Address table for asynchronous transactions. */
|
||||
i3c_master_command_table_t (*i3c_async_command_table)[SOC_I3C_MASTER_COMMAND_TABLE_NUM]; /**< Command table for asynchronous transactions. */
|
||||
QueueHandle_t trans_queues[I3C_TRANS_QUEUE_MAX]; /**< Array of transaction queues for different states. */
|
||||
intr_handle_t intr_handle; /**< Interrupt handle for I3C interrupts. */
|
||||
_Atomic i3c_fsm_t fsm; /**< Current state of the I3C finite state machine. */
|
||||
uint8_t num_trans_inflight; /**< Number of in-flight transactions. */
|
||||
size_t queue_depth; /**< Depth of the transaction queue. */
|
||||
SemaphoreHandle_t bus_lock_mux; /**< Semaphore for bus locking. */
|
||||
portMUX_TYPE spinlock; /**< Spinlock for protecting critical sections. */
|
||||
esp_pm_lock_handle_t pm_lock; /**< Power management lock handle. */
|
||||
size_t cache_line_size; /**< Cache line size for doing C2M operation */
|
||||
#if CONFIG_PM_ENABLE
|
||||
char pm_lock_name[I3C_MASTER_PM_LOCK_NAME_LEN_MAX]; /**< Name of the power management lock (if enabled). */
|
||||
#endif
|
||||
uint8_t *write_fifo_buffer_pointer; /**< Pointer to the write FIFO buffer. */
|
||||
size_t write_buffer_left_size; /**< Remaining size of the write buffer. */
|
||||
uint8_t *read_fifo_buffer_pointer; /**< Pointer to the read FIFO buffer. */
|
||||
size_t read_buffer_left_size; /**< Remaining size of the read buffer. */
|
||||
gdma_channel_handle_t dma_tx_chan; /**< DMA channel handle for TX. */
|
||||
gdma_channel_handle_t dma_rx_chan; /**< DMA channel handle for RX. */
|
||||
gdma_link_list_handle_t tx_dma_link; /**< Linked list for TX DMA. */
|
||||
gdma_link_list_handle_t rx_dma_link; /**< Linked list for RX DMA. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Structure representing an I2C device managed by the I3C master.
|
||||
*
|
||||
* This structure contains all the necessary information and configuration
|
||||
* required to communicate with an I2C device on an I3C master bus.
|
||||
*/
|
||||
struct i3c_master_i2c_dev_t {
|
||||
i3c_master_bus_t *bus_handle; /**< Handle to the I3C master bus managing this device. */
|
||||
uint8_t address; /**< 7-bit I2C address of the device. */
|
||||
uint32_t scl_speed_hz; /**< I2C clock speed for this device, in Hz. */
|
||||
bool bypass_ack_check; /**< Flag indicating whether to bypass ACK checking during communication. */
|
||||
i3c_master_i2c_callback_t on_trans_done; /**< Callback function invoked upon transaction completion. */
|
||||
void *user_ctx; /**< User-defined context passed to the callback function. */
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
105
components/esp_driver_i3c/include/driver/i3c_master.h
Normal file
105
components/esp_driver_i3c/include/driver/i3c_master.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "hal/gpio_types.h"
|
||||
#include "driver/i3c_master_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configuration structure for I3C master bus
|
||||
*
|
||||
* This structure defines the configuration parameters for the I3C master bus,
|
||||
* including GPIO pins, clock source, queue depth, interrupt priority, and other options.
|
||||
*/
|
||||
typedef struct {
|
||||
gpio_num_t sda_io_num; /*!< GPIO number of I3C SDA signal, pulled-up internally */
|
||||
gpio_num_t scl_io_num; /*!< GPIO number of I3C SCL signal, pulled-up internally */
|
||||
i3c_master_clock_source_t clock_source; /*!< Clock source of I3C master bus */
|
||||
size_t trans_queue_depth; /*!< Depth of internal transfer queue, increase this value can support more transfers pending in the background, only valid in asynchronous transaction. (Typically max_device_num * per_transaction)*/
|
||||
size_t max_transfer_size; /*!< Maximum transfer size in one transaction, in bytes. This decides the number of DMA nodes will be used for each transaction */
|
||||
size_t dma_burst_size; /*!< DMA burst size, in bytes */
|
||||
int intr_priority; /*!< I3C interrupt priority, if set to 0, driver will select the default priority (1,2,3). */
|
||||
struct {
|
||||
uint32_t enable_internal_pullup : 1; /*!< Enable internal pullups. Note: This is not strong enough to pullup buses under high-speed frequency. Recommend proper external pull-up if possible */
|
||||
uint32_t enable_internal_opendrain : 1; /*!< Pull-Push mode on I3C SCL/SDA pins by default. Set true if open-drain mode is needed */
|
||||
uint32_t use_dma : 1; /*!< Use dma transaction. Set to 1 to enable the use of DMA for handling data transfers, which can improve performance for large transactions. */
|
||||
uint32_t enable_async_trans : 1; /*!< Enable asynchronous transactions, allowing the master to perform other tasks while a transaction is in progress. Only works when `use_dma` is set as true. */
|
||||
} flags; /*!< I3C master config flags */
|
||||
} i3c_master_bus_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create a new I3C master bus.
|
||||
*
|
||||
* This function initializes a new I3C master bus with the provided configuration.
|
||||
*
|
||||
* @param[in] bus_config Pointer to the I3C master bus configuration structure.
|
||||
* @param[out] ret_bus_handle Pointer to the location where the handle of the newly created bus will be stored.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Bus created successfully.
|
||||
* - ESP_ERR_INVALID_ARG: Invalid configuration or parameters.
|
||||
* - ESP_ERR_NO_MEM: Memory allocation failed.
|
||||
*/
|
||||
esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_master_bus_handle_t *ret_bus_handle);
|
||||
|
||||
/**
|
||||
* @brief Deletes an I3C master bus and releases associated resources.
|
||||
*
|
||||
* This function deinitializes and deletes the specified I3C master bus instance. It ensures
|
||||
* that all resources allocated for the bus are properly released. The caller must ensure
|
||||
* that no active operations are ongoing on the bus before invoking this function.
|
||||
*
|
||||
* @param bus_handle Handle to the I3C master bus to be deleted. This handle must have been
|
||||
* previously obtained from a successful bus initialization.
|
||||
*
|
||||
* @return
|
||||
* - `ESP_OK`: The bus was successfully deleted.
|
||||
* - `ESP_ERR_INVALID_ARG`: The provided `bus_handle` is invalid (e.g., null or uninitialized).
|
||||
*/
|
||||
esp_err_t i3c_del_master_bus(i3c_master_bus_handle_t bus_handle);
|
||||
|
||||
/**
|
||||
* @brief Wait for all pending I3C transactions done
|
||||
*
|
||||
* @param[in] bus_handle I3C bus handle
|
||||
* @param[in] timeout_ms Wait timeout, in ms. Specially, -1 means to wait forever.
|
||||
* @return
|
||||
* - ESP_OK: Flush transactions successfully
|
||||
* - ESP_ERR_INVALID_ARG: Flush transactions failed because of invalid argument
|
||||
* - ESP_ERR_TIMEOUT: Flush transactions failed because of timeout
|
||||
*/
|
||||
esp_err_t i3c_master_bus_wait_all_done(i3c_master_bus_handle_t bus_handle, int timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Toggle between synchronous and asynchronous transfer modes for the I3C master bus.
|
||||
*
|
||||
* This function allows switching the I3C master bus between synchronous and asynchronous
|
||||
* transfer modes. It ensures that the necessary preconditions are met before making the switch:
|
||||
* - If switching to asynchronous mode, it requires that the async memory has been initialized.
|
||||
* - If switching to synchronous mode, it verifies that no pending transactions are in the queue.
|
||||
*
|
||||
* @param[in] bus_handle Handle to the I3C master bus.
|
||||
* @param[in] async_mode Boolean value indicating the target mode:
|
||||
* - `true` for asynchronous mode.
|
||||
* - `false` for synchronous mode.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: The mode was successfully toggled.
|
||||
* - ESP_ERR_INVALID_ARG: Invalid input arguments.
|
||||
* - ESP_ERR_INVALID_STATE: Cannot switch to synchronous mode due to pending transactions.
|
||||
*/
|
||||
esp_err_t i3c_master_toggle_transfer_async_mode(i3c_master_bus_handle_t bus_handle, bool async_mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
173
components/esp_driver_i3c/include/driver/i3c_master_i2c.h
Normal file
173
components/esp_driver_i3c/include/driver/i3c_master_i2c.h
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "hal/gpio_types.h"
|
||||
#include "driver/i3c_master_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configuration structure for I2C device on an I3C bus
|
||||
*
|
||||
* This structure defines the configuration parameters for an I2C device
|
||||
* when connected to an I3C bus. It includes the device address, clock speed,
|
||||
* and additional configuration flags.
|
||||
*
|
||||
* @note I3C bus can't handle I2C device which has CLOCK STRETCH feature.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t device_address; /*!< Device address. Must be 7-bit in I3C-I2C mode */
|
||||
uint32_t scl_speed_hz; /*!< I2C Device SCL line frequency. */
|
||||
struct {
|
||||
uint32_t disable_ack_check : 1; /*!< Disable ACK check. By default this is 0, this means ack check is enabled, the transaction will be stopped and API returns error when nack is detected. */
|
||||
} flags; /*!< I2C Devices config flags */
|
||||
} i3c_device_i2c_config_t;
|
||||
|
||||
/**
|
||||
* @brief Group of I3C-I2C master callbacks, can be used to get status during transaction or doing other small things. But take care potential concurrency issues.
|
||||
* @note The callbacks are all running under ISR context
|
||||
* @note When CONFIG_I3C_MASTER_ISR_CACHE_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM.
|
||||
* The variables used in the function should be in the SRAM as well.
|
||||
*/
|
||||
typedef struct {
|
||||
i3c_master_i2c_callback_t on_trans_done; /*!< I3C-I2C master transaction finish callback */
|
||||
} i3c_master_i2c_event_callbacks_t;
|
||||
|
||||
/**
|
||||
* @brief Attach an I2C device to the I3C master bus.
|
||||
*
|
||||
* This function registers an I2C device for communication on the I3C master bus.
|
||||
*
|
||||
* @param[in] bus_handle Handle to the I3C master bus.
|
||||
* @param[in] dev_config Pointer to the configuration structure for the I2C device.
|
||||
* @param[out] ret_handle Pointer to the location where the handle of the attached I2C device will be stored.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Device attached successfully.
|
||||
* - ESP_ERR_INVALID_ARG: Invalid parameters or device configuration.
|
||||
* - ESP_ERR_NO_MEM: Memory allocation failed.
|
||||
*/
|
||||
esp_err_t i3c_master_bus_add_i2c_device(i3c_master_bus_handle_t bus_handle, const i3c_device_i2c_config_t *dev_config, i3c_master_i2c_device_handle_t *ret_handle);
|
||||
|
||||
/**
|
||||
* @brief Detach an I2C device from the I3C master bus.
|
||||
*
|
||||
* This function removes an I2C device that was previously attached to the I3C
|
||||
* master bus using `i3c_master_bus_add_i2c_device`. It releases any resources
|
||||
* associated with the device handle and invalidates the device handle.
|
||||
*
|
||||
* @param dev_handle Handle to the I2C device to be detached.
|
||||
* This handle is obtained from `i3c_master_bus_add_i2c_device`.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Device successfully detached from the bus.
|
||||
* - ESP_ERR_INVALID_ARG: The device handle is invalid or NULL.
|
||||
*/
|
||||
esp_err_t i3c_master_bus_rm_i2c_device(i3c_master_i2c_device_handle_t dev_handle);
|
||||
|
||||
/**
|
||||
* @brief Transmit data to an I2C device on the I3C master bus.
|
||||
*
|
||||
* Sends a block of data to the specified I2C device.
|
||||
*
|
||||
* @param[in] dev_handle Handle to the I2C device.
|
||||
* @param[in] write_buffer Pointer to the buffer containing data to transmit.
|
||||
* @param[in] write_size Size of the data in bytes.
|
||||
* @param[in] xfer_timeout_ms Timeout for the operation in milliseconds.
|
||||
*
|
||||
* @note If `enable_async_trans` is set, this function operates in asynchronous transmission mode.
|
||||
* In this mode, the function returns immediately after being called, even if the transmission
|
||||
* is not yet completed. The completion status and any related events should be obtained
|
||||
* through the corresponding callback.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Data transmitted successfully.
|
||||
* - ESP_ERR_TIMEOUT: Transfer timed out.
|
||||
* - ESP_ERR_INVALID_ARG: Invalid parameters.
|
||||
*/
|
||||
esp_err_t i3c_master_i2c_device_transmit(i3c_master_i2c_device_handle_t dev_handle, const uint8_t *write_buffer, size_t write_size, int xfer_timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Receive data from an I2C device on the I3C master bus.
|
||||
*
|
||||
* Reads a block of data from the specified I2C device.
|
||||
*
|
||||
* @param[in] dev_handle Handle to the I2C device.
|
||||
* @param[out] read_buffer Pointer to the buffer where received data will be stored.
|
||||
* @param[in] read_size Number of bytes to read.
|
||||
* @param[in] xfer_timeout_ms Timeout for the operation in milliseconds.
|
||||
*
|
||||
* @note If `enable_async_trans` is set, this function operates in asynchronous transmission mode.
|
||||
* In this mode, the function returns immediately after being called, even if the transmission
|
||||
* is not yet completed. The completion status and any related events should be obtained
|
||||
* through the corresponding callback.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Data received successfully.
|
||||
* - ESP_ERR_TIMEOUT: Transfer timed out.
|
||||
* - ESP_ERR_INVALID_ARG: Invalid parameters.
|
||||
*/
|
||||
esp_err_t i3c_master_i2c_device_receive(i3c_master_i2c_device_handle_t dev_handle, uint8_t *read_buffer, size_t read_size, int xfer_timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Perform a transmit-then-receive operation with an I2C device on the I3C master bus.
|
||||
*
|
||||
* First sends data to the specified I2C device, then reads a block of data from it.
|
||||
*
|
||||
* @param[in] dev_handle Handle to the I2C device.
|
||||
* @param[in] write_buffer Pointer to the buffer containing data to transmit.
|
||||
* @param[in] write_size Size of the data in bytes to transmit.
|
||||
* @param[out] read_buffer Pointer to the buffer where received data will be stored.
|
||||
* @param[in] read_size Number of bytes to read.
|
||||
* @param[in] xfer_timeout_ms Timeout for the operation in milliseconds.
|
||||
*
|
||||
* @note If `enable_async_trans` is set, this function operates in asynchronous transmission mode.
|
||||
* In this mode, the function returns immediately after being called, even if the transmission
|
||||
* is not yet completed. The completion status and any related events should be obtained
|
||||
* through the corresponding callback.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Transmit and receive operations completed successfully.
|
||||
* - ESP_ERR_TIMEOUT: Transfer timed out.
|
||||
* - ESP_ERR_INVALID_ARG: Invalid parameters.
|
||||
*/
|
||||
esp_err_t i3c_master_i2c_device_transmit_receive(i3c_master_i2c_device_handle_t dev_handle, const uint8_t *write_buffer, size_t write_size, uint8_t *read_buffer, size_t read_size, int xfer_timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Register event callbacks for an I2C device operating on an I3C master bus
|
||||
*
|
||||
* This function allows the user to register callback functions for handling specific
|
||||
* events related to an I2C device on the I3C master bus. The registered callbacks
|
||||
* will be invoked during corresponding events, enabling custom handling of device-specific
|
||||
* behaviors.
|
||||
*
|
||||
* @param dev_handle Handle to the I2C device
|
||||
* This handle is obtained when the device is added to the I3C bus.
|
||||
*
|
||||
* @param cbs Pointer to a structure containing the event callback functions
|
||||
* The `cbs` parameter should point to a valid `i3c_master_i2c_event_callbacks_t`
|
||||
* structure that specifies the callbacks for various I2C-related events.
|
||||
*
|
||||
* @param user_data User-defined data to be passed to the callbacks
|
||||
* This pointer is passed to all callbacks as a parameter, allowing the user
|
||||
* to associate context or state with the callbacks.
|
||||
*
|
||||
* @return
|
||||
* - `ESP_OK`: The operation completed successfully.
|
||||
* - `ESP_ERR_INVALID_ARG`: One or more parameters are invalid.
|
||||
* - `ESP_ERR_NO_MEM`: Insufficient memory to register the callbacks.
|
||||
*/
|
||||
esp_err_t i3c_master_i2c_device_register_event_callbacks(i3c_master_i2c_device_handle_t dev_handle, const i3c_master_i2c_event_callbacks_t *cbs, void *user_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
63
components/esp_driver_i3c/include/driver/i3c_master_types.h
Normal file
63
components/esp_driver_i3c/include/driver/i3c_master_types.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include "hal/i3c_master_types.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type of I3C master bus handle
|
||||
*/
|
||||
typedef struct i3c_master_bus_t *i3c_master_bus_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Type of I2C device handle on I3C master bus
|
||||
*/
|
||||
typedef struct i3c_master_i2c_dev_t *i3c_master_i2c_device_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Enumeration for I3C event.
|
||||
*/
|
||||
typedef enum {
|
||||
I3C_MASTER_EVENT_DONE, /*!< I3C bus transaction done */
|
||||
I3C_MASTER_EVENT_NACK, /*!< I3C bus nack */
|
||||
} i3c_master_event_t;
|
||||
|
||||
/**
|
||||
* @brief Structure representing event data for I3C/I2C master operations.
|
||||
*/
|
||||
typedef struct {
|
||||
i3c_master_event_t event; /*!< The event type that occurred (e.g., transfer complete, error). */
|
||||
uint8_t *data; /*!< Pointer to the data buffer for the event (e.g., received data). */
|
||||
size_t recv_size; /*!< The size of the data received, in bytes. */
|
||||
} i3c_i2c_master_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief Type definition for a callback function used in I3C/I2C master operations.
|
||||
*
|
||||
* This callback function is invoked when an event occurs during I3C/I2C master communication.
|
||||
*
|
||||
* @param i2c_dev Handle to the I2C device associated with the event.
|
||||
* @param evt_data Pointer to the structure containing event details and relevant data.
|
||||
* @param arg User-provided argument passed during callback registration.
|
||||
*
|
||||
* @return Whether a high priority task has been waken up by this function.
|
||||
*/
|
||||
typedef bool (*i3c_master_i2c_callback_t)(i3c_master_i2c_device_handle_t i2c_dev, const i3c_i2c_master_event_data_t *evt_data, void *arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
18
components/esp_driver_i3c/linker.lf
Normal file
18
components/esp_driver_i3c/linker.lf
Normal file
@@ -0,0 +1,18 @@
|
||||
[mapping:i3c_driver]
|
||||
archive: libesp_driver_i3c.a
|
||||
entries:
|
||||
if I3C_MASTER_ISR_CACHE_SAFE = y:
|
||||
i3c_master: i3c_master_isr_handler_default (noflash)
|
||||
i3c_master: s_do_dma_transaction (noflash)
|
||||
i3c_master: handle_tx_data_buf_thld_int (noflash)
|
||||
i3c_master: handle_transfer_complete_int (noflash)
|
||||
i3c_master: handle_rx_data_buf_thld_int (noflash)
|
||||
|
||||
[mapping:i3c_driver_gdma]
|
||||
archive: libesp_hw_support.a
|
||||
entries:
|
||||
if I3C_MASTER_ISR_CACHE_SAFE = y:
|
||||
gdma_link: gdma_link_mount_buffers (noflash)
|
||||
gdma_link: gdma_link_get_head_addr (noflash)
|
||||
gdma: gdma_start (noflash)
|
||||
gdma_link: gdma_link_count_buffer_size_till_eof (noflash)
|
@@ -31,6 +31,7 @@ extern "C" {
|
||||
#define I3C_LL_MASTER_TRANSMIT_EVENT_INTR (I3C_MST_TX_DATA_BUF_THLD_INT_ENA_M | I3C_MST_TRANSFER_COMPLETE_INT_ENA_M | I3C_MST_COMMAND_DONE_INT_ENA_M)
|
||||
#define I3C_LL_MASTER_RECEIVE_EVENT_INTR (I3C_MST_RX_DATA_BUF_THLD_INT_ENA_M | I3C_MST_TRANSFER_COMPLETE_INT_ENA_M | I3C_MST_COMMAND_DONE_INT_ENA_M)
|
||||
#define I3C_MASTER_LL_DEFAULT_SETUP_TIME (600)
|
||||
<<<<<<< HEAD
|
||||
|
||||
/**
|
||||
* @brief I3C master command types
|
||||
@@ -278,6 +279,8 @@ typedef union {
|
||||
*/
|
||||
uint32_t val; ///< Raw 32-bit value of the address table entry.
|
||||
} i3c_master_ll_device_address_descriptor_t;
|
||||
=======
|
||||
>>>>>>> 0fb9d0462d1 (feat(i3c): Add support for i2c mode in i3c peripheral)
|
||||
|
||||
typedef enum {
|
||||
I3C_MASTER_LL_FIFO_WM_LENGTH_2 = 0x0,
|
||||
@@ -287,6 +290,29 @@ typedef enum {
|
||||
I3C_MASTER_LL_FIFO_WM_LENGTH_31 = 0x4,
|
||||
} i3c_master_ll_fifo_wm_t;
|
||||
|
||||
/**
|
||||
* @brief I3C Master Error State Enumeration
|
||||
*/
|
||||
typedef enum {
|
||||
I3C_MASTER_LL_NO_ERROR = 0, ///< Indicates that the I3C master encountered no errors.
|
||||
I3C_MASTER_LL_READ_LENGTH_MISMATCH = 3, ///< The length of data read from the slave device does not match the expected or requested length.
|
||||
I3C_MASTER_LL_BROADCAST_ADDRESS_NACK_ERROR = 4, ///< Broadcast Address NACK Error.
|
||||
I3C_MASTER_LL_ADDRESS_NACK_OR_DYNAMIC_ADDRESS_NACK = 5, ///< Address NACK or Dynamic Address NACK.
|
||||
I3C_MASTER_LL_BUFFER_RX_OVERFLOW_TX_UNDERFLOW = 6, ///< Buffer RX Overflow or TX Underflow.
|
||||
I3C_MASTER_LL_I2C_SLAVE_WRITE_DATA_NACK_ERROR = 9, ///< I2C Slave Write Data NACK Error.
|
||||
} i3c_master_ll_error_state_enum_t;
|
||||
|
||||
/**
|
||||
* @brief I3C master operating mode
|
||||
*
|
||||
* This enumeration defines the operating modes for an I3C master.
|
||||
* It can either operate in the I3C protocol mode or be backward-compatible with I2C devices.
|
||||
*/
|
||||
typedef enum {
|
||||
I3C_MASTER_LL_MODE_I3C = 0, ///< I3C works under I3C mode
|
||||
I3C_MASTER_LL_MODE_I2C = 1, ///< I3C works under I2C mode
|
||||
} i3c_master_ll_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Set the clock source for the I3C master
|
||||
*
|
||||
@@ -312,7 +338,11 @@ static inline void i3c_master_ll_set_source_clk(i3c_mst_dev_t *hw, i3c_master_cl
|
||||
* @param device_number Number of devices
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
<<<<<<< HEAD
|
||||
static inline void i3c_master_ll_set_device_address_table(i3c_mst_dev_t *hw, i3c_master_ll_device_address_descriptor_t *addr_table, size_t device_number)
|
||||
=======
|
||||
static inline void i3c_master_ll_set_address_device_table(i3c_mst_dev_t *hw, i3c_master_address_table_t *addr_table, size_t device_number)
|
||||
>>>>>>> 0fb9d0462d1 (feat(i3c): Add support for i2c mode in i3c peripheral)
|
||||
{
|
||||
for (int i = 0; i < device_number; i++) {
|
||||
I3C_MST_MEM.dev_addr_table[i].val = addr_table[i].val;
|
||||
@@ -375,7 +405,11 @@ static inline void i3c_master_ll_reset_register(i3c_mst_dev_t *hw)
|
||||
* @param command_num Number of commands
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
<<<<<<< HEAD
|
||||
static inline void i3c_master_ll_set_command(i3c_mst_dev_t *hw, i3c_master_ll_command_descriptor_t *command_buf, size_t command_num)
|
||||
=======
|
||||
static inline void i3c_master_ll_set_command(i3c_mst_dev_t *hw, i3c_master_command_table_t *command_buf, size_t command_num)
|
||||
>>>>>>> 0fb9d0462d1 (feat(i3c): Add support for i2c mode in i3c peripheral)
|
||||
{
|
||||
for (int i = 0; i < command_num; i++) {
|
||||
I3C_MST_MEM.command_buf_port.reg_command = command_buf[i].cmd_l.val;
|
||||
@@ -708,7 +742,11 @@ static inline void i3c_master_ll_enable_rx_by_dma(i3c_mst_dev_t *dev, bool enabl
|
||||
* @return Response data
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
<<<<<<< HEAD
|
||||
static inline i3c_master_ll_response_descriptor_t i3c_master_ll_get_response_data(i3c_mst_dev_t *dev)
|
||||
=======
|
||||
static inline uint32_t i3c_master_ll_get_response_buffer_value(i3c_mst_dev_t *dev)
|
||||
>>>>>>> 0fb9d0462d1 (feat(i3c): Add support for i2c mode in i3c peripheral)
|
||||
{
|
||||
return (i3c_master_ll_response_descriptor_t)(I3C_MST_MEM.response_buf_port.val);
|
||||
}
|
||||
|
Reference in New Issue
Block a user