feat(i3c): Add example for i2c mode in i3c peripheral

This commit is contained in:
C.S.M
2025-05-26 16:30:29 +08:00
parent 1d07729b91
commit 9b17b8470e
28 changed files with 639 additions and 411 deletions

View File

@@ -717,3 +717,4 @@ mainmenu "Espressif IoT Development Framework Configuration"
- CONFIG_USB_HOST_EXT_PORT_RESET_ATTEMPTS - CONFIG_USB_HOST_EXT_PORT_RESET_ATTEMPTS
- CONFIG_LIBC_PICOLIBC - CONFIG_LIBC_PICOLIBC
- CONFIG_GDMA_ENABLE_WEIGHTED_ARBITRATION - CONFIG_GDMA_ENABLE_WEIGHTED_ARBITRATION
- CONFIG_I3C_MASTER_ENABLED

View File

@@ -4,7 +4,7 @@ set(srcs)
set(include "include") set(include "include")
# I3C related source files. # I3C related source files.
if(CONFIG_SOC_I3C_MASTER_SUPPORTED) if(CONFIG_I3C_MASTER_ENABLED)
list(APPEND srcs "i3c_master.c" list(APPEND srcs "i3c_master.c"
) )
endif() endif()

View File

@@ -1,5 +1,10 @@
menu "ESP-Driver:I3C Master Configurations" menu "ESP-Driver:I3C Master Configurations"
depends on SOC_I3C_MASTER_SUPPORTED
config I3C_MASTER_ENABLED
bool
default y if SOC_I3C_MASTER_SUPPORTED && IDF_EXPERIMENTAL_FEATURES
default n
config I3C_MASTER_ISR_CACHE_SAFE config I3C_MASTER_ISR_CACHE_SAFE
bool "I3C ISR Cache-Safe" bool "I3C ISR Cache-Safe"
select I3C_MASTER_ISR_HANDLER_IN_IRAM select I3C_MASTER_ISR_HANDLER_IN_IRAM

View File

@@ -11,41 +11,35 @@
#include "esp_types.h" #include "esp_types.h"
#include "esp_attr.h" #include "esp_attr.h"
#include "esp_check.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_log.h"
#include "esp_intr_alloc.h" #include "esp_intr_alloc.h"
#include "esp_clk_tree.h"
#include "esp_cache.h"
#include "esp_pm.h"
#include "esp_memory_utils.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include "freertos/idf_additions.h"
#include "esp_private/periph_ctrl.h" #include "esp_private/periph_ctrl.h"
#include "esp_private/esp_clk.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 "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/esp_clk_tree_common.h"
#include "esp_private/gdma.h" #include "esp_private/gdma.h"
#include "esp_private/gdma_link.h" #include "esp_private/gdma_link.h"
#include "esp_private/esp_dma_utils.h" #include "esp_private/esp_dma_utils.h"
#include "esp_cache.h" #include "esp_private/esp_cache_private.h"
#include "driver/gpio.h"
#include "driver/i3c_master.h"
#include "driver/i3c_master_i2c.h"
#include "hal/gpio_hal.h"
#include "hal/i3c_master_hal.h"
#include "hal/dma_types.h"
#include "hal/cache_hal.h" #include "hal/cache_hal.h"
#include "hal/cache_ll.h" #include "hal/cache_ll.h"
#include "esp_pm.h" #include "hal/i3c_master_ll.h"
#include "esp_memory_utils.h" #include "i3c_master_private.h"
#include "esp_private/esp_cache_private.h" #include "soc/clk_tree_defs.h"
#include "soc/i3c_master_periph.h"
static const char *TAG = "i3c.master"; static const char *TAG = "i3c.master";
@@ -61,70 +55,47 @@ static bool i3c_master_bus_occupied(i3c_master_bus_num_t bus_num)
return s_i3c_master_platform.buses[bus_num] != NULL; 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) // Forward declaration for DMA transaction handler
static esp_err_t do_dma_transaction_handler(i3c_master_bus_handle_t bus_handle, i3c_i2c_transaction_desc_t *trans);
static esp_err_t do_fifo_transaction_handler(i3c_master_bus_handle_t bus_handle, i3c_i2c_transaction_desc_t *trans)
{ {
esp_err_t err = ESP_OK;
bus_handle->cur_trans = trans; bus_handle->cur_trans = trans;
bus_handle->read_buffer_left_size = trans->read_buffer ? trans->command_table[0].cmd_h.regular.dl : 0;
bus_handle->read_fifo_buffer_pointer = trans->read_buffer;
portENTER_CRITICAL_SAFE(&bus_handle->spinlock); size_t actual_write_size = 0;
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->scl_freq_hz > 400 * 1000) {
if (trans->command_table[i].cmd_l.regular.rnw == I3C_MASTER_WRITE_TRANSFER) { i3c_master_ll_set_i2c_fast_mode_plus_timing(bus_handle->hal.dev, bus_handle->clock_source_freq, trans->scl_freq_hz);
size_t write_size = trans->command_table[i].cmd_h.regular.dl; } else {
size_t dma_aligned_size = I3C_ALIGN_UP(write_size, I3C_MASTER_DMA_INTERFACE_ALIGNMENT); i3c_master_ll_set_i2c_fast_mode_timing(bus_handle->hal.dev, bus_handle->clock_source_freq, trans->scl_freq_hz);
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 i3c_master_ll_set_device_address_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);
portENTER_CRITICAL_SAFE(&bus_handle->spinlock); portENTER_CRITICAL_SAFE(&bus_handle->spinlock);
i3c_master_ll_start_transaction(bus_handle->hal.dev); i3c_master_ll_enable_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_TRANSMIT_EVENT_INTR | I3C_LL_MASTER_RECEIVE_EVENT_INTR);
i3c_master_ll_clear_intr_mask(bus_handle->hal.dev, I3C_LL_MASTER_TRANSMIT_EVENT_INTR | I3C_LL_MASTER_RECEIVE_EVENT_INTR);
portEXIT_CRITICAL_SAFE(&bus_handle->spinlock); portEXIT_CRITICAL_SAFE(&bus_handle->spinlock);
if (trans->write_buffer && trans->command_table[0].cmd_h.regular.dl >= I3C_LL_MASTER_DATA_BUFFER_SIZE) {
actual_write_size = I3C_LL_MASTER_DATA_BUFFER_SIZE;
} else {
actual_write_size = trans->write_buffer ? trans->command_table[0].cmd_h.regular.dl : 0;
}
i3c_master_ll_write_tx_port(bus_handle->hal.dev, trans->write_buffer, actual_write_size);
bus_handle->write_fifo_buffer_pointer = trans->write_buffer;
bus_handle->write_buffer_left_size = trans->write_buffer ? trans->command_table[0].cmd_h.regular.dl : 0;
bus_handle->write_fifo_buffer_pointer += actual_write_size;
bus_handle->write_buffer_left_size -= actual_write_size;
i3c_master_ll_start_transaction(bus_handle->hal.dev);
return ESP_OK; return ESP_OK;
} }
@@ -133,7 +104,7 @@ static esp_err_t s_do_dma_transaction(i3c_master_bus_handle_t bus_handle, i3c_i2
If data to be sent is larger than fifo, we can add data and continue If data to be sent is larger than fifo, we can add data and continue
transfer with buf threshold interrupt. transfer with buf threshold interrupt.
*/ */
static bool handle_tx_data_buf_thld_int(i3c_master_bus_handle_t i3c_master, uint32_t int_mask) static bool handle_tx_data_buf_threshold_int(i3c_master_bus_handle_t i3c_master, uint32_t int_mask)
{ {
portENTER_CRITICAL_SAFE(&i3c_master->spinlock); 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 empty_size = i3c_master_ll_get_tx_fifo_empty_count(i3c_master->hal.dev) * 4;
@@ -163,7 +134,7 @@ static bool handle_tx_data_buf_thld_int(i3c_master_bus_handle_t i3c_master, uint
*/ */
static bool handle_transfer_complete_int(i3c_master_bus_handle_t i3c_master) static bool handle_transfer_complete_int(i3c_master_bus_handle_t i3c_master)
{ {
i3c_master_event_t event = I3C_MASTER_EVENT_DONE; i3c_master_event_t event = I3C_MASTER_EVENT_TRANS_DONE;
BaseType_t do_yield = pdFALSE; BaseType_t do_yield = pdFALSE;
bool need_yield = false; bool need_yield = false;
@@ -177,8 +148,8 @@ static bool handle_transfer_complete_int(i3c_master_bus_handle_t i3c_master)
portEXIT_CRITICAL_SAFE(&i3c_master->spinlock); portEXIT_CRITICAL_SAFE(&i3c_master->spinlock);
} }
i3c_master_response_data_t response_data; i3c_master_ll_response_descriptor_t response_data;
response_data.val = i3c_master_ll_get_response_buffer_value(i3c_master->hal.dev); response_data = i3c_master_ll_get_response_data(i3c_master->hal.dev);
if (response_data.err_sts == I3C_MASTER_LL_ADDRESS_NACK_OR_DYNAMIC_ADDRESS_NACK) { if (response_data.err_sts == I3C_MASTER_LL_ADDRESS_NACK_OR_DYNAMIC_ADDRESS_NACK) {
event = I3C_MASTER_EVENT_NACK; event = I3C_MASTER_EVENT_NACK;
} }
@@ -192,7 +163,7 @@ static bool handle_transfer_complete_int(i3c_master_bus_handle_t i3c_master)
i3c_i2c_transaction_desc_t *trans_desc = NULL; i3c_i2c_transaction_desc_t *trans_desc = NULL;
i3c_fsm_t expected_fsm = I3C_FSM_RUN; i3c_fsm_t expected_fsm = I3C_FSM_RUN;
if (atomic_compare_exchange_strong(&i3c_master->fsm, &expected_fsm, I3C_FSM_ENABLE_WAIT)) { if (atomic_compare_exchange_strong(&i3c_master->fsm, &expected_fsm, I3C_FSM_WAIT)) {
trans_desc = i3c_master->cur_trans; trans_desc = i3c_master->cur_trans;
xQueueSendFromISR(i3c_master->trans_queues[I3C_TRANS_QUEUE_COMPLETE], &trans_desc, &do_yield); xQueueSendFromISR(i3c_master->trans_queues[I3C_TRANS_QUEUE_COMPLETE], &trans_desc, &do_yield);
if (do_yield) { if (do_yield) {
@@ -201,21 +172,14 @@ static bool handle_transfer_complete_int(i3c_master_bus_handle_t i3c_master)
atomic_store(&i3c_master->fsm, I3C_FSM_ENABLE); atomic_store(&i3c_master->fsm, I3C_FSM_ENABLE);
} }
i3c_master_i2c_device_handle_t i2c_dev = NULL; i3c_master_i2c_device_handle_t i2c_dev = i3c_master->cur_trans->dev_handle;
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) { 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 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); 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); 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 = { i3c_master_i2c_device_event_data_t evt_data = {
.data = i3c_master->cur_trans->read_buffer, .data = i3c_master->cur_trans->read_buffer,
.recv_size = dma_rcv_size, .data_size = dma_rcv_size,
.event = event, .event = event,
}; };
@@ -227,10 +191,10 @@ static bool handle_transfer_complete_int(i3c_master_bus_handle_t i3c_master)
} }
expected_fsm = I3C_FSM_ENABLE; expected_fsm = I3C_FSM_ENABLE;
if (atomic_compare_exchange_strong(&i3c_master->fsm, &expected_fsm, I3C_FSM_RUN_WAIT)) { if (atomic_compare_exchange_strong(&i3c_master->fsm, &expected_fsm, I3C_FSM_WAIT)) {
if (xQueueReceiveFromISR(i3c_master->trans_queues[I3C_TRANS_QUEUE_PROGRESS], &trans_desc, &do_yield) == pdTRUE) { if (xQueueReceiveFromISR(i3c_master->trans_queues[I3C_TRANS_QUEUE_PROGRESS], &trans_desc, &do_yield) == pdTRUE) {
atomic_store(&i3c_master->fsm, I3C_FSM_RUN); atomic_store(&i3c_master->fsm, I3C_FSM_RUN);
s_do_dma_transaction(i3c_master, trans_desc); i3c_master->transaction_handler(i3c_master, trans_desc);
if (do_yield) { if (do_yield) {
need_yield = true; need_yield = true;
} }
@@ -242,7 +206,7 @@ static bool handle_transfer_complete_int(i3c_master_bus_handle_t i3c_master)
return need_yield; return need_yield;
} }
static void handle_rx_data_buf_threshhold_int(i3c_master_bus_handle_t i3c_master) static void handle_rx_data_buf_threshold_int(i3c_master_bus_handle_t i3c_master)
{ {
if (i3c_master->use_dma_transaction == false) { if (i3c_master->use_dma_transaction == false) {
portENTER_CRITICAL_SAFE(&i3c_master->spinlock); portENTER_CRITICAL_SAFE(&i3c_master->spinlock);
@@ -267,7 +231,7 @@ static void i3c_master_isr_handler_default(void *arg)
portEXIT_CRITICAL_SAFE(&i3c_master->spinlock); portEXIT_CRITICAL_SAFE(&i3c_master->spinlock);
if (int_mask & I3C_MST_TX_DATA_BUF_THLD_INT_ST) { if (int_mask & I3C_MST_TX_DATA_BUF_THLD_INT_ST) {
if (handle_tx_data_buf_thld_int(i3c_master, int_mask)) { if (handle_tx_data_buf_threshold_int(i3c_master, int_mask)) {
need_yield = true; need_yield = true;
} }
} }
@@ -279,7 +243,7 @@ static void i3c_master_isr_handler_default(void *arg)
} }
if (int_mask & I3C_MST_RX_DATA_BUF_THLD_INT_ST) { if (int_mask & I3C_MST_RX_DATA_BUF_THLD_INT_ST) {
handle_rx_data_buf_threshhold_int(i3c_master); handle_rx_data_buf_threshold_int(i3c_master);
} }
if (need_yield) { if (need_yield) {
@@ -304,10 +268,10 @@ static esp_err_t i3c_master_async_transaction_preparation(i3c_master_bus_t *i3c_
ESP_RETURN_ON_FALSE(xQueueSend(i3c_master_handle->trans_queues[I3C_TRANS_QUEUE_READY], &p_trans_desc, 0) == pdTRUE, 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"); 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); i3c_master_handle->i3c_async_addr_table = (i3c_master_ll_device_address_descriptor_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"); 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); i3c_master_handle->i3c_async_command_table = (i3c_master_ll_command_descriptor_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"); 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->ops_prepare_idx = 0;
@@ -317,21 +281,25 @@ static esp_err_t i3c_master_async_transaction_preparation(i3c_master_bus_t *i3c_
return ESP_OK; 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) static esp_err_t i3c_master_init_dma(i3c_master_bus_t *i3c_master_handle, const i3c_master_dma_config_t *dma_config)
{ {
// Initialize i3c-dma connection // Initialize i3c-dma connection
i3c_master_ll_enable_tx_by_dma(i3c_master_handle->hal.dev, true); 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); i3c_master_ll_enable_rx_by_dma(i3c_master_handle->hal.dev, true);
// Initialize DMA TX channel // Initialize DMA TX channel
gdma_channel_alloc_config_t dma_cfg = {}; gdma_channel_alloc_config_t dma_cfg = {
#if CONFIG_I3C_MASTER_ISR_CACHE_SAFE
.flags.isr_cache_safe = true,
#endif
};
dma_cfg.direction = GDMA_CHANNEL_DIRECTION_TX; 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"); 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_connect(i3c_master_handle->dma_tx_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_I3C, 0));
gdma_transfer_config_t transfer_cfg = { gdma_transfer_config_t transfer_cfg = {
.access_ext_mem = false, .access_ext_mem = false,
.max_data_burst_size = bus_config->dma_burst_size ? bus_config->dma_burst_size : 16, .max_data_burst_size = dma_config->dma_burst_size ? dma_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"); ESP_RETURN_ON_ERROR(gdma_config_transfer(i3c_master_handle->dma_tx_chan, &transfer_cfg), TAG, "Config DMA tx channel transfer failed");
@@ -339,7 +307,7 @@ static esp_err_t i3c_master_init_dma(i3c_master_bus_t *i3c_master_handle, const
size_t int_mem_align = 0; size_t int_mem_align = 0;
gdma_get_alignment_constraints(i3c_master_handle->dma_tx_chan, &int_mem_align, NULL); 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 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); size_t num_dma_nodes = esp_dma_calculate_node_count(dma_config->max_transfer_size, buffer_alignment, DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
gdma_link_list_config_t dma_link_config = { gdma_link_list_config_t dma_link_config = {
.buffer_alignment = buffer_alignment, // no special buffer alignment for i3c master buffer .buffer_alignment = buffer_alignment, // no special buffer alignment for i3c master buffer
.item_alignment = 4, // 4 bytes alignment for AHB-DMA .item_alignment = 4, // 4 bytes alignment for AHB-DMA
@@ -360,38 +328,22 @@ static esp_err_t i3c_master_init_dma(i3c_master_bus_t *i3c_master_handle, const
return ESP_OK; return ESP_OK;
} }
static esp_err_t s_i3c_pins_config(const i3c_master_bus_config_t *bus_config) static esp_err_t i3c_pins_config(const i3c_master_bus_config_t *bus_config)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
// SDA pin configurations // SDA pin configurations
ESP_RETURN_ON_ERROR(gpio_set_level(bus_config->sda_io_num, 1), TAG, "i2c sda pin set level failed"); 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); 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); 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); gpio_matrix_output(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); gpio_matrix_input(bus_config->sda_io_num, i3c_master_periph_signal->sda_in_sig, 0);
// SCL pin configurations // SCL pin configurations
ESP_RETURN_ON_ERROR(gpio_set_level(bus_config->scl_io_num, 1), TAG, "i2c scl pin set level failed"); 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); 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); 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); gpio_matrix_output(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); gpio_matrix_input(bus_config->scl_io_num, i3c_master_periph_signal->scl_in_sig, 0);
return ret; return ret;
} }
@@ -454,11 +406,16 @@ static esp_err_t i3c_master_bus_destroy(i3c_master_bus_handle_t bus_handle)
ESP_RETURN_ON_ERROR(esp_intr_free(bus_handle->intr_handle), TAG, "delete interrupt service failed"); ESP_RETURN_ON_ERROR(esp_intr_free(bus_handle->intr_handle), TAG, "delete interrupt service failed");
} }
// Free DMA resources if used if (bus_handle->clock_source) {
if (bus_handle->use_dma_transaction) { esp_clk_tree_enable_src((soc_module_clk_t)bus_handle->clock_source, false);
i3c_master_deinit_dma(bus_handle);
} }
#if CONFIG_PM_ENABLE
if (bus_handle->pm_lock) {
esp_pm_lock_delete(bus_handle->pm_lock);
}
#endif
// Free async transaction resources // Free async transaction resources
if (bus_handle->async_transaction) { if (bus_handle->async_transaction) {
i3c_master_del_async_transaction_source(bus_handle); i3c_master_del_async_transaction_source(bus_handle);
@@ -483,10 +440,105 @@ esp_err_t i3c_del_master_bus(i3c_master_bus_handle_t bus_handle)
return ESP_OK; return ESP_OK;
} }
// DMA transaction handler implementation - only linked when DMA is enabled
static esp_err_t do_dma_transaction_handler(i3c_master_bus_handle_t bus_handle, i3c_i2c_transaction_desc_t *trans)
{
esp_err_t err = ESP_OK;
bus_handle->cur_trans = trans;
printf("here, do dma\n");
portENTER_CRITICAL_SAFE(&bus_handle->spinlock);
if (trans->scl_freq_hz > 400 * 1000) {
i3c_master_ll_set_i2c_fast_mode_plus_timing(bus_handle->hal.dev, bus_handle->clock_source_freq, trans->scl_freq_hz);
} else {
i3c_master_ll_set_i2c_fast_mode_timing(bus_handle->hal.dev, bus_handle->clock_source_freq, trans->scl_freq_hz);
}
i3c_master_ll_set_device_address_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_LL_TRANSFER_DIR_WRITE) {
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);
err = 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_LL_TRANSFER_DIR_READ) {
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);
printf("here, start transaction\n");
return ESP_OK;
}
esp_err_t i3c_master_bus_decorate_dma(i3c_master_bus_handle_t bus_handle, const i3c_master_dma_config_t *dma_config)
{
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "invalid bus handle");
// Initialize DMA using the max transfer size from DMA configuration
if (dma_config) {
ESP_RETURN_ON_ERROR(i3c_master_init_dma(bus_handle, dma_config), TAG, "DMA initialization failed");
// Mark DMA as initialized and enable DMA transactions
bus_handle->dma_initialized = true;
bus_handle->use_dma_transaction = true;
// Switch to DMA transaction handler
bus_handle->transaction_handler = do_dma_transaction_handler;
} else {
i3c_master_deinit_dma(bus_handle);
bus_handle->dma_initialized = false;
bus_handle->use_dma_transaction = false;
// Switch back to FIFO transaction handler
bus_handle->transaction_handler = do_fifo_transaction_handler;
}
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_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 && ret_bus_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
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_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; esp_err_t ret = ESP_OK;
@@ -517,8 +569,8 @@ esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_mast
/// clock enable /// clock enable
I3C_MASTER_RCC_ATOMIC() { 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_ll_enable_bus_clock(i3c_master_handle->hal.dev, true);
i3c_master_ll_reset_register(i3c_master_handle->hal.dev);
} }
I3C_MASTER_CLOCK_SRC_ATOMIC() { I3C_MASTER_CLOCK_SRC_ATOMIC() {
i3c_master_ll_enable_controller_clock(i3c_master_handle->hal.dev, true); i3c_master_ll_enable_controller_clock(i3c_master_handle->hal.dev, true);
@@ -533,9 +585,12 @@ esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_mast
i3c_master_ll_set_source_clk(i3c_master_handle->hal.dev, i3c_master_handle->clock_source); 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->dma_initialized = false;
i3c_master_handle->async_transaction = bus_config->flags.enable_async_trans; // TODO: Only use dma can use async transaction. i3c_master_handle->use_dma_transaction = false;
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"); i3c_master_handle->async_transaction = bus_config->flags.enable_async_trans;
// Set default transaction handler to FIFO mode
i3c_master_handle->transaction_handler = do_fifo_transaction_handler;
esp_clk_tree_src_get_freq_hz(i3c_master_handle->clock_source, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &periph_src_clk_hz); esp_clk_tree_src_get_freq_hz(i3c_master_handle->clock_source, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &periph_src_clk_hz);
@@ -556,19 +611,18 @@ esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_mast
i3c_master_handle->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; i3c_master_handle->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
/// pins configuration /// pins configuration
ESP_GOTO_ON_ERROR(s_i3c_pins_config(bus_config), err, TAG, "pins initialization failed"); ESP_GOTO_ON_ERROR(i3c_pins_config(bus_config), err, TAG, "pins initialization failed");
esp_cache_get_alignment(MALLOC_CAP_INTERNAL, &i3c_master_handle->cache_line_size); esp_cache_get_alignment(MALLOC_CAP_INTERNAL, &i3c_master_handle->cache_line_size);
#if CONFIG_PM_ENABLE #if CONFIG_PM_ENABLE
bool need_pm_lock = true; bool need_pm_lock = true;
// to make the I2C work reliable, the source clock must stay alive and unchanged // to make the I3C work reliable, the source clock must stay alive and unchanged
// driver will create different pm lock for that purpose, according to different clock source // 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; esp_pm_lock_type_t pm_lock_type = ESP_PM_NO_LIGHT_SLEEP;
if (need_pm_lock) { 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_periph_signal[0].module_name, &i3c_master_handle->pm_lock);
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"); ESP_GOTO_ON_ERROR(ret, err, TAG, "create pm lock failed");
} }
@@ -586,7 +640,7 @@ esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_mast
// interrupt configurations // interrupt configurations
if (bus_config->intr_priority) { 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); 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; int isr_flags = I3C_MASTER_INTR_ALLOC_FLAG;
@@ -597,12 +651,12 @@ esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_mast
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); 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"); ESP_GOTO_ON_ERROR(ret, err, TAG, "install i3c master interrupt failed");
// Initialize dma if dma transaction is needed. // Disable DMA by default, will be enabled when decorator is called
if (i3c_master_handle->use_dma_transaction) { i3c_master_ll_enable_tx_by_dma(i3c_master_handle->hal.dev, false);
ESP_GOTO_ON_ERROR(i3c_master_init_dma(i3c_master_handle, bus_config), err, TAG, "dma initialization failed"); i3c_master_ll_enable_rx_by_dma(i3c_master_handle->hal.dev, false);
}
// Initialize async transaction memory if async transaction is needed. // Initialize async transaction memory if async transaction is needed.
// Note: DMA dependency will be checked at runtime when transactions are executed
if (i3c_master_handle->async_transaction) { if (i3c_master_handle->async_transaction) {
ESP_GOTO_ON_ERROR(i3c_master_async_transaction_preparation(i3c_master_handle), err, TAG, "async transaction resource failed"); ESP_GOTO_ON_ERROR(i3c_master_async_transaction_preparation(i3c_master_handle), err, TAG, "async transaction resource failed");
} }
@@ -623,16 +677,9 @@ 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"); 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_master_bus_handle_t i3c_master = handle->bus_handle;
i3c_i2c_master_device_list_t *device_item; portENTER_CRITICAL_SAFE(&handle->bus_handle->spinlock);
xSemaphoreTake(i3c_master->bus_lock_mux, portMAX_DELAY); SLIST_REMOVE(&i3c_master->device_list, handle, i3c_master_i2c_dev_t, next);
SLIST_FOREACH(device_item, &i3c_master->device_list, next) { portEXIT_CRITICAL_SAFE(&handle->bus_handle->spinlock);
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) { if (handle) {
free(handle); free(handle);
@@ -643,44 +690,32 @@ esp_err_t i3c_master_bus_rm_i2c_device(i3c_master_i2c_device_handle_t handle)
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 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((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, 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"); ESP_RETURN_ON_FALSE(dev_config->scl_freq_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); i3c_master_i2c_dev_t *i3c_i2c_dev = (i3c_master_i2c_dev_t *)heap_caps_calloc(1, sizeof(i3c_master_i2c_dev_t), MALLOC_CAP_INTERNAL);
ESP_RETURN_ON_FALSE(i3c_i2c_dev, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); 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->address = dev_config->device_address;
i3c_i2c_dev->bus_handle = bus_handle; i3c_i2c_dev->bus_handle = bus_handle;
i3c_i2c_dev->scl_speed_hz = dev_config->scl_speed_hz; i3c_i2c_dev->scl_freq_hz = dev_config->scl_freq_hz;
i3c_i2c_dev->bypass_ack_check = dev_config->flags.disable_ack_check;
*ret_handle = i3c_i2c_dev; *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); portENTER_CRITICAL_SAFE(&bus_handle->spinlock);
ESP_GOTO_ON_FALSE((device_item != NULL), ESP_ERR_NO_MEM, err, TAG, "no memory for i3c-i2c device item`"); SLIST_INSERT_HEAD(&bus_handle->device_list, i3c_i2c_dev, next);
device_item->i2c_dev = i3c_i2c_dev; portEXIT_CRITICAL_SAFE(&bus_handle->spinlock);
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; 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) static esp_err_t do_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; 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_addr_table[bus_handle->ops_prepare_idx], trans_desc->addr_table, trans_desc->addr_table_num * sizeof(i3c_master_ll_device_address_descriptor_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)); memcpy(bus_handle->i3c_async_command_table[bus_handle->ops_prepare_idx], trans_desc->command_table, trans_desc->command_table_num * sizeof(i3c_master_ll_command_descriptor_t));
i3c_i2c_transaction_desc_t *t = NULL; i3c_i2c_transaction_desc_t *t = NULL;
@@ -698,7 +733,8 @@ static esp_err_t i3c_master_prepare_async_transaction(i3c_master_i2c_device_hand
t->command_table_num = trans_desc->command_table_num; t->command_table_num = trans_desc->command_table_num;
t->write_buffer = trans_desc->write_buffer; t->write_buffer = trans_desc->write_buffer;
t->read_buffer = trans_desc->read_buffer; t->read_buffer = trans_desc->read_buffer;
t->scl_speed_hz = trans_desc->scl_speed_hz; t->scl_freq_hz = trans_desc->scl_freq_hz;
t->dev_handle = dev_handle;
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"); 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->ops_prepare_idx = (bus_handle->ops_prepare_idx + 1) % bus_handle->queue_depth;
@@ -706,10 +742,10 @@ static esp_err_t i3c_master_prepare_async_transaction(i3c_master_i2c_device_hand
i3c_fsm_t expected_fsm = I3C_FSM_ENABLE; i3c_fsm_t expected_fsm = I3C_FSM_ENABLE;
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
if (atomic_compare_exchange_strong(&bus_handle->fsm, &expected_fsm, I3C_FSM_RUN_WAIT)) { if (atomic_compare_exchange_strong(&bus_handle->fsm, &expected_fsm, I3C_FSM_WAIT)) {
if (xQueueReceive(bus_handle->trans_queues[I3C_TRANS_QUEUE_PROGRESS], &t, 0)) { if (xQueueReceive(bus_handle->trans_queues[I3C_TRANS_QUEUE_PROGRESS], &t, 0)) {
atomic_store(&bus_handle->fsm, I3C_FSM_RUN); atomic_store(&bus_handle->fsm, I3C_FSM_RUN);
ret = s_do_dma_transaction(bus_handle, t); ret = bus_handle->transaction_handler(bus_handle, t);
assert(ret == ESP_OK); assert(ret == ESP_OK);
} else { } else {
atomic_store(&bus_handle->fsm, I3C_FSM_ENABLE); atomic_store(&bus_handle->fsm, I3C_FSM_ENABLE);
@@ -719,41 +755,6 @@ static esp_err_t i3c_master_prepare_async_transaction(i3c_master_i2c_device_hand
return ESP_OK; 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. * 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. * Depending on the configuration, the transaction can be handled asynchronously, using DMA, or via FIFO.
@@ -765,21 +766,27 @@ static esp_err_t i3c_master_prepare_transaction(i3c_master_i2c_device_handle_t d
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
TickType_t ticks_to_wait = (xfer_timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(xfer_timeout_ms); TickType_t ticks_to_wait = (xfer_timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(xfer_timeout_ms);
// Check if async transaction requires DMA
if (dev_handle->bus_handle->async_transaction && !dev_handle->bus_handle->dma_initialized) {
ESP_LOGE(TAG, "Async transaction requires DMA, please call i3c_master_bus_decorate_dma() first");
return ESP_ERR_INVALID_STATE;
}
if (xSemaphoreTake(dev_handle->bus_handle->bus_lock_mux, ticks_to_wait) != pdTRUE) { if (xSemaphoreTake(dev_handle->bus_handle->bus_lock_mux, ticks_to_wait) != pdTRUE) {
ESP_LOGE(TAG, "I3C software timeout"); ESP_LOGE(TAG, "I3C software timeout");
return ESP_ERR_TIMEOUT; return ESP_ERR_TIMEOUT;
} }
#if CONFIG_PM_ENABLE
if (dev_handle->bus_handle->pm_lock) { 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"); ESP_GOTO_ON_ERROR(esp_pm_lock_acquire(dev_handle->bus_handle->pm_lock), err, TAG, "acquire pm_lock failed");
} }
#endif
if (dev_handle->bus_handle->async_transaction) { 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"); ESP_GOTO_ON_ERROR(do_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 { } else {
ESP_GOTO_ON_ERROR(i3c_master_prepare_fifo_transaction(dev_handle, trans_desc), err, TAG, "Prepare fifo transaction failed"); ESP_GOTO_ON_ERROR(dev_handle->bus_handle->transaction_handler(dev_handle->bus_handle, trans_desc), err, TAG, "transaction handler failed");
} }
if (dev_handle->bus_handle->async_transaction == false) { if (dev_handle->bus_handle->async_transaction == false) {
@@ -788,9 +795,11 @@ static esp_err_t i3c_master_prepare_transaction(i3c_master_i2c_device_handle_t d
ESP_GOTO_ON_FALSE(NULL, ESP_ERR_TIMEOUT, err, TAG, "event queue wait timeout. Please check whether stretch happened on i3c bus"); ESP_GOTO_ON_FALSE(NULL, ESP_ERR_TIMEOUT, err, TAG, "event queue wait timeout. Please check whether stretch happened on i3c bus");
} else { } else {
if (event == I3C_MASTER_EVENT_NACK) { if (event == I3C_MASTER_EVENT_NACK) {
ESP_GOTO_ON_FALSE(NULL, ESP_ERR_INVALID_STATE, err, TAG, "i3c-i2c nack detected"); ESP_LOGD(TAG, "%s(%d): i3c-i2c nack detected", __FUNCTION__, __LINE__);
ret = ESP_ERR_INVALID_STATE;
goto err;
} }
if (event == I3C_MASTER_EVENT_DONE) { if (event == I3C_MASTER_EVENT_TRANS_DONE) {
if (dev_handle->bus_handle->use_dma_transaction) { if (dev_handle->bus_handle->use_dma_transaction) {
if (trans_desc->read_buffer) { 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 dma_rcv_size = gdma_link_count_buffer_size_till_eof(dev_handle->bus_handle->rx_dma_link, 0);
@@ -803,9 +812,11 @@ static esp_err_t i3c_master_prepare_transaction(i3c_master_i2c_device_handle_t d
} }
#if CONFIG_PM_ENABLE
if (dev_handle->bus_handle->pm_lock) { 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"); ESP_GOTO_ON_ERROR(esp_pm_lock_release(dev_handle->bus_handle->pm_lock), err, TAG, "release pm_lock failed");
} }
#endif
xSemaphoreGive(dev_handle->bus_handle->bus_lock_mux); xSemaphoreGive(dev_handle->bus_handle->bus_lock_mux);
return ret; return ret;
@@ -823,12 +834,14 @@ esp_err_t i3c_master_i2c_device_transmit(i3c_master_i2c_device_handle_t dev_hand
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); 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] = { i3c_master_ll_i2c_speed_mode_t i2c_work_mode = (dev_handle->scl_freq_hz > 400 * 1000) ? I3C_MASTER_LL_I2C_FAST_MODE_PLUS : I3C_MASTER_LL_I2C_FAST_MODE;
{.dat.i2c = {.static_addr = dev_handle->address, .d = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}},
i3c_master_ll_device_address_descriptor_t addr_table[1] = {
{.i2c_static = {.static_addr = dev_handle->address, .dnrc = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}},
}; };
i3c_master_command_table_t command[1] = { i3c_master_ll_command_descriptor_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}}, {.cmd_l.regular = {.cmd_attr = I3C_MASTER_LL_COMMAND_REGULAR, .tid = 0x0, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = i2c_work_mode, .rnw = I3C_MASTER_LL_TRANSFER_DIR_WRITE, .roc = 0x1, .toc = 0x1}, .cmd_h.regular = {.dl = write_size}},
}; };
i3c_i2c_transaction_desc_t trans_desc = { i3c_i2c_transaction_desc_t trans_desc = {
@@ -838,7 +851,8 @@ esp_err_t i3c_master_i2c_device_transmit(i3c_master_i2c_device_handle_t dev_hand
.command_table_num = 1, .command_table_num = 1,
.write_buffer = (uint8_t *)write_buffer, .write_buffer = (uint8_t *)write_buffer,
.read_buffer = NULL, .read_buffer = NULL,
.scl_speed_hz = dev_handle->scl_speed_hz, .scl_freq_hz = dev_handle->scl_freq_hz,
.dev_handle = dev_handle,
}; };
return i3c_master_prepare_transaction(dev_handle, &trans_desc, xfer_timeout_ms); return i3c_master_prepare_transaction(dev_handle, &trans_desc, xfer_timeout_ms);
@@ -853,12 +867,14 @@ esp_err_t i3c_master_i2c_device_receive(i3c_master_i2c_device_handle_t dev_handl
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)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] = { i3c_master_ll_i2c_speed_mode_t i2c_work_mode = (dev_handle->scl_freq_hz > 400 * 1000) ? I3C_MASTER_LL_I2C_FAST_MODE_PLUS : I3C_MASTER_LL_I2C_FAST_MODE;
{.dat.i2c = {.static_addr = dev_handle->address, .d = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}},
i3c_master_ll_device_address_descriptor_t addr_table[1] = {
{.i2c_static = {.static_addr = dev_handle->address, .dnrc = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}},
}; };
i3c_master_command_table_t command[1] = { i3c_master_ll_command_descriptor_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}}, {.cmd_l.regular = {.cmd_attr = I3C_MASTER_LL_COMMAND_REGULAR, .tid = 0x0, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = i2c_work_mode, .rnw = I3C_MASTER_LL_TRANSFER_DIR_READ, .roc = 0x1, .toc = 0x1}, .cmd_h.regular = {.dl = read_size}},
}; };
i3c_i2c_transaction_desc_t trans_desc = { i3c_i2c_transaction_desc_t trans_desc = {
@@ -868,7 +884,8 @@ esp_err_t i3c_master_i2c_device_receive(i3c_master_i2c_device_handle_t dev_handl
.command_table_num = 1, .command_table_num = 1,
.write_buffer = NULL, .write_buffer = NULL,
.read_buffer = read_buffer, .read_buffer = read_buffer,
.scl_speed_hz = dev_handle->scl_speed_hz, .scl_freq_hz = dev_handle->scl_freq_hz,
.dev_handle = dev_handle,
}; };
return i3c_master_prepare_transaction(dev_handle, &trans_desc, xfer_timeout_ms); return i3c_master_prepare_transaction(dev_handle, &trans_desc, xfer_timeout_ms);
@@ -885,13 +902,15 @@ esp_err_t i3c_master_i2c_device_transmit_receive(i3c_master_i2c_device_handle_t
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); 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] = { i3c_master_ll_i2c_speed_mode_t i2c_work_mode = (dev_handle->scl_freq_hz > 400 * 1000) ? I3C_MASTER_LL_I2C_FAST_MODE_PLUS : I3C_MASTER_LL_I2C_FAST_MODE;
{.dat.i2c = {.static_addr = dev_handle->address, .d = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}},
i3c_master_ll_device_address_descriptor_t addr_table[1] = {
{.i2c_static = {.static_addr = dev_handle->address, .dnrc = 0x0, .mode = I3C_MASTER_LL_MODE_I2C,}},
}; };
i3c_master_command_table_t command[2] = { i3c_master_ll_command_descriptor_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_LL_COMMAND_REGULAR, .tid = 0x0, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = i2c_work_mode, .rnw = I3C_MASTER_LL_TRANSFER_DIR_WRITE, .roc = 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}}, {.cmd_l.regular = {.cmd_attr = I3C_MASTER_LL_COMMAND_REGULAR, .tid = 0x1, .cmd = 0x0, .cp = 0x0, .dev_indx = 0x0, .mode = i2c_work_mode, .rnw = I3C_MASTER_LL_TRANSFER_DIR_READ, .roc = 0x1, .toc = 0x1}, .cmd_h.regular = {.dl = read_size}},
}; };
i3c_i2c_transaction_desc_t trans_desc = { i3c_i2c_transaction_desc_t trans_desc = {
@@ -901,7 +920,8 @@ esp_err_t i3c_master_i2c_device_transmit_receive(i3c_master_i2c_device_handle_t
.command_table_num = 2, .command_table_num = 2,
.write_buffer = (uint8_t *)write_buffer, .write_buffer = (uint8_t *)write_buffer,
.read_buffer = read_buffer, .read_buffer = read_buffer,
.scl_speed_hz = dev_handle->scl_speed_hz, .scl_freq_hz = dev_handle->scl_freq_hz,
.dev_handle = dev_handle,
}; };
return i3c_master_prepare_transaction(dev_handle, &trans_desc, xfer_timeout_ms); return i3c_master_prepare_transaction(dev_handle, &trans_desc, xfer_timeout_ms);
@@ -948,28 +968,10 @@ esp_err_t i3c_master_bus_wait_all_done(i3c_master_bus_handle_t bus_handle, int t
return ESP_OK; return ESP_OK;
} }
esp_err_t i3c_master_toggle_transfer_async_mode(i3c_master_bus_handle_t bus_handle, bool async_mode) #if CONFIG_I3C_MASTER_ENABLE_DEBUG_LOG
__attribute__((constructor))
static void i3c_override_default_log_level(void)
{ {
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); esp_log_level_set(TAG, ESP_LOG_VERBOSE);
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;
} }
#endif

View File

@@ -8,14 +8,28 @@
#include <stdatomic.h> #include <stdatomic.h>
#include <sys/queue.h> #include <sys/queue.h>
// SOC specific headers should be included early
#include "soc/soc_caps.h"
#include "sdkconfig.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 i3c master driver
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#endif
#include "esp_log.h"
// FreeRTOS headers must be included in order: FreeRTOS.h first, then others
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "hal/i3c_master_types.h" #include "hal/i3c_master_types.h"
#include "hal/i3c_master_hal.h" #include "hal/i3c_master_hal.h"
#include "esp_dma_utils.h" #include "esp_dma_utils.h"
#include "esp_private/gdma.h" #include "esp_private/gdma.h"
#include "esp_private/gdma_link.h" #include "esp_private/gdma_link.h"
#include "esp_private/periph_ctrl.h" #include "esp_private/periph_ctrl.h"
#include "esp_intr_types.h"
#include "driver/i3c_master_types.h"
#include "esp_pm.h" #include "esp_pm.h"
#include "sdkconfig.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -23,8 +37,6 @@ extern "C" {
#define I3C_MASTER_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED #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. // interface between i3c and dma need 4 bytes aligned.
#define I3C_MASTER_DMA_INTERFACE_ALIGNMENT (4) #define I3C_MASTER_DMA_INTERFACE_ALIGNMENT (4)
@@ -69,25 +81,18 @@ typedef struct i3c_master_i2c_dev_t i3c_master_i2c_dev_t;
*/ */
typedef int i3c_master_bus_num_t; 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. * @brief Structure representing a transaction descriptor for I3C/I2C operations.
*/ */
typedef struct { typedef struct {
i3c_master_address_table_t *addr_table; /**< Pointer to the address table, mapping device addresses. */ i3c_master_ll_device_address_descriptor_t *addr_table; /**< Pointer to the address table, mapping device addresses. */
size_t addr_table_num; /**< Number of entries in the address table. */ 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. */ i3c_master_ll_command_descriptor_t *command_table; /**< Pointer to the command table for the transaction. */
size_t command_table_num; /**< Number of commands in the command table. */ 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 *write_buffer; /**< Pointer to the data buffer for writing. */
uint8_t *read_buffer; /**< Pointer to the data buffer for reading. */ 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. */ uint32_t scl_freq_hz; /**< Speed of the SCL clock in Hz for the transaction. */
i3c_master_i2c_device_handle_t dev_handle; /**< Direct storage of device handle. */
} i3c_i2c_transaction_desc_t; } i3c_i2c_transaction_desc_t;
/** /**
@@ -104,12 +109,16 @@ enum {
* @brief Enumeration representing the states of the I3C finite state machine (FSM). * @brief Enumeration representing the states of the I3C finite state machine (FSM).
*/ */
typedef enum { typedef enum {
I3C_FSM_ENABLE_WAIT, /**< FSM is waiting to enable the I3C system. */ I3C_FSM_WAIT, /**< FSM is waiting for the I3C system to be ready. */
I3C_FSM_ENABLE, /**< FSM is enabling 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_RUN, /**< FSM is in the running state, actively handling I3C operations. */
} i3c_fsm_t; } i3c_fsm_t;
/**
* @brief Function pointer type for transaction handler
*/
typedef esp_err_t (*i3c_transaction_handler_t)(i3c_master_bus_t *bus_handle, i3c_i2c_transaction_desc_t *trans_desc);
/** /**
* @brief Structure representing the I3C master bus. * @brief Structure representing the I3C master bus.
* *
@@ -122,16 +131,17 @@ struct i3c_master_bus_t {
i3c_master_hal_context_t hal; /**< HAL layer context for each port (bus). */ 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. */ i3c_master_clock_source_t clock_source; /**< Source of the I3C clock. */
uint32_t clock_source_freq; /**< Frequency of the clock source in Hz. */ uint32_t clock_source_freq; /**< Frequency of the clock source in Hz. */
bool dma_initialized; /**< Flag indicating whether DMA has been initialized via decorator. */
bool use_dma_transaction; /**< Flag indicating whether DMA is used for transactions. */ bool use_dma_transaction; /**< Flag indicating whether DMA is used for transactions. */
bool async_transaction; /**< Flag indicating whether asynchronous transactions are enabled. */ bool async_transaction; /**< Flag indicating whether asynchronous transactions are enabled. */
QueueHandle_t event_queue; /**< Queue for handling I3C events. */ 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. */ SLIST_HEAD(i3c_i2c_device_list_head, i3c_master_i2c_dev_t) device_list; /**< List of I2C devices on the bus. */
i3c_i2c_transaction_desc_t *cur_trans; /**< Pointer to the current transaction descriptor. */ 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. */ i3c_i2c_transaction_desc_t *trans_desc_pool; /**< Pool of pre-allocated transaction descriptors. */
uint32_t ops_prepare_idx; /**< Index for preparing operations. */ uint32_t ops_prepare_idx; /**< Index for preparing operations. */
bool async_memory_allocated; /**< The async transaction is allocated or not */ 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_ll_device_address_descriptor_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. */ i3c_master_ll_command_descriptor_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. */ 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. */ intr_handle_t intr_handle; /**< Interrupt handle for I3C interrupts. */
_Atomic i3c_fsm_t fsm; /**< Current state of the I3C finite state machine. */ _Atomic i3c_fsm_t fsm; /**< Current state of the I3C finite state machine. */
@@ -139,11 +149,10 @@ struct i3c_master_bus_t {
size_t queue_depth; /**< Depth of the transaction queue. */ size_t queue_depth; /**< Depth of the transaction queue. */
SemaphoreHandle_t bus_lock_mux; /**< Semaphore for bus locking. */ SemaphoreHandle_t bus_lock_mux; /**< Semaphore for bus locking. */
portMUX_TYPE spinlock; /**< Spinlock for protecting critical sections. */ portMUX_TYPE spinlock; /**< Spinlock for protecting critical sections. */
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock; /**< Power management lock handle. */ 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 #endif
size_t cache_line_size; /**< Cache line size for doing C2M operation */
uint8_t *write_fifo_buffer_pointer; /**< Pointer to the write FIFO buffer. */ uint8_t *write_fifo_buffer_pointer; /**< Pointer to the write FIFO buffer. */
size_t write_buffer_left_size; /**< Remaining size of the write 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. */ uint8_t *read_fifo_buffer_pointer; /**< Pointer to the read FIFO buffer. */
@@ -152,6 +161,7 @@ struct i3c_master_bus_t {
gdma_channel_handle_t dma_rx_chan; /**< DMA channel handle for RX. */ 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 tx_dma_link; /**< Linked list for TX DMA. */
gdma_link_list_handle_t rx_dma_link; /**< Linked list for RX DMA. */ gdma_link_list_handle_t rx_dma_link; /**< Linked list for RX DMA. */
i3c_transaction_handler_t transaction_handler; /**< Function pointer for transaction handling (FIFO or DMA) */
}; };
/** /**
@@ -163,10 +173,10 @@ struct i3c_master_bus_t {
struct i3c_master_i2c_dev_t { struct i3c_master_i2c_dev_t {
i3c_master_bus_t *bus_handle; /**< Handle to the I3C master bus managing this device. */ 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. */ uint8_t address; /**< 7-bit I2C address of the device. */
uint32_t scl_speed_hz; /**< I2C clock speed for this device, in Hz. */ uint32_t scl_freq_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. */ 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. */ void *user_ctx; /**< User-defined context passed to the callback function. */
SLIST_ENTRY(i3c_master_i2c_dev_t) next; /**< Pointer to the next device in the single-linked list. */
}; };
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -8,7 +8,6 @@
#include <stdint.h> #include <stdint.h>
#include "esp_err.h" #include "esp_err.h"
#include "hal/gpio_types.h"
#include "driver/i3c_master_types.h" #include "driver/i3c_master_types.h"
#ifdef __cplusplus #ifdef __cplusplus
@@ -22,21 +21,26 @@ extern "C" {
* including GPIO pins, clock source, queue depth, interrupt priority, and other options. * including GPIO pins, clock source, queue depth, interrupt priority, and other options.
*/ */
typedef struct { typedef struct {
gpio_num_t sda_io_num; /*!< GPIO number of I3C SDA signal, pulled-up internally */ gpio_num_t sda_io_num; /*!< GPIO number of I3C SDA signal*/
gpio_num_t scl_io_num; /*!< GPIO number of I3C SCL signal, pulled-up internally */ gpio_num_t scl_io_num; /*!< GPIO number of I3C SCL signal*/
i3c_master_clock_source_t clock_source; /*!< Clock source of I3C master bus */ 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 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). */ int intr_priority; /*!< I3C interrupt priority, if set to 0, driver will select the default priority (1,2,3). */
struct { 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_async_trans : 1; /*!< Enable asynchronous transactions, allowing the master to perform other tasks while a transaction is in progress. Only works when DMA is enabled via i3c_master_bus_decorate_dma(). */
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 */ } flags; /*!< I3C master config flags */
} i3c_master_bus_config_t; } i3c_master_bus_config_t;
/**
* @brief Configuration structure for I3C master bus DMA decorator
*
* This structure defines the DMA configuration parameters for the I3C master bus.
*/
typedef struct {
size_t max_transfer_size; /*!< Maximum transfer size in one transaction, in bytes. This decides the number of DMA nodes */
size_t dma_burst_size; /*!< DMA burst size, in bytes. If 0, driver will use default value (16 bytes) */
} i3c_master_dma_config_t;
/** /**
* @brief Create a new I3C master bus. * @brief Create a new I3C master bus.
* *
@@ -68,6 +72,23 @@ esp_err_t i3c_new_master_bus(const i3c_master_bus_config_t *bus_config, i3c_mast
*/ */
esp_err_t i3c_del_master_bus(i3c_master_bus_handle_t bus_handle); esp_err_t i3c_del_master_bus(i3c_master_bus_handle_t bus_handle);
/**
* @brief Enable DMA for I3C master bus (decorator pattern)
*
* This function enables DMA functionality for an existing I3C master bus. It follows the decorator
* pattern to avoid linking DMA code when not needed, thus reducing binary size.
*
* @param[in] bus_handle Handle to the I3C master bus to be decorated with DMA capability.
* @param[in] dma_config Pointer to the DMA configuration structure. If NULL, DMA will be disabled and fifo will be used.
*
* @return
* - ESP_OK: DMA decorator applied successfully.
* - ESP_ERR_INVALID_ARG: Invalid bus handle or configuration.
* - ESP_ERR_INVALID_STATE: Bus is already decorated with DMA or bus is in use.
* - ESP_ERR_NO_MEM: Memory allocation failed for DMA resources.
*/
esp_err_t i3c_master_bus_decorate_dma(i3c_master_bus_handle_t bus_handle, const i3c_master_dma_config_t *dma_config);
/** /**
* @brief Wait for all pending I3C transactions done * @brief Wait for all pending I3C transactions done
* *
@@ -80,26 +101,6 @@ esp_err_t i3c_del_master_bus(i3c_master_bus_handle_t bus_handle);
*/ */
esp_err_t i3c_master_bus_wait_all_done(i3c_master_bus_handle_t bus_handle, int timeout_ms); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@@ -8,7 +8,6 @@
#include <stdint.h> #include <stdint.h>
#include "esp_err.h" #include "esp_err.h"
#include "hal/gpio_types.h"
#include "driver/i3c_master_types.h" #include "driver/i3c_master_types.h"
#ifdef __cplusplus #ifdef __cplusplus
@@ -26,10 +25,7 @@ extern "C" {
*/ */
typedef struct { typedef struct {
uint8_t device_address; /*!< Device address. Must be 7-bit in I3C-I2C mode */ uint8_t device_address; /*!< Device address. Must be 7-bit in I3C-I2C mode */
uint32_t scl_speed_hz; /*!< I2C Device SCL line frequency. */ uint32_t scl_freq_hz; /*!< I2C Device SCL line frequency. */ /*!< I2C Devices config flags */
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; } i3c_device_i2c_config_t;
/** /**
@@ -82,7 +78,7 @@ esp_err_t i3c_master_bus_rm_i2c_device(i3c_master_i2c_device_handle_t dev_handle
* @param[in] dev_handle Handle to the 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_buffer Pointer to the buffer containing data to transmit.
* @param[in] write_size Size of the data in bytes. * @param[in] write_size Size of the data in bytes.
* @param[in] xfer_timeout_ms Timeout for the operation in milliseconds. * @param[in] xfer_timeout_ms Timeout for the operation in milliseconds. Note: -1 means wait forever.
* *
* @note If `enable_async_trans` is set, this function operates in asynchronous transmission mode. * @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 * In this mode, the function returns immediately after being called, even if the transmission
@@ -104,7 +100,7 @@ esp_err_t i3c_master_i2c_device_transmit(i3c_master_i2c_device_handle_t dev_hand
* @param[in] dev_handle Handle to the 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[out] read_buffer Pointer to the buffer where received data will be stored.
* @param[in] read_size Number of bytes to read. * @param[in] read_size Number of bytes to read.
* @param[in] xfer_timeout_ms Timeout for the operation in milliseconds. * @param[in] xfer_timeout_ms Timeout for the operation in milliseconds. Note: -1 means wait forever.
* *
* @note If `enable_async_trans` is set, this function operates in asynchronous transmission mode. * @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 * In this mode, the function returns immediately after being called, even if the transmission
@@ -128,7 +124,7 @@ esp_err_t i3c_master_i2c_device_receive(i3c_master_i2c_device_handle_t dev_handl
* @param[in] write_size Size of the data in bytes 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[out] read_buffer Pointer to the buffer where received data will be stored.
* @param[in] read_size Number of bytes to read. * @param[in] read_size Number of bytes to read.
* @param[in] xfer_timeout_ms Timeout for the operation in milliseconds. * @param[in] xfer_timeout_ms Timeout for the operation in milliseconds. Note: -1 means wait forever.
* *
* @note If `enable_async_trans` is set, this function operates in asynchronous transmission mode. * @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 * In this mode, the function returns immediately after being called, even if the transmission

View File

@@ -11,8 +11,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stddef.h> #include <stddef.h>
#include "hal/i3c_master_types.h" #include "hal/i3c_master_types.h"
#include "soc/soc_caps.h" #include "hal/gpio_types.h"
#include "sdkconfig.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -32,8 +31,8 @@ typedef struct i3c_master_i2c_dev_t *i3c_master_i2c_device_handle_t;
* @brief Enumeration for I3C event. * @brief Enumeration for I3C event.
*/ */
typedef enum { typedef enum {
I3C_MASTER_EVENT_DONE, /*!< I3C bus transaction done */ I3C_MASTER_EVENT_TRANS_DONE, /*!< I3C bus transaction done */
I3C_MASTER_EVENT_NACK, /*!< I3C bus nack */ I3C_MASTER_EVENT_NACK, /*!< I3C bus nack */
} i3c_master_event_t; } i3c_master_event_t;
/** /**
@@ -42,8 +41,8 @@ typedef enum {
typedef struct { typedef struct {
i3c_master_event_t event; /*!< The event type that occurred (e.g., transfer complete, error). */ 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). */ 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. */ size_t data_size; /*!< The size of the data received, in bytes. */
} i3c_i2c_master_event_data_t; } i3c_master_i2c_device_event_data_t;
/** /**
* @brief Type definition for a callback function used in I3C/I2C master operations. * @brief Type definition for a callback function used in I3C/I2C master operations.
@@ -56,7 +55,7 @@ typedef struct {
* *
* @return Whether a high priority task has been waken up by this function. * @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); typedef bool (*i3c_master_i2c_callback_t)(i3c_master_i2c_device_handle_t i2c_dev, const i3c_master_i2c_device_event_data_t *evt_data, void *arg);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -1,18 +1,18 @@
[mapping:i3c_driver] [mapping:i3c_driver]
archive: libesp_driver_i3c.a archive: libesp_driver_i3c.a
entries: entries:
if I3C_MASTER_ISR_CACHE_SAFE = y: if I3C_MASTER_ISR_HANDLER_IN_IRAM = y:
i3c_master: i3c_master_isr_handler_default (noflash) i3c_master: i3c_master_isr_handler_default (noflash)
i3c_master: s_do_dma_transaction (noflash) i3c_master: do_dma_transaction_handler (noflash)
i3c_master: handle_tx_data_buf_thld_int (noflash) i3c_master: do_fifo_transaction_handler (noflash)
i3c_master: handle_tx_data_buf_threshold_int (noflash)
i3c_master: handle_transfer_complete_int (noflash) i3c_master: handle_transfer_complete_int (noflash)
i3c_master: handle_rx_data_buf_thld_int (noflash) i3c_master: handle_rx_data_buf_threshold_int (noflash)
[mapping:i3c_driver_gdma] [mapping:i3c_driver_gdma]
archive: libesp_hw_support.a archive: libesp_hw_support.a
entries: entries:
if I3C_MASTER_ISR_CACHE_SAFE = y: if I3C_MASTER_ISR_HANDLER_IN_IRAM = y:
gdma_link: gdma_link_mount_buffers (noflash) gdma_link: gdma_link_mount_buffers (noflash)
gdma_link: gdma_link_get_head_addr (noflash) gdma_link: gdma_link_get_head_addr (noflash)
gdma: gdma_start (noflash)
gdma_link: gdma_link_count_buffer_size_till_eof (noflash) gdma_link: gdma_link_count_buffer_size_till_eof (noflash)

View File

@@ -11,17 +11,18 @@ set(EXTRA_COMPONENT_DIRS
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(i3c_master_test) project(i3c_master_test)
idf_build_get_property(elf EXECUTABLE)
if(CONFIG_COMPILER_DUMP_RTL_FILES) if(CONFIG_COMPILER_DUMP_RTL_FILES)
add_custom_target(check_test_app_sections ALL add_custom_target(check_test_app_sections ALL
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
--rtl-dirs ${CMAKE_BINARY_DIR}/esp-idf/esp_driver_i3c/,${CMAKE_BINARY_DIR}/esp-idf/hal/ --rtl-dirs ${CMAKE_BINARY_DIR}/esp-idf/esp_driver_i3c/,${CMAKE_BINARY_DIR}/esp-idf/hal/
--elf-file ${CMAKE_BINARY_DIR}/i3c_master_test.elf --elf-file ${CMAKE_BINARY_DIR}/i3c_master_test.elf
find-refs find-refs
--from-sections=.iram0.text --from-sections=.iram0.text
--to-sections=.flash.text,.flash.rodata --to-sections=.flash.text,.flash.rodata
--exit-code --exit-code
DEPENDS ${elf} DEPENDS ${elf}
) )
endif() endif()
message(STATUS "Checking i3c registers are not read-write by half-word") message(STATUS "Checking i3c registers are not read-write by half-word")

View File

@@ -10,7 +10,7 @@
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#define LEAKS (700) #define LEAKS (100)
void setUp(void) void setUp(void)
{ {
@@ -24,5 +24,18 @@ void tearDown(void)
void app_main(void) void app_main(void)
{ {
// ,--.,----. ,-----. ,--------.,------. ,---. ,--------.
// | |'.-. |' .--./ '--. .--'| .---'' .-''--. .--'
// | | .' < | | | | | `--, `. `-. | |
// | |/'-' |' '--'\ | | | `---..-' | | |
// `--'`----' `-----' `--' `------'`-----' `--'
printf(",--.,----. ,-----. ,--------.,------. ,---. ,--------. \n");
printf("| |'.-. |' .--./ '--. .--'| .---'' .-''--. .--' \n");
printf("| | .' < | | | | | `--, `. `-. | | \n");
printf("| |/'-' |' '--'\\ | | | `---..-' | | | \n");
printf("`--'`----' `-----' `--' `------'`-----' `--' \n");
unity_run_menu(); unity_run_menu();
} }

View File

@@ -23,11 +23,6 @@ TEST_CASE("I3C bus install-uninstall test", "[i3c]")
.clock_source = I3C_MASTER_CLK_SRC_DEFAULT, .clock_source = I3C_MASTER_CLK_SRC_DEFAULT,
.scl_io_num = 5, .scl_io_num = 5,
.sda_io_num = 6, .sda_io_num = 6,
.flags.enable_internal_opendrain = true,
.flags.enable_internal_pullup = true,
.flags.use_dma = true,
.flags.enable_async_trans = true,
.max_transfer_size = 1024,
.trans_queue_depth = 30, .trans_queue_depth = 30,
}; };
i3c_master_bus_handle_t bus_handle; i3c_master_bus_handle_t bus_handle;
@@ -50,11 +45,6 @@ TEST_CASE("I3C driver memory leaking check", "[i3c]")
.clock_source = I3C_MASTER_CLK_SRC_DEFAULT, .clock_source = I3C_MASTER_CLK_SRC_DEFAULT,
.scl_io_num = 5, .scl_io_num = 5,
.sda_io_num = 6, .sda_io_num = 6,
.flags.enable_internal_opendrain = true,
.flags.enable_internal_pullup = true,
.flags.use_dma = true,
.flags.enable_async_trans = true,
.max_transfer_size = 1024,
.trans_queue_depth = 30, .trans_queue_depth = 30,
}; };
i3c_master_bus_handle_t bus_handle; i3c_master_bus_handle_t bus_handle;
@@ -75,11 +65,7 @@ TEST_CASE("I3C device add & remove check", "[i3c]")
.clock_source = I3C_MASTER_CLK_SRC_DEFAULT, .clock_source = I3C_MASTER_CLK_SRC_DEFAULT,
.scl_io_num = 5, .scl_io_num = 5,
.sda_io_num = 6, .sda_io_num = 6,
.flags.enable_internal_opendrain = true,
.flags.enable_internal_pullup = true,
.flags.use_dma = true,
.flags.enable_async_trans = true, .flags.enable_async_trans = true,
.max_transfer_size = 1024,
.trans_queue_depth = 30, .trans_queue_depth = 30,
}; };
i3c_master_bus_handle_t bus_handle; i3c_master_bus_handle_t bus_handle;
@@ -87,30 +73,30 @@ TEST_CASE("I3C device add & remove check", "[i3c]")
TEST_ESP_OK(i3c_new_master_bus(&i2c_mst_config_1, &bus_handle)); TEST_ESP_OK(i3c_new_master_bus(&i2c_mst_config_1, &bus_handle));
i3c_device_i2c_config_t dev_cfg_1 = { i3c_device_i2c_config_t dev_cfg_1 = {
.scl_speed_hz = 100 * 1000, .scl_freq_hz = 100 * 1000,
.device_address = 0x10, .device_address = 0x10,
}; };
i3c_master_i2c_device_handle_t dev_1; i3c_master_i2c_device_handle_t dev_1;
i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg_1, &dev_1); TEST_ESP_OK(i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg_1, &dev_1));
i3c_device_i2c_config_t dev_cfg_2 = { i3c_device_i2c_config_t dev_cfg_2 = {
.scl_speed_hz = 100 * 1000, .scl_freq_hz = 100 * 1000,
.device_address = 0x20, .device_address = 0x20,
}; };
i3c_master_i2c_device_handle_t dev_2; i3c_master_i2c_device_handle_t dev_2;
i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg_2, &dev_2); TEST_ESP_OK(i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg_2, &dev_2));
i3c_device_i2c_config_t dev_cfg_3 = { i3c_device_i2c_config_t dev_cfg_3 = {
.scl_speed_hz = 100 * 1000, .scl_freq_hz = 100 * 1000,
.device_address = 0x30, .device_address = 0x30,
}; };
i3c_master_i2c_device_handle_t dev_3; i3c_master_i2c_device_handle_t dev_3;
i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg_3, &dev_3); TEST_ESP_OK(i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg_3, &dev_3));
i3c_master_bus_rm_i2c_device(dev_1); TEST_ESP_OK(i3c_master_bus_rm_i2c_device(dev_1));
i3c_master_bus_rm_i2c_device(dev_2); TEST_ESP_OK(i3c_master_bus_rm_i2c_device(dev_2));
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, i3c_del_master_bus(bus_handle)); TEST_ESP_ERR(ESP_ERR_INVALID_STATE, i3c_del_master_bus(bus_handle));
i3c_master_bus_rm_i2c_device(dev_3); TEST_ESP_OK(i3c_master_bus_rm_i2c_device(dev_3));
TEST_ESP_OK(i3c_del_master_bus(bus_handle)); TEST_ESP_OK(i3c_del_master_bus(bus_handle));
} }

View File

@@ -1,7 +1,7 @@
CONFIG_PM_ENABLE=y
CONFIG_COMPILER_DUMP_RTL_FILES=y CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_NONE=y CONFIG_COMPILER_OPTIMIZATION_NONE=y
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_I3C_MASTER_ISR_CACHE_SAFE=y CONFIG_I3C_MASTER_ISR_CACHE_SAFE=y
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_HAL_ASSERTION_SILENT=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y

View File

@@ -1,5 +1,6 @@
CONFIG_PM_ENABLE=y CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_PM_DFS_INIT_AUTO=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@@ -1,2 +1,3 @@
CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT_INIT=n CONFIG_ESP_TASK_WDT_INIT=n
CONFIG_IDF_EXPERIMENTAL_FEATURES=y

View File

@@ -31,7 +31,6 @@ 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_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_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) #define I3C_MASTER_LL_DEFAULT_SETUP_TIME (600)
<<<<<<< HEAD
/** /**
* @brief I3C master command types * @brief I3C master command types
@@ -221,6 +220,17 @@ typedef enum {
I3C_MASTER_LL_MODE_I2C = 1, ///< I3C works under I2C mode I3C_MASTER_LL_MODE_I2C = 1, ///< I3C works under I2C mode
} i3c_master_ll_mode_t; } i3c_master_ll_mode_t;
/**
* @brief I2C under I3C master speed mode
*
* This enumeration defines the speed 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_I2C_FAST_MODE = 0, ///< I3C works under I2C fast mode
I3C_MASTER_LL_I2C_FAST_MODE_PLUS = 1, ///< I3C works under I2C fast mode plus
} i3c_master_ll_i2c_speed_mode_t;
/** /**
* @brief I3C Device address descriptor * @brief I3C Device address descriptor
* *
@@ -279,8 +289,6 @@ typedef union {
*/ */
uint32_t val; ///< Raw 32-bit value of the address table entry. uint32_t val; ///< Raw 32-bit value of the address table entry.
} i3c_master_ll_device_address_descriptor_t; } i3c_master_ll_device_address_descriptor_t;
=======
>>>>>>> 0fb9d0462d1 (feat(i3c): Add support for i2c mode in i3c peripheral)
typedef enum { typedef enum {
I3C_MASTER_LL_FIFO_WM_LENGTH_2 = 0x0, I3C_MASTER_LL_FIFO_WM_LENGTH_2 = 0x0,
@@ -290,29 +298,6 @@ typedef enum {
I3C_MASTER_LL_FIFO_WM_LENGTH_31 = 0x4, I3C_MASTER_LL_FIFO_WM_LENGTH_31 = 0x4,
} i3c_master_ll_fifo_wm_t; } 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 * @brief Set the clock source for the I3C master
* *
@@ -322,8 +307,19 @@ typedef enum {
static inline void i3c_master_ll_set_source_clk(i3c_mst_dev_t *hw, i3c_master_clock_source_t src_clk) static inline void i3c_master_ll_set_source_clk(i3c_mst_dev_t *hw, i3c_master_clock_source_t src_clk)
{ {
(void)hw; // Suppress unused parameter warning (void)hw; // Suppress unused parameter warning
// src_clk : (1) for PLL_F160M, (0) for XTAL switch (src_clk) {
HP_SYS_CLKRST.peri_clk_ctrl119.reg_i3c_mst_clk_src_sel = (src_clk == I3C_MASTER_CLK_SRC_PLL_F160M) ? 1 : 0; case I3C_MASTER_CLK_SRC_XTAL:
HP_SYS_CLKRST.peri_clk_ctrl119.reg_i3c_mst_clk_src_sel = 0;
break;
case I3C_MASTER_CLK_SRC_PLL_F160M:
HP_SYS_CLKRST.peri_clk_ctrl119.reg_i3c_mst_clk_src_sel = 1;
break;
case I3C_MASTER_CLK_SRC_PLL_F120M:
HP_SYS_CLKRST.peri_clk_ctrl119.reg_i3c_mst_clk_src_sel = 2;
break;
default:
HAL_ASSERT(false);
}
} }
/// use a macro to wrap the function, force the caller to use it in a critical section /// use a macro to wrap the function, force the caller to use it in a critical section
@@ -338,11 +334,7 @@ static inline void i3c_master_ll_set_source_clk(i3c_mst_dev_t *hw, i3c_master_cl
* @param device_number Number of devices * @param device_number Number of devices
*/ */
__attribute__((always_inline)) __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_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++) { for (int i = 0; i < device_number; i++) {
I3C_MST_MEM.dev_addr_table[i].val = addr_table[i].val; I3C_MST_MEM.dev_addr_table[i].val = addr_table[i].val;
@@ -405,11 +397,7 @@ static inline void i3c_master_ll_reset_register(i3c_mst_dev_t *hw)
* @param command_num Number of commands * @param command_num Number of commands
*/ */
__attribute__((always_inline)) __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_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++) { for (int i = 0; i < command_num; i++) {
I3C_MST_MEM.command_buf_port.reg_command = command_buf[i].cmd_l.val; I3C_MST_MEM.command_buf_port.reg_command = command_buf[i].cmd_l.val;
@@ -472,13 +460,14 @@ static inline void i3c_master_ll_set_i2c_fast_mode_timing(i3c_mst_dev_t *hw, uin
} }
/** /**
* @brief Set the fast+ mode timing for I2C master * @brief Set the fast mode+ timing for I2C master
*
* @param hw I3C master hardware instance * @param hw I3C master hardware instance
* @param clock_source_freq Clock source frequency * @param clock_source_freq Clock source frequency
* @param scl_freq SCL frequency * @param scl_freq SCL frequency
*/ */
__attribute__((always_inline)) __attribute__((always_inline))
static inline void i3c_master_ll_set_i2c_fast_plus_mode_timing(i3c_mst_dev_t *hw, uint32_t clock_source_freq, uint32_t scl_freq) static inline void i3c_master_ll_set_i2c_fast_mode_plus_timing(i3c_mst_dev_t *hw, uint32_t clock_source_freq, uint32_t scl_freq)
{ {
uint32_t period_cnt = clock_source_freq / scl_freq / 2; uint32_t period_cnt = clock_source_freq / scl_freq / 2;
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->scl_i2c_fmp_time, reg_i2c_fmp_high_period, (period_cnt - 1)); HAL_FORCE_MODIFY_U32_REG_FIELD(hw->scl_i2c_fmp_time, reg_i2c_fmp_high_period, (period_cnt - 1));
@@ -742,11 +731,7 @@ static inline void i3c_master_ll_enable_rx_by_dma(i3c_mst_dev_t *dev, bool enabl
* @return Response data * @return Response data
*/ */
__attribute__((always_inline)) __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 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); return (i3c_master_ll_response_descriptor_t)(I3C_MST_MEM.response_buf_port.val);
} }

View File

@@ -13,6 +13,7 @@
*/ */
const i3c_master_signal_conn_t i3c_master_periph_signal[SOC_I3C_MASTER_PERIPH_NUM] = { const i3c_master_signal_conn_t i3c_master_periph_signal[SOC_I3C_MASTER_PERIPH_NUM] = {
{ {
.module_name = "I3C_MASTER",
.sda_out_sig = I3C_MST_SDA_PAD_OUT_IDX, .sda_out_sig = I3C_MST_SDA_PAD_OUT_IDX,
.sda_in_sig = I3C_MST_SDA_PAD_IN_IDX, .sda_in_sig = I3C_MST_SDA_PAD_IN_IDX,
.scl_out_sig = I3C_MST_SCL_PAD_OUT_IDX, .scl_out_sig = I3C_MST_SCL_PAD_OUT_IDX,

View File

@@ -2139,6 +2139,14 @@ config SOC_I3C_MASTER_PERIPH_NUM
bool bool
default y default y
config SOC_I3C_MASTER_ADDRESS_TABLE_NUM
int
default 12
config SOC_I3C_MASTER_COMMAND_TABLE_NUM
int
default 12
config SOC_LP_CORE_SUPPORT_ETM config SOC_LP_CORE_SUPPORT_ETM
bool bool
default y default y

View File

@@ -801,6 +801,8 @@
/*--------------------------- I3C ---------------------------------*/ /*--------------------------- I3C ---------------------------------*/
#define SOC_I3C_MASTER_PERIPH_NUM (1) #define SOC_I3C_MASTER_PERIPH_NUM (1)
#define SOC_I3C_MASTER_ADDRESS_TABLE_NUM (12)
#define SOC_I3C_MASTER_COMMAND_TABLE_NUM (12)
/*------------------------------------- ULP CAPS -------------------------------------*/ /*------------------------------------- ULP CAPS -------------------------------------*/
#define SOC_LP_CORE_SUPPORT_ETM (1) /*!< LP Core supports ETM */ #define SOC_LP_CORE_SUPPORT_ETM (1) /*!< LP Core supports ETM */

View File

@@ -16,6 +16,7 @@ extern "C" {
#if SOC_I3C_MASTER_SUPPORTED #if SOC_I3C_MASTER_SUPPORTED
typedef struct { typedef struct {
const char *module_name; // peripheral name
const uint8_t sda_out_sig; const uint8_t sda_out_sig;
const uint8_t sda_in_sig; const uint8_t sda_in_sig;
const uint8_t scl_out_sig; const uint8_t scl_out_sig;

View File

@@ -178,6 +178,12 @@ examples/peripherals/i2s/i2s_recorder:
- esp_driver_spi - esp_driver_spi
- esp_driver_i2s - esp_driver_i2s
examples/peripherals/i3c/i3c_i2c_basic:
disable:
- if: SOC_I3C_MASTER_SUPPORTED != 1
depends_components:
- esp_driver_i3c
examples/peripherals/isp/multi_pipelines: examples/peripherals/isp/multi_pipelines:
disable: disable:
- if: SOC_MIPI_CSI_SUPPORTED != 1 - if: SOC_MIPI_CSI_SUPPORTED != 1

View File

@@ -0,0 +1,8 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(i3c_i2c_basic)

View File

@@ -0,0 +1,53 @@
| Supported Targets | ESP32-P4 |
| ----------------- | -------- |
# Basic I3C_I2C Master Example
This code demonstrates how to make the I3C controller work in the legacy I2C mode and read/write an I2C sensor on the bus.
## Overview
This example demonstrates basic usage of I3C driver by reading and writing from a I2C connected sensor. Please note that according to I3C protocol, I2C sensor connects to I3C bus should only supports 7-bits mode, and no clock stretch is allowed. For more restrictions, please refer to I3C protocol.
## How to use example
### Hardware Required
To run this example, you should have an Espressif development board based on a chip listed in supported targets as well as a ICM42688. The ICM-42688 is a high-performance, low-power, 6-axis inertial measurement unit (IMU). It integrates a 3-axis gyroscope and a 3-axis accelerometer in a compact package, making it suitable for a wide range of motion-tracking applications.
#### Pin Assignment
**Note:** The following pin assignments are used by default, you can change these in the `menuconfig` .
| | SDA | SCL |
| ---------------- | -------------- | -------------- |
| ESP I3C Master | I3C_MASTER_SDA | I3C_MASTER_SCL |
| ICM42688 Sensor | SDA | SCL |
For the actual default value of `I3C_MASTER_SDA` and `I3C_MASTER_SCL` see `Example Configuration` in `menuconfig`.
### Build and Flash
Enter `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
```bash
I (23676) example: temp is 28.804348, accel (x,y,z) is (-1.033691, -1.349121, 1.077637)g, gyro (x,y,z) is (-11.718750, 8.789062, 1.647949)dps
I (23776) example: temp is 28.442028, accel (x,y,z) is (-1.248047, -1.398438, 1.038574)g, gyro (x,y,z) is (-29.418945, 20.324707, -0.610352)dps
I (23876) example: temp is 28.442028, accel (x,y,z) is (-1.100586, -1.314453, 1.043457)g, gyro (x,y,z) is (-1.953125, 12.512207, -2.746582)dps
I (23976) example: temp is 28.623188, accel (x,y,z) is (-1.093750, -1.402832, 0.952148)g, gyro (x,y,z) is (-9.521484, 40.344238, -12.939453)dps
I (24076) example: temp is 28.623188, accel (x,y,z) is (-1.229004, -1.395508, 0.868652)g, gyro (x,y,z) is (-3.173828, 44.921875, -15.380859)dps
I (24176) example: temp is 28.623188, accel (x,y,z) is (-1.069824, -1.367676, 0.959473)g, gyro (x,y,z) is (-10.253906, 12.268066, 0.366211)dps
I (24276) example: temp is 28.562801, accel (x,y,z) is (-1.075195, -1.382324, 0.959961)g, gyro (x,y,z) is (-7.080078, 6.591797, -0.305176)dps
I (24376) example: temp is 28.442028, accel (x,y,z) is (-1.081543, -1.389648, 0.953125)g, gyro (x,y,z) is (-6.958008, 4.943848, 0.610352)dps
I (24476) example: temp is 28.502415, accel (x,y,z) is (-1.090820, -1.385742, 0.945801)g, gyro (x,y,z) is (-6.591797, 4.150391, 0.000000)dps
```
## Troubleshooting
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)

View File

@@ -0,0 +1,5 @@
set(srcs "i3c_i2c_basic_main.c")
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES esp_driver_i3c
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,18 @@
menu "Example Configuration"
menu "I3C Master"
config I3C_MASTER_SCL
int "SCL GPIO Num"
default 4
help
GPIO number for I3C Master clock line.
config I3C_MASTER_SDA
int "SDA GPIO Num"
default 5
help
GPIO number for I3C Master data line.
endmenu
endmenu

View File

@@ -0,0 +1,120 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <string.h>
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/i3c_master.h"
#include "driver/i3c_master_i2c.h"
#define ICM_42688_ADDRESS 0x69
#define ICM_42688_WHO_AM_I_REG 0x75
#define ICM_42688_WHO_AM_I_VAL 0x47
#define ICM_42688_REG_PWR_MGMT0 0x4E
#define ICM_42688_REG_ACCEL_CONFIG0 0x50
#define ICM_42688_REG_GYRO_CONFIG0 0x4F
#define ICM_42688_REG_TEMP_DATA1 0x1D
#define ICM_42688_REG_ACCEL_DATA_X1 0x1F
#define ICM_42688_REG_GYRO_DATA_X1 0x25
#define ICM_42688_ACCEL_RANGE (16.0f)
#define ICM_42688_GYRO_RANGE (2000.0f)
#define ADC_MAX (32768.0f)
#define I3C_I2C_TIMEOUT_MS (5000)
typedef struct {
float accel_x;
float accel_y;
float accel_z;
float gyro_x;
float gyro_y;
float gyro_z;
float temp;
} icm42688_data_t;
static const char *TAG = "example";
void icm42688_init(i3c_master_i2c_device_handle_t icm42688_handle, i3c_master_bus_handle_t bus_handle)
{
uint8_t hw_id;
uint8_t write_hw_id = ICM_42688_WHO_AM_I_REG;
ESP_ERROR_CHECK(i3c_master_i2c_device_transmit_receive(icm42688_handle, &write_hw_id, 1, &hw_id, 1, -1));
if (hw_id != ICM_42688_WHO_AM_I_VAL) {
ESP_LOGE(TAG, "Invalid Hardware ID: 0x%02X", hw_id);
return;
}
uint8_t init_cmd[2] = {ICM_42688_REG_PWR_MGMT0, 0x0f}; // 0x0f stands for enable GYRO_MODE and ACCEL_MODE
ESP_ERROR_CHECK(i3c_master_i2c_device_transmit(icm42688_handle, init_cmd, 2, I3C_I2C_TIMEOUT_MS));
uint8_t accel_cfg[2] = {ICM_42688_REG_ACCEL_CONFIG0, 0x23}; // 0x23 stands for range +-8g with 8khz
ESP_ERROR_CHECK(i3c_master_i2c_device_transmit(icm42688_handle, accel_cfg, 2, I3C_I2C_TIMEOUT_MS));
uint8_t gyro_cfg[2] = {ICM_42688_REG_GYRO_CONFIG0, 0x23}; // 0x23 stands for range +-1000dps with 8khz
ESP_ERROR_CHECK(i3c_master_i2c_device_transmit(icm42688_handle, gyro_cfg, 2, I3C_I2C_TIMEOUT_MS));
}
void icm42688_read_data(i3c_master_i2c_device_handle_t icm42688_handle, icm42688_data_t *data)
{
uint8_t raw_data[14];
uint8_t temp_data = ICM_42688_REG_TEMP_DATA1;
ESP_ERROR_CHECK(i3c_master_i2c_device_transmit_receive(icm42688_handle, &temp_data, 1, raw_data, sizeof(raw_data), I3C_I2C_TIMEOUT_MS));
int16_t temp_raw = (raw_data[0] << 8) | raw_data[1];
// Provided by datasheet
data->temp = (temp_raw / 132.48f) + 25.0f;
int16_t accel_x = (raw_data[2] << 8) | raw_data[3];
int16_t accel_y = (raw_data[4] << 8) | raw_data[5];
int16_t accel_z = (raw_data[6] << 8) | raw_data[7];
data->accel_x = accel_x * (ICM_42688_ACCEL_RANGE / ADC_MAX);
data->accel_y = accel_y * (ICM_42688_ACCEL_RANGE / ADC_MAX);
data->accel_z = accel_z * (ICM_42688_ACCEL_RANGE / ADC_MAX);
int16_t gyro_x = (raw_data[8] << 8) | raw_data[9];
int16_t gyro_y = (raw_data[10] << 8) | raw_data[11];
int16_t gyro_z = (raw_data[12] << 8) | raw_data[13];
data->gyro_x = gyro_x * (ICM_42688_GYRO_RANGE / ADC_MAX);
data->gyro_y = gyro_y * (ICM_42688_GYRO_RANGE / ADC_MAX);
data->gyro_z = gyro_z * (ICM_42688_GYRO_RANGE / ADC_MAX);
}
void app_main(void)
{
i3c_master_bus_handle_t bus_handle = NULL;
i3c_master_i2c_device_handle_t icm42688_handle = NULL;
i3c_master_bus_config_t i3c_bus_config = {
.clock_source = I3C_MASTER_CLK_SRC_DEFAULT,
.scl_io_num = CONFIG_I3C_MASTER_SCL,
.sda_io_num = CONFIG_I3C_MASTER_SDA,
};
ESP_ERROR_CHECK(i3c_new_master_bus(&i3c_bus_config, &bus_handle));
i3c_device_i2c_config_t dev_cfg = {
.device_address = ICM_42688_ADDRESS,
.scl_freq_hz = 100000,
};
ESP_ERROR_CHECK(i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg, &icm42688_handle));
icm42688_init(icm42688_handle, bus_handle);
icm42688_data_t *data = heap_caps_malloc(sizeof(icm42688_data_t), MALLOC_CAP_DEFAULT);
assert(data);
while (1) {
icm42688_read_data(icm42688_handle, data);
ESP_LOGI(TAG, "temp is %f, accel (x,y,z) is (%f, %f, %f)g, gyro (x,y,z) is (%f, %f, %f)dps", data->temp, data->accel_x, data->accel_y, data->accel_z, data->gyro_x, data->gyro_y, data->gyro_z);
vTaskDelay(pdMS_TO_TICKS(100));
}
}

View File

@@ -0,0 +1 @@
CONFIG_IDF_EXPERIMENTAL_FEATURES=y

View File

@@ -522,3 +522,7 @@
- -
re_variables: ['driver/sigmadelta.h'] re_variables: ['driver/sigmadelta.h']
hint_variables: ['legacy Sigma-Delta', 'driver/sdm.h', 'esp_driver_sdm'] hint_variables: ['legacy Sigma-Delta', 'driver/sdm.h', 'esp_driver_sdm']
-
re: undefined reference to `i3c_new_master_bus'
hint: "The I3C master driver is not fully supported in IDF. To use this driver please enable `IDF_EXPERIMENTAL_FEATURES`"